Page view counter

It Ain't You, Babe… A Not-a-bug bug in DataGrid

SLLogoWords

 

I'll be writing a set of mini-tutorials on the DataGrid that will, as the King advises, begins at the beginning, goes on till it comes to the end and then stops[1], but before I do, a "Set Of Behaviors That Is Perfectly Understandable But Not At All What The Programmer Expects (SOBTIPUBNAAWTPE)" has surfaced in DataGrid.  (I was told this is not a bug – my response was a bit snide).

Since this is causing a great deal of frustration and confusion, I wanted to alert you to it before you pull all your hair out – I'll also let you know as soon as I have the details of the work around.

iStock_ ice create spilled Large

To understand the bug (oops) you need to understand two properties associated with DataBinding Validation for two-way binding.

In English that means that when you enter data that will be written back to the data source, Silverlight will validate the data and handle two types of exceptions for you if you set the right properties.  The two types of exceptions are:

  1. Exceptions that are thrown when the binding engine tries to convert the type of the data
  2. Exceptions that are thrown from within the binding object's set accessor

The two properties that you need to set are NotifyOnValidationError and ValidatesOnException.  They both default to false. You want to set them to true; if you do the exceptions are turned into BindingValidationError events – and even better they are bubbling events which means that you can put your event handler on the containing control. 

The way this is supposed to work is that you can associate an event handler for the BindingValidationError with the DataGrid itself, and if there is a problem binding the data in any of the columns it will bubble up to that one handler, rather than firing an exception that might bring your application to a stand still. 

As a practical test of this, and stealing borrowing from examples from Reid Maker (who pointed out the SOBTIPUBNAAWTPE in private correspondence) and from Manish Dalal's Blog (who illustrated how this should work during Beta 2) I tried to prove to myself that it wasn't a bug by trying various variations (e.g., moving the event handler in and out of Xaml, moving the exception generation in and out of an event, generating the exception in each of the two ways, and so forth).

Here is the code, somewhat simplified

The Data Source Class

using System;
using System.ComponentModel;

namespace DataGridBindingValidationTester
{
   public class TestData : INotifyPropertyChanged
   {
      public event PropertyChangedEventHandler PropertyChanged;
      private int id = 0;
      private string name = string.Empty;

      private void NotifyChange( String name )
      {
         if ( PropertyChanged != null )
         {
            PropertyChanged( this, new PropertyChangedEventArgs( name ) );
         }
      }

      public string Name
      {
         get { return name; }
         set
         {
            name = value;
            NotifyChange( "Name" );
         }
      }

      public int Id
      {
         get
         {
            return id;
         }
         set
         {
            if ( value == 9 )
            {
               throw new Exception( "can't have 9" );
            }
            id = value;
            NotifyChange( "Id" );
         }
      }
   }
}

Page Xaml

<UserControl x:Class="DataGridBindingValidationTester.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="400" Height="300" xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data">
    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.RowDefinitions>
            <RowDefinition Height="0.15*"/>
            <RowDefinition Height="0.85*"/>
        </Grid.RowDefinitions>
        <TextBlock Margin="18,8,56,8" x:Name="Output" FontSize="14" Text="Error Messages Show Here" TextWrapping="Wrap"/>
        <data:DataGrid x:Name="TestDataGrid" Margin="10,10,10,10" Grid.Row="1"  AutoGenerateColumns="False" >
            <data:DataGrid.Columns>
                <data:DataGridTextColumn Header="Name" Binding="{Binding Name}"  />
                <data:DataGridTemplateColumn Header="ID">
                    <data:DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock  Text="{Binding Id}"  />
                        </DataTemplate>
                     </data:DataGridTemplateColumn.CellTemplate>
                    <data:DataGridTemplateColumn.CellEditingTemplate>
                        <DataTemplate>
                            <TextBox Text="{Binding Id,Mode=TwoWay,NotifyOnValidationError=true,ValidatesOnExceptions=true}" />
                        </DataTemplate>
                    </data:DataGridTemplateColumn.CellEditingTemplate>
                </data:DataGridTemplateColumn>
            </data:DataGrid.Columns>
        </data:DataGrid>
    </Grid>
