<!-- Download Links -->
<!-- Article image -->
<!-- Add the rest of your HTML here -->
I needed an ordinary button that would act like it was being constantly clicked as long as the mouse button
was held down. Sounds pretty simple. You see this kind of thing in games and in XWindows projects I have seen
widgets that did this everywhere. But I couldn't find one. Noooo!
I did find some neat code that did a "mouse repeat" which sounded like it would do the job, albeit less
cleanly. This just repeated the Left Mouse Button Down message as long as the button was down. Cool. But button
controls only send the
BN_CLICKED message to the owning dialog when the mouse button goes UP
BN_CLICKED message is the typical one that you handle and that VC++ supports in its resource
So, it looked like I was going to have to subclass the standard
CButton control (subclassing is how one
intercepts messages to a control and is a Windows thing, not a C++ thing). Windows subclassing is something I
had never done. Fortunately there are some good subclassing articles at CodeProject.
What it does
The new button class,
CRepeatButton, captures the LButtonDown, LButtonUp, and Timer messages.
- When the left mouse button goes down, a timer is set. The duration is the same as for keyboard auto-repeat.
Also, the initial
BN_CLICKED message is posted to the parent of the control.
- When the timer fires the first time, the timer is reset to the interval between clicks during auto-repeat.
BN_CLICKED message is sent to the parent.
- Every time the timer goes off after this, the
BN_CLICKED message is posted.
If the mouse leaves the control's rectangle or if the mouse button is lifted, the timer is cancelled and no
further messages are posted to the parent.
So far, not too bad. However, since this button is acting slightly strangely, I felt it was important to give
some kind of visual and audible clues. So when in auto-repeat mode (after the initial delay), the background color
of the button changes to yellow. This involved the dreaded
DrawItem function and turning on Owner
Draw. But I grabbed this code from somebody else so it hardly hurt at all.
And each time the
BN_CLICKED message is posted, a "click" wav image is played.
Since this may annoy some, I have included lots of comments for removing these features.
How I did it
stole reused some code I found here at CodeProject:
How do I get it in my project?
There are probably better, cleaner, more graceful ways to do this, but this worked and was pretty simple.
First, I needed to let VC++ know about the new class, so I used the Class Wizard to create a new class based upon
CRepeatButton. The Class Wizard creates RepeatButton.h and RepeatButton.cpp
Then I copied in my own RepeatButton.h and RepeatButton.cpp files.
I then rebuilt the project on the general principle that making too many changes too fast confuses VC++.
Back to the resource editor to add in the new buttons: Add an ordinary button to the dialog and change the resource id
as needed. For the demo project included here, I called it
IDC_BUTTON1. Astoundingly, this is the very name
the resource editor assigned it!
Now use the Class Wizard to add a member variable to the dialog. The variable Category is Control (not that there is a lot
of choice). And the variable type is
That's it! The ordinary
CButton has been hijacked and is now effectively a
I built this with VC++ 6.0 (MFC 4.2) but I don't think it is all that version-specific.
One little weirdness is that the WAV sound is not a file nor a resource. It is so small (10 milliseconds) that I couldn't
bear the thought of the overhead of searching for it every time through the loop. So I converted the file to an array of
characters and send the address to the
sndPlaySound() function. But this will annoy some so I included comments
to undo this...