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?
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);
}
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
{
....
}
}
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;
}
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;
}
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é.
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.
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>
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
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.
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;
}
}