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


182

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

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

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

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


9
WPF ObjectDataProvider-열거 형을 ComboBox에 바인딩 여기 에서 전체 소스 코드 예제를 다운로드 할 수도 있습니다.

내 의견으로는 가장 좋은 답변은 다음과 같습니다. 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 광기와 비슷한 것들을 보았습니다. 하고 당신에게 알려줄 것입니다. Btw는 여기에 설명 된 것과 동일한 문제입니다. social.msdn.microsoft.com/Forums/en-US/wpf/thread/…
Joan Venge

2
참조를 추가 xmlns:DllAlias="clr-namespace:NamespaceInsideDll; assembly=DllAssemblyName"하고 XAML을 추가 하여 사용해야합니다. 다음은 가이드입니다. msdn.microsoft.com/en-us/library/ms747086.aspx
Kyrylo M

4
ReSharper와 같은 도구를 사용할 수 있습니다. 참조 된 모든 어셈블리를 구문 분석하고 포함해야 할 사항을 제안합니다. 쓸 필요가 없습니다-옵션에서 선택하십시오.
Kyrylo M

117

바인딩하는 모든 객체가 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>


9
이 일을하려면, 나는 만들어야했습니다 ValueDescription공공 속성이 클래스 ValueDescription
Perchik

4
예, Tuple<T1, T2>또는 클래스 KeyValuePair<TKey, TValue>대신 또는 ValueDescription클래스 를 사용하도록이 코드를 변경할 수 있으며 직접 만들 필요는 없습니다.
Nick

SelectedClass뿐만 아니라 두 ViewModel 속성 모두에 OnPropertyChanged (또는 이와 동등한)를 구현해야했습니다.
Will

목록을 반환하는 속성에 대해서는 OnPropertyChanged를 구현할 필요가 없습니다. 이 목록은 Enum의 값에서 생성됩니다. 런타임 중에는 변경되지 않으며 변경되지 않을 때 변경된 사람에게 알리지 않아도됩니다. 또한 업데이트 된 버전에서는 list 속성이 전혀 필요하지 않습니다.
Nick

콤보 박스의 ItemSource와 SelectedValue는 어떻게 같은 속성입니까? ItemsSource가 목록 일 필요는 없습니까? 아, EnumHelper가 객체 목록을 만드는 것은 당황합니다. ItemSource를 채우기 위해 별도의 객체 목록을 유지할 필요가 없기 때문에 실제로 ViewModel이 더 단순 해집니다.
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에서 열거 형 (또는 말한대로 모델)을 참조하는 것입니다. 귀하의 솔루션은 (DRY 규칙)을 좋아하지 않는 뷰 모델에서 2 개의 속성과 백업 필드를 생성하고 있습니다. 물론 e.ToString()표시 이름 으로 사용할 필요는 없습니다 . 자신 만의 번역기, 설명 속성 파서를 사용할 수 있습니다.
tom.maruska

2
@ tom.maruska 나는 내 대답에 대한 답변을 얻으려고하지 않지만 그것을 가져 왔기 때문에 2 개의 속성을 갖는 것이 다른 목적을 제공하는 2 개의 고유 속성 인 DRY 규칙을 위반하지 않습니다. 그리고 답은 속성을 추가해야합니다 (이 속성을 직접 호출 {Binding Path=WhereEverYouWant}했음). 양방향 바인딩을 지원하려면 백업 필드가 있어야합니다. 따라서이 작업을 수행하여 2 개의 속성과 1 개의 백업 필드를 바꾸지 않고 단일 행 읽기 전용 속성을 1 개만 바꿉니다.
Nick

예 @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

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

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


첫 번째 구현은을 사용 KeyValuePair했지만 결국 KeyValuePair에는 간단한 값을 사용하지 않는 단순한 클래스를 작성하지 않기 위해 키-값 쌍이 아닌 것을 나타내는 데 사용하기로 결정했습니다 . ValueDescription클래스는 5 선이며, 그 중 2는 있습니다 {}

8

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

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


감사합니다. 코드의 첫 부분을 보여 주시겠습니까? 열거 형 값을 배열로 저장할 위치를 잘 모르겠습니까? 열거 형 속성은 다른 클래스에 있습니다. xaml 내에서이 GetValues ​​단계를 수행 할 수 있습니까?
Joan Venge

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. 열거 형 값 목록을 열거 형으로 만들거나 (문자열이나 정수로 변환하지 않음) ComboBox ItemsSource를 바인딩했습니다.
  2. 그런 다음 ComboBox ItemSelected를 해당 유형이 열거 형인 공용 속성에 바인딩 할 수 있습니다

재미를 위해 이것에 도움이되는 작은 템플릿 클래스를 만들어 MSDN 샘플 페이지에 게시했습니다 . 여분의 비트를 사용하면 열거 형 이름을 선택적으로 재정의하고 열거 형 중 일부를 숨길 수 있습니다. 내 코드는 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


저에게는 이것이 질문에 대한 최상의 해결책입니다. 간단하고 이해하기 쉽고 구현하기 쉽습니다.
Informagic

이 솔루션의 문제점은 지역화 할 수 없다는 것입니다.
로빈 데이비스

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

열거 형 개체 모델 속성에 직접 바인딩하는 경우 이러한 종류의 열거 형 값 변환기를 사용하여 Rogers와 Greg의 대답을 확장해야합니다.


1

열거 형의 int 표현이 아닌 ViewModel의 실제 열거 형 속성에 바인딩하는 경우 까다로워집니다. 위의 모든 예제에서 예상되는 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

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.ItemObject. 내 코드로 테스트했지만 다른 수정이 필요하지 않습니다. 이는 하나의 도우미 클래스가 모든 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에서는 ViewModel에서 원본 enum을 에 바인딩 할 수 SelectedEffectStyle있지만 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 <효과 스타일 조회> (); 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 가지 방법입니다.
Laki Politis

그럴 수 있지. 나는 개인적으로 이미 내장 된 기능을 사용하는 것을 선호하지만 그것은 저의 선호입니다.
Johnathon Sullinger

알겠습니다! 나는 완전히 이해한다. 나는 웹 개발에서 나오는 소프트웨어 개발을 강요 받았다. 나는 WPF에 최신이 아니었고 내가 갔을 때 많이 배워야했습니다. 나는 여전히 WPF / XAML 컨트롤의 모든 복잡한 점을 이해하지 못하므로 일이 어떻게 작동 할 것인지에 대한 솔루션보다 더 많은 문제를 발견했습니다. 그러나 나는이 대화에 감사한다. 좀 더 연구하게 만들었습니다.
Laki Politis

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.