Problems binding to SelectedValue with Microsoft’s WPF DataGrid

I had been seeing some odd exceptions being thrown by the WPF DataGrid code when interacting with the “new row” place holder.

GridEditing - SelectedItem FormatException

I could identify that the error was occurring because I had data-bound to the SelectedItem property on the DataGrid like so:

<toolkit:DataGrid ItemsSource="{Binding Persons}" AutoGenerateColumns="False"
                  SelectedItem="{Binding SelectedPerson}"
                  IsSynchronizedWithCurrentItem="True">

Both the Persons collection and SelectedPerson are properties on my ViewModel (VM). Its possible to use CollectionViewSource.GetDefaultView(Persons).CurrentItem – but I find it useful to expose and bind a simple read/write property. I’ve used this previously for ListView and ListBox without a problem.

I spent some time debugging this right down through BindingExpression and DependencyObject.SetValue. As far as I can tell the exception is thrown because a ConvertBack method (on the default converter) fails when dealing with the MS.Internal.NamedObject that represents the NewItemPlaceholder. This instance is used to represent the blank “new row” if CanUserAddRows is set to True (and the collection supports it). In fact it appears as if the FormatException is actually being thrown within an exception handler whilst attempting to Trace the binding failure. Whoops!

Initially I tried simply putting an try/catch block around the DataGrid code shown above. However, the exception occurred under various conditions – focus on new row, begin edit on a new row and rollback on a new row. Not all of these could be easily caught because they would leave the grid in an invalid state. Eventually the answer (HACK) became obvious – to use a converter on the binding.

using System;
using System.Windows;
using System.Windows.Data;

namespace GridEditing.Converters
{
    public class IgnoreNewItemPlaceHolderConverter : IValueConverter
    {
        private const string NewItemPlaceholderName = "{NewItemPlaceholder}";

        public object Convert( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture )
        {
            return value;
        }

        public object ConvertBack( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture )
        {
            if ( value != null && value.ToString() == NewItemPlaceholderName )
                return DependencyProperty.UnsetValue;
            return value;
        }
    }
}

All we are doing here is not binding when we encounter the “new row” instance. Notice the curly braces in the NewItemPlaceholder ToString() representation? This, I believe, is why it causes the FormatException since the ToString() is used to construct a formatString which is then passed to the TraceEvent method. However, because the curly braces aren’t escaped it expects a string token number, e.g. {0} as per string.Format().

Anyhow, using the converter above means that binding to DataGrid.SelectedValue works as expected with the “new row” place holder.

<Window.Resources>   
    <converters:IgnoreNewItemPlaceHolderConverter x:Key="ignoreNewItemPlaceHolderConverter"/>
</Window.Resources>
<toolkit:DataGrid ItemsSource="{Binding Persons}" AutoGenerateColumns="False"
SelectedItem="{Binding SelectedPerson,Converter={StaticResource ignoreNewItemPlaceHolderConverter
}}" IsSynchronizedWithCurrentItem="True">
 

Survey results

Trackbacks
  • No trackbacks exist for this post.
Comments

Leave a comment

Submitted comments are subject to moderation before being displayed.

 Name

 Email (will not be published)

 Website

Your comment is 0 characters limited to 3000 characters.