Colin Eberhardt's Adventures in WPF

Binding a Silverlight DataGrid to dynamic data Part 2 – editable data and INotifyPropertyChanged

April 22nd, 2009

In my previous blog post I described a method for solving the commonly faced problem of binding a Silverlight DataGrid to dynamic data, the form of which is not know at compile time. This blog post extends on the method previously described, adding change notification, allowing the DataGrid to synchronise the UI with changes to the bound data and to allow the user to edit the DataGrid’s contents.

To briefly recap my previous post, the dynamic data is copied to a collection of Rows:

public class Row
{
  private Dictionary<string, object> _data = new Dictionary<string, object>();
 
  public object this [string index]
  {
    get { return _data[index]; }
    set { _data[index] = value; }
  }
}

Where the row ‘properties’ which are implemented via the string indexer bound to the grid via a ValueConverter. The XAML for the DataGrid looks like the following:

<data:DataGrid Name="_dataGrid" AutoGenerateColumns="False"  IsReadOnly="False" Margin="5">
    <data:DataGrid.Columns>
        <data:DataGridTextColumn Header="Forename"
                                 Binding="{Binding Converter={StaticResource RowIndexConverter},
                                    ConverterParameter=Forename}"/>
        <data:DataGridTextColumn Header="Surname" 
                                 Binding="{Binding Converter={StaticResource RowIndexConverter},
                                    ConverterParameter=Surname}"/>
        ...               
    </data:DataGrid.Columns>
</data:DataGrid>

Where the ValueConverter takes the ConverterParameter and uses it to index the bound Row instance:

public class RowIndexConverter : IValueConverter
{
  public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  {
    Row row = value as Row;
    string index = parameter as string;
    return row[index];
  } 
}

It is important to note that the Binding does not have a Path defined. As a result, the binding source is the Row instance itself rather than a property of the Row as would more normally be the case when binding data. The rest of the post described how to create a collection view which sorts the collection via the Row’s string indexer. You can read about this in full in my previous blog post.

Now, one question a few people have asked me is how to implement INotifyPropertyChanged on the Row class in order to make the DataGrid editable. Simply implementing INotifyPropertyChanged on the Row class, with events raised from the index setter, will not make the grid editable or synchronised with the bound data. The underlying problem here is that the Binding does not have a Path specified, therefore, the binding framework does not know which property name to look out for when handling PropertyChanged events. Interestingly I would expect the above bindings to be updated if a PropertyChanged event were raised with a null or empty PropertyName string, however they do not.

So if the Binding framework needs a Path for the binding in order for change notification to work, we will have to fabricate one for this purpose! We can modify the Row class as follows, adding a Data property which is simply a reference to the Row instance itself:

public class Row : INotifyPropertyChanged
{
    private Dictionary<string, object> _data = new Dictionary<string, object>();
 
    public object this [string index]
    {
        get
        {
            return _data[index];
        }
        set
        {
            _data[index] = value;
 
            OnPropertyChanged("Data");
        }
    }
 
    public object Data
    {
        get
        {
            return this;
        }
    }
 
    public event PropertyChangedEventHandler PropertyChanged;
 
    protected void OnPropertyChanged(string property)
    {
        if (PropertyChanged!=null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
    }
}

In order to bind this class we modify our bindings so that the source Path points to the Data property:

...
<data:DataGridTextColumn Header="Forename"
                           Binding="{Binding Path=Data, Converter={StaticResource RowIndexConverter},
                           ConverterParameter=Forename}"/>
<data:DataGridTextColumn Header="Forename"
                           Binding="{Binding Path=Data, Converter={StaticResource RowIndexConverter},
                           ConverterParameter=Surname}"/>
...

With this change in place, all of our bindings are bound to the Row’s Data property. Again, the Row’s indexer is being accessed via our value converter. However, if any ‘property’ change via the string indexer setter will result in all of our bindings being updated for that Row. I know, it is not the most efficient solution – but at least it work!

The next problem is making the DataGrid editable. The problem we face here is that all of our bindings are to the same property, Data. When a cell is edited, the binding framework will quite happily call the Data properties setter, with the updated value. However, how do we know which property of the Row was really being set? The only place we hold this information is in the ConverterParameter which is fed to our RowIndexConverter value converter. Fortunately, there is a solution to this problem, when the binding framework updates the bound object, it will first invoke the ConvertBack method on our value converter. This gives us the opportunity to ‘capture’ the converter parameter and feed it into the setter of our Data property, as shown below:

