0
votes

I have a datagrid where columns gets populated dynamically. Wich i have to do in code since users can add columns / remove columns / modify column properties throught a custom columns settings system.

Ive created an attached property as a dependencyproperty like this:

public class DataGridColumnBehavior
{
    public static readonly DependencyProperty ColumnsSourceProperty =
        DependencyProperty.RegisterAttached("ColumnsSource",
                                            typeof (ObservableCollection<ColumnDescriptor>),
                                            typeof (DataGridColumnBehavior),
                                            new UIPropertyMetadata(null, ColumnsSourcePropertyChanged));

    private static void ColumnsSourcePropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
    {
        var dataGrid = source as DataGrid;
        var columnDescriptors = e.NewValue as ObservableCollection<ColumnDescriptor>;

        if (dataGrid != null && columnDescriptors != null && columnDescriptors.Any())
        {
            dataGrid.Columns.Clear();

            columnDescriptors.ToList().ForEach(col =>
                {
                    var gridColumn = CreateDataGridColumn(col);

                    if(gridColumn != null)
                        dataGrid.Columns.Add(gridColumn); 
                });
        }
    }

    private static DataGridColumn CreateDataGridColumn(ColumnDescriptor columnDescriptor)
    {
        Type propertyType = null;

        if (columnDescriptor.ParentDtoType != null)
            propertyType = columnDescriptor.ParentDtoType.GetProperty(columnDescriptor.DataPropertyName).PropertyType;

        DataGridBoundColumn returnColumn;

        if (propertyType == typeof (bool))
        {
            returnColumn = new DataGridCheckBoxColumn();
        }
        else if (propertyType == typeof (string) && columnDescriptor.LookupOn)
        {
            returnColumn = new DataGridLookupColumn();                
        }
        else if (propertyType == typeof (DateTime?) || propertyType == typeof(DateTime))
        {
            returnColumn = new DataGridDateColumn();
        }
        else
        {
            returnColumn = new DataGridTextColumn();
        }

        returnColumn.Header = columnDescriptor.HeaderText;
        returnColumn.Width = columnDescriptor.Width;
        returnColumn.Binding = new Binding(columnDescriptor.DataPropertyName) { UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged, Mode = BindingMode.TwoWay };
        returnColumn.IsReadOnly = columnDescriptor.TextReadOnly;
        returnColumn.Visibility = columnDescriptor.Visible ? Visibility.Visible : Visibility.Collapsed;

        return returnColumn;
    }

    public static void SetColumnsSource(DependencyObject element, ObservableCollection<DataGridColumn> value)
    {
        element.SetValue(ColumnsSourceProperty, value);
    }

    public static ObservableCollection<DataGridColumn> GetColumnsSource(DependencyObject element)
    {
        return (ObservableCollection<DataGridColumn>)element.GetValue(ColumnsSourceProperty);
    }

XAML:

<DataGrid Grid.Column="6" Grid.ColumnSpan="5" Grid.RowSpan="5" Grid.Row="8" Name="KorlistaDataGrid" MaxHeight="106" behavior:DataGridColumnBehavior.ColumnsSource="{Binding ColumnsListView}" ItemsSource="{Binding MyObservableCollection}" SelectedItem="{Binding ListViewSelectedItem, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" CanUserAddRows="False" CanUserDeleteRows="False" CanUserReorderColumns="False" AutoGenerateColumns="False">
            <DataGrid.ContextMenu>
                <ContextMenu>
                    <MenuItem Header="XXX" Command="{Binding ContextMenuCommand}" IsEnabled="{Binding CMSIsEnabled, UpdateSourceTrigger=PropertyChanged}"></MenuItem>
                </ContextMenu>
            </DataGrid.ContextMenu>
        </DataGrid>

My custom column:

public class DataGridDateColumn : DataGridTextColumn
{
    protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
    {
        var dataGridBoundColumn = cell.Column as DataGridBoundColumn;
        var datePicker = new DatePicker { Width = 50, HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center };

        if (dataGridBoundColumn != null)
        {
            datePicker.SetBinding(TextBox.TextProperty, dataGridBoundColumn.Binding);

            var bindingExpression = datePicker.GetBindingExpression(TextBox.TextProperty);

            if (bindingExpression != null)
            {
                bindingExpression.UpdateSource();
                bindingExpression.UpdateTarget();
            }
        }

        return datePicker;
    }
}

Everything is looking great, my columns are like i want them, all bound values from my itemssource are seen as expected.

And finally my question: How do configure the binding here so that:

  1. The bound value is displayed in my datepicker in editmode
  2. If i change the value in editmode (manually or via the picker) i obviously want that value to be updated in my datasource and displayed in viewmode after i leave editmode.

Thanks in advance!

1

1 Answers

1
votes

Ok i solved it myself :)

This worked

protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
{
    var dataGridBoundColumn = cell.Column as DataGridBoundColumn;
    var datePicker = new DatePicker { Width = 50, HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center };
    var cellContent = cell.Content as TextBlock;

    if (dataGridBoundColumn != null)
    {
        var bindingExpression = (cell.Content as TextBlock) != null ? BindingOperations.GetBindingExpression(cellContent, TextBlock.TextProperty) : null;

        if (bindingExpression != null)
        {
            var newBindning = new Binding(bindingExpression.ParentBinding.Path.Path)
                {
                    UpdateSourceTrigger = bindingExpression.ParentBinding.UpdateSourceTrigger, Mode = bindingExpression.ParentBinding.Mode
                };

            datePicker.SetBinding(DatePicker.TextProperty, newBindning);
        }
    }

    return datePicker;
}