2011-02-18 5 views
1

J'ai développé un contrôle ErrorProvider qui hérite de Decorator. Il valide tous les éléments du contrôle liés à quelque chose. Il parcourt chaque liaison et dans un FrameworkElement et ajoute une ExceptionValidationRule ainsi qu'une DataErrorValidation aux ValidationRules de la liaison.Ajout d'une règle ExceptionValidationRule à une liaison dans le code

Ceci est la méthode qui fait le travail:

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 

Apparemment, le DataErrorValidationRule est appliqué à la liaison, mais le ExceptionValidationRule n'est pas.

Est-ce que quelqu'un sait pourquoi cela pourrait être le cas?

Editer: Ok, donc un peu plus d'infos sur le problème.

J'ai lu des tonnes de documentation MSDN sur Validation et la classe de liaison. Le Binding.UpdateSourceExceptionFilter Property vous permet de spécifier une fonction qui gère les exceptions qui se produisent sur une liaison si un ExceptionValidationRule a été associé à la liaison.

J'ai ajouté une méthode pour la propriété UpdateSourceExceptionFilter et devine quoi! Il a été exécuté. MAIS!! Même si j'ai renvoyé l'exception, un objet ValidationError n'a pas été ajouté à la collection Validation.Errors de l'élément lié même si la documentation MSDN a dit qu'il serait ...

J'ai mis en commentaire le code qui ajoute le ExceptionValidationRule dynamiquement et ajoutée manuellement une à la liaison en XAML comme ceci:

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

la méthode UpdateSourceException a été exécuté (je ne changerai) et l'erreur a été ajouté aux Validation.Errors comme MSDN dit. Ce qui est curieux à propos de tout cela, c'est le fait que ExceptionValidationRule est en fait ajouté à la liaison quand il est fait à travers le code VB.NET (ou sinon le UpdateSourceException n'aurait jamais été exécuté); cependant, le Validate.Errors n'est pas mis à jour avec l'erreur.

Comme je l'ai dit plus tôt, la DataErrorValidationRule est ajoutée à la liaison et fonctionne correctement ... J'ai juste des problèmes avec ExceptionValidationRule.

Ma solution:

Il se trouve que je devais appeler le BindingOperations.SetBinding Method pour la liaison et la propriété d'appliquer les règles de validation à la liaison. Je n'avais aucun moyen de récupérer le DependencyProperty que pour l'élément/liaison dans ma méthode ApplyValidationRulesToBindings donc j'ai déplacé le code qui a appliqué des règles à la méthode de rappel fourni à ma méthode qui récupère toutes les liaisons récursivement.

Voici ma solution:

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

Merci!

-Frinny

Répondre

2

Je ne suis pas vraiment la suite comment fonctionne votre code, mais vous ne pouvez pas modifier une liaison après qu'il a été utilisé, de sorte que vous ne pouvez pas ajouter ValidationRule s à une liaison existante. Je pense que vous devrez copier la liaison, la propriété pour la propriété, puis ajouter les ValidationRule et ensuite définir la nouvelle liaison copiée avec BindingOperations.SetBinding(...).

Une autre approche peut être de créer votre propre sous-classé Binding où vous ajoutez le ValidationRule s directement par exemple

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

utiles comme l'

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

Mise à jour

Je ne pense pas vous avez compris ma réponse. Vous ne pouvez pas modifier une liaison une fois qu'elle est utilisée, ce que vous essayez de faire ne fonctionnera pas. Voici un exemple d'application C# qui montre cela. Il contient trois TextBox s où

  • D'abord TextBox ajoute la liaison avec le ExceptionValidationRule en XAML
  • Second TextBox ajoute la liaison en XAML et ajoute le ExceptionValidationRule dans son événement Loaded
  • Troisième TextBox ajoute la liaison avec le ExceptionValidationRule dans l'événement Loaded

La règle ExceptionValidationRule fonctionnera pour TextBox 1 et 3 mais pas pour 2. Téléchargé l'échantillon ici: http://www.mediafire.com/?venm09dy66q4rmq

Mise à jour 2
Vous pouvez obtenir ce travail si vous définissez de nouveau la fixation après avoir ajouté la règle de validation comme

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); 

Je ne suis pas Assurez-vous que votre méthode GetBindings ressemble, mais peut-être que vous pourriez ajouter une méthode SetBindings où vous définissez les liaisons à nouveau et appelez cette méthode après avoir ajouté le ExceptionValidationRule s

+0

J'ai déjà lu le fil qui a suggéré thi s. – Frinavale

+0

Je n'aimais pas l'idée de devoir créer ma propre classe de liaison à cette fin, car il faudrait que je modifie beaucoup de XAML pour l'utiliser. Le but de tout cela était d'éviter ce processus. En outre, la méthode ValidationRules.Add fonctionne sur une liaison existante (elle est même indiquée dans la documentation: http://msdn.microsoft.com/fr-fr/library/system.windows.data.binding.validationrules.aspx) jure que je perds l'esprit sur toute cette affaire! Je suis désolé si je rencontre mal, mais pour la vie de moi, je ne peux pas faire fonctionner ça! – Frinavale

+0

Je suis en train de mettre à jour mon post pour inclure plus d'informations sur ce que j'ai essayé de contourner ce problème. – Frinavale

Questions connexes