UI未调用INotifyDataErrorInfo.GetErrors()

Joh*_*ger 3 data-binding wpf mvvm inotifydataerrorinfo

我有一个模型实现两者INotifyPropertyChangedINotifyDataErrorInfo.当我修改了属性时,属性更改了事件触发,但由于某种原因,当我引发Error事件处理程序时,UI会调用GetErrors方法.这导致验证错误未呈现给UI.

有人可以看看我如何设置INotifyDataErrorInfo并告诉我我是否做错了什么?

基础模型实施

public class BaseChangeNotify : INotifyPropertyChanged, INotifyDataErrorInfo
{
    private bool isDirty;

    private Dictionary<string, List<string>> errors = new Dictionary<string, List<string>>();

    public BaseChangeNotify()
    {
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

    public bool IsDirty
    {
        get
        {
            return this.isDirty;
        }

        set
        {
            this.isDirty = value;
            this.OnPropertyChanged();
        }
    }

    public bool HasErrors
    {
        get
        {
            return this.errors.Count(e => e.GetType() == typeof(ErrorMessage)) > 0;
        }
    }

    public IEnumerable GetErrors(string propertyName)
    {
        if (string.IsNullOrEmpty(propertyName) ||
            !this.errors.ContainsKey(propertyName))
        {
            return null;
        }

        return this.errors[propertyName];/*.Where(e => (e is ErrorMessage));*/
    }

    protected virtual void AddError(string propertyName, string error, bool isWarning = false)
    {
        if (!this.errors.ContainsKey(propertyName))
        {
            this.errors[propertyName] = new List<string>();
        }

        if (!this.errors[propertyName].Contains(error))
        {
            if (isWarning)
            {
                this.errors[propertyName].Add(error);
            }
            else
            {
                this.errors[propertyName].Insert(0, error);
            }

            this.OnErrorsChanged(propertyName);
        }
    }

    protected virtual void RemoveError(string propertyName, string error)
    {
        if (this.errors.ContainsKey(propertyName) &&
            this.errors[propertyName].Contains(error))
        {
            this.errors[propertyName].Remove(error);

            if (this.errors[propertyName].Count == 0)
            {
                this.errors.Remove(propertyName);
            }

            this.OnErrorsChanged(propertyName);
        }
    }

    public virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        // Perform the IsDirty check so we don't get stuck in a infinite loop.
        if (propertyName != "IsDirty")
        {
            this.IsDirty = true; // Each time a property value is changed, we set the dirty bool.
        }

        if (this.PropertyChanged != null)
        {
            // Invoke the event handlers attached by other objects.
            try
            {
                // When unit testing, this will always be null.
                if (Application.Current != null)
                {
                    try
                    {
                        Application.Current.Dispatcher.Invoke(() =>
                            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)));

                    }
                    catch (Exception)
                    {

                        throw;
                    }
                }
                else
                {
                    this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                }
            }
            catch (Exception)
            {
                throw;
            }
        }
    }

    /// <summary>
    /// Called when an error has changed for this instance.
    /// </summary>
    /// <param name="propertyName">Name of the property.</param>
    public virtual void OnErrorsChanged([CallerMemberName] string propertyName = "")
    {
        if (string.IsNullOrWhiteSpace(propertyName))
        {
            return;
        }

        if (this.ErrorsChanged != null)
        {
            this.ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

模型使用实现

public class PayItem : BaseChangeNotify
{
    private Section section;

    public Section Section
    {
        get
        {
            return this.section;
        }

        set
        {
            this.section = value;
            this.ValidateSection();
            this.OnPropertyChanged();
        }
    }

    private void ValidateSection([CallerMemberName] string propertyName = "")
    {
        const string sectionError = "You must select a Section.";
        if (this.Section == null || this.Section.Name.Length > 1)
        {
            this.AddError(propertyName, sectionError);
        }
        else
        {
            this.RemoveError(propertyName, sectionError);
        }
    }
Run Code Online (Sandbox Code Playgroud)

View试图使用它

<ComboBox Name="SectionComboBox"
          ItemsSource="{Binding Path=ProjectSections}"
          SelectedItem="{Binding Path=SelectedPayItem.Section, 
                         NotifyOnValidationError=True,
                         UpdateSourceTrigger=PropertyChanged}">
Run Code Online (Sandbox Code Playgroud)

该应用程序正在WPF中编写,WPF文档非常稀缺.我已经通过了阅读的Silverlight文档连同其他一些博客文章,我发现在互联网上,并在每一个不同的方式博客作者建议已经落实.每次结果相同时,该GetErrors()方法永远不会受到绑定引擎的影响.

谁能看到我做错了什么?当我的模型具有其属性集时,我可以单步执行调试器并最终在OnErrorsChanged事件处理程序中结束,并调用该事件.当它被调用时没有任何事情发生,所以我很难过.

在此先感谢您的帮助.

乔纳森

编辑

另外我想指出,我在过去几个月里一直在基类中使用IDataErrorInfo而没有任何问题.绑定工作,错误报告给视图,一切都很高兴.当我从IDataErrorInfo更改为INotifyDataErrorInfo时,验证似乎停止与View通信.

Tom*_*Tom 7

在引发ErrorsChanged事件时,INotifyDataErrorInfo.HasErrors属性必须返回true.否则绑定引擎会忽略错误.您的HasErrors属性将始终返回false.发生这种情况是因为您正在检查ErrorMessage类型的项目,但您的字典包含KeyValuePair类型的项目<string,List <string >>.除此之外,计算所有项目是非常无效的.你应该使用.Any()代替.

顺便说一句,INotifyDataErrorInfo的MSDN文档说明如下:

请注意,绑定引擎从不使用HasErrors属性,但您可以在自定义错误报告中使用它.

这是完全错误的,我需要花费数小时才能找到它.