我在WPF 4.5项目中做了一个简单的INotifyDataErrorInfo实现。这是WPF的新界面,但已在Silverlight中使用了一段时间。
我知道NET4.5仍然被认为是alpha版,但我正在尝试解决它是否是我的代码或错误的框架。
接口按预期工作,但在将对象绑定到DataGrid时失败。
我收到的例外是:
System.NullReferenceException未被用户代码
处理 Message =对象引用未设置为对象的实例 Source = PresentationFramework StackTrace: 在MS.Internal.Data.ClrBindingWorker.OnDataErrorsChanged(INotifyDataErrorInfo) indei,String propName) 在MS.Internal.Data.PropertyPathWorker.OnErrorsChanged(Object sender,DataErrorsChangedEventArgs e) 在System.Windows.WeakEventManager.ListenerList`1.DeliverEvent(Object sender,EventArgs e,Type managerType) 在System.Windows.WeakEventManager.DeliverEvent(Object sender,EventArgs args) 在System.ComponentModel.ErrorsChangedEventManager.OnErrorsChanged(Object sender,DataErrorsChangedEventArgs args) 在INotifyDataErrorInfoTest中的INotifyDataErrorInfoTest.Person.NotifyErrorsChanged(String属性):第109行 在INotifyDataErrorInfoTest中的INotifyDataErrorInfoTest.Person.AddErrorForProperty(字符串属性,字符串错误):第122行 在INotifyDataErrorInfoTest中的INotifyDataErrorInfoTest.Person.Validate(String propertyName):Person.cs:第150行 at INotifyDataErrorInfoTest.Person.set_FirstName(String value)INotifyDataErrorInfoTest \ Person.cs:第18行
代码位于http://dl.dropbox.com/u/14740106/INotifyDataErrorInfoTest.zip
下方或项目中如果一致认为这是一个错误,那么我将发布到MS Connect。
测试: 有两个文本框绑定到Person对象的单个实例。将第一个文本框设置为具有值的James,它将无法通过验证并显示红色框。如果将网格中任何用户的名字设置为James,则会抛出异常。
PS:我知道它不是MVVM,但它只是为了证明或反驳这个问题。 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>
答案 0 :(得分:6)
答案是肯定的。我打开了一个ticket with Microsoft,他们已经确认代码很好,这是.NET 4.5 DataGrid的一个错误。
这是我们的错误,不是你的。编辑DataGrid单元格时,DataGrid会丢弃“显示”模板的绑定,并将其替换为“编辑”模板的绑定。丢弃的绑定应该停止监听INDEI.ErrorsChanged事件。他们没有(这是错误),但他们不准备再参加活动了。当事件到达时,会发生空引用崩溃。
这将在最终版本中修复。感谢您查找并报告。
非常大的bug必须等到最终版本。我们希望它能在下一个版本中得到解决。