2008-09-24 19 views
104

En WPF puede configurar la validación basada en los errores arrojados en su capa de datos durante la vinculación de datos utilizando ExceptionValidationRule o DataErrorValidationRule.Detección de errores de validación de WPF

Supongamos que tiene un grupo de controles configurados de esta manera y tiene un botón Guardar. Cuando el usuario hace clic en el botón Guardar, debe asegurarse de que no haya errores de validación antes de continuar con el guardado. Si hay errores de validación, quiere gritarles.

En WPF, ¿cómo averigua si algunos de sus controles de datos enlazados tienen errores de validación?

Respuesta

127

Esta publicación fue extremadamente útil. Gracias a todos los que contribuyeron. Aquí hay una versión de LINQ que amarás u odiarás.

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

¡Me gusta mucho esta solución en particular! – ChristopheD

+0

Acabo de tropezar con este hilo. Pequeña función muy útil. ¡Gracias! –

+0

¡Tú ... eres ... increíble! –

0

Puede iterar sobre todos los árboles de control recursivamente y verificar la propiedad adjunta Validation.HasErrorProperty, luego enfocarse en la primera que encuentre en ella.

también puede utilizar muchas soluciones ya escritos- se puede comprobar this rosca para un ejemplo y más información

46

El siguiente código (de Programación libro WPF por Chris Vender & Ian Griffiths) valida todas las normas vinculantes respecto a una objeto de dependencia y sus hijos:

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

} 

puede llamar a este en tu ahorrar clic de botón controlador de eventos como este en su página/ventana

private void saveButton_Click(object sender, RoutedEventArgs e) 
{ 

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

    .... 
    } 
} 
+1

Wow, funciona bien. Me ahorra mucho tiempo Muchas gracias. – Ewerton

30

El código publicado no me funcionaba cuando usaba un ListBox. He reescrito y ahora funciona:

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

Vota tu solución para trabajar en mis ItemsControl. –

+0

Estoy usando esta solución para verificar si mi cuadrícula de datos tiene errores de validación. Sin embargo, este método es invocado en mi método viewmodel canexecute method, y creo que acceder a los objetos visuales del árbol de alguna manera viola el patrón MVVM, ¿no? ¿Alguna alternativa? –

0

En respuesta aogan forma, en lugar de iterar explícitamente a través de reglas de validación, mejor sólo invocar 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

tenía el mismo problema y trataron las soluciones aportadas. Una combinación de las soluciones de H-Man2 y skiba_k funcionó casi bien para mí, por una sola excepción: My Window tiene un TabControl. Y las reglas de validación solo se evalúan para el TabItem que está actualmente visible. Así que reemplacé VisualTreeHelper por LogicalTreeHelper. Ahora funciona.

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

Una solución excelente ... – ChristopheD

2

Ofrecería una pequeña optimización.

Si lo hace muchas veces sobre los mismos controles, puede agregar el código anterior para mantener una lista de controles que realmente tienen reglas de validación. Luego, cuando necesite verificar la validez, solo revise esos controles, en lugar de todo el árbol visual. Esto probaría ser mucho mejor si tiene muchos de esos controles.

0

que podría estar interesado en la aplicación de la muestraBookLibrary del WPF Application Framework (WAF). Muestra cómo usar la validación en WPF y cómo controlar el botón Guardar cuando existen errores de validación.

7

Además de la gran LINQ aplicación de Dean, me he divertido envolver el código en una extensión para DependencyObjects:

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

Esto hace que sea muy agradable teniendo en cuenta reuseablity.

1

Aquí hay un library para la validación de formularios en WPF. Nuget package here.

muestra:

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

La idea es que se define un marco de validación a través de la propiedad adjunta le dice qué controles de entrada para realizar un seguimiento. Entonces podemos hacer:

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