Travis
Travis

Reputation: 11

WPF ComboBox for Enum values using autogenerated datagrid

I see Description attribute on enum values in WPF DataGrid with AutoGenerateColumns however there is no answer for autogenerated columns with Enum. I've tried a few different approaches with no success.

I basically just want to show the Description attribute of the enums in the autogenerated columns.

I have tried using DataTemplates for cell and cellediting, the cell one works just fine, it shows the textblock as expected with the description value. However the combobox in the celleditingtemplate is not binding correctly. Oddly enough when I switch them and use the cellediting template for the celltemplate it shows the correct values in the combobox. It seems like the binding value passed to the celleditingtemplate is the whole datagridrow.

Is there either a way to narrow it down so I can extract the enum type and pass that to a converter to populate a list for the selected items or...

I've been beating my head against the wall for a while now and have gotten nowhere!

if (e.PropertyType.IsEnum)
{
    EnumDataGridTemplateColumn col = new EnumDataGridTemplateColumn();
    col.ColumnName = e.PropertyName;
    col.SortMemberPath = e.PropertyName;
    col.CellTemplate = (DataTemplate)FindResource("EnumDataGridTemplate");
    col.CellEditingTemplate = (DataTemplate)FindResource("EnumDataGridTemplateEdit");
    e.Column = col;
}

Is in the AutoGeneratingColumn and the xaml for the templates is:

<DataTemplate x:Key="EnumDataGridTemplate">
    <ContentPresenter Content="{Binding Path=., Mode=OneWay, Converter={StaticResource enumToStringConverter}}" VerticalAlignment="Center"/>
</DataTemplate>

<DataTemplate x:Key="EnumDataGridTemplateEdit">
    <ComboBox
            ItemsSource="{Binding Path=., Converter={StaticResource enumToListConverter}}"
            SelectedItem="{Binding Path=., Mode=TwoWay}">
        <ComboBox.ItemTemplate>
            <DataTemplate>
                <TextBlock  Text="{Binding Path=.,Mode=OneWay,
                        Converter={StaticResource enumToStringConverter}}"
                        VerticalAlignment="Center"/>
            </DataTemplate>
        </ComboBox.ItemTemplate>
    </ComboBox>
</DataTemplate>

Not sure if there is a different approach I should use?

Converter:

public class EnumToListConverter : IValueConverter
{
    public object Convert(object value, Type targetType,
        object parameter, CultureInfo culture)
    {
        if (value is Enum)
        {
            return EnumConverter.EnumToList(value.GetType());
        }
        return EnumConverter.EnumToList(typeof(dummy));
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new Exception("Cant convert back");
    }
}

The problem here is that the value passed to the converter is the viewmodel, and not the individual property as expected (like in the celltemplate).

public class EnumDataGridTemplateColumn : DataGridTemplateColumn
{
    public string ColumnName
    {
        get;
        set;
    }

    public Type enumType
    {
        get;
        set;
    }

    protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
    {
        // The DataGridTemplateColumn uses ContentPresenter with your DataTemplate.
        ContentPresenter cp = (ContentPresenter)base.GenerateElement(cell, dataItem);
        // Reset the Binding to the specific column. The default binding is to the DataRowView.
        BindingOperations.SetBinding(cp, ContentPresenter.ContentProperty, new Binding(this.ColumnName));
        return cp;
    }
}

Any help would be greatly appreciated.

Thank you.

Upvotes: 1

Views: 920

Answers (1)

Travis
Travis

Reputation: 13

I end up having to completely build the custom templates in code. Not ideal, but functional:

    private void DataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
    {
        if (dataGridColumnInfo.ColumnInfo == null) return;

        try
        {
            string headername = e.Column.Header.ToString();

            bool cancel = true;

            foreach (ColumnInfo colInfo in dataGridColumnInfo.ColumnInfo)
            {
                if (colInfo.PropertyName != e.PropertyName) continue;
                cancel = false;
                if (e.PropertyType.IsEnum)
                {
                    var templateColumn = new DataGridTemplateColumn();
                    templateColumn.Header = colInfo.Header;
                    templateColumn.CellTemplate = this.BuildCustomCellTemplate(colInfo.PropertyName);
                    templateColumn.CellEditingTemplate = this.BuildCustomCellEditTemplate(colInfo.PropertyName, e.PropertyType);
                    templateColumn.SortMemberPath = colInfo.PropertyName;
                    e.Column = templateColumn;
                }
                colInfo.Apply(e.Column);
                break;
            }

            e.Cancel = cancel;
        }
        catch (Exception ex)
        {
            Log.CreateError("WPF", ex, Log.Priority.Emergency);
            throw;
        }
    }

    // builds custom template
    private DataTemplate BuildCustomCellTemplate(string columnName)
    {
        var template = new DataTemplate();

        var textBlock = new FrameworkElementFactory(typeof(TextBlock));
        template.VisualTree = textBlock;

        var binding = new Binding();
        binding.Path = new PropertyPath(columnName);
        binding.Converter = new EnumToStringConverter();
        textBlock.SetValue(TextBlock.TextProperty, binding);
        textBlock.SetValue(TextBlock.MarginProperty, new Thickness(0, 0, 6, 0)); 
        return template;
    }

    private DataTemplate BuildCustomCellEditTemplate(string columnName, Type enumType)
    {
        var template = new DataTemplate();
        var itemTemplate = new DataTemplate();

        var comboBox = new FrameworkElementFactory(typeof(ComboBox));
        template.VisualTree = comboBox;

        var enumList = Enum.GetValues(enumType);
        var binding = new Binding();
        binding.Source = enumList;
        comboBox.SetValue(ComboBox.ItemsSourceProperty, binding);


        var valueBinding = new Binding();
        valueBinding.Path = new PropertyPath(columnName);
        valueBinding.Mode = BindingMode.TwoWay;
        comboBox.SetValue(ComboBox.SelectedItemProperty, valueBinding);

        // Squeeze the pretty combobox in the cell
        comboBox.SetValue(ComboBox.MarginProperty, new Thickness(0));
        comboBox.SetValue(ComboBox.PaddingProperty, new Thickness(0));
        comboBox.SetValue(ComboBox.ItemTemplateProperty, itemTemplate);

        // Add the content presenter that will show the pretty value
        var content = new FrameworkElementFactory(typeof(ContentPresenter));
        itemTemplate.VisualTree = content;
        var contentBinding = new Binding();
        contentBinding.Path = new PropertyPath(".");
        contentBinding.Converter = new EnumToStringConverter();
        contentBinding.Mode = BindingMode.OneWay;
        content.SetValue(ContentPresenter.ContentProperty, contentBinding);
        content.SetValue(ContentPresenter.MarginProperty, new Thickness(0, 0, 6, 0));
        return template;
    }

Upvotes: 1

Related Questions