源BLog https://kmatyaszek.github.io/wpf%20validation/2019/03/06/wpf-validation-using-idataerrorinfo.html
示例工程 https://github.com/JustMySpace/DemoCollections.WPF/tree/main/%E5%B1%9E%E6%80%A7%E9%AA%8C%E8%AF%81/INotifyDataErrorInfo%E5%AE%9E%E7%8E%B0%E5%B1%9E%E6%80%A7%E9%AA%8C%E8%AF%81
优点在于支持复杂的验证逻辑,但是要与Attribute 如 MaxLength、Required等结合需要自己补充一些逻辑,且在现有的Mvvm框架中较难以动态代理的方式添加这些逻辑。
INotifyDataErrorInfo是.NET 4.5中增加的一个接口,可是实现自定义的规则验证并反馈结果到界面,该接口包含三个接口:
HasErrors
标识是否有验证错误GetErrors
返回IEnumerable类型的错误集合,当propertyName 不为空时,返回特定属性的验证错误,当propertyName 为空字符串或Null时,戴白哦整个实体的验证错误ErrorsChanged
当验证错误发生变化时,必须触发这个事件
一个简单的示例
界面:
<TextBox Text="{Binding UserName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}">
<Validation.ErrorTemplate>
<ControlTemplate>
<StackPanel>
<AdornedElementPlaceholder x:Name="textBox" />
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ErrorContent}" Foreground="Red" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ControlTemplate>
</Validation.ErrorTemplate>
</TextBox>
VM:
public class MainViewModel : BindableBase, INotifyDataErrorInfo
{
private string _userName;
private readonly Dictionary<string, List<string>> _errorsByPropertyName = new Dictionary<string, List<string>>();
public MainViewModel()
{
UserName = null;
}
public string UserName
{
get => _userName;
set
{
_userName = value;
ValidateUserName();
RaisePropertyChanged();
}
}
public bool HasErrors => _errorsByPropertyName.Any();
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public IEnumerable GetErrors(string propertyName)
{
return _errorsByPropertyName.ContainsKey(propertyName) ?
_errorsByPropertyName[propertyName] : null;
}
private void OnErrorsChanged(string propertyName)
{
ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
}
private void ValidateUserName()
{
ClearErrors(nameof(UserName));
if (string.IsNullOrWhiteSpace(UserName))
AddError(nameof(UserName), "Username cannot be empty.");
if (string.Equals(UserName, "Admin", StringComparison.OrdinalIgnoreCase))
AddError(nameof(UserName), "Admin is not valid username.");
if (UserName == null || UserName?.Length <= 5)
AddError(nameof(UserName), "Username must be at least 6 characters long.");
}
private void AddError(string propertyName, string error)
{
if (!_errorsByPropertyName.ContainsKey(propertyName))
_errorsByPropertyName[propertyName] = new List<string>();
if (!_errorsByPropertyName[propertyName].Contains(error))
{
_errorsByPropertyName[propertyName].Add(error);
OnErrorsChanged(propertyName);
}
}
private void ClearErrors(string propertyName)
{
if (_errorsByPropertyName.ContainsKey(propertyName))
{
_errorsByPropertyName.Remove(propertyName);
OnErrorsChanged(propertyName);
}
}
}