Home > Code Library > SmartMouse

Last Update 29 Oct 2001

 


Search:



  

Options:

   

   

   

   



Trap the mouse!

About this page

This article describes how to get rid of 'DblClick' events by using library 'SmartSubclass'. It is a good example of how to use this library and it also gives a comprehensive understanding of how and when mouse messages are posted. 

Downloads

SmartMouse Demo

Requires SmartSubclass

How does VB handle mouse messages?

Each control in Visual Basic normally comes with 5 mouse events: Click, DblClick, MouseDown, MouseUp and MouseMove.
For most applications that's enough and we rarely need Click and MouseDown at the same time. But there are situations when our code needs to rely strongly on mouse messages and that's when we find that VB has its limitations.

If we examine how VB receives mouse events and in what order those are processed we find the following sequence:

Action Generated VB events
Single Mouse Click MouseDown
MouseUp
Click
MouseMove

Action Generated VB events
Double Mouse Click MouseDown
MouseUp
Click
MouseMove
DblClick
MouseUp


Have you noticed something strange? Well, if we forget about mousemove and we focus on the other four events (click, dblclick, mouseup and mousedown) you will see that the succesion of messages generated by the double mouse click is slightly different to the one generated by a single mouse click. A single mouse click generates {mousedown, mouseup, click} whereas a double mouse click generates {mousedown, mouseup, click, dblclick, mouseup}. The second mousedown message is translated into a dblclick message and there isn't a click message following the second mouseup.

Why is that? This is not a VB problem, it comes directly from Microsoft Windows. It is due to the fact that Visual Basic controls are created with windows belonging to classes that have the CS_DBLCLKS class style - we will talk about this later.

Now let's propose a simple VB application: imagine that we're in the process of implementing a button control. We will have to trap both mousedown and mouseup events in order to draw the button pushed or released.


Draw the edge sunken when the
mouse is clicked and draw the edge
raised when the mouse is released

The problem seems simple and we'll only have to code the UserControl_MouseDown and UserControl_MouseUp events. We also want the button to only respond to left clicks because the right click is going to be used for a pop-up menu. The code will probably look something like:

Private Sub UserControl_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
    If Button = vbLeftButton Then
        Call
DrawMyEdgeSunken
    End If
End Sub

Private Sub
UserControl_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
    If Button = vbLeftButton Then
        Call
DrawMyEdgeRaised
    End If
End Sub


So after finishing our first version of the button we run the program and we start playing with the new control: we left click... and the button seems to be pushed, we release the mouse... and the button seems to release too. And we click, and we click again, and we start clicking faster and... hey!, what's happening? the mouse seems to be released all the time and we're loosing the push-pop effect! Well, the explanation is simple: when we fast click over the button (up, down, up, down) Windows sends a DblClick event instead of the second MouseDown. This makes it seem as if the button is always released, because the second MouseDown event is never fired.

Somebody may say: "that's easy, we only have to code the DblClick event and make it work as a MouseDown". Yes, that could be a good solution but ... how do we know if the user is left double-clicking rather than right double-clicking? And this is when we start getting problems with VB!

MouseDown and MouseUp events include information about which button the user is pressing and where (x,y) the user is clicking, whereas both Click and DblClick events contain none of this information. We wouldn't be able to know if the user is pressing the right button just by using the DblClick event. Yes, there are other ways round it (we could use flags and other techniques to solve this problem) but... maybe there's a better solution.

What if we could get rid of the DblClick event? What if a double-click is just that, two mouse clicks?

We could think about removing the CS_DBLCLKS style from our window class, but this would affect the rest of the system. We could also think about sending a MouseDown event from the DblClick, but we still need to know how and where the mouse has been clicked.

The solution I propose is based on the SmartSubClass library. We're going to define a new smartsubclass variable, make it listen to all messages posted to the usercontrol and to replace all DblClick events with MouseDowns.

Add the following code to the usercontrol:

Option Explicit

Dim WithEvents oSniff As SmartSubClass

'API declarations:
Private Const WM_LBUTTONDBLCLK = &H203
Private Const WM_RBUTTONDBLCLK = &H206
Private Const WM_LBUTTONDOWN = &H201
Private Const WM_RBUTTONDOWN = &H204

'
' Initialise the SmartSubClass variable...
'
Private Sub UserControl_Initialize()
    Set oSniff = New SmartSubClass
    oSniff.SubClassHwnd UserControl.Hwnd,
True
End Sub

'
' Make sure that usercontrol is unsubclassed before terminating...
'
Private Sub UserControl_Terminate()
    oSniff.SubClassHwnd UserControl.Hwnd,
False
End Sub

'
' Convert DoubleClick into MouseDown messages...
'
Private Sub oSniff_NewMessage( _
    ByVal hwnd As Long, _
    uMsg As Long, _
    
wParam As Long, _
    lParam As Long, _
    Cancel As Boolean)

    Select Case uMsg
        Case WM_LBUTTONDBLCLK
            uMsg = WM_LBUTTONDOWN

        Case WM_RBUTTONDBLCLK
            uMsg = WM_RBUTTONDOWN
    End Select
    
End Sub


Easy, isn't it? Now we have full control, not only of the mouse messages but of absolutely all messages posted to the usercontrol. We will be the first to receive a message and we will decide whether to cancel, modify or just let the message pass through. With this solution the usercontrol will no longer receive double-click messages. It will receive mousedown events instead.

There are other mouse messages that VB doesn't make visible to the developer. The following is a list of those messages with a short description of their purpose. You can modify the above example to receive any of these "extra" messages.

WM_CAPTURECHANGED
Sent to the window that is losing the mouse capture.
WM_MOUSEACTIVATE
Sent when the cursor is in an inactive window and the user presses a mouse button.
WM_MOUSEHOVER
Posted to a window when the cursor hovers over the client area of the window.
WM_MOUSELEAVE
Posted to a window when the cursor leaves the client area of the window.
WM_MOUSEWHEEL
Sent to the focus window when the mouse wheel is rotated.

Finally, you can find more examples on how to get more control of your mouse by visiting the article Flat Button control.

 
Copyright © 2001, Andrés Pons (andres@vbsmart.com). All rights reserved.