【问题标题】:WPF Validation: Clearing all validation errorsWPF 验证:清除所有验证错误
【发布时间】:2011-03-10 01:12:14
【问题描述】:

我有一个 WPF UserControl,其中包含许多其他控件。 文本框就是其中之一。 每个 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 和具体的 FrameworkElement,我想在全球范围内为所有这些课程。

我是否应该遍历 Visual Tree(这确实是我不喜欢的)还是有更好的解决方案?

【问题讨论】:

  • 当您绑定正确的值时,它会自动重置验证错误。您是否在视图模型中使用 INotifyPropertyChanged 接口,并且属性会引发属性更改事件?
  • 是的,我正在使用它。这就是我最初尝试过的。但是 DataSource 没有变化。因此 WPF 不会反映旧值

标签: wpf validation


【解决方案1】:

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

【讨论】:

  • 哦,明白了,它对我有用,因为我使用 ValidatesOnExceptions 选项并在设置器中抛出验证异常。所以原始(有效)值不会被无效覆盖。
【解决方案2】:

我不确定你的意思

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

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

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

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

【讨论】:

    【解决方案3】:

    我遇到了同样的问题。页面上有多个经过验证的控件。我找到/制作了这个解决方案来更新(并清除所有验证)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;
            }
        }
    }
    

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

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

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

        【讨论】:

          猜你喜欢
          • 2011-07-03
          • 2019-03-01
          • 2012-11-23
          • 2015-10-04
          • 2013-02-18
          • 1970-01-01
          • 1970-01-01
          • 2011-06-29
          • 1970-01-01
          相关资源
          最近更新 更多