WPF 擁有豐富的資料繫結與驗證體系,支援從簡單屬性驗證到複雜交叉屬性業務規則。本文系統整理三大核心驗證機制、BindingGroup 進階用法、自訂錯誤顯示樣板,並以現場技術人員活動回報實例完整示範。
| 機制 | 觸發時機 | 適用場景 | 優點 | 缺點 |
|---|---|---|---|---|
| Exception (ValidatesOnExceptions) | 屬性 Setter 擲出異常 / 型別轉換失敗 | 型別轉換守衛、不可變物件驗證 | 最簡單、自動捕獲轉換錯誤 | 異常控制流效能較差、難以表達複雜規則 |
| ValidationRule | Binding 管線中 (依 ValidationStep: RawProposedValue/ConvertedProposedValue/UpdatedValue/CommittedValue) | 單一層性格式/模式驗證 (如 Regex、範圍) | 可在 XAML 宣告、可攜帶參數、支援多規則鏈 | 無法存取其他屬性值 (跨屬性驗證無力) |
| IDataErrorInfo | 來源屬性更新後、或 BindingGroup 提交時 | 跨屬性業務規則、整體物件驗證 | 可存取完整物件狀態、集中管理業務規則 | 索引器常形成巨大 switch、屬性名稱字串硬編碼、Setter 前不觸發 |
實體:Activity (Title, Description, ActivityDate, ActivityType, Inventory, Customer, Objective, Reason)
驗證規則:
<TextBox Text="{Binding Activity.Title, ValidatesOnExceptions=True, UpdateSourceTrigger=PropertyChanged}" />
<TextBox>
<TextBox.Text>
<Binding Path="Activity.Inventory" ValidatesOnExceptions="True" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:InventoryValidationRule InventoryPattern="^\D?(\d{3})\D?\D?(\d{3})\D?(\d{4})$" />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
InventoryValidationRule 繼承 ValidationRule,覆寫 Validate 以 Regex 驗證逗號分隔型號清單。
ViewModel 實作 IDataErrorInfo:
public string this[string propertyName]
{
get
{
switch (propertyName)
{
case "Inventory":
if (ActivityType?.Name == "Install" && string.IsNullOrWhiteSpace(Inventory))
return "安裝類型必須填寫耗材清單";
break;
// ... 其他屬性驗證
}
return null;
}
}
關鍵技巧:當 ActivityType 變更時,主動發送 PropertyChanged("Inventory") 強制 Inventory Binding 重新評估 IDataErrorInfo。
<Grid x:Name="CrossCoupledPropsGrid">
<Grid.BindingGroup>
<BindingGroup>
<BindingGroup.ValidationRules>
<local:CustomerObjectiveOrReasonValidationRule ValidationStep="UpdatedValue" ValidatesOnTargetUpdated="True" />
</BindingGroup.ValidationRules>
</BindingGroup>
</Grid.BindingGroup>
<!-- Customer ComboBox, Objective ComboBox, Reason TextBox -->
</Grid>
CustomerObjectiveOrReasonValidationRule.Validate 接收 BindingGroup,從 Items[0] 取得 ViewModel,檢查三欄位是否至少一個有值。各控件掛接 SelectionChanged/TextChanged 呼叫 BindingGroup.CommitEdit() 即時驗證。
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip">
<Setter.Value>
<Binding Path="(Validation.Errors).CurrentItem.ErrorContent" RelativeSource="{x:Static RelativeSource.Self}" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
<ControlTemplate x:Key="InputErrorTemplate">
<DockPanel>
<Ellipse DockPanel.Dock="Right" Width="10" Height="10" Margin="2,0" ToolTip="資料無效">
<Ellipse.Fill><LinearGradientBrush><GradientStop Color="#11FF1111" Offset="0"/><GradientStop Color="#FFFF0000" Offset="1"/></LinearGradientBrush></Ellipse.Fill>
</Ellipse>
<AdornedElementPlaceholder />
</DockPanel>
</ControlTemplate>
<Style TargetType="TextBox">
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource InputErrorTemplate}" />
</Style>
實務專案中需組合多種機制:Exception 守衛型別轉換、ValidationRule 處理單欄格式、IDataErrorInfo 解決跨屬性業務邏輯、BindingGroup 處理多欄位整體約束。搭配 Validation.ErrorTemplate 與 ToolTip Style,打造專業級驗證體驗。完整範例碼:MSDN Code Gallery。