2009-11-30 3 views
6

Je dois être capable de gérer le double-clic et un seul clic événement sur le StackPanel WPF. Mais il n'y a rien de tel que l'événement DoubleClick de StackPanel. Je veux faire 2 opérations différentes dans ces 2 EventHandlers.WPF StackPanel avec Click AND DoubleClick

Une idée de comment faire cela?

Merci

+7

Je ne peux toujours pas comprendre comment les gens pourraient Microsoft omettre une telle fonctionnalité de base. –

Répondre

5

La meilleure façon est à droite de votre propre gestionnaire de bouton de la souris avec un délai d'attente - si l'événement est déclenché à nouveau dans le délai d'attente, puis le feu votre message doubleclick, sinon appelez le seul gestionnaire de clic. Voici quelques exemples de code (Edit: trouvée à l'origine 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); 
     } 
    } 
} 

Ensuite, dans le contrôle que vous souhaitez recevoir les événements:

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

+1 - un milliard de fois plus complet que ma réponse – MoominTroll

+0

@MoominTroll haha, il se trouve que je viens de mettre en œuvre dans notre application: D –

+2

Je dois avoir mal lu ... Vous commencez un nouveau fil chaque fois que le la souris est cliquée et ce n'est pas un double-clic? Wow ... C'est ... hum ... spécial. Veuillez considérer la solution ci-dessous en utilisant "e.ClickCount> = 2". – joce

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

Ensuite, dans le gestionnaire d'événements:

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

Devrait fonctionner. Notez qu'un simple clic dans le StackPanel atteindrait l'événement (mais échouera le contrôle if)

+0

Pas aussi performant que la gestion d'un événement de double-clic, mais assez rapide quand il n'est pas là. +1 – Randolpho

+0

Oui, j'ai essayé cela .. mais j'ai besoin de gérer à la fois Single et DoubleClick .. clickcount n'aide pas vraiment, car il semble que doubleclick == singleclick aussi. –

+1

@ PaN1C_Showt1Me Que diriez-vous de faire 'if (e.ClickCount> = 2) {HandleDoubelClick(); } else if (e.ClickCount> = 1) {HandleSingleClick();} ' – joce

4

Une autre option consiste à ajouter une liaison MouseBinding aux InputBindings sur StackElement, puis à ajouter un CommandBinding activé par MouseBinding. Dans l'ensemble, il s'agit d'une meilleure pratique que les mécanismes basés sur les événements car cela évite les problèmes de fuite de mémoire causés par des références fortes. Il prévoit également la séparation de la logique de commande de la représentation. Cela étant dit, ce n'est pas aussi simple et attacher à l'événement fait un grand raccourci.

Et il va sans dire que l'arrière-plan de votre stackpanel sera au moins transparent ou qu'il ne sera pas détecté par le test de clic de souris lorsque vous cliquerez sur le "background". Les arrière-plans nuls sont ignorés par détection de coup.

0

J'ai eu un problème similaire (répondre à un événement de simple clic et effectuer un travail supplémentaire en cas d'événement de double clic). Je résolu le problème de cette façon:

1) définir et déclarer un entier de maintien de la dernière estampille temporelle de clic

int lastClickTimestamp; 

2) dans la méthode Window_Loaded initialiser la variable précédemment déclarée avec un certain nombre supérieur à 200

lastClickTimestamp = 1000; 

3) ajouter un gestionnaire de bouton de la souris sur votre panneau de pile

stackPanel.MouseLeftButtonUp += new MouseButtonEventHandler(stackPanel_MouseLeftButtonUp); 

4) ajouter la méthode suivante

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

     //single click 
    } 

Ce code est inutile si vous avez besoin pour détecter séparément seul clic et double événement click. Cette situation introduit un peu plus de complexité, mais peut définitivement être résolue.

0

Réponse complète en utilisant WPF DispatcherTimer. Nous créons le minuteur à la demande et le déconnecter lorsque cela est fait afin d'éviter le colmatage des ressources.

Le 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(); 
}; 

Le 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

... ans plus tard. @ La solution de MoominTroll est parfaitement acceptable. Une autre option consiste à envelopper le panneau de pile dans un contrôle de contenu qui prend en charge l'événement de double clic.

<ContentControl MouseDoubleClick="DoubleClickHandler" > 
    <StackPanel> 

    </StackPanel> 
</ContentControl> 
0

Il existe une solution plus simple pour cela.

En cas de StackPanel PreviewMouseLeftDown (par exemple), vous pouvez vérifier si la propriété MouseButtonEventArgs.ClickCount a une valeur de 2. 1 = Un seul clic 2 = Double Cliquez sur