</UserControl>

Page.xaml.cs

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace DataGridBindingValidationTester
{
   public partial class Page : UserControl
   {
      public Page()
      {
         InitializeComponent();
         Loaded += new RoutedEventHandler( Page_Loaded );
         TestDataGrid.BindingValidationError += 
            new EventHandler<ValidationErrorEventArgs>( TestDataGrid_BindingValidationError );
      }

      void TestDataGrid_BindingValidationError( object sender, ValidationErrorEventArgs e )
      {
         if ( e.Action == ValidationErrorEventAction.Added )
         {
            ( (Control) e.OriginalSource ).Background = new SolidColorBrush( Colors.Red );
            Output.Text = "Error: " + e.Error.Exception.Message;
         }
         else if ( e.Action == ValidationErrorEventAction.Removed )
         {
            ( (Control) e.OriginalSource ).Background = new SolidColorBrush( Colors.White );
         }

      }

      void Page_Loaded( object sender, RoutedEventArgs e )
      {
         List<TestData> tests = new List<TestData>();

         tests.Add( new TestData { Id = 1, Name = "A" } );
         tests.Add( new TestData { Id = 4, Name = "B" } );
         tests.Add( new TestData { Id = 2, Name = "C" } );
         tests.Add( new TestData { Id = 5, Name = "D" } );
         tests.Add( new TestData { Id = 3, Name = "E" } );
         tests.Add( new TestData { Id = 8, Name = "F" } );
         tests.Add( new TestData { Id = 7, Name = "G" } );
         TestDataGrid.ItemsSource = tests;
         
      }
   }
}

The key to why this should work is that the DataGrid's Cell Editing Template for Binding the ID (when the user is entering data) has a DataTemplate that uses a TextBox that is bound with both of the necessary properties,

<DataTemplate>
    <TextBox Text="{Binding Id,
                    Mode=TwoWay,
                    NotifyOnValidationError=true,
                    ValidatesOnExceptions=true}" />
</DataTemplate>

In addition, the event handler is created in the class constructor and implemented in the code behind.

TestDataGrid.BindingValidationError += 
   new EventHandler<ValidationErrorEventArgs>( TestDataGrid_BindingValidationError );

void TestDataGrid_BindingValidationError( object sender, ValidationErrorEventArgs e )
{
   if ( e.Action == ValidationErrorEventAction.Added )
   {
      ( (Control) e.OriginalSource ).Background = new SolidColorBrush( Colors.Red );
      Output.Text = "Error: " + e.Error.Exception.Message;
   }
   else if ( e.Action == ValidationErrorEventAction.Removed )
   {
      ( (Control) e.OriginalSource ).Background = new SolidColorBrush( Colors.White );
   }

}

The bug is that this does not work.  The reason this is not a bug but a SUBTIPUBNAAWTPE is this: what is actually causing this to fail is not that the event wouldn't be raised, but rather that by the time the error might be raised, the DataGrid has already shifted back from TextBox to TextBlock – and the properties are no longer set.

(Okay, my snide answer was this: …it may not be a BindingValidation bug, but it is a Silverlight bug by any reasonable definition; or it is like saying that it isn’t a bug when your brakes fail because they were never really designed to stop the car if the car is on an actual road, though they work just great up  on the lift.  )

More about this soon…. including a much more systematic review of DataGrid.

[1] Alice in Wonderland – Public Domain – Project Gutenberg

Published Wednesday, October 22, 2008 8:48 AM by jesseliberty
Filed under:

Comments

# re: It Ain't You, Babe… A Not-a-bug bug in DataGrid

Thank you for removing the line numbers in you posting.  this makes it a lot easier to verify your results.

Wednesday, October 22, 2008 9:11 AM by Steve Strong

