Frage

In WPF können Sie Setup-Validierung basiert auf Fehler in der Datenschicht geworfen während Datenbindung der ExceptionValidationRule oder DataErrorValidationRule verwendet wird.

Angenommen, Sie hatten eine Reihe von Kontrollen auf diese Weise eingerichtet und man hatte eine Schaltfläche Speichern. Wenn der Benutzer auf die Schaltfläche Speichern klickt, müssen Sie es sicherstellen, dass keine Validierungsfehler sind mit der speichern, bevor Sie fortfahren. Wenn es Validierungsfehler sind, möchten Sie sie brüllen.

In WPF, wie finden Sie heraus, ob alle Ihre Daten banden Steuerelemente Set Validierungsfehler?

War es hilfreich?

Lösung

Dieser Beitrag war sehr hilfreich. Vielen Dank an alle, die dazu beigetragen. Hier ist eine LINQ-Version, die Sie entweder lieben oder hassen.

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

Andere Tipps

Der folgende Code (aus Programming WPF Buch von Chris Sell & Ian Griffiths) validiert alle verbindlichen Regeln für ein Abhängigkeitsobjekt und seine Kinder:

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

}

Sie können diese rufen in Ihrem Schaltfläche Click-Ereignishandler speichern, wie dies in Ihrer Seite / Fenster

private void saveButton_Click(object sender, RoutedEventArgs e)
{

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

    ....
   }
}

Der entsandte Code nicht für mich arbeiten, wenn eine List-Box verwenden. Ich schrieb es und jetzt funktioniert es:

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

Wir hatten das gleiche Problem und versucht, die bereitgestellten Lösungen. Eine Kombination von H-Man2 der und Lösungen skiba_k arbeitete für mich fast in Ordnung, für eine Ausnahme: My Window ein TabControl hat. Und die Validierungsregeln nur für den TabItem erhalten ausgewertet, die derzeit sichtbar ist. So ersetzte ich VisualTreeHelper durch LogicalTreeHelper. Jetzt funktioniert es.

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

Neben der großen LINQ-Implementierung von Dean, ich hatte Spaß für DependencyObjects den Code in eine Erweiterung Verpackung:

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

Dies macht es extrem schön wenn man bedenkt reuseablity.

Ich würde eine kleine Optimierung bieten.

Wenn Sie tun dies viele Male über die gleichen Kontrollen, können Sie den obigen Code fügen Sie eine Liste der Steuerelemente zu halten, die tatsächlich Validierungsregeln haben. Dann, wenn Sie auf ihre Gültigkeit überprüfen müssen, nur die Einstellungen gehen, statt der gesamten visuellen Struktur. Dies würde sich viel besser sein, wenn Sie viele solche Kontrollen haben.

Hier ist ein Bibliothek für Formularvalidierung in WPF. Nuget Paket hier .

Beispiel:

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

Die Idee ist, dass wir eine Validierung Umfang über die angefügten Eigenschaft sagen sie definieren, welche Eingabe zu verfolgen steuert. Dann können wir tun:

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

Sie können iterieren alle Steuerelemente Baum rekursiv und überprüfen Sie die angefügten Eigenschaft Validation.HasErrorProperty, dann auf den ersten konzentrieren Sie es finden.

Sie können auch viele nutzen bereits geschriebene Lösungen Sie können this Gewinde für ein Beispiel und weitere Informationen

Vielleicht haben Sie Interesse an der Booklibrary Beispielanwendung der WPF Application Framework ( WAF) . Es zeigt, wie die Validierung in WPF verwenden und wie die Schaltfläche Speichern steuern, wenn Validierungsfehler vorhanden ist.

In Antwort Form aogan, statt explizit durch Validierungsregeln laufen, besser nur expression.UpdateSource(): aufrufen

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;
    }
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top