65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.11/5 (8投票s)

2008年2月7日

CPOL

5分钟阅读

viewsIcon

47550

downloadIcon

925

您是否曾经想过如何在 Windows 窗体控件上处理三次、四次点击?这个方法可以做到。

引言

我又一次对如何在 Windows 窗体中处理点击和双击事件的方式感到沮丧。我又一次坐下来编写了一个多击处理程序,因为我找不到我上次存放它的地方。这是我写过的最简单的一个,它能让我处理任何数量的鼠标点击,我可以将这些点击应用于任何继承了 System.Windows.Forms.Control 的 Windows 控件。

您可能已经注意到,在 Microsoft Word 中,双击会选择整个单词,三次点击会选择整个段落。这段代码可以帮助您实现四次点击选择整页,五次点击选择整个硬盘(但不是在 Microsoft Word 中)。现在,我可以实现那个秘密应用程序后门,当我在窗体的左下角点击 15 次时,它会给我访问金库的权限。

工作原理

我写了一个简单的 VB.NET 代码片段,可以粘贴到管理控件的窗体中。一小段代码实现了:

  1. 当您点击一个启用了多击功能的控件时启动的 Timer
  2. Timer 停止之前,用于计算您点击控件次数的计数器;
  3. 一个处理 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 事件。一次双击触发四个事件,这必须加以限制。

我没有实现 ClickDoubleClick 事件。在双击时,我为 MouseClick 事件计数一次,为 MouseDoubleClick 事件计数一次,总共得到两次点击——非常直接。如果您点击三次,我会得到一个双击加上一个单击,总共三次点击。四次点击则是一个双击后又一个双击,以此类推。

我遵循的标准是使用标准的 Windows 方法来识别多击。要被视为同一多击的一部分:

  1. 一次点击必须在有限的时间内发生于前一次点击之后。
  2. 后续的点击必须发生在第一次点击附近有限的区域内。
  3. 每次都必须点击同一个按钮。

如果上述任何条件不满足,多击计数将重新从一开始。

  • 点击之间的有限时间由 System.Windows.Forms.SystemInformation.DoubleClickTime 定义。
  • 后续点击的有限区域由 System.Windows.Forms.SystemInformation.DoubleClickSize 定义。
  • 每次发生额外的有效点击时,TimerInterval 会被重置为 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 停止时,事件处理程序会包含指定以下信息的:

  1. 被点击控件的 ID;
  2. 总点击次数;
  3. 被点击的鼠标按钮;以及
  4. 被点击的有限区域。

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

示例

我提供了两个小的下载包:

  1. 源代码 - “multiclick_src.zip” - 一个 VB.NET 项目;以及
  2. 一个演示可执行文件 - “multiclick_exe.zip”。

演示是一个简单的窗体,包含多个控件:ButtonLabelTextBoxPictureBoxRichTextBox。所有这些控件都可以被多击,包括窗体本身,每次都会弹出一个消息框,告诉您点击了什么以及点击了多少次。

多击功能实现在窗体本身——这是其中几乎所有实际内容,所以很容易找到。

最后

如果有人想将此代码片段移植到其他语言,请随意尝试,并在留言区发布结果,例如在“C# 实现”下等。如果存在错误或有更好的解决方案,请告诉我。同时,祝您使用愉快!

如果有人实际实现了超过四次点击的变体,请告诉我。我从来没有真正尝试过三次以上的。

历史

  • 2008 年 2 月 8 日 - 创建。
© . All rights reserved.