有时候我们会按照特定需要来修改系统的显示语言(资源),本文即简要地描述了修改当前线程的区域性并使用本地化资源展现。
首先对需要使用不同区域性设置的用户界面设置不同的语言资源:
选择窗体,在属性栏中修改其Language属性,此时用户界面的Localizable属性也已经被调整为true;
然后编辑该区域性语言的本地化资源,即各个控件的可本地化的所有属性都可以,并保存;
private void btnApply_Click(object sender,EventArgs e)
{
if(cmbLanguage.SelectedIndex < 0)
{
return;
}
string sCultureName = cmbLanguage.SelectedItem.ToString();
// 更改当前线程的 CultureInfo
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(sCultureName);
//对当前窗体应用更改后的资源
ApplyResource();
}
然后增加一个修改区域性的功能:
其实质就是设置当期线程的当前区域性,即Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(sCultureName);
但是我们会发现这样一个缺陷:通常我们会使用到多线程处理多个事务,于是修改当前线程的当前区域性对其他线程无效,即使是主线程的区域性被设置,子线程依然不会应用其区域性设置,于是需要给每个新建的子线程设置区域性。
而将其设置为主线程的区域性即可解决这样一个问题:t.CurrentUICulture = Thread.CurrentThread.CurrentUICulture;
private void btnFromLanguage_Click(object sender,EventArgs e)
{
Thread t = new Thread(new ThreadStart(StartForm2));
// 使用多线程时,由于各个线程的区域性不一样,所以要引用当前主线程的区域性
t.CurrentUICulture = Thread.CurrentThread.CurrentUICulture;
t.Start();
}
private void StartForm2()
{
new TestForm().ShowDialog();
}
设置线程的区域性之后只会给未启动的线程(Start之前)在启动之后,使用区域性资源初始化用户界面,而对于已经初始化的用户界面,则要读取本地化资源刷新当前设置,这里使用到资源管理器,并用其提供的ApplyResources方法来给指定的对象(其value参数)赋予给定资源名称(其objectName参数)对应的资源内容,为了应用于其包含的所有子控件,还应遍历其包含的所有子控件并同样对其使用ApplyResources方法:
/// <summary>
/// 应用资源
/// </summary>
private void ApplyResource()
{
ComponentResourceManager res = new ComponentResourceManager(typeof(Jokes.Language.LanguageTest));
foreach(Control ctl in Controls)
{
ApplyResource(ctl,res);
}
res.ApplyResources(this,"$this");
}
/// <summary>
/// 使用由指定资源管理器管理的资源给控件及其子控件绑定资源
/// </summary>
/// <param name="control">
/// 将绑定资源的控件(及其子控件)
/// </param>
/// <param name="resourceManager">
/// 管理资源的资源管理器对象
/// </param>
private void ApplyResource(Control control,ComponentResourceManager resourceManager)
{
if(null == control || null == resourceManager)
{
return;
}
foreach(Control ctl in control.Controls)
{
if(ctl.Controls != null && ctl.Controls.Count > 0)
{
// 递归的效率通常在递归层次超过5-6层时明显较低,不建议使用
ApplyResource(ctl,resourceManager);
}
resourceManager.ApplyResources(ctl,ctl.Name);
}
resourceManager.ApplyResources(control,control.Name);
}
到此基本上就已经实现了,但是还有一些特殊的,比如已经打开的多文档窗体或多个非模式化窗体,这种情况下,那还得再对每一个子窗体或相关对象也重新刷新一次其区域性资源,否则只能关闭之,并再次启动。如果是用其他线程创建的对象,还要考虑线程间访问等问题,故比较折中的方法是在线程启动之前设置其区域性。