如何将枚举动态绑定到下拉列表[关闭]

问题描述 投票:0回答:1

我的 WPF 应用程序中有下拉菜单,其中的选择是基于其他控件的选定值的动态选择。

目前我正在对选择进行硬编码,例如-

if (dropDown1.SelectedValue == "Name 1")
      dropDown2.ItemSource = new List<string>() { "one 1", "Two 2", "Three 3" };
else
      dropDown2.ItemSource = new List<string>() { "one 1", "Three 3" };

但是,在整个应用程序中,下拉项中的更改可能必须复制多达 100 个位置。因此,我正在研究使用

Enum
s 来填充其选择取决于第一个值的所有控件的选择列表,而不是使用静态文本。

在这种情况下,我有两个问题

  1. 是否可以通过数据绑定轻松地将
    Enum
    属性绑定到下拉列表(例如
    ComboBox
    ),这个规模如何?
  2. 如果我将
    Enum
    属性绑定到
    ComboBox
    我如何自定义标签以使其易于阅读,例如在视图中使用空格(例如:“one 1”)?
c# wpf mvvm enums dropdown
1个回答
0
投票

使用

Enum
来填充下拉值绝对是可能的,而且我一直在做。尽管有多种可能的方法,但我首选的方法是使用绑定和两个转换器 - 一个将
Enum
值转换为可以绑定到
ItemsSource
的数组,另一个转换单个选定值。可以通过使用
DescriptionAttribute
属性来处理空格。这是代码:

这是

Enum
值的支持视图模型:

internal class EnumViewModel
{
    public EnumViewModel(Enum value)
    {
        this.Value = value;
        var backingField = value.GetType().GetField(value.ToString());
        var attr = backingField.GetCustomAttribute<DescriptionAttribute>();
        if (attr != null)
            this.Name = attr.Description;
        else
            this.Name = value.ToString();
    }

    public Enum Value
    {
        get;
    }

    public string Name
    {
        get;
    }

    // This is needed to ensure SelectedItem
    // works properly.
    public override bool Equals(object? obj)
    {
        if (obj is EnumViewModel evm)
            return Enum.Equals(this.Value, evm.Value);
        return false;
    }

    public override int GetHashCode()
    {
        return this.Value.GetHashCode();
    }

    public override string ToString()
    {
        return this.Name;
    }
}

两个 XAML 转换器:

// IMPORTANT - Generally use OneTime binding here, otherwise the choice array
// will be re-created every time the underlying bound value changes. If the choice list might change, use a second backing 
// property to regenerate the choice list, see below
public class EnumToItemsSourceConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (!(value is Enum e))
            throw new Exception("EnumToItemsSourceConverter requires binding to an Enum value");
        var values = Enum.GetValues(e.GetType());
        return values.Cast<Enum>().Select(v => new EnumViewModel(v)).ToArray();
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

public class EnumToSelectedItemConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (!(value is Enum e))
            throw new Exception("EnumToSelectedItemConverter requires binding to an Enum value");
        return new EnumViewModel(e);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (!(value is EnumViewModel evm))
            throw new Exception("EnumToSelectedItemConverter requires binding to an Enum value");
        return evm.Value;
    }
}

如果这看起来工作量很大,请考虑在示例中使用它是多么简单:

C#:

public enum TestEnum
{
    FirstChoice,
    SecondChoice,
    [Description("Custom Choice")]
    CustomChoice
}

public class TestViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;

    TestEnum _TestValue = TestEnum.FirstChoice;
    public TestEnum TestValue
    {
        get => _TestValue;
        set
        {
            _TestValue = value;
            PropertyChanged?.Invoke(
                this,
                new PropertyChangedEventArgs(nameof(TestValue)));
        }
    }
}

XAML:

    <ComboBox ItemsSource="{Binding TestValue, 
                    Mode=OneTime,
                    Converter={StaticResource EnumToItemsSourceConverter}}"
              SelectedItem="{Binding TestValue, 
                    Mode=TwoWay, 
                    Converter={StaticResource EnumToSelectedItemConverter}}"
              />


以上符合 99% 的情况,但是你有一个有点不寻常的要求,要根据另一个选择来改变选择。尽管对视图模型进行了一些小的更改,但这没有问题。我们只需要向视图模型添加第二个属性,其特定目的是生成选择列表,然后将实际值绑定到通用

Enum
而不是强类型:

public enum TestEnum
{
    FirstChoice,
    SecondChoice,
    [Description("Custom Choice")]
    CustomChoice
}

public enum TestEnum2
{
    OtherChoice,
    AnotherChoice,
    YetAnotherChoice
}

public class TestViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;

    Enum _ChoiceGenerator = default(TestEnum);
    public Enum ChoiceGenerator
    {
        get => _ChoiceGenerator;
        set
        {
            _ChoiceGenerator = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ChoiceGenerator)));
        }
    }

    Enum _TestValue = default(TestEnum);
    public Enum TestValue
    {
        get => _TestValue;
        set
        {
            _TestValue = value;
            PropertyChanged?.Invoke(
                this,
                new PropertyChangedEventArgs(nameof(TestValue)));
        }
    }

    bool _UseOtherChoiceList;
    public bool UseOtherChoiceList
    {
        get => _UseOtherChoiceList;
        set
        {
            _UseOtherChoiceList = value;
            PropertyChanged?.Invoke(
                this,
                new PropertyChangedEventArgs(nameof(UseOtherChoiceList)));
            if (value)
                this.ChoiceGenerator = this.TestValue = default(TestEnum2);
            else
                this.ChoiceGenerator = this.TestValue = default(TestEnum);
        }
    }
}

XAML:

    <ComboBox ItemsSource="{Binding ChoiceGenerator, 
                    Converter={StaticResource EnumToItemsSourceConverter}}"
              SelectedItem="{Binding TestValue, 
                    Mode=TwoWay, 
                    Converter={StaticResource EnumToSelectedItemConverter}}"
              />
    <CheckBox IsChecked="{Binding UseOtherChoiceList}">Switch Choices</CheckBox>

注意在这种不寻常的情况下,我们将使用默认模式而不是一次性绑定

ItemsSource
,因为我们确实希望它是动态的。

最后,您关心可伸缩性这一事实,选择列表需要传播到整个应用程序中多达 100 个位置,这也使得这种方法比评论中建议的替代方法更合适,因为

ChoiceGenerator
属性可以获取任意数量的
ComboBox
ItemsSource
s - 一旦您更新
ChoiceGenerator
就会自动更改 - 而每个
SelectedValue
都可以绑定到它自己的 viewmodel 属性。您将无法使用
MarkupExtension
s 或我能想到的任何其他方法来做到这一点。

© www.soinside.com 2019 - 2024. All rights reserved.