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


182

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

基本上,我有一个类来保存我绑定的所有属性,方法是首先将DataContext设置为此类,然后在xaml文件中指定如下所示的绑定:

<ComboBox ItemsSource="{Binding Path=EffectStyle}"/>

但这不会在ComboBoxas项目中显示枚举值。


9
这是您要寻找的内容:WPF ObjectDataProvider-将枚举绑定到ComboBox您也可以从此处下载完整的源代码示例。

最好的答案在我看来是: stackoverflow.com/questions/58743/...
gimpy

Answers:


306

您可以通过将以下代码放入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上阅读的名称空间和程序集。


1
从第一个链接测试示例,即可正常工作。请参阅我的答案中添加的代码和注释。
Kyrylo M

1
在MSDN论坛(social.msdn.microsoft.com/Forums/en/wpf/thread/…)上找到了您的问题。尝试清理和重建项目。也许您应该在另一个问题上在这里提出该问题。这是我唯一可以建议的...无论如何,显示的示例是正确的。
Kyrylo M

1
谢谢,这很奇怪,但是我看到过类似的东西,而且带有WPF疯狂。会做,让你知道。顺便说一句,这里描述的是同样的问题:social.msdn.microsoft.com/Forums/en-US/wpf/thread/…–
琼·

2
您需要添加对它的引用并添加xmlns:DllAlias="clr-namespace:NamespaceInsideDll; assembly=DllAssemblyName"XAML以使用它。这是指南:msdn.microsoft.com/en-us/library/ms747086.aspx
Kyrylo M

4
您可以使用ReSharper之类的工具。它解析所有引用的程序集,并给出需要包括的建议。无需编写-只需从选项中选择即可。
Kyrylo M

116

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

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

当我想将绑定Enum到时ComboBox,我要显示的文本永远不会与的值匹配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,
  ...
}

首先,我创建了带有几个处理枚举的方法的帮助器类。一种方法获取特定值的描述,另一种方法获取类型的所有值及其描述。

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个属性View即可绑定到SelectedValueItemsSource组合框:

private DayOfWeek dayOfWeek;

public DayOfWeek SelectedDay
{
  get { return dayOfWeek; }
  set
  {
    if (dayOfWeek != value)
    {
      dayOfWeek = value;
      OnPropertyChanged(nameof(SelectedDay));
    }
  }
}

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

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

要实现此解决方案,您只需要复制我的EnumHelper课程和EnumToCollectionConverter课程。他们将与任何枚举一起工作。另外,我没有在此处包括它,但是ValueDescription该类只是一个简单的类,具有2个公共对象属性,一个称为Value,一个称为Description。您可以自己创建,也可以更改代码以使用Tuple<object, object>KeyValuePair<object, object>


9
为了使这项工作,我不得不创建一个ValueDescription具有公共属性的类ValueDescription
Perchik

4
是的,您也可以更改此代码以使用Tuple<T1, T2>or或or KeyValuePair<TKey, TValue>代替ValueDescription该类,这样就不必创建自己的类。
尼克

我需要为两个ViewModel属性(而不仅仅是SelectedClass)实现OnPropertyChanged(或等效方法)。
2016年

您不需要为返回列表的属性实现OnPropertyChanged。该列表是根据枚举中的值生成的。它在运行时将永远不会更改,并且当它永远不会更改时,就不需要通知任何人它已更改。此外,对于更新的版本,甚至根本不需要list属性。
尼克

组合框的ItemSource和SelectedValue如何具有相同的属性。ItemsSource是否不需要是列表?哦,我知道了,这是因为EnumHelper创建了一个对象列表。这实际上使我的ViewModel更简单,因为我不必维护单独的对象列表来填充ItemSource。
Stealth Rabbi

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() });
        }
    }
  2. 几乎就是所有...现在在XAML中使用它:

        <ComboBox DisplayMemberPath="DisplayName"
              ItemsSource="{persons:EnumToItemsSource {x:Type enums:States}}"
              SelectedValue="{Binding Path=WhereEverYouWant}"
              SelectedValuePath="Value" />
  3. 将“ enums:States”更改为您的枚举


1
@Nick:可接受的答案也是在xaml中引用枚举(或您所说的模型)。您的解决方案是在视图模型中创建2个属性和支持字段,我不喜欢(DRY规则)。当然,您不必使用e.ToString()显示名称。您可以使用自己的转换器,descrtiption属性解析器。
tom.maruska

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

@Nick是的,您对这个属性和支持字段是正确的:)
tom.maruska

24

使用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}}"

根据本文


4
完美的解决方案。系统的命名空间,如kirmir的回答:xmlns:System="clr-namespace:System;assembly=mscorlib"
Jonathan Twite '16

