Visual Basic Intrinsic Control Routines
SendMessage: Visually Pressing a Command Button Through Code
     
Posted:   Wednesday June 2, 1999
Updated:   Monday December 26, 2011
     
Applies to:   VB4-32, VB5, VB6
Developed with:   VB6, Windows NT4
OS restrictions:   None
Author:   VBnet - Randy Birch
     

Related:  

SetWindowLong: Changing Button Caption Alignment (non-graphical only)
Pure VB: Mimicking a Command Button ForeColor Property
WM_DRAWITEM: Set Command Button ForeColor
SendMessage: Visually Pressing a Command Button Through Code
     
 Prerequisites
None.

buttonpress.gif (3651 bytes)While option and check buttons each provide a way to both visually indicate a change in state and activate its associated code through setting the Value properties, the Command button's value property simply fires the underlying button click event without providing any visual feedback that the event was fired. Some circumstances may dictate a need to not only execute the button's Click event code, but also indicate visually to the user that the button has been activated. This code provides two simple demos to achieve the same end.

The first demo, coded in the Timer1 routine, shows how to "press a button" strictly through code using the BM_SETSTATE message. This message simply changes the highlight state of a button via the argument passed as the wParam member of SendMessage. Since "highlighting" is defined as positioning the cursor over a button and pressing and holding the left mouse button, with subsequent removal when the user releases the button, the BM_SETSTATE message therefore only changes the appearance of a button. It has no effect on the actual pressed state of a button, so it will not fire the button's associated Click event.

To add the expanded functionality of firing the button's Click event, the code is duplicated in Timer2 where two additional messages are sent - WM_LBUTTONDOWN and WM_LBUTTONUP. In normal operation, these messages are posted when the user presses or releases the left mouse button while the cursor is in the client area of a window (or in our case button).  For a click to register, both messages must occur. Paired with the BM_SETSTATE message, sending the WM_LBUTTONxxx messages will cause the click events to fire as well as appearing visually depressed. Without the BM_SETSTATE message, the click events to fire but with only a change in focus  (the button's focus rect is set) as the visual indicator that the code has executed.

Normally this code would be placed in a routine called passing the hwnd of the button to activate. For this example however, I've chosen to demo the code in Timer events.  And as usual, most code below simply supports the logic to enable/disable buttons and timers - the actual code needed to perform the visual press is 3 to 5 lines..

 BAS Module Code
None.

 Form Code
On a new form, add two timers (Timer1 & Timer2), and seven buttons in a control array (Command1(0) - Command1(6). Delete Command1(0) to allow use of the RND function in the Timer event to select the button index 1through 6. Add two additional buttons (Command 2 and Command 3), and a label (Label1). Add the following to the form:

Option Explicit
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Copyright ©1996-2011 VBnet/Randy Birch, All Rights Reserved.
' Some pages may also contain other copyrights by the author.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Distribution: You can freely use this code in your own
'               applications, but you may not reproduce 
'               or publish this code on any web site,
'               online service, or distribute as source 
'               on any media without express permission.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Private Declare Function SendMessage Lib "user32" _
   Alias "SendMessageA" _
  (ByVal hwnd As Long, _
   ByVal wMsg As Long, _
   ByVal wParam As Long, _
   lParam As Any) As Long

Private Const BM_SETSTATE = &HF3
Private Const WM_LBUTTONDOWN = &H201
Private Const WM_LBUTTONUP = &H202


Private Sub Command1_Click(Index As Integer)

   Label1.Caption = "Button " & Index & " pressed."
   
End Sub


Private Sub Command2_Click()

  'assure the other timer is disabled
   Timer2.Enabled = False
   
  'Set the timer and toggle the caption
   Timer1.Interval = 750
   Timer1.Enabled = Not Timer1.Enabled
   
  'disable/enable the other button as appropriate
   Command3.Enabled = Not Timer1.Enabled
   
   Command2.Caption = IIf(Timer1.Enabled, "Press to stop", _
                          "Start visual press only demo")   
End Sub


Private Sub Command3_Click()

  'assure the other timer is disabled
   Timer1.Enabled = False
   
  'Set the timer and toggle the caption
   Timer2.Interval = 750
   Timer2.Enabled = Not Timer2.Enabled
   
  'disable/enable the other button as appropriate
   Command2.Enabled = Not Timer2.Enabled
   
   Command3.Caption = IIf(Timer2.Enabled, "Press to stop", _
                          "Start visual press && click demo")

End Sub


Private Sub Form_Load()

  'randomize the random number generator
   Randomize CSng(TimeValue(Time))
   
  'set the initial captions
   Command2.Caption = IIf(Timer1.Enabled, "Press to stop", _
                          "Start visual press only demo")
                          
   Command3.Caption = IIf(Timer2.Enabled, "Press to stop", _
                          "Start visual press && click demo")
   
   Label1.Caption = "Click event shown here"

End Sub


Private Sub Timer1_Timer()

   Static currButton As Integer
   Static currHwnd As Long
   
  'restore the last button state to normal
   Call SendMessage(currHwnd, BM_SETSTATE, 0, ByVal 0&)
     
  'generate a random button index between 1 and 6
   currButton = Int(Rnd(1) * 6) + 1
   
  'get the handle to that button
   currHwnd = Command1(currButton).hwnd
   
  'and change its state.
   Call SendMessage(currHwnd, BM_SETSTATE, 1, ByVal 0&)
   
End Sub


Private Sub Timer2_Timer()

   Static currButton As Integer
   Static currHwnd As Long
   
  'restore the last button state to normal
   Call SendMessage(currHwnd, BM_SETSTATE, 0, ByVal 0&)
     
  'generate a random button index between 1 and 6
   currButton = Int(Rnd(1) * 6) + 1
   
  'get the handle to that button
   currHwnd = Command1(currButton).hwnd
   
  'and send mouse down and up messages to
  'fire its click event, and visibly indicate
  'its pressed state.
   Call SendMessage(currHwnd, WM_LBUTTONDOWN, 0, ByVal 0&)
   Call SendMessage(currHwnd, WM_LBUTTONUP, 0, ByVal 0&)
   Call SendMessage(currHwnd, BM_SETSTATE, 1, ByVal 0&)
   
  'allow the label to repaint
   DoEvents

End Sub
 Comments
When Command2 is pressed, the different buttons in the command button array will alternate becoming pressed/unpressed via the timer code. The label, reflecting the firing of the Click event, will remain unchanged. Pressing Command3 will again toggle the pressed/unpressed state, but now Label 1 will indicate the button "clicked".

This same technique can be extended to pressing buttons in other applications once the button hwnd has been determined.


 
 

PayPal Link
Make payments with PayPal - it's fast, free and secure!

 
 
 
 

Copyright ©1996-2011 VBnet and Randy Birch. All Rights Reserved.
Terms of Use  |  Your Privacy

 

Hit Counter