2009-11-30 19 views
6

Necesito ser capaz de manejar el doble clic en y un solo clic evento en el WPF StackPanel. Pero no existe el evento DoubleClick de StackPanel. Quiero hacer 2 operaciones diferentes en estos 2 manejadores de eventos.WPF StackPanel con Click Y DoubleClick

¿Alguna idea de cómo hacer eso?

Gracias

+7

Todavía no puedo entender cómo la gente podía Microsoft omite tal funcionalidad básica. –

Respuesta

5

la mejor manera es para enderezar su propio controlador de botón del ratón con un tiempo de espera - si el evento se dispara de nuevo dentro del período de tiempo de espera, a continuación, disparar su mensaje de DoubleClick, de lo contrario llamar al único controlador de clic. He aquí algunos ejemplos de código (Editar: encontrado originalmente here):

/// <summary> 
/// For double clicks 
/// </summary> 
public class MouseClickManager { 
    private event MouseButtonEventHandler _click; 
    private event MouseButtonEventHandler _doubleClick; 

    public event MouseButtonEventHandler Click { 
     add { _click += value; } 
     remove { _click -= value; } 
    } 

    public event MouseButtonEventHandler DoubleClick { 
     add { _doubleClick += value; } 
     remove { _doubleClick -= value; } 
    } 

    /// <summary> 
    /// Gets or sets a value indicating whether this <see cref="MouseClickManager"/> is clicked. 
    /// </summary> 
    /// <value><c>true</c> if clicked; otherwise, <c>false</c>.</value> 
    private bool Clicked { get; set; } 

    /// <summary> 
    /// Gets or sets the timeout. 
    /// </summary> 
    /// <value>The timeout.</value> 
    public int DoubleClickTimeout { get; set; } 

    /// <summary> 
    /// Initializes a new instance of the <see cref="MouseClickManager"/> class. 
    /// </summary> 
    /// <param name="control">The control.</param> 
    public MouseClickManager(int doubleClickTimeout) { 
     this.Clicked = false; 
     this.DoubleClickTimeout = doubleClickTimeout; 
    } 

    /// <summary> 
    /// Handles the click. 
    /// </summary> 
    /// <param name="sender">The sender.</param> 
    /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param> 
    public void HandleClick(object sender, MouseButtonEventArgs e) { 
     lock (this) { 
      if (this.Clicked) { 
       this.Clicked = false; 
       OnDoubleClick(sender, e); 
      } 
      else { 
       this.Clicked = true; 
       ParameterizedThreadStart threadStart = new ParameterizedThreadStart(ResetThread); 
       Thread thread = new Thread(threadStart); 
       thread.Start(e); 
      } 
     } 
    } 

    /// <summary> 
    /// Resets the thread. 
    /// </summary> 
    /// <param name="state">The state.</param> 
    private void ResetThread(object state) { 
     Thread.Sleep(this.DoubleClickTimeout); 

     lock (this) { 
      if (this.Clicked) { 
       this.Clicked = false; 
       OnClick(this, (MouseButtonEventArgs)state); 
      } 
     } 
    } 

    /// <summary> 
    /// Called when [click]. 
    /// </summary> 
    /// <param name="sender">The sender.</param> 
    /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param> 
    private void OnClick(object sender, MouseButtonEventArgs e) { 
     if (_click != null) { 
      if (sender is Control) { 
       (sender as Control).Dispatcher.BeginInvoke(_click, sender, e); 
      } 
     } 
    } 

    /// <summary> 
    /// Called when [double click]. 
    /// </summary> 
    /// <param name="sender">The sender.</param> 
    /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param> 
    private void OnDoubleClick(object sender, MouseButtonEventArgs e) { 
     if (_doubleClick != null) { 
      _doubleClick(sender, e); 
     } 
    } 
} 

Luego, en el control que desea recibir los eventos:

MouseClickManager fMouseManager = new MouseClickManager(200); 
fMouseManager.Click += new MouseButtonEventHandler(YourControl_Click); 
fMouseManager.DoubleClick += new MouseButtonEventHandler(YourControl_DoubleClick); 
+0

+1 - mil millones de veces más completa que mi respuesta – MoominTroll

+0

@MoominTroll jaja, sucede que acabo de implementar esto en nuestra aplicación: D –

+2

Debo haber leído mal ... Estás comenzando un nuevo hilo cada vez que el se hace clic en el mouse y no es un doble clic? Wow ... Eso es ... hum ... especial. Considere la siguiente solución usando "e.ClickCount> = 2". – joce

23
<StackPanel MouseDown="StackPanel_MouseDown"> 
    <!--stackpanel content--> 
    <TextBlock>Hello</TextBlock> 
