Creating DataGridDateColumn for DataGrid

When we want to display/edit date values in DataGrid,   we could add a templatecolumn add a TextBlock to the CellTemplate and DatePicker to CellEditingTemplate. but that gets repetitive, if we are doing the same in multiple places

we can create this custom column by inheriting from DataGridBoundColumn and overriding few methods

Here is the minimal code to create a DataGridDateColumn
 public class DataGridDateColumn : DataGridBoundColumn
    {
        public string DateFormat { get; set; }
        protected override void CancelCellEdit(FrameworkElement editingElement, object uneditedValue)
        {
            DatePicker dp = editingElement as DatePicker;
            if (dp != null)
            {
                dp.SelectedDate = DateTime.Parse(uneditedValue.ToString());
            }
        }
        protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
        {
            DatePicker dp = new DatePicker();
            Binding b = new Binding();
            b.Path = this.Binding.Path;
            b.Source = this.Binding.Source;
            if (DateFormat != null)
            {
                DateTimeConverter dtc = new DateTimeConverter();
                b.Converter = dtc;
                b.ConverterParameter = DateFormat;
            }
            dp.SetBinding(DatePicker.SelectedDateProperty, this.Binding);

            return dp;
        }

        protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
        {
            TextBlock txt = new TextBlock();
            Binding b = new Binding();
            b.Path = this.Binding.Path;
            b.Source = this.Binding.Source;
            if (DateFormat != null)
            {
                DateTimeConverter dtc = new DateTimeConverter();              
                b.Converter = dtc;
                b.ConverterParameter = DateFormat;
            }
            txt.SetBinding(TextBlock.TextProperty, b);
            return txt;
        }

        protected override object PrepareCellForEdit(FrameworkElement editingElement, RoutedEventArgs editingEventArgs)
        {
            DatePicker dp = editingElement as DatePicker;
            if (dp != null)
            {
                DateTime? dt = dp.SelectedDate;
                if (dt.HasValue)
                return dt.Value;
            }
            return DateTime.Today ;
        }
    }

This is a converter, in case we want to format the date value

    public class DateTimeConverter : IValueConverter
    {
        public object Convert(object value,
                           Type targetType,
                           object parameter,
                           CultureInfo culture)
        {
            DateTime date = (DateTime)value;
            return date.ToString(parameter.ToString());
        }

        public object ConvertBack(object value,
                                  Type targetType,
                                  object parameter,
                                  CultureInfo culture)
        {
            string strValue = value.ToString();
            DateTime resultDateTime;
            if (DateTime.TryParse(strValue, out resultDateTime))
            {
                return resultDateTime;
            }
            return value;
        }
    }

Once we have these classes and namespace decleration(in my ex. it is mapped to local)
we can just say

<local : DataGridDateColumn DateFormat=”yyyy-MM-dd” Binding=”{Binding HireDate}”/>

Advertisements

