Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A Simple Method for Handling Multiple Clicking on Windows Forms Controls

0.00/5 (No votes)
7 Feb 2008 3  
Ever wanted to be able to handle triple, quadruple clicks on a Windows Forms control? This will do it.

Introduction

Once again, I became frustrated at the way I seem to have to handle click and double-click events in Windows Forms. Once again, I sat down and wrote a multi-click handler because I couldn't find where I stashed the last one. This one is the simplest one I've written so far, and it will allow me to handle any number of mouse clicks I feel like throwing at any Windows control that inherits System.Windows.Forms.Control.

You have probably noticed that in Microsoft Word, a double-click selects the whole word, and a triple-click selects the entire paragraph. This snippet will allow you to implement the quadruple-click to select the whole page, and the quintuple-click to select the whole hard drive (but not in Microsoft Word). Now, I can implement that secret application backdoor where I click 15 times on the bottom-left of the form and it gives me access to the gold vault.

How it works

I have written a simple VB.NET snippet which is pasted into the form that manages your controls. A small piece of code that implements:

  1. A Timer that is started when you click on a multi-click-enabled control;
  2. A counter for how many times you click the control before the Timer goes off;
  3. A method that handles the Timer "Tick" event that is triggered when it goes off.

This time, there is no class, no call-back, no fancy anything. It is just a snippet that needs not much more than three lines modified per control enabled. The rest is just cut/copy/paste.

The snippet below is a single private method for managing clicks on the form itself. Management of other controls is almost identical. The method sets itself up to handle two events:

  • Me.MouseClick; and
  • Me.MouseDoubleClick.

It calls a standard embedded method, ClickTimer_Click, passing its ID string, the mouse button, and the location of the mouse click. This method is just replicated and hacked for every control that needs multi-click enabling - easy.

#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

Notes to Form1_Clicked:

Notice that the Form1_Clicked method handles two events. In fact, an event handler can be set to handle many different events. The two events handled are "MouseClick" and "MouseDoubleClick", but there are two other click events: "Click" and "DoubleClick", what about them? OK, we could handle them also by defining the method as:

Private Sub Form1_Clicked (ByVal sender As Object, _
        ByVal e As System.Windows.Forms.MouseEventArgs) _
        Handles Me.MouseClick, Me.MouseDoubleClick, Me.Click, Me.DoubleClick

However, this would double-count the number of clicks to Form1.

The way control click events seem to work is that if multiple event handlers are set up to handle an event, they are all triggered sequentially when it occurs. MouseClick handles a mouse-click event, so does Click - they will both go off in sequence when the mouse is clicked. Furthermore, DoubleClick is first and foremost a click event as well, so when there is a DoubleClick action, the Click event goes off first, followed by the MouseClick event, followed by the DoubleClick event, followed by the MouseDoubleClick event. Four events for one double-click has to be constrained somewhat.

I have not implemented the Click or DoubleClick events. On a double-click, I count once for the MouseClick event and once for the MouseDoubleClick event, and get two clicks - very straightforward. If you click three times, I get a double-click followed by a single-click and count three clicks. Four clicks gives double-click then double-click, etc., etc.

The standard I have followed for identifying multi-clicks is a standard Windows methodology. To be considered as part of the same multi-click:

  1. A click must follow the previous click within a limited amount of time.
  2. Subsequent clicks must take place within a limited area in the vicinity of the first click.
  3. The same button must be clicked each time.

If any of the above conditions are not met, the multi-click count starts again at one.

  • The limited time between clicks is defined by System.Windows.Forms.SystemInformation.DoubleClickTime.
  • The limited area for subsequent clicks is defined by System.Windows.Forms.SystemInformation.DoubleClickSize.
  • Each time an additional valid click occurs, the Timer's Interval is reset to DoubleClickTime, allowing the entry of any number of clicks, limited only by a 32-bit signed integer (some two-billion-plus - go for it).

The ClickTimer_Click code used for every control 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 

The infrastructure

The infrastructure methods and data are simply pasted directly into the form. They comprise of a single region containing several data fields to manage click activity and counting, plus three methods: one to initialise, one to manage the clicks, and one to handle the timer going off. All of this code collapses down in its own region, and can be pasted to the bottom of the Form class, or wherever.

When the user stops clicking and the Timer goes off, the event handler has data specifying the:

  1. ID of the control that was clicked;
  2. Total number of clicks;
  3. Mouse button that was clicked; and
  4. Limited region that was clicked.

Inside the Timer event handler method, the key customisation occurs, within a Select/Case statement, with a Case for each control ID, fairly straightforward - it can be a router method.

The ClickTimer_TickHandler code used for every control click:

  ''' <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

The samples

I have provided two small download packages:

  1. The source code - "multiclick_src.zip" - a VB.NET project; and
  2. A demonstration executable - "multiclick_exe.zip".

The demonstration is a simple form with several controls: Button, Label, TextBox, PictureBox, and RichTextBox. All of these controls can be multi-clicked, plus the form itself, each time resulting in a message-box telling you what was clicked and how many times.

The multi-click functionality is implemented in the form itself - it is about all that is actually in there, so it is easy to find.

Finally

If anyone wants to port the snippet to other languages, go for it, and maybe post the results here in the message section, say under "C# Implementation", etc. If there are bugs or better solutions, please let me know. Meanwhile, enjoy!

If anyone actually implements more than a four-click variation, tell us about it. I've never done more than three in anger.

History

  • 8-Feb-2008 - Created.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here