处理 Windows Forms 控件上多次单击的简单方法






4.11/5 (8投票s)
您是否曾经想过如何在 Windows 窗体控件上处理三次、四次点击?这个方法可以做到。
引言
我又一次对如何在 Windows 窗体中处理点击和双击事件的方式感到沮丧。我又一次坐下来编写了一个多击处理程序,因为我找不到我上次存放它的地方。这是我写过的最简单的一个,它能让我处理任何数量的鼠标点击,我可以将这些点击应用于任何继承了 System.Windows.Forms.Control
的 Windows 控件。
您可能已经注意到,在 Microsoft Word 中,双击会选择整个单词,三次点击会选择整个段落。这段代码可以帮助您实现四次点击选择整页,五次点击选择整个硬盘(但不是在 Microsoft Word 中)。现在,我可以实现那个秘密应用程序后门,当我在窗体的左下角点击 15 次时,它会给我访问金库的权限。
工作原理
我写了一个简单的 VB.NET 代码片段,可以粘贴到管理控件的窗体中。一小段代码实现了:
- 当您点击一个启用了多击功能的控件时启动的
Timer
; - 在
Timer
停止之前,用于计算您点击控件次数的计数器; - 一个处理
Timer
停止时触发的“Tick
”事件的方法。
这次,没有类,没有回调,没有花哨的东西。它只是一个片段,每个启用了多击功能的控件只需要修改三行左右。其余的只是复制/粘贴。
下面的代码片段是一个用于管理窗体本身点击的单一私有方法。管理其他控件几乎是相同的。该方法设置自己来处理两个事件:
Me.MouseClick
;和Me.MouseDoubleClick
.
它调用一个标准的嵌入方法 ClickTimer_Click
,并将控件的 ID 字符串、鼠标按钮和鼠标点击位置传递给它。这个方法就是为每个需要启用多击功能的控件进行复制和修改——很简单。
#Region "[=== CLICK EVENTS FOR CONTROL FORM1 ===]"
''' <summary>
''' Event handler for this form mouse Click and DoubleClick events.
''' </summary>
''' <param name="sender">Event object owner (Mouse).</param>
''' <param name="e">Event arguments.</param>
''' <remarks></remarks>
Private Sub Form1_Clicked (ByVal sender As Object, _
ByVal e As System.Windows.Forms.MouseEventArgs) _
Handles Me.MouseClick, Me.MouseDoubleClick
' Set up a constant to identify the control
' for which we are currently handling events.
Const kCLICKEVENT_OWNER As String = "FORM1"
Me.ClickTimer_Click(kCLICKEVENT_OWNER, e.Button, e.Location)
End Sub
#End Region
Form1_Clicked 的注意事项
请注意,Form1_Clicked
方法处理两个事件。实际上,一个事件处理程序可以设置为处理许多不同的事件。这里处理的两个事件是“MouseClick
”和“MouseDoubleClick
”,但还有两个其他的点击事件:“Click
”和“DoubleClick
”,那它们呢?好的,我们也可以通过将方法定义为
Private Sub Form1_Clicked (ByVal sender As Object, _
ByVal e As System.Windows.Forms.MouseEventArgs) _
Handles Me.MouseClick, Me.MouseDoubleClick, Me.Click, Me.DoubleClick
然而,这会将 Form1
的点击次数加倍。
控件点击事件似乎的工作方式是,如果多个事件处理程序设置为处理一个事件,当事件发生时,它们都会按顺序触发。MouseClick
处理鼠标点击事件,Click
也一样——当鼠标被点击时,它们都会按顺序触发。此外,DoubleClick
本质上也是一个点击事件,所以在发生 DoubleClick
操作时,会先发生 Click
事件,然后是 MouseClick
事件,然后是 DoubleClick
事件,最后是 MouseDoubleClick
事件。一次双击触发四个事件,这必须加以限制。
我没有实现 Click
或 DoubleClick
事件。在双击时,我为 MouseClick
事件计数一次,为 MouseDoubleClick
事件计数一次,总共得到两次点击——非常直接。如果您点击三次,我会得到一个双击加上一个单击,总共三次点击。四次点击则是一个双击后又一个双击,以此类推。
我遵循的标准是使用标准的 Windows 方法来识别多击。要被视为同一多击的一部分:
- 一次点击必须在有限的时间内发生于前一次点击之后。
- 后续的点击必须发生在第一次点击附近有限的区域内。
- 每次都必须点击同一个按钮。
如果上述任何条件不满足,多击计数将重新从一开始。
- 点击之间的有限时间由
System.Windows.Forms.SystemInformation.DoubleClickTime
定义。 - 后续点击的有限区域由
System.Windows.Forms.SystemInformation.DoubleClickSize
定义。 - 每次发生额外的有效点击时,
Timer
的Interval
会被重置为DoubleClickTime
,这样就可以输入任意数量的点击,仅受 32 位有符号整数的限制(大约二十亿多——尽情尝试吧)。
用于每次控件点击的 ClickTimer_Click 代码
''' <summary>
''' Register a mouse click (or double click) event.
''' </summary>
''' <param name="aOwnerControl">Click-Timer owner control (string).</param>
''' <param name="aMouseButton">Mouse button clicked.</param>
''' <param name="aClickPoint">Click point for definition of the valid
''' double-click area.</param>
''' <remarks></remarks>
Private Sub ClickTimer_Click _
(ByVal aOwnerControl As String _
, ByVal aMouseButton As System.Windows.Forms.MouseButtons _
, ByVal aClickPoint As System.Drawing.Point)
' Handle this click event.
If Me.fClickTimer.Enabled Then
' The Click-Timer is going, stop it and check we have not
' changed controls or mouse buttons.
Me.fClickTimer.Stop()
If (Me.fClickOwner = aOwnerControl) _
AndAlso (Me.fClickButton = aMouseButton) _
AndAlso Me.fClickArea.Contains(aClickPoint) Then
' Working with the same control, same button within a
' valid double-click area so bump the count.
Me.fClickCount += 1
' Set the system default double-click delay.
Me.fClickTimer.Interval = Me.fClickDelay
' Start the Click-Timer.
Me.fClickTimer.Start()
Else
' Not working with the same control. Initialise the Click-Timer.
Me.ClickTimer_Initialise(aOwnerControl, aMouseButton, aClickPoint)
End If
Else
' The timer is not enabled. Initialise the Click-Timer.
Me.ClickTimer_Initialise(aOwnerControl, aMouseButton, aClickPoint)
End If
End Sub
基础设施
基础设施方法和数据直接粘贴到窗体中。它们包含一个单一的区域,其中包含几个数据字段来管理点击活动和计数,以及三个方法:一个用于初始化,一个用于管理点击,还有一个用于处理计时器停止。所有这些代码都折叠在自己的区域中,可以粘贴到 Form
类的底部,或任何其他位置。
当用户停止点击并且 Timer
停止时,事件处理程序会包含指定以下信息的:
- 被点击控件的 ID;
- 总点击次数;
- 被点击的鼠标按钮;以及
- 被点击的有限区域。
在 Timer
事件处理方法内部,关键的定制发生在 Select/Case
语句中,每个控件 ID 都有一个 Case
,这非常直接——它也可以是一个路由方法。
用于每次控件点击的 ClickTimer_TickHandler 代码
''' <summary>
''' Click-Timer "Tick" event handler.
''' </summary>
''' <param name="sender">Event object owner.</param>
''' <param name="e">Event arguments.</param>
''' <remarks></remarks>
Private Sub ClickTimer_TickHandler (ByVal sender As Object, _
ByVal e As EventArgs) Handles fClickTimer.Tick
Dim xStr, xCtl As String
' Stop the Timer.
Me.fClickTimer.Stop()
' Handle the case for each control.
Select Case Me.fClickOwner
Case "BUTTON1"
xCtl = "Button1"
Case "FORM1"
xCtl = "Form1"
Case "LABEL1"
xCtl = "Label1"
Case "PICTUREBOX1"
xCtl = "PictureBox1"
Case "RICHTEXTBOX1"
xCtl = "RichTextBox1"
Case "TEXTBOX1"
xCtl = "TextBox1"
Case Else
xCtl = "Unknown Control"
End Select
xStr = "Number of clicks for [" + xCtl _
+ "] are [" + Me.fClickCount.ToString + "]."
xStr += vbCrLf + "Mouse button clicked was [" _
+ Me.fClickButton.ToString + "]."
xStr += vbCrLf + vbCrLf + "Sub ClickTimerTickHandler needs to be" _
+ " customised to process the clicks for " + xCtl + "."
MsgBox(xStr)
Me.fClickCount = 0
End Sub
示例
我提供了两个小的下载包:
- 源代码 - “multiclick_src.zip” - 一个 VB.NET 项目;以及
- 一个演示可执行文件 - “multiclick_exe.zip”。
演示是一个简单的窗体,包含多个控件:Button
、Label
、TextBox
、PictureBox
和 RichTextBox
。所有这些控件都可以被多击,包括窗体本身,每次都会弹出一个消息框,告诉您点击了什么以及点击了多少次。
多击功能实现在窗体本身——这是其中几乎所有实际内容,所以很容易找到。
最后
如果有人想将此代码片段移植到其他语言,请随意尝试,并在留言区发布结果,例如在“C# 实现”下等。如果存在错误或有更好的解决方案,请告诉我。同时,祝您使用愉快!
如果有人实际实现了超过四次点击的变体,请告诉我。我从来没有真正尝试过三次以上的。
历史
- 2008 年 2 月 8 日 - 创建。