2011-01-27 24 views
7

Ho una situazione in cui sto gestendo entrambi gli eventi singoli del mouse su & con doppio clic del mouse su un modulo. In entrambi i casi è necessario caricare qualcosa, tuttavia quando si verifica un doppio clic, non desidero eseguire il codice associato all'evento con un solo clic.Intercetta il clic del mouse singolo o doppio - Esegui solo il codice doppio clic sul doppio clic

C'è un modo per intercettare il clic del mouse e controllare se doppio o singolo e quindi eseguire l'evento giusto in modo appropriato?

Forse intercettando il WndProc della finestra o qualcosa del genere?

+0

È questo WinForms o WPF? – ChrisF

+0

questo è WinForms –

risposta

6

No, è praticamente impossibile a meno che non si disponga di una macchina del tempo. E in realtà non ha nemmeno senso una volta capito come Windows distingue il doppio clic da un clic.

Si scopre che Raymond Chen (uno sviluppatore del team Shell di Windows) spiega esattamente ciò in un post di blog intitolato "Logical consequences of the way Windows converts single-clicks into double-clicks".

In particolare, Windows conosce solo di interpretare qualcosa come un doppio clic perché un secondo clic si è verificato entro l'intervallo specificato dal GetDoubleClickTime function. Perché richiederebbe chiaroveggenza (come Raymond lo eleva così eloquentemente) per determinare in anticipo di di tempo se qualcosa dovesse essere un singolo o doppio clic, il gestore di finestre va avanti e invia un WM_LBUTTONDOWN message non appena viene ricevuto il primo clic. Lo WM_LBUTTONDBLCLK message viene inviato solo più tardi, dopo che il secondo clic è stato confermato per rappresentare effettivamente un doppio clic. Il risultato è che l'applicazione riceverà sempre dueWM_LBUTTONDOWN messaggi per ogni messaggio WM_LBUTTONDBLCLK ricevuto.

Ora i progettisti di .NET Framework hanno capito come funziona questo processo e progettato gli eventi che vengono generati di conseguenza. Ovviamente, non possono fare nulla in merito a un singolo clic che si verifica sempre prima di un doppio clic, ma sono stati in grado di sopprimere il secondo messaggio di clic se viene stabilito che l'utente intendeva far parte di un evento di doppio clic. Per quanto la documentazione per il Control.MouseClick event (che corrisponde grosso modo al messaggio WM_LBUTTONDOWN) ci dice:

Due singoli scatti che si verificano abbastanza vicino nel tempo, come determinato dalle impostazioni del mouse del sistema operativo dell'utente, genererà un MouseDoubleClick evento invece del secondo evento MouseClick.

articolo del blog di Raymond che ho linkato sopra non, invece, di proporre una possibile soluzione per le applicazioni e gli sviluppatori che insistono su un progetto in cui l'azione del doppio clic è estraneo all'azione solo clic (anche se nessuno di noi consiglia di farlo effettivamente):

Ora supponiamo che tu sia un programma che tuttavia vuole continuare con il disegno ambiguo di avere l'azione di doppio clic non correlata all'azione con un solo clic. cosa fai?

