Introduction
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
(the BN_CLICKED
message is the typical one that you handle and that VC++ supports in its resource
editor)!
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.
And another
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
Basically, I 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
CButton
called 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 CRepeatButton
(not CButton
).
That's it! The ordinary CButton
has been hijacked and is now effectively a CRepeatButton
.
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...
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.