對於WPF中的驗證, View驗證實現起來很簡單, 可以通道 Validation.ErrorEvent 冒泡傳遞到View的邏輯樹上, 只是, 通常這樣做的情況下, 我們需要為View添加事件代碼監聽這類錯誤事件, 然后進行處理。
這樣做可以說是非常簡單, 但是這樣的硬編碼的, 基本上每個模塊每個功能, 你都必不可少的為其進行重復的工作, 這是一項非常枯燥且無聊的體力活!
於是, 則考慮MVVM的架構中, 如何把這種模式傳遞到ViewModel中, 使得前端的驗證, 對於ViewModel仍然有效。
實現的原理, 如圖所示:
在此之前, 對於View前端驗證需要做的一些操作步驟,
- 1.為驗證的屬性添加自定義驗證類
- 2.設置驗證錯誤的通知屬性 NotifyOnValidationError="True" 。 注: 如此一來, 則可以產生Validation.ErrorEvent事件
- 3.通過自定義的 ValidationExceptionBehavior 繼承於 Behavior, 用於監聽 Validation.ErrorEvent 的錯誤事件。
- 4.在 ValidationExceptionBehavior 中通過 AssociatedObjectde的DataContex獲取到關聯當前View的DataContex, 從而改變DataContext的后端驗證條件。
1.設置屬性自定義的驗證類並添加 NotifyOnValidationError="True" 屬性
<TextBox Margin="15 0 10 0"
Style="{StaticResource MaterialDesignFloatingHintTextBox}" materialDesign:HintAssist.Hint="登錄名 *">
<TextBox.Text>
<Binding Path="Model.Account" UpdateSourceTrigger="PropertyChanged" NotifyOnValidationError="True">
<Binding.ValidationRules>
<domain:CustomizeValidationRule validationType="Str"
minLength="3" maxLength="10"
errorMessage="輸入長度范圍 [3-10]字"
ValidatesOnTargetUpdated="True" />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
2.自定義 IValidationExceptionHandler 接口, ViewModel繼承IValidationExceptionHandler , 用於接收前端的驗證結果。
public interface IValidationExceptionHandler
{
/// <summary>
/// 是否有效
/// </summary>
bool IsValid
{
get;
set;
}
}
3. 自定義 ValidationExceptionBehavior, 用於監聽處理View的錯誤事件
/// <summary>
/// 驗證行為類,可以獲得附加到的對象
/// </summary>
public class ValidationExceptionBehavior : Behavior<FrameworkElement>
{
/// <summary>
/// 錯誤計數器
/// </summary>
private int _validationExceptionCount = 0;
/// <summary>
/// 附加對象時
/// </summary>
protected override void OnAttached()
{
//附加對象時,給對象增加一個監聽驗證錯誤事件的能力,注意該事件是冒泡的
this.AssociatedObject.AddHandler(Validation.ErrorEvent, new EventHandler<ValidationErrorEventArgs>(this.OnValidationError));
}
#region 獲取實現接口的對象
/// <summary>
/// 獲取對象
/// </summary>
/// <returns></returns>
private IValidationExceptionHandler GetValidationExceptionHandler()
{
if (this.AssociatedObject.DataContext is IValidationExceptionHandler)
{
var handler = this.AssociatedObject.DataContext as IValidationExceptionHandler;
return handler;
}
return null;
}
#endregion
#region 驗證事件方法
/// <summary>
/// 驗證事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnValidationError(object sender, ValidationErrorEventArgs e)
{
try
{
var handler = GetValidationExceptionHandler();
var element = e.OriginalSource as UIElement;
if (handler == null || element == null)
return;
if (e.Action == ValidationErrorEventAction.Added)
{
_validationExceptionCount++;
}
else if (e.Action == ValidationErrorEventAction.Removed)
{
_validationExceptionCount--;
}
handler.IsValid = _validationExceptionCount == 0;
}
catch (Exception ex)
{
throw ex;
}
}
#endregion
}
4. View容器最外層注冊添加的監聽錯誤事件 ValidationExceptionBehavior
<i:Interaction.Behaviors>
<domain:ValidationExceptionBehavior></domain:ValidationExceptionBehavior>
</i:Interaction.Behaviors>
5. ViewModel 通過實現 IValidationExceptionHandler 來獲取前端的驗證結果
根據前端驗證的結果, 正確保存, 錯誤進行提示
public override void Save()
{
if (!this.IsValid)
{
MessageBox.Show("輸入的格式有誤,請重新輸入!");
return;
}
base.Save();
}
效果: