WPF的表單驗證,自然比不上WEB多姿多彩。一般有三種方式進行表單驗證:
1. 代碼直接驗證(不少人直接用 MessageBox 彈出消息,Windows 的老傳統),雖然夠醒目,卻太過打擾用戶,不好。
2. 通過異常驗證(數據綁定,並設置 ValidatesOnExceptions=True ),調試時煩死人,不好。
3. 通過內置接口 IDataErrorInfo 、INotifyDataErrorInfo 進行驗證(數據綁定),這應該是目前較為理想的驗證方式。這兩個接口都位於 System.ComponentModel 空間,與 INotifyPropertyChanged 在一起。
只說第三種驗證方式。
這兩個接口,個人較喜歡第二個,原因是第一個接口不太靈活,而且是為了兼容而存在的,不推薦使用。第二要靈活得多。
為了突出驗證消息的提示效果,可添加這樣一個樣式(定義為字典資源,用起來更方便):
<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>
一、首先定義一個集合,用於保存驗證消息:
private readonly IDictionary<string, IList<string>> m_errors = new Dictionary<string, IList<string>>();
二、下面是實現接口,具體用法見注釋:
// 該方法僅為簡化屬性調用。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)); }
三、屬性這樣調用驗證代碼(以及更新事件):
public string Password
{
get { return m_password; }
set
{
m_password = value;
// 由於第三種驗證方式比較溫和,不能像異常那樣阻止屬性值更新,因此該方法放在下面沒有問題。
Validate(() => !Regex.IsMatch(value, "4"), "數字 4 不祥");
}
}
四、前台代碼並沒有什么特殊的,只要你別忘了應用上面的樣式。 ValidatesOnNotifyDataErrors=True 可不寫,但建議寫上,有提示作用:
<TextBox Text="{Binding Path=Password, ValidatesOnNotifyDataErrors=True}"></TextBox>
完。