Bene, una cosa che si potrebbe fare è non fare nulla al ricevimento del messaggio WM_LBUTTONDOWN oltre a impostare un timer da attivare in GetDoubleClickTime() millisecondi. [Corretto 10am.] Se ricevi un messaggio WM_LBUTTONDBLCLK entro quel tempo, dopotutto era un doppio clic. Se non lo fai, allora deve essere stato un singolo clic, quindi puoi fare la tua azione con un solo clic (anche se un po 'tardi).

+0

Ho cancellato la mia risposta perché ho completamente perso il punto della domanda. – ChrisF

+0

Questo potrebbe aiutare: http://stackoverflow.com/questions/9553595/how-to-run-same-code-in-click-event-as-the-double-click-event – CrazyTim

4

Ecco il codice per fare ciò che hai richiesto.

Public Class Form1 
    Const WM_LBUTTONDOWN As Integer = &H201 
    Const WM_LBUTTONDBLCLK As Integer = &H203 

    Private WithEvents tmrDoubleClicks As Timer 

    Dim isDblClk As Boolean 
    Dim firstClickTime As Date 
    Dim doubleClickInterval As Integer 

    Sub New() 

     ' This call is required by the designer. 
     InitializeComponent() 
     tmrDoubleClicks = New Timer 

     ' Add any initialization after the InitializeComponent() call. 
     tmrDoubleClicks.Interval = 50 
     doubleClickInterval = CInt(Val(Microsoft.Win32.Registry.CurrentUser. 
             OpenSubKey("Control Panel\Mouse"). 
             GetValue("DoubleClickSpeed", 1000))) 
    End Sub 

    Protected Overrides Sub Dispose(ByVal disposing As Boolean) 
     Try 
      If disposing AndAlso components IsNot Nothing Then 
       components.Dispose() 
      End If 
      If disposing AndAlso tmrDoubleClicks IsNot Nothing Then 
       tmrDoubleClicks.Dispose() 
      End If 
     Finally 
      MyBase.Dispose(disposing) 
     End Try 
    End Sub 

    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message) 
     Select Case m.Msg 
      Case WM_LBUTTONDOWN 
       If Not isDblClk Then 
        firstClickTime = Now 
        tmrDoubleClicks.Start() 
       End If 

      Case WM_LBUTTONDBLCLK 
       isDblClk = True 
       tmrDoubleClicks.Stop() 
       DoubleClickActivity() 
       isDblClk = False 

      Case Else 
       MyBase.WndProc(m) 
     End Select 
    End Sub 

    Private Sub DoubleClickActivity() 
     'implement double click activity here 
     Dim r As New Random(Now.TimeOfDay.Seconds) 
     Me.BackColor = Color.FromArgb(r.Next(0, 255), 
             r.Next(0, 255), 
             r.Next(0, 255)) 
    End Sub 

    Private Sub SingleClickActivity() 
     'implement single click activity here 
     Beep() 
    End Sub 

    Private Sub tmrDoubleClicks_Tick(ByVal sender As Object, 
            ByVal e As System.EventArgs 
            ) Handles tmrDoubleClicks.Tick 

     If Now.Subtract(firstClickTime).TotalMilliseconds > 
      doubleClickInterval Then 

      'since there was no other click within the doubleclick speed, 
      'stop waiting and fire the single click activity 
      isDblClk = False 
      tmrDoubleClicks.Stop() 
      SingleClickActivity() 
     End If 
    End Sub 
End Class 

Il punto cruciale di questo codice è quello di ritardare sparare l'evento click fino al tempo doppio click trascorre. Se entro quel tempo, si verifica un altro evento click in quel momento, viene chiamato l'evento doppio clic senza chiamare l'evento click. Se, tuttavia, non c'è un doppio clic, viene chiamato l'evento click.

Questo ritardo è particolarmente evidente nei computer con una velocità di doppio clic più lunga. Su un computer tipico, la velocità del doppio clic è di 500 ms, quindi il codice eseguirà l'evento click tra 500 ms e 600 ms dopo che si è verificato un clic.

+1

Non ho potuto fare a meno di notare che non si chiami mai il metodo 'Dispose' per l'oggetto' Timer' che si crea nel costruttore. Questo è * quasi * degno di un downvote da parte mia. –

+0

@Cody: mi dispiace. Solitamente inserisco i metodi 'Dispose' nel file Form.Designer.vb (dove è presente il codice generato dal designer). Grazie per segnalarlo. –

+0

Non è un grosso problema, è solo che troppe persone dimenticano questo o non lo sanno affatto. Quando pubblichi una soluzione completa che qualcuno potrebbe copiare e incollare nel proprio IDE, è importante assicurarsi di indirizzare questo genere di cose. E, dopo un ulteriore pensiero, in realtà ha più senso aggiungere semplicemente il controllo del timer alla raccolta 'components' del modulo. In questo modo, verrà automaticamente eliminato dal codice generato dal designer senza che nessuno debba ricordare esplicitamente di aggiungerlo. 'System.Windows.Forms.Timer' eredita da' Component', dopo tutto. –