1

我一直在开发一个继承自 Decorator 的 ErrorProvider 控件。它验证控件中绑定到某物的任何元素。它遍历每个绑定并在 FrameworkElement 内循环,并将 ExceptionValidationRule 和 DataErrorValidation 添加到绑定的 ValidationRules。

这是完成工作的方法:

Private Sub ApplyValidationRulesToBindings()
    Dim bindings As Dictionary(Of FrameworkElement, List(Of Binding)) = GetBindings()

    For Each felement In bindings.Keys
        Dim knownBindings As List(Of Binding) = bindings(felement)

        For Each knownBinding As Binding In knownBindings
            'Applying Exception and Data Error validation rules'
            knownBinding.ValidationRules.Add(New ExceptionValidationRule())
            knownBinding.ValidationRules.Add(New DataErrorValidationRule())
        Next

    Next
End Sub

显然 DataErrorValidationRule 应用于绑定,但 ExceptionValidationRule 不是。

有谁知道为什么会这样?

编辑: 好的,关于这个问题的更多信息。

我一直在阅读大量有关 Validation 和 Binding 类的 MSDN 文档。如果ExceptionValidationRule已与绑定相关联,则Binding.UpdateSourceExceptionFilter属性允许您指定处理绑定上发生的任何异常的函数。

我为 UpdateSourceExceptionFilter 属性添加了一个方法,你猜怎么着!它被执行了。但!!即使我返回了异常,ValidationError 对象也没有添加到绑定元素的 Validation.Errors 集合中,即使 MSDN 文档说它会......

我注释掉了动态添加 ExceptionValidationRule 的代码,并手动将一个添加到 XAML 中的绑定,如下所示:

<TextBox HorizontalAlignment="Left" Name="TextBox1" VerticalAlignment="Top" 
                                   Width="200">
  <TextBox.Text>
    <Binding Path="Name">
      <Binding.ValidationRules>
        <ExceptionValidationRule />
      </Binding.ValidationRules>
    </Binding>
  </TextBox.Text> 
</TextBox>

执行了 UpdateSourceException 方法(我没有更改它),并将错误添加到 MSDN 所述的 Validation.Errors 中。

对这整件事感到好奇的是,当通过 VB.NET 代码完成时,ExceptionValidationRule 实际上已添加到绑定中(否则 UpdateSourceException 将永远不会执行);但是,Validate.Errors 不会随错误更新。

正如我之前所说,DataErrorValidationRule 已添加到绑定中并且可以正常工作......我只是遇到了 ExceptionValidationRule 的问题。

我的解决方案:

事实证明,我必须调用绑定的BindingOperations.SetBinding 方法和将验证规则应用于绑定的属性。我无法在ApplyValidationRulesToBindings方法中检索元素/绑定的 DependencyProperty,因此我将应用规则的代码移动到回调方法,提供了递归检索所有绑定的方法。

这是我的解决方案:

''' <summary>'
''' Gets and returns a list of all bindings. '
''' </summary>'
''' <returns>A list of all known bindings.</returns>'
Private Function GetBindings() As Dictionary(Of FrameworkElement, List(Of Binding))
    If _bindings Is Nothing OrElse _bindings.Count = 0 Then
        _bindings = New Dictionary(Of FrameworkElement, List(Of Binding))
        FindBindingsRecursively(Me.Parent, AddressOf RetrieveBindings)
    End If
    Return _bindings
End Function


