检测WPF验证错误

时间:2008-09-24 14:22:11

标签: wpf validation data-binding

在WPF中,您可以使用ExceptionValidationRuleDataErrorValidationRule根据数据绑定期间在数据层中抛出的错误设置验证。

假设你有一堆这样设置的控件,你有一个Save按钮。当用户单击“保存”按钮时,您需要确保在继续保存之前没有验证错误。如果存在验证错误,您希望对它们感到高兴。

在WPF中,如何确定是否有任何数据绑定控件设置了验证错误?

10 个答案:

答案 0 :(得分:132)

这篇文章非常有帮助。感谢所有贡献者。这是一个你会喜欢或讨厌的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);
}

答案 1 :(得分:47)

以下代码(来自Chris Sell&amp; Ian Griffiths的Programming 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
   {

    ....
   }
}

答案 2 :(得分:31)

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

答案 3 :(得分:15)

遇到同样的问题并尝试了提供的解决方案。 H-Man2和skiba_k解决方案的组合对我来说几乎没问题,但有一个例外:My Window有一个TabControl。并且仅针对当前可见的TabItem评估验证规则。所以我用LogicalTreeHelper替换了VisualTreeHelper。现在它有效。

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

答案 4 :(得分:7)

除了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());
}

考虑到重复使用,这非常好。

答案 5 :(得分:2)

我会提供一个小优化。

如果您在同一控件上多次执行此操作,则可以添加上述代码以保留实际具有验证规则的控件列表。然后,只要您需要检查有效性,只需检查那些控件,而不是整个可视树。 如果你有很多这样的控制,这将证明要好得多。

答案 6 :(得分:1)

这是WPF中表单验证的libraryNuget package here

样品:

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

答案 7 :(得分:0)

您可以递归迭代所有控件树并检查附加属性Validation.HasErrorProperty,然后专注于您在其中找到的第一个。

您还可以使用许多已编写的解决方案 您可以查看this主题以获取示例和更多信息

答案 8 :(得分:0)

在回答形式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;
    }
}

答案 9 :(得分:0)

您可能对 WPF Application Framework (WAF) BookLibrary 示例应用感兴趣。它显示了如何在WPF中使用验证以及如何在存在验证错误时控制“保存”按钮。