我试图找到一个简单的例子,枚举显示为是。我所看到的所有示例都试图添加好看的显示字符串,但我不想要那种复杂性。
基本上,我有一个类,通过首先将DataContext设置为这个类,然后在xaml文件中指定绑定,保存我绑定的所有属性:
<ComboBox ItemsSource="{Binding Path=EffectStyle}"/>
但这不会在组合框中显示枚举值作为项。
我试图找到一个简单的例子,枚举显示为是。我所看到的所有示例都试图添加好看的显示字符串,但我不想要那种复杂性。
基本上,我有一个类,通过首先将DataContext设置为这个类,然后在xaml文件中指定绑定,保存我绑定的所有属性:
<ComboBox ItemsSource="{Binding Path=EffectStyle}"/>
但这不会在组合框中显示枚举值作为项。
当前回答
我喜欢在ViewModel中绑定定义的所有对象,所以我尽量避免在xaml中使用<ObjectDataProvider>。
我的解决方案不使用视图中定义的数据,也不使用隐藏代码。只有一个DataBinding,一个可重用的ValueConverter,一个用于获取任意Enum类型的描述集合的方法,以及ViewModel中要绑定的单个属性。
当我想将一个Enum绑定到一个组合框时,我想要显示的文本永远不匹配Enum的值,所以我使用[Description()]属性(from System.ComponentModel)给它我实际上想在组合框中看到的文本。如果我有一周中的一天,它看起来会像这样:
public enum DayOfWeek
{
// add an optional blank value for default/no selection
[Description("")]
NOT_SET = 0,
[Description("Sunday")]
SUNDAY,
[Description("Monday")]
MONDAY,
...
}
首先,我创建了一个helper类,其中包含几个处理枚举的方法。一个方法获取一个特定值的描述,另一个方法获取一个类型的所有值及其描述。
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();
}
}
接下来,我们创建一个ValueConverter。从MarkupExtension继承使它更容易在XAML中使用,因此我们不必将其声明为资源。
[ValueConversion(typeof(Enum), typeof(IEnumerable<ValueDescription>))]
public class EnumToCollectionConverter : MarkupExtension, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return EnumHelper.GetAllValuesAndDescriptions(value.GetType());
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
我的ViewModel只需要1个属性,我的视图可以绑定到组合框的SelectedValue和ItemsSource:
private DayOfWeek dayOfWeek;
public DayOfWeek SelectedDay
{
get { return dayOfWeek; }
set
{
if (dayOfWeek != value)
{
dayOfWeek = value;
OnPropertyChanged(nameof(SelectedDay));
}
}
}
最后绑定ComboBox视图(在ItemsSource绑定中使用ValueConverter)…
<ComboBox ItemsSource="{Binding Path=SelectedDay, Converter={x:EnumToCollectionConverter}, Mode=OneTime}"
SelectedValuePath="Value"
DisplayMemberPath="Description"
SelectedValue="{Binding Path=SelectedDay, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
要实现这个解决方案,你只需要复制我的EnumHelper类和EnumToCollectionConverter类。它们可以处理任何枚举。同样,我没有在这里包括它,但ValueDescription类只是一个简单的类,具有两个公共对象属性,一个叫Value,一个叫Description。您可以自己创建,或者您可以更改代码使用Tuple<object, object>或KeyValuePair<object, object>
对于那些想要查看ValueDescription类的人:
public class ValueDescription
{
public object Value {get; set};
public object Description {get; set};
}
其他回答
我喜欢汤姆。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("_", " ");
}
}
您需要在枚举中创建一个值的数组,可以通过调用system . enumn . getvalues()来创建,并将您想要的项的枚举的Type传递给它。
如果您为ItemsSource属性指定了这一点,那么它应该用枚举的所有值填充。您可能希望将SelectedItem绑定到EffectStyle(假设它是相同枚举的属性,并且包含当前值)。
它的工作原理非常简单。 xaml
<ComboBox ItemsSource="{Binding MyEnumArray}">
.cs
public Array MyEnumArray
{
get { return Enum.GetValues(typeof(MyEnum)); }
}
我只是保持简单。我在ViewModel中创建了一个枚举值的项目列表:
public enum InputsOutputsBoth
{
Inputs,
Outputs,
Both
}
private IList<InputsOutputsBoth> _ioTypes = new List<InputsOutputsBoth>()
{
InputsOutputsBoth.Both,
InputsOutputsBoth.Inputs,
InputsOutputsBoth.Outputs
};
public IEnumerable<InputsOutputsBoth> IoTypes
{
get { return _ioTypes; }
set { }
}
private InputsOutputsBoth _selectedIoType;
public InputsOutputsBoth SelectedIoType
{
get { return _selectedIoType; }
set
{
_selectedIoType = value;
OnPropertyChanged("SelectedIoType");
OnSelectionChanged();
}
}
在我的xaml代码中,我只需要这个:
<ComboBox ItemsSource="{Binding IoTypes}" SelectedItem="{Binding SelectedIoType, Mode=TwoWay}">
你可以通过在Window Loaded事件处理程序中放置以下代码来做到这一点,例如:
yourComboBox.ItemsSource = Enum.GetValues(typeof(EffectStyle)).Cast<EffectStyle>();
如果你需要在XAML中绑定它,你需要使用ObjectDataProvider创建可用的对象作为绑定源:
<Window x:Class="YourNamespace.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:StyleAlias="clr-namespace:Motion.VideoEffects">
<Window.Resources>
<ObjectDataProvider x:Key="dataFromEnum" MethodName="GetValues"
ObjectType="{x:Type System:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="StyleAlias:EffectStyle"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
<Grid>
<ComboBox ItemsSource="{Binding Source={StaticResource dataFromEnum}}"
SelectedItem="{Binding Path=CurrentEffectStyle}" />
</Grid>
</Window>
请注意下面的代码:
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:StyleAlias="clr-namespace:Motion.VideoEffects"
指导如何映射可以在MSDN上读取的命名空间和程序集。