BarryLib
BarryLib

Reputation: 299

How to change UI language using resource dictionary at run time?

I want to change my project language by button_click event, so I use ResourceDictionary to do it like this:

XAML

    <Button Content="{DynamicResource LanguageSetting}" Click="btn_LanguageSetting_Click"/>

Code Behind

    public static string windowCurrentLanguageFile = "Language/en.xaml";
    private void btn_LanguageSetting_Click(object sender, RoutedEventArgs e)
    {
        windowCurrentLanguageFile = windowCurrentLanguageFile == "Language/en.xaml"
            ? "Language/fr.xaml"
            : "Language/en.xaml";

        var rd = new ResourceDictionary() { Source = new Uri(windowCurrentLanguageFile, UriKind.RelativeOrAbsolute) };

        if (this.Resources.MergedDictionaries.Count == 0)
            this.Resources.MergedDictionaries.Add(rd);
        else
            this.Resources.MergedDictionaries[0] = rd;
    }

This works fine for the xaml file, but I also want to change my viewmodel's language in the code behind.

My ItemsControl in the xaml:

<ItemsControl ItemsSource="{Binding ItemOperate}">
        <ItemsControl.ItemTemplate>
            <DataTemplate DataType="{x:Type viewmodel:SelectableViewModel}">
                <Border x:Name="Border" Padding="0,8,0,8" BorderThickness="0 0 0 1" BorderBrush="{DynamicResource MaterialDesignDivider}">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition SharedSizeGroup="Checkerz" />
                            <ColumnDefinition />
                        </Grid.ColumnDefinitions>
                        <ToggleButton VerticalAlignment="Center" IsChecked="{Binding IsSelected}"
                                      Style="{StaticResource MaterialDesignActionLightToggleButton}"
                                      Content="{Binding Code}" />
                        <StackPanel Margin="8 0 0 0" Grid.Column="7">
                            <TextBlock FontWeight="Bold" Text="{Binding Name}" />
                            <TextBlock Text="{Binding Description}" />
                        </StackPanel>
                    </Grid>
                </Border>
                <DataTemplate.Triggers>
                    <DataTrigger Binding="{Binding IsSelected}" Value="True">
                        <Setter TargetName="Border" Property="Background" Value="{DynamicResource MaterialDesignSelection}" />
                    </DataTrigger>
                </DataTemplate.Triggers>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>

Which Binding to the ViewModel like this:

public class SelectableViewModel : INotifyPropertyChanged
{ 
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }

    private bool _isSelected;
    public bool IsSelected
    {
        get { return _isSelected; }
        set
        {
            if (_isSelected == value) return;
            _isSelected = value;
            OnPropertyChanged();
        }
    }

    private char _code;
    public char Code
    {
        get { return _code; }
        set
        {
            if (_code == value) return;
            _code = value;
            OnPropertyChanged();
        }
    }

    private string _name;
    public string Name
    {
        get { return _name; }
        set
        {
            if (_name == value) return;
            _name = value;
            OnPropertyChanged();
        }
    }

    private string _description;
    public string Description
    {
        get { return _description; }
        set
        {
            if (_description == value) return;
            _description = value;
            OnPropertyChanged();
        }
    }
}

And

    public MainViewModel()
    {
        _itemOperate = CreateData();
    }

    private static ObservableCollection<SelectableViewModel> CreateData()
    {
        return new ObservableCollection<SelectableViewModel>
            {
                new SelectableViewModel
                {
                    Code = 'E', 
                    Name = "Erase",
                    Description = "Erase The MCU Chip By Page"
                },
                new SelectableViewModel
                {
                    Code = 'D',
                    Name = "Detect",
                    Description = "Detect The MCU Flash",
                },
                new SelectableViewModel
                {
                    Code = 'P',
                    Name = "Programming",
                    Description = "Programming The MCU Chip By Hex File",
                },
                new SelectableViewModel
                {
                    Code = 'V',
                    Name = "Verify",
                    Description = "Verify The Downing Code",
                },
                new SelectableViewModel
                {
                    Code ='L',
                    Name = "Lock",
                    Description = "Lock The Code To Protect The MCU",
                }
            };
    }

