2009-04-11 19 views
5

Estoy creando un control canvas. Este lienzo raíz tiene varios elementos superpuestos (lienzos también). Esto se hace para que cada niño pueda manejar su propio dibujo y luego puedo componer el resultado final con cualquier combinación de niños para obtener el comportamiento deseado.controles superpuestos C# wpf que no reciben eventos de mouse

Esto está funcionando muy bien en lo que respecta a la representación. Sin embargo, esto no funciona tan bien con los eventos del mouse. Los trabajos acontecimientos manera ratón son los siguientes (utilizando PreviewMouseMove como ejemplo):

1- Si la lona de la raíz está en ratón, caso de incendio 2- Comprobar todos los niños, si uno está bajo el ratón, caso de incendio y detener

Como tal, solo el primer hijo que agregue recibirá el evento de mover el mouse. El evento no se propaga a todos los niños porque se superponen.

Para superar esto, me trataron los siguientes: 1- eventos de ratón Override en el lienzo de la raíz 2- Para cada evento, encontrar todos los niños que quieren controlar el evento usando VisualTreeHelper.HitTest 3- Para todos los niños que devolvió un resultado válido de la prueba de aciertos (es decir: bajo el mouse y dispuesto a manejar el evento (IsHitTestVisible == true)), ???

Aquí es donde estoy atascado, de alguna manera tengo que enviar el evento del mouse a todos los niños, y detener el flujo normal del evento para asegurarme de que el primer hijo no lo reciba dos veces (mediante handle = true en evento).

Al usar RaiseEvent con el mismo evento pasado a los niños, las cosas parecen funcionar, pero de alguna manera también se plantea el evento en el elemento principal (lienzo raíz). Para eludir esto, necesitaba crear una copia del evento y configurar la fuerza para establecer el origen, aunque parece ser más un hack que una solución. ¿Hay una forma adecuada de hacer lo que estoy tratando de hacer? El ejemplo del código sigue.

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

Tenga en cuenta que también necesitaré esto para trabajar con información sobre herramientas dentro de hijos de hijos del lienzo raíz. –

+1

¿Esto es para Silverlight? WPF? Agregue las etiquetas relevantes, obtendrá una respuesta más rápido. – SirDemon

Respuesta

0

me encontré con el ejemplo de código interesante así que decidimos probarlo ... pero tenía que hacer una pequeña modificación para que funcione correctamente en mis cosas.

que tenía que cambiar el segundo "si" en el método HitTestFilter de la siguiente manera:

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

Como se puede ver Quité el inútil "! Elemento = esta" al final (ya probado que la condición en el 1er "si") y agregué "element == null" al comienzo.

¿Por qué? Porque en algún momento durante el filtrado, el tipo de parámetro fue System.Windows.Media.ContainerVisual, que no hereda de UIElement y, por lo tanto, el elemento se establecerá como nulo y se devolverá ContinueSkipSelfAndChildren. Pero no quiero omitir a los niños porque mi lienzo está dentro de su colección "Niños" y los elementos de UIE con los que quiero dar un golpe están contenidos en el lienzo.

3

Creo que debería usar los eventos de vista previa porque esos son RoutingStrategy.Tunnel desde la ventana al control más alto en Z-Order, y los eventos normales son RoutingStrategy.Bubble.

En este RoutedEvents hay una propiedad Handle cuando es cierto, el sistema dejará de atravesar el árbol visual porque alguien usó este evento.

0

Al igual que @GuerreroTook dijo, debe resolver esto mediante el uso de RoutedEvents de WPF (más información here.

Cuestiones relacionadas