Обнаружение ошибок валидации WPF - PullRequest
114 голосов
/ 24 сентября 2008

В WPF вы можете настроить проверку на основе ошибок, возникших в вашем уровне данных во время привязки данных, используя ExceptionValidationRule или DataErrorValidationRule.

Предположим, у вас настроено несколько элементов управления, и у вас есть кнопка Сохранить. Когда пользователь нажимает кнопку «Сохранить», необходимо убедиться в отсутствии ошибок проверки, прежде чем продолжить сохранение. Если есть ошибки валидации, вы хотите их исправить.

Как вы узнаете в WPF, есть ли в ваших элементах управления привязкой данных установленные ошибки проверки?

Ответы [ 10 ]

132 голосов
/ 10 января 2011

Этот пост был чрезвычайно полезным. Спасибо всем, кто внес свой вклад. Вот версия LINQ, которую вы будете любить или ненавидеть.

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);
}
47 голосов
/ 24 сентября 2008

Следующий код (из книги «Программирование WPF» Криса Села и Иана Гриффитса) проверяет все правила связывания для объекта зависимости и его дочерних элементов:

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

}

Вы можете вызвать это в вашем обработчике нажатия кнопки сохранения, как это показано на вашей странице / в окне

private void saveButton_Click(object sender, RoutedEventArgs e)
{

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

    ....
   }
}
31 голосов
/ 19 февраля 2009

Размещенный код не работал для меня при использовании ListBox. Я переписал его, и теперь он работает:

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;
}
15 голосов
/ 23 октября 2009

Была такая же проблема и попробовал предоставленные решения. Комбинация решений H-Man2 и skiba_k для меня почти сработала, за одним исключением: в моем окне есть TabControl. И правила проверки оцениваются только для TabItem, который в данный момент видим. Поэтому я заменил VisualTreeHelper на LogicalTreeHelper. Теперь это работает.

    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;
    }
7 голосов
/ 01 ноября 2012

В дополнение к замечательной реализации Дина в LINQ, я с удовольствием упаковал код в расширение для DependencyObjects:

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

Это очень приятно, учитывая возможность повторного использования.

2 голосов
/ 01 июля 2010

Я бы предложил небольшую оптимизацию.

Если вы делаете это много раз для одних и тех же элементов управления, вы можете добавить приведенный выше код, чтобы сохранить список элементов управления, которые фактически имеют правила проверки. Затем всякий раз, когда вам нужно проверить правильность, переходите только к этим элементам управления вместо всего визуального дерева. Было бы намного лучше, если бы у вас было много таких элементов управления.

1 голос
/ 10 февраля 2016

Вот библиотека для проверки формы в WPF. Пакет Nuget здесь .

Пример:

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

Идея состоит в том, что мы определяем область проверки с помощью присоединенного свойства, сообщая ему, какие элементы управления вводом отслеживать. Тогда мы можем сделать:

<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>
0 голосов
/ 16 августа 2010

Вас может заинтересовать BookLibrary пример приложения WPF Application Framework (WAF) . В нем показано, как использовать проверку в WPF и как управлять кнопкой «Сохранить» при наличии ошибок проверки.

0 голосов
/ 06 июля 2009

В форме ответа aogan вместо явной итерации по правилам проверки лучше просто вызвать 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 голосов
/ 24 сентября 2008

Вы можете рекурсивно выполнить итерацию по всему дереву элементов управления и проверить прикрепленное свойство Validation.HasErrorProperty, а затем сосредоточиться на первом, найденном в нем.

вы также можете использовать множество уже написанных решений Вы можете проверить эту ветку для примера и дополнительной информации

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...