【问题标题】:Replacing IDataErrorInfo with INotifyDataErrorInfo用 INotifyDataErrorInfo 替换 IDataErrorInfo
【发布时间】:2020-02-18 01:12:42
【问题描述】:

我有一个名为Person 的类,它有两个属性FirstNameLastName,、两个Constructors、一个ICommand,以及INotifyPropertyChangedIDataErrorInfo 所需的常用材料:

class Person : ObservableCollection<Person>, INotifyPropertyChanged, IDataErrorInfo
{
    string firstName, lastName;

    #region Properties
    [Required(ErrorMessage = "First Name is Required")]
    [RegularExpression("test", ErrorMessage = "It's to be test")]
    public string FirstName {
        get => firstName;
        set { firstName = value; OnPropertyChanged(); }
    }

    [Required]
    [RegularExpression("test", ErrorMessage = "It also has to be test")]
    public string LastName {
        get => lastName;
        set { lastName = value; OnPropertyChanged(); }
    }
    #endregion Properties

    #region Constructors
    public Person(){
        AddToList = new Command(CanAdd, Add);
    }

    public Person(string fName, string lName){
        FirstName = fName;
        LastName = lName;
    }
    #endregion Constructors

    #region Command
    public ICommand AddToList { get; set; }
    bool CanAdd(object para) => Validator.TryValidateObject(this, new ValidationContext(this), null, true);
    void Add(object para){
        Add(new Person(FirstName, LastName));
        FirstName = LastName = null;
    }
    #endregion Command

    #region INotifyPropertyChanged
    public new event PropertyChangedEventHandler PropertyChanged;
    void OnPropertyChanged([CallerMemberName] string name = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    #endregion INotifyPropertyChanged

    #region IDataErrorInfo
    public string Error => null;
    public string this[string columnName] {
        get {
            var ValidationResults = new List<ValidationResult>();
            if (Validator.TryValidateProperty(
                    GetType().GetProperty(columnName).GetValue(this),
                    new ValidationContext(this) { MemberName = columnName },
                    ValidationResults
                )) return null;

            return ValidationResults.First().ErrorMessage;
        }
    }
    #endregion IDataErrorInfo
}

xaml 中,我有两个TextBox 绑定到Person 的FirstNameLastName,两个Label 用于验证错误消息和一个Button,绑定到ICommand,以添加Person在下面ListView

<Window ...>
    <Window.Resources>
        <local:Person x:Key="Person"/>
    </Window.Resources>

    <Grid DataContext="{StaticResource Person}">
        <StackPanel>
            <TextBox x:Name="Fname" Text="{Binding FirstName, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
            <Label Content="{Binding (Validation.Errors)[0].ErrorContent, ElementName=Fname}"/>

            <TextBox x:Name="Lname" Text="{Binding LastName, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"/>
            <Label Content="{Binding (Validation.Errors).CurrentItem.ErrorContent, ElementName=Lname}"/>

            <Button Content="Click" Command="{Binding AddToList}" />

            <ListView x:Name="lv" ItemsSource="{Binding}">
                <ListView.View>
                    <GridView>
                        <GridViewColumn Header="First Name" Width="200" 
                            DisplayMemberBinding="{Binding FirstName}"/>
                        <GridViewColumn Header="Last Name" Width="200" 
                            DisplayMemberBinding="{Binding LastName}" />
                    </GridView>
                </ListView.View>
            </ListView>
        </StackPanel>
    </Grid>
</Window>

它有效,如果名字和姓氏无效,我会收到错误消息,并且只要名字和姓氏中的任何一个无效,按钮就会保持禁用状态。我只想用INotifyDataErrorInfo 替换IDataErrorInfo 部分。我必须对 Person 类和 xaml 进行哪些更改才能保持相同的功能?

【问题讨论】:

    标签: .net-core idataerrorinfo inotifydataerrorinfo


    【解决方案1】:

