检测 WPF 验证错误

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/127477/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 19:38:54  来源:igfitidea点击:

Detecting WPF Validation Errors

wpfvalidationdata-binding

提问by Kevin Berridge

In WPF you can setup validation based on errors thrown in your Data Layer during Data Binding using the ExceptionValidationRuleor DataErrorValidationRule.

在 WPF 中,您可以使用ExceptionValidationRule或设置基于数据绑定期间数据层中抛出的错误的验证DataErrorValidationRule

Suppose you had a bunch of controls set up this way and you had a Save button. When the user clicks the Save button, you need to make sure there are no validation errors before proceeding with the save. If there are validation errors, you want to holler at them.

假设您以这种方式设置了一堆控件,并且您有一个保存按钮。当用户单击“保存”按钮时,您需要确保在继续保存之前没有验证错误。如果存在验证错误,您想对它们大喊大叫。

In WPF, how do you find out if any of your Data Bound controls have validation errors set?

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

回答by Dean

This post was extremely helpful. Thanks to all who contributed. Here is a LINQ version that you will either love or hate.

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

回答by aogan

The following code (from Programming WPF book by Chris Sell & Ian Griffiths) validates all binding rules on a dependency object and its children:

以下代码(来自 Chris Sell 和 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;
    }

}

You can call this in your save button click event handler like this in your page/window

您可以在页面/窗口中像这样在保存按钮单击事件处理程序中调用它

private void saveButton_Click(object sender, RoutedEventArgs e)
{

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

    ....
   }
}

回答by H-Man2

The posted code did not work for me when using a ListBox. I rewrote it and now it works:

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

回答by H-Man2

Had the same problem and tried the provided solutions. A combination of H-Man2's and skiba_k's solutions worked almost fine for me, for one exception: My Window has a TabControl. And the validation rules only get evaluated for the TabItem that is currently visible. So I replaced VisualTreeHelper by LogicalTreeHelper. Now it works.

遇到了同样的问题并尝试了提供的解决方案。H-Man2 和skiba_k 的解决方案的组合对我来说几乎没有问题,只有一个例外:我的窗口有一个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;
    }

回答by Matthias Loerke

In addition to the great LINQ-implementation of Dean, I had fun wrapping the code into an extension for DependencyObjects:

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

This makes it extremely nice considering reuseablity.

考虑到可重用性,这使得它非常好。

回答by Johan Larsson

Here is a libraryfor form validation in WPF. Nuget package here.

这是WPF 中用于表单验证的Nuget 包在这里

Sample:

样本:

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

The idea is that we define a validation scope via the attached property telling it what input controls to track. Then we can do:

这个想法是我们通过附加属性定义一个验证范围,告诉它要跟踪哪些输入控件。然后我们可以这样做:

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

回答by sprite

I would offer a small optimization.

我会提供一个小的优化。

If you do this many times over the same controls, you can add the above code to keep a list of controls that actually have validation rules. Then whenever you need to check for validity, only go over those controls, instead of the whole visual tree. This would prove to be much better if you have many such controls.

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

回答by jbe

You might be interested in the BookLibrarysample application of the WPF Application Framework (WAF). It shows how to use validation in WPF and how to control the Save button when validation errors exists.

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

回答by jbe

In answer form aogan, instead of explicitly iterate through validation rules, better just invoke expression.UpdateSource():

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

回答by user21243

You can iterate over all your controls tree recursively and check the attached property Validation.HasErrorProperty, then focus on the first one you find in it.

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

you can also use many already-written solutions you can check thisthread for an example and more information

您还可以使用许多已经编写好的解决方案,您可以查看线程以获取示例和更多信息