本文章是由機器翻譯。

輸入驗證

使用 WPF 強制執行複雜的商務資料規則

Brian Noyes

下載程式碼範例

Microsoft Windows Presentation Foundation (WPF) 有豐富的資料繫結系統。 除了的鬆散連接,從支援的邏輯和資料模型-檢視-ViewModel (MVVM) 模式透過使用者介面定義的索引鍵的程式,資料繫結系統會有功能強大且更具彈性的商務資料驗證案例的支援。 資料繫結機制,在 WPF 中的包含幾個用於評估輸入資料的有效性,當您建立一個可編輯的檢視選項。 再加上,WPF 樣板和樣式的控制項的功能讓您能夠輕鬆地自訂您指示給使用者的驗證錯誤的方式。

支援複雜的規則,並向使用者顯示驗證錯誤,您通常需要使用可用的驗證機制的組合。 取得複雜的商務規則時,甚至是看似簡單資料輸入的表單可以呈現驗證挑戰。 常見的案例牽涉到兩個簡單的規則,在一個個別的屬性層級和一個屬性的有效性取決於另一個屬性值的位置的交叉連結的屬性。 但是,驗證支援在 WPF 資料繫結會讓它容易解決這些挑戰。

這個本文請參閱如何使用 IDataErrorInfo 介面實作、 ValidationRules、 BindingGroups、 例外狀況,與驗證相關的附加的屬性和事件到您的資料驗證所需要的地址。 您也請參閱自訂驗證錯誤 ErrorTemplates 和工具提示顯示的方式。 這個的文件中,我假設您已熟悉 WPF 的基本的資料繫結功能。 更多的背景,請參閱 「 John Papa ’s 2007 年 12 月 MSDN Magazine 文章,「 在 WPF 中的資料繫結 的 」。

資料驗證概觀