在Visual Studio 2017的WPF项目中效果很好
。– Sorush

10

Nick的回答确实对我有所帮助,但我意识到可以对其进行一些微调,以避免额外的类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>();
   }
}

最后是XAML:

<ComboBox ItemSource="{Binding Path=PlayerClassList}"
          DisplayMemberPath="Value"
          SelectedValuePath="Key"
          SelectedValue="{Binding Path=SelectedClass}" />

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


我的第一个实现确实使用了a,KeyValuePair但最终我决定使用a KeyValuePair来表示不是键值对的某些东西,只是为了避免编写平凡的简单类并没有多大意义。该ValueDescription班只有5条线路,其中2只是{}
尼克

8

您将需要在枚举中创建值的数组,可以通过调用System.Enum.GetValues()来创建该数组,并将所需Type项的枚举传递给它。

如果为该ItemsSource属性指定此属性,则应使用所有枚举的值填充该属性。您可能想绑定SelectedItemEffectStyle它(假设它是同一个枚举的属性,并且包含当前值)。


谢谢,您能显示代码的第一部分吗?我不确定将枚举值存储在哪里?枚举属性位于另一个类中。我可以在xaml中执行此GetValues步骤吗?
琼·芬格

4

以上所有帖子都错过了一个简单的技巧。可以从SelectedValue的绑定中找出如何自动地填充ItemsSource,以便XAML标记正确。

<Controls:EnumComboBox SelectedValue="{Binding Fool}"/>

例如在我的ViewModel中

public enum FoolEnum
    {
        AAA, BBB, CCC, DDD

    };


    FoolEnum _Fool;
    public FoolEnum Fool
    {
        get { return _Fool; }
        set { ValidateRaiseAndSetIfChanged(ref _Fool, value); }
    }

ValidateRaiseAndSetIfChanged是我的INPC挂钩。您的可能会有所不同。

EnumComboBox的实现如下,但是首先我需要一个小帮手来获取我的枚举字符串和值

    public static List<Tuple<object, string, int>> EnumToList(Type t)
    {
        return Enum
            .GetValues(t)
            .Cast<object>()
            .Select(x=>Tuple.Create(x, x.ToString(), (int)x))
            .ToList();
    }

和主类(请注意,我正在使用ReactiveUI通过WhenAny来挂接属性更改)

using ReactiveUI;
using ReactiveUI.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Windows;
using System.Windows.Documents;

namespace My.Controls
{
    public class EnumComboBox : System.Windows.Controls.ComboBox
    {
        static EnumComboBox()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(EnumComboBox), new FrameworkPropertyMetadata(typeof(EnumComboBox)));
        }

        protected override void OnInitialized( EventArgs e )
        {
            base.OnInitialized(e);

            this.WhenAnyValue(p => p.SelectedValue)
                .Where(p => p != null)
                .Select(o => o.GetType())
                .Where(t => t.IsEnum)
                .DistinctUntilChanged()
                .ObserveOn(RxApp.MainThreadScheduler)
                .Subscribe(FillItems);
        }

        private void FillItems(Type enumType)
        {
            List<KeyValuePair<object, string>> values = new List<KeyValuePair<object,string>>();

            foreach (var idx in EnumUtils.EnumToList(enumType))
            {
                values.Add(new KeyValuePair<object, string>(idx.Item1, idx.Item2));
            }

            this.ItemsSource = values.Select(o=>o.Key.ToString()).ToList();

            UpdateLayout();
            this.ItemsSource = values;
            this.DisplayMemberPath = "Value";
            this.SelectedValuePath = "Key";

        }
    }
}

您还需要在Generic.XAML中正确设置样式,否则您的框将不会呈现任何内容,您将把头发拉出来。

<Style TargetType="{x:Type local:EnumComboBox}" BasedOn="{StaticResource {x:Type ComboBox}}">
</Style>

就是这样。显然可以将其扩展为支持i18n,但会使职位更长。


3

通用应用程序的工作方式似乎有所不同。它没有功能齐全的XAML的所有功能。对我有用的是:

  1. 我创建了一个枚举值列表作为枚举(不转换为字符串或整数),并将ComboBox ItemsSource绑定到该列表。
  2. 然后,我可以将ComboBox ItemSelected绑定到我的公共属性,该属性的类型为有问题的枚举

只是为了好玩,我整理了一个模板化类来帮助解决这个问题,并将其发布到MSDN Samples页面。多余的位使我可以选择覆盖枚举的名称,并让我隐藏一些枚举。我的代码看起来很像Nick的代码(如上),我希望我之前已经看过。

运行样本; 它包括对枚举的多个双向绑定


3

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