public class RowIndexConverter : IValueConverter
{
    ...
 
    public object ConvertBack(object value, Type targetType,
        object parameter, CultureInfo culture)
    {
        return new PropertyValueChange(parameter as string, value);
    }
}

Where PropertyValueChanged is a simple value object:

public class PropertyValueChange
{
    private string _propertyName;
    private object _value;
 
    public object Value
    {
        get { return _value; }
    }
 
    public string PropertyName
    {
        get { return _propertyName; }
    }
 
    public PropertyValueChange(string propertyName, object value)
    {
        _propertyName = propertyName;
        _value = value;
    }
}

The setter for the Data property will now be invoked with an instance of PropertyValueChanged, giving it all the information it requires to invoke the string indexer with the correct ‘property’ name:

public class Row : INotifyPropertyChanged
{
    ...
 
    public object Data
    {
        get { return this; }
        set
        {
            PropertyValueChange setter = value as PropertyValueChange;
            _data[setter.PropertyName] = setter.Value;
        }
    }
    ...
}

With this change in place, we the user is able to edit the data bound to the DataGrid, and the binding ensures that the DataGrid UI is synchronised with any changes to the bound data that may happen elsewhere in our code.

One final thought that struck me is that this technique uses the Binding’s associated ValueConverter to access the Row’s string indexer, but what if we wanted to use a ValueConverter for something else like formatting? Fortunately it is a pretty straightforward process to nest one value converter inside another. For details, see the attached project. For now, have quick play with the grid below, where all the columns are editable and the button click event sets the Row’s Age property demonstrating how the DataGrid is synchronised:

private void Button_Click(object sender, RoutedEventArgs e)
{
    // demonstrate the property changes from code-behind still work.
    Random rand = new Random();
    foreach (var row in _rows)
    {
        row["Age"] = rand.Next(10) + 5;
    }
}

Also, the Age column has a value converter associated …

So there you have it, binding to dynamic data with INotifyPropertyChanged implemented and an editable DataGrid. The only reservation I have with this technique is that I am really bending the binding framework to achieve the results, having a Data property which expects different types for the getter and setter is a little ugly … use with caution!

You can download the project sourcecode: silverlightdynamicbinding2.zip

Regards, Colin E.

Binding a Silverlight DataGrid to dynamic data via IDictionary

April 17th, 2009

This post demonstrates a technique for binding a Silverlight DataGrid to dynamic data, the structure of which is not know at compile-time …

Update: I have extended this technique to add include change notification so that the DataGrid can be made editable. Read all about it in part two.

With Silverlight binding data to a DataGrid is a very straightforward process. With an XML datasource, you can simply parse your data via Linq to SQL into an anonymous type then bind the resulting collection to the grid. Just a few simple lines of code. But, what if the data you want to bind to your grid is dynamic? That is, at compile time you do not know how many columns are required, or what their contents is. This is a common problem that Silverlight users have faced again, and again, and again and again!

The most obvious solution to this problem is to create a dictionary for each row of your DataGrid, and bind your columns to your values via the dictionary’s string indexer:

Binding="{Binding Path=[Name]}"

However, unfortunately the PropertyPath syntax used for binding does not understand indexers, making it impossible to bind to a dictionary.

Vladimir Bodurov presents an ingenious solution to this problem by dynamically generating a new Type based on the values present within the dictionary, i.e. If the dictionary contains the keys “Name” and “Age”, a Type will be generated that has properties of Name and Age. Crazy stuff! Here I would like to show an alternative method that does not use intermediate Language or other black magic!

We will start with a simple example of a class which can be used to store ‘dynamic’ data for rendering in our DataGrid:

public class Row
{
  private Dictionary<string, object> _data = new Dictionary<string, object>();
  public object this [string index]
  {
    get { return _data[index]; }
    set { _data[index] = value; }
  }
}

We can populate a collection of these objects and associate them with a DataGrid as follows:

Random rand = new Random();
 
var rows = new ObservableCollection<Row>();
for (int i = 0; i < 200; i++)
{
  Row row = new Row();
  row["Forename"] = s_names[rand.Next(s_names.Length)];
  row["Surname"] = s_surnames[rand.Next(s_surnames.Length)];
  row["Age"] = rand.Next(40) + 10;
  row["Shoesize"] = rand.Next(10) + 5;
  rows.Add(row);
}
 
_dataGrid.ItemsSource = rows;

However, as mentioned earlier, we cannot create a binding path that accesses our Rows indexer, so just how do we bind the columns to our data?

The classic .NET solution to this problem would be to create a custom property descriptor. When databinding, the properties of an object are not accessed directly, rather they are accessed via their associated property descriptor. A custom property descriptor can be supplied for our Row class, via the ICustomTypeDescriptor interface for example, that exposes properties which when accessed invoke our indexer. This is how the .NET DataRowView exposes its properties. Unfortunately there is one small snag here … Silverlight does not include the required interfaces to create custom properties.

A simple workaround to this problem is to use a binding that binds each row directly to each Row item rather than a specific property of the item, then use a value converter to access the indexer and extract the required value. Here is an example:

<data:DataGrid Name="_dataGrid" AutoGenerateColumns="False" Height="300" IsReadOnly="False">
  <data:DataGrid.Columns>
    <data:DataGridTextColumn Header="Forename" Binding="{Binding Converter={StaticResource RowIndexConverter}, ConverterParameter=Forename}"/> 
    <data:DataGridTextColumn Header="Surname" Binding="{Binding Converter={StaticResource RowIndexConverter}, ConverterParameter=Surname}"/> 
    <data:DataGridTextColumn Header="Age" Binding="{Binding Converter={StaticResource RowIndexConverter}, ConverterParameter=Age}"/> 
    <data:DataGridTextColumn Header="Shoesize" Binding="{Binding Converter={StaticResource RowIndexConverter}, ConverterParameter=Shoesize}"/> 
  </data:DataGrid.Columns>
</data:DataGrid>

And here is the value converter:

public class RowIndexConverter : IValueConverter
{
  public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  {
    Row row = value as Row;
    string index = parameter as string;
    return row[index];
  }
 
  public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  {
    throw new NotImplementedException();
  }
}

In the above XAML, the binding expression does not have a Path specified, therefore, the binding source is our Row instance rather than a property of the Row. The RowIndexConverter value converter simply uses the supplied ConverterParameter for each column to access the Row indexer and extract the correct value. This works quite nicely, and we are able to see our data within the grid:

grid

However, there is one problem here, if you click on the column headers the grid does not sort the data. The columns of a DataGrid have CanUserSort and SortMemberPath properties, however setting these will not help because the DataGrid will expect the bound object to have a property with the given name which of course it does not! To solve this problem (without resorting to dynamically generated types) we need to delve a little deeper into the way in which the DataGrid binds to the data.

A lot of insight can be gained from the documentation of ICollectionView:

The DataGrid control uses this interface to access the indicated functionality in the data source assigned to its ItemsSource property. If the ItemsSource implements IList, but does not implement ICollectionView, the DataGrid wraps the ItemsSource in an internal ICollectionView implementation.

The ICollectionView interface is responsible for filtering, sorting and grouping of the data bound to the DataGrid. WPF also has the ICollectionViewinterface, you can read a good overview of its features on Marlon Grech’s blog. However, whereas WPF wraps the DataContext itself in a view which is shared across multiple controls, it would appear that Silverlight restricts its usage to the DataGrid. This causes problems if, for example, you want to synchronize the current item between controls (However, Laurent Bugnion has a novel solution for emulating this behaviour).

So, it is the ICollectionView which is responsible for sorting our data. The internal implementation of this interface which the DataGrid creates will expects our object to expose the bound properties, which explains why it does not work. However, if we supply our own ICollectionView interface, we can take control of sorting and implement it ourselves, accessing our ‘property’ values via the Row’s string indexer.

The ICollectionView interface has a lot of methods, events and properties, fortunately I was able to find a suitable implementation of this interface on Manish Dalal’s blog. He had implemented this interface, on a class which extends ObservableCollection, in order to bind a DataGrid to a collection where the data from the server is being paged, hence sorting must be done server side. This gives us pretty much everything we need here, a collection class which is able to manage sorting itself. The only change required is to the ICollectiomView.Refresh() method which is responsible for refreshing the view after the SortDescriptions have changed.

The implementation of this method is as follows:

public class SortableCollectionView : ObservableCollection<Row>, ICollectionView
{
 
  ...
  public void Refresh()
  {
      IEnumerable<Row> rows = this;
      IOrderedEnumerable<Row> orderedRows = null;
 
      // use the OrderBy and ThenBy LINQ extension methods to
      // sort our data
      bool firstSort = true;
      for (int sortIndex = 0; sortIndex < _sort.Count; sortIndex++)
      {
          SortDescription sort = _sort[sortIndex];
          Func<Row, object> function = row => row[sort.PropertyName];
          if (firstSort)
          {
              orderedRows = sort.Direction == ListSortDirection.Ascending ?
                  rows.OrderBy(function) : rows.OrderByDescending(function);
 
              firstSort = false;
          }
          else
          {
              orderedRows = sort.Direction == ListSortDirection.Ascending ?
                  orderedRows.ThenBy(function) : orderedRows.ThenByDescending(function);
          }
      }
 
      _suppressCollectionChanged = true;
 
      // re-order this collection based on the result if the above
      int index = 0;
      foreach (var row in orderedRows)
      {
          this[index++] = row;
      }
 
      _suppressCollectionChanged = false;
 
      // raise the required notification
      this.OnCollectionChanged(
          new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
   }
 
  ...
}

When the user clicks on a grid view header, it modifies the bounds ICollectionView.SortDescription to reflect this change in state. The implementation of ICollectiomView from Manish’s blog invokes the Refresh method whenever the ICollectionView.SortDescription collection changes. In the above implementation we use the OrderBy and ThenBy LINQ extension methods to order the data. Interestingly, OrderBy is a method on IEnumerable, whereas ThenBy is a methods on the IOrderedEnumerable, hence the funny looking logic involving ‘firstSort’. Once the sorting has been performed, the underlying collection is re-ordered to match. The only subtle part is that we use a boolean field, _suppressCollectionChanged to suppress the numerous CollectionChanged events that would be fired by ObservableCollection during this process. Finally we raise NotifyCollectionChanged, resulting in the DataGrid updating to reflect the sort order.

Putting it all together, we simply populate our SortableCollectionView and bind this to the DataGrid, modifying the XAML to explicitly inform the grid that it can sort and which property each column sorts on (usually this is inferred from the Binding Path):

<data:DataGrid Name="_dataGrid" AutoGenerateColumns="False"  IsReadOnly="False" Margin="5">
    <data:DataGrid.Columns>
        <data:DataGridTextColumn Header="Forename" CanUserSort="True" SortMemberPath="Forename" 
                                  Binding="{Binding Converter={StaticResource RowIndexConverter},
                                    ConverterParameter=Forename}"/>
        <data:DataGridTextColumn Header="Surname" CanUserSort="True" SortMemberPath="Surname" 
                                 Binding="{Binding Converter={StaticResource RowIndexConverter},
                                    ConverterParameter=Surname}"/>
        <data:DataGridTextColumn Header="Age" CanUserSort="True" SortMemberPath="Age" 
                                 Binding="{Binding Converter={StaticResource RowIndexConverter},
                                    ConverterParameter=Age}"/>
        <data:DataGridTextColumn Header="Shoesize" CanUserSort="True" SortMemberPath="Shoesize" 
                                 Binding="{Binding Converter={StaticResource RowIndexConverter},
                                    ConverterParameter=Shoesize}"/>                
    </data:DataGrid.Columns>
</data:DataGrid>

And here it is in action:

You can download the complete solution here: silverlightdynamicbinding.zip.

Regards,
Colin E.

Styling hard-to-reach elements in control templates with attached behaviours

February 10th, 2009

OK, the title of this blog post is not very snappy, but it is not an easy problem to describe in a few short words. Here’s the rub, the WPF DataGrid has a select-all button located in the top-left corner, just like Excel and many other grid controls / applications. However, with the default style, this button is barely visible and I would not be surprised if a user of the grid failed to see it.

select-all

Unfortunately restyling this button is not all that straightforward. The more complex WPF controls like the DataGrid and ListView typically expose the template used to construct, and the style applied to their various visual elements.  However, the DataGrid does not expose a style or template for the select-all button.

Fortunately, all is not lost. With WPF, if a control does not expose a style for one of its component parts, you can replicate and replace its entire template. Sometimes this approach feels a little heavy-handed … “I can’t seem to find a way to fit those flashy alloy wheels to my car, so I’ll just by a new car that already has the wheels I like”.

I want to explore a more lightweight approach.

Whilst it is usually preferable to modify a controls template from XAML, in cases like this, I prefer the more concise approach of modifying them in code-behind. In order to do this, you need to handle the FrameworkElement.Loaded event on the control whos template you wish to modify. This event is fired after the control’s visual tree has been constructed, to verify this add a breakpoint in the event handler and inspect the visual tree with Mole. The image below shows the visual tree of my DataGrid which I have expanded to locate the select-all button.

datagrid-mole

You can see that the button is the first element, four steps down the visual tree, therefore it should be easy to locate by walking the visual tree. Once it has been reached we can simply replace its template to the one which we desire:

private void Grid_Loaded(object sender, RoutedEventArgs e)
{
    DependencyObject dep = sender as DependencyObject;
 
    // Navigate down the visual tree to the button
    while (!(dep is Button))
    {
        dep = VisualTreeHelper.GetChild(dep, 0);
    }
    Button button = dep as Button;
 
    // apply our new template
    object res = FindResource("SelectAllButtonTemplate");
    button.Template = res as ControlTemplate
}

The template SelectAllButtonTemplate is simply defined as a resource of our Window.

This works just fine for our single grid, but what if we want to use the same trick on multiple DataGrids? (Besides, we have code-behind, which in WPF is a bit of a dirty word!).

The Attached Behaviour pattern is a useful WPF pattern that proves very useful in this situation. In brief, attached properties add state to a your WPF elements, whereas attached behaviours add behaviour. When an attached property becomes associated with a dependency object, an event is raised. We can handle this event and register handlers on the events of the object being attached to, in this case, our DataGrid’s Loaded event.

The following code is our attached behaviour in its entirety:

public static class DataGridStyleBehaviour
{
    #region attached property
 
    public static ControlTemplate GetSelectAllButtonTemplate(DataGrid obj)
    {
        return (ControlTemplate)obj.GetValue(SelectAllButtonTemplateProperty);
    }
 
    public static void SetSelectAllButtonTemplate(DataGrid obj, ControlTemplate value)
    {
        obj.SetValue(SelectAllButtonTemplateProperty, value);
    }
 
    public static readonly DependencyProperty SelectAllButtonTemplateProperty =
        DependencyProperty.RegisterAttached("SelectAllButtonTemplate",
        typeof(ControlTemplate), typeof(DataGridStyleBehaviour),
        new UIPropertyMetadata(null, OnSelectAllButtonTemplateChanged));
 
    #endregion
 
    #region property behaviour
 
    // property change event handler for SelectAllButtonTemplate
    private static void OnSelectAllButtonTemplateChanged(
        DependencyObject depObj, DependencyPropertyChangedEventArgs e)
    {
        DataGrid grid = depObj as DataGrid;
        if (grid == null)
            return;
 
        // handle the grid's Loaded event
        grid.Loaded += new RoutedEventHandler(Grid_Loaded);
    }
 
    // Handles the DataGrid's Loaded event
    private static void Grid_Loaded(object sender, RoutedEventArgs e)
    {
        DataGrid grid = sender as DataGrid;
        DependencyObject dep = grid;
 
        // Navigate down the visual tree to the button
        while (!(dep is Button))
        {
            dep = VisualTreeHelper.GetChild(dep, 0);
        }
        Button button = dep as Button;
 
        // apply our new template
        ControlTemplate template = GetSelectAllButtonTemplate(grid);
        button.Template = template;
    }
 
    #endregion
}

This attached property can be used as illustrated below, where we apply the SelectAllButtonTemplate from our Window’s resources to a DataGrid:

<Window ...
    Title="BBC News RSS" Height="300" Width="400">
 
    <Window.Resources>
        <XmlDataProvider x:Key="NewsFeed"
                     Source="http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/front_page/rss.xml"  
                     XPath="//item" />
 
        <ControlTemplate x:Key="SelectAllButtonTemplate" TargetType="{x:Type Button}">
            <Grid>
                <Rectangle  x:Name="Border"
                            Fill="Pink" 
                            SnapsToDevicePixels="True" />
                <Polygon   x:Name="Arrow"
                           HorizontalAlignment="Right"
                           VerticalAlignment="Bottom"
                           Margin="8,8,3,3"
                           Opacity="0.15"
                           Fill="Black"
                           Stretch="Uniform"
                           Points="0,10 10,10 10,0" />
            </Grid>            
        </ControlTemplate>
    </Window.Resources>
 
    <Grid>
        <dg:DataGrid Name="dataGrid" AutoGenerateColumns="False" 
                     local:DataGridStyleBehaviour.SelectAllButtonTemplate="{StaticResource SelectAllButtonTemplate}"
                     ItemsSource="{Binding Source={StaticResource NewsFeed}}" RowHeaderWidth="25">
            <dg:DataGrid.Columns>
                <dg:DataGridTextColumn Header="Title" Binding="{Binding XPath=title}" Width="*"/>
                <dg:DataGridHyperlinkColumn   Header="Url" Binding="{Binding XPath=link}" Width="*"/>
                <dg:DataGridTextColumn Header="Date" Binding="{Binding XPath=pubDate}" Width="*"/>
            </dg:DataGrid.Columns>
        </dg:DataGrid>
    </Grid>
