列挙型をWPFのコンボボックスコントロールにバインドする方法


182

列挙型がそのまま表示される簡単な例を見つけようとしています。私が見たすべての例は見栄えの良い表示文字列を追加しようとしていますが、その複雑さは望んでいません。

基本的に、最初にDataContextをこのクラスに設定し、次にxamlファイルで次のようなバインディングを指定することにより、バインドするすべてのプロパティを保持するクラスがあります。

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

しかし、これはComboBoxasアイテムの列挙値を表示しません。


9
:ここでは何を探しているコンボボックスに列挙型をバインド- WPFたObjectDataProviderあなたはまた、そこからの完全なソースコードの例をダウンロードすることができます。

:私の意見ではベストの答えはである stackoverflow.com/questions/58743/...
gimpy

回答:


306

Loadedたとえば、次のコードをWindow イベントハンドラーに配置することで、コードから実行できます。

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/...
ジョアンVenge

2
それへの参照を追加しxmlns:DllAlias="clr-namespace:NamespaceInsideDll; assembly=DllAssemblyName"、それを使用するためにXAMLを追加する必要があります。ガイドは次のとおりです。msdn.microsoft.com
en

4
ReSharperなどのツールを使用できます。参照されるすべてのアセンブリを解析し、何を含める必要があるかを提案します。書く必要はありません-オプションから選択するだけです。
Kyrylo M

117

私はバインドしているすべてのオブジェクトをmy ViewModelで定義するのが好きなので<ObjectDataProvider>、可能な場合はxamlでの使用を避けます。

私のソリューションでは、ビューで定義されたデータも分離コードも使用していません。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,
  ...
}

最初に、列挙型を処理するためのいくつかのメソッドを含むヘルパークラスを作成しました。1つのメソッドは特定の値の説明を取得し、もう1つのメソッドはすべての値と型のそれらの説明を取得します。

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とコンボボックスのView両方にバインドできる1つのプロパティが必要です: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、と呼ばれる1 Description。あなたはそれを自分で作成することができたり、あなたが使用するコードを変更することができますTuple<object, object>KeyValuePair<object, object>


9
この作品を作るために、私が作成しなければならなかったValueDescriptionためにパブリックプロパティを持つクラスをValueしてDescription
Perchik

4
はい、クラスの代わりにTuple<T1, T2>orまたはor を使用するようにこのコードを変更することもできます。そうすれば、独自のコードを作成する必要はありません。KeyValuePair<TKey, TValue>ValueDescription
ニック

SelectedClassだけでなく、両方のViewModelプロパティにOnPropertyChanged(または同等のもの)を実装する必要がありました。
ウィル

リストを返すプロパティにOnPropertyChangedを実装する必要はありません。リストはEnumの値から生成されます。実行時に変更されることはなく、変更されない場合、変更されたことを誰かに通知する必要もありません。また、更新されたバージョンでは、リストプロパティはまったく必要ありません。
ニック

コンボボックスのItemSourceとSelectedValueはどのように同じプロパティですか?ItemsSourceはリストである必要はありませんか?ああ、なるほど、EnumHelperがオブジェクトのリストを作成するためです。これにより、ItemSourceに入力するためにオブジェクトの個別のリストを維持する必要がないため、実際にはViewModelがより単純になります。
ステルスラビ

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つのプロパティとバッキングフィールドを作成することです。そしてもちろん、e.ToString()表示名に使用する必要はありません。独自のトランスレータ、記述属性パーサーなどを使用できます。
tom.maruska

2
@ tom.maruska私は私の答えとあなたの答えを理解するつもりはありませんが、あなたがそれを育てたので、2つのプロパティを持つことは、異なる目的に役立つ2つの異なるプロパティである場合、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

Visual Studio 2017のWPFのプロジェクトに適しています
Sorush

11

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が、結局私はKeyValuePair、単純なクラスの記述を避けるために、キーと値のペアではないものを表すためにa を使用することにしました。ValueDescriptionクラスは唯一の5行で、そのうちの2だけある{}
ニック・

8

