A minimalistic template for an editable WPF DataGrid 30 September 2009 Diederik-Krols WPF By default, a WPF DataGrid operates in its birthday suit, with no decorations at all. For your end user this is not intuitive, so you should provide some fig-leafs here and there. In my humble opinion, at least two rows should be easily identifiable in any editable grid: the 'new' row (generally at the bottom of the grid), and the row that is currently being edited. Both rows deserve their own template. As you certainly know, you can get very far with WPF templating. In this article however I'll go for the minimalistic approach and display an asterisk in front of the 'new' row, and a pencil in front of the row in edit mode. An asterisk for the 'New' row This is what we're trying to achieve: We will get this result by applying a data template to the row's header. So start with a data template in XAML, somewhere in a resource element: <DataTemplate x:Key="AsteriskTemplate" > <TextBlock Margin="0" HorizontalAlignment="Center" VerticalAlignment="Center" ToolTip="New" Text="*"> </TextBlock> </DataTemplate> This template should be applied when loading the row, so hook an event handler to LoadingRow, also in XAML: <toolkit:DataGrid x:Name="DriversDataGrid" ItemsSource="{Binding Source={x:Static local:FormulaOneDriver.GetAll}}" CommandManager.PreviewExecuted="DriversDataGrid_PreviewDeleteCommandHandler" LoadingRow="DriversDataGrid_LoadingRow" RowEditEnding="DriversDataGrid_RowEditEnding" Here's the C# code for the event handler: /// <summary> /// Apply Asterisk DataTemplate to New Row /// </summary> /// <param name="sender">Sender of the event: the DataGrid.</param> /// <param name="e">Event arguments.</param> private void DriversDataGrid_LoadingRow(object sender, DataGridRowEventArgs e) { if (e.Row.Item == CollectionView.NewItemPlaceholder) { e.Row.HeaderTemplate = (DataTemplate)DriversDataGrid.FindResource("AsteriskTemplate"); e.Row.UpdateLayout(); } } A pencil for the 'Edit' row This is what we're trying to achieve: Again we define the look of the row header through a data template: <DataTemplate x:Key="PencilTemplate" > <Image Source="/DataGridSample;component/pencil.png" Margin="0" Height="11" Width="11" HorizontalAlignment="Center" VerticalAlignment="Center" /> </DataTemplate> The template is applied when we enter edit-mode. So you should handle the BeginningEdit event. When leaving edit mode, don't forget to revert to the default template, through the RowEditEnding event: <toolkit:DataGrid x:Name="DriversDataGrid" ItemsSource="{Binding Source={x:Static local:FormulaOneDriver.GetAll}}" CommandManager.PreviewExecuted="DriversDataGrid_PreviewDeleteCommandHandler" LoadingRow="DriversDataGrid_LoadingRow" BeginningEdit="DriversDataGrid_BeginningEdit" RowEditEnding="DriversDataGrid_RowEditEnding" Here's the event handler to apply the template: /// <summary> /// A row goes in edit mode. So display the pencil in its header. /// </summary> /// <param name="sender">Sender of the event: the DataGrid.</param> /// <param name="e">Event arguments.</param> private void DriversDataGrid_BeginningEdit(object sender, DataGridBeginningEditEventArgs e) { e.Row.HeaderTemplate = (DataTemplate)DriversDataGrid.FindResource("PencilTemplate"); e.Row.UpdateLayout(); } And here's how to remove the template: /// <summary> /// A row leaves edit mode: persist it, and revert to the default header. /// </summary> /// <param name="sender">Sender of the event: the DataGrid.</param> /// <param name="e">Event arguments.</param> private void DriversDataGrid_RowEditEnding(object sender, DataGridRowEditEndingEventArgs e) { [...] e.Row.HeaderTemplate = null; }
Workaround: WPF DataGrid hangs Visual Studio.NET 29 September 2009 Diederik-Krols WPF Welcome to a WPF DataGrid crash course ... literally. It will show you how Cider can give you a hangover. It took me 45 minutes, 10 process kills, and a full reboot to figure out what was going on in my application, so I gladly share my experience. Part one: the bug Create a WPF Form with an empty DataGrid, like this: <Window x:Class="U2UConsult.DockOfTheBay.DataGridWorkaround" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:toolkit="http://schemas.microsoft.com/wpf/2008/toolkit" Title="Bug Workaround" SizeToContent="WidthAndHeight"> <Grid> <toolkit:DataGrid> <toolkit:DataGrid.Columns> </toolkit:DataGrid.Columns> </toolkit:DataGrid> </Grid> </Window> Then, add a style that adds a Margin to the Button control, e.g. as a Resource for the Window: <!-- Kaboom--> <Window.Resources> <Style TargetType="{x:Type Button}"> <Setter Property="Margin" Value="3" /> </Style> </Window.Resources> Now is a good time to save your solution. Add a column to the DataGrid: <toolkit:DataGridTextColumn Header="Test" /> You will probably see something like this now, with an ever increasing counter: Cider -the WPF Designer for Visual Studio.NET- entered an infinite loop. Start Task Manager and inspect Visual Studio.NET's process, it's consuming close to 100% CPU: Kill the process. What you just experienced is a confirmed bug. The current version of the WPF DataGrid hangs Visual Studio.NET's Designer when buttons are decorated with a margin. Part two: the Workaround So now you know what may cause this behavior. Fortunately this shouldn't stop you from using a DataGrid, because the workaround is simple. All you need to do is embed a style in your DataGrid that resets the button margin to zero: <toolkit:DataGrid> <!-- Workaround for bug: No button margins allowed --> <Style TargetType="{x:Type Button}"> <Setter Property="Margin" Value="0" /> </Style> <!-- End of Workaround --> <toolkit:DataGrid.Columns> <toolkit:DataGridTextColumn Header="Test" /> </toolkit:DataGrid.Columns> </toolkit:DataGrid> This is just one of the many inconveniences with the current version of the toolkit. I strongly advise you to regularly check the issue tracker if you're planning to use its components in production.
Inserting, Updating, and Deleting from a WPF DataGrid 29 September 2009 Diederik-Krols WPF You already know how easy it is to implement databinding in the WPF DataGrid from a previous article. Let's dive just a little bit deeper, and decorate this application with the code to trigger insert-, update- and delete-calls against the underlying Model and/or Data Access Layer. I'll stick to the M-V-VM approach where the View decides when to update the Model (usually via executing a Command), but the ViewModel decides how to update the Model (by implementing that same Command). For starters, decorate the Formula 1 Driver class with the corresponding -dummy- Save() and Delete() methods: /// <summary> /// Persists a Formula 1 Driver /// </summary> public void Save() { // Ask the Model or the DAL to persist me... } /// <summary> /// Deletes a Formula 1 Driver. /// </summary> public void Delete() { // Ask the Model or the DAL to delete me... } Insert and Update Objects are persisted (inserted or updated) when the user leaves a row that he was editing. Moving to another cell in the same row updates the corresponding property through data binding, but doesn't signal the Model (or the Data Access Layer) yet. The only useful event is DataGrid.RowEditEnding. This is fired just before committing the modified row. Here's the registration of the event handler, in XAML: <toolkit:DataGrid x:Name="DriversDataGrid" ItemsSource="{Binding Source={x:Static local:FormulaOneDriver.GetAll}}" RowEditEnding="DriversDataGrid_RowEditEnding" AutoGenerateColumns="False" CanUserAddRows="True" The RowEditEnding event is called when the user leaves Edit-mode, and can be the result of Cancelling, or Committing. Of course you only need to react to a Commit. This is the even handler: /// <summary> /// Persists a row after editing (update or insert). /// </summary> /// <param name="sender">Sender of the event: the DataGrid.</param> /// <param name="e">Event arguments.</param> private void DriversDataGrid_RowEditEnding(object sender, DataGridRowEditEndingEventArgs e) { // Only act on Commit if (e.EditAction == DataGridEditAction.Commit) { FormulaOneDriver driver = e.Row.DataContext as FormulaOneDriver; driver.Save(); } } Important remark In its current implementation, a DataGridTemplateColumn will NOT trigger any of the DataGrid's events. There seems to be no elegant work-around for this. In this sample, if you only change the LatestVictory date of a Formula 1 Driver, the RowEditEnding will not fire, hence the Save() method on the Formula 1 Driver will NOT be called. Needless to say that this may be a showstopper ! Delete Objects are deleted if the user selects one or multiple rows in the DataGrid and hits the Delete button. In most cases we would like to ask the user for a confirmation. We can do this by intercepting the Execute call via tunelling, and allow the user to cancel the delete command before it actually executes. This sounds pretty impressive, but it just involves writing an event handler for CommandManger.PreviewExecute. You can register this event handler in XAML: <toolkit:DataGrid x:Name="DriversDataGrid" ItemsSource="{Binding Source={x:Static local:FormulaOneDriver.GetAll}}" CommandManager.PreviewExecuted="DriversDataGrid_PreviewDeleteCommandHandler" RowEditEnding="DriversDataGrid_RowEditEnding" AutoGenerateColumns="False" CanUserAddRows="True" This is the actual eventhandler code, in which we can cancel the event: /// <summary> /// Asks for confirmation of a delete. /// </summary> /// <param name="sender">Sender of the event: the DataGrid.</param> /// <param name="e">Event arguments.</param> private void DriversDataGrid_PreviewDeleteCommandHandler(object sender, ExecutedRoutedEventArgs e) { if (e.Command == DataGrid.DeleteCommand) { if (!(MessageBox.Show("Are you sure you want to delete?", "Please confirm.", MessageBoxButton.YesNo) == MessageBoxResult.Yes)) { // Cancel Delete. e.Handled = true; } } } Here's how it looks like in action: If the Delete action is not cancelled, then the underlying collection is updated. This triggers its CollectionChanged event, in which you can call the Formula 1 Driver's Delete() method: /// <summary> /// The collection of drivers just changed: add or remove /// </summary> /// <param name="sender">Sernder of the Event.</param> /// <param name="e">Event Arguments.</param> private void Drivers_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { // Only Delete if (e.Action == NotifyCollectionChangedAction.Remove) { foreach (FormulaOneDriver driver in e.OldItems) { driver.Delete(); } } } Here's the registration of the event handler, put it e.g. in the constructor of the form: ObservableCollection<FormulaOneDriver> drivers = this.DriversDataGrid.ItemsSource as ObservableCollection<FormulaOneDriver>; drivers.CollectionChanged += new NotifyCollectionChangedEventHandler(this.Drivers_CollectionChanged); For completeness, here's the XAML for the sample form: <Window x:Class="U2UConsult.DockOfTheBay.DataGridSampleWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:U2UConsult.DockOfTheBay" xmlns:toolkit="http://schemas.microsoft.com/wpf/2008/toolkit" Title="WPF DataGrid CRUD Sample" SizeToContent="WidthAndHeight" Icon="/DataGridSample;component/dotbay.png" > <Window.Resources> <Style TargetType="{x:Type toolkit:DatePickerTextBox}"> <Setter Property="Text" Value="None" /> <Setter Property="Background" Value="#00000000" /> </Style> </Window.Resources> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <toolkit:DataGrid x:Name="DriversDataGrid" ItemsSource="{Binding Source={x:Static local:FormulaOneDriver.GetAll}}" CommandManager.PreviewExecuted="DriversDataGrid_PreviewDeleteCommandHandler" RowEditEnding="DriversDataGrid_RowEditEnding" AutoGenerateColumns="False" CanUserAddRows="True" CanUserDeleteRows="True" CanUserSortColumns="True" CanUserReorderColumns="True" AlternatingRowBackground="WhiteSmoke" RowHeaderWidth="16" Grid.Column="0" Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Center"> <toolkit:DataGrid.Columns> <toolkit:DataGridTextColumn Header="Name" Binding="{Binding Name}" CanUserReorder="True" IsReadOnly="False" CanUserSort="True" SortMemberPath="Name"/> <toolkit:DataGridComboBoxColumn x:Name="TeamsCombo" Header="Team" ItemsSource="{Binding Source={x:Static local:FormulaOneTeam.GetAll}}" SelectedValueBinding="{Binding TeamId}" SelectedValuePath="Key" DisplayMemberPath="Value.Name" SortMemberPath="Team.Name" /> <toolkit:DataGridTextColumn Header="Pole Positions" Binding="{Binding PolePositions, NotifyOnSourceUpdated=True}" CanUserSort="True" /> <toolkit:DataGridTemplateColumn Header="Latest Victory" CanUserSort="True" SortMemberPath="LatestVictory"> <toolkit:DataGridTemplateColumn.CellTemplate > <DataTemplate > <toolkit:DatePicker SelectedDate="{Binding LatestVictory}" BorderThickness="0" /> </DataTemplate > </toolkit:DataGridTemplateColumn.CellTemplate > <toolkit:DataGridTemplateColumn.CellEditingTemplate > <DataTemplate > <toolkit:DatePicker SelectedDate="{Binding LatestVictory, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" BorderThickness="0"/> </DataTemplate > </toolkit:DataGridTemplateColumn.CellEditingTemplate > </toolkit:DataGridTemplateColumn> </toolkit:DataGrid.Columns> </toolkit:DataGrid> </Grid> </Window> And the C# as well: namespace U2UConsult.DockOfTheBay { using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Windows; using System.Windows.Input; using Microsoft.Windows.Controls; /// <summary> /// WPF DataGrid CRUD Sample. /// </summary> public partial class DataGridSampleWindow : Window { /// <summary> /// Initializes a new instance of the DataGridSampleWindow class. /// </summary> public DataGridSampleWindow() { InitializeComponent(); ObservableCollection<FormulaOneDriver> drivers = this.DriversDataGrid.ItemsSource as ObservableCollection<FormulaOneDriver>; drivers.CollectionChanged += new NotifyCollectionChangedEventHandler(this.Drivers_CollectionChanged); } /// <summary> /// Persists a row after editing (update or insert). /// </summary> /// <param name="sender">Sender of the event: the DataGrid.</param> /// <param name="e">Event arguments.</param> private void DriversDataGrid_RowEditEnding(object sender, DataGridRowEditEndingEventArgs e) { // Only act on Commit if (e.EditAction == DataGridEditAction.Commit) { FormulaOneDriver driver = e.Row.DataContext as FormulaOneDriver; driver.Save(); } } /// <summary> /// Asks for confirmation of a delete. /// </summary> /// <param name="sender">Sender of the event: the DataGrid.</param> /// <param name="e">Event arguments.</param> private void DriversDataGrid_PreviewDeleteCommandHandler(object sender, ExecutedRoutedEventArgs e) { if (e.Command == DataGrid.DeleteCommand) { if (!(MessageBox.Show("Are you sure you want to delete?", "Please confirm.", MessageBoxButton.YesNo) == MessageBoxResult.Yes)) { // Cancel Delete. e.Handled = true; } } } /// <summary> /// The collection of drivers just changed: add or remove /// </summary> /// <param name="sender">Sernder of the Event.</param> /// <param name="e">Event Arguments.</param> private void Drivers_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { // Only Delete if (e.Action == NotifyCollectionChangedAction.Remove) { foreach (FormulaOneDriver driver in e.OldItems) { driver.Delete(); } } } } } The Formula 1 Driver and Formula 1 Team classes are still the same from the previous article -except for the dummy Save() and Delete() methods.
Codeless two-way data binding to a WPF DataGrid 26 September 2009 Diederik-Krols WPF WPF 4.0 will finally contain a DataGrid. If you can't wait for that one, then all you have to do is download the current release of the WPF Toolkit. The DataGrid control in this toolkit is considered as stable, so why not give it a test drive? Let's create a list of Formula 1 Drivers and two-way data bind it to a WPF DataGrid. In our little object model -the ViewModel if you like- a Formula 1 Driver is associated to a Formula 1 Team: Here's the startup code for the Formula 1 Driver class: namespace U2UConsult.DockOfTheBay { using System.Collections.Generic; /// <summary> /// A Formula1 Team. /// </summary> public class FormulaOneTeam { /// <summary> /// Gets the entire list of Formula1 teams. /// </summary> public static Dictionary<int, FormulaOneTeam> GetAll { get { return new Dictionary<int, FormulaOneTeam>() { { 0, new FormulaOneTeam { TeamId = 0, Name = "Unknown" } }, { 1, new FormulaOneTeam { TeamId = 1, Name = "Nintendo" } }, { 2, new FormulaOneTeam { TeamId = 2, Name = "Top Gear" } }, { 3, new FormulaOneTeam { TeamId = 3, Name = "Wacky Races" } } }; } } /// <summary> /// Gets or sets the id of the Formula1 team. /// </summary> public int TeamId { get; set; } /// <summary> /// Gets or sets the name of the Formula1 team. /// </summary> public string Name { get; set; } } } In a real life scenario a class that's involved in data binding should implement the INotifyPropertyChanged and IDataError interfaces. An example of the latter can be found in a previous article, an alternative for validating is the BindingGroup class, that I will discuss in a future article (no hyperlink yet ). In M-V-VM applications you will probably implement this behavior by inheriting from some ViewModel base class. Here's the implementation of the Formula 1 Team class: namespace U2UConsult.DockOfTheBay { using System.Collections.Generic; /// <summary> /// A Formula1 Team. /// </summary> public class FormulaOneTeam { /// <summary> /// The list of all Formula 1 Teams. /// </summary> private static Dictionary<int, FormulaOneTeam> getAll; /// <summary> /// Initializes static members of the FormulaOneTeam class. /// </summary> static FormulaOneTeam() { getAll = new Dictionary<int, FormulaOneTeam>() { { 0, new FormulaOneTeam { TeamId = 0, Name = "Unknown" } }, { 1, new FormulaOneTeam { TeamId = 1, Name = "Nintendo" } }, { 2, new FormulaOneTeam { TeamId = 2, Name = "Top Gear" } }, { 3, new FormulaOneTeam { TeamId = 3, Name = "Wacky Races" } } }; } /// <summary> /// Gets the entire list of Formula 1 Teams. /// </summary> public static Dictionary<int, FormulaOneTeam> GetAll { get { return getAll; } } /// <summary> /// Gets or sets the id of the Formula 1 Team. /// </summary> public int TeamId { get; set; } /// <summary> /// Gets or sets the name of the Formula 1 Team. /// </summary> public string Name { get; set; } } } The ideal collection type for complex data binding (that's binding one control to a collection of objects) is ObservableCollection(T). ObservableCollection(T) is to WPF what BindingList(T) is to WinForms. ObservableCollection(T) only implements the INotifyCollectionChanged interface. This is sufficient to do complex data binding -at least for WPF's ItemControl subclasses like ListView, ComboBox and DataGrid. In a WinForms application the ObservableCollection(T) class loses all its magic. To continue with the sample, add to the Formula 1 Driver class a method that returns such a collection: /// <summary> /// Gets a two-way bindable list of Formula 1 drivers. /// </summary> public static ObservableCollection<FormulaOneDriver> GetAll { get { ObservableCollection<FormulaOneDriver> drivers = new ObservableCollection<FormulaOneDriver>() { new FormulaOneDriver(){ Name = "Super Mario", TeamId = 1, PolePositions = 2 }, new FormulaOneDriver(){ Name = "The Stig", TeamId = 2, PolePositions = 20, LatestVictory = DateTime.Today }, new FormulaOneDriver(){ Name = "Dick Dastardley", TeamId = 3, PolePositions = 0 }, new FormulaOneDriver(){ Name = "Luigi", TeamId = 1, PolePositions = 2 } }; return drivers; } } If you use this collection as ItemSource of the DataGrid, then the result should look like this: The DataGrid has a couple of intuitive properties, as you can see in its XAML definition: <toolkit:DataGrid x:Name="DriversDataGrid" ItemsSource="{Binding Source={x:Static local:FormulaOneDriver.GetAll}}" AutoGenerateColumns="False" CanUserAddRows="True" CanUserDeleteRows="True" CanUserSortColumns="True" CanUserReorderColumns="True" AlternatingRowBackground="WhiteSmoke" RowHeaderWidth="16" Grid.Column="0" Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Center"> <!-- toolkit:DataGrid.Columns [...] --> </toolkit:DataGrid> The DataGrid can be populated with four column types: DataGridTextColumn, DataGridCheckBoxColumn, DataGridComboBoxColumn, DataGridHyperlinkColumn, and DataGridTemplateColumn. This is an example of a DataGridTextColumn: <toolkit:DataGridTextColumn Header="Name" Binding="{Binding Name}" CanUserReorder="True" IsReadOnly="False" CanUserSort="True" SortMemberPath="Name"/> Let's look for an excuse to use a DataGridComboBoxColumn. It's not a good idea to confront end users with technical keys, so instead of showing the team's identity we'll display its name. When the grid cell is in edit mode, we let the user select from a ComboBox: The embedded ComboBox is bound to a Dictionary, its citizens have a Key and a Value property that you should respectively bind to SelectedValuePath and DisplayMemberPath: <toolkit:DataGridComboBoxColumn x:Name="TeamsCombo" Header="Team" ItemsSource="{Binding Source={x:Static local:FormulaOneTeam.GetAll}}" SelectedValueBinding="{Binding TeamId}" SelectedValuePath="Key" DisplayMemberPath="Value.Name" SortMemberPath="Team.Name" /> Make sure to specify the correct SortMemberPath. Users can sort the rows by clicking on the column header(s). The 'Team' column is bound to the TeamId, but it displays the team's name, so it should sort by name. The following screenshot shows that the grid is sorted on the Team column (notice the sort icon in the column header): The LatestVictory property of the Formula 1 Driver is of the type DateTime. In edit mode, it makes sense to use a DatePicker control to let the user select the date. This control can also be found in the WPF Toolkit. Here's how it can be used in a DataGridTemplateColumn: <toolkit:DataGridTemplateColumn Header="Latest Victory" CanUserSort="True" SortMemberPath="LatestVictory"> <toolkit:DataGridTemplateColumn.CellTemplate > <DataTemplate > <toolkit:DatePicker SelectedDate="{Binding LatestVictory}" BorderThickness="0"/> </DataTemplate > </toolkit:DataGridTemplateColumn.CellTemplate > </toolkit:DataGridTemplateColumn> Just like any WPF control the DataPicker is über-stylable. I don't like the default GUI settings for it, so I tweaked the GUI a little bit by hiding its border and giving the embedded TextBox a transparent backcolor. You have access to this via a Style. That's also the place to override the default watermark (through the Text property): <Style TargetType="{x:Type toolkit:DatePickerTextBox}"> <Setter Property="Text" Value="None" /> <Setter Property="Background" Value="#00000000" /> </Style> Here's how the DatePicker looks like when it's expanded: When inserting is allowed, then the DataGrid displays an empty row at the bottom. Unfortunately there seems to be no way to display the intuitive asterisk in its row header. When you start editing this row, the default constructor of the bound type is called to initialize the cells. Here's how the one for the Formula 1 driver looks like: /// <summary> /// Initializes a new instance of the FormulaOneDriver class. /// </summary> public FormulaOneDriver() { this.Name = "<Name>"; } For completeness' sake, here's the full XAML for the sample form: <Window x:Class="U2UConsult.DockOfTheBay.DataGridSampleWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:U2UConsult.DockOfTheBay" xmlns:toolkit="http://schemas.microsoft.com/wpf/2008/toolkit" Title="WPF DataGrid Sample" SizeToContent="WidthAndHeight" Icon="/DataGridSample;component/dotbay.png" > <Window.Resources> <Style TargetType="{x:Type toolkit:DatePickerTextBox}"> <Setter Property="Text" Value="None" /> <Setter Property="Background" Value="#00000000" /> </Style> </Window.Resources> <Grid> <toolkit:DataGrid x:Name="DriversDataGrid" ItemsSource="{Binding Source={x:Static local:FormulaOneDriver.GetAll}}" AutoGenerateColumns="False" CanUserAddRows="True" CanUserDeleteRows="True" CanUserSortColumns="True" CanUserReorderColumns="True" AlternatingRowBackground="WhiteSmoke" RowHeaderWidth="16" Grid.Column="0" Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Center"> <toolkit:DataGrid.Columns> <toolkit:DataGridTextColumn Header="Name" Binding="{Binding Name}" CanUserReorder="True" IsReadOnly="False" CanUserSort="True" SortMemberPath="Name"/> <toolkit:DataGridComboBoxColumn x:Name="TeamsCombo" Header="Team" ItemsSource="{Binding Source={x:Static local:FormulaOneTeam.GetAll}}" SelectedValueBinding="{Binding TeamId}" SelectedValuePath="Key" DisplayMemberPath="Value.Name" SortMemberPath="Team.Name" /> <toolkit:DataGridTextColumn Header="Pole Positions" Binding="{Binding PolePositions}" CanUserSort="True" /> <toolkit:DataGridTemplateColumn Header="Latest Victory" CanUserSort="True" SortMemberPath="LatestVictory"> <toolkit:DataGridTemplateColumn.CellTemplate > <DataTemplate > <toolkit:DatePicker SelectedDate="{Binding LatestVictory}" BorderThickness="0"/> </DataTemplate > </toolkit:DataGridTemplateColumn.CellTemplate > </toolkit:DataGridTemplateColumn> </toolkit:DataGrid.Columns> </toolkit:DataGrid> </Grid> </Window> And here's the C# code: namespace U2UConsult.DockOfTheBay { using System.Windows; /// <summary> /// Two-way binding to a Data Grid Sample. /// </summary> /// <remarks>Not much to see here: it's all in the XAML.</remarks> public partial class DataGridSampleWindow : Window { /// <summary> /// Initializes a new instance of the DataGridSampleWindow class. /// </summary> public DataGridSampleWindow() { InitializeComponent(); } } }