给定一个枚举...

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
}

还有一个价值转换器...

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

资源...

    <local:ImageFormatValueConverter x:Key="ImageFormatValueConverter"/>

XAML声明...

    <ComboBox Grid.Row="9" ItemsSource="{Binding Source={StaticResource ImageFormatValueConverter}, Path=Strings}"
              SelectedItem="{Binding Format, Converter={StaticResource ImageFormatValueConverter}}"/>

查看模型...

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

结果组合框...

ComboBox绑定到枚举


对我来说,这是解决该问题的最佳方法:简单,易于理解,易于实现。
式的

该解决方案的问题在于它不可本地化。
罗宾·戴维斯

@RobinDavies您可以对其进行本地化。需要自定义的DescriptionAttribute,我已经构建了一些。见这太问题的一些想法:stackoverflow.com/questions/7398653/...
AQuirky

2
public class EnumItemsConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (!value.GetType().IsEnum)
            return false;

        var enumName = value.GetType();
        var obj = Enum.Parse(enumName, value.ToString());

        return System.Convert.ToInt32(obj);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return Enum.ToObject(targetType, System.Convert.ToInt32(value));
    }
}

如果直接绑定到枚举对象模型属性,则应使用此类Enum值转换器扩展Rogers和Greg的答案。


1

如果要绑定到ViewModel上的实际枚举属性,而不是枚举的int表示形式,则事情会很棘手。我发现有必要绑定到字符串表示形式,而不是上面所有示例中期望的int值。

您可以通过将一个简单的文本框绑定到要在ViewModel上绑定到的属性来判断是否是这种情况。如果显示文本,请绑定到字符串。如果显示数字,请绑定到该值。注意我曾经两次使用Display,这通常是一个错误,但这是它唯一的工作方式。

<ComboBox SelectedValue="{Binding ElementMap.EdiDataType, Mode=TwoWay}"
                      DisplayMemberPath="Display"
                      SelectedValuePath="Display"
                      ItemsSource="{Binding Source={core:EnumToItemsSource {x:Type edi:EdiDataType}}}" />

格雷格


这个答案似乎是不完整的:*什么是/ core /?
trapicki 2014年

1

我喜欢tom.maruska的答案,但是我需要支持模板在运行时可能遇到的任何枚举类型。为此,我必须使用绑定来指定标记扩展的类型。我可以通过nicolay.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("_", " ");
    }
}

1

简单明了的解释:http : //brianlagunas.com/a-better-way-to-data-bind-enums-in-wpf/

xmlns:local="clr-namespace:BindingEnums"
xmlns:sys="clr-namespace:System;assembly=mscorlib"

...

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

...

<Grid>
    <ComboBox HorizontalAlignment="Center" VerticalAlignment="Center" MinWidth="150"
              ItemsSource="{Binding Source={StaticResource dataFromEnum}}"/>
</Grid>

0

使用ReactiveUI,我创建了以下替代解决方案。这不是一个优雅的多合一解决方案,但我认为至少它是可读的。

就我而言,将列表绑定enum到控件是很少见的,因此我不需要在整个代码库中扩展解决方案。但是,可以通过将代码更改EffectStyleLookup.Item为来使代码更通用Object。我用我的代码对其进行了测试,不需要其他修改。这意味着一个助手类可以应用于任何enum列表。尽管那样会降低其可读性-对其影响ReactiveList<EnumLookupHelper>不大。

使用以下帮助程序类:

public class EffectStyleLookup
{
    public EffectStyle Item { get; set; }
    public string Display { get; set; }
}

在ViewModel中,转换枚举列表并将其公开为属性:

public ViewModel : ReactiveObject
{
  private ReactiveList<EffectStyleLookup> _effectStyles;
  public ReactiveList<EffectStyleLookup> EffectStyles
  {
    get { return _effectStyles; }
    set { this.RaiseAndSetIfChanged(ref _effectStyles, value); }
  }

  // See below for more on this
  private EffectStyle _selectedEffectStyle;
  public EffectStyle SelectedEffectStyle
  {
    get { return _selectedEffectStyle; }
    set { this.RaiseAndSetIfChanged(ref _selectedEffectStyle, value); }
  }

  public ViewModel() 
  {
    // Convert a list of enums into a ReactiveList
    var list = (IList<EffectStyle>)Enum.GetValues(typeof(EffectStyle))
      .Select( x => new EffectStyleLookup() { 
        Item = x, 
        Display = x.ToString()
      });

    EffectStyles = new ReactiveList<EffectStyle>( list );
  }
}

在中ComboBox,利用SelectedValuePath属性绑定到原始enum值:

<ComboBox Name="EffectStyle" DisplayMemberPath="Display" SelectedValuePath="Item" />

在View中,这使我们可以将原始对象绑定enumSelectedEffectStyleViewModel中,但是在中显示ToString()ComboBox

this.WhenActivated( d =>
{
  d( this.OneWayBind(ViewModel, vm => vm.EffectStyles, v => v.EffectStyle.ItemsSource) );
  d( this.Bind(ViewModel, vm => vm.SelectedEffectStyle, v => v.EffectStyle.SelectedValue) );
});

我认为您的ViewModel错误。1)不应该是EffectStyleLookup的ReactiveList吗?,2)您应该首先创建一个空的ReactiveList <T>()。然后添加项目。最后:ReactiveList <T>现在已弃用(但仍然有效)。EffectStyles =新的ReactiveList <EffectStyleLookup>(); EffectStyles.AddRange(list); 感谢您抽出宝贵的时间来展示此内容。
user1040323 '19

0

我要添加我的评论(可悲的是,在VB中,但是这个概念很容易在心跳中复制到C#中),因为我只需要引用此内容,并且不喜欢任何答案,因为它们太复杂了。不必那么困难。

所以我想出了一种简单的方法。将枚举器绑定到字典。将该字典绑定到组合框。

我的组合框:

<ComboBox x:Name="cmbRole" VerticalAlignment="Stretch" IsEditable="False" Padding="2" 
    Margin="0" FontSize="11" HorizontalAlignment="Stretch" TabIndex="104" 
    SelectedValuePath="Key" DisplayMemberPath="Value" />

我的后台代码。希望这可以帮助其他人。

Dim tDict As New Dictionary(Of Integer, String)
Dim types = [Enum].GetValues(GetType(Helper.Enumerators.AllowedType))
For Each x As Helper.Enumerators.AllowedType In types
    Dim z = x.ToString()
    Dim y = CInt(x)
    tDict.Add(y, z)
Next

cmbRole.ClearValue(ItemsControl.ItemsSourceProperty)
cmbRole.ItemsSource = tDict

Kyrylo的答案比您的答案简单得多-我不知道这有什么复杂之处?他要求代码转换为零。
Johnathon Sullinger

我不想将所有逻辑都交给XAML。我更喜欢以自己的方式来做逻辑(并非总是最好的方式),但是它使我能够理解某些地方和计划无法按计划进行的原因。他的方法不太复杂,但是依靠XAML / WPF进行逻辑处理。我只是不喜欢那个。10,000种为猫皮的方法,您知道吗?
Laki Politis

很公平。我个人更喜欢使用开箱即用的内置功能,但这只是我的偏爱;)每个人都有!
Johnathon Sullinger

是的先生!我完全明白。我被迫从事来自Web开发的软件开发。我还没有掌握WPF的最新知识,因此不得不学习很多东西。我仍然不了解WPF / XAML控件的所有复杂性,因此在我希望事情如何运作方面,我发现比解决方案更多的麻烦。但我很感谢这次谈话。这让我做了更多的研究。
Laki Politis

0

尼克的解决方案可以进一步简化,没有花哨的事情,您只需要一个转换器即可:

[ValueConversion(typeof(Enum), typeof(IEnumerable<Enum>))]
public class EnumToCollectionConverter : MarkupExtension, IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var r = Enum.GetValues(value.GetType());
        return r;
    }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return null;
    }
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return this;
    }
}

然后,您可以在希望出现组合框的任何地方使用它:

<ComboBox ItemsSource="{Binding PagePosition, Converter={converter:EnumToCollectionConverter}, Mode=OneTime}"  SelectedItem="{Binding PagePosition}" />

0

我不建议直接执行此操作,但希望这可以激发一个好的解决方案。

假设您的枚举是Foo。然后,您可以执行类似的操作。

public class FooViewModel : ViewModel
{
    private int _fooValue;

    public int FooValue
    {
        get => _fooValue;
        set
        {
            _fooValue = value;
            OnPropertyChange();
            OnPropertyChange(nameof(Foo));
            OnPropertyChange(nameof(FooName));
        }
    }
    public Foo Foo 
    { 
        get => (Foo)FooValue; 
        set 
        { 
            _fooValue = (int)value;
            OnPropertyChange();
            OnPropertyChange(nameof(FooValue));
            OnPropertyChange(nameof(FooName));
        } 
    }
    public string FooName { get => Enum.GetName(typeof(Foo), Foo); }

    public FooViewModel(Foo foo)
    {
        Foo = foo;
    }
}

然后在Window.Load方法上,您可以将所有枚举加载到ObservableCollection<FooViewModel>,您可以将其设置为组合框的DataContext。


0

我只是保持简单。我在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}">
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.