WPF的表单验证,天然比不上WEB多姿多彩。通常有三种方式进行表单验证:this
1. 代码直接验证(很多人直接用 MessageBox 弹出消息,Windows 的老传统),虽然够醒目,却太过打扰用户,很差。spa
2. 经过异常验证(数据绑定,并设置 ValidatesOnExceptions=True ),调试时烦死人,很差。.net
3. 经过内置接口 IDataErrorInfo 、INotifyDataErrorInfo 进行验证(数据绑定),这应该是目前较为理想的验证方式。这两个接口都位于 System.ComponentModel 空间,与 INotifyPropertyChanged 在一块儿。调试
只说第三种验证方式。code
这两个接口,我的较喜欢第二个,缘由是第一个接口不太灵活,并且是为了兼容而存在的,不推荐使用。第二要灵活得多。blog
为了突出验证消息的提示效果,可添加这样一个样式(定义为字典资源,用起来更方便):接口
<Style TargetType="TextBox"> <Setter Property="Validation.ErrorTemplate"> <Setter.Value> <ControlTemplate> <DockPanel> <TextBlock DockPanel.Dock="Bottom" Foreground="Red" Text="{Binding ElementName=adornedElement, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}"></TextBlock> <Border BorderBrush="Red" BorderThickness="1"> <AdornedElementPlaceholder Name="adornedElement"></AdornedElementPlaceholder> </Border> </DockPanel> </ControlTemplate> </Setter.Value> </Setter> </Style>
1、首先定义一个集合,用于保存验证消息:事件
private readonly IDictionary<string, IList<string>> m_errors = new Dictionary<string, IList<string>>();
2、下面是实现接口,具体用法见注释:资源
// 该方法仅为简化属性调用。CallerMemberName 特性是 .net 4.5 才加入的,位于 System.Runtime.CompilerServices 空间。
public void Validate(Func<bool> valid, string error, [CallerMemberName]string prop = null) { if (valid()) ClearError(prop); else SetError(new[] { error }, prop); OnChanged(prop); }
// 验证属性事件。
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged; // 属性更改事件。 public event PropertyChangedEventHandler PropertyChanged; // 该方法由 wpf 调用。 public IEnumerable GetErrors(string propertyName) { if (string.IsNullOrEmpty(propertyName)) return null; // SelectMany 方法颇有用,若没有它,错误消息不能正确显示,除非你重写集合,并重写其 ToString 方法。它帮你省了不少麻烦。 var errors = m_errors.SelectMany(e => e.Value); return errors; } // 添加错误消息。 public void SetError(IList<string> errors, string prop) { m_errors.Remove(prop); m_errors.Add(prop, errors); OnError(prop); } // 清除错误消息。 public void ClearError(string prop) { m_errors.Remove(prop); OnError(prop); } // 验证属性时调用。 private void OnError(string prop) { ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(prop)); }
3、属性这样调用验证代码(以及更新事件):get
public string Password
{
get { return m_password; }
set
{
m_password = value;
// 因为第三种验证方式比较温和,不能像异常那样阻止属性值更新,所以该方法放在下面没有问题。
Validate(() => !Regex.IsMatch(value, "4"), "数字 4 不祥");
}
}
4、前台代码并无什么特殊的,只要你别忘了应用上面的样式。 ValidatesOnNotifyDataErrors=True 可不写,但建议写上,有提示做用:
<TextBox Text="{Binding Path=Password, ValidatesOnNotifyDataErrors=True}"></TextBox>
完。