简体   繁体   English

是否为WPF 4.5 DataGrids打破了INotifyDataErrorInfo

[英]Is INotifyDataErrorInfo broken for WPF 4.5 DataGrids

I have made a simple implementation of INotifyDataErrorInfo in a WPF 4.5 project. 我在WPF 4.5项目中做了一个简单的INotifyDataErrorInfo实现。 This is a new interface for WPF but has been available in Silverlight for some time. 这是WPF的新界面,但已在Silverlight中使用了一段时间。

I know that NET4.5 is still considered alpha but I'm trying to work out if it is my code or the framework at fault. 我知道NET4.5仍然被认为是alpha,但我正在尝试解决它是我的代码还是错误的框架。

The interface works as expected but fails when an object is bound to a DataGrid. 接口按预期工作,但在将对象绑定到DataGrid时失败。

The exception I receive is: 我收到的例外是:

System.NullReferenceException was unhandled by user code System.NullReferenceException未被用户代码处理
Message=Object reference not set to an instance of an object. Message =对象引用未设置为对象的实例。
Source=PresentationFramework StackTrace: at MS.Internal.Data.ClrBindingWorker.OnDataErrorsChanged(INotifyDataErrorInfo indei, String propName) at MS.Internal.Data.PropertyPathWorker.OnErrorsChanged(Object sender, DataErrorsChangedEventArgs e) at System.Windows.WeakEventManager.ListenerList`1.DeliverEvent(Object sender, EventArgs e, Type managerType) at System.Windows.WeakEventManager.DeliverEvent(Object sender, EventArgs args) at System.ComponentModel.ErrorsChangedEventManager.OnErrorsChanged(Object sender, DataErrorsChangedEventArgs args) at INotifyDataErrorInfoTest.Person.NotifyErrorsChanged(String property) in INotifyDataErrorInfoTest\\Person.cs:line 109 at INotifyDataErrorInfoTest.Person.AddErrorForProperty(String property, String error) in INotifyDataErrorInfoTest\\Person.cs:line 122 at INotifyDataErrorInfoTest.Person.Validate(String propertyName) in INotifyDataErrorInfoTest\\Person.cs:line 150 at INotifyDataErrorInfoTest.Person.set_FirstName(String value) in INotifyDataErrorInfoTest Source = PresentationFramework StackTrace:位于System.Windows.WeakEventManager.ListenerList`1的MS.Internal.Data.PropertyPathWorker.OnErrorsChanged(Object sender,DataErrorsChangedEventArgs e)的MS.Internal.Data.ClrBindingWorker.OnDataErrorsChanged(INotifyDataErrorInfo indei,String propName)。位于System.ComponentModel.ErrorsChangedEventManager.OnErrorsChanged(Object sender,DataErrorsChangedEventArgs args)的System.Windows.WeakEventManager.DeliverEvent(Object sender,EventArgs args)中的DeliverEvent(Object sender,EventArgs e,Type managerType)INotifyDataErrorInfoTest.Person.NotifyErrorsChanged(String property) )inotifyDataErrorInfoTest \\ Person.cs:INotifyDataErrorInfoTest中的INotifyDataErrorInfoTest.Person.AddErrorForProperty(字符串属性,字符串错误)中的第109行:INotifyDataErrorInfoTest \\ Person.cs中的INotifyDataErrorInfoTest.Person.Validate(String propertyName)中的第122行:行122 INotifyDataErrorInfoTest中的INotifyDataErrorInfoTest.Person.set_FirstName(String value)中的150 \\Person.cs:line 18 \\ Person.cs:第18行

The code is below or in project at http://dl.dropbox.com/u/14740106/INotifyDataErrorInfoTest.zip 代码位于http://dl.dropbox.com/u/14740106/INotifyDataErrorInfoTest.zip下方或项目中

If consensus is that this is a bug then I will post to MS Connect. 如果共识是这是一个错误,那么我将发布到MS Connect。

Testing: There are two textboxes bound to a single instance of a Person object. 测试:有两个文本框绑定到Person对象的单个实例。 Set the first textbox to have a value of James and it will fail validation and show the red box. 将第一个文本框设置为具有值的James,它将无法通过验证并显示红色框。 If you set the first name of any user in the grid to James the exception will be thrown. 如果将网格中任何用户的名字设置为James,则会抛出异常。

PS: I know it is not MVVM but it is just to prove or disprove the problem. 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>

The answer is YES. 答案是肯定的。 I opened a ticket with Microsoft and they have confirmed that the code is fine and it is a bug with the .NET 4.5 DataGrid. 和微软开了一张票,他们确认代码很好,这是.NET 4.5 DataGrid的一个错误。

This is our bug, not yours. 这是我们的错误,不是你的。 When you edit a DataGrid cell, the DataGrid discards bindings for the "display" template and replaces them with bindings for the "edit" template. 编辑DataGrid单元格时,DataGrid会丢弃“显示”模板的绑定,并将其替换为“编辑”模板的绑定。 The discarded bindings should stop listening to the INDEI.ErrorsChanged event. 丢弃的绑定应该停止监听INDEI.ErrorsChanged事件。 They don't (that's the bug), but they are not prepared to get the event any more. 他们没有(这是错误),但他们不准备再参加活动了。 When the event arrives, a null-reference crash occurs. 当事件到达时,发生空引用崩溃。

This will be fixed in the final release. 这将在最终版本中修复。 Thanks for finding and reporting it. 感谢您查找并报告。

Pretty big bug to have to wait until the final release. 非常大的bug必须等到最终版本。 Let's hope it gets fixed by the next release. 我们希望它能在下一个版本中得到解决。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM