将TypeConverter直接绑定到枚举

问题描述:

我有以下情形:我有一个枚举,并且想要将它显示在DataGridViewTextBoxColumn的DataGridView(数据绑定)中。将TypeConverter直接绑定到枚举

这里是我的枚举:

//[TypeConverter(typeof(EnumStringConverter))] 
    public enum YesNoNA 
    { 
     [EnumDescription("Yes")] 
     Yes, 
     [EnumDescription("No")] 
     No, 
     [EnumDescription("N/A")] 
     NA 
    } 

这里是一个使用它的简单的属性:

[TypeConverter(typeof(EnumStringConverter))] 
    public YesNoNA HighLimitWithinBounds { get; protected set; } 

在我上面的情况,类型转换器工作得很好。它为我做了转换。

但是,这比我的理想解决方案更复杂。如果我把这个类型转换器放在Enum本身(取消上面的代码注释),并在属性上注释掉类型转换器,那么这个类型转换器就不再被调用了!

我已经在其他类上做了这个约定,它工作得很好。

为什么把一个typeconverter直接放在一个枚举上不工作?

供参考,这是我的TypeConverter:

public class EnumStringConverter : TypeConverter 
    { 
     public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, Object value, Type destinationType) 
     { 
     if (value != null && destinationType == typeof(string)) 
     { 
      return "Edited to protect the innocent!"; 
     } 
     return TypeDescriptor.GetConverter(typeof(Enum)).ConvertTo(context, culture, value, destinationType); 
     } 
     public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) 
     { 
     if (destinationType == typeof(string)) 
     { 
      return true; 
     } 
     return base.CanConvertTo(context, destinationType); 
     } 
    }; 
+1

也许你应该试试这个方法:http://en.wikipedia.org/wiki/Rubber_duck_debugging – espais 2013-02-22 17:46:44

+0

可能的重复[如何在C#枚举中重写ToString?](http://*.com/questions/796607 /如何覆盖-tostring -in -c-sharp-enums) – 2013-02-22 17:48:31

+0

Defaintely不相关。上面已经实现了该线程中解决方案的答案。这个问题正在更进一步。 – greggorob64 2013-02-22 18:12:55

我不知道你是什么意思“这是比我理想的解决方案更加复杂”。我有一种做法与您的做法不同,但它可能不会那么复杂。我会说我的方式涉及更多的开销,但是在您的应用程序中越来越多地使用它。它使您的应用程序准备好可本地化,并且意味着您不必为每个枚举值添加属性。

1)使一个资源管理器缓存

这部分是可选的;但是如果您像我一样多次使用多个资源文件,则可以通过减少完成多少反射来提高性能。

using System; 
using System.Collections.Generic; 
using System.Reflection; 
using System.Resources; 

namespace AppResourceLib.Public.Reflection 
{ 
    internal static class ResourceManagerCache 
    { 
    private static Dictionary<Type, ResourceManager> _resourceManagerMap = 
     new Dictionary<Type, ResourceManager>(); 

    public static ResourceManager GetResourceManager(Type resourceType) 
    { 
     ResourceManager resourceManager = null; 

     // Make sure the type is valid. 
     if (null != resourceType) 
     { 
     // Try getting the cached resource manager. 
     if (!ResourceManagerCache._resourceManagerMap.TryGetValue(resourceType, out resourceManager)) 
     { 
      // If it is not in the cache create it. 
      resourceManager = resourceType.InvokeMember(
      "ResourceManager", 
      (BindingFlags.GetProperty | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic), 
      null,             
      null,             
      null) as ResourceManager; 

      // If it was created, add the resource manager to the cache. 
      if (null != resourceManager) 
      { 
      ResourceManagerCache._resourceManagerMap.Add(resourceType, resourceManager); 
      } 
     } 
     } 

     return resourceManager; 
    } 
    } 
} 

2)创建本地化描述属性

这是你将适用于枚举类型的属性。关于这一点很酷的是你不必为每个枚举添加一个属性,只需要在枚举类型声明之上添加一个属性即可。

using System; 
using System.ComponentModel; 

namespace AppResourceLib.Public.Reflection 
{ 
    /// <summary> 
    /// A resource type attribute that can be applied to enumerations. 
    /// </summary> 
    [AttributeUsage(AttributeTargets.Enum)] 
    public sealed class LocalizedDescriptionAttribute : Attribute 
    { 
    /// <summary> 
    /// The type of resource associated with the enum type. 
    /// </summary> 
    private Type _resourceType; 

    public LocalizedDescriptionAttribute(Type resourceType) 
    { 
     this._resourceType = resourceType; 
    } 

    /// <summary> 
    /// The type of resource associated with the enum type. 
    /// </summary> 
    public Type ResourceType 
    { 
     get 
     { 
     return this._resourceType; 
     } 
    } 
    } 
} 

3)创建本地化说明转换

该功能可将枚举值到字符串你给它的字符串资源(的.resx)文件。

using System; 
using System.Globalization; 
using System.Linq; 
using System.Reflection; 
using System.Resources; 
using System.Windows.Data; 

