Posts

Detecting Triple-Clicks

Visual Basic 6; and, well- nearly any other relatively modern language; provides a way to detect mouse events. These usually include mouse down, mouse up, mouseclicks, and double clicks. Absent are any higher “N-clicks”; I speak, for the most part, of the triple click.

The triple-click is a awkward sort of mouse input; but it’s quite simple to detect. It’s three clicks in a row. However, this is not what the program will see. For the most part, the program will see Mousedown,Mouseup,mousedown,doubleclick,mouseup; that is, windows will replace the mouse-down with the double-click message.

Visual Basic, and of course a number of other programming environments, translate windows messages into events of some sort. Specifically, Visual Basic has Click, Double-Click, mouseup, and mousedown. the behaviour and ordering mentioned above can be seen if one is to log the events as they occur.

Detecting triple clicks is pretty easy, however; the idea is that a “triple click” is when you click three times and each click is within the double-click time from the previous click. this is easily facilitated by recording the DoubleClick time and testing the difference between the next click time and the previous click, and, if it is less then the doubleclicktime() (retrieved by the GetDoubleClickTime() API function) then register it as a TripleClick.

But such a mess! why not make a reusable class that can be used whenever triple-click functionality is desired? This is how I handle this type of problem- a missing event; I usually create a new class that raises that event, and only requires to be initialized and told what object it’s checking.Thus is born the CTripleClick class.

But exactly how, from the class’s perspective, does it achieve it’s functionality? There are actually a few options, of different complexity- the easiest, and maybe the safest, would be to use the Visual Basic “withevents” keyword on the object in question. However, this is problematic in that the CTripleClick Class is intended for use in various scenario’s- it might be used to detect triple-clicks on commandbuttons, pictureboxes, forms, usercontrols, and any number of other objects. Withevents, however, requires a specific classname. the “VBControlExtender” object can be used for controls, but we’d still need to hook a Form, Usercontrol, and all the intrinsic controls in different withevents variables. So that method is out of the question. There is one alternative, however.

Subclassing

Subclassing can provide a short and sweet solution to this problem. The way I used to subclass- the “unsafe” way, was to leverage Visual Basic’s “addressof” keyword and use SetWindowLong to redirect the window Procedure to my routine. This is unsafe because pressing the end button or even usually going into break mode, will cause Visual Basic to crash completely. This is the main reason many VB6 programmers avoid subclassing like the plague.

However, I have come across several implementations of safer subclassing methods that also do away with the Module dependency- thus making it possible to subclass as well as have no modules in a project, if that is desired for some reason. The implementation I have been using is from Planet-Source-code; namely, this entry, which I believe to be a excellent implementation. Other implementations can be found at such places as VBAccelerator, but this is the one I was used to, so I used it :P.

Subclassing is a fairly simple concept; you “take over” the messages being sent to a window. You can then decide if you want to pass the message along to the “true” recipient, or keep them in the dark about it. The reason this particular trait is important is that it is necessary for the objective of this class. My intent is to emulate what the double-click event does; that is, Windows sends Mousedown, mouseup,double-click,mouseup. When somebody triple-clicks, the intended messages that would be sent should be Mousedown,Mouseup,doubleclick,mouseup,tripleclick,mouseup. This will require us to hide the mousedown message that triggers the tripleclick event we fire.

My first attempt at this had some side-issues. I was hiding mouse-down perfectly fine, by setting the “lhandled” byref argument passed into my iSuperclass implementation to true. However, this also caused a few odd issues both with VB events and the way the windows reacted, mostly related to mouse capture. The solution was to call “DefWindowProc” which performs the default operations for a window. In this case, it handles the focus and mouse capture of the window.

For further study, consult the source I made for this, in a test project called, rightly enough, TripleClick. I have uploaded it here: