Перехватывать одиночный или двойной щелчок мышью - выполнять код двойного щелчка только при двойном щелчке - PullRequest
7 голосов
/ 27 января 2011

У меня есть ситуация, когда я обрабатываю как одиночные, так и двойные события щелчка мышью в форме.В обоих случаях что-то должно быть загружено, однако, когда происходит двойной щелчок, я не хочу выполнять код, присоединенный к событию одиночного щелчка.

Есть ли способ перехватить щелчки мыши и проверить, если двойнойили одиночный, а затем выполнить правильное событие соответствующим образом?

Возможно, перехватив WndProc окна или что-то еще?

Ответы [ 2 ]

6 голосов
/ 27 января 2011

Нет, это почти невозможно, если у вас нет машины времени. И это даже не имеет смысла, когда вы понимаете, как Windows отличает двойной щелчок от одного щелчка.

Оказывается, Рэймонд Чен (Raymond Chen) (разработчик из команды Windows Shell) точно объясняет это в записи блога под названием " Логические последствия того, как Windows преобразует одиночные щелчки в двойные щелчки ".

В частности, Windows знает, что нужно интерпретировать что-то как двойной щелчок, потому что второй щелчок произошел в интервале, заданном функцией GetDoubleClickTime .Поскольку для ясности (как красноречиво выразился Рэймонд) потребуется определенное время, чтобы определить впереди времени, если что-то будет одним или двойным щелчком, оконный менеджер отправляется вперед и отправляет WM_LBUTTONDOWNсообщение , как только будет получен первый щелчок.Сообщение WM_LBUTTONDBLCLK отправляется только позже, после подтверждения второго щелчка, который фактически представляет собой двойной щелчок.В результате ваше приложение всегда будет получать два WM_LBUTTONDOWN сообщения для каждого полученного WM_LBUTTONDBLCLK сообщения.

Теперь разработчики .NET Framework поняли, как работает этот процесс, и спроектировали его.события, которые поднимаются соответственно.Конечно, они ничего не могут сделать с одним щелчком, который всегда происходит перед двойным щелчком, но они смогли подавить сообщение о втором щелчке, если будет установлено, что пользователь намеревался принять участие в событии двойного щелчка.Как указывается в документации к событию Control.MouseClick (что примерно соответствует сообщению WM_LBUTTONDOWN):

Два отдельных щелчка, которые происходят достаточно близко во времени, как определенов соответствии с настройками мыши операционной системы пользователя будет генерироваться событие MouseDoubleClick вместо второго события MouseClick.

Статья в блоге Раймонда, на которую я ссылался выше, предлагает возможноеОбходной путь для приложений и разработчиков, которые настаивают на дизайне, в котором действие двойного щелчка не связано с действием одного щелчка (хотя никто из нас не рекомендует вам это делать):

Теперь предположим, что вы 'это программа, которая, тем не менее, хочет продолжить сомнительный дизайн, когда действие двойного щелчка не связано с действием одного щелчка.Что вы делаете?

Ну, одну вещь, которую вы могли бы сделать, это ничего не делать при получении сообщения WM_LBUTTONDOWN, кроме установки таймера на срабатывание в GetDoubleClickTime() миллисекунд.[Исправлено в 10:00.] Если вы получили сообщение WM_LBUTTONDBLCLK за это время, то это был двойной щелчок.Если вы этого не сделаете, то это должен был быть один щелчок, чтобы вы могли выполнить действие одним щелчком (хотя и с небольшим опозданием).

4 голосов
/ 28 января 2011

Вот код для выполнения того, что вы просили.

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

Суть этого кода в том, чтобы отложить срабатывание события click до истечения времени двойного щелчка.Если в течение этого времени в это время происходит другое событие щелчка, событие двойного щелчка вызывается без вызова события щелчка.Если, однако, двойного щелчка нет, событие щелчка вызывается.

Эта задержка особенно заметна на компьютерах с более высокой скоростью двойного щелчка.На типичном компьютере скорость двойного щелчка составляет 500 мс, поэтому код будет запускать событие щелчка где-то между 500 мс и 600 мс после нажатия.

...