源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);}}}
