Вопрос

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

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

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

Это было полезно?

Решение

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

Другие советы

Следующий код (из книги Криса Селла и Яна Гриффитса «Программирование 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
   {

    ....
   }
}

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

Была та же проблема, попробовал предложенные решения.Комбинация решений 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;
    }

Помимо великолепной реализации Dean на LINQ, мне было интересно обернуть код в расширение для DependencyObjects:

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

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

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

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

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

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

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

Возможно, вас заинтересует КнигаБиблиотека образец заявления о Платформа приложений WPF (WAF).В нем показано, как использовать проверку в WPF и как управлять кнопкой «Сохранить» при наличии ошибок проверки.

В форме ответа, вместо того, чтобы явно перебирать правила проверки, лучше просто вызвать 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;
    }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top