枚举的多语言显示
早上看到jhh0111昨天关于枚举的双语显示问题的帖子,中午想了想,其实这就是一个Globalization的问题,虽然jhh0111的方案可以工作,但是扩展性不强——当枚举数量很大时可用性很低;另外,如果绑定到其他控件(比如Grid控件)也会有一些问题。
这里给出一个通用的解决方案,虽然复杂了一些,但是可用性和扩张性均有大大提高。
显然这里自己去实现自定义格式化,即通过IFormatable、IFormatProvider、ICustomFormatter等接口已达到Globalization有点小题大作了,而另外一个很容易想到的点是通过DiaplayMember实现显示值得自定义(对于简单Binding,例如ComboBox、ListBox等只用重载ToString就可以了)。
既然有了方向,那就动手了。
首先,我们希望Binding整个枚举类型的每一个值,也就是说,我们需要把这个枚举的所有值变成一个数据源,为了实现这一点,我们可以使用Enum上的helper方法Enum.GetValues(Type)来返回一个对所有值得枚举,然后依次添加到IList对象或者IListSource接口即可。
https://files.cnblogs.com/winkingzhang/Demo_2008_03_25.rar
这里给出一个通用的解决方案,虽然复杂了一些,但是可用性和扩张性均有大大提高。
显然这里自己去实现自定义格式化,即通过IFormatable、IFormatProvider、ICustomFormatter等接口已达到Globalization有点小题大作了,而另外一个很容易想到的点是通过DiaplayMember实现显示值得自定义(对于简单Binding,例如ComboBox、ListBox等只用重载ToString就可以了)。
既然有了方向,那就动手了。
首先,我们希望Binding整个枚举类型的每一个值,也就是说,我们需要把这个枚举的所有值变成一个数据源,为了实现这一点,我们可以使用Enum上的helper方法Enum.GetValues(Type)来返回一个对所有值得枚举,然后依次添加到IList对象或者IListSource接口即可。
1if (!typeof(EnumType).IsEnum)
2{
3 throw new NotSupportedException("Can not support type: " + typeof(EnumType).FullName);
4 // It's better use resource version as below.
5 // throw new NotSupportedException(SR.GetString("TYPE_NOT_SUPPORT", typeof(EnumType).FullName));
6}
7
8// Use Enum helper enumerator list all enum values and add to current context.
9foreach (EnumType value in Enum.GetValues(typeof(EnumType)))
10{
11 //TODO: add each value to IList
12 base.Add(new EnumAdapter(value));
13}
然后,取到了值,由于我们希望自定义Binding显示,那么需要对枚举值进行封装,而在这个封装里面,我们可以实现多语言的支持。2{
3 throw new NotSupportedException("Can not support type: " + typeof(EnumType).FullName);
4 // It's better use resource version as below.
5 // throw new NotSupportedException(SR.GetString("TYPE_NOT_SUPPORT", typeof(EnumType).FullName));
6}
7
8// Use Enum helper enumerator list all enum values and add to current context.
9foreach (EnumType value in Enum.GetValues(typeof(EnumType)))
10{
11 //TODO: add each value to IList
12 base.Add(new EnumAdapter(value));
13}
1/// <summary>
2/// Enum value adapter, used to get values from each Cultures.
3/// </summary>
4public sealed class EnumAdapter
5{
6 /// <summary>
7 /// Storage the actual Enum value.
8 /// </summary>
9 private EnumType _value;
10
11 /// <summary>
12 /// Constructor an <see cref="EnumAdapter"/>.
13 /// </summary>
14 /// <param name="value">The enum value.</param>
15 /// <exception cref="">
16 ///
17 /// </exception>
18 public EnumAdapter(EnumType value)
19 {
20 if (!Enum.IsDefined(typeof(EnumType), value))
21 {
22 throw new ArgumentException(string.Format("{0} is not defined in {1}", value, typeof(EnumType).Name), "value");
23 // It's better use resource version as below.
24 // throw new ArgumentException(SR.GetString("ENUM_NOT_DEFINED_FMT_KEY", value, typeof(EnumType).Name), "value");
25 }
26 _value = value;
27 }
28
29 /// <summary>
30 /// Gets the actual enum value.
31 /// </summary>
32 public EnumType Value
33 {
34 get { return _value; }
35 }
36
37 /// <summary>
38 /// Gets the display value for enum value by search local resource with currrent UI culture
39 /// and special key which is concated from Enum type name and Enum value name.
40 /// </summary>
41 /// <remarks>
42 /// This would get correct display value by accessing location resource with current UI Culture.
43 /// </remarks>
44 public string DisplayValue
45 {
46 get { return SR.GetString(string.Format("{0}.{1}", typeof(EnumType).Name, _value.ToString())); }
47 }
48
49 //TODO: If you want more, please add below
50}
至此,整个功能的框架已经完成,下面我们来看看一些细节——如何对资源读取和管理的封装:2/// Enum value adapter, used to get values from each Cultures.
3/// </summary>
4public sealed class EnumAdapter
5{
6 /// <summary>
7 /// Storage the actual Enum value.
8 /// </summary>
9 private EnumType _value;
10
11 /// <summary>
12 /// Constructor an <see cref="EnumAdapter"/>.
13 /// </summary>
14 /// <param name="value">The enum value.</param>
15 /// <exception cref="">
16 ///
17 /// </exception>
18 public EnumAdapter(EnumType value)
19 {
20 if (!Enum.IsDefined(typeof(EnumType), value))
21 {
22 throw new ArgumentException(string.Format("{0} is not defined in {1}", value, typeof(EnumType).Name), "value");
23 // It's better use resource version as below.
24 // throw new ArgumentException(SR.GetString("ENUM_NOT_DEFINED_FMT_KEY", value, typeof(EnumType).Name), "value");
25 }
26 _value = value;
27 }
28
29 /// <summary>
30 /// Gets the actual enum value.
31 /// </summary>
32 public EnumType Value
33 {
34 get { return _value; }
35 }
36
37 /// <summary>
38 /// Gets the display value for enum value by search local resource with currrent UI culture
39 /// and special key which is concated from Enum type name and Enum value name.
40 /// </summary>
41 /// <remarks>
42 /// This would get correct display value by accessing location resource with current UI Culture.
43 /// </remarks>
44 public string DisplayValue
45 {
46 get { return SR.GetString(string.Format("{0}.{1}", typeof(EnumType).Name, _value.ToString())); }
47 }
48
49 //TODO: If you want more, please add below
50}
1/// <summary>
2/// Constructor a new <see cref="SR"/>.
3/// </summary>
4internal SR()
5{
6 //TODO: If you modified resource location, please update here
7 this.resources = new System.Resources.ResourceManager(
8 string.Concat(typeof(EnumAdapter).Namespace, ".Resource"),
9 base.GetType().Assembly);
10}
11
12/// <summary>
13/// Get singleton instance.
14/// </summary>
15/// <returns>A singleton <see cref="SR"/></returns>
16private static SR GetLoader()
17{
18 if (loader == null)
19 {
20 lock (SR.InternalSyncObject)
21 {
22 if (loader == null)
23 {
24 loader = new SR();
25 }
26 }
27 }
28 return loader;
29}
30
31/// <summary>
32/// Gets an object from resources by special key, which provided by <paramref name="name"/>.
33/// </summary>
34/// <param name="name">Resource accessed key</param>
35/// <returns>return stored object in resource. if resource not found, return <paramref name="name"/> as object.</returns>
36public static object GetObject(string name)
37{
38 SR loader = GetLoader();
39 if (loader == null)
40 {
41 return null;
42 }
43 try
44 {
45 return loader.resources.GetObject(name, Culture);
46 }
47 catch { }
48 return name;
49}
50
51/// <summary>
52/// Gets a string from resources by special key, which provided by <paramref name="name"/>.
53/// </summary>
54/// <param name="name">Resource accessed key</param>
55/// <returns>return stored string in resource. If resource not found, retuen <paramref name="name"/> as result.</returns>
56public static string GetString(string name)
57{
58 SR loader = GetLoader();
59 if (loader == null)
60 {
61 return null;
62 }
63 try
64 {
65 return loader.resources.GetString(name, Culture);
66 }
67 catch { }
68 return name;
69}
70
71/// <summary>
72/// Gets a formatted string from resources by special key, which provided by <paramref name="name"/> and optional parameters.
73/// </summary>
74/// <param name="name">Resource accessed key</param>
75/// <param name="args">format arguments.</param>
76/// <returns>return stored string in resource. If resource not found, use <paramref name="name"/> as formator, return the formatted string.</retur
77public static string GetString(string name, params object[] args)
78{
79 SR loader = GetLoader();
80 if (loader == null)
81 {
82 return null;
83 }
84 string format = name;
85 try
86 {
87 format = loader.resources.GetString(name, Culture);
88 }
89 catch { }
90
91 if ((args == null) || (args.Length <= 0))
92 {
93 return format;
94 }
95
96 // It's better cut long arg for formating.
97 for (int i = 0; i < args.Length; i++)
98 {
99 string arg = args[i] as string;
100 if ((arg != null) && (arg.Length > 0x400))
101 {
102 args[i] = arg.Substring(0, 0x3fd) + "";
103 }
104 }
105 return string.Format(System.Globalization.CultureInfo.CurrentCulture, format, args);
106}
OK,大功告成,有了这么一个封装,在应用里就可以简单的这么几句够搞定。2/// Constructor a new <see cref="SR"/>.
3/// </summary>
4internal SR()
5{
6 //TODO: If you modified resource location, please update here
7 this.resources = new System.Resources.ResourceManager(
8 string.Concat(typeof(EnumAdapter).Namespace, ".Resource"),
9 base.GetType().Assembly);
10}
11
12/// <summary>
13/// Get singleton instance.
14/// </summary>
15/// <returns>A singleton <see cref="SR"/></returns>
16private static SR GetLoader()
17{
18 if (loader == null)
19 {
20 lock (SR.InternalSyncObject)
21 {
22 if (loader == null)
23 {
24 loader = new SR();
25 }
26 }
27 }
28 return loader;
29}
30
31/// <summary>
32/// Gets an object from resources by special key, which provided by <paramref name="name"/>.
33/// </summary>
34/// <param name="name">Resource accessed key</param>
35/// <returns>return stored object in resource. if resource not found, return <paramref name="name"/> as object.</returns>
36public static object GetObject(string name)
37{
38 SR loader = GetLoader();
39 if (loader == null)
40 {
41 return null;
42 }
43 try
44 {
45 return loader.resources.GetObject(name, Culture);
46 }
47 catch { }
48 return name;
49}
50
51/// <summary>
52/// Gets a string from resources by special key, which provided by <paramref name="name"/>.
53/// </summary>
54/// <param name="name">Resource accessed key</param>
55/// <returns>return stored string in resource. If resource not found, retuen <paramref name="name"/> as result.</returns>
56public static string GetString(string name)
57{
58 SR loader = GetLoader();
59 if (loader == null)
60 {
61 return null;
62 }
63 try
64 {
65 return loader.resources.GetString(name, Culture);
66 }
67 catch { }
68 return name;
69}
70
71/// <summary>
72/// Gets a formatted string from resources by special key, which provided by <paramref name="name"/> and optional parameters.
73/// </summary>
74/// <param name="name">Resource accessed key</param>
75/// <param name="args">format arguments.</param>
76/// <returns>return stored string in resource. If resource not found, use <paramref name="name"/> as formator, return the formatted string.</retur
77public static string GetString(string name, params object[] args)
78{
79 SR loader = GetLoader();
80 if (loader == null)
81 {
82 return null;
83 }
84 string format = name;
85 try
86 {
87 format = loader.resources.GetString(name, Culture);
88 }
89 catch { }
90
91 if ((args == null) || (args.Length <= 0))
92 {
93 return format;
94 }
95
96 // It's better cut long arg for formating.
97 for (int i = 0; i < args.Length; i++)
98 {
99 string arg = args[i] as string;
100 if ((arg != null) && (arg.Length > 0x400))
101 {
102 args[i] = arg.Substring(0, 0x3fd) + "";
103 }
104 }
105 return string.Format(System.Globalization.CultureInfo.CurrentCulture, format, args);
106}
1private void Form1_Load(object sender, EventArgs e)
2{
3 this.comboBox1.DataSource = new EnumDataSource<Sex>();
4 this.comboBox1.DisplayMember = "DisplayValue";
5 this.comboBox1.ValueMember = "Value";
6}
7
8public enum Sex
9{
10 Male,
11 Female
12}
你可以从这里下载本篇中的相关示例代码:2{
3 this.comboBox1.DataSource = new EnumDataSource<Sex>();
4 this.comboBox1.DisplayMember = "DisplayValue";
5 this.comboBox1.ValueMember = "Value";
6}
7
8public enum Sex
9{
10 Male,
11 Female
12}
https://files.cnblogs.com/winkingzhang/Demo_2008_03_25.rar
To be the apostrophe which changed “Impossible” into “I’m possible”
----------------------------------------------------
WinkingZhang's Blog (http://winkingzhang.cnblogs.com)
GCDN(http://gcdn.grapecity.com/cs)