So how I suppose to change the language my model SelectableViewModel Code, Name, Description instead of using this hard code? Thanks!

Upvotes: 2

Views: 2379

Answers (1)

Dragos Stoica
Dragos Stoica

Reputation: 1935

I will propose you a solution for changing the language at runtime.

  1. Create the resource manager:

    public class CultureResources
    {
        private static bool isAvailableCulture;
        private static readonly List<CultureInfo> SupportedCultures = new List<CultureInfo>();
        private static ObjectDataProvider provider;
    
        public CultureResources()
        {
            GetAvailableCultures();
        }
    
            /// <summary>
            /// Gets automatically all supported cultures resource files.
            /// </summary>
            public void GetAvailableCultures()
            {
    
                if (!isAvailableCulture)
                {
                    var appStartupPath = AppDomain.CurrentDomain.BaseDirectory;
    
                    foreach (string dir in Directory.GetDirectories(appStartupPath))
                    {
                        try
                        {
                            DirectoryInfo dirinfo = new DirectoryInfo(dir);
                            var culture = CultureInfo.GetCultureInfo(dirinfo.Name);
                            SupportedCultures.Add(culture);
                        }
                        catch (ArgumentException)
                        {
                        }
                    }
                    isAvailableCulture = true;
                }
            }
    
            /// <summary>
            /// Retrieves the current resources based on the current culture info
            /// </summary>
            /// <returns></returns>
            public Resources GetResourceInstance()
            {
                return new Resources();
            }
    
            /// <summary>
            /// Gets the ObjectDataProvider wrapped with the current culture resource, to update all localized UIElements by calling ObjectDataProvider.Refresh()
            /// </summary>
            public static ObjectDataProvider ResourceProvider
            {
                get {
                    return provider ??
                           (provider = (ObjectDataProvider)System.Windows.Application.Current.FindResource("Resources"));
                }
            }
    
            /// <summary>
            /// Changes the culture
            /// </summary>
            /// <param name="culture"></param>
            public  void ChangeCulture(CultureInfo culture)
            {
                if (SupportedCultures.Contains(culture))
                {
                    Resources.Culture = culture;
                    ResourceProvider.Refresh();
                }
                else
                {
                    var ci = new CultureInfo("en");
    
                    Resources.Culture = ci;
                    ResourceProvider.Refresh();
                }
            }
    
            /// <summary>
            /// Sets english as default language
            /// </summary>
            public void SetDefaultCulture()
            {
                CultureInfo ci = new CultureInfo("en");
    
                Resources.Culture = ci;
                ResourceProvider.Refresh();
            }
    
            /// <summary>
            /// Returns localized resource specified by the key
            /// </summary>
            /// <param name="key">The key in the resources</param>
            /// <returns></returns>
            public static string GetValue(string key)
            {
                if (key == null) throw new ArgumentNullException();
                return Resources.ResourceManager.GetString(key, Resources.Culture);
            }
    
            /// <summary>
            /// Sets the new culture
            /// </summary>
            /// <param name="cultureInfo">  new CultureInfo("de-DE");  new CultureInfo("en-gb");</param>
            public void SetCulture(CultureInfo cultureInfo)
            {
    
                //get language short format - {de} {en} 
                var ci = new CultureInfo(cultureInfo.Name.Substring(0,2));
                ChangeCulture(ci);
                Thread.CurrentThread.CurrentCulture = cultureInfo;
                Thread.CurrentThread.CurrentUICulture = cultureInfo;
               // CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("en-GB");
    
            }
        }
    
  2. Define resource file for each language type as you want. enter image description here above I've defined the default for English and the second file for German (you can see the ".de" extension. So you do for any other language. Be sure that you open the Resources.resx properties and select as Custom Tool the value PublicResXFileCodeGenerator. This step is necessary in order to expose the Resource through an object provider. enter image description here

  3. Register the Resource provider via an object provider in the App.xaml: enter image description here

  4. Usage: in Xaml :

    Text="{Binding ResourcesText1, Source={StaticResource Resources}}"

From *.cs : Resources.ResourcesText1.

P.S. When you change the culture be sure you call SetCulture method from the CultureResouces.

Upvotes: 3

Related Questions