Обнаружение ошибок проверки WPF

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

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

В WPF, как вы узнаете, установлен ли какой-либо из ваших элементов управления Data Bound?

Ответ 1

Это сообщение было очень полезно. Спасибо всем, кто внес свой вклад. Вот версия 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);
}

Ответ 2

Следующий код (из программы программирования WPF Chris Sell и Ian Griffiths) проверяет все правила привязки для объекта зависимостей и его дочерних элементов:

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
   {

    ....
   }
}

Ответ 3

Опубликованный код не работал у меня при использовании 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;
}

Ответ 4

Имел ту же проблему и попробовал предоставленные решения. Комбинация решений H-Man2 и skiba_k работала почти отлично для меня, за одним исключением: My Window имеет 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;
    }

Ответ 5

В дополнение к отличной LINK-реализации Dean, мне было весело переносить код в расширение для DependencyObjects:

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

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

Ответ 6

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

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

Ответ 7

Ниже представлена ​​библиотека для проверки формы в 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>

Ответ 8

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

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

Ответ 9

В форме ответа 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;
    }
}

Ответ 10

Вам может быть интересно приложение BookLibrary приложения WPF Application Framework (WAF). Он показывает, как использовать проверку в WPF и как управлять кнопкой "Сохранить", когда существуют ошибки проверки.