c# 버튼 모양 - WPF에서 콤보 상자 컨트롤에 열거 형을 바인딩하는 방법?




7 Answers

내가 바인딩하는 모든 객체가 ViewModel 정의되어 있으므로 가능하면 xaml에 <ObjectDataProvider> 를 사용하지 않는 것이 좋습니다.

내 솔루션은 뷰에 정의 된 데이터를 사용하지 않으며 코드 숨김도 없습니다. DataBinding, 재사용 가능한 ValueConverter, 모든 Enum 유형에 대한 설명 모음을 가져 오는 메소드 및 바인딩 할 ViewModel의 단일 속성.

EnumComboBox 에 바인딩하려면 표시 할 텍스트가 Enum 값과 일치하지 않으므로 [Description()] 특성을 사용하여 실제로 ComboBox 에서 보려는 텍스트를 제공합니다. 게임에서 캐릭터 클래스 열거 형을 사용하면 다음과 같이 보일 것입니다.

public enum PlayerClass
{
  // add an optional blank value for default/no selection
  [Description("")]
  NOT_SET = 0,
  [Description("Shadow Knight")]
  SHADOW_KNIGHT,
  ...
}

먼저 열거 형을 처리하는 두 가지 방법으로 도우미 클래스를 만들었습니다. 하나의 메소드는 특정 값에 대한 설명을 가져오고, 다른 메소드는 모든 값과 해당 유형에 대한 설명을 가져옵니다.

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 PlayerClass playerClass;

public PlayerClass SelectedClass
{
  get { return playerClass; }
  set
  {
    if (playerClass != value)
    {
      playerClass = value;
      OnPropertyChanged(nameof(SelectedClass));
    }
  }
}

마지막으로 ( ItemsSource 바인딩의 ValueConverter 를 사용하여) ComboBox 보기를 바인딩합니다.

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

이 솔루션을 구현하려면 EnumHelper 클래스와 EnumToCollectionConverter 클래스를 복사하면됩니다. 그들은 어떤 enum과도 작동 할 것입니다. 또한 여기에 포함시키지 않았지만 ValueDescription 클래스는 Value 라는 속성, Description 이라는 Description 이라는 2 개의 공용 객체 속성이있는 단순한 클래스입니다. 직접 만들거나 Tuple<object, object> 또는 KeyValuePair<object, object> 를 사용하도록 코드를 변경할 수 있습니다.

custom combobox style

enum이있는 그대로 간단한 예제를 찾으려고합니다. 필자가 보았던 모든 예제는 멋진 디스플레이 문자열을 추가하려고 시도하지만 그 복잡성을 원하지 않습니다.

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

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

하지만이 항목으로 ComboBox 열거 형 값을 표시하지 않습니다.




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



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

ItemsSource 속성에이 값을 지정하면 모든 Enum 값으로 채워야합니다. SelectedItemEffectStyle 에 바인딩하려고합니다 (동일한 열거 형의 속성이고 현재 값을 포함한다고 가정).




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의 대답을 확장해야합니다.




열거 형의 int 표현이 아닌 ViewModel의 실제 enum 속성에 바인딩하는 경우 작업이 까다로워집니다. 위의 모든 예에서 예상되는대로 int 값이 아닌 문자열 표현에 바인딩해야한다는 것을 알았습니다.

이 경우에 ViewModel에 바인딩하려는 속성에 간단한 텍스트 상자를 바인딩하여 알 수 있습니다. 텍스트가 표시되면 문자열에 바인딩하십시오. 숫자가 있으면 값에 바인딩하십시오. 참고 디스플레이를 두 번 사용했는데 일반적으로 오류가 발생하지만 작동하는 유일한 방법입니다.

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

그렉




이 질문에 대한 여러 가지 우수한 답변이 있으며 겸허하게 내 의견을 제출합니다. 나는 내 것이 다소 단순하고 우아하다는 것을 알았다. 값 변환기 만 필요합니다.

열거 형을 감안할 때 ...

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

결과 콤보 박스 ...




내 의견을 추가 할 수 있습니다 (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



Related