作为示例,请使用以下代码:
public enum ExampleEnum { FooBar, BarFoo }
public class ExampleClass : INotifyPropertyChanged
{
private ExampleEnum example;
public ExampleEnum ExampleProperty
{ get { return example; } { /* set and notify */; } }
}
Run Code Online (Sandbox Code Playgroud)
我想要将属性ExampleProperty数据绑定到ComboBox,以便它显示选项"FooBar"和"BarFoo"并在TwoWay模式下工作.最理想的是我希望我的ComboBox定义看起来像这样:
<ComboBox ItemsSource="What goes here?" SelectedItem="{Binding Path=ExampleProperty}" />
Run Code Online (Sandbox Code Playgroud)
目前我在我的Window中安装了ComboBox.SelectionChanged和ExampleClass.PropertyChanged事件的处理程序,我手动执行绑定.
是否有更好或某种规范的方式?您通常会使用转换器吗?如何使用正确的值填充ComboBox?我现在甚至不想开始使用i18n.
编辑
所以回答了一个问题:如何使用正确的值填充ComboBox.
通过静态Enum.GetValues方法中的ObjectDataProvider将Enum值作为字符串列表检索:
<Window.Resources>
<ObjectDataProvider MethodName="GetValues"
ObjectType="{x:Type sys:Enum}"
x:Key="ExampleEnumValues">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="ExampleEnum" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
Run Code Online (Sandbox Code Playgroud)
这个我可以用作我的ComboBox的ItemsSource:
<ComboBox ItemsSource="{Binding Source={StaticResource ExampleEnumValues}}"/>
Run Code Online (Sandbox Code Playgroud)
Gre*_*vec 205
您可以创建自定义标记扩展.
用法示例:
enum Status
{
[Description("Available.")]
Available,
[Description("Not here right now.")]
Away,
[Description("I don't have time right now.")]
Busy
}
Run Code Online (Sandbox Code Playgroud)
在XAML的顶部:
xmlns:my="clr-namespace:namespace_to_enumeration_extension_class
Run Code Online (Sandbox Code Playgroud)
然后...
<ComboBox
ItemsSource="{Binding Source={my:Enumeration {x:Type my:Status}}}"
DisplayMemberPath="Description"
SelectedValue="{Binding CurrentStatus}"
SelectedValuePath="Value" />
Run Code Online (Sandbox Code Playgroud)
并实施......
public class EnumerationExtension : MarkupExtension
{
private Type _enumType;
public EnumerationExtension(Type enumType)
{
if (enumType == null)
throw new ArgumentNullException("enumType");
EnumType = enumType;
}
public Type EnumType
{
get { return _enumType; }
private set
{
if (_enumType == value)
return;
var enumType = Nullable.GetUnderlyingType(value) ?? value;
if (enumType.IsEnum == false)
throw new ArgumentException("Type must be an Enum.");
_enumType = value;
}
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
var enumValues = Enum.GetValues(EnumType);
return (
from object enumValue in enumValues
select new EnumerationMember{
Value = enumValue,
Description = GetDescription(enumValue)
}).ToArray();
}
private string GetDescription(object enumValue)
{
var descriptionAttribute = EnumType
.GetField(enumValue.ToString())
.GetCustomAttributes(typeof (DescriptionAttribute), false)
.FirstOrDefault() as DescriptionAttribute;
return descriptionAttribute != null
? descriptionAttribute.Description
: enumValue.ToString();
}
public class EnumerationMember
{
public string Description { get; set; }
public object Value { get; set; }
}
}
Run Code Online (Sandbox Code Playgroud)
小智 171
在viewmodel中,您可以:
public MyEnumType SelectedMyEnumType
{
get { return _selectedMyEnumType; }
set {
_selectedMyEnumType = value;
OnPropertyChanged("SelectedMyEnumType");
}
}
public IEnumerable<MyEnumType> MyEnumTypeValues
{
get
{
return Enum.GetValues(typeof(MyEnumType))
.Cast<MyEnumType>();
}
}
Run Code Online (Sandbox Code Playgroud)
在XAML中,ItemSource绑定到MyEnumTypeValues,SelectedItem绑定到SelectedMyEnumType.
<ComboBox SelectedItem="{Binding SelectedMyEnumType}" ItemsSource="{Binding MyEnumTypeValues}"></ComboBox>
Run Code Online (Sandbox Code Playgroud)
Cop*_*ick 85
我不想在UI中使用枚举的名称.我更喜欢为user(DisplayMemberPath)使用不同的值,并为value(在这种情况下为enum)使用不同的值(SelectedValuePath).这两个值可以打包KeyValuePair并存储在字典中.
XAML
<ComboBox Name="fooBarComboBox"
ItemsSource="{Binding Path=ExampleEnumsWithCaptions}"
DisplayMemberPath="Value"
SelectedValuePath="Key"
SelectedValue="{Binding Path=ExampleProperty, Mode=TwoWay}" >
Run Code Online (Sandbox Code Playgroud)
C#
public Dictionary<ExampleEnum, string> ExampleEnumsWithCaptions { get; } =
new Dictionary<ExampleEnum, string>()
{
{ExampleEnum.FooBar, "Foo Bar"},
{ExampleEnum.BarFoo, "Reversed Foo Bar"},
//{ExampleEnum.None, "Hidden in UI"},
};
private ExampleEnum example;
public ExampleEnum ExampleProperty
{
get { return example; }
set { /* set and notify */; }
}
Run Code Online (Sandbox Code Playgroud)
编辑:与MVVM模式兼容.
rud*_*ler 40
我不知道是否可以仅在XAML中使用,但请尝试以下方法:
为ComboBox命名,以便在代码隐藏中访问它:"typesComboBox1"
现在尝试以下方法
typesComboBox1.ItemsSource = Enum.GetValues(typeof(ExampleEnum));
Run Code Online (Sandbox Code Playgroud)
Mar*_*age 23
根据ageektrapped提供的已接受但现已删除的答案,我创建了一个没有一些更高级功能的精简版本.这里包含所有代码,允许您复制粘贴它,而不是被link-rot阻止.
我使用的System.ComponentModel.DescriptionAttribute是真正用于设计时的描述.如果您不喜欢使用此属性,您可以创建自己的属性,但我认为使用此属性确实可以完成工作.如果不使用该属性,则名称将默认为代码中枚举值的名称.
public enum ExampleEnum {
[Description("Foo Bar")]
FooBar,
[Description("Bar Foo")]
BarFoo
}
Run Code Online (Sandbox Code Playgroud)
以下是用作项目来源的类:
public class EnumItemsSource : Collection<String>, IValueConverter {
Type type;
IDictionary<Object, Object> valueToNameMap;
IDictionary<Object, Object> nameToValueMap;
public Type Type {
get { return this.type; }
set {
if (!value.IsEnum)
throw new ArgumentException("Type is not an enum.", "value");
this.type = value;
Initialize();
}
}
public Object Convert(Object value, Type targetType, Object parameter, CultureInfo culture) {
return this.valueToNameMap[value];
}
public Object ConvertBack(Object value, Type targetType, Object parameter, CultureInfo culture) {
return this.nameToValueMap[value];
}
void Initialize() {
this.valueToNameMap = this.type
.GetFields(BindingFlags.Static | BindingFlags.Public)
.ToDictionary(fi => fi.GetValue(null), GetDescription);
this.nameToValueMap = this.valueToNameMap
.ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
Clear();
foreach (String name in this.nameToValueMap.Keys)
Add(name);
}
static Object GetDescription(FieldInfo fieldInfo) {
var descriptionAttribute =
(DescriptionAttribute) Attribute.GetCustomAttribute(fieldInfo, typeof(DescriptionAttribute));
return descriptionAttribute != null ? descriptionAttribute.Description : fieldInfo.Name;
}
}
Run Code Online (Sandbox Code Playgroud)
您可以在XAML中使用它,如下所示:
<Windows.Resources>
<local:EnumItemsSource
x:Key="ExampleEnumItemsSource"
Type="{x:Type local:ExampleEnum}"/>
</Windows.Resources>
<ComboBox
ItemsSource="{StaticResource ExampleEnumItemsSource}"
SelectedValue="{Binding ExampleProperty, Converter={StaticResource ExampleEnumItemsSource}}"/>
Run Code Online (Sandbox Code Playgroud)
dru*_*uss 22
使用ObjectDataProvider:
<ObjectDataProvider x:Key="enumValues"
MethodName="GetValues" ObjectType="{x:Type System:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="local:ExampleEnum"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
Run Code Online (Sandbox Code Playgroud)
然后绑定到静态资源:
ItemsSource="{Binding Source={StaticResource enumValues}}"
Run Code Online (Sandbox Code Playgroud)
你可以考虑这样的事情:
为textblock或要用于显示枚举的任何其他控件定义样式:
<Style x:Key="enumStyle" TargetType="{x:Type TextBlock}">
<Setter Property="Text" Value="<NULL>"/>
<Style.Triggers>
<Trigger Property="Tag">
<Trigger.Value>
<proj:YourEnum>Value1<proj:YourEnum>
</Trigger.Value>
<Setter Property="Text" Value="{DynamicResource yourFriendlyValue1}"/>
</Trigger>
<!-- add more triggers here to reflect your enum -->
</Style.Triggers>
</Style>
Run Code Online (Sandbox Code Playgroud)为ComboBoxItem定义您的样式
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Tag="{Binding}" Style="{StaticResource enumStyle}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
Run Code Online (Sandbox Code Playgroud)添加一个组合框并使用您的枚举值加载它:
<ComboBox SelectedValue="{Binding Path=your property goes here}" SelectedValuePath="Content">
<ComboBox.Items>
<ComboBoxItem>
<proj:YourEnum>Value1</proj:YourEnum>
</ComboBoxItem>
</ComboBox.Items>
</ComboBox>
Run Code Online (Sandbox Code Playgroud)如果你的枚举很大,你当然可以在代码中做同样的事情,节省大量的打字.我喜欢这种方法,因为它使本地化变得容易 - 你定义了所有模板一次,然后,你只更新你的字符串资源文件.
这是使用辅助方法的通用解决方案。这也可以处理任何基础类型的枚举(byte、sbyte、uint、long 等)
辅助方法:
static IEnumerable<object> GetEnum<T>() {
var type = typeof(T);
var names = Enum.GetNames(type);
var values = Enum.GetValues(type);
var pairs =
Enumerable.Range(0, names.Length)
.Select(i => new {
Name = names.GetValue(i)
, Value = values.GetValue(i) })
.OrderBy(pair => pair.Name);
return pairs;
}//method
Run Code Online (Sandbox Code Playgroud)
查看型号:
public IEnumerable<object> EnumSearchTypes {
get {
return GetEnum<SearchTypes>();
}
}//property
Run Code Online (Sandbox Code Playgroud)
组合框:
<ComboBox
SelectedValue ="{Binding SearchType}"
ItemsSource ="{Binding EnumSearchTypes}"
DisplayMemberPath ="Name"
SelectedValuePath ="Value"
/>
Run Code Online (Sandbox Code Playgroud)
我最喜欢的方法是使用,ValueConverter这样ItemsSource和SelectedValue都绑定到同一属性。这不需要其他属性即可保持ViewModel的美观和整洁。
<ComboBox ItemsSource="{Binding Path=ExampleProperty, Converter={x:EnumToCollectionConverter}, Mode=OneTime}"
SelectedValuePath="Value"
DisplayMemberPath="Description"
SelectedValue="{Binding Path=ExampleProperty}" />
Run Code Online (Sandbox Code Playgroud)
以及转换器的定义:
public static class EnumHelper
{
public static string Description(this Enum e)
{
return (e.GetType()
.GetField(e.ToString())
.GetCustomAttributes(typeof(DescriptionAttribute), false)
.FirstOrDefault() as DescriptionAttribute)?.Description ?? e.ToString();
}
}
[ValueConversion(typeof(Enum), typeof(IEnumerable<ValueDescription>))]
public class EnumToCollectionConverter : MarkupExtension, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return Enum.GetValues(value.GetType())
.Cast<Enum>()
.Select(e => new ValueDescription() { Value = e, Description = e.Description()})
.ToList();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
Run Code Online (Sandbox Code Playgroud)
该转换器可与任何枚举一起使用。ValueDescription只是具有Value属性和Description属性的简单类。您可以轻松地使用Tuplewith Item1和and Item2,或者使用KeyValuePairwith Key和Value代替Value and Description或您选择的任何其他类,只要它可以容纳枚举值和该枚举值的字符串描述即可。