cjbarth
cjbarth

Reputation: 4479

ResourceDictionary Source Binding to Module (for Localization)

I have a XAML Window that has a set of strings that are bound to objects like so:

<Label Content="{StaticResource LabelUserName}" HorizontalAlignment="Right" Name="Label1" VerticalAlignment="Center" />

This code works fine when I define my ResouceDictionary like this:

<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>

However, I want to beable to change where the strings are bound from, so I created a module that looks like this:

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

Then I decided I would try changing the Source to a Binding string. However, I've tried the following and none work:

<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}" />

All generate the same error:

ArgumentNullException was thrown on "ResourceDictionary": Value cannot be null.
Parameter name: item

What can I do to get this working?

I should mention that the StarUI namespace is already defined as the following:

xmlns:StarUI="clr-namespace:StarUI"

EDIT:
I found that with the change I made to the module I can get the code to compile and run if I use:

<ResourceDictionary Source="{x:Static StarUI:LocalizationChooser.GetLocalization}" />

However, I still don't have design-time support. Since the module is returning a static item that doesn't need to lookup anything, is there a way to get design-time support here? If not, is there a way to specify in the XAML a static reference for design-time and then they dynamic reference for run-time?

EDIT 2:
What I finally did was to write the XAML specifying the Source statically and then setting the constructor to 'overwrite' that. Since dictionary entries with the same name will be referenced based on the last one added, I can add a dictionary at run-time but still have a dictionary at design-time.

I used the following to localize my code simply, including design-time support and enabling dynamic changing of languages.

In my XAML I included:

<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>

In my code-behind:

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 is in a Public Module:

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

A sample of my ResourceDictionary XAML is:

<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>

Then I just access the language string via:

<Label Content="{DynamicResource LabelUserName}" Name="Label1" />

Now everything works great!

Upvotes: 3

Views: 7882

Answers (2)

lukiller
lukiller

Reputation: 1207

If you move your localization resources to a separate assembly, then you can still use static resources within your ResourceDictionary files. You will need to add the "assembly" uri to your header ns reference (ie: xmlns:loc="clr-namespace:MyDLL.Strings;assembly=MyDll.Localization").

Then you can use your localization resources as always with no extra code:

<Label Content="{x:Static loc:LocalizationStrings.TitleXXX}" />

I always use my localization resources in separate assemblies.

Upvotes: 0

CodeNaked
CodeNaked

Reputation: 41393

You would need to switch from StaticResource to DynamicResource, since your resource isn't immediately available.

But you cannot using Bindings on the ResourceDictionary properties, as it does not derive from DependencyObject.

Upvotes: 4

Related Questions