枚举的多语言显示

早上看到jhh0111昨天关于枚举的双语显示问题的帖子,中午想了想,其实这就是一个Globalization的问题,虽然jhh0111的方案可以工作,但是扩展性不强——当枚举数量很大时可用性很低;另外,如果绑定到其他控件(比如Grid控件)也会有一些问题。
这里给出一个通用的解决方案,虽然复杂了一些,但是可用性和扩张性均有大大提高。
显然这里自己去实现自定义格式化,即通过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显示,那么需要对枚举值进行封装,而在这个封装里面,我们可以实现多语言的支持。
 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}
至此,整个功能的框架已经完成,下面我们来看看一些细节——如何对资源读取和管理的封装:
  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(00x3fd+ "";
103        }

104    }

105    return string.Format(System.Globalization.CultureInfo.CurrentCulture, format, args);
106}
OK,大功告成,有了这么一个封装,在应用里就可以简单的这么几句够搞定。
 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}
你可以从这里下载本篇中的相关示例代码:
http://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 Community (http://gcdn.grapecity.com/cs)
posted @ 2008-03-25 14:52 winkingzhang 阅读(2054) 评论(16)  编辑 收藏 网摘

  回复  引用  查看    
#1楼2008-03-25 15:24 | 毁于随      
博主,能告诉我一下,你是如何给窗体加上的资源文件的吗?我看到窗体中的显示也是中文的.是Form1的资源文件,我用VS2005新建的窗体没有看到资源文件,不知道你是如何加进去的.
  回复  引用  查看    
#2楼2008-03-25 15:38 | 小智      
博主,我觉得用Attribute可能比较直观一点
  回复  引用  查看    
#3楼[楼主]2008-03-25 15:45 | winkingzhang      
--引用--------------------------------------------------
毁于随: 博主,能告诉我一下,你是如何给窗体加上的资源文件的吗?我看到窗体中的显示也是中文的.是Form1的资源文件,我用VS2005新建的窗体没有看到资源文件,不知道你是如何加进去的.
--------------------------------------------------------
你在Form的Designer下把Language改成对于的语言,然后编辑就可以了。

--引用--------------------------------------------------
小智: 博主,我觉得用Attribute可能比较直观一点
--------------------------------------------------------
这个是通用解决方案,Attribute虽然易用,但会影响扩展。

  回复  引用  查看    
#4楼2008-03-25 16:01 | 中华鹰      
不错,感谢博主的这个更好的解决方案。
另外,土土地问,用Attribute怎么实现呢?

  回复  引用  查看    
#5楼2008-03-25 16:02 | 毁于随      
博主的这篇文章真不错..Net2.0的特性我才刚刚认真的对待,使我对泛型,单件模式的应用中多线程的处理,资源文件的使用都有所了解了..
  回复  引用  查看    
#6楼2008-03-25 16:05 | 毁于随      
Form1.zh-CHS.resx,这个文件是手动加进去的,还是IDE自动生成的呢?
  回复  引用  查看    
#7楼[楼主]2008-03-25 16:12 | winkingzhang      
--引用--------------------------------------------------
毁于随: Form1.zh-CHS.resx,这个文件是手动加进去的,还是IDE自动生成的呢?
--------------------------------------------------------
这个是IDE自动生成的。

  回复  引用  查看    
#9楼2008-03-26 00:28 | 簡簡單單..      
--引用------------------------------------------------
使用 Attribute 怎么实现?
------------------------------------------------------

  回复  引用  查看    
#10楼2008-03-26 08:49 | 毁于随      
--引用--------------------------------------------------
winkingzhang: --引用--------------------------------------------------
毁于随: Form1.zh-CHS.resx,这个文件是手动加进去的,还是IDE自动生成的呢?
--------------------------------------------------------
这个是IDE自动生成的。
--------------------------------------------------------
麻烦博主再告诉我一下,这个是如何用IDE生成的呢?我没有发现.....

  回复  引用  查看    
#11楼[楼主]2008-03-26 09:35 | winkingzhang      
@毁于随

我录下了操作,参考:

http://www.cnblogs.com/Files/winkingzhang/Demo_Video.rar">Demo_Video.rar

  回复  引用  查看    
#12楼2008-03-26 12:28 | 风海迷沙      
还不是一般的复杂
  回复  引用  查看    
#14楼2008-03-26 16:53 | 毁于随      
@winkingzhang
非常感谢!

  回复  引用    
#15楼2008-03-27 10:08 | NetCase[未注册用户]
/// <summary>
/// 把Designer.cs中的声明提出来
/// </summary>
System.ComponentModel.ComponentResourceManager resources;

public Form1()
{
InitializeComponent();

comboBox1.DataSource = new EnumToLocalizeDataSource(resources).GetEunmToLocalize<sex>();
comboBox1.DisplayMember = "display";
comboBox1.ValueMember = "value";
}
}

/// <summary>
/// 获取枚举本地化数据源
/// </summary>
public class EnumToLocalizeDataSource
{
private ComponentResourceManager _sr;
public EnumToLocalizeDataSource(ComponentResourceManager sr)
{
_sr = sr;
}

public IList GetEunmToLocalize<T>()
{
if (!typeof(T).IsEnum)
return null;

ArrayList returnValue = new ArrayList();
foreach (string tmp in Enum.GetNames(typeof(T)))
{
returnValue.Add(new { display = _sr.GetString(tmp) ?? string.Empty, value = tmp });
}

return returnValue;
}
}

public enum sex
{
Male, Female
}

  回复  引用    
#16楼2008-03-27 10:10 | NetCase[未注册用户]
刚才的returnValue.Add(new { display = _sr.GetString(tmp) ?? string.Empty, value = tmp });
换成returnValue.Add(new { display = _sr.GetString(tmp) ?? tmp, value = tmp });
会更好一点




发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 1121315




相关文章:

相关链接: