2009-04-11 6 views
5

Je suis en train de créer un contrôle de canevas. Cette toile racine a plusieurs enfants qui se chevauchent (toile aussi). Ceci est fait pour que chaque enfant puisse gérer son propre dessin et je peux ensuite composer le résultat final avec n'importe quelle combinaison d'enfants pour obtenir le comportement désiré.C# wpf les contrôles qui se chevauchent ne reçoivent pas d'événements de souris

Cela fonctionne très bien en ce qui concerne le rendu. Cela ne fonctionne pas si bien avec les événements de souris cependant. Les événements de la souris façon dont les travaux sont les suivantes (en utilisant PreviewMouseMove comme exemple):

1- Si la toile racine est sous la souris, un événement d'incendie 2- Vérifiez tous les enfants, si l'on est sous la souris, un événement d'incendie et arrêter

En tant que tel, seul le premier enfant que j'ajouterai recevra l'événement de déplacement de la souris. L'événement n'est pas propagé à tous les enfants car ils se chevauchent.

Pour surmonter ce problème, j'ai essayé les suivantes: 1- Remplacer les événements de la souris dans la toile racine 2- Pour chaque événement, trouver tous les enfants qui veulent gérer l'événement en utilisant VisualTreeHelper.HitTest 3- Pour tous les enfants qui retourné un résultat de test de succès valide (ie: sous la souris et prêt à gérer l'événement (IsHitTestVisible == true)), ???

C'est où je suis coincé, j'ai besoin d'envoyer l'événement de la souris à tous les enfants, et arrêter le flux normal de l'événement pour s'assurer que le premier enfant ne le reçoit pas deux fois (via handle = true dans le un événement). En utilisant RaiseEvent avec le même événement passé sur les enfants, les choses semblent fonctionner mais d'une manière ou d'une autre elles soulèvent aussi l'événement sur le parent (canvas racine). Pour contourner cela, j'ai besoin de créer une copie de l'événement et de définir la force de la source, même si elle semble être plus un hack qu'une solution. Y a-t-il une bonne façon de faire ce que j'essaie de faire? L'exemple de code suit.

public class CustomCanvas : Canvas 
    { 
     private List<object> m_HitTestResults = new List<object>(); 

     public new event MouseEventHandler MouseMove; 

     public CustomCanvas() 
     { 
      base.PreviewMouseMove += new MouseEventHandler(CustomCanvas_MouseMove); 
     } 

     private void CustomCanvas_MouseMove(object sender, MouseEventArgs e) 
     { 
// Hack here, why is the event raised on the parent as well??? 
      if (e.OriginalSource == this) 
      { 
       return; 
      } 

       Point pt = e.GetPosition((UIElement)sender); 
       m_HitTestResults.Clear(); 

       VisualTreeHelper.HitTest(this, 
        new HitTestFilterCallback(OnHitTest), 
        new HitTestResultCallback(OnHitTest), 
        new PointHitTestParameters(pt)); 

       MouseEventArgs tmpe = new MouseEventArgs(e.MouseDevice, e.Timestamp, e.StylusDevice); 
       tmpe.RoutedEvent = e.RoutedEvent; 
       tmpe.Source = this; 

       foreach (object hit in m_HitTestResults) 
       { 
        UIElement element = hit as UIElement; 
        if (element != null) 
        { 
// This somehow raises the event on us as well as the element here, why??? 
         element.RaiseEvent(tmpe); 
        } 
       } 


      var handlers = MouseMove; 
      if (handlers != null) 
      { 
       handlers(sender, e); 
      } 

      e.Handled = true; 
     } 

     private HitTestFilterBehavior OnHitTest(DependencyObject o) 
     { 
      UIElement element = o as UIElement; 
      if (element == this) 
      { 
       return HitTestFilterBehavior.ContinueSkipSelf; 
      } 
      else if (element != null && element.IsHitTestVisible && element != this) 
      { 
       return HitTestFilterBehavior.Continue; 
      } 
      return HitTestFilterBehavior.ContinueSkipSelfAndChildren; 
     } 

     private HitTestResultBehavior OnHitTest(HitTestResult result) 
     { 
      // Add the hit test result to the list that will be processed after the enumeration. 
      m_HitTestResults.Add(result.VisualHit); 
      // Set the behavior to return visuals at all z-order levels. 
      return HitTestResultBehavior.Continue; 
     } 
+0

Notez que j'aurai également besoin de ceci pour travailler avec des info-bulles à l'intérieur des enfants des enfants de la toile de fond. –

+1

Est-ce pour silverlight? WPF? Ajoutez les tags correspondants, vous obtiendrez une réponse plus rapidement. – SirDemon

Répondre

0

J'ai trouvé votre exemple de code intéressant donc je lui ai donné un essai ... mais je devais faire une petite modification pour que cela fonctionne correctement dans mes affaires.

je devais changer la 2ème « si » dans la méthode HitTestFilter comme suit:

if (element == null || element.IsHitTestVisible) 

Comme vous pouvez le voir, j'enlevé l'inutile « ! Élément = ce » à la fin (vous avez déjà testé cette condition dans le 1er "si") et j'ai ajouté "element == null" au début.

Pourquoi? Parce qu'à un certain moment pendant le filtrage le type de paramètre était System.Windows.Media.ContainerVisual qui n'hérite pas de UIElement et donc l'élément serait défini sur null et ContinueSkipSelfAndChildren serait retourné. Mais je ne veux pas ignorer les enfants parce que mon Canvas est contenu dans sa collection "Enfants" et que les UIElements avec lesquels je veux m'intéresser sont contenus dans Canvas.

3

Je pense que vous devez utiliser les événements de prévisualisation car ceux-ci sont RoutingStrategy.Tunnel de la fenêtre au contrôle le plus élevé dans Z-Order, et les événements normaux sont RoutingStrategy.Bubble.

Dans ce RoutedEvents il y a une propriété Handle quand c'est vrai que le système s'arrêtera pour traverser l'arbre visuel parce que quelqu'un a utilisé cet événement.

0

Tout comme @GuerreroTook dit, vous devriez résoudre ce problème en utilisant RoutedEvents de WPF (plus d'informations here.

Questions connexes