将WPF ComboBox绑定到枚举并隐藏某些值

3

我有一个WPF的下拉框是绑定到一个枚举类型的,就像这样:

<Window.Resources>
    <local:EnumDescriptionConverter x:Key="enumDescriptionConverter"/>
    <ObjectDataProvider MethodName="GetValues" ObjectType="{x:Type sys:Enum}" x:Key="cityNamesDataProvider">
        <ObjectDataProvider.MethodParameters>
            <x:Type TypeName="local:MyModel+CityNamesEnum"/>
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
</Window.Resources>

<ComboBox x:Name="cityNameComboBox" ItemsSource="{Binding Source={StaticResource cityNamesDataProvider}}" SelectionChanged="cityNameComboBox_SelectionChanged">
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Converter={StaticResource enumDescriptionConverter}}"/>
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

我正在绑定的枚举有描述属性,看起来像这样:

public enum CityNamesEnum
{
    [Description("New York City")]
    NewYorkCity,
    [Description("Chicago")]
    Chicago,
    [Description("Los Angeles")]
    LosAngeles
}

我并不总是想显示每个枚举值。是否可以切换一个或多个枚举值的可见性?如果这些是ComboBoxItems,我认为我可以简单地将.Visibility属性设置为隐藏,但由于它们是枚举值,我不确定是否可能。有人知道吗?

2个回答

8
为什么不创建一个普通的C#方法来为您过滤数据,然后让ObjectDataProvider指向该方法呢?
static method IEnumerable<CityNamesEnum> MyFilter() {
  yield return CityNames.NewYorkCity;
  yield return CityNames.Chicago;
}

XAML

<ObjectDataProvider 
   MethodName="MyFilter" 
   ObjectType="{x:Type local:TheType}" 
   x:Key="cityNamesDataProvider">
</ObjectDataProvider>

谢谢,这是一个好建议。实际上,我需要根据另一个组合框中选择的项目来过滤此特定组合框的枚举值。我会看看如何将您的想法融入到解决方案中。 - bmt22033

0

这个例子适用于ComboBox,但对于任何枚举绑定都有效。它将确切地做你想要的事情:隐藏没有描述的枚举值。这也提供了一种简单的绑定方法(同时可以通过函数“SortEnumValuesByIndex()”轻松地按索引排序,但可以轻松更改)。

来源:

这个答案基于Brian Lagunas' EnumBindingSourceExtension + EnumDescriptionTypeConverter的原始工作。我对它进行了修改以更好地满足我的需求。

我做了什么更改:

扩展了Enum类,添加了一个布尔函数,用于检查EnumValue是否具有[Description]属性。
public static bool HasDescriptionAttribute(this Enum value)
{
    var attribute = value.GetType().GetField(value.ToString())
                        .GetCustomAttributes(typeof(DescriptionAttribute), false)
                        .FirstOrDefault();                                

    return (attribute != null);
}

修改了Brian在“EnumDescriptionTypeConverter”中的“ConvertTo()”函数,以便在未应用[Description]属性的情况下返回“null”。
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
{
    ...
    // Original: return ((attributes.Length > 0) && (!String.IsNullOrEmpty(attributes[0].Description))) ? attributes[0].Description : value.ToString();
    return ((attributes.Length > 0) && (!String.IsNullOrEmpty(attributes[0].Description))) ? attributes[0].Description : null;
}

通过编辑它的返回值并调用自己的函数来修改“EnumBindingSourceExtension”中的“ProvideValue()”函数:
ProvideValue(IServiceProvider serviceProvider)
{
    ...
    // Original: return enumValues
    return SortEnumValuesByIndex(enumValues);

    ...
    // Original: return tempArray
    return SortEnumValuesByIndex(tempArray);
}

并添加函数以按索引对枚举进行排序(原始代码在跨项目时存在问题...),并剥离任何没有[Description]属性的值:
private object SortEnumValuesByIndex(Array enumValues)
{
    var values = enumValues.Cast<Enum>().ToList();
    var indexed = new Dictionary<int, Enum>();
    foreach (var value in values)
    {
        int index = (int)Convert.ChangeType(value, Enum.GetUnderlyingType(value.GetType()));
        indexed.Add(index, value);
    }