</StackPanel> 

Luego, en el controlador de eventos:

private void StackPanel_MouseDown(object sender, MouseButtonEventArgs e) 
    { 
     if (e.ClickCount >= 2) 
     { 
      string hello; //only hit here on double click 
     } 
    } 

Debería funcionar. Tenga en cuenta que un solo clic en el StackPanel golpearía el evento (pero fallaría si se marca)

+0

No es tan eficaz como manejar un evento de doble clic, pero lo suficientemente rápido cuando no está allí. +1 – Randolpho

+0

Sí, lo he intentado ... pero necesito manejar tanto Single como DoubleClick ... clickcount realmente no ayuda, porque parece que doubleclick == singleclick también. –

+1

@ PaN1C_Showt1Me ¿Qué tal si haces 'if (e.ClickCount> = 2) {HandleDoubelClick(); } else if (e.ClickCount> = 1) {HandleSingleClick();} ' – joce

4

Otra opción es agregar un MouseBinding a los InputBindings en el StackElement y luego agregar un CommandBinding que se active mediante MouseBinding. En general, esta es una mejor práctica que los mecanismos basados ​​en eventos porque evita los problemas de pérdida de memoria causados ​​por referencias fuertes. También proporciona la separación de la lógica de comando de la representación.

Dicho esto, no es tan sencillo y adjuntar al evento lo convierte en un gran atajo.

Y no hace falta decir, que su fondo StackPanel al menos transparente o no será capturado por la prueba de posicionamiento clic del ratón cuando se hace clic en el "fondo". Los fondos nulos son omitidos por la detección de aciertos.

0

que tenía un problema similar (responder a evento de clic único y hacer un trabajo adicional en caso de evento de doble clic). He resuelto el problema de esta manera:

1) definir y declarar algún entero para sostener último clic marca de tiempo

int lastClickTimestamp; 

2) en el método Window_Loaded initialize declarado previamente variable, con un número mayor de 200

lastClickTimestamp = 1000; 

3) añadir ratón controlador de botón a su panel de pila

stackPanel.MouseLeftButtonUp += new MouseButtonEventHandler(stackPanel_MouseLeftButtonUp); 

4) añadir siguiente método

void stackPanel_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) 
    { 
     if (e.Timestamp - lastClickTimeStamp < 200) 
     { 
      //double click 
     } 
     lastClickTimeStamp = e.Timestamp; 

     //single click 
    } 

Este código es inútil si necesita detectar separado solo clic y evento de doble clic. Esa situación introduce un poco más de complejidad, pero definitivamente se puede resolver.

0

Respuesta completa usando WPF DispatcherTimer. Creamos el temporizador bajo demanda y lo desconectamos cuando terminamos para evitar la obstrucción de los recursos.

El C#:

using System; 
using System.Diagnostics; 
using System.Runtime.InteropServices; 
using System.Windows; 
using System.Windows.Input; 
using System.Windows.Threading; 

public partial class MainWindow : Window 
{ 
    DispatcherTimer dt; 

    bool clear_timer() 
    { 
     if (dt == null) 
      return false; 
     dt.Tick -= _single_click; 
     dt = null; 
     return true; 
    } 

    private void _click(Object sender, MouseButtonEventArgs e) 
    { 
     if (clear_timer()) 
      Debug.Print("double click"); 
     else 
      dt = new DispatcherTimer(
         TimeSpan.FromMilliseconds(GetDoubleClickTime()), 
         DispatcherPriority.Normal, 
         _single_click, 
         Dispatcher); 
    } 

    void _single_click(Object sender, EventArgs e) 
    { 
     clear_timer(); 
     Debug.Print("single click"); 
    } 

    public MainWindow() { InitializeComponent(); } 

    [DllImport("user32.dll")] 
    static extern uint GetDoubleClickTime(); 
}; 

El XAML:

<Window x:Class="MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> 
    <StackPanel Orientation="Horizontal" 
       Background="AliceBlue" 
       Width="100" 
       Height="100" 
       MouseLeftButtonDown="_click" /> 
</Window> 
4

... años más tarde. La solución de @MoominTroll es perfectamente aceptable. Otra opción es envolver el panel de pila en un control de contenido que admita el evento de doble clic.

<ContentControl MouseDoubleClick="DoubleClickHandler" > 
    <StackPanel> 

    </StackPanel> 
</ContentControl> 
0

Existe una solución más fácil para hacer esto.

En caso de PreviewMouseLeftDown StackPanel (por ejemplo), se puede comprobar si la propiedad MouseButtonEventArgs.ClickCount tiene un valor de 2. 1 = Un solo clic 2 = doble clic