WPF将ListBox绑定到枚举,显示Description属性

wztqucjr  于 2023-02-25  发布在  其他
关注(0)|答案(6)|浏览(320)

是否可以使用ObjectDataProvider方法将列表框绑定到枚举,并以某种方式设置其样式以显示Description属性?如果可以,将如何进行此操作...?

a7qyws3x

a7qyws3x1#

是的,这是可能的。这样就行了。假设我们有枚举

public enum MyEnum
{
    [Description("MyEnum1 Description")]
    MyEnum1,
    [Description("MyEnum2 Description")]
    MyEnum2,
    [Description("MyEnum3 Description")]
    MyEnum3
}

然后我们可以将ObjectDataProvider用作
x一个一个一个一个x一个一个二个x
对于列表框,我们将ItemsSource设置为MyEnumValues,并使用Converter应用ItemTemplate。

<ListBox Name="c_myListBox" SelectedIndex="0" Margin="8"
        ItemsSource="{Binding Source={StaticResource MyEnumValues}}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Converter={StaticResource EnumDescriptionConverter}}"/>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

在转换器中,我们获取描述并将其返回

public class EnumDescriptionConverter : IValueConverter
{
    private string GetEnumDescription(Enum enumObj)
    {
        FieldInfo fieldInfo = enumObj.GetType().GetField(enumObj.ToString());

        object[] attribArray = fieldInfo.GetCustomAttributes(false);

        if (attribArray.Length == 0)
        {
            return enumObj.ToString();
        }
        else
        {
            DescriptionAttribute attrib = attribArray[0] as DescriptionAttribute;
            return attrib.Description;
        }
    }

    object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        Enum myEnum = (Enum)value;
        string description = GetEnumDescription(myEnum);
        return description;
    }

    object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return string.Empty;
    }
}

GetEnumDescription方法可能应该放在其他地方,但您会得到这样的想法:)
检查GetEnumDescription as extension method

gj3fmq9x

gj3fmq9x2#

另一个解决方案是定制MarkupExtension,它从枚举类型生成项,这使得xaml更加紧凑和可读。

using System.ComponentModel;

namespace EnumDemo
{
    public enum Numbers
    {
        [Description("1")]
        One,

        [Description("2")]
        Two,

        Three,
    }
}

用法示例:

<Window x:Class="EnumDemo.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:EnumDemo">

    <ListBox ItemsSource="{local:EnumToCollection EnumType={x:Type local:Numbers}}"/>

</Window>

标记扩展实现

using System;
using System.ComponentModel;
using System.Linq;
using System.Windows.Markup;

namespace EnumDemo
{
    public class EnumToCollectionExtension : MarkupExtension
    {
        public Type EnumType { get; set; }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            if (EnumType == null) throw new ArgumentNullException(nameof(EnumType));

            return Enum.GetValues(EnumType).Cast<Enum>().Select(EnumToDescriptionOrString);
        }

        private string EnumToDescriptionOrString(Enum value)
        {
            return value.GetType().GetField(value.ToString())
                       .GetCustomAttributes(typeof(DescriptionAttribute), false)
                       .Cast<DescriptionAttribute>()
                       .FirstOrDefault()?.Description ?? value.ToString();
        }
    }
}
dddzy1tm

dddzy1tm3#

如果绑定到枚举,则可能通过IValueConverter将其转换为说明。
有关如何完成此操作的说明,请参见Binding ComboBoxes to enums... in Silverlight!
有关详细信息,请参见http://msdn.microsoft.com/en-us/library/system.windows.data.ivalueconverter.aspx

k4aesqcs

k4aesqcs4#

您可以在项目中定义资源文件(*. resx文件)。在该文件中,您必须定义"键-值对",如下所示:

"YellowCars" : "Yellow Cars",
"RedCars" : "Red Cars",

等等...
keys等于你的enum-entries,类似于:

public enum CarColors
{
    YellowCars,
    RedCars
}

等等...
使用WPF时,可以在XAML代码中实现如下内容:

<ComboBox ItemsSource="{Binding Source={StaticResource CarColors}}" SelectedValue="{Binding CarColor, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Converter={StaticResource CarColorConverter}}" />
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

然后你必须编写你的转换器,类似这样:

using System;
using System.Globalization;
using System.Resources;
using System.Windows.Data;

public class CarColorConverter : IValueConverter
{
    private static ResourceManager CarColors = new ResourceManager(typeof(Properties.CarColors));

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var key = ((Enum)value).ToString();
        var result = CarColors.GetString(key);
        if (result == null) {
            result = key;
        }

        return result;
    }

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

我的回答迟了7年;- )但是也许它可以被别人使用!

jobtbby3

jobtbby35#

嗯,有可能。
ListBox可以帮助我们做到这一点,无需转换器。
该方法的步骤如下:
创建一个列表框,将列表框的ItemsSource设置为枚举,并将列表框的SelectedItem绑定到selected属性。
然后将创建每个ListBoxItem。

*步骤1:定义枚举。

public enum EnumValueNames
{ 
   EnumValueName1, 
   EnumValueName2, 
   EnumValueName3
}

然后将以下属性添加到您的DataContext(或MVVM的ViewModel)中,该属性记录了选中的项目。

public EnumValueNames SelectedEnumValueName { get; set; }

*步骤2:将枚举添加到Window、UserControl或Grid等的静态资源中。

<Window.Resources>
        <ObjectDataProvider MethodName="GetValues"
                            ObjectType="{x:Type system:Enum}"
                            x:Key="EnumValueNames">
            <ObjectDataProvider.MethodParameters>
                <x:Type TypeName="local:EnumValueNames" />
            </ObjectDataProvider.MethodParameters>
        </ObjectDataProvider>
    </Window.Resources>

*步骤3:使用列表框填充每个项目

<ListBox ItemsSource="{Binding Source={StaticResource EnumValueNames}}"
              SelectedItem="{Binding SelectedEnumValueName, Mode=TwoWay}" />

参考文件:https://www.codeproject.com/Articles/130137/Binding-TextBlock-ListBox-RadioButtons-to-Enums

ruarlubt

ruarlubt6#

  • 此处的示例适用于ComboBox,但对任何枚举绑定都同样适用。*

来源:

这个anwser是基于Brian Lagunas' EnumBindingSourceExtension + EnumDescriptionTypeConverter的原始工作。我已经为它做了修改,以更好地适应我的需要。

我更改的内容:

1.使用布尔函数扩展了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);
}

1.修改了"EnumDescriptionTypeConverter"中Brian的"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;
}

1.通过调用我自己的函数编辑Brian的返回值,修改了"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>();
}

此示例已应用于组合框:

  • 注意:上传图像到服务器失败,所以我添加了一个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;

"转换预设"枚举和Picture of the ComboBox Rendered

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

"输出"枚举和Picture of the ComboBox Rendered

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

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

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

相关问题