Detección de Errores de Validación de WPF


En WPF puede configurar la validación basada en errores lanzados en su Capa de Datos durante el Enlace de Datos utilizando ExceptionValidationRule o DataErrorValidationRule.

Supongamos que tiene un montón 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, debes gritarles.

En WPF, ¿cómo averiguar si alguno de sus controles enlazados a datos tiene validación errores establecidos?

Author: Hossein Narimani Rad, 2008-09-24

10 answers

Este post 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);
}
 132
Author: Dean,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-22 06:31:46

El siguiente código (del libro Programming WPF de Chris Sell & Ian Griffiths) valida todas las reglas vinculantes en un 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 esto en su botón guardar, haga clic en el controlador de eventos de esta manera en su página/ventana

private void saveButton_Click(object sender, RoutedEventArgs e)
{

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

    ....
   }
}
 47
Author: aogan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-24 19:44:13

El código publicado no funcionó para mí cuando uso un ListBox. Lo reescribí 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;
}
 30
Author: H-Man2,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-02-19 14:31:20

Tuvo el mismo problema y probó las soluciones proporcionadas. Una combinación de soluciones de H-Man2 y skiba_k funcionó casi bien para mí, con una excepción: Mi ventana tiene un TabControl. Y las reglas de validación solo se evalúan para el TabItem que está visible actualmente. Así que sustituí 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;
    }
 15
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-10-23 12:06:54

Además de la gran implementación LINQ de Dean, me divertí envolviendo 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 lo hace extremadamente agradable teniendo en cuenta la reuseablity.

 7
Author: Matthias Loerke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-31 22:06:53

Ofrecería una pequeña optimización.

Si hace esto 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, cada vez que necesite verificar la validez, solo repase esos controles, en lugar de todo el árbol visual. Esto resultaría ser mucho mejor si usted tiene muchos de estos controles.

 2
Author: sprite,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-01 08:40:31

Aquí hay una biblioteca para la validación de formularios en WPF. Paquete Nuget aquí.

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 definamos un ámbito de validación a través de la propiedad adjunta diciéndole qué controles de entrada rastrear. 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>
 1
Author: Johan Larsson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-10 11:19:37

Puede iterar sobre todo el árbol de controles recursivamente y comprobar la validación de la propiedad adjunta.HasErrorProperty, luego concéntrate en el primero que encuentres en él.

También puede usar muchas soluciones ya escritas puedes consultar este hilo para ver un ejemplo y más información

 0
Author: user21243,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-24 14:29:35

Usted puede estar interesado en el BookLibrary ejemplo de aplicación de la 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.

 0
Author: jbe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-16 17:19:46

En la forma de respuesta aogan, en lugar de iterar explícitamente a través de reglas de validación, mejor simplemente 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;
    }
}
 0
Author: Dan Neely,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-28 18:08:00