在代码中将ExceptionValidationRule添加到绑定

时间:2011-02-18 17:53:54

标签: wpf validation validationrules

我一直在开发一个继承自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文档。 Binding.UpdateSourceExceptionFilter Property允许您指定一个函数,用于处理绑定时发生的任何异常(如果ExceptionValidationRule已与Binding关联)。

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

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

<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 Method和属性将验证规则应用于绑定。我无法检索我的 ApplyValidationRulesToBindings 方法中的元素/绑定的DependencyProperty,所​​以我将应用规则的代码移动到了回调方法,前提是我的方法以递归方式检索所有绑定。 / p>

这是我的解决方案:

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

谢谢!

-Frinny

1 个答案:

答案 0 :(得分:2)

我并没有真正关注你的代码是如何工作的,但是在使用它之后你无法修改它,所以你不能将ValidationRule添加到现有的Binding中。我想你必须复制Binding,Property for Property然后添加ValidationRule,然后用BindingOperations.SetBinding(...)设置新复制的Binding。

另一种方法可能是创建自己的子类Binding,直接添加ValidationRule,例如

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

可用

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

<强>更新

我认为你不理解我的答案。您无法在使用后修改绑定,因此您尝试执行的操作将无法正常工作。这是一个C#示例应用程序,显示了这一点。它包含三个TextBox s

  • 首先TextBox在Xaml中添加带有ExceptionValidationRule的Binding
  • 第二个TextBox在Xaml中添加Binding并在其Loaded事件中添加ExceptionValidationRule
  • 第三个TextBox在Loaded事件中添加了Binding和ExceptionValidationRule

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

更新2
如果在添加验证规则(如

)后再次设置Binding,则可以使此工作正常
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方法看起来如何,但也许你可以添加SetBindings方法再次设置Bindings并在添加{{1}后调用该方法} s