Question

Dans WPF, vous pouvez configurer la validation en fonction des erreurs générées dans votre couche de données lors de la liaison de données à l'aide de ExceptionValidationRule ou DataErrorValidationRule .

Supposons que de nombreux contrôles soient configurés de cette manière et que vous disposiez d’un bouton Enregistrer. Lorsque l'utilisateur clique sur le bouton Enregistrer, vous devez vous assurer qu'il n'y a pas d'erreur de validation avant de procéder à l'enregistrement. S'il y a des erreurs de validation, vous voulez les crier.

Dans WPF, comment savoir si des erreurs de validation sont configurées sur l'un de vos contrôles Data Bound?

Était-ce utile?

La solution

Ce message 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);
}

Autres conseils

Le code suivant (tiré du livre Programming WPF de Chris Sell & Ian Griffiths) valide toutes les règles de liaison 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 appeler cela dans le gestionnaire d’événements de clic du bouton de sauvegarde comme ceci dans votre page / fenêtre

private void saveButton_Click(object sender, RoutedEventArgs e)
{

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

    ....
   }
}

Le code posté ne fonctionnait pas pour moi lors de l'utilisation d'un contrôle ListBox. Je l'ai réécrit et maintenant ça marche:

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

Avait le même problème et essayé les solutions fournies. Une combinaison des solutions de H-Man2 et de skiba_k a presque fonctionné de manière satisfaisante pour moi, à une exception près: My Window a un TabControl. Et les règles de validation ne sont évaluées que pour le TabItem actuellement visible. J'ai donc remplacé VisualTreeHelper par LogicalTreeHelper. Maintenant ça marche.

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

En plus de l'excellente implémentation LINQ de Dean, je me suis amusé à envelopper le code dans une extension pour DependencyObjects:

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 compte tenu de la réutilisation.

Je voudrais offrir une petite optimisation.

Si vous faites cela plusieurs fois sur les mêmes contrôles, vous pouvez ajouter le code ci-dessus pour conserver une liste des contrôles qui ont réellement des règles de validation. Ensuite, chaque fois que vous devez vérifier la validité, examinez uniquement ces contrôles, au lieu de l’arborescence visuelle. Cela s’avérerait beaucoup mieux si vous avez beaucoup de contrôles de ce type.

Voici une bibliothèque pour la validation de formulaire dans WPF. Le paquet Nuget ici .

Exemple:

<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 étendue de validation via la propriété attachée en lui indiquant les contrôles d'entrée à suivre. Ensuite, 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>

Vous pouvez parcourir toutes les arborescences de contrôles de manière récursive et vérifier la propriété attachée Validation.HasErrorProperty, puis vous concentrer sur la première que vous y trouverez.

vous pouvez également utiliser de nombreuses solutions déjà écrites vous pouvez vérifier cette thread pour un exemple et plus d'informations

Vous êtes peut-être intéressé par l'exemple d'application BookLibrary du Framework d'application WPF ( WAF) . Il montre comment utiliser la validation dans WPF et comment contrôler le bouton Enregistrer en cas d'erreur de validation.

Dans le formulaire de réponse, au lieu d'itérer explicitement les règles de validation, il est préférable d'appeler 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;
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top