您必須確定資料是否有效之前它太遠取得這些變更的來源, 幾乎任何時間,您可以輸入或修改系統的應用程式中的資料 (在此情況下,使用者。 此外,需要他們所輸入的資料無效的使用者提供清楚的指示,並希望也提供一些指示,說明如何修正它。 這些項目是很容易,只要您知道要使用哪一種功能,以及當執行使用 WPF。

當您使用資料繫結在 WPF 中存在的商務資料時,您通常使用繫結物件提供目標控制項上的單一屬性與資料來源的物件屬性之間的資料管線。 為相關的驗證,您通常執行 TwoWay 資料繫結 — 也就是說,除了傳送到目標屬性的顯示的 [來源] 屬性中的資料編輯的資料也順著來源的目標 的 圖 1 所示。

Figure 1 Data Flow in TwoWay Data Binding
圖 1 流程 TwoWay 資料中的資料繫結

有三種機制,用來判斷透過資料繫結控制項中輸入的資料是否有效。 這些彙總 的 圖 2 中。

圖 2 繫結驗證機制

驗證機制 描述
例外狀況 藉由設定 ValidatesOnExceptions 屬性繫結物件上,如果嘗試設定已修改的值,在 [來源物件] 屬性的程序中引發例外狀況驗證錯誤將該繫結的。
ValidationRules 繫結類別有一個屬性,以提供驗證規則衍生的類別執行個體的集合。 這些 ValidationRules 需要覆寫每次在繫結控制項中的資料變更時,繫結會呼叫的驗證方法。 如果驗證方法會傳回無效的 ValidationResult 物件的繫結設定驗證錯誤。
IDataErrorInfo 繫結的資料來源物件上實作 IDataErrorInfo 介面,並設定繫結物件上的 ValidatesOnDataErrors 屬性在繫結將從繫結的資料來源物件公開的 IDataErrorInfo API 的呼叫。 如果非空值] 或 [非空白的字串會傳回從這些屬性呼叫,驗證錯誤設定該繫結。

當使用者輸入或修改 TwoWay 資料繫結的資料時,工作流程會介入:

  • 資料輸入或修改由使用者透過按鍵、 滑鼠、 觸控式或畫筆互動與程式的項目產生變更的項目上的屬性。
  • 必要時,資料會被轉換成資料來源屬性型別。
  • 設定來源屬性值。
  • Binding.SourceUpdated 附加事件引發。
  • 例外狀況由繫結被攔截,如果在 [資料來源] 屬性 setter 由擲回,可以用來表示驗證錯誤。
  • 如果實作 IDataErrorInfo 的屬性稱為上資料的來源物件。
  • 驗證錯誤指示會呈現給使用者,並附加 Validation.Error 事件引發。

您可以看到有幾個點的機制,根據您選擇位置可以導致驗證錯誤,在程序中。 不顯示在清單中是 [ValidationRules 引發的地方。 因為他們可以在處理程序取決於您在 [驗證規則] 設定 ValidationStep 屬性值中的不同點引發包括轉換之後, 的型別轉換之前更新屬性後,或已變更的值已認可 (如果資料物件實作 IEditableObject) 時。 預設值是發生在型別轉換之前的 RawProposedValue。 點,當資料從轉換目標控制項屬性的型別為資料來源物件的屬性型別通常會隱含發生不用涉及任何您的程式碼如 TextBox 中的數字輸入。 這個型別轉換處理程序可以擲回例外狀況應該用來指示給使用者的驗證錯誤。

如果 [來源物件] 屬性 can’t 甚至會寫入值,很顯然它是無效的輸入。 如果選擇 ValidationRules 連結它們會叫用由 ValidationStep] 屬性來表示處理序中點和它們可以傳回任何邏輯是內嵌在它們,或從這些呼叫為基礎的驗證錯誤。 如果 [來源物件] 屬性 setter 擲回的例外狀況,應該幾乎都被視為驗證的錯誤與型別轉換大小寫。

最後,如果您實作 IDataErrorInfo,您將新增至您的資料來源物件,該介面的索引子屬性將呼叫已被設定,看看是否有驗證錯誤,該介面從傳回的字串為基礎的屬性。 我涵蓋每個詳細這些機制有點稍後。

當您想要發生的驗證是另一個必須做的決定。 當繫結] 會將資料寫入基礎的來源物件屬性時,就會發生驗證。 當驗證所需的繫結的大部分屬性設定為 [PropertyChanged UpdateSourceTrigger 屬性被指定位置。 例如 TextBox.Text 的某些屬性表示焦點離開用來編輯資料的控制項時,會發生驗證的 FocusChange 變更值。 若要表示驗證已明確地叫用在繫結上的明確,也可以設定值。 我在本文稍後將討論在 BindingGroup 使用外顯的模式。

特別是對文字方塊的驗證案例中您通常想要為相當直接的意見反應提供給使用者。 若要支援的您應該設定 PropertyChanged 要繫結的 UpdateSourceTrigger 屬性:

Text="{Binding Path=Activity.Description, UpdateSourceTrigger=PropertyChanged}

開啟許多的真實的驗證案例需要使用一種以上的這些機制。 每個都有其優點和缺點] 中根據您 ’re 關心和驗證邏輯可以所在位置的驗證錯誤的類型。

商務驗證案例

若要使它更具象,let’s 逐步解說 semi-real 商務內容的編輯案例,和請參閱每個這些機制可以進入遊戲。 這種情況下,並驗證規則會根據實際的應用程式,我撰寫的客戶,在其中相當簡單的表單會需要幾乎每一個驗證機制,因為支援的商務規則使用的驗證。 本文使用的簡單應用程式,我採用每個機制,來示範它們的使用,即使它們不 ’re 所有必要的明確。

let’s 假設您需要撰寫應用程式來支援人員執行 in-home 客戶支援電話 (纜線] 份子,但也會嘗試接-賣出其他的功能及服務的人認為) 欄位技術人員。 在技術人員會執行在該欄位中的每個活動的他需要輸入的活動報告,告訴他以及與相關資料的幾個部分。 物件模型是 的 圖 3 所示。

Figure 3 Object Model for the Sample Application
圖 3 的 範例應用程式的物件模型

