Is INotifyDataErrorInfo broken for WPF 4.5 DataGrids

I made a simple implementation of INotifyDataErrorInfo in a WPF 4.5 project. This is a new interface for WPF, but it has been available in Silverlight for some time.

I know that NET4.5 is still considered alpha, but I am trying to work if it is my code or framework that is to blame.

The interface works as expected, but fails when the object is bound to a DataGrid.

The exception that I get is:

System.NullReferenceException was not handled by user code
Message = The reference to the object is not installed in the object instance.
Source = PresentationFramework StackTrace: in MS.Internal.Data.ClrBindingWorker.OnDataErrorsChanged (INotifyDataErrorInfo indei, String propName) in MS.Internal.Data.PropertyPathWorker.OnErrorsChanged.` DeliverEvent (sender object, EventArgs e, type managerType) in System.Windows.WeakEventManager.DeliverEvent (object sender, EventArgs arguments) in System.ComponentModel.ErrorsChangedEventManager.OnErrorsChangedEventRotEfreTerEntrEntrEnterFeRentErEnterFeRentErEnterFeRentrEnterFeRentErFeRentEfreTerFeRentErEnterFeRentErEnterFeRentrEntrEnterFeRentErEnterFeRentErEnterFeRentErEnterItemErEnterFeRentErEnterItem ) in INotifyDataErrorInfoTest \ Person.cs: string 109 in INotifyDataErrorInfoTest.Person.AddErrorForProperty (String property, String error) in INotifyDataErrorInfoTest \ Person.cs : line 122 in INotifyDataErrorInfoTest.Person.Validate (String propertyName) in INotifyDataErrorInfoTest \ Person.cs: line 150 in INotifyDataErrorInfoTest.Person.set_FirstName (String value) in INotifyDataErrorInfoTest \ Person.cs:

The code is below or in the project at http://dl.dropbox.com/u/14740106/INotifyDataErrorInfoTest.zip

If the consensus is that this is a mistake, I will send an MS Connect message.

Testing: There are two text fields attached to one instance of the Person object. Set the first text box to James and he will not check the check and show a red frame. If you set any username in the grid for James, an exception will be thrown.

PS: I know that this is not MVVM, but simply to prove or refute the problem.

public class Person : INotifyDataErrorInfo, INotifyPropertyChanged { string _firstName; public string FirstName { get { return _firstName; } set { _firstName = value; Validate("FirstName"); OnPropertyChanged("FirstName"); } } string _lastName; public string LastName { get { return _lastName; } set { _lastName = value; Validate("LastName"); OnPropertyChanged("LastName"); } } public Person() { } public Person(string first, string last) { this._firstName = first; this._lastName = last; } #region INotifyPropertyChanged Members /// <summary> /// Event to indicate that a property has changed. /// </summary> [field: NonSerialized] public event PropertyChangedEventHandler PropertyChanged; /// <summary> /// Called when a property is changed /// </summary> /// <param name="propertyName">The name of the property that has changed.</param> protected virtual void OnPropertyChanged(string propertyName) { OnPropertyChanged(new PropertyChangedEventArgs(propertyName)); } /// <summary> /// Called when a property is changed /// </summary> /// <param name="e">PropertyChangedEventArgs</param> protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) { //Validate the property Validate(e.PropertyName); if (null != PropertyChanged) { PropertyChanged(this, e); } } #endregion #region INotifyDataErrorInfo Members public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged; private Dictionary<string, List<string>> _errors = new Dictionary<string, List<string>>(); public IEnumerable GetErrors(string propertyName) { if (string.IsNullOrEmpty(propertyName)) { return (_errors.Values); } MakeOrCreatePropertyErrorList(propertyName); return _errors[propertyName]; } public bool HasErrors { get { return (_errors.Where(c => c.Value.Count > 0).Count() > 0); } } void NotifyErrorsChanged(string property) { if (ErrorsChanged != null) { ErrorsChanged(this, new DataErrorsChangedEventArgs(property)); } } public void ClearErrorFromProperty(string property) { MakeOrCreatePropertyErrorList(property); _errors[property].Clear(); NotifyErrorsChanged(property); } public void AddErrorForProperty(string property, string error) { MakeOrCreatePropertyErrorList(property); _errors[property].Add(error); NotifyErrorsChanged(property); } void MakeOrCreatePropertyErrorList(string propertyName) { if (!_errors.ContainsKey(propertyName)) { _errors[propertyName] = new List<string>(); } } #endregion /// <summary> /// Force the object to validate itself using the assigned business rules. /// </summary> /// <param name="propertyName">Name of the property to validate.</param> public void Validate(string propertyName) { if (string.IsNullOrEmpty(propertyName)) { return; } if (propertyName == "FirstName") { if (FirstName == "James") { AddErrorForProperty(propertyName, "FirstName can't be James"); } else { ClearErrorFromProperty(propertyName); } } } } public class NameList : ObservableCollection<Person> { public NameList() : base() { Add(new Person("Willa", "Cather")); Add(new Person("Isak", "Dinesen")); Add(new Person("Victor", "Hugo")); Add(new Person("Jules", "Verne")); } } public partial class MainWindow : Window { Person _person = new Person(); public MainWindow() { InitializeComponent(); DataContext = this; } public Person Person { get { return _person; } } } <Window x:Class="INotifyDataErrorInfoTest.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:c="clr-namespace:INotifyDataErrorInfoTest" Title="MainWindow" Height="350" Width="525"> <Window.Resources> <c:NameList x:Key="NameListData"/> </Window.Resources> <StackPanel> <StackPanel.Resources> <Style TargetType="TextBox"> <Setter Property="Margin" Value="5"></Setter> </Style> </StackPanel.Resources> <TextBox Text="{Binding Person.FirstName, Mode=TwoWay, ValidatesOnNotifyDataErrors=True}"/> <TextBox Text="{Binding Person.LastName, Mode=TwoWay, ValidatesOnNotifyDataErrors=True}"/> <TextBlock>To generate an error, set the FirstName of any row to James. </TextBlock> <DataGrid ItemsSource="{Binding Source={StaticResource NameListData}}" AutoGenerateColumns="True"></DataGrid> </StackPanel> </Window> 
+4
source share
1 answer

Answer: YES. I opened a ticket with Microsoft and they confirmed that the code is ok and this is a bug with .NET 4.5 DataGrid.

This is our mistake, not yours. When you edit a DataGrid cell, the DataGrid removes the bindings for the "display" template and replaces them with the bindings for the "edit" template. Discarded bindings should stop listening for the INDEI.ErrorsChanged event. They do not (which is a mistake), but they are not ready to accept the event anymore. When an event arrives, a failure with a null reference occurs.

This will be fixed in the final version. Thank you for finding and reporting this.

Pretty big mistake to wait until the final version. Let him hope that he will be fixed in the next release.

+6
source

All Articles