Taurib
Taurib

Reputation: 461

C# WPF binding localized text to element

So I made a little test application to test out, if this binding localized text to TextBlock works. I have my doubts, that it should/could be done better and if yes, then your recommendations would be really nice!

Also I have in XAML code commented out TextBlock, that does Bind value directly form resx, but I couldn't get it working.. Any ideas about how I could change that text there would be awesome as well! :)

XAML:

<Window x:Class="LocalizationTestWpf.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:LocalizationTestWpf.Resources"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">


    <Grid>
        <Button x:Name="btnChange" Content="Button" HorizontalAlignment="Left" Margin="206,192,0,0" VerticalAlignment="Top" Width="75" Click="btnChange_Click"/>
        <!--<TextBlock x:Name="txtDisplay" HorizontalAlignment="Center" Margin="206,138,236,152" TextWrapping="Wrap" Text="{x:Static local:strings.Hello}" VerticalAlignment="Center" TextAlignment="Center" Width="75" Height="29"/>-->
        <TextBlock x:Name="txtDisplay" HorizontalAlignment="Center" Margin="206,138,236,152" TextWrapping="Wrap" Text="{Binding SayHello}" VerticalAlignment="Center" TextAlignment="Center" Width="75" Height="29"/>

    </Grid>
</Window>

C#:

// http://social.technet.microsoft.com/wiki/contents/articles/22420.binding-to-resources-resx-files-in-xaml.aspx    -- how to bind WPF

        int step = 0;

        public MainWindow()
        {
            Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("et-EE");
            // Adds my class (object) as datacontext, so I can bind those values.
            DataContext = new StringValues();
            InitializeComponent();
        }

        private void btnChange_Click(object sender, RoutedEventArgs e)
        {
            switch (step)
            {
                case 0:
                    // Change language
                    Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("fr-FR");
                    // Update DataContext -- Do I need to do it like that?
                    DataContext = new StringValues();
                    InitializeComponent();

                    btnChange.Content = "Russian";
                    step++;
                    break;
                // Following steps are same as 'case 0', only different language.
                case 1:
                    Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("ru-RU");
                    DataContext = new StringValues();
                    btnChange.Content = "English";
                    step++;
                    break;
                case 2:
                    Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-US");
                    DataContext = new StringValues();
                    btnChange.Content = "Estonian";
                    step++;
                    break;
                default:
                    Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("et-EE");
                    DataContext = new StringValues();
                    btnChange.Content = "France";
                    step = 0;
                    break;
            }
        }


    }

    public class StringValues
    {
        public string SayHello
        {
            get { return strings.Hello; }
        }
    }

Project folder:

Project directory

How WPF looks like in VS (note, that textblock is empty with this binding method):

MainWindows.xaml

Sample RESX file:

.resx file

Upvotes: 1

Views: 3029

Answers (1)

Szabolcs D&#233;zsi
Szabolcs D&#233;zsi

Reputation: 8843

A possible approach can be to use ResourceDictionarys instead of resx files.

XAML:

<Window x:Class="BindToResources.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel>
        <Button x:Name="btnChange" Content="Button" Click="btnChange_Click"/>
        <TextBlock x:Name="txtDisplay" Text="{DynamicResource Hello}" />
    </StackPanel>
</Window>

Code behind:

public partial class MainWindow : Window
{
    int step = 0;

    public MainWindow()
    {
        InitializeComponent();

        var cultureInfo = CultureInfo.GetCultureInfo("et-EE");
        Thread.CurrentThread.CurrentUICulture = cultureInfo;
        SetLocalization(cultureInfo);
    }

    private void btnChange_Click(object sender, RoutedEventArgs e)
    {
        CultureInfo cultureInfo;

        switch (step)
        {
            case 0:
                cultureInfo = CultureInfo.GetCultureInfo("fr-FR");
                break;
            case 1:
                cultureInfo = CultureInfo.GetCultureInfo("ru-RU");
                break;
            case 2:
                cultureInfo = CultureInfo.GetCultureInfo("en-US");
                break;
            default:
                cultureInfo = CultureInfo.GetCultureInfo("et-EE");
                break;
        }

        Thread.CurrentThread.CurrentUICulture = cultureInfo;
        SetLocalization(cultureInfo);
        btnChange.Content = cultureInfo.EnglishName;
        step = ++step % 4;
    }

    private static void SetLocalization(CultureInfo cultureInfo)
    {
        var dict = new ResourceDictionary
        {
            Source = new Uri(string.Format("pack://application:,,,/Languages/{0}.xaml", cultureInfo.Name))
        };

        var existingDict = Application.Current.Resources.MergedDictionaries.FirstOrDefault(
            rd => rd.Source.OriginalString.StartsWith("pack://application:,,,/Languages/"));

        if (existingDict != null)
        {
            Application.Current.Resources.MergedDictionaries.Remove(existingDict);
        }

        Application.Current.Resources.MergedDictionaries.Add(dict);
    }
}

You have ResourceDictionary classes for each language (language_code.xaml format):

Solution Explorer

You define the localized strings in each dictionary.

Example of Russian:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:system="clr-namespace:System;assembly=mscorlib">
    <system:String x:Key="Hello">Здравствуйте</system:String>
</ResourceDictionary>

Example of Estonian:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:system="clr-namespace:System;assembly=mscorlib">
    <system:String x:Key="Hello">Tere</system:String>
</ResourceDictionary>

So the main idea here is to use DynamicResource for the lookup as you can see in the first XAML and add the correct ResourceDictionary to Application.Current.Resources during runtime.

This is working for me:

End result

Upvotes: 4

Related Questions