    return indexed.OrderBy(x => x.Key).Select(x => x.Value).Where(x => x.HasDescriptionAttribute()).Cast<Enum>();
}     

这个例子已经应用到了ComboBoxes:

注意:上传图片到服务器失败,所以我添加了有关图片的URL

<ComboBox x:Name="ConversionPreset_ComboBox" Grid.Row="4" Grid.Column="1" Margin="5,5,5,5" ItemsSource="{objects:EnumBindingSource EnumType={x:Type enums:ConversionPreset}}" SelectedIndex="2" SelectionChanged="ConversionPreset_ComboBox_SelectionChanged" />
<ComboBox x:Name="OutputType_ComboBox" Grid.Row="4" Grid.Column="2" Margin="5,5,5,5" ItemsSource="{objects:EnumBindingSource EnumType={x:Type enums:Output}}" SelectedIndex="1" SelectionChanged="OutputType_ComboBox_SelectionChanged" />

有代码后台:

    private Enumeration.Output Output { get; set; }

    private void OutputType_ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        => Output = (Enumeration.Output)OutputType_ComboBox.SelectedItem;

    private Enumeration.ConversionPreset ConversionPreset { get; set; }

    private void ConversionPreset_ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        => ConversionPreset = (Enumeration.ConversionPreset)ConversionPreset_ComboBox.SelectedItem;

"ConversionPreset" 枚举和 ComboBox 渲染的图片

[TypeConverter(typeof(EnumDescriptionTypeConverter))]
public enum ConversionPreset
{
    [Description("Very Slow (Smaller File Size)")]
    VerySlow = -2,

    [Description("Slow (Smaller File Size)")]
    Slow = -1,

    [Description("Medium (Balanced File Size)")]
    Medium = 0,

    [Description("Fast (Bigger File Size)")]
    Fast = 1,

    [Description("Very Fast (Bigger File Size)")]
    VeryFast = 2,

    [Description("Ultra Fast (Biggest File Size)")]
    UltraFast = 3
}

"Output" 枚举和 ComboBox 渲染的图片

[TypeConverter(typeof(EnumDescriptionTypeConverter))]
public enum Output
{
    // This will be hidden in the Output
    None = -1,

    [Description("Video")]
    Video = 0,

    [Description("Audio")]
    Audio = 1
}


更新

上面的代码旨在绑定枚举并管理它们的输出,而无需手动填充ComboBoxes的Code Behind。 我刚刚读到您想从代码中处理它,以便更好地控制ComboBox内容。 这应该可以让您顺利开始。只需创建一个类并将此内容粘贴到其中:

using System;
using System.ComponentModel;

namespace Attributes
{
    public class Hidden : Attribute { }

    public class StartingDescription : DescriptionAttribute
    {
        public StartingDescription(string description) : base(description) { }
    }

    public class FinishedDescription : DescriptionAttribute
    {
        public FinishedDescription(string description) : base(description) { }
    }
}

namespace Enumerations
{
    using Attributes;

    public enum Order
    {
        None = -1,
        [Description("Get"),
         StartingDescription("Getting"),
         FinishedDescription("Got")]
        Get = 0,
        [Description("Initialize"),
         StartingDescription("Initializing"),
         FinishedDescription("Initialized")]
        Initialize = 1,
        [Description("Download"),
         StartingDescription("Downloading"),
         FinishedDescription("Downloaded")]
        Download = 2
    }

    public enum Output
    {
        [Hidden]
        None = -1,

        [Description("Video")]
        Video = 33,

        [Description("Audio")]
        Audio = 44
    }
}

namespace Classes
{
    using System.Linq;

    public static class Extensions
    {
        public static string Format(this string value, params object?[] args)
            => String.Format(value, args);

        public static bool HasAttribute(this Enum enumValue, Type attributeType)
        {
            var result = enumValue.GetType().GetField(enumValue.ToString())
                            .GetCustomAttributes(attributeType, false)
                            .FirstOrDefault();

            return (result != null);
        }

        public static int GetIndex(this Enum enumValue)
            => (int)Convert.ChangeType(enumValue, Enum.GetUnderlyingType(enumValue.GetType()));

