检测WPF验证错误
在WPF中,我们可以使用ExceptionValidationRule或者DataErrorValidationRule基于数据绑定期间在数据层中引发的错误来设置验证。
假设我们以这种方式设置了一堆控件,并且有一个"保存"按钮。当用户单击"保存"按钮时,我们需要确保没有验证错误,然后再继续保存。如果存在验证错误,则要大声疾呼。
在WPF中,如何确定是否有任何数据绑定控件设置了验证错误?
解决方案
我们可以递归遍历所有控件树,并检查添加的属性Validation.HasErrorProperty,然后专注于在其中找到的第一个控件。
我们还可以使用许多已经编写的解决方案
我们可以检查该线程的示例和更多信息
以下代码(来自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;
}
}
我们可以在页面/窗口中的"保存"按钮单击事件处理程序中调用此方法
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;
}
在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;
}
}
遇到相同的问题,并尝试了提供的解决方案。 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;
}
我会提供一个小的优化。
如果对同一控件多次执行此操作,则可以添加上面的代码以保留实际上具有验证规则的控件列表。然后,每当我们需要检查有效性时,只需遍历那些控件即可,而不是整个可视树。
如果我们有许多这样的控件,这将被证明会更好。
我们可能对WPF应用程序框架(WAF)的BookLibrary示例应用程序感兴趣。它显示了如何在WPF中使用验证以及存在验证错误时如何控制"保存"按钮。

