2009-04-11 16 views
5

Sto costruendo un controllo canvas. Questa tela radice ha diversi bambini sovrapposti (anche tela). Questo è fatto in modo che ogni bambino possa gestire il proprio disegno e io posso quindi comporre il risultato finale con qualsiasi combinazione di bambini per ottenere il comportamento desiderato.C# wpf controlli sovrapposti che non ricevono gli eventi del mouse

Questo funziona molto bene per quanto riguarda il rendering. Tuttavia, questo non funziona molto bene con gli eventi del mouse. Le opere Eventi modo del mouse sono i seguenti (utilizzando PreviewMouseMove come esempio):

1- Se tela radice è sotto il mouse, evento fuoco 2- Controllare tutti i bambini, se uno è sotto il mouse, evento fuoco e fermare

Come tale, solo il primo figlio che aggiungo riceverà l'evento di spostamento del mouse. L'evento non viene propagato a tutti i bambini perché si sovrappongono.

Per ovviare a questo, ho cercato i seguenti: 1- Ignora gli eventi del mouse nella tela radice 2- Per ogni evento, trovare tutti i bambini che vogliono gestire l'evento utilizzando VisualTreeHelper.HitTest 3- Per tutti i bambini che ha restituito un risultato di hit test valido (es .: sotto il mouse e disposto a gestire l'evento (IsHitTestVisible == true)), ???

Questo è il punto in cui sono bloccato, in qualche modo devo inviare l'evento del mouse a tutti i bambini e interrompere il normale flusso dell'evento per accertarmi che il primo figlio non lo riceva due volte (tramite handled = true nel evento).

Utilizzando RaiseEvent con lo stesso evento trasmesso ai bambini, le cose sembrano funzionare ma in qualche modo solleva l'evento sul genitore (tela principale). Per bypassare questo ho avuto bisogno di creare una copia dell'evento e impostare la forza per impostare la fonte anche se sembra essere più un hack che una soluzione. Esiste un modo corretto per fare ciò che sto cercando di fare? Segue l'esempio di codice.

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

Si noti che avrò anche bisogno di questo per lavorare con le descrizioni dei comandi all'interno dei figli dei figli della tela radice. –

+1

E 'questo per Silverlight? WPF? Aggiungi i tag pertinenti, otterrai una risposta più veloce. – SirDemon

risposta

0

ho trovato il tuo esempio di codice interessante così ho dato una prova ... ma ho dovuto fare una piccola modifica per farlo funzionare correttamente nella mia roba.

ho dovuto cambiare il 2 ° "se" nel metodo HitTestFilter come segue:

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

Come potete vedere ho tolto l'inutile "! Elemento = presente" alla fine (che già testati tale condizione nel 1 ° "se") e ho aggiunto "elemento == null" all'inizio.

Perché? Perché ad un certo punto durante il filtraggio il tipo di parametro era System.Windows.Media.ContainerVisual che non eredita da UIElement e quindi l'elemento sarebbe impostato su null e ContinueSkipSelfAndChildren verrebbe restituito. Ma non voglio saltare i bambini perché la mia tela è contenuta nella sua collezione "Bambini" e gli UIlementi che voglio testare sono contenuti in Canvas.

3

Penso che dovresti usare gli eventi di anteprima perché quelli sono RoutingStrategy.Tunnel da Window al controllo più alto in Z-Order, e gli eventi normali sono RoutingStrategy.Bubble.

In questo oggetto RoutedEvents esiste una proprietà Handle quando è vero che il sistema si fermerà per attraversare l'albero visivo perché qualcuno ha utilizzato questo evento.

0

Proprio come @GuerreroTook ha detto, è necessario risolvere questo problema utilizzando RoutedEvents di WPF (ulteriori informazioni here.

Problemi correlati