#wpf #localization #binding #resourcedictionary
#wpf #локализация #привязка #resourcedictionary
Вопрос:
У меня есть окно XAML, в котором есть набор строк, которые привязаны к объектам следующим образом:
<Label Content="{StaticResource LabelUserName}" HorizontalAlignment="Right" Name="Label1" VerticalAlignment="Center" />
Этот код отлично работает, когда я определяю свой ResouceDictionary следующим образом:
<Window.Resources>
<ResourceDictionary >
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Strings/Strings_en-US.xaml" />
</ResourceDictionary.MergedDictionaries>
<ObjectDataProvider x:Key="objLogon" ObjectType="{x:Type StarUtilities:Logon}" />
</ResourceDictionary>
</Window.Resources>
Однако я хочу иметь возможность изменять, откуда привязаны строки, поэтому я создал модуль, который выглядит следующим образом:
Public Module LocalizationChooser
Public ReadOnly Property GetLocalization As Uri
Get
Return New Uri("/Strings/Strings_en-US.xaml", UriKind.Relative)
End Get
End Property
End Module
Затем я решил, что попробую изменить Source
на строку привязки. Однако я пробовал следующее, и ни одно из них не работает:
<ResourceDictionary Source="{Binding Source={x:Static Member=StarUI:LocalizationChooser.GetLocalization}}" />
<ResourceDictionary Source="{x:Static Member=StarUI:LocalizationChooser.GetLocalization}" />
<ResourceDictionary Source="{Binding Source=StarUI:LocalizationChooser.GetLocalization}" />
<ResourceDictionary Source="{Binding Source=LocalizationChooser, Path=GetLocalization}" />
<ResourceDictionary Source="{Binding Source=StarUI:LocalizationChooser, Path=GetLocalization}" />
<ResourceDictionary Source="{Binding Source={x:Static Member=StarUI:LocalizationChooser.GetLocalization}, Path=GetLocalization}" />
<ResourceDictionary Source="{Binding Source={x:Static Member=StarUI:LocalizationChooser.GetLocalization}, Path=LocalizationChooser.GetLocalization}" />
<ResourceDictionary Source="{Binding Source={x:Static Member=StarUI:LocalizationChooser.GetLocalization}, Path=StarUI:LocalizationChooser.GetLocalization}" />
Все генерируют одну и ту же ошибку:
Для «ResourceDictionary» было вызвано исключение ArgumentNullException: значение не может быть нулевым.
Имя параметра: item
Что я могу сделать, чтобы это заработало?
Я должен упомянуть, что пространство имен StarUI уже определено следующим образом:
xmlns:StarUI="clr-namespace:StarUI"
Редактировать:
Я обнаружил, что с изменением, которое я внес в модуль, я могу получить код для компиляции и запуска, если я использую:
<ResourceDictionary Source="{x:Static StarUI:LocalizationChooser.GetLocalization}" />
Однако у меня все еще нет поддержки во время разработки. Поскольку модуль возвращает статический элемент, которому не нужно ничего искать, есть ли способ получить поддержку во время разработки здесь? Если нет, есть ли способ указать в XAML статическую ссылку для времени разработки, а затем динамическую ссылку для времени выполнения?
ПРАВКА 2:
Что я, наконец, сделал, так это написал XAML, определяющий Source
статически, а затем настроил конструктор на «перезапись» этого. Поскольку ссылки на словарные записи с одинаковыми именами будут основываться на последней добавленной, я могу добавить словарь во время выполнения, но все еще иметь словарь во время разработки.
Я использовал следующее, чтобы просто локализовать свой код, включая поддержку во время разработки и включение динамической смены языков.
В моем XAML я включил:
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary x:Name="LocalizationStrings" Source="/Strings/Strings_en-US.xaml" />
</ResourceDictionary.MergedDictionaries>
<ObjectDataProvider x:Key="objLogon" ObjectType="{x:Type StarUtilities:Logon}" />
</ResourceDictionary>
</Window.Resources>
В моем коде-за:
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Me.Resources.MergedDictionaries.Add(GetLocalization)
End Sub
getLocation находится в общедоступном модуле:
Public ReadOnly Property GetLocalization As ResourceDictionary
Get
Dim resourceLocalization As New ResourceDictionary
resourceLocalization.Source = New Uri("/Strings/Strings_zh-cn.xaml", UriKind.Relative)
Return resourceLocalization
End Get
End Property
Пример моего ResourceDictionary XAML является:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<sys:String x:Key="ButtonLogon">Logon</sys:String>
<sys:String x:Key="ButtonCancel">Cancel</sys:String>
<sys:String x:Key="LabelUserName">User Name:</sys:String>
<sys:String x:Key="LabelPassword">Password:</sys:String>
<sys:String x:Key="LabelKey">Decryption Key:</sys:String>
</ResourceDictionary>
Затем я просто получаю доступ к языковой строке через:
<Label Content="{DynamicResource LabelUserName}" Name="Label1" />
Теперь все работает отлично!
Комментарии:
1. Похоже, что моей первой ошибкой было то, что модуль возвращал строку вместо URI. Я отредактировал приведенный выше код, чтобы отразить правильный способ выполнения этого.
Ответ №1:
Вам потребуется переключиться со StaticResource на DynamicResource, поскольку ваш ресурс доступен не сразу.
Но вы не можете использовать привязки к свойствам ResourceDictionary, поскольку они не являются производными от DependencyObject .
Комментарии:
1. Я все еще получаю ошибку времени разработки даже после изменения на
Content="{DynamicResource LabelUserName}"
Это то, что вы имели в виду, что я должен изменить?2. @cjbarth — Да. Извините, я пропустил, что у вас была привязка к ResourceDictionary. Источник. Это недопустимо, поскольку ResourceDictionary не является производным от DepedencyObject.
3. @cjbarth — Вам также может потребоваться использовать относительный или абсолютный UriKind.
4. Интересно… Итак, во время выполнения все работает нормально, поскольку ResourceDictionary не является производным от DependencyObject, он не может предоставить мне поддержку во время разработки? Есть ли способ указать некоторый код в исходном коде (например, IF([Привязка],[Строка])), чтобы я мог видеть значения времени разработки на основе предопределенного значения, а затем, если есть значение времени выполнения (т. Е. привязка не является ничем), использовать это вместо?
5. Хм, лучшее, что вы, вероятно, могли бы сделать, это определить вещи статически в вашем XAML, а затем поменять местами с вашим динамическим ResourceDictionary в вашей Windows. Ресурс во время выполнения. Однако при этом будут некоторые накладные расходы. Вы можете попробовать расширение XAML #if-#endif Джоша Смита (или что-то подобное), но, скорее всего, у него также будут проблемы.
Ответ №2:
Если вы переместите свои ресурсы локализации в отдельную сборку, вы все равно сможете использовать статические ресурсы в своих файлах ResourceDictionary. Вам нужно будет добавить uri «assembly» к вашей ссылке на заголовок ns (т.Е.: xmlns:loc="clr-namespace:MyDLL.Strings;assembly=MyDll.Localization"
).
Затем вы можете использовать свои ресурсы локализации, как всегда, без дополнительного кода:
<Label Content="{x:Static loc:LocalizationStrings.TitleXXX}" />
Я всегда использую свои ресурсы локализации в отдельных сборках.