使用者填寫的資料的主要的片段是包括標題、 [ActivityDate、 一個 ActivityType (下拉式清單選取預先定義的活動類型的) 及說明 」 的活動物件。 它們也需要與他們的活動相關的三種可能性。 他們需要選取其中一個客戶活動的執行清單中的客戶指派給他們或公司從一份公司目標,已相關活動的目標,或如果 「 客戶 」 或 「 目標 」 都不套用此活動可以手動輸入一個原因。

以下是應用程式需要強制執行驗證規則:

  • 標題和描述是必要的欄位。
  • [ActivityDate 必須是 7 天早於目前日期] 和 [不得晚於比七天以後不早。
  • 如果已選取 [ActivityType 安裝 物品欄欄位是必要的而且應該指出所耗用的設備,從技術人員 ’s 卡車片段。 存貨項目需要輸入為以逗點分隔清單,與預期的模型數字結構,為輸入的項目。
  • 必須提供一個以上的客戶、 目標或原因。

這看起來好像很簡單的需求,但最後兩個特別沒有那麼簡單地址,因為它們表示屬性之間的交叉聯繫。 正在執行的應用程式與某些無效的資料 — 標示紅色方塊) 的 圖 4 所示。

Figure 4 A Dialog Showing ToolTips and Invalid Data
圖 4 的 工具提示和錯誤資料變更的對話方塊

例外狀況驗證

最簡單形式,就是驗證的讓設定視為驗證錯誤的目標屬性的程序中引發的例外狀況。 曾經在繫結設定的目標屬性之前,將會造成從型別轉換程序的例外狀況 ; 它可能會導致從明確的擲回的例外狀況在屬性 setter 中 ; 或可能會導致從呼叫出商務物件從取得在擲回例外狀況的 setter 進一步向下堆疊。

若要將這項機制您只需在 ValidatesOnExceptions 屬性設定為真繫結物件上:

Text="{Binding Path=Activity.Title, ValidatesOnExceptions=True}"

嘗試設定來源物件的屬性 (在此情況下 Activity.Title) 時,擲回例外狀況時,驗證錯誤會設定在控制項上。 預設驗證錯誤指標是紅色 的 [圖 5] 所示的控制項周圍的框線。

Figure 5 A Validation Error
圖 5 的 A 驗證錯誤

因為例外狀況可以出現在型別轉換程序,它 ’s 輸入繫結上設定此屬性,只要有 ’s 無法在型別轉換,任何機會,即使支援屬性只在成員變數 (搭配的例外狀況沒有機會設定值很好的作法。

就例如假設您是要作為輸入控制項中的文字方塊相似,日期時間屬性。 如果使用者輸入的字串,can’t 轉換 ValidatesOnExceptions 會是唯一的方法,您的繫結可能表示發生的錯誤,因為 [來源物件] 屬性會永遠不會被呼叫。

如果您只需要特定的檢視,例如無效的資料時停用命令,您可以在控制項上攔截 Validation.Error 附加的事件。 您也需要 NotifyOnValidationError 屬性設定為真時,繫結上。

<TextBox Name="ageTextBox" 
  Text ="{Binding Path=Age, 
    ValidatesOnExceptions=True, 
    NotifyOnValidationError=True}" 
    Validation.Error="OnValidationError".../>

驗證規則驗證

在某些的情況下,您可能想結合的驗證,在使用者介面層級,並需要更複雜的邏輯,以判斷輸入是否有效。 範例] 應用程式,請考慮物品欄欄位的驗證規則。 如果資料輸入它必須遵循特定模式的型號的逗號分隔清單。 一個驗證規則可以輕易地容納這因為它完全上所設定的值而定。 在 [驗證規則] 可以使用 string.Split 呼叫開啟為字串] 陣列輸入,然後使用規則運算式來檢查個別的組件是否符合指定的模式。 若要執行此動作,您可以定義一個驗證規則,如 的 [圖 6] 所示。

圖 6 的 驗證規則,以驗證字串陣列

public class InventoryValidationRule : ValidationRule {

  public override ValidationResult Validate(
    object value, CultureInfo cultureInfo) {

    if (InventoryPattern == null)
      return ValidationResult.ValidResult;

    if (!(value is string))
      return new ValidationResult(false, 
     "Inventory should be a comma separated list of model numbers as a string");

    string[] pieces = value.ToString().Split(‘,’);
    Regex m_RegEx = new Regex(InventoryPattern);

    foreach (string item in pieces) {
      Match match = m_RegEx.Match(item);
      if (match == null || match == Match.Empty)
        return new ValidationResult(
          false, "Invalid input format");
    }

    return ValidationResult.ValidResult;
  }

  public string InventoryPattern { get; set; }
}

從使用,點的 XAML 讓他們更具彈性,可以設定一個驗證規則上所公開的屬性。 這個驗證規則會略過 can’t 轉換成字串陣列的值。 但它當規則可以執行 string.Split,然後會使用一個 RegEx 驗證以逗點分隔清單中的每個字串符合透過 InventoryPattern 屬性所設定的模式。

當您以有效的旗標設為 False,則傳回一個 ValidationResult 時,您所提供的錯誤訊息可用來在 UI 中的使用者呈現錯誤我稍後說明。 ValidationRules 的一個缺點是您需要在 XAML 中,來連結,向上擴充繫結項目,如下列程式碼所示:

<TextBox Name="inventoryTextBox"...>
  <TextBox.Text>
    <Binding Path="Activity.Inventory" 
             ValidatesOnExceptions="True" 
             UpdateSourceTrigger="PropertyChanged" 
             ValidatesOnDataErrors="True">
      <Binding.ValidationRules>
        <local:InventoryValidationRule 
          InventoryPattern="^\D?(\d{3})\D?\D?(\d{3})\D?(\d{4})$"/>
      </Binding.ValidationRules>
    </Binding>
  </TextBox.Text>
</TextBox>

在此的範例我繫結會仍然引發驗證錯誤如果例外狀況發生,因為將 ValidatesOnExceptions] 屬性被設定為 True,和我也支援 IDataErrorInfo 被設定為我教您接下來的 true 的 ValidatesOnDataErrors 為基礎的驗證。

如果您附加至相同屬性的多個 ValidationRules 這些規則可以各有不同的值,ValidationStep 屬性,或他們可以有相同的值。 相同 ValidationStep 中的規則會宣告的順序進行評估。 之前在稍後 ValidationSteps 很明顯地執行較早 ValidationSteps 的規則。 項目可能不明顯,是如果一個驗證規則會傳回錯誤的後續的規則都不會評估。 因此只有一個指示錯誤因 ValidationRules 會第一個驗證錯誤。

IDataErrorInfo 驗證

IDataErrorInfo 介面需要公開一個屬性和一個索引子的實作器:

public interface IDataErrorInfo {
  string Error { get; }
  string this[string propertyName] { get; }
}

