This project has moved. For the latest updates, please go here.

DataGridControl - Formatting Auto-Generated DateTime? Column

May 2 at 9:41 PM
How do I change the format used to display a DateTime? Columns' Bound / Source Property of a DataGridControl Bound to an ObservableCollection<MyItemClass>?

I've tried, in the Window.Loaded Event Handler, for the applicable Column, setting its:
  1. CellContentStringFormat (to "ddd MM/dd/yy hh:mm tt") which resulted in the literal "ddd MM/dd/yy hh:mm tt" (without the quotes) being displayed in all Rows.
  2. DisplayedValueConverter to an IValueConverter that "Convert"(s) a DateTime? to a String of Format "ddd MM/dd/yy hh:mm tt".
Developer
May 3 at 1:19 PM
Hi,

Your second option almost works. Here's how I would use it :
Use a column with a CellContentTemplate :
 <xcdg:Column FieldName="OrderDate"
                             Title="Order Date"
                             Width="120"
                             CellContentTemplate="{StaticResource DateDataTemplate}" />
The DataTemplate would use a DateConverter :
 <view:DisplayedValueConverter x:Key="DisplayedValueConverter" />

  <DataTemplate x:Key="DateDataTemplate">
      <TextBlock Text="{Binding Converter={StaticResource DisplayedValueConverter} }" />
    </DataTemplate>
And the DateConverter would use the DateTime.ToString() method to convert the date :
 public class DisplayedValueConverter : IValueConverter
  {
    #region IValueConverter Members
    
    public object Convert( object value, Type targetType, object parameter, CultureInfo culture )
    {
      if( value == null )
        return null;

      System.DateTime dateTime = ( System.DateTime )value;
      return dateTime.ToString( "ddd MM/ dd / yy hh:mm tt" );
    }

    public object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture )
    {
      throw new NotImplementedException();
    }

    #endregion
  }
――――
Get more controls, features, updates and technical support with Xceed Toolkit Plus for WPF
May 3 at 3:32 PM
Edited May 3 at 3:33 PM
Thanks for the quick response!
  1. Where do a I place the "xcdg:Column" Element? If I place it inside the "xcdg:DataGridControl" Element, I get an error saying "Items collection must be empty before using ItemsSource." when I call Window.Show.
  2. a. With .NET's DataGrid, all I have to do is Handle the AutoGeneratingColumn Event and for each "e.Column.SortMemberPath == nameof(MyItemClass.MyDateTime)" set "((System.Windows.Data.Binding)((System.Windows.Controls.DataGridBoundColumn)e.Column).Binding).StringFormat" = "ddd MM/ dd / yy hh:mm tt" (e.g. without having to, in XAML, hard-code a Column's Bound Source Property Name and the Column's Width). Is your solution the closest DataGridControl equivalent to that? b. What does DataGridControl not have a AutoGeneratingColumn Event?
  3. When / how would one, in Code Behind vs. XAML, use the CellContentStringFormat Property?
  4. When / how would one, in Code Behind vs. XAML, use the DisplayedValueConverter Property?
Developer
May 3 at 6:23 PM
Hi,
  1. "xcdg:Column" goes in the DataGridControl's Columns collection, ex :
  <xcdg:DataGridControl x:Name="_dataGrid"
                            ItemsSource="{Binding Source={StaticResource cvsOrders}}" >   
            <xcdg:DataGridControl.Columns>
                <xcdg:Column FieldName="OrderID"
                             Title="Order"
                             Width="100"
                             IsMainColumn="True">
                </xcdg:Column>  
                <xcdg:Column FieldName="OrderDate"
                             Title="Order Date"
                             Width="120"
                             CellContentTemplate="{StaticResource DateDataTemplate}" />  
                <xcdg:Column FieldName="ShipRegion"
                              Visible="False" />               
            </xcdg:DataGridControl.Columns>
        </xcdg:DataGridControl>
  1. In the DataGridControl, you don't need to define all columns individually. If you don't, they will have a default width and look. You could set the AutoCreateColumns to False and only define the columns you want to see and apply specific configuration on them. We don't see the need in our design to have an equivalent to Microsoft AutoGeneratingColumn event. The binding can be done in XAML for each column.
  2. You can try with to use the CellContentStringFormat on a Column to format your date in xaml :
 <xcdg:Column FieldName="OrderDate"
                             Title="Order Date"
                             Width="120"
                             CellContentStringFormat="{}{0:ddd MM/ dd / yy hh:mm tt}"/>
or in code-behind :
 _dataGrid.Columns[ "OrderDate" ].CellContentStringFormat = "{0:ddd MM/ dd / yy hh:mm tt}";
You can use XAML or code-behind for this, it's the same look.
  1. You can try with to use the CellContentTemplate on a Column to format your date in xaml as shown earlier.
    Or in code-behind with Resources defined in XAML:
_dataGrid.Columns[ "OrderDate" ].CellContentTemplate = this.Resources[ "DateDataTemplate" ] as DataTemplate;
or in full code-behind:
      var dataTemplate = new DataTemplate();
      var date = new FrameworkElementFactory( typeof( TextBlock ) );
      date.SetBinding( TextBlock.TextProperty, new Binding( "." ) { Converter = new DisplayedValueConverter() } );
      dataTemplate.VisualTree = date;

      _dataGrid.Columns[ "OrderDate" ].CellContentTemplate = dataTemplate;
XAML is simpler to use for DataTemplates and bindings.


For your situation, using the CellContentStringFormat is easier.

――――
Get more controls, features, updates and technical support with Xceed Toolkit Plus for WPF
May 5 at 11:38 PM
Edited May 6 at 12:00 AM
Thanks for the options.
  1. Re. "You could set the AutoCreateColumns to False and only define the columns you want to see and apply specific configuration on them. We don't see the need in our design to have an equivalent to Microsoft AutoGeneratingColumn event. The binding can be done in XAML for each column.": Umm, having to turn off the entire, automatic feature A (i.e., AutoCreateColumns) such that I have to manually (i.e. in XAML) do what A would've done automatically for me just so I can work around the lack of having feature A.1 (i.e. AutoGeneratingColumn Event) which would've allowed me to override (albeit in Code-Behind / Behavior Bound to an Event vs. XAML), only for some # of elements / ways / times <= all elements / ways / times, what A does automatically does NOT mean there is no "need" for feature A.1. Doing A manually requires me to: a) do it manually for all (vs. <= all) elements / ways / times and b) do so in XAML (vs. Code-Behind) where there is no Compile-Time Identifier and Type checking.
  2. Re. setting CellContentStringFormat in Code-Behind: 2.1) You didn't specify where I could to that. Like I said in my original post, I was trying to do it in the Window.Loaded Event Handler. That would not work using either my originally-posted format ("ddd MM/dd/yy hh:mm tt") or in either of the 2 you provided ("{}{0:ddd MM/ dd / yy hh:mm tt}" or "{0:ddd MM/ dd / yy hh:mm tt}"). I finally got it to work by: a) setting it in the DataGridControl.ItemsSourceChangeCompleted Event Handler and b) using a third format ("{0:ddd MM/dd/yy hh:mm tt}"). 2.1) Where is the documentation saying that I have to: a) prefix the Format String with a "0:" and b) enclose the Format String in braces vs. the Format String required by a .NET String.ToString Method's "format" Parameter. I didn't see anything describing that in the docs for the CellContentStringFormat Property (here: https://xceed.com/wp-content/documentation/xceed-datagrid-for-wpf/webframe.html#Xceed.Wpf.DataGrid~Xceed.Wpf.DataGrid.ColumnBase~CellContentStringFormat.html).
  3. Re. CellContentTemplate via full Code-Behind: I verified that worked also but, again, not if I set it inside the Window.Loaded Event Handler my original post said I was using. It worked when set inside the DataGridControl.ItemsSourceChangeCompleted Event Handler.
  4. Re. "When / how would one, in Code Behind vs. XAML, use the DisplayedValueConverter Property?": I didn't see an answer to that question (from my 5/3/17 10:32 am comment). I tried it from the DataGridControl.ItemsSourceChangeCompleted Event Handler, and it still did nothing, even though both CellContentStringFormat and CellContentTemplate methods worked from that same Event Handler and the latter method worked using the same IConverter I was trying to set DisplayedValueConverter to.
May 18 at 4:30 PM
Could I get a response please?
Developer
May 19 at 2:12 PM
Edited May 19 at 2:13 PM
Hi,

ONE. Instead of managing an non-available AutocreateColumn event, you could register to the DataGridControl.Columns.CollectionChanged event (called for a column added, removed or a reset) and then scan the columns to adjust what you want (width...). ex :
  public partial class MainWindow : Window
  {
    public MainWindow()
    {
      InitializeComponent();

      // Scan columns here (for columns defined in XAML)
      this.ScanColumns( null, _dataGrid.Columns );

      _dataGrid.Columns.CollectionChanged += Columns_CollectionChanged;

      _dataGrid.ItemsSource = new List<MyData>()
      {
        new MyData() { OrderID = 101, OrderDate = new DateTime( 2017, 5, 10), ShipRegion = "USA" },
        new MyData() { OrderID = 122, OrderDate = new DateTime( 2017, 5, 15), ShipRegion = "Canada" },
        new MyData() { OrderID = 133, OrderDate = new DateTime( 2017, 4, 25), ShipRegion = "Mexico" },
      };
    }

    private void Columns_CollectionChanged( object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e )
    {
      // Scan columns here for columns modified later
      this.ScanColumns( e.OldItems as IList, e.NewItems as IList );
    }

    private void ScanColumns( IList oldColumns, IList newColumns )
    {
      if( newColumns != null )
      {
        foreach( var column in newColumns )
        {
          var c = column as Column;
          if( (c != null) && (c.FieldName == "OrderID") )
          {
            c.Width = 200;
          }
        }
      }
    }
  }
TWO. You can use this :
_dataGrid.Columns[ "OrderDate" ].CellContentStringFormat = "{0:ddd MM/ dd / yy hh:mm tt}";
right after the InitializeComponent(); of the MainWindow. You can try it by modifying the DataGridView.xaml.cs file, which is the sample for the DataGridControl in the Toolkit LiveExplorer. You can find this file here : Xceed.Wpf.Toolkit.LiveExplorer/Samples/DataGrid/Views/DatagridView.xaml.cs.

By the way, you said you made it working by using a third format ("{0:ddd MM/dd/yy hh:mm tt}"), which is exactly the same I gave you : "{0:ddd MM/ dd / yy hh:mm tt}".

You can have a look at this page on how to define a Date Format string : https://msdn.microsoft.com/en-us/library/8kb3ddd4(v=vs.110).aspx.

THREE. Again, in the DataGridControl sample from the Toolkit LiveExplorer, simply setting :
var dataTemplate = new DataTemplate();
      var date = new FrameworkElementFactory( typeof( TextBlock ) );
      date.SetBinding( TextBlock.TextProperty, new Binding( "." ) { Converter = new DisplayedValueConverter() } );
      dataTemplate.VisualTree = date;

      _dataGrid.Columns[ "OrderDate" ].CellContentTemplate = dataTemplate;
right after the InitializeComponent(); call from the MainWindow is a good place for it to work. It's a binding, so right after the DataGridControl is defined, you can set it and the binding will be realized when needed.
You can alos do it in the MainWindow.Loaded event. This is done after the DataGridControl is defined in XAML. The Binding will be realized when needed. Please try it in the DataGridControl sample of the LiveExplorer.

FOUR. The DisplayedValueConverter is not a property, but a Converter defined earlier in this thread. The goal is to use it inside the Binding, through the Binding.Converter property, to convert the bound value to a DateTime.

Please copy the following code in a new C# project and run it :
<Window x:Class="WpfApplication125.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication125"
        mc:Ignorable="d"
        xmlns:xcdg="http://schemas.xceed.com/wpf/xaml/datagrid"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
    <xcdg:DataGridControl x:Name="_dataGrid">
      <xcdg:DataGridControl.Columns>
        <xcdg:Column FieldName="OrderID"
                     Title="Order"
                     Width="100"
                     IsMainColumn="True">
        </xcdg:Column>
        <xcdg:Column FieldName="OrderDate"
                     Title="Order Date"
                     Width="150"/>
        <xcdg:Column FieldName="ShipRegion"
                     Visible="False" />
      </xcdg:DataGridControl.Columns>
    </xcdg:DataGridControl>
  </Grid>
</Window>
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace WpfApplication125
{
  /// <summary>
  /// Interaction logic for MainWindow.xaml
  /// </summary>
  public partial class MainWindow : Window
  {
    public MainWindow()
    {
      InitializeComponent();

      _dataGrid.ItemsSource = new List<MyData>()
      {
        new MyData() { OrderID = 101, OrderDate = new DateTime( 2017, 5, 10), ShipRegion = "USA" },
        new MyData() { OrderID = 122, OrderDate = new DateTime( 2017, 5, 15), ShipRegion = "Canada" },
        new MyData() { OrderID = 133, OrderDate = new DateTime( 2017, 4, 25), ShipRegion = "Mexico" },
      };

      // This can also be done in MainWindow.Loaded event handler !
      var dataTemplate = new DataTemplate();
      var date = new FrameworkElementFactory( typeof( TextBlock ) );
      date.SetBinding( TextBlock.TextProperty, new Binding( "." ) { Converter = new DisplayedValueConverter() } );
      dataTemplate.VisualTree = date;

      _dataGrid.Columns[ "OrderDate" ].CellContentTemplate = dataTemplate;
    }
  }

  public class MyData
  {
    public int OrderID
    {
      get;
      set;
    }

    public DateTime OrderDate
    {
      get;
      set;
    }

    public string ShipRegion
    {
      get;
      set;
    }
  }

  public class DisplayedValueConverter : IValueConverter
  {
    #region IValueConverter Members

    public object Convert( object value, Type targetType, object parameter, CultureInfo culture )
    {
      if( value == null )
        return null;

      System.DateTime dateTime = ( System.DateTime )value;
      return dateTime.ToString( "ddd MM/ dd / yy hh:mm tt" );
    }

    public object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture )
    {
      throw new NotImplementedException();
    }

    #endregion
  }
}
――――
Get more controls, features, updates and technical support with Xceed Toolkit Plus for WPF