分享两种实现Winform程序的多语言支持的解决方案
因公司业务需要,需要将原有的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)代码如下:
public class LanguageHelper
{
#region 简繁体转换
/// <summary>
/// 内容的语言转化
/// </summary>
/// <param name="parent"></param>
public static void SetControlLanguageText(System.Windows.Forms.Control parent)
{
if (parent.HasChildren)
{
foreach (System.Windows.Forms.Control ctrl in parent.Controls)
{
SetContainerLanguage(ctrl);
}
}
else
{
SetLanguage(parent);
}
}
#endregion
#region 控件简繁体语言转换 /// <summary>
/// 设置容器类控件的语言
/// </summary>
/// <param name="ctrl"></param>
/// <returns></returns>
private static void SetContainerLanguage(System.Windows.Forms.Control ctrl)
{
if (ctrl is DataGridView)
{
try
{
DataGridView dataGridView = (DataGridView)ctrl;
foreach (DataGridViewColumn dgvc in dataGridView.Columns)
{
try
{
if (dgvc.HeaderText.ToString() != "" && dgvc.Visible)
{
dgvc.HeaderText = GetLanguageText(dgvc.HeaderText);
}
}
catch
{
}
}
}
catch (Exception)
{ }
}
if (ctrl is MenuStrip)
{
MenuStrip menuStrip = (MenuStrip)ctrl;
foreach (ToolStripMenuItem toolItem in menuStrip.Items)
{
try
{
toolItem.Text = GetLanguageText(toolItem.Text);
}
catch (Exception)
{
}
finally
{
if (toolItem.DropDownItems.Count > 0)
{
GetItemText(toolItem);
}
}
}
}
else if (ctrl is TreeView)
{
TreeView treeView = (TreeView)ctrl;
foreach (TreeNode node in treeView.Nodes)
{
try
{
node.Text = GetLanguageText(node.Text);
}
catch (Exception)
{
}
finally
{
if (node.Nodes.Count > 0)
{
GetNodeText(node);
}
}
}
}
else if (ctrl is TabControl)
{
TabControl tabCtrl = (TabControl)ctrl;
try
{
foreach (TabPage tabPage in tabCtrl.TabPages)
{
tabPage.Text = GetLanguageText(tabPage.Text);
}
}
catch (Exception)
{
}
}
else if (ctrl is StatusStrip)
{
StatusStrip statusStrip = (StatusStrip)ctrl;
foreach (ToolStripItem toolItem in statusStrip.Items)
{
try
{
toolItem.Text = GetLanguageText(toolItem.Text);
}
catch (Exception)
{
}
finally
{
ToolStripDropDownButton tsDDBtn = toolItem as ToolStripDropDownButton;
if (tsDDBtn != null && tsDDBtn.DropDownItems.Count > 0)
{
GetItemText(tsDDBtn);
}
}
}
}
else if (ctrl is ToolStrip)
{
ToolStrip statusStrip = (ToolStrip)ctrl;
foreach (ToolStripItem toolItem in statusStrip.Items)
{
try
{
toolItem.Text = GetLanguageText(toolItem.Text);
}
catch (Exception)
{
}
}
}
else if (ctrl is CheckedListBox)
{
CheckedListBox chkListBox = (CheckedListBox)ctrl;
try
{
for (int n = 0; n < chkListBox.Items.Count; n++)
{
chkListBox.Items[n] = GetLanguageText(chkListBox.Items[n].ToString());
}
}
catch (Exception)
{ }
} if (ctrl.HasChildren)
{
foreach (System.Windows.Forms.Control c in ctrl.Controls)
{
SetContainerLanguage(c);
}
}
else
{
SetLanguage(ctrl);
} }
/// <summary>
/// 设置普通控件的语言
/// </summary>
/// <param name="ctrl"></param>
/// <returns></returns>
private static void SetLanguage(System.Windows.Forms.Control ctrl)
{
if (true)
{
if (ctrl is CheckBox)
{
CheckBox checkBox = (CheckBox)ctrl;
try
{
checkBox.Text = GetLanguageText(checkBox.Text);
}
catch (Exception)
{
}
}
else if (ctrl is Label)
{
Label label = (Label)ctrl;
try
{
label.Text = GetLanguageText(label.Text);
}
catch (Exception)
{
}
} else if (ctrl is Button)
{
Button button = (Button)ctrl;
try
{
button.Text = GetLanguageText(button.Text);
}
catch (Exception)
{
}
}
else if (ctrl is GroupBox)
{
GroupBox groupBox = (GroupBox)ctrl;
try
{
groupBox.Text = GetLanguageText(groupBox.Text);
}
catch (Exception)
{
}
}
else if (ctrl is RadioButton)
{
RadioButton radioButton = (RadioButton)ctrl;
try
{
radioButton.Text = GetLanguageText(radioButton.Text);
}
catch (Exception)
{
}
}
} }
/// <summary>
/// 递归转化菜单
/// </summary>
/// <param name="menuItem"></param>
private static void GetItemText(ToolStripDropDownItem menuItem)
{
foreach (ToolStripItem toolItem in menuItem.DropDownItems)
{
try
{
toolItem.Text = GetLanguageText(toolItem.Text);
}
catch (Exception)
{
}
finally
{
if (toolItem is ToolStripDropDownItem)
{
ToolStripDropDownItem subMenuStrip = (ToolStripDropDownItem)toolItem;
if (subMenuStrip.DropDownItems.Count > 0)
{
GetItemText(subMenuStrip);
}
}
} }
}
/// <summary>
/// 递归转化树
/// </summary>
/// <param name="menuItem"></param>
private static void GetNodeText(TreeNode node)
{ foreach (TreeNode treeNode in node.Nodes)
{
try
{
treeNode.Text = GetLanguageText(treeNode.Text);
}
catch (Exception)
{
}
finally
{
if (treeNode.Nodes.Count > 0)
{
GetNodeText(treeNode);
}
}
}
} /// <summary>
/// 根据语言标识符得到转换后的值
/// </summary>
/// <param name="languageFlag"></param>
/// <param name="value"></param>
/// <returns></returns>
public static string GetLanguageText(string value)
{
string languageFlag = Thread.CurrentThread.CurrentUICulture.Name;
if (string.IsNullOrWhiteSpace(value))
{
return value;
} switch (languageFlag.ToUpper())
{
case "ZH-CHT":
{
return ToTraditional(value);
}
default:
{
return ToSimplified(value);
}
}
} /// <summary>
/// 简体转换为繁体
/// </summary>
/// <param name="str">简体字</param>
/// <returns>繁体字</returns>
private static string ToTraditional(string str)
{
if (string.IsNullOrEmpty(str))
return str;
return ChineseConverter.Convert(str, ChineseConversionDirection.SimplifiedToTraditional); }
/// <summary>
/// 繁体转换为简体
/// </summary>
/// <param name="str">繁体字</param>
/// <returns>简体字</returns>
private static string ToSimplified(string str)
{
if (string.IsNullOrEmpty(str))
return str;
return ChineseConverter.Convert(str, ChineseConversionDirection.TraditionalToSimplified);
}
#endregion }
该类逻辑很简单,就是从一个父控件开始,遍历所有的子控件,并根据不同的控件类型将控件相应的文本内容转换成简体或繁体,调用方法: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:
public partial class MyStyleDataGridView : MyDataGridView, IMultiLanguageContainerControl
{
static Dictionary<string, string> LanDict = new Dictionary<string, string>();
Dictionary<object, string> cloumnHeaderTexts = new Dictionary<object, string>(); public MyStyleDataGridView()
{
cloumnHeaderTexts.Clear();
this.CellValueChanged += MyStyleDataGridView_CellValueChanged;
} private void MyStyleDataGridView_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex < 0)
{
if (System.Threading.Thread.CurrentThread.CurrentUICulture.Name != "zh-CHS")
{
var headerCell = this.Columns[e.ColumnIndex].HeaderCell as DataGridViewColumnHeaderCell; if (headerCell != null)
{
string headerCellValue = headerCell.Value.ToString();
if (LanDict.ContainsKey(headerCellValue))
{
headerCell.Value = LanDict[headerCellValue];
}
else
{
if (ChildrenChanged != null)
{
ChildrenAddedEventArgs args = new ChildrenAddedEventArgs();
args[headerCell] = headerCellValue;
ChildrenChanged(this, args);
LanDict[headerCellValue] = args[headerCell];
headerCell.Value = args[headerCell];
args = null;
}
else
{
string langText = LanguageHelper.GetLanguageText(headerCellValue);
LanDict[headerCellValue] = langText;
headerCell.Value = langText;
}
}
}
}
}
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Dictionary<object, string> DefaultLangTexts
{
get
{
return cloumnHeaderTexts;
}
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Dictionary<object, string> CurrentLangTexts
{
get
{
try
{
Dictionary<object, string> dict = new Dictionary<object, string>();
foreach (DataGridViewColumn item in this.Columns)
{
if (LanDict.ContainsKey(item.HeaderText))
{
dict.Add(item, LanDict[item.HeaderText]);
}
}
return dict;
}
catch (Exception)
{
return null;
}
}
set
{
if (System.Threading.Thread.CurrentThread.CurrentUICulture.Name != "zh-CHS")
{
foreach (var item in value)
{
if (LanDict.ContainsKey(cloumnHeaderTexts[item.Key]))
{
LanDict[cloumnHeaderTexts[item.Key]] = item.Value;
}
else
{
LanDict.Add(cloumnHeaderTexts[item.Key], item.Value);
} DataGridViewColumn dgvc = item.Key as DataGridViewColumn;
if (dgvc != null)
{
dgvc.HeaderText = item.Value;
}
}
}
}
} public Control this[string ctrlName]
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
} public void SetItemCurrentLangText(string ctrlName, string langText)
{
if (this.Columns[ctrlName] != null)
this.Columns[ctrlName].HeaderText = langText;
} protected override void OnColumnAdded(DataGridViewColumnEventArgs e)
{
base.OnColumnAdded(e);
if (!string.IsNullOrWhiteSpace(e.Column.HeaderText))
{
if (LanDict.ContainsKey(e.Column.HeaderText))
{
e.Column.HeaderText = LanDict[e.Column.HeaderText];
}
else
{
cloumnHeaderTexts.Add(e.Column, e.Column.HeaderText);
if (ChildrenChanged != null)
{
ChildrenAddedEventArgs args = new ChildrenAddedEventArgs();
args[e.Column.HeaderCell] = e.Column.HeaderText;
ChildrenChanged(this, args);
LanDict[e.Column.HeaderText] = args[e.Column.HeaderCell];
e.Column.HeaderText = args[e.Column.HeaderCell];
args = null;
}
else
{
string langText = LanguageHelper.GetLanguageText(e.Column.HeaderText);
LanDict[e.Column.HeaderText] = langText;
e.Column.HeaderText = langText;
}
}
}
} [Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public event EventHandler<ChildrenAddedEventArgs> ChildrenChanged;
}
MyStyleMenuStrip:(MyStyleToolStrip、MyStyleStatusStrip实现代码均相同)
public partial class MyStyleMenuStrip : MenuStrip, IMultiLanguageContainerControl
{
static Dictionary<string, string> LanDict = new Dictionary<string, string>();
Dictionary<object, string> toolStripItemTexts = new Dictionary<object, string>(); public MyStyleMenuStrip()
{
toolStripItemTexts.Clear(); this.Renderer.RenderItemText += (s, e) =>
{
if (e.Item is ToolStripSeparator ||
e.Item is ToolStripComboBox ||
e.Item is ToolStripTextBox)
{
return; }
if (e.ToolStrip == this || GetItemTopToolStrip(e.Item) == this)
{
e.Item.TextChanged += Item_TextChanged;
if (LanDict.ContainsValue(e.Item.Text))
{
return;
}
if (LanDict.ContainsKey(e.Item.Text))
{
e.Item.Text = LanDict[e.Item.Text];
}
else
{
SetItemLangText(e.Item);
}
}
};
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Dictionary<object, string> DefaultLangTexts
{
get
{
foreach (ToolStripItem item in Items)
{
GetItemText(item);
}
return toolStripItemTexts;
}
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Dictionary<object, string> CurrentLangTexts
{
get
{
try
{
Dictionary<object, string> dict = new Dictionary<object, string>();
foreach (ToolStripItem item in this.Items)
{
if (LanDict.ContainsKey(item.Text))
{
dict.Add(item, LanDict[item.Text]);
}
}
return dict;
}
catch (Exception)
{
return null;
}
}
set
{
if (System.Threading.Thread.CurrentThread.CurrentUICulture.Name != "zh-CHS")
{
foreach (var item in value)
{
if (LanDict.ContainsKey(toolStripItemTexts[item.Key]))
{
LanDict[toolStripItemTexts[item.Key]] = item.Value;
}
else
{
LanDict.Add(toolStripItemTexts[item.Key], item.Value);
} ToolStripItem tsi = item.Key as ToolStripItem;
if (tsi != null)
{
tsi.Text = item.Value;
}
}
}
}
} public Control this[string ctrlName]
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
} public void SetItemCurrentLangText(string ctrlName, string langText)
{
if (this.Items[ctrlName] != null)
this.Items[ctrlName].Text = langText;
}
protected override void OnItemAdded(ToolStripItemEventArgs e)
{
base.OnItemAdded(e);
if (e.Item.Text.Length == 0 || e.Item.Text == "还原(&R)" || e.Item.Text == "最小化(&N)" || e.Item.Text == "关闭(&C)")
{
return;
}
if (e.Item is ToolStripSeparator ||
e.Item is ToolStripComboBox ||
e.Item is ToolStripTextBox)
{
return;
}
e.Item.TextChanged += Item_TextChanged;
if (!string.IsNullOrWhiteSpace(e.Item.Text))
{
if (LanDict.ContainsKey(e.Item.Text))
{
e.Item.Text = LanDict[e.Item.Text];
}
else
{
toolStripItemTexts.Add(e.Item, e.Item.Text);
}
}
} private void Item_TextChanged(object sender, EventArgs e)
{
ToolStripItem Item = sender as ToolStripItem;
if (Item == null)
{
return;
}
if (string.IsNullOrWhiteSpace(Item.Text))
{
return;
}
if (LanDict.ContainsValue(Item.Text))
{
return;
}
if (LanDict.ContainsKey(Item.Text))
{
Item.Text = LanDict[Item.Text];
}
else
{
if (toolStripItemTexts.ContainsKey(Item))
{
toolStripItemTexts[Item] = Item.Text;
}
else
{
toolStripItemTexts.Add(Item, Item.Text);
}
SetItemLangText(Item);
}
} private void GetItemText(ToolStripItem item)
{
ToolStripMenuItem menuItem = item as ToolStripMenuItem;
if (menuItem == null)
{
return;
}
foreach (ToolStripItem tsmi in menuItem.DropDownItems)
{
if (tsmi is ToolStripSeparator ||
tsmi is ToolStripComboBox ||
tsmi is ToolStripTextBox)
{
continue;
}
tsmi.TextChanged += Item_TextChanged;
if (!string.IsNullOrWhiteSpace(tsmi.Text))
{
if (LanDict.ContainsKey(tsmi.Text))
{
tsmi.Text = LanDict[tsmi.Text];
}
else
{
toolStripItemTexts.Add(tsmi, tsmi.Text);
}
}
GetItemText(tsmi);
}
} private MyStyleMenuStrip GetItemTopToolStrip(ToolStripItem item)
{
if (item.OwnerItem == null)
{
return item.Owner as MyStyleMenuStrip;
}
else
{
return GetItemTopToolStrip(item.OwnerItem);
}
} [Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public event EventHandler<ChildrenAddedEventArgs> ChildrenChanged; private void SetItemLangText(ToolStripItem item)
{
if (ChildrenChanged != null)
{
ChildrenAddedEventArgs args = new ChildrenAddedEventArgs();
args[item] = item.Text;
ChildrenChanged(this, args);
LanDict[item.Text] = args[item];
item.Text = args[item];
args = null;
}
else
{
string langText = LanguageHelper.GetLanguageText(item.Text);
LanDict[item.Text] = langText;
item.Text = langText;
}
}
}
MyStyleTabControl:
public partial class MyStyleTabControl : MyTabControl, IMultiLanguageContainerControl
{
static Dictionary<string, string> LanDict = new Dictionary<string, string>();
Dictionary<object, string> tabPageTexts = new Dictionary<object, string>(); public MyStyleTabControl()
{
tabPageTexts.Clear();
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Dictionary<object, string> DefaultLangTexts
{
get
{
return tabPageTexts;
}
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Dictionary<object, string> CurrentLangTexts
{
get
{
try
{
Dictionary<object, string> dict = new Dictionary<object, string>();
foreach (TabPage item in this.TabPages)
{
if (LanDict.ContainsKey(item.Text))
{
dict.Add(item, LanDict[item.Text]);
}
}
return dict;
}
catch (Exception)
{
return null;
}
}
set
{
if (System.Threading.Thread.CurrentThread.CurrentUICulture.Name != "zh-CHS")
{
foreach (var item in value)
{
if (LanDict.ContainsKey(tabPageTexts[item.Key]))
{
LanDict[tabPageTexts[item.Key]] = item.Value;
}
else
{
LanDict.Add(tabPageTexts[item.Key], item.Value);
} TabPage tp = item.Key as TabPage;
if (tp != null)
{
tp.Text = item.Value;
}
}
}
}
} public Control this[string ctrlName]
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
} public void SetItemCurrentLangText(string ctrlName, string langText)
{
if (this.TabPages[ctrlName] != null)
this.TabPages[ctrlName].Text = langText;
} protected override void OnControlAdded(ControlEventArgs e)
{
base.OnControlAdded(e); TabPage page = null;
if ((page = e.Control as TabPage) != null)
{
page.TextChanged += page_TextChanged;
if (!string.IsNullOrWhiteSpace(page.Text))
{
if (LanDict.ContainsKey(page.Text))
{
page.Text = LanDict[page.Text];
}
else
{
tabPageTexts.Add(page, page.Text);
}
}
}
} void page_TextChanged(object sender, EventArgs e)
{
TabPage page = sender as TabPage;
if (page == null)
{
return;
}
if (string.IsNullOrWhiteSpace(page.Text))
{
return;
}
if (LanDict.ContainsValue(page.Text))
{
return;
}
if (LanDict.ContainsKey(page.Text))
{
page.Text = LanDict[page.Text];
}
else
{
if (tabPageTexts.ContainsKey(page))
{
tabPageTexts[page] = page.Text;
}
else
{
tabPageTexts.Add(page, page.Text);
}
if (ChildrenChanged != null)
{
ChildrenAddedEventArgs args = new ChildrenAddedEventArgs();
args[page] = page.Text;
ChildrenChanged(this, args);
LanDict[page.Text] = args[page];
page.Text = args[page];
args = null;
}
else
{
string langText = LanguageHelper.GetLanguageText(page.Text);
LanDict[page.Text] = langText;
page.Text = langText;
}
}
} [Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public event EventHandler<ChildrenAddedEventArgs> ChildrenChanged;
}
MyStyleTreeView:
public partial class MyStyleTreeView : MyTreeView, IMultiLanguageContainerControl
{
static Dictionary<string, string> LanDict = new Dictionary<string, string>();
Dictionary<object, string> nodeTexts = new Dictionary<object, string>(); public MyStyleTreeView()
{
nodeTexts.Clear();
this.DrawMode = TreeViewDrawMode.OwnerDrawText;
this.AfterExpand += MyStyleTreeView_AfterExpand;
} private void MyStyleTreeView_AfterExpand(object sender, TreeViewEventArgs e)
{
this.Refresh();
}
protected override void OnDrawNode(DrawTreeNodeEventArgs e)
{
IntPtr hdc = e.Graphics.GetHdc();
Graphics g = Graphics.FromHdc(hdc);
g.SmoothingMode = SmoothingMode.AntiAlias;
Font nodeFont = e.Node.NodeFont;
if (nodeFont == null) nodeFont = this.Font;
Color foreColor = Color.Black;
Color backColor = this.BackColor;
if (this.ContainsFocus && (e.Node.IsSelected || (e.State & TreeNodeStates.Focused) != 0))
{
foreColor = Color.White;
backColor = Color.FromArgb(51, 153, 255);
} string txt = e.Node.Text;
if (!string.IsNullOrWhiteSpace(txt) && !DesignMode)
{
if (LanDict.ContainsKey(e.Node.Text))
{
txt = LanDict[e.Node.Text];
}
else
{
if (ChildrenChanged != null)
{
ChildrenAddedEventArgs args = new ChildrenAddedEventArgs();
args[e.Node] = txt;
ChildrenChanged(this, args);
LanDict[txt] = args[e.Node];
txt = args[e.Node];
args = null;
}
else
{
string langText = LanguageHelper.GetLanguageText(txt);
LanDict[txt] = langText;
txt = langText;
}
}
}
SizeF txtSize = g.MeasureString(txt, Font);
Rectangle txtRect = new Rectangle(e.Bounds.X, e.Bounds.Y, (int)txtSize.Width + 2, e.Bounds.Height);
using (SolidBrush sb = new SolidBrush(backColor))
{
g.FillRectangle(sb, txtRect);
}
TextRenderer.DrawText(
g,
txt,
Font,
txtRect,
foreColor,
TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter); e.Graphics.ReleaseHdc(hdc);
g.Dispose();
} [Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Dictionary<object, string> DefaultLangTexts
{
get
{
nodeTexts.Clear();
foreach (TreeNode item in this.Nodes)
{
if (LanDict.ContainsKey(item.Text))
{
item.Text = LanDict[item.Text];
}
else
{
nodeTexts.Add(item, item.Text);
}
GetNodeText(item);
} return nodeTexts;
}
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Dictionary<object, string> CurrentLangTexts
{
get
{
try
{
Dictionary<object, string> dict = new Dictionary<object, string>();
Dictionary<object, string> defaultLangTexts = DefaultLangTexts;
foreach (var item in defaultLangTexts)
{
if (LanDict.ContainsKey(item.Value))
{
dict[item.Key] = LanDict[item.Value];
}
else
{
dict[item.Key] = "";
}
}
return dict;
}
catch (Exception)
{
return null;
}
}
set
{
if (System.Threading.Thread.CurrentThread.CurrentUICulture.Name != "zh-CHS")
{
foreach (var item in value)
{
if (LanDict.ContainsKey(nodeTexts[item.Key]))
{
LanDict[nodeTexts[item.Key]] = item.Value;
}
else
{
LanDict.Add(nodeTexts[item.Key], item.Value);
}
}
}
}
} public Control this[string ctrlName]
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
} public void SetItemCurrentLangText(string ctrlName, string langText)
{
if (this.Nodes[ctrlName] != null)
this.Nodes[ctrlName].Text = langText;
} private void GetNodeText(TreeNode node)
{
if (node.Nodes.Count == 0)
{
return;
}
foreach (TreeNode tn in node.Nodes)
{
if (LanDict.ContainsKey(tn.Text))
{
tn.Text = LanDict[tn.Text];
}
else
{
nodeTexts.Add(tn, tn.Text);
}
GetNodeText(tn);
}
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public event EventHandler<ChildrenAddedEventArgs> ChildrenChanged; }
二次封装这些控件的目的是:1.暴露统一的属性,便于直接遍历并赋值(仅当直接改变文本内容语言时需要);2.当文本内容或子控件文本内容或新增子控件发生改变时,能够根据语言缓存字典,快速直接的自我简繁转换,无需再次遍历;
五.LanguageHelper:语方转换公共类,与方案一原理相同,但相对方案一要简单很多,代码如下:
public class LanguageHelper
{
#region 控件内容简繁体转换 /// <summary>
/// 设置控件的语言
/// </summary>
/// <param name="parent"></param>
public static void SetControlLanguageText(System.Windows.Forms.Control ctrl)
{
if (ctrl.HasChildren)
{
foreach (Control item in ctrl.Controls)
{
if (item is IMultiLanguageControl)
{
IMultiLanguageControl il = item as IMultiLanguageControl;
string value = GetLanguageText(il.DefaultLangText);
il.CurrentLangText = value;
}
if (item is IMultiLanguageContainerControl)
{
IMultiLanguageContainerControl lic = item as IMultiLanguageContainerControl;
Dictionary<object, string> dic = new Dictionary<object, string>();
foreach (var v in lic.DefaultLangTexts)
{
dic.Add(v.Key, GetLanguageText(v.Value));
}
lic.CurrentLangTexts = dic;
lic.ChildrenChanged += (s, e) =>
{
List<object> keyList = new List<object>();
foreach (var key in e.LangTexts.Keys)
{
keyList.Add(key);
}
keyList.ForEach(x =>
{
e[x] = GetLanguageText(e[x]);
});
keyList = null;
};
} if (item.HasChildren)
{
SetControlLanguageText(item);
}
}
}
else
{
IMultiLanguageControl il = ctrl as IMultiLanguageControl;
string value = GetLanguageText(il.DefaultLangText);
il.CurrentLangText = value;
} } /// <summary>
/// 根据语言标识符得到转换后的值
/// </summary>
/// <param name="languageFlag"></param>
/// <param name="value"></param>
/// <returns></returns>
public static string GetLanguageText(string value)
{ string languageFlag = Thread.CurrentThread.CurrentUICulture.Name; if (value == null || value == string.Empty)
{
return value;
}
switch (languageFlag.ToUpper())
{
case "ZH-CHT":
{
return ToTraditional(value);
}
default:
{
return ToSimplified(value);
}
}
} /// <summary>
/// 简体转换为繁体
/// </summary>
/// <param name="str">简体字</param>
/// <returns>繁体字</returns>
private static string ToTraditional(string str)
{
if (string.IsNullOrEmpty(str))
return str;
return ChineseConverter.Convert(str, ChineseConversionDirection.SimplifiedToTraditional); }
/// <summary>
/// 繁体转换为简体
/// </summary>
/// <param name="str">繁体字</param>
/// <returns>简体字</returns>
private static string ToSimplified(string str)
{
if (string.IsNullOrEmpty(str))
return str;
return ChineseConverter.Convert(str, ChineseConversionDirection.TraditionalToSimplified);
}
#endregion }
Common.IsChsLanguage方法定义如下:
public static bool IsChsLanguage()
{
return System.Threading.Thread.CurrentThread.CurrentUICulture.Name.Equals("zh-CHS", StringComparison.OrdinalIgnoreCase);
}
多语言支持的容器类控件的实现难点是:捕获子控件文本内容的改变,由于没有现成的事件或方法,故需要通过其它的途径来实现文本内容改时能够进行处理。
以上就是本文的全部内容,有人可能会说,为何不采用资源文件的形式,原因文章一开头就说明了,是在原有的系统上,且不能改变原有的编码风格,故才花了这么大的力气来实现这个简繁转换的功能,我公司经领导确认最终采用的方案二。文中若有不足,欢迎交流,谢谢!
注:控件的实现代码都贴出来了,大家若需要的话,可以直接COPY走,另外为了系统安全,简繁体的系统截图我就不贴出来了,大家可以自行测试。
分享两种实现Winform程序的多语言支持的解决方案的更多相关文章
- Androidstudio实现一个简易的加法器——分享两种方法实现(日常作业练习)
Androidstudio实现一个简易的加法器——分享两种方法实现(日常作业练习) ...
- 为TextView设置两种状态,程序中可以动态切换
经常会需要用文字的两种状态来表示当前系统的某两种状态.比如: 这里的第一个TextView和后两个TextView就表示了两种状态.我们可以在程序的动态的切换状态(而不是直接修改颜色) ...
- php 分享两种给图片加水印的方法
本文章向码农们介绍 php 给图片加水印的两种方法,感兴趣的码农可以参考一下本文章的源代码. 方法一:PHP最简单的加水印方法 <?php // http://www.manongjc.com ...
- 【iOS开发-72】设置状态栏的两种方式、程序生命周期以及更好地理解几大类(对象)之间的关系
(1)设置状态栏的2种方式 --第一种方式就是我们在控制器中设置,系统默认就是交给视图控制器去管理的,这样不同视图控制器能够自己定义不同的状态栏例如以下: -(BOOL)prefersStatusBa ...
- Scipy的stats模块包含了多种概率分布的随机变量,随机变量分为连续和离散两种。+忽略程序中警告信息+np.newaxis解释
- 两种最常用的破解centos7忘掉密码的解决方案
第一种方法:设置光盘为第一启动项 第一步:进入bios界面 虚拟机中:点击左上角的虚拟机-->电源-->打开电源时进入固件 第二步:使用左右键选择上方的boot 按住shift+ 加号 ...
- 本地化SilverLight应用程序(多语言支持)
原文 http://www.cnblogs.com/seaworm/archive/2010/11/30/1892325.html 利用资源文件(Resources File)使SilverLight ...
- java web学习总结(二十九) -------------------JavaBean的两种开发模式
SUN公司推出JSP技术后,同时也推荐了两种web应用程序的开发模式,一种是JSP+JavaBean模式,一种是Servlet+JSP+JavaBean模式. 一.JSP+JavaBean开发模式 1 ...
- iOS中的两种主要架构及其优缺点浅析
凡是程序的开发者,应该对程序的架构都不陌生.一个程序的架构的好坏对这个程序有着非常重要的作用.今天我们来看一下iOS开发中用要的两种主流的程序架构.这个过程中我们主要以例子的形式展开. 我们来看第一种 ...
随机推荐
- 关于 Chrome 浏览器中 onresize 事件的 Bug
我在写插件时用到了 onresize 事件,在反复地测试后发现该事件在 Chrome 及 Opera(内核基本与 Chrome 相同,以下统称 Chrome)浏览器打开时就会执行,这种情况也许不能算作 ...
- 理解Maven中的SNAPSHOT版本和正式版本
Maven中建立的依赖管理方式基本已成为Java语言依赖管理的事实标准,Maven的替代者Gradle也基本沿用了Maven的依赖管理机制.在Maven依赖管理中,唯一标识一个依赖项是由该依赖项的三个 ...
- iOS开发系列--打造自己的“美图秀秀”
--绘图与滤镜全面解析 概述 在iOS中可以很容易的开发出绚丽的界面效果,一方面得益于成功系统的设计,另一方面得益于它强大的开发框架.今天我们将围绕iOS中两大图形.图像绘图框架进行介绍:Quartz ...
- Linux中进行单文件内容的复制
文件内容复制的常规方法: 开辟一段空间,不断读取文件的内容并写入另一文件当中,这种方法好在安全,一般在类型允许的最大范围内是安全的,缺点就是复制内容的时间长 一次性复制文件的内容,这种方法必须首先获取 ...
- lua 学习笔记(1)
一.lua函数赋值与函数调用 在lua中函数名也是作为一种变量出现的,即函数和所有其他值一样都是匿名的,当要使用某个函数时,需要将该函数赋值给一个变量,这样在函数块的其他地方就可以通过 ...
- [原] KVM 虚拟化原理探究(6)— 块设备IO虚拟化
KVM 虚拟化原理探究(6)- 块设备IO虚拟化 标签(空格分隔): KVM [toc] 块设备IO虚拟化简介 上一篇文章讲到了网络IO虚拟化,作为另外一个重要的虚拟化资源,块设备IO的虚拟化也是同样 ...
- Ajax使用WCF实现小票pos机打印源码
通过ajax跨域方式调用WCF服务,实现小票pos机的打印,源码提供web方式,客户端方式测试,服务驻留右侧底部任务栏,可控制服务开启暂停,用户可自定义小票打印模板,配合零售录入. qq 22945 ...
- 【夯实PHP基础】UML序列图总结
原文地址 序列图主要用于展示对象之间交互的顺序. 序列图将交互关系表示为一个二维图.纵向是时间轴,时间沿竖线向下延伸.横向轴代表了在协作中各独立对象的类元角色.类元角色用生命线表示.当对象存在时,角色 ...
- hibernate的基本xml文件配置
需要导入基本的包hibernate下的bin下的required和同bin下optional里的c3p0包下的所有jar文件,当然要导入mysql的驱动包了.下面需要注意的是hibernate的版本就 ...
- 使用DeviceOne实现微信小程序功能
微信小程序即将推出,还没推出就火的不行了.基于微信这个巨大平台,小程序必然能有巨大成功.不过它并不能完全取代App,该开发App还得开发.如果我们自己想实现一个基于自己的APP包含类似微信的小程序功能 ...