首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

WPF 資料繫結驗證深度實戰:ValidationRule、自訂錯誤模板與 BindingGroup 多欄位聯合驗證

     編輯:關於.NET

WPF 驗證管線:從目標到源的四階段防線

當 Mode=TwoWay 且 UpdateSourceTrigger 觸發時,WPF 繫結引擎依序執行:

  1. 移除舊錯誤:清除目標元素 Validation.Errors 集合。
  2. 自訂 ValidationRule:逐一呼叫 Validate(value, culture),任一失敗即建立 ValidationError 加入集合,停止後續規則。
  3. 值轉換器:所有規則通過後執行 IValueConverter.Convert。
  4. 源屬性 Setter:寫入來源物件屬性;若拋出例外且繫結掛載 ExceptionValidationRule,則轉為 ValidationError。

任何方向的成功傳輸皆會清除 Validation.Errors,Validation.HasError 附加屬性隨集合空/非空自動切換。

三種驗證策略逐層遞進

1. 內建 ExceptionValidationRule — 快速原型開發

實體屬性 Setter 中拋出例外即可:

public string Title {
    set {
        if (string.IsNullOrEmpty(value)) throw new ApplicationException("標題不可為空");
        _title = value;
    }
}

XAML:

<TextBox.Text>
  <Binding Path="Title" UpdateSourceTrigger="PropertyChanged">
    <Binding.ValidationRules><ExceptionValidationRule/></Binding.ValidationRules>
  </Binding>
</TextBox.Text>

預設 LostFocus 觸發,改 PropertyChanged 即時驗證。缺點:錯誤訊息僅能透過 ToolTip 或自訂模板顯示,無法在 XAML 直接控制樣式邏輯。

2. 自訂 ValidationRule + 錯誤模板 — 生產級單欄驗證

public class EnumValidationRule : ValidationRule {
    public string EnumClass { get; set; }
    public string ErrorMessage { get; set; }
    public override ValidationResult Validate(object value, CultureInfo culture) {
        var input = (value ?? "").ToString();
        var type = Type.GetType("MyApp." + EnumClass);
        return string.IsNullOrEmpty(input) || !Enum.IsDefined(type, input)
            ? new ValidationResult(false, ErrorMessage)
            : ValidationResult.ValidResult;
    }
}

XAML 掛載並啟用即時觸發:

<Binding Path="Title" UpdateSourceTrigger="PropertyChanged">
  <Binding.ValidationRules>
    <local:EnumValidationRule EnumClass="TitleEnum" ErrorMessage="請輸入有效枚舉值"/>
  </Binding.ValidationRules>
</Binding>

通用錯誤樣式(置於 ResourceDictionary 復用):

<Style TargetType="TextBox">
  <Style.Triggers>
    <Trigger Property="Validation.HasError" Value="True">
      <Setter Property="Validation.ErrorTemplate">
        <Setter.Value>
          <ControlTemplate>
            <DockPanel LastChildFill="True">
              <TextBlock DockPanel.Dock="Right" Foreground="Red" FontSize="12"
                Text="{Binding ElementName=adorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}"/>
              <Border BorderBrush="Red" BorderThickness="1">
                <AdornedElementPlaceholder Name="adorner"/>
              </Border>
            </DockPanel>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Trigger>
  </Style.Triggers>
</Style>

AdornedElementPlaceholder 佔位原控件位置,紅框+右側錯誤文字,視覺效果即時呈現。

3. BindingGroup — 表單級/列表級跨欄位聯合驗證

單一控件驗證無法處理「開始日期必須早於結束日期」等跨欄位規則。BindingGroup 將同一 DataContext 下多個繫結納入同一驗證單元:

<Grid>
  <Grid.BindingGroup>
    <BindingGroup>
      <BindingGroup.ValidationRules>
        <local:EmployeeValidationRule ValidationStep="ConvertedProposedValue"/>
      </BindingGroup.ValidationRules>
    </BindingGroup>
  </Grid.BindingGroup>

  <TextBox Text="{Binding Path=StartDate, ValidationStep=ConvertedProposedValue}"/>
  <TextBox Text="{Binding Path=EndDate, ValidationStep=ConvertedProposedValue}"/>
  <Button Content="提交" Click="Submit_Click"/>
</Grid>

驗證規則可取得完整實體物件:

public class EmployeeValidationRule : ValidationRule {
    public override ValidationResult Validate(object value, CultureInfo culture) {
        var group = (BindingGroup)value;
        var emp = (Employee)group.Items[0];
        var start = (DateTime)group.GetValue(emp, "StartDate");
        var end = (DateTime)group.GetValue(emp, "EndDate");
        return start > end
            ? new ValidationResult(false, $"開始日期 {start:d} 不得晚於結束日期 {end:d}")
            : ValidationResult.ValidResult;
    }
}

提交時呼叫 group.CommitEdit() 觸發驗證並寫入源;group.ValidateWithoutUpdate() 僅驗證不寫入。

BindingGroup 核心成員速查

成員類型用途
ItemsIList當前 DataContext 實體集合(通常單一實體)
BindingExpressionsReadOnlyCollection<BindingExpression>所有參與驗證的繫結表達式
GetValue(item, propertyName)object取得指定實體的屬性值(已轉換後)
ValidationRulesCollection<ValidationRule>掛載的驗證規則
CommitEdit()void驗證通過後寫入源,觸發 IEditableObject.EndEdit
CancelEdit()void撤銷編輯,觸發 IEditableObject.CancelEdit
BeginEdit()void開始編輯,觸發 IEditableObject.BeginEdit

ValidationStep 時機選擇指南

  • RawProposedValue (0):原始字串,適合格式檢查(如 Regex)。
  • ConvertedProposedValue (1,預設):經 Converter 轉換後,適合業務規則(跨欄位比較)。
  • UpdatedValue (2):源屬性 Setter 執行後,適合資料庫唯一鍵衝突檢查。
  • CommittedValue (3):資料庫 Commit 後,極少用。

最佳實踐清單

  • 單欄驗證優先用 ValidationRule + 共用 ErrorTemplate Style,保持 XAML 乾淨。
  • 跨欄位、跨實體規則統一歸入 BindingGroup.ValidationRules,避免分散在多個控件。
  • 大量資料編輯畫面(DataGrid/ListView)為每行建立獨立 BindingGroup(ItemBindingGroup),隔離驗證範圍。
  • 驗證錯誤視覺化統一透過 Validation.ErrorTemplate,勿在 Trigger 中硬編碼顏色,利於主題切換。
  • 非同步伺服器端驗證(如遠端唯一性檢查)可在 UpdatedValue 階段發起,配合 INotifyDataErrorInfo (.NET 4.5+) 實現更豐富的非同步驗證體驗。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved