2008-09-24 103 views
104

在WPF中,你可以根據數據過程中的數據層引發的錯誤使用ExceptionValidationRuleDataErrorValidationRule綁定設置驗證。檢測WPF驗證錯誤

假設你有一大堆的這種方式設置控制和你有一個保存按鈕。當用戶單擊保存按鈕時,在繼續保存之前,您需要確保沒有驗證錯誤。如果存在驗證錯誤,您需要對它們進行解析。

在WPF中,你如何找出如果您的任何數據綁定控件都設置驗證錯誤?

回答

127

這篇文章非常有幫助。感謝所有貢獻者。這是一個LINQ版本,你會愛或恨。

private void CanExecute(object sender, CanExecuteRoutedEventArgs e) 
{ 
    e.CanExecute = IsValid(sender as DependencyObject); 
} 

private bool IsValid(DependencyObject obj) 
{ 
    // The dependency object is valid if it has no errors and all 
    // of its children (that are dependency objects) are error-free. 
    return !Validation.GetHasError(obj) && 
    LogicalTreeHelper.GetChildren(obj) 
    .OfType<DependencyObject>() 
    .All(IsValid); 
} 
0

你可以在你的所有控件樹遞歸遍歷並檢查附加屬性Validation.HasErrorProperty,然後專注於你在那裏找到的第一個。

您還可以使用許多已經寫好的解決方案 您可以檢查this線程的例子,更多信息

46

下面的代碼(從編程WPF書克里斯賣&伊恩·格里菲思)驗證對所有具有約束力的規則依賴對象及其子:

public static class Validator 
{ 

    public static bool IsValid(DependencyObject parent) 
    { 
     // Validate all the bindings on the parent 
     bool valid = true; 
     LocalValueEnumerator localValues = parent.GetLocalValueEnumerator(); 
     while (localValues.MoveNext()) 
     { 
      LocalValueEntry entry = localValues.Current; 
      if (BindingOperations.IsDataBound(parent, entry.Property)) 
      { 
       Binding binding = BindingOperations.GetBinding(parent, entry.Property); 
       foreach (ValidationRule rule in binding.ValidationRules) 
       { 
        ValidationResult result = rule.Validate(parent.GetValue(entry.Property), null); 
        if (!result.IsValid) 
        { 
         BindingExpression expression = BindingOperations.GetBindingExpression(parent, entry.Property); 
         System.Windows.Controls.Validation.MarkInvalid(expression, new ValidationError(rule, expression, result.ErrorContent, null)); 
         valid = false; 
        } 
       } 
      } 
     } 

     // Validate all the bindings on the children 
     for (int i = 0; i != VisualTreeHelper.GetChildrenCount(parent); ++i) 
     { 
      DependencyObject child = VisualTreeHelper.GetChild(parent, i); 
      if (!IsValid(child)) { valid = false; } 
     } 

     return valid; 
    } 

} 

您可以在保存按鈕單擊事件處理程序這樣在你的頁面/窗口調用這個

private void saveButton_Click(object sender, RoutedEventArgs e) 
{ 

    if (Validator.IsValid(this)) // is valid 
    { 

    .... 
    } 
} 
+1

哇,它工作正常。 它爲我節省了很多時間 非常感謝。 – Ewerton 2010-03-25 20:41:15

30

使用列表框時,張貼代碼並沒有爲我工作。我改寫了它,現在它的工作原理:

public static bool IsValid(DependencyObject parent) 
{ 
    if (Validation.GetHasError(parent)) 
     return false; 

    // Validate all the bindings on the children 
    for (int i = 0; i != VisualTreeHelper.GetChildrenCount(parent); ++i) 
    { 
     DependencyObject child = VisualTreeHelper.GetChild(parent, i); 
     if (!IsValid(child)) { return false; } 
    } 

    return true; 
} 
+1

爲您的解決方案投票處理我的ItemsControl。 – 2014-08-26 10:47:29

+0

我正在使用此解決方案來檢查我的數據網格是否有驗證錯誤。然而,這個方法在我的viewmodel命令canexecute方法上被調用,並且我認爲訪問可視樹對象在某種程度上違反了MVVM模式,不是嗎?任何替代品? – 2016-04-11 15:07:02

