Détection des erreurs de Validation du FPF

dans WPF vous pouvez configurer la validation basée sur les erreurs lancées dans votre couche de données pendant la liaison de données en utilisant le ExceptionValidationRule ou DataErrorValidationRule .

supposez que vous ayez un tas de commandes configurées de cette façon et que vous ayez un bouton de sauvegarde. Lorsque l'utilisateur clique sur le bouton Enregistrer, vous devez vous assurer qu'il n'y a pas d'erreurs de validation avant de procéder à l'enregistrement. S'il y a des erreurs de validation, vous devez les signaler.

dans WPF, comment savoir si l'un de vos contrôles liés aux données comporte des erreurs de validation?

110
demandé sur Hossein Narimani Rad 2008-09-24 18:22:10

10 réponses

Ce post a été extrêmement utile. Merci à tous ceux qui ont contribué. Voici une version de LINQ que vous aimerez ou détesterez.

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
répondu Dean 2015-01-22 06:31:46

le code suivant (de programmation WPF book by Chris Sell & Ian Griffiths) valide toutes les règles contraignantes sur un objet de dépendance et ses enfants:

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

}

vous pouvez l'appeler dans votre bouton Enregistrer cliquez sur event handler comme ceci dans votre page/fenêtre ""

private void saveButton_Click(object sender, RoutedEventArgs e)
{

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

    ....
   }
}
47
répondu aogan 2008-09-24 19:44:13

le code affiché ne fonctionnait pas pour moi lorsque j'utilisais une liste de diffusion. Je l'ai réécrit et maintenant il fonctionne:

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
répondu H-Man2 2009-02-19 14:31:20

Avait le même problème et essayé les solutions apportées. Une combinaison des solutions de H-Man2 et de skiba_k a fonctionné presque très bien pour moi, à une exception près: ma fenêtre a un TabControl. Et les règles de validation ne sont évaluées que pour le TabItem qui est actuellement visible. J'ai donc remplacé VisualTreeHelper par LogicalTreeHelper. Maintenant, il fonctionne.

    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
répondu 2009-10-23 12:06:54

en plus de la grande LINQ-implémentation de Dean, j'ai eu du plaisir à envelopper le code dans une extension pour les objets de dépendance:

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

cela le rend extrêmement agréable en considérant la réutilisabilité.

7
répondu Matthias Loerke 2012-10-31 22:06:53

j'offrirais une petite optimisation.

si vous faites cela plusieurs fois sur les mêmes contrôles, vous pouvez ajouter le code ci-dessus pour garder une liste de contrôles qui ont réellement des règles de validation. Ensuite, chaque fois que vous avez besoin de vérifier la validité, passez seulement ces contrôles, au lieu de l'arbre visuel complet. Ce serait beaucoup mieux si vous aviez beaucoup de tels contrôles.

2
répondu sprite 2010-07-01 08:40:31

Voici une bibliothèque pour la validation du formulaire dans WPF. package Nuget ici .

échantillon:

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

l'idée est que nous définissions une portée de validation via la propriété ci-jointe lui indiquant les contrôles d'entrée à suivre. Alors nous pouvons faire:

<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
répondu Johan Larsson 2016-02-10 11:19:37

vous pouvez itérer sur toute l'arborescence des contrôles de façon récursive et vérifier la Validation de la propriété ci-jointe.HasErrorProperty, puis se concentrer sur le premier que vous y trouvez.

vous pouvez également utiliser de nombreuses solutions déjà écrites vous pouvez vérifier ce" fil 151940920 " pour un exemple et plus d'informations

0
répondu user21243 2008-09-24 14:29:35

Vous pourriez être intéressé par le BookLibrary exemple d'application de la WPF Cadre applicatif (WAF) . Il montre comment utiliser la validation dans WPF et comment contrôler le bouton Enregistrer quand des erreurs de validation existent.

0
répondu jbe 2010-08-16 17:19:46

dans le formulaire de réponse aogan, au lieu d'itérer explicitement par des règles de validation, mieux vaut simplement invoquer 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
répondu Dan Neely 2016-10-28 18:08:00