</Window>

The result is a beautiful pink select-all button that I don’t think any user would miss in a hurry:

pinkbutton

And a clean code-behind file for our Window :-)

You can download the demo project for this blog post in the following zip file, wpfstylinghiddenelements.

One last thing … if you are implementing a WPF / Silverlight control, please expose the styles and templates for all component parts of your control!

Regards, Colin E.

WPF DataGrid – Committing changes cell-by-cell

January 21st, 2009

In my recent codeproject article on the DataGrid I described a number of techniques for handling the updates to DataTables which are bound to the grid. These examples all worked on the assumption that you want to keep your database synchronised with the DataGrid, with changes being committed on a row-by-row basis, i.e. when the user finishes editing a row the changes are written to the database. The WPF DataGrid operates in a row-oriented manner making this a relatively straightforward scenario to implement.

However, what if you want to commit changes on a cell-by-cell basis? Firstly, lets have a look at the problem in a bit more detail. The following code displays a DataGrid, together with a ‘details’ view. Note that IsSynchronizedWithCurrentItem is set to true so that the details view and currently selected item within the grid will remain synchronised:

<DockPanel DataContext="{Binding Source={StaticResource PersonData}}">
 
  <Border DockPanel.Dock="Bottom" Padding="10">
    <Grid x:Name="RootElement">
      <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="1.8*"/>
      </Grid.ColumnDefinitions>
 
      <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition/>
        <RowDefinition/>
      </Grid.RowDefinitions>
 
      <Label Content="Forename:"/>
      <TextBox Grid.Column="1" Text="{Binding Forename}"/>
 
      <Label Grid.Row="1" Content="Surname:"/>
      <TextBox  Grid.Row="1" Grid.Column="1" Text="{Binding Surname}"/>
 
      <Label Grid.Row="2"  Content="Age:"/>
      <TextBox Grid.Row="2"   Grid.Column="1" Text="{Binding Age}"/>
    </Grid>
  </Border>
 
  <dg:DataGrid ItemsSource="{Binding}" Name="dataGrid"
         IsSynchronizedWithCurrentItem="true"/>
</DockPanel>

The ‘PersonData’ in this case is a DataTable that is constructed in the code-behind.

Now, with this example, if you make changes to a persons surname, then click on the age cell and make changes there, the changes in surname are not reflected in the details view below:

rownotcommitted

In the above example the user has entered the surname “Blunt” and has moved onto the age cell, however the changes are not reflected in the details view below.

Why is this?

The reason is that when you bind to a DataTable, you are actually binding to your DataTable’s DefaultView, which is of type DataView. As a result, each row of your table will be bound to a DataRowView. If you look at the documentation for DataRowView you will find that it implements the IEditableObject interface which is the significant factor here. This interface allows you to perform trasnactional changes to your object, i.e. you can change the object’s properties within a ‘transaction’, then commit then all in a single atomic action. By default, when you bind to a DataGrid this occurs when the user finishes editing a row, either by moving focus or hitting Enter. In order to allow cell-by-cell changes, we need to commit each time the user moves from one cell to the next in the currently selected row.

The DataGrid exposes a CellEditEnding event which looks like a good candidate for this, from the event arguments we can locate the current EditingElement (i.e. the TextBox which now occupies or cell that is in edit mode), the cell’s Column and Row, and from here we can locate the Row.Item whcih is our bound DataRowView. You might think that we can just commit the change in this event handler, however, this is an ‘Ending’ event, not an ‘Ended’ event. In other words the value has not yet been written to the row. This catches me out far too often – as does its RowEditEnding counterpart!

So, if we cannot commit the edit here, how about the CurrentCellChanged event? however this event does not tell us which cell we just left. Although a simple combination of the two provides the effect we are after:

private DataRowView rowBeingEdited = null;
 
private void dataGrid_CellEditEnding(object sender,
                                  DataGridCellEditEndingEventArgs e)
{
    DataRowView rowView = e.Row.Item as DataRowView;
    rowBeingEdited = rowView;
}
 
private void dataGrid_CurrentCellChanged(object sender, EventArgs e)
{
    if (rowBeingEdited != null)
    {
        rowBeingEdited.EndEdit();
    }
}

With this change in place, changes are committed cell-by-cell and the two view remain synchronised:

cellsynchronized

You can download an example project, wpfdatagridcellbycellcommits, changing the file extension from .doc to .zip.

Regards, Colin E.