0

在回答澳苷的形式,而不是通過驗證規則明確地迭代,更好的只是調用expression.UpdateSource():

if (BindingOperations.IsDataBound(parent, entry.Property)) 
{ 
    Binding binding = BindingOperations.GetBinding(parent, entry.Property); 
    if (binding.ValidationRules.Count > 0) 
    { 
     BindingExpression expression 
      = BindingOperations.GetBindingExpression(parent, entry.Property); 
     expression.UpdateSource(); 

     if (expression.HasError) valid = false; 
    } 
} 
15

有同樣的問題,並試圖提供的解決方案。 H-Man2's和skiba_k的解決方案的組合對我來說幾乎沒有問題,但有一個例外:我的窗口有一個TabControl。驗證規則僅針對當前可見的TabItem進行評估。所以我用LogicalTreeHelper替換了VisualTreeHelper。現在它可以工作。

public static bool IsValid(DependencyObject parent) 
    { 
     // Validate all the bindings on the parent 
     bool valid = true; 
     LocalValueEnumerator localValues = parent.GetLocalValueEnumerator(); 
     while (localValues.MoveNext()) 
     { 
      LocalValueEntry entry = localValues.Current; 
      if (BindingOperations.IsDataBound(parent, entry.Property)) 
      { 
       Binding binding = BindingOperations.GetBinding(parent, entry.Property); 
       if (binding.ValidationRules.Count > 0) 
       { 
        BindingExpression expression = BindingOperations.GetBindingExpression(parent, entry.Property); 
        expression.UpdateSource(); 

        if (expression.HasError) 
        { 
         valid = false; 
        } 
       } 
      } 
     } 

     // Validate all the bindings on the children 
     System.Collections.IEnumerable children = LogicalTreeHelper.GetChildren(parent); 
     foreach (object obj in children) 
     { 
      if (obj is DependencyObject) 
      { 
       DependencyObject child = (DependencyObject)obj; 
       if (!IsValid(child)) { valid = false; } 
      } 
     } 
     return valid; 
    } 
+0

一個很好的解決方案... – ChristopheD 2011-05-27 14:25:35

2

我會提供一個小優化。

如果你這樣做了相同的控制很多次,你可以添加上面的代碼,以保持實際有驗證規則控制列表。然後,無論何時您需要檢查有效性,只需查看這些控件,而不是整個可視化樹。 如果你有很多這樣的控件,這將證明會好得多。

0

您可能會感興趣的WPF Application Framework (WAF)BookLibrary示例應用程序。它顯示瞭如何在WPF中使用驗證以及如何在存在驗證錯誤時控制Save按鈕。

7

除了偉大的LINQ的執行院長,我很開心包裝代碼爲擴展DependencyObjects:

public static bool IsValid(this DependencyObject instance) 
{ 
    // Validate recursivly 
    return !Validation.GetHasError(instance) && LogicalTreeHelper.GetChildren(instance).OfType<DependencyObject>().All(child => child.IsValid()); 
} 

這使得它非常漂亮考慮reuseablity。

1

這是一個用於在WPF中進行表單驗證的libraryNuget package here

樣品:

<Border BorderBrush="{Binding Path=(validationScope:Scope.HasErrors), 
           Converter={local:BoolToBrushConverter}, 
           ElementName=Form}" 
     BorderThickness="1"> 
    <StackPanel x:Name="Form" validationScope:Scope.ForInputTypes="{x:Static validationScope:InputTypeCollection.Default}"> 
     <TextBox Text="{Binding SomeProperty}" /> 
     <TextBox Text="{Binding SomeOtherProperty}" /> 
    </StackPanel> 
</Border> 

的想法是,我們通過附加屬性告訴它什麼樣的輸入控制跟蹤定義的驗證範圍。 然後我們可以這樣做:

<ItemsControl ItemsSource="{Binding Path=(validationScope:Scope.Errors), 
            ElementName=Form}"> 
    <ItemsControl.ItemTemplate> 
     <DataTemplate DataType="{x:Type ValidationError}"> 
      <TextBlock Foreground="Red" 
         Text="{Binding ErrorContent}" /> 
     </DataTemplate> 
    </ItemsControl.ItemTemplate> 
</ItemsControl>