简体   繁体   中英

Automatically generating DataGrid column from enum

I'm using columns autogeneration feature of WPF DataGrid control. One of it's columns is a select column - based on some enum.

The enum looks like this:

public enum MyTypes {
  Integer = 1,
  Float = 2
  IntegerArray = 3,
  FloatArray = 4
}

I'd like to show the array types not as IntegerArray, FloatArray, but as Integer[], Float[] in the autogenerated dropdown list.

In other words - cell will contain a dropdown list with values Integer, Float, IntegerArray, FloatArray, and I want them to be Integer, Float, Integer[], Float[]. Obviously I can't change IntegerArray to Integer[] inside my MyTypes declaration.

How do I do that?

EDIT:

Pushpray's answer below works only partially - I get enum fields description (so instead of having FloatArr in the ComboBox I'm getting Float[], but when the column holding the ComboBox looses focus, then I get NullReferenceException .

here is how I offer to solve your issue

result

结果

we will start by defining the desired values as Description attribute to the enum values

public enum MyTypes
{
    Integer = 1,
    Float = 2,
    [Description("Integer[]")]
    IntegerArray = 3,
    [Description("Float[]")]
    FloatArray = 4
}

then create a class with a method to enumerate the list from the enum type that will take Description attribute into account if applied

namespace CSharpWPF
{
    public class EnumHelper
    {
        public static IEnumerable<string> GetEnumDescriptions(Type enumType)
        {
            foreach (var item in Enum.GetNames(enumType))
            {
                FieldInfo fi = enumType.GetField(item);

                DescriptionAttribute[] attributes =
                    (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);

                if (attributes != null && attributes.Length > 0)
                    yield return attributes[0].Description;
                else
                    yield return item;
            }
        }
    }
}

finally use ObjectDataProvider to use the enumerator method GetEnumDescriptions in the class EnumHelper , and use the same as the source for the DataGridComboBoxColumn 's ItemsSource

sample xaml

<DataGrid xmlns:l="clr-namespace:CSharpWPF">
    <DataGrid.Columns>
        <DataGridComboBoxColumn Header="EnumValues" >
            <DataGridComboBoxColumn.ItemsSource>
                <Binding>
                    <Binding.Source>
                        <ObjectDataProvider MethodName="GetEnumDescriptions"
                                            ObjectType="{x:Type l:EnumHelper}">
                            <ObjectDataProvider.MethodParameters>
                                <x:Type TypeName="l:MyTypes" />
                            </ObjectDataProvider.MethodParameters>
                        </ObjectDataProvider>
                    </Binding.Source>
                </Binding>
            </DataGridComboBoxColumn.ItemsSource>
        </DataGridComboBoxColumn>
    </DataGrid.Columns>
</DataGrid>

Using with Auto generating columns

<DataGrid x:Name="dGrid" 
          AutoGenerateColumns="True" 
          AutoGeneratingColumn="DataGrid_OnAutoGeneratingColumn"
          xmlns:l="clr-namespace:CSharpWPF">
    <DataGrid.Resources>
        <l:EnumHelper x:Key="EnumHelper" />
        <ObjectDataProvider x:Key="EnumValues"
                            MethodName="GetEnumDescriptions"
                            ObjectType="{x:Type l:EnumHelper}">
            <ObjectDataProvider.MethodParameters>
                <x:Type TypeName="l:MyTypes" />
            </ObjectDataProvider.MethodParameters>
        </ObjectDataProvider>
        <DataTemplate x:Key="MyTypesCellTemplate">
            <TextBlock Text="{Binding EnumValue, Converter={StaticResource EnumHelper}}"/>
        </DataTemplate>
        <DataTemplate x:Key="MyTypesCellEditingTemplate">
            <ComboBox  SelectedItem="{Binding EnumValue, Converter={StaticResource EnumHelper}}"
                ItemsSource="{Binding Source={StaticResource EnumValues}}" />
        </DataTemplate>
    </DataGrid.Resources>
</DataGrid>

event handler

    private void DataGrid_OnAutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
    {
        if (e.PropertyType == typeof(MyTypes))
        {
            DataGridTemplateColumn templateColumn = new DataGridTemplateColumn();
            templateColumn.Header = e.Column.Header;
            templateColumn.CellTemplate = (DataTemplate)dGrid.Resources["MyTypesCellTemplate"];
            templateColumn.CellEditingTemplate = (DataTemplate)dGrid.Resources["MyTypesCellEditingTemplate"];
            e.Column = templateColumn;
        }
    }

EnumHelper class

