如何将枚举绑定到WPF中的组合框控件?

Joa*_*nge 169 .net c# data-binding wpf xaml

我试图找到一个简单的例子,其中枚举按原样显示.我见过的所有示例都尝试添加漂亮的显示字符串,但我不希望这种复杂性.

基本上我有一个类,它包含我绑定的所有属性,首先将DataContext设置为此类,然后在xaml文件中指定这样的绑定:

<ComboBox ItemsSource="{Binding Path=EffectStyle}"/>
Run Code Online (Sandbox Code Playgroud)

但是这并没有在ComboBoxas项中显示枚举值.

Kyr*_*o M 285

您可以通过在Window Loaded事件处理程序中放置以下代码从代码中执行此操作,例如:

yourComboBox.ItemsSource = Enum.GetValues(typeof(EffectStyle)).Cast<EffectStyle>();
Run Code Online (Sandbox Code Playgroud)

如果需要在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>
Run Code Online (Sandbox Code Playgroud)

请注意下一个代码:

xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:StyleAlias="clr-namespace:Motion.VideoEffects"
Run Code Online (Sandbox Code Playgroud)

指导如何映射您可以在MSDN上阅读的命名空间和程序集.

  • 您可以使用ReSharper之类的工具.它解析所有引用的程序集,并提供需要包含的建议.无需编写 - 只需从选项中进行选择即可. (4认同)
  • 您需要添加对它的引用并在XAML中添加`xmlns:DllAlias ="clr-namespace:NamespaceInsideDll; assembly = DllAssemblyName"`以使用它.这是指南:http://msdn.microsoft.com/en-us/library/ms747086.aspx (2认同)

Nic*_*ick 106

我喜欢我绑定的所有对象都在我的中定义ViewModel,因此我尽量避免<ObjectDataProvider>在xaml中使用.

我的解决方案不使用View中定义的数据,也不使用代码隐藏.只有一个DataBinding,一个可重用的ValueConverter,一个获取任何Enum类型描述集合的方法,以及要绑定到ViewModel中的单个属性.

当我想要绑定EnumComboBox我要显示的文本永远不会匹配的值时Enum,所以我使用该[Description()]属性为它提供我实际想要在其中看到的文本ComboBox.如果我在游戏中有一个字符类枚举,它看起来像这样:

public enum DayOfWeek
{
  // add an optional blank value for default/no selection
  [Description("")]
  NOT_SET = 0,
  [Description("Sunday")]
  SUNDAY,
  [Description("Monday")]
  MONDAY,
  ...
}
Run Code Online (Sandbox Code Playgroud)

首先,我使用几种方法创建了辅助类来处理枚举.一种方法获取特定值的描述,另一种方法获取所有值及其对类型的描述.

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();
  }
}
Run Code Online (Sandbox Code Playgroud)

接下来,我们创建一个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;
  }
}
Run Code Online (Sandbox Code Playgroud)

ViewModel只需要一个属性,我View可以绑定到组合框SelectedValueItemsSource组合框:

private DayOfWeek dayOfWeek;