namespace AppResourceLib.Public.Reflection 
{ 
    [ValueConversion(typeof(Object), typeof(String))] 
    public class LocalizedDescriptionConverter : IValueConverter 
    { 
    public Object Convert(Object value, Type targetType, Object param, CultureInfo cultureInfo) 
    { 
     String description = null; 

     if (null != value) 
     { 
     // If everything fails then at least return the value.ToString(). 
     description = value.ToString(); 

     // Get the LocalizedDescriptionAttribute of the object. 
     LocalizedDescriptionAttribute attribute = 
      value.GetType().GetCustomAttribute(typeof(LocalizedDescriptionAttribute)) 
      as LocalizedDescriptionAttribute; 

     // Make sure we found a LocalizedDescriptionAttribute. 
     if (null != attribute) 
     {   
      ResourceManager resourceManager = 
      ResourceManagerCache.GetResourceManager(attribute.ResourceType); 

      if (null != resourceManager) 
      { 
      // Use the ResourceManager to get the description you gave the object value. 
      // Here we just use the object value.ToString() (the name of the object) to get 
      // the string in the .resx file. The only constraint here is that you have to 
      // name your object description strings in the .resx file the same as your objects. 
      // The benefit is that you only have to declare the LocalizedDescriptionAttribute 
      // above the object type, not an attribute over every object. 
      // And this way is localizable. 
      description = resourceManager.GetString(value.ToString(), cultureInfo); 

      String formatString = (param as String); 

      // If a format string was passed in as a parameter, 
      // make a string out of that. 
      if (!String.IsNullOrEmpty(formatString)) 
      { 
       formatString = formatString.Replace("\\t", "\t"); 
       formatString = formatString.Replace("\\n", "\n"); 
       formatString = formatString.Replace("\\r", "\r"); 

       description = String.Format(formatString, value.ToString(), description);    
      }   
      } 
     } 
     } 

     return description;  
    } 

    public Object ConvertBack(Object value, Type targetType, Object param, CultureInfo cultureInfo) 
    { 
     throw new NotImplementedException(); 

     return null; 
     } 
    } 
} 

4)创建资源(的.resx)字符串文件

现在要创建将包含你想为你的枚举密钥值的风格描述一个资源文件。我的意思是,在字符串资源的“名称”列中,将放置单个枚举的确切名称,并在“值”列中放入转换该枚举时要获取的字符串。
例如,假设您有以下Enums。

public enum MyColors 
{ 
    Black, 
    Blue, 
    White 
} 

那么你的字符串资源文件应该是这样的......

名称|价值

黑色|深色
蓝色|凉爽的颜色
白色|色泽鲜艳

5)属性

创建枚举现在我们终于使枚举声明与LocalizedDescription。您传入LocalizedDescription属性的参数是字符串资源文件的类型。现在,当使用转换器时,它将看到枚举类型的属性,获取资源文件,查找与特定枚举值的字符串值匹配的键字符串,并从资源文件中返回值作为转换后的字符串。

using AppResourceLib.Public; 
using AppResourceLib.Public.Reflection; 

namespace MyEnums 
{ 
    [LocalizedDescription(typeof(MyColorStrings))] 
    public enum MyColors 
    { 
    Black, 
    Blue, 
    White 
    } 
} 

主要的缺点,这种方法是,如果在你的资源文件中的“名称”键匹配您的枚举值的名称,它只会工作。这是在资源文件中引用字符串值而不给每个枚举描述属性的唯一方法。那么你如何使用它来显示值呢?这里是一个例子...

在您的xaml代码中,您将希望使数据提供程序将枚举的值传递给您的UI元素(我在此处使用ComboBox ...)。然后,您将希望使转换器可用并为您的UI元素创建模板以使用enum转换器。所以在这里...

 <!-- Enum Colors --> 
    <ObjectDataProvider x:Key="MyColorEnums" 
         MethodName="GetValues" 
         ObjectType="{x:Type sys:Enum}"> 
    <ObjectDataProvider.MethodParameters> 
     <x:Type TypeName="MyColors"/> 
    </ObjectDataProvider.MethodParameters> 
    </ObjectDataProvider> 


    <!-- Enum Type Converter --> 
    <LocalizedDescriptionConverter x:Key="EnumConverter"/> 


    <!-- Dropdown Expand ComboBox Template --> 
    <DataTemplate x:Key="MyColorsComboBoxTemplate"> 
    <Label Content="{Binding Path=., Mode=OneWay, 
     Converter={StaticResource EnumConverter}}" 
      Height="Auto" Margin="0" VerticalAlignment="Center"/> 
    </DataTemplate> 

    <!-- And finally the ComboBox that will display all of your enum values 
    but will use the strings from the resource file instead of enum.ToString() --> 
    <ComboBox Width="80" HorizontalAlignment="Left" 
    ItemTemplate="{StaticResource MyColorsComboBoxTemplate}" 
    ItemsSource="{Binding Source={StaticResource MyColorEnums}}"> 

哇,对不起,这是如此之久。我不确定这对你来说太复杂了,但这是另一种选择。希望能帮助到你!

+1

我的例子与你非常相似(你更进一步使用资源文件,我在其他地方也使用它来获得多语言支持)。 复杂我提到的区别是,我的例子需要一个类型转换器*在属性*而不是在枚举本身。 不幸的是,我不认为我们的例子会匹配。你正在使用xaml,并使用纯净的.net winforms即时通讯。你在拿你的组合框。你的xaml专门将组合框绑定到你需要的转换器上,这正是我想要避免的(我不认为我会成功)。 感谢您的回复! – greggorob64 2013-03-04 19:27:38

+0

哦,我明白你的意思了。对不起,我忍不住。 – akagixxer 2013-03-05 03:55:40