2009-04-11 7 views
5

Ich baue eine Canvas-Steuerung. Dieser Root-Canvas hat mehrere überlappende Kinder (Canvas). Dies geschieht, damit jedes Kind mit seiner eigenen Zeichnung umgehen kann und ich dann das Endergebnis mit einer beliebigen Kombination von Kindern zusammenstellen kann, um das gewünschte Verhalten zu erhalten.C# wpf überlappende Steuerelemente, die keine Mausereignisse empfangen

Dies funktioniert sehr gut, was das Rendern betrifft. Bei Mausereignissen funktioniert das jedoch nicht so gut. Die Art und Weise Mausereignisse Werke sind wie folgt (mit Preview als Beispiel):

1- Wenn Wurzel Leinwand ist unter Maus, Feuerereignis 2- Überprüfen Sie alle Kinder, wenn man unter der Maus, Brandereignis ist und stoppen

Daher wird nur das erste Kind, das ich hinzufüge, das Mausbewegungsereignis erhalten. Das Ereignis wird nicht an alle untergeordneten Elemente weitergegeben, da sie sich überschneiden.

Um dies zu überwinden, habe ich versucht, die folgenden: 1- Aufschalten Mausereignisse in der Wurzel Leinwand 2- Für jedes Ereignis, alle Kinder finden, die das Ereignis mit VisualTreeHelper.HitTest 3- Für alle Kinder zu handhaben möchten, dass gab ein gültiges Hit-Testergebnis zurück (dh: unter Maus und bereit, das Ereignis zu behandeln (IsHitTestVisible == true)), ???

Hier bin ich fest, ich muss irgendwie das Mausereignis an alle Kinder senden und den normalen Ablauf des Ereignisses stoppen, um sicherzustellen, dass das erste Kind es nicht zweimal empfängt (via handled = true in der Veranstaltung).

Durch die Verwendung von RaiseEvent mit demselben Ereignis, das an die untergeordneten Elemente übergeben wurde, scheinen die Dinge zu funktionieren, aber irgendwie wird das Ereignis auch auf dem übergeordneten Element (Stammarbeitsfläche) ausgelöst. Um dies zu umgehen, musste ich eine Kopie des Ereignisses erstellen und die Quelle setzen, obwohl es eher ein Hack als eine Lösung zu sein scheint. Gibt es einen richtigen Weg, um das zu tun, was ich versuche? Codebeispiel folgt.

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

Beachten Sie, dass ich diese auch verwenden muss, um mit QuickInfos innerhalb von Kindern von Kindern des Stammarbeitsbereichs zu arbeiten. –

+1

Ist das für Silverlight? WPF? Fügen Sie die relevanten Tags hinzu, Sie erhalten schneller eine Antwort. – SirDemon

Antwort

0

fand ich Ihren Code Beispiel interessant, so habe ich es ausprobiert ... aber ich hatte eine kleine Änderung zu machen, es in meinen Sachen richtig zu arbeiten.

Ich hatte die zweiten „wenn“ in der HitTestFilter Methode wie folgt zu ändern:

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

Wie Sie uns das nutzlose entfernt sehen! „Element = diese“ am Ende (bereits Sie diese Bedingung getestet im 1. "wenn") und ich habe "element == null" am Anfang hinzugefügt.

Warum? Da zu irgendeinem Zeitpunkt während der Filterung der Parametertyp System.Windows.Media.ContainerVisual war, der nicht von UIElement erbt und so das Element auf null gesetzt und ContinueSkipSelfAndChildren zurückgegeben würde. Aber ich möchte die Kinder nicht überspringen, da mein Canvas in der "Children" -Sammlung enthalten ist und die UIElements, mit denen ich hittet, in Canvas enthalten sind.

3

Ich denke, Sie sollten die Preview-Ereignisse verwenden, da diese RoutingStrategy.Tunnel von Windows zum höchsten Steuerelement in Z-Reihenfolge sind, und normale Ereignisse sind RoutingStrategy.Bubble.

In diesem RoutedEvents gibt es eine Eigenschaft behandeln, wenn es wahr ist, wird das System aufhören, den visuellen Baum zu durchlaufen, weil jemand dieses Ereignis verwendet.

0

Genau wie @GuerreroTook gesagt hat, sollten Sie dies mit WPF RoutedEvents lösen (weitere Informationen here.

Verwandte Themen