[C#] WPF에서 열거 형을 콤보 상자 컨트롤에 바인딩하는 방법은 무엇입니까?

열거 형이 그대로 표시되는 간단한 예를 찾으려고합니다. 내가 본 모든 예제는 멋진 표시 문자열을 추가하려고 시도하지만 그 복잡성을 원하지 않습니다.

기본적으로 DataContext를이 클래스로 설정 한 다음 xaml 파일에서 이와 같은 바인딩을 지정하여 바인딩하는 모든 속성을 보유하는 클래스가 있습니다.

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

그러나 이것은 ComboBoxas 항목 에 열거 형 값을 표시하지 않습니다 .



답변

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에서 읽을 수있는 네임 스페이스 및 어셈블리를 매핑하는 방법에 대해 설명합니다 .


답변

바인딩하는 모든 객체가 my에 정의되기를 원 ViewModel하므로 <ObjectDataProvider>가능한 경우 xaml에서 사용하지 않으려 고 합니다.

내 솔루션은 View에 정의 된 데이터와 코드 숨김을 사용하지 않습니다. DataType, 재사용 가능한 ValueConverter, Enum 유형에 대한 설명 모음을 가져 오는 메소드 및 바인딩 할 ViewModel의 단일 특성 만 있습니다.

내가 바인딩 할 때 EnumA와 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하나 와 콤보 상자 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>


답변

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’를 열거 형으로 변경하십시오.


답변

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

이 기사를 기반으로


답변

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

이것이 다른 사람들에게 도움이되기를 바랍니다.


답변

열거 형에 값의 배열을 만들어야합니다. 열거 형은 System.Enum.GetValues ​​() 를 호출하여 만들 수 있으며 , Type원하는 열거 형의 항목을 전달합니다.

ItemsSource속성 에이 속성 을 지정하면 모든 열거 형 값으로 채워 져야합니다. 당신은 아마 바인딩 할 SelectedItemEffectStyle(이 같은 열거의 속성이며, 현재의 값이 포함 된 가정).


답변

위의 모든 게시물은 간단한 트릭을 놓쳤습니다. 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을 지원하도록 확장 될 수 있지만 게시물이 더 길어질 것입니다.