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:
- The bound value is displayed in my datepicker in editmode
- 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!