''' <summary>'
''' Recursively goes through the control tree, looking for bindings on the current data context.'
''' </summary>'
''' <param name="element">The root element to start searching at.</param>'
''' <param name="callbackDelegate">A delegate called when a binding if found.</param>'
Private Sub FindBindingsRecursively(ByVal element As DependencyObject, ByVal callbackDelegate As FoundBindingCallbackDelegate)

    ' See if we should display the errors on this element'
    Dim members As MemberInfo() = element.[GetType]().GetMembers(BindingFlags.[Static] Or BindingFlags.[Public] Or BindingFlags.FlattenHierarchy)

    For Each member As MemberInfo In members
        Dim dp As DependencyProperty = Nothing
        ' Check to see if the field or property we were given is a dependency property'
        If member.MemberType = MemberTypes.Field Then
            Dim field As FieldInfo = DirectCast(member, FieldInfo)
            If GetType(DependencyProperty).IsAssignableFrom(field.FieldType) Then
                dp = DirectCast(field.GetValue(element), DependencyProperty)
            End If
        ElseIf member.MemberType = MemberTypes.[Property] Then
            Dim prop As PropertyInfo = DirectCast(member, PropertyInfo)
            If GetType(DependencyProperty).IsAssignableFrom(prop.PropertyType) Then
                dp = DirectCast(prop.GetValue(element, Nothing), DependencyProperty)
            End If

        End If
        If dp IsNot Nothing Then
            ' we have a dependency property. '
            'Checking if it has a binding and if so, checking if it is bound to the property we are interested in'
            Dim bb As Binding = BindingOperations.GetBinding(element, dp)
            If bb IsNot Nothing Then
                ' This element has a DependencyProperty that we know of that is bound to the property we are interested in. '
                ' Passing the information to the call back method so that the caller can handle it.'
                If TypeOf element Is FrameworkElement Then
                    If Me.DataContext IsNot Nothing AndAlso DirectCast(element, FrameworkElement).DataContext IsNot Nothing Then
                        callbackDelegate(DirectCast(element, FrameworkElement), bb, dp)
                    End If
                End If
            End If
        End If
    Next

    'Recursing through any child elements'
    If TypeOf element Is FrameworkElement OrElse TypeOf element Is FrameworkContentElement Then
        For Each childElement As Object In LogicalTreeHelper.GetChildren(element)
            If TypeOf childElement Is DependencyObject Then
                FindBindingsRecursively(DirectCast(childElement, DependencyObject), callbackDelegate)
            End If
        Next
    End If
End Sub

''' <summary>'
''' Called when recursively populating the Bindings dictionary with FrameworkElements(key) and their corresponding list of Bindings(value)'
''' </summary>'
''' <param name="element">The element the binding belongs to</param>'
''' <param name="binding">The Binding that belongs to the element</param>'
''' <param name="dp">The DependencyProperty that the binding is bound to</param>'
''' <remarks></remarks>'
Sub RetrieveBindings(ByVal element As FrameworkElement, ByVal binding As Binding, ByVal dp As DependencyProperty)
    'Applying an exception validation and data error validation rules to the binding' 
    'to ensure that validation occurs for the element'
    If binding.ValidationRules.ToList.Find(Function(x) GetType(ExceptionValidationRule) Is (x.GetType)) Is Nothing Then
        binding.ValidationRules.Add(New ExceptionValidationRule())
        binding.ValidationRules.Add(New DataErrorValidationRule())
        binding.UpdateSourceExceptionFilter = New UpdateSourceExceptionFilterCallback(AddressOf ReturnExceptionHandler)
        'Resetting the binding to include the validation rules just added'
        BindingOperations.SetBinding(element, dp, binding)
    End If

    ' Remember this bound element. This is used to display error messages for each property.'
    If _bindings.ContainsKey(element) Then
        DirectCast(_bindings(element), List(Of Binding)).Add(binding)
    Else
        _bindings.Add(element, New List(Of Binding)({binding}))
    End If
End Sub

谢谢!

-弗林尼

4

1 回答 1

2

我并没有真正关注您的代码是如何工作的,但是您无法在使用 Binding 后对其进行修改,因此您无法将ValidationRules 添加到现有的 Binding 中。我认为您必须复制 Binding,Property for Property,然后添加ValidationRules,然后将新复制的 Binding 设置为BindingOperations.SetBinding(...).

另一种方法可能是创建您自己的子类绑定,您可以在其中ValidationRule直接添加 s,例如

public class ExBinding : Binding
{
    public ExBinding()
    {
        NotifyOnValidationError = true;
        ValidationRules.Add(new ExceptionValidationRule());
        ValidationRules.Add(new DataErrorValidationRule());
    }
}

可用喜欢

<TextBox Text="{local:ExBinding Path=MyProperty}"/>

更新

我想你没听懂我的回答。绑定一旦使用就无法修改,因此您尝试执行的操作将不起作用。这是一个 C# 示例应用程序,它显示了这一点。它包含三个TextBoxs 其中

  • 首先TextBox在 Xaml 中添加与 ExceptionValidationRule 的绑定
  • 其次TextBox在 Xaml 中添加 Binding 并在其 Loaded 事件中添加 ExceptionValidationRule
  • 第三个TextBox在 Loaded 事件中添加与 ExceptionValidationRule 的 Binding

ExceptionValidationRule 适用于TextBox1 和 3,但不适用于 2。在此处上传示例:http ://www.mediafire.com/?venm09dy66q4rmq

更新 2
如果在添加验证规则后再次设置绑定,您可以使其工作,例如

BindingExpression bindingExpression = textBox.GetBindingExpression(TextBox.TextProperty);
Binding textBinding = bindingExpression.ParentBinding;
textBinding.ValidationRules.Add(new ExceptionValidationRule());
// Set the Binding again after the `ExceptionValidationRule` has been added
BindingOperations.SetBinding(textBox, TextBox.TextProperty, textBinding);

我不确定您的GetBindings方法看起来如何,但也许您可以添加一个方法,在其中再次设置 Bindings 并在添加sSetBindings后调用该方法ExceptionValidationRule

于 2011-02-18T20:49:44.747 回答