用 INotifyDataErrorInfo 替换 IDataErrorInfo
Replacing IDataErrorInfo with INotifyDataErrorInfo
我有一个名为 Person
的 class 有两个属性 FirstName
、LastName,
两个 Constructors
、一个 ICommand
和一些常用的东西INotifyPropertyChanged
和 IDataErrorInfo
需要:
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
}
in xaml
我有两个 TextBox
绑定到 Person 的 FirstName
和 LastName
,两个 Label
用于验证错误消息和一个 Button
,绑定到ICommand,在下面的ListView
中添加Person
:
<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
class 和 xaml
中进行哪些更改才能保持相同的功能?
每次引发 ErrorsChanged
事件时,框架都会调用 GetErrors
。因为有一个 HasErrors
属性 应该 return true
只要有任何验证错误,在 属性 设置器中验证并缓存验证是有意义的Dictionary<string, List<ValidationResult>>
.
中的错误
请参考以下示例实现:
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
区域:
#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
其他部位不要碰,也可以!
我有一个名为 Person
的 class 有两个属性 FirstName
、LastName,
两个 Constructors
、一个 ICommand
和一些常用的东西INotifyPropertyChanged
和 IDataErrorInfo
需要:
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
}
in xaml
我有两个 TextBox
绑定到 Person 的 FirstName
和 LastName
,两个 Label
用于验证错误消息和一个 Button
,绑定到ICommand,在下面的ListView
中添加Person
:
<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
class 和 xaml
中进行哪些更改才能保持相同的功能?
每次引发 ErrorsChanged
事件时,框架都会调用 GetErrors
。因为有一个 HasErrors
属性 应该 return true
只要有任何验证错误,在 属性 设置器中验证并缓存验证是有意义的Dictionary<string, List<ValidationResult>>
.
请参考以下示例实现:
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
区域:
#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
其他部位不要碰,也可以!