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



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



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



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

723   10  

10 ответов:

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

    ....
   }
}

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

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

в дополнение к отличной LINQ-реализации Dean, мне было весело обернуть код в расширение для 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>

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

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

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

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

Comments

    Ничего не найдено.