动态生成,分组单选按钮并将其绑定到枚举

问题描述

我想在我的viewmodel上有一个Enum,在这种情况下,它代表用户可以选择的一组操作。目前,我使用IValueConverters来获取显示值并设置所选的Enum,从而对XAML中的每个单选按钮进行硬编码并绑定到各自的Enum成员。

这可以正常工作,但是,我希望它为Enum中的每个成员动态生成并(如果可能)对RadioButtons进行分组。理想情况下,我希望基于枚举的GroupName添加一个枚举成员的单选按钮并将其放置在它所属的组(扩展器)中。

我一直在寻找实现这一目标的方法,但是到目前为止,还没有找到适合我并且不难理解的东西。但是也许有人在此方面提供了一些帮助我的建议,或者是一个巧妙的解决方案。

枚举:

using System.ComponentModel.DataAnnotations;

namespace CommonLibrary.SystemSetup.Enums
{
    public enum UtilityOperation
    {
        [display(Name="Reboot",GroupName ="Client Control")]
        ClientReboot,[display(Name = "Shutdown",GroupName = "Client Control")]
        ClientShutdown,[display(Name = "Remote Desktop",GroupName = "Client Control")]
        ClientRDPControl,}
}

查看:

<UserControl.Resources>
    <views:EnumToBoolConverter x:Key="EnumToBoolConverter" />
    <views:EnumTodisplayNameConverter x:Key="EnumToNameConverter" />
</UserControl.Resources>
    
<Expander Foreground="White" VerticalAlignment="Center" Background="Transparent" Margin="10 10 5 5">
                       
    <!-- Sub-menu header. -->
    <Expander.Header>
        <TextBlock Text="Client Control"/>
    </Expander.Header>
                        
    <StackPanel>
                        
        <RadioButton Margin="10 0 0 10"
        Content="{Binding Source={x:Static enum:UtilityOperation.ClientReboot},Converter={StaticResource EnumToNameConverter}}"
        IsChecked="{Binding Path=E,Converter={StaticResource EnumToBoolConverter},ConverterParameter={x:Static enum:UtilityOperation.ClientReboot}}" />

        <RadioButton Margin="10 0 0 10"
        Content="{Binding Source={x:Static enum:UtilityOperation.ClientShutdown},ConverterParameter={x:Static enum:UtilityOperation.ClientShutdown}}" />

        <RadioButton Margin="10 0 0 10"
        Content="{Binding Source={x:Static enum:UtilityOperation.ClientRDP},ConverterParameter={x:Static enum:UtilityOperation.ClientRDP}}" />
                       
    </StackPanel>

</Expander>

IValueConverters

public class EnumToBoolConverter : IValueConverter
{
    public object Convert(object value,Type targettype,object parameter,System.Globalization.CultureInfo culture)
    {
        return parameter != null && parameter.Equals(value);
    }

    public object ConvertBack(object value,System.Globalization.CultureInfo culture)
    {
        return value != null && value.Equals(true) ? parameter : DependencyProperty.UnsetValue;
    }
}

public class EnumTodisplayNameConverter : MarkupExtension,IValueConverter
{
    public object Convert(object value,CultureInfo culture)
    {
        return ((Enum)value).GetAttributeOfType<displayAttribute>().Name;
    }

    public object ConvertBack(object value,CultureInfo culture)
    {
        throw new NotSupportedException();
    }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return this;
    }
}

}

解决方法

这是我做类似事情的一个例子。基本方法是重新模板/(.*)并使用它动态创建单选按钮。视图模型包含要显示的值列表,这些值是基于枚举值创建的(一次初始化)。那些绑定到ListBox

此代码是从真实的应用程序中切出的,但是您可能需要对其进行调整才能编译/运行而不会出现错误。

其中某些当然是基于我在SO上学到的东西。

首先-代表每个单选按钮条目的类:

ListBox

我们用于将数据附加到枚举的自定义属性:

    public class ListItem
    {
        public string Desc { get; set; }
        public EnumType ID { get; set; }
        public int SortOrder { get; set; }
    }

用于读取属性值的代码:

[AttributeUsage(AttributeTargets.Field,Inherited = false,AllowMultiple = false)]
public class EnumMetadataAttribute : Attribute
{
    public string Description { get; set; }

    public int SortOrder { get; set; }

    public EnumMetadataAttribute(string desc)
    {
        Description = desc;
        SortOrder = 0;
    }

    public EnumMetadataAttribute(string desc,int sortOrder)
    {
        Description = desc;
        SortOrder = sortOrder;
    }
}

绝对有争议的是,使用静态对象列表来存储这种数据而不是属性枚举会更好;但这确实很好用,并且具有成为我们可以添加到现有枚举中的优点。

示例枚举:

// based on https://stackoverflow.com/a/19621488/3195477
public static class EnumerationExtensions
{
    // This extension method is broken out so you can use a similar pattern with
    // other MetaData elements in the future. This is your base method for each.
    static T GetAttribute<T>(this Enum value) where T : Attribute
    {
        var type = value.GetType();
        var memberInfo = type.GetMember(value.ToString());
        var attributes = memberInfo[0].GetCustomAttributes(typeof(T),false);
        return attributes.Length > 0
          ? (T)attributes[0]
          : null;
    }

    static EnumMetadataAttribute GetEnumMetadataAttribute(this Enum value)
    {
        var attr = value.GetAttribute<EnumMetadataAttribute>();
        return attr ?? new EnumMetadataAttribute(value.ToString(),0);
    }

    public static string GetDescription(this Enum value) => value.GetEnumMetadataAttribute().Description;

    public static int GetSortOrder(this Enum value) => value.GetEnumMetadataAttribute().SortOrder;
}

自动遍历所有枚举值的代码:

public enum EnumName
{
    [EnumMetadata("None",1)]
    None,[EnumMetadata("Option2",2)]
    Option2,[EnumMetadata("Option3",3)]
    Option3,}

在viewmodel类中:

    void InitRadioButtonList()
    {
        foreach (EnumType enumval in Enum.GetValues(typeof(EnumName)))
        {
            RadioButtonList.Add(new ListItem
            {
                ID = enumval,Item = enumval.GetDescription(),SortOrder = enumval.GetSortOrder(),});
        }
    }

以下是绑定到上面创建的列表的XAML:

   public List<ListItem> RadioButtonList { get; } = new List<ListItem>();
   public List<ListItem> SortedRadioButtonList { get { return RadioButtonList.OrderBy(x => x.SortOrder); } }

最后是 <WrapPanel> <Label>Field Name:</Label> <ListBox ItemsSource="{Binding SortedRadioButtonList}" SelectedValuePath="ID" SelectedValue="{Binding SortedRadioButtonList}" Style="{StaticResource RadioButtonListBoxStyle}" > <ListBox.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding Desc}"/> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </WrapPanel> 风格:

ListBox

更新-我最近阅读了https://brianlagunas.com/a-better-way-to-data-bind-enums-in-wpf/,它具有一些相似之处和不同之处,也值得一读。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...