錯誤屬性用來指示一的整個物件的錯誤和索引子用來指示在個別的屬性層級的錯誤。 它們都使用相同:傳回非空值] 或 [非空白的字串表示驗證錯誤。 在就另外您所傳回的字串可以用於的使用者顯示錯誤我稍後說明。

當您使用個別的控制項繫結至個別的屬性在資料來源物件上時,介面的最重要的部分就是索引子。 錯誤屬性只能用於案例 (例如,當物件顯示在 DataGrid 或一個 BindingGroup。 Error 屬性用來表示錯誤資料列] 層級中,而索引子用來指示在儲存格層級錯誤。

實作 IDataErrorInfo 有一個大的缺點:索引子的實作通常會導致大切換大小寫陳述式,具有一個的物件中的每個屬性名稱的大小寫,而且您具有參數,並根據字串,並傳回字串比對來指示錯誤。 此外,IDataErrorInfo 的實作不會呼叫,直到已經在物件上設定屬性值。 如果其他物件已經在物件上訂閱 INotifyPropertyChanged.PropertyChanged,它們將已經已通知的變更,並可能已經開始 IDataErrorInfo 實作即將宣告無效的資料為基礎的工作。 如果,可能是您的應用程式的問題,您需要從屬性 setters 擲回例外狀況時 ’re 以所設定的值不太快樂。

IDataErrorInfo 良好的好處是它很容易地址交叉連結的內容。 就例如除了使用驗證輸入的格式的 [物品欄] 欄位的驗證規則,請記住物品欄欄位必須填入 [ActivityType 是安裝時的需求。 本身的驗證規則具有資料繫結物件的其他屬性不允許存取。 它只是取得傳遞被設定為最多繫結在繫結屬性的值。 此需求時 ActivityType 屬性取得設定您會導致驗證發生在物品欄屬性,以傳回不正確的結果,如果物品欄中的值是空的將會設定為安裝的 ActivityType 時所需要的地址。

若要達成此目的您需要 IDataErrorInfo,使您可以檢閱 [物品欄] 和 [ActivityType 屬性評估庫存時, 如此處所示:

public string this[string propertyName] {
  get { return IsValid(propertyName); }
}

private string IsValid(string propertyName) {
  switch (propertyName) {
    ...
    case "Inventory":
      if (ActivityType != null && 
        ActivityType.Name == "Install" &&  
        string.IsNullOrWhiteSpace(Inventory))
        return "Inventory expended must be entered for installs";
      break;
}

此外,您需要取得 ActivityType 屬性變更時,叫用驗證清單繫結。 通常,一個繫結只能查詢 IDataErrorInfo 實作,或呼叫 ValidationRules,如果該屬性變更在 UI 中。 在本例中,我要觸發繫結驗證的重新評估,即使 [物品欄] 屬性未變更,但相關的 ActivityType。

有兩種方式可以取得 ActivityType 屬性變更時,重新整理清單繫結。 第一,最簡單的方式是 PropertyChanged 事件發佈的清單,當您設定 [ActivityType:

ActivityType _ActivityType;
public ActivityType ActivityType {
  get { return _ActivityType; }
  set { 
    if (value != _ActivityType) {
      _ActivityType = value;
      PropertyChanged(this, 
        new PropertyChangedEventArgs("ActivityType"));
      PropertyChanged(this, 
        new PropertyChangedEventArgs("Inventory"));
    }
  }
}

這會導致重新整理並重新評估該繫結驗證繫結。

第二種方式,就是掛上 ActivityType 下拉式方塊或它的父項目之一 Binding.SourceUpdated 附加的事件觸發從程式碼後置處理常式,該事件的繫結重新整理:

<ComboBox Name="activityTypeIdComboBox" 
  Binding.SourceUpdated="OnPropertySet"...

private void OnPropetySet(object sender, 
  DataTransferEventArgs e) {

  if (activityTypeIdComboBox == e.TargetObject) {
    inventoryTextBox.GetBindingExpression(
      TextBox.TextProperty).UpdateSource();
  }
}

呼叫 UpdateSource 上一個繫結以程式設計方式使其繫結的目標項目中的目前值寫入來源] 屬性觸發驗證鏈結,如同使用者就必須編輯控制項。

使用 BindingGroup 交叉結合內容]。

Microsoft.NET Framework 3.5 SP1 已加入 BindingGroup 功能。 一個 BindingGroup 是特別設計來讓您評估在一群繫結驗證一次。 就例如您可以讓使用者以填滿整個表單中] 和 [等候直到她在按下送出] 或 [儲存] 按鈕,以評估表單的驗證規則,然後一次顯示驗證錯誤。 在 [範例] 應用程式中我必須需求,至少一個客戶、 目標或原因必須提供。 一個 BindingGroup 可用來評估的表單也子集。

若要將一個 BindingGroup 您需要一組控制項與一般共用共同的祖系項目在其上連結。 在 [範例] 應用程式中客戶下拉式方塊、 目標下拉式方塊和原因 TextBox 所有存留內相同的格線版面配置。 BindingGroup 是 FrameworkElement 上的屬性。 它有 ValidationRules 集合屬性,您可以填入一或多個驗證規則的物件。 下列 XAML 顯示 BindingGroup 連結,範例應用程式:

<Grid>...
<Grid.BindingGroup>
  <BindingGroup>
    <BindingGroup.ValidationRules>
      <local:CustomerObjectiveOrReasonValidationRule 
        ValidationStep="UpdatedValue" 
        ValidatesOnTargetUpdated="True"/>
    </BindingGroup.ValidationRules>
  </BindingGroup>
</Grid.BindingGroup>
</Grid>

在此的範例我需要將 [CustomerObjectiveOrReasonValidationRule 的執行個體加入集合。 ValidationStep 屬性可讓您有一些控制傳遞至規則的值。 UpdatedValue 表示使用之後它會寫入資料來源物件寫入的值。 您也可以選擇讓您的 ValidationStep 的值會使用未經處理的輸入來自使用者、 在套用型別和值的轉換後的值或 「 已確認 」 的值表示實作 IEditableObject 介面的交易變更物件的屬性。

ValidatesOnTargetUpdated 旗標會使目標屬性透過繫結] 設定來評估每次規則。 這包括初始設定時,因此您必須立即驗證錯誤指示,如果初始資料無效,以及每次使用者變更控制項屬於 [BindingGroup 中的值。

連結到一個 BindingGroup 的驗證規則運作有點不同比一個驗證規則連結到單一的繫結。 圖 7 顯示連結到前一個程式碼範例所示的 BindingGroup 的驗證規則。

圖 7 的 一個 BindingGroup 的驗證規則

public class CustomerObjectiveOrReasonValidationRule : 
  ValidationRule {

  public override ValidationResult Validate(
    object value, CultureInfo cultureInfo) {

    BindingGroup bindingGroup = value as BindingGroup;
    if (bindingGroup == null) 
      return new ValidationResult(false, 
        "CustomerObjectiveOrReasonValidationRule should only be used with a BindingGroup");

    if (bindingGroup.Items.Count == 1) {
      object item = bindingGroup.Items[0];
      ActivityEditorViewModel viewModel = 
        item as ActivityEditorViewModel;
      if (viewModel != null && viewModel.Activity != null && 
        !viewModel.Activity.CustomerObjectiveOrReasonEntered())
        return new ValidationResult(false, 
          "You must enter one of Customer, Objective, or Reason to a valid entry");
    }
    return ValidationResult.ValidResult;
  }
}

攔截最多的驗證規則中一個單一的繫結中傳遞的值是從設為路徑的繫結的資料來源屬性的單一值。 如果是一個的 BindingGroup 會傳遞至驗證規則的值會是本身的 BindingGroup。 它包含的 [包含] 項目在這種情況下,格線 DataContext 填入的項目集合。

我範例] 應用程式使用 MVVM] 模式,因此檢視的 DataContext 是本身的 ViewModel。 項目集合包含只在 ViewModel 單一參考。 從 ViewModel,我可以取得在它的 [活動] 屬性。 活動類別在這種情況下已決定是否至少一個客戶、 目標或原因已輸入,don’t 需要重複該邏輯中,驗證規則的驗證方法。

因為如果您 ’re 滿意資料中傳遞的值,請使用其他 ValidationRules 涵蓋稍早,會傳回一個 ValidationResult.ValidResult。 如果您 ’re 不快樂,您建構新的 ValidationResult 假的有效旗標與字串訊息,指出問題可以再用來顯示用途。

設定 ValidatesOnTargetUpdated 旗標並不足以取得自動,不過射擊 ValidationRules。 BindingGroup 的設計是周圍的明確觸發驗證整個群組的一般是透過一些像是一個送出,或儲存在表單上的按鈕按下的控制項的概念。 在某些的情況下使用者 don’t 想要它們考慮編輯程序完成,所以記住這種方法設定為 [BindingGroup 之前,先與驗證錯誤指示。

在 [範例] 應用程式中,我想立即驗證錯誤的意見反應提供給使用者的隨時他變更表單中的項目。 若要執行一個的 BindingGroup 與您有連接個別輸入控制項的群組的一部份,並且具有事件上的適當變更事件這些事件的處理常式會觸發 [BindingGroup 評估。 在 [範例] 應用程式中,這表示連結上兩個 ComboBoxes ComboBox.SelectionChanged 事件和 TextBox.TextChanged 事件,在文字方塊上。 這些所有指向單一處理的方法,在程式碼後置中:

private void OnCommitBindingGroup(
  object sender, EventArgs e) {

  CrossCoupledPropsGrid.BindingGroup.CommitEdit();
}

請注意,驗證] 顯示的 [紅色框線將會顯示在 [BindingGroup 位於,例如 的 圖 4 所示範例的應用程式在格線的 [FrameworkElement 的預設值。 您也可以改變使用 Validation.ValidationAdornerSite 和 Validation.ValidationAdornerSiteFor 驗證指示顯示的位置附加屬性。 預設情況下,個別的控制項也會顯示紅色框線有其個別的驗證錯誤。 在 [範例] 應用程式中我關閉這些框線藉由設定 [ErrorTemplate 為 null 透過樣式。

與.NET Framework 3.5 SP1 BindingGroup,可能遇到的錯誤初始表單上的載入,驗證適當的顯示問題,即使該驗證規則上設定 ValidatesOnTargetUpdated 屬性。 我發現此因應措施,就是 「 jiggle 」 中 [BindingGroup 繫結的屬性之一。 範例] 應用程式中,您可以新增並移除結尾的任何文字一開始會呈現在載入事件中檢視的文字方塊中的空間如下:

string originalText = m_ProductTextBox.Text;
m_ProductTextBox.Text += " ";
m_ProductTextBox.Text = originalText;

這會導致 BindingGroup ValidationRules,引發,因為其中一個被收納的繫結屬性已變更,造成每個繫結来呼叫的驗證。 這個行為固定 [.NET] Framework 4.0 中,應該會有無需取得初始顯示驗證錯誤的解決方法,只是在驗證規則 ValidatesOnTargetUpdated 屬性設定為真。

驗證錯誤顯示

前面提過,WPF 顯示驗證錯誤的預設方法是繪製控制項周圍的紅色框線。 通常您想要自訂此以其他方式顯示錯誤。 此外,預設不是顯示驗證錯誤相關聯的錯誤訊息。 錯誤訊息顯示在工具提示中,只有在有驗證錯誤存在時,才為常見的需求。 自訂驗證錯誤顯示是透過一組附加與驗證相關的屬性和樣式的組合相當簡單。

若要加入顯示錯誤的工具提示文字是輕而易舉的事。 您只需要定義適用於每當發生驗證錯誤時,驗證錯誤文字控制項設定 [工具提示] 屬性中輸入控制項的樣式。 若要支援這,有兩個需要採用的附加的屬性:Validation.HasError 和 Validation.Errors。 樣式,以設定工具提示的 TextBox 類型為目標如下所示:

<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>

您可以看到 [樣式只包含 Validation.HasError 附加屬性的屬性觸發程序。 HasError 屬性會設為 true 時為繫結更新其來源物件的屬性,並驗證機制會產生錯誤。 發生的例外狀況可能來自驗證規則或 IDataErrorInfo 呼叫。 [樣式],然後使用會包含的錯誤字串集合,如果有驗證錯誤存在的 [附加的 Validation.Errors] 屬性。 您可以使用該集合型別中的 CurrentItem 屬性只擷取集合中的第一個字串。 或者,您可以設計資料繫結至集合,並顯示 ErrorContent 屬性,每個項目在清單為導向的控制項中的項目。

若要變更控制項的預設驗證錯誤顯示紅色框線以外的其他,您必須 Validation.ErrorTemplate 附加屬性設定您想要自訂的控制項中的新範本。 在範例應用程式而不被顯示紅色框線的小型的紅色漸層圓形會顯示右邊的每個控制項含有錯誤。 若要執行該動作您定義會用來作為 [ErrorTemplate 的控制項範本。

<ControlTemplate x:Key="InputErrorTemplate">
  <DockPanel>
    <Ellipse DockPanel.Dock="Right" Margin="2,0" 
             ToolTip="Contains invalid data"
             Width="10" Height="10">
      <Ellipse.Fill>
        <LinearGradientBrush>
          <GradientStop Color="#11FF1111" Offset="0" />
          <GradientStop Color="#FFFF0000" Offset="1" />
        </LinearGradientBrush>
      </Ellipse.Fill>
    </Ellipse>
    <AdornedElementPlaceholder />
  </DockPanel>
</ControlTemplate>

若要連結至控制項的控制項範本,只需要設定 Validation.ErrorTemplate 屬性,控制項可以一次執行到一個樣式:

<Style TargetType="TextBox">
  <Setter Property="Validation.ErrorTemplate" 
    Value="{StaticResource InputErrorTemplate}" />
  ...
</Style>

換行上移

這個本文中,我顯示方式,您可以使用三種驗證機制,WPF 資料繫結來解決許多商務資料驗證案例。 您會看到如何使用例外狀況、 ValidationRules 和 IDataErrorInfo 介面位址的單一屬性驗證,以及其驗證規則而定的控制項的其他屬性的目前值的屬性。 您也會看到如何使用 BindingGroups 在一次評估多個繫結,以及如何自訂 WPF 的預設值以外的錯誤顯示。

範例應用程式,這個文件已滿足連接支援它的資料檢視會使用 MVVM 的簡單應用程式中所述的商務規則的驗證的完整集合。

Brian Noyes   是主要的架構設計人員 ( idesign.net )、 Microsoft 地區導演和 Microsoft MVP。 Noyes 是作者和在 Microsoft Tech·Ed、 DevConnections、 DevTeach 和其他會議全球頻繁的喇叭。 請連絡他透過他的部落格,在 briannoyes.net

多虧給來檢閱這份文件的技術專家下列:   Sam 彎曲