- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我有以下情况:我有一个枚举,并想将其绑定(bind)在 DataGridViewTextBoxColumn 上的 DataGridView(数据绑定(bind))中显示。
这是我的枚举:
//[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; }
在我遇到的上述情况中,类型转换器工作得很好。它为我做转换。
但是,这比我理想的解决方案要复杂。如果我将类型转换器放在枚举本身上(取消注释上面的代码),并在属性上注释掉类型转换器,则不再调用类型转换器!
我已经在其他类上完成了这个约定,而且效果很好。
为什么将类型转换器直接放在枚举上不起作用?
作为引用,这是我的类型转换器:
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) 制作资源管理器缓存
这部分是可选的;但是,如果您像我一样多次使用多个资源文件,这可以通过减少完成的反射来提高性能。
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) 字符串文件
现在您要创建一个资源文件,其中将包含您想要的枚举键值样式的描述。我的意思是,在字符串资源的“名称”列中,您将放置各个枚举的确切名称,并在“值”列中放置您在转换该枚举时想要获得的字符串。
例如,假设您有以下枚举。
public enum MyColors
{
Black,
Blue,
White
}
那么你的字符串资源文件应该是这样的……
名字 |值(value)
黑色 |深色
蓝色 |很酷的颜色
白色 |明亮的颜色
5) 创建带属性的枚举
现在我们终于使用 LocalizedDescription 进行 Enum 声明。您传递给 LocalizedDescription 属性的参数是您的字符串资源文件的类型。现在,当使用转换器时,它将看到枚举类型的属性,获取资源文件,查找与特定枚举值的字符串值匹配的键字符串,并从资源文件中返回值作为转换后的字符串。
using AppResourceLib.Public;
using AppResourceLib.Public.Reflection;
namespace MyEnums
{
[LocalizedDescription(typeof(MyColorStrings))]
public enum MyColors
{
Black,
Blue,
White
}
}
这种方法的主要缺点是它只有在资源文件中的“名称”键与枚举值的名称相匹配时才有效。这是在不为每个枚举提供描述属性的情况下引用资源文件中的字符串值的唯一方法。那么如何使用它来显示值呢?这是一个例子...
在您的 xaml 代码中,您将希望创建一个数据提供程序以将枚举值获取到您的 UI 元素(我在这里使用 ComboBox...)。然后,您需要使转换器可用并为您的 UI 元素创建模板以使用枚举转换器。就这样吧……
<!-- 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}}">
哇,抱歉这么长。我不确定这对您来说是否太复杂,但这是另一种选择。希望对您有所帮助!
关于c# - 将 TypeConverter 直接绑定(bind)到枚举,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15029162/
我是一名优秀的程序员,十分优秀!