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

kjthegm6  于 2023-04-22  发布在  其他
关注(0)|答案(1)|浏览(151)

已关闭,该问题为opinion-based,目前不接受回答。
**想改进这个问题吗?**更新问题,以便editing this post可以用事实和引用来回答。

3天前关闭。
这篇文章是昨天编辑并提交审查的。
Improve this question
我在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等下拉列表,以及如何扩展?
1.如果我将Enum属性绑定到ComboBox,我如何自定义标签使其可读,例如使用空格(例如:“一个1”)在视图中?

e3bfsja2

e3bfsja21#

使用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属性可以提供任意数量的ComboBoxItemsSource s -当你更新ChoiceGenerator的时候,它会自动地在任何地方改变,而每个SelectedValue都可以绑定到它自己的viewmodel属性。你不能用MarkupExtension s或我能想到的任何其他方法来做到这一点。

相关问题