public DayOfWeek SelectedDay
{
  get { return dayOfWeek; }
  set
  {
    if (dayOfWeek != value)
    {
      dayOfWeek = value;
      OnPropertyChanged(nameof(SelectedDay));
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

最后绑定ComboBox视图(使用ValueConverterItemsSource绑定)...

<ComboBox ItemsSource="{Binding Path=SelectedDay, Converter={x:EnumToCollectionConverter}, Mode=OneTime}"
          SelectedValuePath="Value"
          DisplayMemberPath="Description"
          SelectedValue="{Binding Path=SelectedDay}" />
Run Code Online (Sandbox Code Playgroud)

要实现此解决方案,您只需复制我的EnumHelper类和EnumToCollectionConverter类.他们将与任何枚举一起使用.此外,我没有在这里包含它,但是这个ValueDescription类只是一个带有2个公共对象属性的简单类,一个叫做Value,一个叫做Description.您可以自己创建,也可以更改代码以使用Tuple<object, object>KeyValuePair<object, object>

  • 为了完成这项工作,我必须创建一个`ValueDescription`类,它具有`Value`和`Description`的公共属性 (7认同)
  • 是的,您也可以更改此代码以使用`Tuple <T1,T2>`或`KeyValuePair <TKey,TValue>`而不是`ValueDescription`类,然后您就不必创建自己的代码. (4认同)
  • 我真的不理解那些需要解决复杂问题的人,甚至不知道如何编写一个只有 2 个“对象”属性而无需手持的类。 (4认同)
  • 您不需要为返回列表的属性实现 OnPropertyChanged。该列表是根据枚举中的值生成的。它在运行时永远不会改变,并且当它永远不会改变时,它永远不需要通知任何人它已经改变了。此外,在更新版本中,甚至根本不需要 list 属性。 (2认同)

tom*_*ska 46

我使用了另一种使用MarkupExtension的解决方案.

  1. 我做了一个提供项目来源的课程:

    public class EnumToItemsSource : MarkupExtension
    {
        private readonly Type _type;
    
        public EnumToItemsSource(Type type)
        {
            _type = type;
        }
    
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return Enum.GetValues(_type)
                .Cast<object>()
                .Select(e => new { Value = (int)e, DisplayName = e.ToString() });
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 这几乎都是......现在在XAML中使用它:

        <ComboBox DisplayMemberPath="DisplayName"
              ItemsSource="{persons:EnumToItemsSource {x:Type enums:States}}"
              SelectedValue="{Binding Path=WhereEverYouWant}"
              SelectedValuePath="Value" />
    
    Run Code Online (Sandbox Code Playgroud)
  3. 将"枚举:状态"更改为您的枚举

  • @ tom.maruska我不是想了解我的答案还是你的答案,但是自从您提出答案以来,拥有2个属性不会违反DRY规则,因为它们是两个具有不同用途的不同属性。而且您的答案还需要添加一个属性(您甚至自己调用了此属性`{Binding Path = WhereEverYouWant}`),并且如果您希望它支持双向绑定,那么您也将为其提供支持字段。因此,您这样做并不是要替换2个属性和1个后备字段,而只是替换1个单行只读属性。 (2认同)

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)

  • 完美简单的解决方案.系统的命名空间,如kirmir的答案:`xmlns:System ="clr-namespace:System; assembly = mscorlib"` (4认同)

小智 14

它工作起来非常好而且简单。
xaml

<ComboBox ItemsSource="{Binding MyEnumArray}">
Run Code Online (Sandbox Code Playgroud)

。CS

public Array MyEnumArray
{
  get { return Enum.GetValues(typeof(MyEnum)); }
}
Run Code Online (Sandbox Code Playgroud)

  • 非常干净的解决方案,也可以让它变得更加“public Array MyEnumArray =&gt; Enum.GetValues(typeof(MyEnum));” (3认同)

小智 9

尼克的回答确实帮助了我,但我意识到它可以稍微调整一下,以避免额外的类,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>();
   }
}
Run Code Online (Sandbox Code Playgroud)

最后是XAML:

<ComboBox ItemSource="{Binding Path=PlayerClassList}"
          DisplayMemberPath="Value"
          SelectedValuePath="Key"
          SelectedValue="{Binding Path=SelectedClass}" />
Run Code Online (Sandbox Code Playgroud)

我希望这对其他人有帮助.


And*_*ndy 8

您需要在枚举中创建一个值数组,可以通过调用System.Enum.GetValues()创建它,并将其传递给Type您想要项目的枚举.

如果为ItemsSource属性指定了此属性,则应使用所有枚举值填充它.你可能要绑定SelectedItemEffectStyle(假设它是相同的枚举的属性,包含当前值).


AQu*_*rky 7

这个问题有很多很好的答案,我谦虚地提交我的答案。我发现我的更简单、更优雅。它只需要一个值转换器。

给定一个枚举...

public enum ImageFormat
{
    [Description("Windows Bitmap")]
    BMP,
    [Description("Graphics Interchange Format")]
    GIF,
    [Description("Joint Photographic Experts Group Format")]
    JPG,
    [Description("Portable Network Graphics Format")]
    PNG,
    [Description("Tagged Image Format")]
    TIFF,
    [Description("Windows Media Photo Format")]
    WDP
}
Run Code Online (Sandbox Code Playgroud)

和一个值转换器...

public class ImageFormatValueConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is ImageFormat format)
        {
            return GetString(format);
        }

        return null;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is string s)
        {
            return Enum.Parse(typeof(ImageFormat), s.Substring(0, s.IndexOf(':')));
        }
        return null;
    }

    public string[] Strings => GetStrings();

    public static string GetString(ImageFormat format)
    {
        return format.ToString() + ": " + GetDescription(format);
    }

    public static string GetDescription(ImageFormat format)
    {
        return format.GetType().GetMember(format.ToString())[0].GetCustomAttribute<DescriptionAttribute>().Description;

    }
    public static string[] GetStrings()
    {
        List<string> list = new List<string>();
        foreach (ImageFormat format in Enum.GetValues(typeof(ImageFormat)))
        {
            list.Add(GetString(format));
        }

        return list.ToArray();
    }
}
Run Code Online (Sandbox Code Playgroud)

资源...

    <local:ImageFormatValueConverter x:Key="ImageFormatValueConverter"/>
Run Code Online (Sandbox Code Playgroud)

XAML 声明...

    <ComboBox Grid.Row="9" ItemsSource="{Binding Source={StaticResource ImageFormatValueConverter}, Path=Strings}"
              SelectedItem="{Binding Format, Converter={StaticResource ImageFormatValueConverter}}"/>
Run Code Online (Sandbox Code Playgroud)

查看型号...

    private ImageFormat _imageFormat = ImageFormat.JPG;
    public ImageFormat Format
    {
        get => _imageFormat;
        set
        {
            if (_imageFormat != value)
            {
                _imageFormat = value;
                OnPropertyChanged();
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

结果组合框...

组合框绑定到枚举