WPF验证:清除所有验证错误

时间:2010-06-30 09:41:36

标签: wpf validation clear

我有一个WPF UserControl,里面有很多其他控件。 TextBoxes就是其中之一。 每个TextBox都有自己的验证:

<TextBox>
    <TextBox.Text>
        <Binding Path="MyPath" StringFormat="{}{0:N}" NotifyOnValidationError="True">
            <Binding.ValidationRules>
                <r:MyValidationRule ValidationType="decimal" />
            </Binding.ValidationRules>
        </Binding>
    <TextBox.Text>
<TextBox>

现在假设用户在其中键入了一些无效字符。它们都会变成红色。

现在我想重置所有验证错误(来自错误输入)和设置最近正确的值来自DataContext

我在构造函数中设置了DataContext,我不想更改它(DataContext = null对我没用):

DataContext = _myDataContext = new MyDataContext(..);

我已经找到的是这些课程:

Validation.ClearInvalid(..)
BindingExpression.UpdateTarget();

我认为这些课程可以帮助我,但是他们需要具体Binding的{​​{1}},而我想为所有这些课程全球化。

我是否应该遍历 Visual Tree (这实际上是我不喜欢的)或者是否有更好的解决方案呢?

5 个答案:

答案 0 :(得分:2)

这就是BindingGroup的用途......你要在所有控件的容器上设置一个BindingGroup,例如:包含它们的面板。这将导致DataContext的更新被保持,直到您在BindingGroup上调用UpdateSources。如果要重置用户的输入,则需要调用CancelEdit,BindingGroup会将容器内的所有控件重置为DataContext的(仍未更改)值。

答案 1 :(得分:1)

为什么不为数据源的所有属性触发NotifyPropertyChanged?这将更新绑定和UI控件应该从datacontext获取值(这是有效的,因此验证错误将被清除)?

答案 2 :(得分:1)

我遇到了同样的问题。页面上有多个经过验证的控件。我发现/使这个解决方案更新(并清除所有验证)DependencyObject的后代:

using System.Linq;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;

/// <summary>
/// Updates all binding targets where the data item is of the specified type.
/// </summary>
/// <param name="root">The root.</param>
/// <param name="depth">The depth.</param>
/// <param name="dataItemType">Type of the data item.</param>
/// <param name="clearInvalid">Clear validation errors from binding.</param>
public static void UpdateAllBindingTargets(this DependencyObject root, int depth, Type dataItemType, bool clearInvalid)
{
    var bindingExpressions = EnumerateDescendentsBindingExpressions(root, depth);
    foreach (BindingExpression be in bindingExpressions.Where(be => be.DataItem != null && be.DataItem.GetType() == dataItemType))
    {
        if (be != null)
        {
            be.UpdateTarget();
            if (clearInvalid)
                System.Windows.Controls.Validation.ClearInvalid(be);
        }
    }
}

/// <summary>
/// Enumerates all binding expressions on descendents.
/// </summary>
/// <param name="root">The root.</param>
/// <param name="depth">The depth.</param>
/// <returns></returns>
public static IEnumerable<BindingExpression> EnumerateDescendentsBindingExpressions(this DependencyObject root, int depth)
{
    return root.EnumerateDescendents(depth).SelectMany(obj => obj.EnumerateBindingExpressions());
}

/// <summary>
/// Enumerates the descendents of the specified root to the specified depth.
/// </summary>
/// <param name="root">The root.</param>
/// <param name="depth">The depth.</param>
public static IEnumerable<DependencyObject> EnumerateDescendents(this DependencyObject root, int depth)
{
    int count = VisualTreeHelper.GetChildrenCount(root);
    for (int i = 0; i < count; i++)
    {
        var child = VisualTreeHelper.GetChild(root, i);
        yield return child;
        if (depth > 0)
        {
            foreach (var descendent in EnumerateDescendents(child, --depth))
                yield return descendent;
        }
    }
}

/// <summary>
/// Enumerates the binding expressions of a Dependency Object.
/// </summary>
/// <param name="element">The parent element.</param>
public static IEnumerable<BindingExpression> EnumerateBindingExpressions(this DependencyObject element)
{
    if (element == null)
    {
        throw new ArgumentNullException("element");
    }

    LocalValueEnumerator lve = element.GetLocalValueEnumerator();

    while (lve.MoveNext())
    {
        LocalValueEntry entry = lve.Current;

        if (BindingOperations.IsDataBound(element, entry.Property))
        {
            if (entry.Value is PriorityBindingExpression)
            {
                foreach (BindingExpression expr in ((PriorityBindingExpression)entry.Value).BindingExpressions)
                    yield return expr;
            }
            else if (entry.Value is MultiBindingExpression)
            {
                foreach (BindingExpression expr in ((MultiBindingExpression)entry.Value).BindingExpressions)
                    yield return expr;
            }
            else
                yield return entry.Value as BindingExpression;
        }
    }
}

答案 3 :(得分:0)

我不确定你的意思

  

我在构造函数中设置了DataContext,我不想更改它   (DataContext = null对我不起帮助)

通常要重置表单上的所有绑定,请执行以下操作:(假设有一个用于views / viewmodel连接的控制器,否则只需在视图上使用代码隐藏。)

var dataContext = view.DataContext;
view.DataContext = null;
view.DataContext = dataContext;

它不会将其更改为新的数据上下文,只是删除数据上下文并重新加载它。这将启动所有绑定以重新加载。

答案 4 :(得分:0)

尽管 hbarck 给出了完全正确的答案,但我想补充一点,对于许多标准 WPF 控件,BindingGroups 是自动创建的。因此,在大多数情况下,以下简单代码足以清除某些控件(例如 DataGrid)内的所有验证错误:

foreach (var bg in BindingOperations.GetSourceUpdatingBindingGroups(myDataGrid))
    bg.CancelEdit();