    每次您引发ErrorsChanged 事件时,框架都会调用GetErrors。由于有一个HasErrors 属性,只要有任何验证错误,它就应该返回true,因此在属性设置器中进行验证并将验证错误缓存在Dictionary&lt;string, List&lt;ValidationResult&gt;&gt; 中是有意义的。

    请参考以下示例实现:

    class Person : ObservableCollection<Person>, INotifyPropertyChanged, INotifyDataErrorInfo
    {
        string firstName, lastName;
    
        #region Properties
        [Required(ErrorMessage = "First Name is Required")]
        [RegularExpression("test", ErrorMessage = "It's to be test")]
        public string FirstName
        {
            get => firstName;
            set { firstName = value; OnPropertyChanged(); Validate(); }
        }
    
        [Required]
        [RegularExpression("test", ErrorMessage = "It also has to be test")]
        public string LastName
        {
            get => lastName;
            set { lastName = value; OnPropertyChanged(); Validate(); }
        }
        #endregion Properties
    
        #region Constructors
        public Person()
        {
            AddToList = new Command(CanAdd, Add);
            Validate(nameof(FirstName));
            Validate(nameof(LastName));
        }
    
        public Person(string fName, string lName)
        {
            FirstName = fName;
            LastName = lName;
        }
        #endregion Constructors
    
        #region Command
        public ICommand AddToList { get; set; }
        bool CanAdd(object para) => _validationResults.Count == 0;
        void Add(object para)
        {
            base.Add(new Person(FirstName, LastName));
            FirstName = LastName = null;
        }
        #endregion Command
    
        #region INotifyPropertyChanged
        public new event PropertyChangedEventHandler PropertyChanged;
        void OnPropertyChanged([CallerMemberName] string name = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        #endregion INotifyPropertyChanged
    
        #region INotifyDataErrorInfo
        private readonly Dictionary<string, List<ValidationResult>> _validationResults = new Dictionary<string, List<ValidationResult>>();
    
        public bool HasErrors => _validationResults.Count > 0;
    
        public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
    
        public System.Collections.IEnumerable GetErrors(string propertyName)
        {
            if (_validationResults.TryGetValue(propertyName, out List<ValidationResult> validationResults))
                return new string[1] { validationResults.First().ErrorMessage };
            return null;
        }
    
        private void Validate([CallerMemberName]string propertyName = "")
        {
            var ValidationResults = new List<ValidationResult>();
            if (Validator.TryValidateProperty(typeof(Person).GetProperty(propertyName).GetValue(this),
                    new ValidationContext(this) { MemberName = propertyName }, ValidationResults))
            {
                _validationResults.Remove(propertyName);
            }
            else
            {
                _validationResults[propertyName] = ValidationResults;
            }
            ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
        }
        #endregion
    }
    

    【讨论】:

    • IDataErrorInfo更复杂!
    【解决方案2】:

    不知道好不好,但是如果我只是将IDataErrorInfo区域替换为以下内容:

    #region IDataErrorInfo
    public bool HasErrors => true;
    public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
    public IEnumerable GetErrors(string propertyName)
    {
        var ValidationResults = new List<ValidationResult>();
        if (Validator.TryValidateProperty(GetType().GetProperty(propertyName).GetValue(this),
                new ValidationContext(this) { MemberName = propertyName }, ValidationResults))
            return null;
    
        return new string[1] { ValidationResults.First().ErrorMessage };
    }
    #endregion IDataErrorInfo
    

    并且不要触摸任何其他部分,它也可以!

    【讨论】:

    • 使用这个实现,你的模型总是处于错误状态,因为HasErrors总是返回true
    • @mm8,只要我在两个TextBox 中输入test,错误就会消失并且按钮状态会改变
    • 当然,但是HasErrors 属性仍然返回true,从纯粹的实现角度来看这很奇怪。
    • @mm8,在GetErrors 之外声明ValidationResults 和在HasErrors 中声明ValidationResults.Count &gt;0 可能会解决问题,对吧?
    • 查看我的答案以获取示例。
    猜你喜欢
    • 2013-10-24
    • 1970-01-01
    • 2019-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多