以下面的代码为例:

public enum ExampleEnum { FooBar, BarFoo }

public class ExampleClass : INotifyPropertyChanged
{
    private ExampleEnum example;

    public ExampleEnum ExampleProperty 
    { get { return example; } { /* set and notify */; } }
}

我想把属性ExampleProperty绑定到一个组合框,这样它就显示了选项“FooBar”和“BarFoo”,并在模式双向工作。最理想的情况下,我希望我的ComboBox定义看起来像这样:

<ComboBox ItemsSource="What goes here?" SelectedItem="{Binding Path=ExampleProperty}" />

目前我有处理程序的组合框。SelectionChanged和ExampleClass。在我手动绑定的窗口中安装了PropertyChanged事件。

有没有更好的或者某种权威的方法?你通常会使用转换器,你将如何填充组合框与正确的值?我现在甚至不想从i18n开始。

Edit

因此,一个问题得到了回答:我如何用正确的值填充组合框。

通过ObjectDataProvider从静态Enum中检索Enum值作为字符串列表。getvalue方法:

<Window.Resources>
    <ObjectDataProvider MethodName="GetValues"
        ObjectType="{x:Type sys:Enum}"
        x:Key="ExampleEnumValues">
        <ObjectDataProvider.MethodParameters>
            <x:Type TypeName="ExampleEnum" />
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
</Window.Resources>

这可以作为我的组合框的ItemsSource:

<ComboBox ItemsSource="{Binding Source={StaticResource ExampleEnumValues}}"/>

当前回答

下面是一个使用helper方法的通用解决方案。 它还可以处理任何底层类型的枚举(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

视图模型:

public IEnumerable<object> EnumSearchTypes {
    get {
        return GetEnum<SearchTypes>();
    }
}//property

下拉列表框:

<ComboBox
    SelectedValue       ="{Binding SearchType}"
    ItemsSource         ="{Binding EnumSearchTypes}"
    DisplayMemberPath   ="Name"
    SelectedValuePath   ="Value"
/>

其他回答

你可以这样考虑:

define a style for textblock, or any other control you want to use to display your enum: <Style x:Key="enumStyle" TargetType="{x:Type TextBlock}"> <Setter Property="Text" Value="&lt;NULL&gt;"/> <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> define your style for ComboBoxItem <Style TargetType="{x:Type ComboBoxItem}"> <Setter Property="ContentTemplate"> <Setter.Value> <DataTemplate> <TextBlock Tag="{Binding}" Style="{StaticResource enumStyle}"/> </DataTemplate> </Setter.Value> </Setter> </Style> add a combobox and load it with your enum values: <ComboBox SelectedValue="{Binding Path=your property goes here}" SelectedValuePath="Content"> <ComboBox.Items> <ComboBoxItem> <proj:YourEnum>Value1</proj:YourEnum> </ComboBoxItem> </ComboBox.Items> </ComboBox>

如果枚举很大,当然可以在代码中执行相同的操作,从而节省大量输入。 我喜欢这种方法,因为它使本地化变得容易——你定义一次所有的模板,然后,你只更新你的字符串资源文件。

看到所有的东西,看到某些过于复杂的解决方案如何成为最琐碎问题的“标准(反)模式”是一件痛苦的事情:应该避免实现MarkupExtension的开销和复杂性,特别是使用属性修饰枚举值。简单地实现一个数据模型。

通常,向用户显示枚举值名称是一个坏主意。枚举不应该显示在UI中。它们是在编程上下文中使用的常量。值名称不用于显示。他们是为了称呼工程师,因此这些名字通常使用特殊的语义和词汇,就像科学词汇一样,不被公众所理解。不要犹豫,为显示的值创建一个专用的源。

当涉及到本地化时,问题变得更加明显。 这就是为什么所有贴出来的答案都是过度设计的。它们使一个非常简单的问题看起来像一个关键问题。 最平凡的解决方案就是最好的解决方案,这是事实。最初问题的主题绝对不是个例外。 我强烈建议不要使用任何提供的答案。尽管它们可能会起作用,但它们会给一个微不足道的问题增加不必要的复杂性。

请注意,您总是可以通过调用静态enum将枚举转换为其值或值名的列表。GetValues或Enum。GetNames,它们都返回一个IEnumerable,你可以直接分配给组合框。ItemsSource属性,例如通过数据绑定。

IEnumerable<ExampleEnum> values = Enum.GetValues<ExampleEnum>();
IEnumerable<string> names = Enum.GetNames<ExampleEnum>();

Usually, when defining an enumeration, you don't have UI in mind. Enumeration value names are not chosen based on UI design rules. Usually, UI labels and text in general are created by people with no developer or programmer background. They usually provide all the required translations to localize the application. There are many good reasons not to mix UI with the application. You would never design a class and name its properties with UI (e.g., DataGrid columns) in mind. You may want your column header to contain whitespaces etc. Same reason why exception messages are directed at developers and not users. You definitely don't want to decorate every property, every exception, enum or whatever data type or member with attributes in order to provide a display name that makes sense to the user in a particular UI context. You don't want to have UI design bleed into your code base and polute your classes. Application and its user interface - this are two different problems. Adding this abstract or virtual extra layer of separation allows e.g., to add enum values that should not be displayed. Or more general, modify code without having to break or modify the UI.

您应该使用一个简单的IValueConverter或一个专门的类来提供这些显示值作为绑定源,而不是使用属性和实现额外的逻辑负载来提取它们的值(使用反射)。 坚持最常见的模式,并为ComboBox项目实现一个数据模型,其中类具有枚举类型的属性作为成员,这有助于您识别ComboBox。SelectedItem(如果你需要枚举值):

ExampleEnum.cs

// Define enumeration without minding any UI elements and context
public enum ExampleEnum 
{ 
    FooBar = 0, 
    BarFoo 
}

ExampleClass.cs

// Define readable enum display values in the UI context.
// Display names can come from a localizable resource.
public class BindingSource : INotifyPropertyChanged
{
    public BindingSource()
    {
        ItemModels = new List<ItemModel> 
        {
            new ItemModel { Label = "Foo Bar Display", Value = ExampleEnum.FooBar },
            new ItemModel { Label = "Bar Foo Display", Value = ExampleEnum.BarFoo }
        }
    }

    public List<ItemModel> ItemModels { get; }

    private ItemModel selectedItemModel;
    public ItemModel SelectedItemModel { get => selectedItemModel; => set and notify; }
}

ItemModel.cs

public class ItemModel
{   
    public string Label { get; set; }
    public ExampleEnum Value { get; set; }
}

MainWindow.xaml

<Window>
  <Window.DataContext>
    <BindingSource />
  </Window.DataContext>

  <ComboBox ItemsSource="{Binding ItemModels}"
            DisplayMemberName="DisplayValue"
            SelectedItem="{Binding SelectedItemModel}" />
</Window>

基于ageektrapped提供的已被接受但现已删除的答案,我创建了一个精简版,没有一些更高级的功能。这里包含的所有代码都允许您复制粘贴它,而不会被链接失效阻塞。

我使用System.ComponentModel.DescriptionAttribute,它实际上用于设计时描述。如果你不喜欢使用这个属性,你可以创建自己的属性,但我认为使用这个属性真的可以完成工作。如果不使用该属性,则名称将默认为代码中枚举值的名称。

public enum ExampleEnum {

  [Description("Foo Bar")]
  FooBar,

  [Description("Bar Foo")]
  BarFoo

}

下面是用作项目源的类:

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;
  }

}