public class EnumHelper : IValueConverter
{
    public static IEnumerable<string> GetEnumDescriptions(Type enumType)
    {
        foreach (var item in Enum.GetNames(enumType))
        {
            FieldInfo fi = enumType.GetField(item);

            DescriptionAttribute[] attributes =
                (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);

            if (attributes != null && attributes.Length > 0)
                yield return attributes[0].Description;
            else
                yield return item;
        }
    }

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value == null)
            return string.Empty;

        FieldInfo fi = value.GetType().GetField(value.ToString());

        DescriptionAttribute[] attributes =
            (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);

        if (attributes != null && attributes.Length > 0)
            return attributes[0].Description;
        else
            return value.ToString();
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value == null)
            return MyTypes.Float;

        Type enumType = typeof(MyTypes);
        foreach (var item in Enum.GetNames(enumType))
        {
            FieldInfo fi = enumType.GetField(item);

            DescriptionAttribute[] attributes =
                (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);

            if (attributes != null && attributes.Length > 0 && string.Equals(attributes[0].Description, value))
                return Enum.Parse(enumType, item);
        }
        return Enum.Parse(enumType, value.ToString());
    }
}

Demo

Here is a working sample code based on the answer above

DataGridEnumsSample.zip (VS 2013)

MD5 Checksum: 9C34BB81857C78375582FAC0E1C8A95D

I have another solution that will allow you to display enums in a DataGrid using auto-generated columns. This works for enum types that are created using reflection.

First, create an EnumTemplateColumn that inherits from DataGridBoundColumn :

public class EnumTemplateColumn : DataGridBoundColumn
{
    private readonly Type enumType;

    public EnumTemplateColumn(Type enumType)
    {
        this.enumType = enumType;
    }

    protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
    {
        string columnHeader = cell.Column.Header.ToString();
        TextBlock textBlock = new TextBlock();
        var dataRowView = (DataRowView) dataItem;
        var enumValue = dataRowView[columnHeader];

        textBlock.Text = Enum.GetName(this.enumType, enumValue);

        return textBlock;
    }

    protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
    {
        throw new NotImplementedException();
    }
}

Next, use the OnAutoGeneratingColumn event of the DataGrid to use the EnumTemplateColumn :

    private void DataGrid_OnAutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
    {
        if (e.PropertyType.IsEnum)
        {
            e.Column = new EnumTemplateColumn(e.PropertyType)
            {
                Header = e.Column.Header,
            };
        }
    }

And the WPF component:

    <DataGrid x:Name="dataGrid"
              Grid.Row="5" 
              ItemsSource="{Binding Path=DataTable}" IsReadOnly="True"
              AutoGeneratingColumn="DataGrid_OnAutoGeneratingColumn"/>

This is how I did it: in the AutoGeneratingColumn event of the DataGrid , replace the default DataGridComboBoxColumn by DataGridTextColumn , and add the binding and converter manually.

    private void dataGrid1_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
    {
        if (e.PropertyType.IsEnum)
        {
            //(e.Column as DataGridComboBoxColumn)

            var col = new DataGridTextColumn
            {
                Header = e.PropertyName
            };

            col.Binding = new Binding(e.PropertyName)
            {
                Converter = new WPFCommon.BindingConverters.EnumConverter()
            };

            // Replace the auto-generated column with the new one.
            e.Column = col;
        }
    }

The converter class,

namespace WPFCommon.BindingConverters
{
    public class EnumConverter:IValueConverter
    {
        //** this does not work for enum value in DataTable
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value == null || string.IsNullOrEmpty(value.ToString()))
                return DependencyProperty.UnsetValue;

            return ((Enum)value).GetDescription();
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return value;
        }
    }
}

The class for enum extension,

public static class EnumExtensions
{
    public static string GetDisplayName(this Enum enu)
    {
        DisplayAttribute attr = GetDisplayAttribute(enu);
        return attr != null ? attr.Name : enu.ToString();
    }

    public static string GetDescription(this Enum enu)
    {
        DescriptionAttribute attr = GetDescriptionAttribute(enu);
        return attr != null ? attr.Description : enu.ToString();
    }

    private static DescriptionAttribute GetDescriptionAttribute(object value)
    {
        Type type = value.GetType();
        if (!type.IsEnum)
        {
            throw new ArgumentException(string.Format("Type {0} is not an enum", type));
        }

        // Get the enum field.
        var field = type.GetField(value.ToString());
        return field == null ? null : field.GetCustomAttribute<DescriptionAttribute>();
    }

    private static DisplayAttribute GetDisplayAttribute(object value)
    {
        Type type = value.GetType();
        if (!type.IsEnum)
        {
            throw new ArgumentException(string.Format("Type {0} is not an enum", type));
        }

        // Get the enum field.
        var field = type.GetField(value.ToString());
        return field == null ? null : field.GetCustomAttribute<DisplayAttribute>();
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM