Wykrywanie błędów walidacji WPF

W WPF możesz skonfigurować walidację na podstawie błędów w warstwie danych podczas powiązania danych za pomocą ExceptionValidationRule LUB DataErrorValidationRule.

Załóżmy, że masz kilka kontrolek ustawionych w ten sposób i masz przycisk Zapisz. Gdy użytkownik kliknie przycisk Zapisz, przed przystąpieniem do zapisywania należy upewnić się, że nie ma błędów walidacji. Jeśli są błędy walidacji, chcesz do nich krzyczeć.

W WPF, jak sprawdzić, czy któreś z kontrolek związanych z danymi ma walidację błędy ustawione?

Author: Hossein Narimani Rad, 2008-09-24

10 answers

Ten post był niezwykle pomocny. Dziękuję wszystkim, którzy przyczynili się. Oto wersja LINQ, którą pokochasz lub znienawidzisz.

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/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-22 06:31:46

Poniższy kod (z książki Programowanie WPF autorstwa Chrisa Sella i Iana Griffiths) sprawdza wszystkie wiążące reguły dotyczące obiektu zależności i jego dzieci:

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

}

Możesz wywołać to w przycisku Zapisz, kliknij opcję obsługi zdarzenia w ten sposób w swojej stronie/oknie

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/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-24 19:44:13

Opublikowany kod nie działał dla mnie przy użyciu Listboxa. Przepisałem i teraz działa:

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/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-19 14:31:20

Miał ten sam problem i wypróbował dostarczone rozwiązania. Połączenie rozwiązań H-Man2 i skiba_k działało dla mnie prawie dobrze, z jednym wyjątkiem: moje okno ma Kontroler TabControl. A reguły walidacji są oceniane tylko dla Tabitemu, który jest obecnie widoczny. Więc zastąpiłem VisualTreeHelper przez LogicalTreeHelper. Teraz działa.

    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/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-23 12:06:54

Oprócz świetnej implementacji LINQ Deana, dobrze się bawiłem pakując kod w rozszerzenie dla DependencyObjects:

public static bool IsValid(this DependencyObject instance)
{
   // Validate recursivly
   return !Validation.GetHasError(instance) &&  LogicalTreeHelper.GetChildren(instance).OfType<DependencyObject>().All(child => child.IsValid());
}
To sprawia, że jest to niezwykle przyjemne, biorąc pod uwagę możliwość ponownego użycia.
 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/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-31 22:06:53

Zaproponowałbym małą optymalizację.

Jeśli zrobisz to wiele razy za pomocą tych samych kontrolek, możesz dodać powyższy kod, aby zachować listę kontrolek, które faktycznie mają reguły walidacji. Wtedy, gdy trzeba sprawdzić ważność, tylko przejść przez te kontrole, zamiast całego drzewa wizualnego. Byłoby to o wiele lepiej, jeśli masz wiele takich kontroli.

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-01 08:40:31

Oto biblioteka do walidacji formularzy w WPF. pakiet Nuget tutaj .

Próbka:

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

Idea polega na tym, że definiujemy zakres walidacji poprzez dołączoną właściwość informującą o tym, jakie kontrolki wejściowe mają śledzić. Wtedy możemy zrobić:

<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/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-10 11:19:37

Możesz rekurencyjnie iterować wszystkie drzewa kontrolek i sprawdzić poprawność dołączonej właściwości.HasErrorProperty, a następnie skupić się na pierwszym, który można znaleźć w nim.

Można również korzystać z wielu już napisanych rozwiązań możesz sprawdzić ten wątek dla przykładu i więcej informacji

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-24 14:29:35

Być może zainteresuje cię BookLibrary przykładowa aplikacja WPF Application Framework (WAF). Pokazuje jak używać walidacji w WPF i jak sterować przyciskiem Zapisz, gdy występują błędy walidacji.

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-16 17:19:46

W formie odpowiedzi a, zamiast jawnie iterować poprzez zasady walidacji, lepiej po prostu wywołać 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/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-28 18:08:00