Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
224 views
in Technique[技术] by (71.8m points)

c# - Changing CurrentUICulture at runtime in a Localizable Form in WinForms

I have been searching about how to change the language of a Form that has the Localizable attribute set to true.

https://msdn.microsoft.com/en-us/library/system.threading.thread.currentuiculture(v=vs.110).aspx

This is to set the language of the form, but this needs to be set before we instantiate the form. This cannot be called after this event.

Searching for information, I have seen the following question: https://stackoverflow.com/a/11738932/3286975 but, as a comment said, I have controls inside of a TabControl and a MenuStrip, so they aren't affected.

I have tried to modify this, by getting all the controls of the Form without luck.

enter image description here

enter image description here

In this menu I call the following callback:

    private void englishToolStripMenuItem_Click_1(object sender, EventArgs e)
    {
        string lang = (string) ((ToolStripMenuItem) sender).Tag;
        base.Culture = CultureInfo.CreateSpecificCulture(lang);
    }

    private void spanishToolStripMenuItem_Click(object sender, EventArgs e)
    {
        string lang = (string) ((ToolStripMenuItem) sender).Tag;
        base.Culture = CultureInfo.CreateSpecificCulture(lang);
    }

I change the Culture by using the Tag.

When I click it nothing happens. Also, I have modified a little bit the ApplyResources method from the mentioned answer.

private void ApplyResources(Control parent, CultureInfo culture)
{
        this.resManager.ApplyResources(parent, parent.Name, culture);

        foreach (Control ctl in parent.IterateAllChildren())
        {
            //this.ApplyResources(ctl, culture);
            this.resManager.ApplyResources(ctl, ctl.Name, culture);
        }
}

Where IterateAllChildren is the following: https://stackoverflow.com/a/16725020/3286975

Also, I tried with (System.LINQ): Controls.OfType<Label>() (because I have one Label to test this) without luck...

enter image description here

enter image description here

But when I select the Spanish language, no text is changed.

So maybe, I'm failling with the childrens. Or maybe by calling the method CreateCulture, I don't know.

Thanks in advance!

EDIT:

I have tested to get the Resource Manager of my form by the Culture Info and it returns the default one everytime:

 ResourceSet resourceSet = new ResourceManager(typeof(frmCredentials)).GetResourceSet(new CultureInfo(lang), true, true);
            foreach (DictionaryEntry entry in resourceSet)
            {
                string resourceKey = entry.Key.ToString();
                object resource = entry.Value; //resourceSet.GetString(resourceKey);
                if (resource.GetType().Equals(typeof(string)))
                    Console.WriteLine("Key: {0}
Value: {1}

", resourceKey, (string) resource);
            }

Where new CultureInfo(lang), I haved tested also: new CultureInfo("es") & Thread.CurrentThread.CurrentCulture (CurrentUICulture) without luck. Is like it never exists or is replaced, but in my design and file explorer I can see the files.

EDIT2:

Maybe is because I'm using ILMerge to merge all dlls in a unique one. I'm reviewing this: Single-assembly multi-language Windows Forms deployment (ILMerge and satellite assemblies / localization) - possible?

Reply to EDIT2:

Yep, deleting ILMerge the problem is solved, and the first solution I gave resolves this. But for some reason, Spanish language is taken as the default language, and when I tried to get the resourceset from it, it didn't return me nothing.

Aso, I have set the Localizable attribute to false, and it didn't created a default resx file with values. I don't know if this is a good practice.

I will try something new...

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

MVVM (Model - View - ViewModel) approach have some benefits which can be useful in your case.

Create new resource files for languages which you will use for localization. Working with form's own resource files can be little bid tricky because it regenerated every time you make change in the designer - so I think own resource file will be easier to maintain and even share with other forms and even projects.

LocalizationValues.resx // (default english), set Access Modifier to "Internal" or "Public"
    "Title": "Title"
    "Description": "Description"

LocalizationValues.es.resx
    "Title": "Título"
    "Description": "Descripción"

Visual Studio generate static class LocalizationValues with properties as keys of .resx file. So "Title" can be accessed as LocalizationValues.Title

Create "viewmodel" class which represents all texts you are using in localization. Class should implements INotifyPropertyChanged interface.

public class LocalizationViewModel : INotifyPropertyChanged
{
    public string Title
    {
        get
        {
            return LocalizationValues.Title;
        }
    }

    public string Description
    {
        get
        {
            return LocalizationValues.Description;
        }
    }

    public void SetLanguage(string language)
    {
        var culture = new CultureInfo(language);
        Thread.CurrentThread.CurrentUICulture = culture;

        // This is important, 
        // By raising PropertyChanged you notify Form to update bounded controls
        NotifyAllPropertyChanged();
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void NotifyAllPropertyChanged()
    {
        // Passing empty string as propertyName
        // will indicate that all properties of viewmodel have changed
        NotifyPropertyChanged(string.Empty);
    }

    protected void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Then in Form bind viewmodel to controls

public partial class YourForm : Form
{
    private LocalizationViewModel _viewmodel;

    public YourForm()
    {
        InitializeComponent();

        _viewmodel = new LocalizationViewModel();

        // Bound controls to correspondent viewmodel's properties
        LblTitle.DataBindings.Add("Text", _viewmodel, "Title", true);
        LblDescription.DataBindings.Add("Text", _viewmodel, "Description", true);
    }

    // Menu buttons to change language
    private void SpanishToolStripMenuItem_Click(object sender, EventArgs e)
    {
        _viewmodel.SetLanguage("es");
    }

    private void EnglishToolStripMenuItem_Click(object sender, EventArgs e)
    {
        _viewmodel.SetLanguage("en");
    }
}

Approach above will provide more benefits then only updating controls. You get clearly separated parts of your application, which can be tested independently from each other.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...