public static class EnumHelper
{
public static string Description(this Enum value)
{
var attributes = value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes.Any())
return (attributes.First() as DescriptionAttribute).Description;
// If no description is found, the least we can do is replace underscores with spaces
// You can add your own custom default formatting logic here
TextInfo ti = CultureInfo.CurrentCulture.TextInfo;
return ti.ToTitleCase(ti.ToLower(value.ToString().Replace("_", " ")));
}
public static IEnumerable<ValueDescription> GetAllValuesAndDescriptions(Type t)
{
if (!t.IsEnum)
throw new ArgumentException($"{nameof(t)} must be an enum type");
return Enum.GetValues(t).Cast<Enum>().Select((e) => new ValueDescription() { Value = e, Description = e.Description() }).ToList();
}
}
public static IEnumerable<KeyValuePair<string, string>> GetAllValuesAndDescriptions<TEnum>() where TEnum : struct, IConvertible, IComparable, IFormattable
{
if (!typeof(TEnum).IsEnum)
{
throw new ArgumentException("TEnum must be an Enumeration type");
}
return from e in Enum.GetValues(typeof(TEnum)).Cast<Enum>()
select new KeyValuePair<string, string>(e.ToString(), e.Description());
}
public IEnumerable<KeyValuePair<string, string>> PlayerClassList
{
get
{
return EnumHelper.GetAllValuesAndDescriptions<PlayerClass>();
}
}
public class EffectStyleLookup
{
public EffectStyle Item { get; set; }
public string Display { get; set; }
}
在ViewModel中,转换枚举列表并将其作为属性公开:
public ViewModel : ReactiveObject
{
private ReactiveList<EffectStyleLookup> _effectStyles;
public ReactiveList<EffectStyleLookup> EffectStyles
{
get { return _effectStyles; }
set { this.RaiseAndSetIfChanged(ref _effectStyles, value); }
}
// See below for more on this
private EffectStyle _selectedEffectStyle;
public EffectStyle SelectedEffectStyle
{
get { return _selectedEffectStyle; }
set { this.RaiseAndSetIfChanged(ref _selectedEffectStyle, value); }
}
public ViewModel()
{
// Convert a list of enums into a ReactiveList
var list = (IList<EffectStyle>)Enum.GetValues(typeof(EffectStyle))
.Select( x => new EffectStyleLookup() {
Item = x,
Display = x.ToString()
});
EffectStyles = new ReactiveList<EffectStyle>( list );
}
}
this.WhenActivated( d =>
{
d( this.OneWayBind(ViewModel, vm => vm.EffectStyles, v => v.EffectStyle.ItemsSource) );
d( this.Bind(ViewModel, vm => vm.SelectedEffectStyle, v => v.EffectStyle.SelectedValue) );
});
Dim tDict As New Dictionary(Of Integer, String)
Dim types = [Enum].GetValues(GetType(Helper.Enumerators.AllowedType))
For Each x As Helper.Enumerators.AllowedType In types
Dim z = x.ToString()
Dim y = CInt(x)
tDict.Add(y, z)
Next
cmbRole.ClearValue(ItemsControl.ItemsSourceProperty)
cmbRole.ItemsSource = tDict
16 回答
您可以通过在Window
Loaded
事件处理程序中放置以下代码从代码中执行此操作,例如:如果需要在XAML中绑定它,则需要使用
ObjectDataProvider
创建可用作绑定源的对象:请注意下一个代码:
指导如何映射您可以在MSDN上阅读的命名空间和程序集 .
我喜欢我绑定的所有对象都在我的_1486723中定义,所以我尽量避免在xaml中使用
<ObjectDataProvider>
.我的解决方案不使用View中定义的数据,也不使用代码隐藏 . 只有一个DataBinding,一个可重用的ValueConverter,一个获取任何Enum类型描述集合的方法,以及要绑定到ViewModel中的单个属性 .
当我想将
Enum
绑定到ComboBox
时,我想要显示的文本永远不会匹配Enum
的值,所以我使用[Description()]
属性为它提供我真正想要在_1486729中看到的文本 . 如果我在游戏中有一个字符类枚举,它看起来像这样:首先,我使用几种方法创建了辅助类来处理枚举 . 一种方法获取特定值的描述,另一种方法获取所有值及其对类型的描述 .
接下来,我们创建一个
ValueConverter
. 继承自MarkupExtension
使得在XAML中使用更容易,因此我们不必将其声明为资源 .我的
ViewModel
只需要一个属性,我的View
可以绑定到组合框的SelectedValue
和ItemsSource
:最后绑定
ComboBox
视图(使用ItemsSource
绑定中的ValueConverter
)...要实现此解决方案,您只需要复制我的
EnumHelper
类和EnumToCollectionConverter
类 . 他们将与任何枚举一起使用 . 此外,我没有在这里包含它,但ValueDescription
类只是一个带有2个公共对象属性的简单类,一个名为Value
,一个名为Description
. 您可以自己创建,也可以更改代码以使用Tuple<object, object>
或KeyValuePair<object, object>
我使用了另一种使用MarkupExtension的解决方案 .
使用ObjectDataProvider:
然后绑定到静态资源:
尼克的回答确实帮助了我,但我意识到它可以稍微调整一下,以避免额外的类,ValueDescription . 我记得框架中已经存在KeyValuePair类,因此可以使用它 .
代码只是略有变化:
最后是XAML:
我希望这对其他人有帮助 .
你需要在枚举中创建一个值数组,可以通过调用System.Enum.GetValues()来创建它,并将它传递给你想要项目的枚举的
Type
.如果为
ItemsSource
属性指定了此属性,则应使用所有枚举值填充它 . 您可能希望将SelectedItem
绑定到EffectStyle
(假设它是同一枚举的属性,并包含当前值) .上述所有帖子都错过了一个简单的伎俩 . 可以从SelectedValue的绑定中找出如何自动填充ItemsSource以使您的XAML标记正好 .
例如,在我的ViewModel中
ValidateRaiseAndSetIfChanged是我的INPC钩子 . 你的可能会有所不同 .
EnumComboBox的实现如下,但首先我需要一个小帮手来获取我的枚举字符串和值
和主类(注意我使用ReactiveUI通过WhenAny挂钩属性更改)
你还需要在Generic.XAML中正确设置样式,否则你的盒子不会呈现任何东西,你会拔掉头发 .
就是这样 . 这显然可以扩展到支持i18n,但会使帖子更长 .
你应该用这种方式扩展Rogers和Greg的答案枚举值转换器,如果您直接绑定到枚举对象模型属性 .
通用应用似乎有点不同;它没有全功能XAML的全部功能 . 对我有用的是:
我创建了一个枚举值列表作为枚举(未转换为字符串或整数)并将ComboBox ItemsSource绑定到
然后我可以将ComboBox ItemSelected绑定到我的公共属性,其类型是有问题的枚举
只是为了好玩,我掀起了一个小模板课来帮助解决这个问题并将其发布到MSDN Samples pages . 额外的位让我可以选择覆盖枚举的名称,让我隐藏一些枚举 . 我的代码看起来很像Nick's(上图),我希望我之前看到过 .
如果您绑定到ViewModel上的实际枚举属性,而不是枚举的int表示,则事情变得棘手 . 我发现有必要绑定到字符串表示,而不是所有上述示例中所期望的int值 .
您可以通过将简单文本框绑定到要在ViewModel上绑定到的属性来判断是否是这种情况 . 如果显示文本,则绑定到字符串 . 如果显示数字,则绑定到该值 . 注意我已经使用了两次Display,这通常是一个错误,但它是唯一的工作方式 .
格雷格
尼克的解决方案可以简化得更多,没什么特别的,你只需要一个转换器:
然后,您可以在任何希望显示组合框的地方使用它:
我喜欢tom.maruska's answer,但我需要支持我的模板在运行时可能遇到的任何枚举类型 . 为此,我必须使用绑定来指定标记扩展的类型 . 我能够在nicolay.anykienko的this answer工作,想出一个非常灵活的标记扩展,无论如何我都能想到它 . 它是这样消耗的:
上面引用的mashed up标记扩展的源代码:
这个问题有很多优秀的答案,我谦卑地提出我的问题 . 我发现我的更简单,更优雅 . 它只需要一个值转换器 .
鉴于枚举......
和一个 Value 转换器......
资源...
XAML声明......
查看型号......
结果组合框......
使用
ReactiveUI
,我've created the following alternate solution. It'不是一个优雅的一体化解决方案,但我认为至少它是可读的 .在我的例子中,将
enum
列表绑定到控件是一种罕见的情况,因此我不需要跨代码库扩展解决方案 . 但是,通过将EffectStyleLookup.Item
更改为Object
,可以使代码更通用 . 我用我的代码测试了它,不需要其他修改 . 这意味着一个助手类可以应用于任何enum
列表 . 虽然这会降低其可读性 - 但是它并没有很好的响应 .使用以下帮助程序类:
在ViewModel中,转换枚举列表并将其作为属性公开:
在
ComboBox
中,利用SelectedValuePath
属性绑定到原始enum
值:在View中,这允许我们将原始
enum
绑定到ViewModel中的SelectedEffectStyle
,但在ComboBox
中显示ToString()
:我正在添加我的评论(遗憾的是,在VB中,这个概念可以很容易地在心跳中复制到C#),因为我只需要引用它并且不喜欢任何答案,因为它们太复杂了 . 它不应该是这么困难 .
所以我想出了一个更简单的方法 . 将枚举器绑定到字典 . 将该字典绑定到Combobox .
我的组合框:
我的代码隐藏 . 希望,这有助于其他人 .
简单明了的解释:http://brianlagunas.com/a-better-way-to-data-bind-enums-in-wpf/
...
...