关于枚举类型的多语言显示,其实就是Globalization的问题。解决方案当然不止一种,这里介绍一种可用性和扩展性的比较好的通用方法。
显然这里自己去实现自定义格式化,即通过IFormatable、IFormatProvider、ICustomFormatter等接口已达到Globalization有点小题大作了,而另外一个很容易想到的点是通过DiaplayMember实现显示值得自定义(对于简单Binding,例如ComboBox、ListBox等只用重载ToString就可以了)。
首先,我们希望Binding整个枚举类型的每一个值,也就是说,我们需要把这个枚举的所有值变成一个数据源,为了实现这一点,我们可以使用Enum上的helper方法Enum.GetValues(Type)来返回一个对所有值得枚举,然后依次添加到IList对象或者IListSource接口即可。
01 |
if (! typeof (EnumType).IsEnum) |
02 |
{ |
03 |
throw new NotSupportedException( "Can not support type: " + typeof (EnumType).FullName); |
04 |
// It's better use resource version as below. |
05 |
// throw new NotSupportedException(SR.GetString("TYPE_NOT_SUPPORT", typeof(EnumType).FullName)); |
06 |
} |
07 |
|
08 |
// Use Enum helper enumerator list all enum values and add to current context. |
09 |
foreach (EnumType value in Enum.GetValues( typeof (EnumType))) |
10 |
{ |
11 |
//TODO: add each value to IList |
12 |
base .Add( new EnumAdapter(value)); |
13 |
} |
然后,取到了值,由于我们希望自定义Binding显示,那么需要对枚举值进行封装,而在这个封装里面,我们可以实现多语言的支持。
01 |
/// <summary> |
02 |
/// Enum value adapter, used to get values from each Cultures. |
03 |
/// </summary> |
04 |
public sealed class EnumAdapter |
05 |
{ |
06 |
/**/ /// <summary> |
07 |
/// Storage the actual Enum value. |
08 |
/// </summary> |
09 |
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 lture |
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 |
} |
至此,整个功能的框架已经完成,下面我们来看看一些细节——如何对资源读取和管理的封装:
001 |
/// <summary> |
002 |
/// Constructor a new <see cref="SR"/>. |
003 |
/// </summary> |
004 |
internal SR() |
005 |
{ |
006 |
//TODO: If you modified resource location, please update here |
007 |
this .resources = new System.Resources.ResourceManager( |
008 |
string .Concat( typeof (EnumAdapter).Namespace, ".Resource" ), |
009 |
base .GetType().Assembly); |
010 |
} |
011 |
|
012 |
/**/ /// <summary> |
013 |
/// Get singleton instance. |
014 |
/// </summary> |
015 |
/// <returns>A singleton <see cref="SR"/></returns> |
016 |
private static SR GetLoader() |
017 |
{ |
018 |
if (loader == null ) |
019 |
{ |
020 |
lock (SR.InternalSyncObject) |
021 |
{ |
022 |
if (loader == null ) |
023 |
{ |
024 |
loader = new SR(); |
025 |
} |
026 |
} |
027 |
} |
028 |
return loader; |
029 |
} |
030 |
|
031 |
/**/ /// <summary> |
032 |
/// Gets an object from resources by special key, which provided by <paramref name="name"/>. |
033 |
/// </summary> |
034 |
/// <param name="name">Resource accessed key</param> |
035 |
/// <returns>return stored object in resource. if resource not found, return <paramref name="name"/> as object.</returns> |
036 |
public static object GetObject( string name) |
037 |
{ |
038 |
SR loader = GetLoader(); |
039 |
if (loader == null ) |
040 |
{ |
041 |
return null ; |
042 |
} |
043 |
try |
044 |
{ |
045 |
return loader.resources.GetObject(name, Culture); |
046 |
} |
047 |
catch { } |
048 |
return name; |
049 |
} |
050 |
|
051 |
/**/ /// <summary> |
052 |
/// Gets a string from resources by special key, which provided by <paramref name="name"/>. |
053 |
/// </summary> |
054 |
/// <param name="name">Resource accessed key</param> |
055 |
/// <returns>return stored string in resource. If resource not found, retuen <paramref name="name"/> as result.</returns> |
056 |
public static string GetString( string name) |
057 |
{ |
058 |
SR loader = GetLoader(); |
059 |
if (loader == null ) |
060 |
{ |
061 |
return null ; |
062 |
} |
063 |
try |
064 |
{ |
065 |
return loader.resources.GetString(name, Culture); |
066 |
} |
067 |
catch { } |
068 |
return name; |
069 |
} |
070 |
|
071 |
/**/ /// <summary> |
072 |
/// Gets a formatted string from resources by special key, which provided by <paramref name="name"/> and optional parameters. |
073 |
/// </summary> |
074 |
/// <param name="name">Resource accessed key</param> |
075 |
/// <param name="args">format arguments.</param> |
076 |
/// <returns>return stored string in resource. If resource not found, use <paramref name="name"/> as formator, return the formatted string.</retur |
077 |
public static string GetString( string name, params object [] args) |
078 |
{ |
079 |
SR loader = GetLoader(); |
080 |
if (loader == null ) |
081 |
{ |
082 |
return null ; |
083 |
} |
084 |
string format = name; |
085 |
try |
086 |
{ |
087 |
format = loader.resources.GetString(name, Culture); |
088 |
} |
089 |
catch { } |
090 |
|
091 |
if ((args == null ) || (args.Length <= 0)) |
092 |
{ |
093 |
return format; |
094 |
} |
095 |
|
096 |
// It's better cut long arg for formating. |
097 |
for ( int i = 0; i < args.Length; i++) |
098 |
{ |
099 |
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,大功告成,有了这么一个封装,在应用里就可以简单的这么几句够搞定。
01 |
private void Form1_Load( object sender, EventArgs e) |
02 |
{ |
03 |
this .comboBox1.DataSource = new EnumDataSource<Sex>(); |
04 |
this .comboBox1.DisplayMember = "DisplayValue" ; |
05 |
this .comboBox1.ValueMember = "Value" ; |
06 |
} |
07 |
|
08 |
public enum Sex |
09 |
{ |
10 |
Male, |
11 |
Female |
12 |
} |