Search code examples
c#wpfbindingdatatemplatedatagridcolumn

Binding DataTemplate to DataGridColumn CellTemplate


I am trying to setup a DatePicker DataGrid column in the code. This column is used with different DataGrids, each displaying different types of objects. From this post I came up with the following code, which works ok:

public DataGridColumn GetDataGridColumn(string header, bool isReadOnly, string fieldName)
{
    DataGridTemplateColumn dgCol = new DataGridTemplateColumn();
    dgCol.Header = header;
    dgCol.IsReadOnly = isReadOnly;

    FrameworkElementFactory dateFactory = new FrameworkElementFactory(typeof(DatePicker));
    dateFactory.SetBinding(DatePicker.SelectedDateProperty, new Binding(fieldName));
    dateFactory.SetValue(DatePicker.IsEnabledProperty, !isReadOnly);

    DataTemplate cellTemplate = new DataTemplate();
    cellTemplate.VisualTree = dateFactory;

    dgCol.CellTemplate = cellTemplate;

    return dgCol;
}

However, since FrameworkElementFactory is a deprecated way to programmatically create templates then I thought I had better search for another approach.

As an alternative I thought about creating a set of DataTemplates as resources and then applying these in the code. This is where I have got to so far but I am not sure how to set up the bindings. Here is my DatePicker template:

<DataTemplate x:Key="DatePickerColumnCellTemplate">
    <DatePicker SelectedDate="{Binding Path=StartDate}" />
</DataTemplate>

And here is the new GetDataGridColumn method:

public DataGridColumn GetDataGridColumn(string header, bool isReadOnly, string fieldName)
{
    DataGridTemplateColumn dgCol = new DataGridTemplateColumn();
    dgCol.Header = header;
    dgCol.IsReadOnly = isReadOnly;

    dgCol.CellTemplate = (DataTemplate)Application.Current.FindResource("DatePickerColumnCellTemplate");

    return dgCol;
}

This new approach seems to be a more elegant solution because now the UI design of the DatePicker column is defined in XAML and only applied using the code. However, now my DatePicker column only displays the start date. The fieldName argument in the method could be "StartDate" or "EndDate" or any other DateTime variable defined in the objects that are being displayed in the data grids. Also I am not sure how to set the DatePicker IsEnabled property to the value of isReadOnly.

If you think this is not the best way to proceed then please let me know.

Thanks.


Solution

  • Since no answers have appeared here is the solution I have come up with.

    I did not find a way to hook up the binding for the template created in XAML but instead started looking at what I was trying to do - create a DatePicker DataGridColumn. So I created my own DatePickerDataGridColumn class by inheriting from the abstract base class for a data grid column:

    public class DatePickerDataGridColumn : DataGridColumn
    {
        public DatePickerDataGridColumn() : base() { }
    
        public BindingBase SelectedDateBinding { get { return _selectedDateBinding; } set { _selectedDateBinding = value; } }
        private BindingBase _selectedDateBinding = null;
    
        protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem) { return null; }
    
        protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
        {
            DatePicker picker = new DatePicker();
            picker.IsEnabled = !IsReadOnly;
            picker.BorderThickness = new Thickness(0.0);
    
            if (SelectedDateBinding != null)
            {
                picker.SetBinding(DatePicker.SelectedDateProperty, SelectedDateBinding);
            }
    
            return picker;
        }
    }
    

    Then my GetDataGridColumn method became:

    public override DataGridColumn GetDataGridColumn(string header, bool isReadOnly, string fieldName)
    {
        DatePickerDataGridColumn dgCol = new DatePickerDataGridColumn();
        dgCol.Header = header;
        dgCol.IsReadOnly = isReadOnly;
    
        Binding b = new Binding(fieldName);
        if (isReadOnly) { b.Mode = BindingMode.OneWay; }
        dgCol.SelectedDateBinding = b;
    
        return dgCol;
    }