Я разрабатывал элемент управления ErrorProvider, который наследуется от Decorator. Он проверяет любые элементы в элементе управления, которые связаны с чем-то. Он проходит по каждой привязке и внутри 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 - нет.
Кто-нибудь знает, почему это так?
Edit:
Итак, немного больше информации о проблеме.
Я читал тонны документации MSDN по проверке и классу привязки. Свойство Binding.UpdateSourceExceptionFilter позволяет указать функцию, которая обрабатывает любые исключения, возникающие в привязке, если с привязкой был связан ExceptionValidationRule .
Я добавил метод для свойства UpdateSourceExceptionFilter и угадайте, что! Это было выполнено. НО!! Несмотря на то, что я вернул исключение, объект ValidationError НЕ был добавлен в коллекцию Validation.Errors связанного элемента, хотя в документации MSDN сказано, что это будет ...
Я закомментировал код, который динамически добавляет ExceptionValidationRule, и вручную добавил его в Binding в 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 (я не менял его), и ошибка была добавлена в Validation.Errors, как указано в MSDN.
Что любопытно во всем этом, так это тот факт, что ExceptionValidationRule фактически добавляется в привязку, когда выполняется через код VB.NET (иначе UpdateSourceException никогда бы не запустился); однако Validate.Errors не обновляется с ошибкой.
Как я уже говорил ранее, DataErrorValidationRule добавляется в привязку и работает правильно ... У меня просто проблемы с ExceptionValidationRule.
Мое решение:
Оказывается, мне пришлось вызывать BindingOperations.SetBinding Method для привязки и свойство применять правила проверки к привязке. У меня не было способа извлечь свойство DependencyProperty для элемента / привязки в моем методе ApplyValidationRulesToBindings , поэтому я переместил код, который применил правила к методу обратного вызова, при условии, что мой метод рекурсивно извлекает все привязки.
Вот мое решение:
''' <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