        public static string GetDescription(this Enum enumValue, Type attributetype = null)
        {
            if (attributetype == null)
                attributetype = typeof(DescriptionAttribute);

            var field = enumValue.GetType().GetField(enumValue.ToString());
            var attributes = Attribute.GetCustomAttributes(field, false).Where(x => x.GetType().Equals(attributetype)).Cast<DescriptionAttribute>();
            var value = attributes.FirstOrDefault();
            if (value != null)
                return value.Description;

            return enumValue.ToString();
        }
    }    
}

namespace ExecutionNamespace
{
    using System.Collections.Generic;
    using System.Linq;

    using Classes;
    using Attributes;
    using System.Diagnostics;

    internal class Example
    {
        public static SortedList<int, string> GetEnumByIndex(Type enumType, Type outputType = null)
        {
            if (enumType == null)
                throw new ArgumentNullException("enumType was not Definied");

            var enumValues = Enum.GetValues(enumType).Cast<Enum>().ToList();
            if ((enumValues == null) || (enumValues.Count == 0))
                throw new ArgumentNullException("Could not find any Enumeration Values");

            if (outputType == null)
                outputType = typeof(DescriptionAttribute);
        
            var indexed = new SortedList<int, string>();
            foreach (var value in enumValues)
                if (!value.HasAttribute(typeof(Hidden)))
                    indexed.Add(value.GetIndex(), value.GetDescription(outputType));
            return indexed;
        }

        public static SortedList<string, string> GetEnumByValue(Type enumType, Type outputType = null)
        {
            if (enumType == null)
                throw new ArgumentNullException("enumType was not Definied");

            var enumValues = Enum.GetValues(enumType).Cast<Enum>().ToList();
            if ((enumValues == null) || (enumValues.Count == 0))
                throw new ArgumentNullException("Could not find any Enumeration Values");

            if (outputType == null)
                outputType = typeof(DescriptionAttribute);

            var indexed = new SortedList<string, string>();
            foreach (var value in enumValues)
                if (!value.HasAttribute(typeof(Hidden)))
                    indexed.Add(value.ToString(), value.GetDescription(outputType));
            return indexed;
        }

        public static void Run()
        {
            Type type = null;


            type = typeof(Enumerations.Order);
            Debug.WriteLine("{0} by Index".Format(type.ToString()));
            foreach (var valuePair in GetEnumByIndex(type, typeof(StartingDescription)))
                Debug.WriteLine("Index:{1} & Description:{2} ".Format(type.ToString(), valuePair.Key, valuePair.Value));
            Debug.WriteLine("");

            type = typeof(Enumerations.Order);
            Debug.WriteLine("{0} by Value".Format(type.ToString()));
            foreach (var valuePair in GetEnumByValue(type, typeof(StartingDescription)))
                Debug.WriteLine("Value:{1} & Description:{2} ".Format(type.ToString(), valuePair.Key, valuePair.Value));
            Debug.WriteLine("");



            type = typeof(Enumerations.Output);
            Debug.WriteLine("{0} by Index".Format(type.ToString()));
            foreach (var valuePair in GetEnumByIndex(type))
                Debug.WriteLine("Index:{1} & Description:{2} ".Format(type.ToString(), valuePair.Key, valuePair.Value));
            Debug.WriteLine("");

            type = typeof(Enumerations.Output);
            Debug.WriteLine("{0} by Value".Format(type.ToString()));
            foreach (var valuePair in GetEnumByValue(type))
                Debug.WriteLine("Value:{1} & Description:{2} ".Format(type.ToString(), valuePair.Key, valuePair.Value));
            Debug.WriteLine("");
        }
    }
}

然后只需在按钮单击时执行此操作,观看输出窗口:

private void Button_Click(object sender, RoutedEventArgs e)
{
    ExecutionNamespace.Example.Run();            
}

它将会输出以下内容:

Enumerations.Order by Index
Index:-1 & Description:None
Index:0 & Description:Getting
Index:1 & Description:Initializing
Index:2 & Description:Downloading

Enumerations.Order by Value
Value:Download & Description:Downloading
Value:Get & Description:Getting
Value:Initialize & Description:Initializing
Value:None & Description:None

Enumerations.Output by Index
Index:33 & Description:Video
Index:44 & Description:Audio

Enumerations.Output by Value
Value:Audio & Description:Audio
Value:Video & Description:Video

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接