26 thoughts on “Creating DataGridDateColumn for DataGrid

  1. I tried to use it in dynamic column:

    DataGridDateColumn dateColumn = new DataGridDateColumn();
    dateColumn.Binding = new Binding(“mydateproperty”);
    dateColumn.Binding.Mode = BindingMode.TwoWay;
    Columns.Add(dateColumn);

    Issues:

    a. In non-edit mode column still shows full datetime in invariant culture format.
    b. In edit mode down arrow key does not make next row active.

    How to fix?

  2. Yes. If I use
    DataGridTextColumn textColumn2 = new DataGridTextColumn();
    textColumn2.Binding = new Binding(“mydatetimeproperty”);
    textColumn2.Binding.Mode = BindingMode.TwoWay;

    Columns shows and allows to edit datetime in invariant culture format and down arrow works.

    Replacing column class with DataGridDateColumn() still shows invariant DateTime in non-editing mode. In edit mode it shows date in UI culture format but blocks down arrow key.

    1. looks like we need to raise the DataGridCellEditEndedEvent and for that we need the DataGridRow, I will post here if I find a way to that or if I find anything better on the net

      1. This may fix the down arrow issue. But how to show date only when not in edit mode? I tried to use this converter in CellTemplate:

        in mydatagrid.xaml:

        ….

        in mydatagrid.cs:

        DataGridTemplateColumn templateCol = new DataGridTemplateColumn();

        templateCol.CellTemplate = XamlReader.Load(string.Format(@”
        “,
        “mydateprop”)) as DataTemplate;

        Columns.Add(templateCol);

        However DateTimeConverter is not called.

        1. if we modify the code to add 2 variables
          DataGridRow row = null;
          DataGrid g;
          and modify the following methods, it works to some extent, looks like the TextBox is handling the Keydown event, so we are not getting it. What a pain to change something
          protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
          {
          DependencyObject obj = (this.GetCellContent(dataItem) as TextBlock).Parent;
          while (true)
          {
          obj = VisualTreeHelper.GetParent(obj);

          if (obj is DataGridRow)
          {
          row = obj as DataGridRow;

          }
          else if (obj is DataGrid)
          {
          g = obj as DataGrid;
          break;
          }
          }

          DatePicker dp = new DatePicker();
          dp.KeyDown += new KeyEventHandler(dp_KeyDown);

          Binding b = new Binding();
          b.Path = this.Binding.Path;
          b.Source = this.Binding.Source;
          if (DateFormat != null)
          {
          DateTimeConverter dtc = new DateTimeConverter();
          b.Converter = dtc;
          b.ConverterParameter = DateFormat;
          }
          dp.SetBinding(DatePicker.SelectedDateProperty, this.Binding);

          return dp;
          }

          void dp_KeyDown(object sender, KeyEventArgs e)
          {

          g.CommitEdit(DataGridEditingUnit.Row, true);

          if (e.Key == Key.Down)
          g.SelectedIndex += 1;
          else if (e.Key == Key.Up)
          g.SelectedIndex -= 1;
          }

          1. I tried this with dynamic column. Problem persists. dp.KeyDown event handler is *not* called if down arrow key is pressed when editing control is active.
            It is called for Up arrow key only.

          2. I think we have to subclass DatePicker, override the Keydown event and add our code along with the code in the base class

      1. Im looking for a new DataGridColumn (DataGridComboColumn) that inherits from DataGridBoundColumn, but instead of showing a datetime picker, it will show a combobox that will allow a user to pick a value froma list in a datagrid.

          1. There is a DataGridCheckBoxColumn.. but I do not see any DataGridComboBoxColumn. I am using Silverlight 2. Are you sure it is there?

          2. Yes.. I tried that.. and it works.. but doesnt seem very elegant.. and it makes my XAML look messy. Would prefer a new class for this function.

          3. ok. unless there are gotchas if we follow the same pattern and add combobox instead of datepicker it should work. we need to add a few more properties to relay them to combobox

  3. Thanks for the example.

    A few things: this.Binding is a BindingBase type so I did some converting.

    During the load I’m seeing my initual values for dates display. When I try to manually set the selected date my code returns to the previous value. It must be something with the binding since binding source is currently coming up null on the conversion. How do we set the textblock to the correct value when we only have bindinbase to use for manual selections?

    I was able to paste a value into the date column which was nice since you can’t do that with templated columns.

  4. Thanks very much for your response Lee.

    I’m setting the date values for the row initially with some dummy data. The data is stored as just a DataTable and bound to the DataGrid.

    For some reason when I enter the date manually it doesn’t get set either by typing or using the DatePicker.

    1. After you create a Class and declare your namespace in XAML, you could just use it like usual, as indicated at the bottom of the post.

  5. Can someone tell me why I don’t have a this.Binding.Path and this.Binding.Source ?
    I copied the code exactly.
    It seems that this.Binding return a BindingBase and not a Binding anymore…(?)
    What the hack!?!?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s