你可以像这样在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}}"/> 

这是DevExpress基于Gregor S.投票最多的答案给出的具体答案(目前有128票)。

这意味着我们可以在整个应用程序中保持样式一致:

不幸的是,如果没有一些修改,最初的答案不能与DevExpress的ComboBoxEdit一起工作。

首先,ComboBoxEdit的XAML:

<dxe:ComboBoxEdit ItemsSource="{Binding Source={xamlExtensions:XamlExtensionEnumDropdown {x:myEnum:EnumFilter}}}"
    SelectedItem="{Binding BrokerOrderBookingFilterSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
    DisplayMember="Description"
    MinWidth="144" Margin="5" 
    HorizontalAlignment="Left"
    IsTextEditable="False"
    ValidateOnTextInput="False"
    AutoComplete="False"
    IncrementalFiltering="True"
    FilterCondition="Like"
    ImmediatePopup="True"/>

不用说,你需要将xamlExtensions指向包含XAML扩展类的命名空间(下面定义):

xmlns:xamlExtensions="clr-namespace:XamlExtensions"

我们必须将myEnum指向包含枚举的命名空间:

xmlns:myEnum="clr-namespace:MyNamespace"

然后,枚举:

namespace MyNamespace
{
    public enum EnumFilter
    {
        [Description("Free as a bird")]
        Free = 0,

        [Description("I'm Somewhat Busy")]
        SomewhatBusy = 1,

        [Description("I'm Really Busy")]
        ReallyBusy = 2
    }
}

XAML中的问题是我们不能使用SelectedItemValue,因为这会抛出一个错误,因为setter是不可访问的(DevExpress,这是您的一部分疏忽)。所以我们必须修改我们的ViewModel来直接从对象中获取值:

private EnumFilter _filterSelected = EnumFilter.All;
public object FilterSelected
{
    get
    {
        return (EnumFilter)_filterSelected;
    }
    set
    {
        var x = (XamlExtensionEnumDropdown.EnumerationMember)value;
        if (x != null)
        {
            _filterSelected = (EnumFilter)x.Value;
        }
        OnPropertyChanged("FilterSelected");
    }
}

为了完整起见,这里是原始答案(稍微重命名)的XAML扩展:

namespace XamlExtensions
{
    /// <summary>
    ///     Intent: XAML markup extension to add support for enums into any dropdown box, see http://bit.ly/1g70oJy. We can name the items in the
    ///     dropdown box by using the [Description] attribute on the enum values.
    /// </summary>
    public class XamlExtensionEnumDropdown : MarkupExtension
    {
        private Type _enumType;


        public XamlExtensionEnumDropdown(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();
        }

        #region Nested type: EnumerationMember
        public class EnumerationMember
        {
            public string Description { get; set; }
            public object Value { get; set; }
        }
        #endregion
    }
}

免责声明:我与DevExpress没有任何关系。Telerik也是一个很棒的图书馆。

在视图模型中,你可以有:

public MyEnumType SelectedMyEnumType 
{
    get { return _selectedMyEnumType; }
    set { 
            _selectedMyEnumType = value;
            OnPropertyChanged("SelectedMyEnumType");
        }
}

public IEnumerable<MyEnumType> MyEnumTypeValues
{
    get
    {
        return Enum.GetValues(typeof(MyEnumType))
            .Cast<MyEnumType>();
    }
}

在XAML中,ItemSource绑定到MyEnumTypeValues, SelectedItem绑定到SelectedMyEnumType。

<ComboBox SelectedItem="{Binding SelectedMyEnumType}" ItemsSource="{Binding MyEnumTypeValues}"></ComboBox>