【问题标题】:Databinding an enum property to a ComboBox in WPF将枚举属性数据绑定到 WPF 中的组合框
【发布时间】:2010-09-08 16:39:57
【问题描述】:

以如下代码为例:

public enum ExampleEnum { FooBar, BarFoo }

public class ExampleClass : INotifyPropertyChanged
{
    private ExampleEnum example;

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

我希望将属性 ExampleProperty 数据绑定到 ComboBox,以便它显示选项“FooBar”和“BarFoo”并在 TwoWay 模式下工作。理想情况下,我希望我的 ComboBox 定义看起来像这样:

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

目前我在我的窗口中安装了 ComboBox.SelectionChanged 和 ExampleClass.PropertyChanged 事件的处理程序,我在其中手动进行绑定。

有更好的或某种规范的方法吗?您通常会使用转换器吗?如何使用正确的值填充 ComboBox?我现在什至不想开始使用 i18n。

编辑

所以回答了一个问题:如何使用正确的值填充 ComboBox。

从静态 Enum.GetValues 方法中通过 ObjectDataProvider 将枚举值作为字符串列表检索:

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

这可以用作我的 ComboBox 的 ItemsSource:

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

【问题讨论】:

  • 我对此进行了探索,并提供了一个您可以在位于 here 的 WPF 中使用(完成本地化)的解决方案。

标签: .net wpf


【解决方案1】:

您可以创建自定义标记扩展。

使用示例:

enum Status
{
    [Description("Available.")]
    Available,
    [Description("Not here right now.")]
    Away,
    [Description("I don't have time right now.")]
    Busy
}

在 XAML 的顶部:

    xmlns:my="clr-namespace:namespace_to_enumeration_extension_class

然后……

<ComboBox 
    ItemsSource="{Binding Source={my:Enumeration {x:Type my:Status}}}" 
    DisplayMemberPath="Description" 
    SelectedValue="{Binding CurrentStatus}"  
    SelectedValuePath="Value"  /> 

以及实现...

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

【讨论】:

  • @Gregor S. my:Enumeration 是什么?
  • @Crown 'my' 是您在 xaml 文件顶部声明的命名空间前缀:例如 xmlns:my="clr-namespace:namespace_to_enumeration_extension_class。枚举是 EnumerationExtension 的缩写,在 xaml 中您不需要必须写出整个扩展类名。
  • +1,但是 WPF 完成最简单的事情所需的代码量确实令人头晕
  • 我不太喜欢它让您在视图中使用对模型的一部分(枚举类型)的引用的方式,在 ItemsSource 参数中。为了保持视图和模型解耦,我需要在 ViewModel 中创建枚举的副本并编写 ViewModel 代码以在两者之间进行转换……这将使解决方案不再那么简单。或者有没有办法从 ViewModel 提供类型本身?
  • 另一个限制是,如果您有多种语言,则不能这样做。
【解决方案2】:

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

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 绑定到 MyEnumTypeValuesSelectedItem 绑定到 SelectedMyEnumType

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

【讨论】:

  • 这在我的 Universal 应用程序中运行得非常好,而且很容易实现。谢谢!
  • 这非常有效,并且需要的代码要少得多。
【解决方案3】:

我不喜欢在 UI 中使用枚举的名称。我更喜欢为用户使用不同的值(DisplayMemberPath)和不同的值(在这种情况下为枚举)(SelectedValuePath)。这两个值可以打包到KeyValuePair 并存储在字典中。

XAML

<ComboBox Name="fooBarComboBox" 
          ItemsSource="{Binding Path=ExampleEnumsWithCaptions}" 
          DisplayMemberPath="Value" 
          SelectedValuePath="Key"
          SelectedValue="{Binding Path=ExampleProperty, Mode=TwoWay}" > 

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

编辑:与 MVVM 模式兼容。

【讨论】:

