我试图找到一个简单的例子,枚举显示为是。我所看到的所有示例都试图添加好看的显示字符串,但我不想要那种复杂性。
基本上,我有一个类,通过首先将DataContext设置为这个类,然后在xaml文件中指定绑定,保存我绑定的所有属性:
<ComboBox ItemsSource="{Binding Path=EffectStyle}"/>
但这不会在组合框中显示枚举值作为项。
我试图找到一个简单的例子,枚举显示为是。我所看到的所有示例都试图添加好看的显示字符串,但我不想要那种复杂性。
基本上,我有一个类,通过首先将DataContext设置为这个类,然后在xaml文件中指定绑定,保存我绑定的所有属性:
<ComboBox ItemsSource="{Binding Path=EffectStyle}"/>
但这不会在组合框中显示枚举值作为项。
当前回答
上面所有的帖子都忽略了一个简单的技巧。可以从SelectedValue的绑定中找到如何自动地填充ItemsSource,以便您的XAML标记是正确的。
<Controls:EnumComboBox SelectedValue="{Binding Fool}"/>
例如,在ViewModel中
public enum FoolEnum
{
AAA, BBB, CCC, DDD
};
FoolEnum _Fool;
public FoolEnum Fool
{
get { return _Fool; }
set { ValidateRaiseAndSetIfChanged(ref _Fool, value); }
}
ValidateRaiseAndSetIfChanged是我的INPC钩子。你的可能不同。
EnumComboBox的实现如下所示,但首先我需要一个小助手来获取我的枚举字符串和值
public static List<Tuple<object, string, int>> EnumToList(Type t)
{
return Enum
.GetValues(t)
.Cast<object>()
.Select(x=>Tuple.Create(x, x.ToString(), (int)x))
.ToList();
}
和主类(注意,我使用ReactiveUI通过WhenAny钩子属性更改)
using ReactiveUI;
using ReactiveUI.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Windows;
using System.Windows.Documents;
namespace My.Controls
{
public class EnumComboBox : System.Windows.Controls.ComboBox
{
static EnumComboBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(EnumComboBox), new FrameworkPropertyMetadata(typeof(EnumComboBox)));
}
protected override void OnInitialized( EventArgs e )
{
base.OnInitialized(e);
this.WhenAnyValue(p => p.SelectedValue)
.Where(p => p != null)
.Select(o => o.GetType())
.Where(t => t.IsEnum)
.DistinctUntilChanged()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(FillItems);
}
private void FillItems(Type enumType)
{
List<KeyValuePair<object, string>> values = new List<KeyValuePair<object,string>>();
foreach (var idx in EnumUtils.EnumToList(enumType))
{
values.Add(new KeyValuePair<object, string>(idx.Item1, idx.Item2));
}
this.ItemsSource = values.Select(o=>o.Key.ToString()).ToList();
UpdateLayout();
this.ItemsSource = values;
this.DisplayMemberPath = "Value";
this.SelectedValuePath = "Key";
}
}
}
您还需要在Generic中正确设置样式。XAML或者你的盒子不会渲染任何东西,你会抓狂的。
<Style TargetType="{x:Type local:EnumComboBox}" BasedOn="{StaticResource {x:Type ComboBox}}">
</Style>
就是这样。这显然可以扩展到支持i18n,但会使帖子更长。
其他回答
您需要在枚举中创建一个值的数组,可以通过调用system . enumn . getvalues()来创建,并将您想要的项的枚举的Type传递给它。
如果您为ItemsSource属性指定了这一点,那么它应该用枚举的所有值填充。您可能希望将SelectedItem绑定到EffectStyle(假设它是相同枚举的属性,并且包含当前值)。
public class EnumItemsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!value.GetType().IsEnum)
return false;
var enumName = value.GetType();
var obj = Enum.Parse(enumName, value.ToString());
return System.Convert.ToInt32(obj);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Enum.ToObject(targetType, System.Convert.ToInt32(value));
}
}
如果您直接绑定到枚举对象模型属性,您应该使用这种Enum值转换器扩展Rogers和Greg的回答。
我不建议这样做,但希望这能启发一个好的解决方案。
假设枚举是Foo。然后你可以这样做。
public class FooViewModel : ViewModel
{
private int _fooValue;
public int FooValue
{
get => _fooValue;
set
{
_fooValue = value;
OnPropertyChange();
OnPropertyChange(nameof(Foo));
OnPropertyChange(nameof(FooName));
}
}
public Foo Foo
{
get => (Foo)FooValue;
set
{
_fooValue = (int)value;
OnPropertyChange();
OnPropertyChange(nameof(FooValue));
OnPropertyChange(nameof(FooName));
}
}
public string FooName { get => Enum.GetName(typeof(Foo), Foo); }
public FooViewModel(Foo foo)
{
Foo = foo;
}
}
然后打开Window。Load方法,你可以将所有枚举加载到ObservableCollection<FooViewModel>中,你可以将它设置为组合框的DataContext。
我喜欢汤姆。maruska的答案,但我需要支持我的模板在运行时可能遇到的任何枚举类型。为此,我必须使用绑定来指定标记扩展的类型。我从尼古拉的回答中得到了答案。anykienko提出了一个非常灵活的标记扩展,它可以在我能想到的任何情况下工作。它是这样消耗的:
<ComboBox SelectedValue="{Binding MyEnumProperty}"
SelectedValuePath="Value"
ItemsSource="{local:EnumToObjectArray SourceEnum={Binding MyEnumProperty}}"
DisplayMemberPath="DisplayName" />
上面引用的混合标记扩展的源代码:
class EnumToObjectArray : MarkupExtension
{
public BindingBase SourceEnum { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
IProvideValueTarget target = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
DependencyObject targetObject;
DependencyProperty targetProperty;
if (target != null && target.TargetObject is DependencyObject && target.TargetProperty is DependencyProperty)
{
targetObject = (DependencyObject)target.TargetObject;
targetProperty = (DependencyProperty)target.TargetProperty;
}
else
{
return this;
}
BindingOperations.SetBinding(targetObject, EnumToObjectArray.SourceEnumBindingSinkProperty, SourceEnum);
var type = targetObject.GetValue(SourceEnumBindingSinkProperty).GetType();
if (type.BaseType != typeof(System.Enum)) return this;
return Enum.GetValues(type)
.Cast<Enum>()
.Select(e => new { Value=e, Name = e.ToString(), DisplayName = Description(e) });
}
private static DependencyProperty SourceEnumBindingSinkProperty = DependencyProperty.RegisterAttached("SourceEnumBindingSink", typeof(Enum)
, typeof(EnumToObjectArray), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits));
/// <summary>
/// Extension method which returns the string specified in the Description attribute, if any. Oherwise, name is returned.
/// </summary>
/// <param name="value">The enum value.</param>
/// <returns></returns>
public static string Description(Enum value)
{
var attrs = value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attrs.Any())
return (attrs.First() as DescriptionAttribute).Description;
//Fallback
return value.ToString().Replace("_", " ");
}
}
Nick的回答确实帮助了我,但我意识到它可以稍微调整一下,以避免额外的类ValueDescription。 我记得框架中已经存在一个KeyValuePair类,所以可以使用这个代替。
代码只发生了轻微的变化:
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>();
}
}
最后是XAML:
<ComboBox ItemSource="{Binding Path=PlayerClassList}"
DisplayMemberPath="Value"
SelectedValuePath="Key"
SelectedValue="{Binding Path=SelectedClass}" />
我希望这对其他人有所帮助。