我有一种情况,我正在处理表单上的单次和双击鼠标事件。在这两种情况下都必须加载一些东西,但是当发生双击时,我不希望执行附加到单击事件的代码。
有没有办法拦截鼠标单击并检查是双键还是单键,然后适当地执行正确的事件?
也许通过拦截窗口的 WndProc 什么的?
我有一种情况,我正在处理表单上的单次和双击鼠标事件。在这两种情况下都必须加载一些东西,但是当发生双击时,我不希望执行附加到单击事件的代码。
有没有办法拦截鼠标单击并检查是双键还是单键,然后适当地执行正确的事件?
也许通过拦截窗口的 WndProc 什么的?
不,这几乎是不可能的,除非你有时光机。一旦您了解了 Windows 如何区分双击和单击,它甚至没有任何意义。
事实证明,Raymond Chen(Windows Shell 团队的开发人员)在一篇题为“ Windows 将单击转换为双击的方式的逻辑后果”的博客文章中准确地解释了这一点。
具体来说,Windows 只知道将某事解释为双击,因为在函数指定的时间间隔内发生了第二次单击。因为它需要千里眼(正如雷蒙德如此雄辩地指出的那样)来提前确定是单击还是双击,所以窗口管理器会继续并在收到第一次单击后立即发送消息。该消息仅在确认第二次单击实际上代表双击之后才发送。结果是您的应用程序将始终为收到的每条消息接收两条消息。GetDoubleClickTime
WM_LBUTTONDOWN
WM_LBUTTONDBLCLK
WM_LBUTTONDOWN
WM_LBUTTONDBLCLK
现在,.NET Framework 设计人员了解了此过程的工作原理并设计了相应引发的事件。当然,他们不能对总是在双击之前发生的单击做任何事情,但是如果确定用户打算将第二次单击消息作为双击事件的一部分,他们就能够抑制第二次单击消息。正如Control.MouseClick
事件的文档(大致对应于WM_LBUTTONDOWN
消息)告诉我们的那样:
根据用户操作系统的鼠标设置,在足够近的时间内发生的两次单击将生成一个
MouseDoubleClick
事件,而不是第二个MouseClick
事件。
但是,我在上面链接的 Raymond 的博客文章确实为坚持双击操作与单击操作无关的设计的应用程序和开发人员提出了一种可能的解决方法(尽管我们都不建议您实际这样做):
现在假设您是一个程序,但仍希望继续采用使双击操作与单击操作无关的可疑设计。你做什么工作?
好吧,您可以做的一件事是在收到
WM_LBUTTONDOWN
消息时什么都不做,除了设置一个以GetDoubleClickTime()
毫秒为单位的计时器。[更正了上午 10 点。] 如果您在那段时间内收到WM_LBUTTONDBLCLK
消息,那毕竟是双击。如果你不这样做,那么它一定是单击,所以你可以做你的单击动作(虽然有点晚)。
这是执行您要求的代码。
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 毫秒之间的某处运行单击事件。