分享两种实现Winform程序的多语言支持的多种解决方案
作者:梦在旅途 发布时间:2023-10-17 21:24:17
因公司业务需要,需要将原有的ERP系统加上支持繁体语言,但不能改变原有的编码方式,即:普通程序员感受不到编码有什么不同。经过我与几个同事的多番沟通,确定了以下两种方案:
方案一:在窗体基类中每次加载并显示窗体时,会自动递归遍历含文本显示的控件(Button,CheckBox,GroupBox,Label,LinkLabel,TextBox,StatusStrip,TabPage,ToolStrip,RadioButton,DateTimePicker,DataGridView,CheckedListBox,TreeView,MenuStrip),并根据不同的控件类型的文本属性调用简繁转换方法进行转换并重新设置新的相应文本属性的内容(比如:繁体内容)
优点:编码简单,对普通程序员的编码无影响(除窗体类的基类由Form类变成MyStyleFormBase类);
缺点:因每次打开窗体都需要遍历控件并进行简繁转换,如果界面上的控件较多,则可能导致打开窗体较慢,影响用户体验,且子控件的文本内容改变时需程序员手动通知,无法自动感知并转换。
具体实现思路如下:
一.对Form类进行二次封装(继承),定义一个MyStyleFormBase类,并在里面加入每次加载并显示窗体类型时,会自动递归遍历含文本显示的控件,并根据不同的控件类型的文本属性调用简繁转换方法进行转换并重新设置新的相应文本属性的内容,这样当所有的窗体都继承MyStyleFormBase类时,均默认就实现了遍历与转换的过程,程序员无需再次编码,甚至都无需知道存在遍历与转换的过程,从而提高了代码的复用性,具体代码如下:
public class MyStyleFormBase : Form
{
public MyStyleFormBase()
{
if (!Thread.CurrentThread.CurrentUICulture.Name.Equals("zh-CHS", StringComparison.OrdinalIgnoreCase)) //如果是简体,则无需转换
{
base.TextChanged += MyStyleFormBase_TextChanged;
base.Shown += MyStyleFormBase_Shown;
}
}
private void MyStyleFormBase_TextChanged(object sender, EventArgs e)
{
this.Text = LanguageHelper.GetLanguageText(this.Text);
}
private void MyStyleFormBase_Shown(object sender, EventArgs e)
{
LanguageHelper.SetControlLanguageText(this);
base.ControlAdded += MyStyleFormBase_ControlAdded;
}
private void MyStyleFormBase_ControlAdded(object sender, ControlEventArgs e)
{
LanguageHelper.SetControlLanguageText(e.Control);
}
/// <summary>
/// 强制通知子控件改变消息
/// </summary>
/// <param name="target"></param>
protected virtual void PerformChildrenChange(Control target)
{
LanguageHelper.SetControlLanguageText(target);
}
/// <summary>
/// 弹出消息框
/// </summary>
/// <param name="text"></param>
/// <param name="caption"></param>
/// <param name="buttons"></param>
/// <param name="icon"></param>
/// <param name="defaultButton"></param>
/// <returns></returns>
protected DialogResult MessageBoxShow(string text, string caption, MessageBoxButtons buttons = MessageBoxButtons.OK, MessageBoxIcon icon = MessageBoxIcon.None, MessageBoxDefaultButton defaultButton = MessageBoxDefaultButton.Button1)
{
return MessageBox.Show(LanguageHelper.GetLanguageText(text), LanguageHelper.GetLanguageText(caption), buttons, icon, defaultButton);
}
}
代码逻辑简要说明:
1.当当前UI的文化区域不为中文简体时(因为本程序本身都是基于简体开发的),就订阅窗体显示事件Shown及窗体标题改变事件TextChanged,作用:当窗体显示时,则会遍历控件并转换为繁体,当标题的文本改变时,也会自动转换为繁体;
2.当窗体显示后订阅窗体的控件增加事件ControlAdded,作用:当窗体显示后,若后续存在代码增加控件时,会自动将控件及其子控件进行繁体的转换,保证一个都不漏;
3.增加一个消息提示框方法,目的是弹出消息窗口前能够将简体文本转换成繁体文本;
4.增加一个强制通知子控件改变消息的方法PerformChildrenChange,当某个控件的文本内容或增加子控件发生时,由于窗体本身无法捕获到,故需要调用该方法来遍历与转换子控件的文本内容;(感觉这里不太好,但目前没有更好的办法,如果大家有更好的办法,欢迎留言评论)
二、LanguageHelper:语方转换公共类(目前仅支持简繁转换,依赖于:ChineseConverter.dll)代码如下:
由于代码过长,请点击下载
该类逻辑很简单,就是从一个父控件开始,遍历所有的子控件,并根据不同的控件类型将控件相应的文本内容转换成简体或繁体,调用方法:SetControlLanguageText
以上二步就实现了多语言的支持了(准确的说是简繁转换),应用到项目中很简单,只需将窗体默认的基类Form改成:MyStyleFormBase即可,如:public partial class FormTest : MyStyleFormBase
方案二:由控件依据当前区域信息+缓存语言字典直接实现各控件自行转换
优点:无需遍历,各控件自行根据区域信息自支转换,因此效率较高,对普通程序员的编码无影响(除窗体类的基类由Form类变成MyStyleFormBase类外,还需要使用支持多语言的控件,这些控件均由普通控件二次封装得来,保留原有的所有属性及事件);
缺点:需将所有带文本显示的控件(如:Button,CheckBox,GroupBox,Label,LinkLabel,TextBox,StatusStrip,TabPage,ToolStrip,RadioButton,DateTimePicker,DataGridView,CheckedListBox,TreeView)均进行二次封装,控件统一命名为:MyStyleXXX
涉及的控件较多,编码相对复杂;
具体实现思路如下:
一.对Form类进行二次封装(继承),定义一个MyStyleFormBase类,里面加入对窗体标题进行修改时,能自动进行多语言转换功能,具体代码如下:
public partial class MyStyleFormBase : Form
{
public MyStyleFormBase()
{
base.TextChanged += MyStyleFormBase_TextChanged;
}
private void MyStyleFormBase_TextChanged(object sender, EventArgs e)
{
if (!Common.IsChsLanguage())
{ this.Text = LanguageHelper.GetLanguageText(this.Text); }
}
/// <summary>
/// 弹出消息框
/// </summary>
/// <param name="text"></param>
/// <param name="caption"></param>
/// <param name="buttons"></param>
/// <param name="icon"></param>
/// <param name="defaultButton"></param>
/// <returns></returns>
protected DialogResult MessageBoxShow(string text, string caption = "提示", MessageBoxButtons buttons = MessageBoxButtons.OK, MessageBoxIcon icon = MessageBoxIcon.None, MessageBoxDefaultButton defaultButton = MessageBoxDefaultButton.Button1)
{
if (!Common.IsChsLanguage())
{
text = LanguageHelper.GetLanguageText(text);
caption = LanguageHelper.GetLanguageText(caption);
}
return MessageBox.Show(text, caption, buttons, icon, defaultButton);
}
}
代码逻辑很简单,就是订阅一个Form.TextChanged事件,以便当根据IsChsLanguage(判断是否为简体模式)判断不是简体时,则需进行Form.Text转换
二.定义多语言支持普通控件及容器控件接口(IMultiLanguageControl、IMultiLanguageContainerControl),具体代码如下:(此处仅是为了作一个规范,支持手动设置转换控件的文本内容)
/// <summary>
/// 支持多语言普通控件(无子控件)
/// </summary>
public interface IMultiLanguageControl
{
string DefaultLangText { get; }
string CurrentLangText { get; set; }
}
/// <summary>
/// 支持多语言容器控件(包含子控件)
/// </summary>
public interface IMultiLanguageContainerControl
{
Dictionary<object, string> DefaultLangTexts { get; }
Dictionary<object, string> CurrentLangTexts { get; set; }
Control this[string ctrlName] { get; set; }
void SetItemCurrentLangText(string ctrlName, string langText);
event EventHandler<ChildrenAddedEventArgs> ChildrenChanged;
}
public class ChildrenAddedEventArgs : EventArgs
{
public Dictionary<object, string> LangTexts { get; private set; }
public ChildrenAddedEventArgs()
{
LangTexts = new Dictionary<object, string>();
}
public ChildrenAddedEventArgs(Dictionary<object, string> langTexts)
{ this.LangTexts = langTexts; }
public string this[object key]
{get
{
return LangTexts[key];
}
set
{
LangTexts[key] = value;
} }}
三、实现支持多语言普通控件:基于原有标准控件(Button,CheckBox,GroupBox,Label,LinkLabel,TextBox,RadioButton,DateTimePicker)进行二次封装,实现IMultiLanguageControl接口,各控件代码如下:
以下是MyStyleButton定义代码,MyStyleCheckBox、MyStyleGroupBox、MyStyleLabel、MyStyleLinkLabel、MyStyleTextBox、MyStyleRadioButton里面的实现代码均相同
public partial class MyStyleButton : MyButton, IMultiLanguageControl
{
static Dictionary<string, string> LanDict = new Dictionary<string, string>();
public MyStyleButton()
{ }
public override string Text
{
get
{
if (!DesignMode && System.Threading.Thread.CurrentThread.CurrentUICulture.Name != "zh-CHS")
{
if (LanDict.ContainsKey(DefaultLangText))
{
return CurrentLangText;
}
else
{
string langText = LanguageHelper.GetLanguageText(base.Text);
LanDict[base.Text] = langText;
return langText;
}
}
return base.Text;
}
set
{
base.Text = value;
}
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string DefaultLangText
{
get
{
return base.Text;
}
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string CurrentLangText
{
get
{
try
{
return LanDict[DefaultLangText];
}
catch (Exception)
{
return "";
}
}
set
{
if (System.Threading.Thread.CurrentThread.CurrentUICulture.Name != "zh-CHS")
{
if (LanDict.ContainsKey(DefaultLangText))
{
LanDict[DefaultLangText] = value;
}
else
{
LanDict.Add(DefaultLangText, value);
}
}
}
}
}
二次封装这些控件的目的是:1.暴露统一的属性,便于直接遍历并赋值(需手动改变文本内容语言的情况);2.当文本内容发生改变时,能够根据语言缓存字典,快速直接的自我简繁转换,无需再次遍历;
四、实现支持多语言容器控件:基于原有标准控件(StatusStrip,TabPage,ToolStrip,DataGridView,CheckedListBox,TreeView)进行二次封装,实现IMultiLanguageContainerControl接口,各控件代码如下:
MyStyleDataGridView:
由于代码过长,请点击下载
MyStyleTabControl:
由于代码过长,请点击下载
二次封装这些控件的目的是:1.暴露统一的属性,便于直接遍历并赋值(仅当直接改变文本内容语言时需要);2.当文本内容或子控件文本内容或新增子控件发生改变时,能够根据语言缓存字典,快速直接的自我简繁转换,无需再次遍历;
五.LanguageHelper:语方转换公共类,与方案一原理相同,但相对方案一要简单很多,代码如下:
由于代码过长,请点击下载
Common.IsChsLanguage方法定义如下:
public static bool IsChsLanguage()
{
return System.Threading.Thread.CurrentThread.CurrentUICulture.Name.Equals("zh-CHS", StringComparison.OrdinalIgnoreCase);
}
多语言支持的容器类控件的实现难点是:捕获子控件文本内容的改变,由于没有现成的事件或方法,故需要通过其它的途径来实现文本内容改时能够进行处理。
注:控件的实现代码都贴出来了,大家若需要的话,可以直接COPY走,另外为了系统安全,简繁体的系统截图我就不贴出来了,大家可以自行测试。
来源:http://www.cnblogs.com/zuowj/p/5878228.html


猜你喜欢
- 1、Hello, KotlinBugly 技术干货系列内容主要涉及移动开发方向,是由 Bugly 邀请腾讯内部各位技术大咖,通过日常工作经验
- Spring Data Elasticsearch提供了ElasticsearchTemplate工具类,实现了POJO与elasticse
- 前言作为一个写java的使用最多的轻量级框架莫过于spring,不管是老项目用到的springmvc,还是现在流行的springboot,都
- 1、概述 限流的含义是在单位时间内确保发往某个模块的请求数量小于某个数值,比如在实现秒杀功能时,需要确保在10秒内发往支付模块的请求数量小
- 使用RateLimiter通过AOP方式进行限流1、引入依赖<!-- guava 限流 --><dependency>
- 本文实例讲述了C#编程读取文档Doc、Docx及Pdf内容的方法。分享给大家供大家参考。具体分析如下:Doc文档:Microsoft Wor
- Vibrator振动器,是手机自带的振动器哦,不要想成岛国用的那种神秘东西哦~~Vibrator是Android给我们提供的用于机身震动的一
- 平均背景法的基本思想是计算每个像素的平均值和标准差作为它的背景模型。平均背景法使用四个OpenCV函数:cvAcc(),累积图像;cvAbs
- 在开发Android应用程序中,经常会自定义View来实现各种各样炫酷的效果,在实现这吊炸天效果的同时,我们往往会定义很多attr属性,这样
- 前言首先,啊,先简单介绍一下优先队列的概念,学数据结构以及出入算法竞赛的相信都对队列这一数据结构十分熟悉,这是一个线性的数据结构.针对队列这
- 预览效果图:需要权限:<uses-permission android:name="com.android.launcher
- 1.由于springboot集成了tomcat,所以打包的时候不再使用war,而是使用jar <groupId>cn&
- 1.JWT定义JWT(Json Web Token)是一种用于双方之间传递安全信息的简洁的、URL安全的表述性声明规范。JWT作为一个开放的
- 问题描述springcloud项目部署或调试时,占用的内存特别多。当部署到服务器上去后,有可能导致服务器内存占用过多而崩溃。解决方案&nbs
- 背景:在写登录界面时,老板就觉得在输入密码的时候谈出来的输入法软键盘把登录按钮遮挡住了(入下图所示,不爽),连输入框都被挡了一半
- 这篇文章主要介绍了Java流程控制顺序结构原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可
- 在构建RESTful数据服务过程中,我们定义了controller、repositories,并用一些注解修饰它们,但是到现在为止我们还没执
- “反射”其实就是利用程序集的元数据信息。 反射可以有很多方法,编写程序时请先导入 System.Reflection 命名空间。1、假设你要
- 最近在开发项目的时候遇到一个问题,在myecilpes上使用googlede gson读取项目中的json文件成功,然后把项目发布到tomc
- Java Boolean 初始化方式1、Boolean(String boolString);以字符串的方式初始化,只有当字符串是“true