# re: It Ain't You, Babe… A Not-a-bug bug in DataGrid

Note: IE8.B2 will only show the code_block vertical scrollbars in compatibility mode.

Wednesday, October 22, 2008 9:28 AM by wisecarver

# re: It Ain't You, Babe… A Not-a-bug bug in DataGrid

Jesse,

Thanks for writing about this. It has been driving me crazy!

The datagrid also seems to have some rendering problems where it does not redraw all of its rows correctly in certain situations.

Like when a button in your grid fires an event and then the user sorts by a column...very frustrating.

Please tell me the DataGrid people are busy working to improve this very valuable control.

BTW if/when an update tot he DataGrid happens how will we get it and start using it instead of the existing one?

Wednesday, October 22, 2008 10:37 AM by wackyphill

# re: It Ain't You, Babe… A Not-a-bug bug in DataGrid

wackyphill- We've been seeing the same thing. There is a thread going in the forums, but so far no response at all from MS. Jesse if you could make sure the right people see this we would appreciate it. I'm supposed to ship in a little over a week and this bug is giving me ulcers. silverlight.net/.../36559.aspx

Wednesday, October 22, 2008 11:48 AM by CoderX

# 2008 October 23 - Links for today &laquo; My (almost) Daily Links

Pingback from  2008 October 23 - Links for today &laquo; My (almost) Daily Links

Thursday, October 23, 2008 5:08 AM by 2008 October 23 - Links for today « My (almost) Daily Links

# Silverlight Cream for October 22, 2008 -- #404

In this issue: Tim Heuer, Jeff Prosise, Jeff Weber, and Jesse Liberty. From SilverlightCream.com : Silverlight

Thursday, October 23, 2008 3:30 PM by Community Blogs

# re: It Ain't You, Babe… A Not-a-bug bug in DataGrid

I am also waiting for removed editing datagrid events, which where there in Beta2. Hopefully DataGrid will be shipped soon as a patch somehow ;)...

Friday, October 24, 2008 5:14 AM by esh

# re: It Ain't You, Babe… A Not-a-bug bug in DataGrid

I don't know if you solved the problem, but i found something reading some blogs and trying some examples:

<DataTemplate>

   <TextBox BindingValidationError="TestDataGrid_BindingValidationError"

        Text="{Binding Id, Mode=TwoWay, NotifyOnValidationError=true,ValidatesOnExceptions=true}" />

</DataTemplate>

If we add the handler to the textbox like you can see above everything works perfect.

Hope this help.

Monday, November 24, 2008 6:50 AM by twinae

# re: It Ain't You, Babe… A Not-a-bug bug in DataGrid

In order to receive notification that a validation error has occurred, you must tell the binding engine that you wish to receive notifications by setting both the ValidatesOnExceptions and NotifyOnValidationError properties to true on the binding object. Setting ValidatesOnExceptions to true tells the binding engine to create a validation error when an exception occurs. Setting NotifyOnValidationError to true tells the binding engine to raise the BindingValidationError event when a validation error occurs and resolves.

Create an event handler on the target object or <br>any of its parents</br> to handle the BindingValidationError event. The BindingValidationError event is a routed event, so if you do not handle it on the element that raised the event, it will continue to bubble up until it is handled. For more information on routed events, see Events and Delegates

I find this from Silverlight SDK.

It looks like a bug in datagrid.

Tuesday, December 16, 2008 10:42 PM by spiderman110

# re: It Ain't You, Babe… A Not-a-bug bug in DataGrid

Its now 5 months later and I'm using Silverlight Toolkit March 2009 and this problem seems to be there still.  Was it fixed it the mean time and am I missing something ?

Thursday, May 28, 2009 10:11 AM by nicos

# Databinding in Silverlight | Gopi's .net Blog

Pingback from  Databinding in Silverlight | Gopi's .net Blog

Monday, August 31, 2009 9:29 AM by Databinding in Silverlight | Gopi's .net Blog