Description attribute on enum values in WPF DataGr

2019-08-11 22:53发布

Here's my enum:

enum Foo 
{
    [Description("the quick")]
    Bar,
    [Description("brown fox")]
    Baz,
    [Description("jumped over")]
    Qux
}

Here's part of my ViewModel:

class MainWindowViewModel : ViewModelBase
{
    public ObservableCollection<RowViewModel> Rows { get { ... } }
}

class RowViewModel : ViewModelBase 
{
    public String Name { get { ... } set { ... } }
    public Foo Foo { get { ... } set { ... } }
}

Here's my XAML:

<DataGrid AutoGeneratingColumn="OnAutoGeneratingColumn" ItemsSource="{Binding Path=Rows}" />

Because MainWindowViewModel.Rows[n].Foo is an enum, WPF will automatically generate a DataGridComboBoxColumn with the enum members as combobox drop-down values, so far so good.

I want the combobox to use the [Description("")] values in the combobox drop-down and display. So I implemented an IValueConverter. I then added a handler for OnAutoGeneratingColumn:

private void OnAutoGeneratingColumn(Object sender, DataGridAutoGeneratingColumnEventArgs e)
{
    DataGridComboBoxColumn dgCol = e.Column as DataGridComboBoxColumn;
    if( dgCol != null && e.PropertyType.IsEnum ) {

        Binding binding = new Binding( e.PropertyName );
        binding.Converter = EnumDescriptionValueConverter.Instance;

        dgCol.TextBinding = binding;
        dgCol.SelectedItemBinding = binding;
    }
}

Unfortunately this doesn't work: the cells appear empty when they have values and the combobox drop-down contains the original enum values rather than descriptions.

1条回答
叼着烟拽天下
2楼-- · 2019-08-11 23:08

You are working too hard to do this, since the enum is not a changeable item, during the VM construction enumerate the enum and extract the descriptions into a list then bind the control to that list.


Example

Take our enums

public enum TheNums
{
    [Description("One")]
    Alpha,
    [Description("Two")]
    Beta,
    [Description("Three")]
    Gamma
}

The extension method to extract descriptions

public static class AttributeExtension
{

/// <summary>If an attribute on an enumeration exists, this will return that 
/// information</summary> 
/// <param name="value">The object which has the attribute.</param> 
/// <returns>The description string of the attribute or string.empty</returns> 
public static string GetAttributeDescription(this object value)
{
    string retVal = string.Empty;
    try
    {
        retVal = value.GetType()
                      .GetField(value.ToString())
                      .GetCustomAttributes(typeof(DescriptionAttribute), false)
                      .OfType<DescriptionAttribute>()
                      .First()
                      .Description;

    }
    catch (NullReferenceException)
    {
        //Occurs when we attempt to get description of an enum value that does not exist 
    }
    finally
    {
        if (string.IsNullOrEmpty(retVal))
            retVal = "Unknown";
    }

    return retVal;
}

}

The creation of our list of strings List<string> EnumDescriptions { get; set; }:

EnumDescriptions = new List<string>()
{
    TheNums.Alpha.GetAttributeDescription(),
    TheNums.Beta.GetAttributeDescription(),
    TheNums.Gamma.GetAttributeDescription()
};

For the sake of simplicity I will add it to the page's datacontext so I don't have to path into the list named EnumDescriptions.

public List<string> EnumDescriptions { get; set; }
public MainWindow()
{
    InitializeComponent();
    EnumDescriptions = new List<string>()
    {
        TheNums.Alpha.GetAttributeDescription(),
        TheNums.Beta.GetAttributeDescription(),
        TheNums.Gamma.GetAttributeDescription()
    };

    DataContext = EnumDescriptions;
}

Then on my page I will bind to it directly, since Listbox inheirits the page's datacontext which is the EnumDescriptions.

<ListBox ItemsSource="{Binding}" Width="100" Height="200"/>

The result is:

enter image description here

Note that in an MVVM implementation, most likely the whole VM instance would be the page's data context, so the binding needs to know the property name (its binding path) off of the data context/ the VM instance, so use Binding EnumDescriptions or Binding Path=EnumDescriptions.

查看更多
登录 后发表回答