列挙型の値の配列を作成する必要があります。これは、System.Enum.GetValues()を呼び出して、Type必要な列挙型のを渡すことで作成できます。

これをItemsSourceプロパティに指定する場合は、列挙型のすべての値を入力する必要があります。おそらくバインドSelectedItemしたいでしょうEffectStyle(同じ列挙型のプロパティであり、現在の値が含まれていると仮定します)。


ありがとう、コードの最初の部分を見せてくれませんか?列挙型の値を配列としてどこに保存するかわかりませんか?enumプロパティは別のクラスにあります。xaml内でこのGetValuesステップを実行できますか?
Joan

4

上記の投稿はすべて、単純なトリックを逃しています。SelectedValueのバインディングから、XAMLマークアップが正しくなるように、ItemsSourceを自動的に設定する方法を見つけることができます。

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

とメインクラス(WhenAnyを介してプロパティの変更をフックするためにReactiveUIを使用していることに注意してください)

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. enumとして列挙値のリストを作成し(文字列や整数に変換されません)、ComboBox ItemsSourceをそれにバインドしました
  2. 次に、ComboBox ItemSelectedを、問題の列挙型であるパブリックプロパティにバインドできます。

おもしろいことに、これを支援するために小さなテンプレートクラスを作成し、MSDNサンプルページに公開しました。追加のビットにより、オプションで列挙型の名前をオーバーライドし、列挙型の一部を非表示にすることができます。私のコードはニック(上記)のようにひどく見えます。

サンプルを実行します。 列挙型への複数の双方向バインディングが含まれています


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


私にとって、これは質問に対する最良の解決策です。シンプルで理解しやすく、実装が簡単です。
Informagic

このソリューションの問題は、ローカライズできないことです。
Robin Davies

ローカライズできる@RobinDavies。私がいくつか作成したカスタムDescriptionAttributeが必要です。いくつかのアイデアのために、このSOの質問を参照してください: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オブジェクトモデルプロパティに直接バインドする場合は、このような種類のEnum値コンバーターを使用して、RogersとGregの答えを拡張する必要があります。


1

列挙型のint表現ではなく、ViewModel上の実際の列挙型プロパティにバインドしている場合は、事態が複雑になります。上記のすべての例で期待されているint値ではなく、文字列表現にバインドする必要があることがわかりました。

これが当てはまるかどうかは、ViewModelでバインドするプロパティに単純なテキストボックスをバインドすることで確認できます。テキストが表示されている場合は、文字列にバインドします。数値が表示されている場合は、値にバインドします。注:通常はエラーになる2回ディスプレイを使用しましたが、それが機能する唯一の方法です。

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

グレッグ


この回答は不完全のようです:* / core /とは何ですか?
trapicki 14年

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。私は自分のコードでテストしましたが、他の変更は必要ありません。つまり、1つのヘルパークラスを任意の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 );
  }
}

ComboBoxSelectedValuePathプロパティを使用して、元のenum値にバインドします。

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

ビューでは、これによりオリジナル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 = new ReactiveList <EffectStyleLookup>(); EffectStyles.AddRange(list); これを示すために時間を割いていただきありがとうございます。
user1040323

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通りありますよね?
ラキポリティス2016年

けっこうだ。私は個人的には、既成の機能をそのまま使用することを好みますが、それは私の好みです;)それぞれが所有しています!
Johnathon Sullinger、2016年

かしこまりました!私は完全に理解しています。私はウェブ開発から来るソフトウェア開発を余儀なくされました。私はWPFについての最新情報を持っていないので、これまでに行ってきたように多くを学ぶ必要がありました。私はまだWPF / XAMLコントロールのすべての複雑さを理解していないので、私は物事がどのように機能するかを期待する方法の解決策よりも多くのしゃっくりを見つけてきました。しかし、私はこの会話に感謝します。それは私にいくつかのより多くの研究をさせるようになりました。
Laki Politis 2016年

0

Nickのソリューションはもっと単純化することができ、派手なことはありません。必要なのは単一のコンバーターだけです。

[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}">
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.