  • 我认为您的回答被低估了,考虑到 ComboBox 本身的期望,这似乎是最好的选择。或许您可以使用Enum.GetValues 在getter 中放置一个字典构建器,但这并不能解决要显示的名称部分。最后,特别是如果实现了 I18n,无论如何,如果枚举发生更改,您将不得不手动更改内容。但是枚举不应该经常改变,如果有的话,是吗? +1
  • 这个答案很棒,它允许本地化枚举描述......谢谢!
  • 这个解决方案非常好,因为它处理枚举和本地化的代码比其他解决方案少!
  • Dictionary 的问题在于键是按哈希值排序的,因此几乎无法控制。虽然有点冗长,但我使用 List> 代替。好主意。
  • @CoperNick @Pragmateek 新修复:public Dictionary&lt;ExampleEnum, string&gt; ExampleEnumsWithCaptions { get; } = new Dictionary&lt;ExampleEnum, string&gt;() { {ExampleEnum.FooBar, "Foo Bar"}, {ExampleEnum.BarFoo, "Reversed Foo Bar"}, //{ExampleEnum.None, "Hidden in UI"}, };
【解决方案4】:

我不知道在 XAML-only 中是否可行,但请尝试以下方法:

为您的 ComboBox 命名,以便您可以在代码隐藏中访问它:“typesComboBox1”

现在试试下面的

typesComboBox1.ItemsSource = Enum.GetValues(typeof(ExampleEnum));

【讨论】:

    【解决方案5】:

    使用 ObjectDataProvider:

    <ObjectDataProvider x:Key="enumValues"
       MethodName="GetValues" ObjectType="{x:Type System:Enum}">
          <ObjectDataProvider.MethodParameters>
               <x:Type TypeName="local:ExampleEnum"/>
          </ObjectDataProvider.MethodParameters>
     </ObjectDataProvider>
    

    然后绑定到静态资源:

    ItemsSource="{Binding Source={StaticResource enumValues}}"
    

    找到这个solution at this blog

    【讨论】:

    • 不错的答案。顺便说一句,它使您不必担心枚举到字符串问题的Converter
    • 链接解决方案似乎已死(韩文还是日文?)。如果我将您的代码放到我的 XAML 资源中,它会说 WPF 项目中不支持 Enum。
    • 你需要在Window标签xlmns定义中添加'xmlns:System="clr-namespace:System;assembly=mscorlib"'
    • 简单优雅的解决方案,这个需要提升!
    【解决方案6】:

    根据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}}"/> 
    

    【讨论】:

      【解决方案7】:

      我最喜欢的方法是使用ValueConverter,以便 ItemsSource 和 SelectedValue 都绑定到同一个属性。这需要不需要额外的属性来保持您的 ViewModel 干净整洁。

      <ComboBox ItemsSource="{Binding Path=ExampleProperty, Converter={x:EnumToCollectionConverter}, Mode=OneTime}"
                SelectedValuePath="Value"
                DisplayMemberPath="Description"
                SelectedValue="{Binding Path=ExampleProperty}" />
      

      以及Converter的定义:

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

      此转换器适用于任何枚举。 ValueDescription 只是一个具有Value 属性和Description 属性的简单类。您可以轻松地将TupleItem1Item2 一起使用,或者将KeyValuePairKeyValue 一起使用,而不是Value 和Description 或您选择的任何其他类,只要它可以保存枚举值和该枚举值的字符串描述。

      【讨论】:

      • 不错的答案!对于ValueDescription 类,如果不需要,可以省略Description 属性。一个只有 Value 属性的简单类也可以工作!
      • 另外,如果要绑定到 RadioButton,则 Convert 方法必须返回字符串列表,即 .Select(e =&gt; e.ToString()),而不是使用 ValueDescription 类。
      • 也可以使用KeyValuePair 代替ValueDescription,例如shown here
      【解决方案8】:

      你可以考虑这样的事情:

      1. 定义文本块的样式,或者您想用来显示枚举的任何其他控件:

        <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>
        
      2. 定义 ComboBoxItem 的样式

        <Style TargetType="{x:Type ComboBoxItem}">
            <Setter Property="ContentTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <TextBlock Tag="{Binding}" Style="{StaticResource enumStyle}"/>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        
      3. 添加一个组合框并使用您的枚举值加载它:

        <ComboBox SelectedValue="{Binding Path=your property goes here}" SelectedValuePath="Content">
            <ComboBox.Items>
                <ComboBoxItem>
                    <proj:YourEnum>Value1</proj:YourEnum>
                </ComboBoxItem>
            </ComboBox.Items>
        </ComboBox>
        

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

      【讨论】:

      • SelectedValuePath="Content" 在这里帮助了我。我将我的 ComboBoxItems 作为字符串值,并且一直无法将 ComboBoxItem 转换为我的枚举类型。谢谢
      【解决方案9】:

      这是一个使用辅助方法的通用解决方案。 这也可以处理任何底层类型的枚举(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"
      />
      

      【讨论】:

        【解决方案10】:

        如果您使用的是 MVVM,根据@rudigrobler 的回答,您可以执行以下操作:

        将以下属性添加到 ViewModel

        public Array ExampleEnumValues => Enum.GetValues(typeof(ExampleEnum));
        

        然后在 XAML 中执行以下操作:

        <ComboBox ItemsSource="{Binding ExampleEnumValues}" ... />
        

        【讨论】:

          【解决方案11】:

          这是一个 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 也是一个很棒的图书馆。

          【讨论】:

          • 郑重声明,我不隶属于 DevExpress。 Telerik 也有非常好的库,他们的库甚至可能不需要这种技术。
          【解决方案12】:

          尝试使用

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

          【讨论】:

          • 这不起作用。组合框只会显示一个空文本,更改它不会做任何事情。我想在这里加入转换器将是最好的解决方案。
          【解决方案13】:

          我创建了一个开源 CodePlex 项目来执行此操作。您可以从here 下载 NuGet 包。

          <enumComboBox:EnumComboBox EnumType="{x:Type demoApplication:Status}" SelectedValue="{Binding Status}" />
          

          【讨论】:

            【解决方案14】:

            代码

                public enum RULE
                {
                    [Description( "Любые, без ограничений" )]
                    any,
                    [Description( "Любые если будет три в ряд" )]
                    anyThree,
                    [Description( "Соседние, без ограничений" )]
                    nearAny,
                    [Description( "Соседние если будет три в ряд" )]
                    nearThree
                }
            
                class ExtendRULE
                {
                    public static object Values
                    {
                        get
                        {
                            List<object> list = new List<object>();
                            foreach( RULE rule in Enum.GetValues( typeof( RULE ) ) )
                            {
                                string desc = rule.GetType().GetMember( rule.ToString() )[0].GetCustomAttribute<DescriptionAttribute>().Description;
                                list.Add( new { value = rule, desc = desc } );
                            }
                            return list;
                        }
                    }
                }
            

            XAML

            <StackPanel>
               <ListBox ItemsSource= "{Binding Source={x:Static model:ExtendRULE.Values}}" DisplayMemberPath="desc" SelectedValuePath="value" SelectedValue="{Binding SelectedRule}"/>
               <ComboBox ItemsSource="{Binding Source={x:Static model:ExtendRULE.Values}}" DisplayMemberPath="desc" SelectedValuePath="value" SelectedValue="{Binding SelectedRule}"/>                        
            </StackPanel>
            

            【讨论】:

              【解决方案15】:

              看到某些过于复杂的解决方案如何成为最微不足道的问题的“标准(反)模式”令人痛苦:实现MarkupExtension 的开销和复杂性,尤其是用属性装饰枚举值应该避免。只需实现一个数据模型。

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

              当涉及到本地化时,问题变得更加明显。
              这就是为什么所有发布的答案都只是过度设计的原因。他们使一个非常简单的问题看起来像一个关键问题。
              事实上,最简单的解决方案是最好的。原始问题的主题绝对是个例外。
              我强烈建议不要提供任何答案。尽管它们可能有效,但它们给一个琐碎的问题增加了不必要的复杂性。

              请注意,您始终可以通过调用静态 Enum.GetValuesEnum.GetNames 将枚举转换为其值或值名称的列表,它们都返回您可以直接分配给 ComboBox.ItemsSourceIEnumerable属性,例如,通过数据绑定。

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

              通常,在定义枚举时,您不会考虑 UI。
              枚举值名称不是根据 UI 设计规则选择的。
              通常,UI 标签和文本通常是由没有开发人员或程序员背景的人创建的。他们通常会提供本地化应用程序所需的所有翻译。
              不将 UI 与应用程序混合使用有很多很好的理由。
              你永远不会在设计一个类并为它的属性命名时考虑到 UI(例如,DataGrid 列)。您可能希望列标题包含空格等。
              异常消息针对开发人员而不是用户的原因相同。您绝对不想用属性装饰每个属性、每个异常、枚举或任何数据类型或成员,以便在特定 UI 上下文中提供对用户有意义的显示名称。
              您不希望 UI 设计渗入您的代码库并污染您的类。
              应用程序及其用户界面 - 这是两个不同的问题。
              添加这个抽象或虚拟的额外分离层允许例如添加不应显示的枚举值。或者更一般地说,修改代码而不必破坏或修改 UI。

              您应该使用 simple 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>
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2018-11-12
                • 2020-02-07
                • 1970-01-01
                • 1970-01-01
                • 2018-01-12
                • 2015-08-20
                相关资源
                最近更新 更多