Click here to Skip to main content
13,796,793 members
Click here to Skip to main content
Add your own
alternative version


52 bookmarked
Posted 14 Apr 2002

An AutoRepeat Button Class

, 14 Apr 2002
Rate this:
Please Sign up or sign in to vote.
A button control that autorepeats after a set period of time
<!-- Download Links --> <!-- Add the rest of your HTML here -->


In the past, I have done custom buttons that auto-repeat "from scratch", that is, I start with a raw CWnd and build on top of it. Recently, there have been a number of questions in the newsgroup inquiring after auto-repeat buttons. My first answer was to say "Just add a timer to a subclassed button", but after mulling it over for a few days, I realized there were some problems with this.

I don't claim this is an elegant solution, but it does work, and it saves having to reinvent all of the button drawing, styles, etc. 

The basic problem is that most auto-repeat buttons have the characteristic that they send a message to the parent when the left button is clicked, and then repeat messages as long as it is held down. 

A regular button doesn't work this way. It sends a message to the parent when the left button is released. This means that if you just set a timer, you will get one event for each timer tick, and one at the end when the button is released.

OK, I cheated. Big Time. Amazingly, I seem to have gotten away with it! So I'll observe that this technique is not without risk. THIS ARTICLE HAS NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING THOSE OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR TASK [if I had a lawyer, he'd make me say something like this, in all caps].

First I used ClassWizard to create a CAutoRepeatButton subclass of CButton. Then I added handlers for OnLButtonDown, OnLButtonUp, and OnTimer. I defined two constants (you could make variables and use these as initial settings if you want programmability) for the initial delay and the repeat delay. Also, a constant for the timer ID. 

#define INITIAL_DELAY 500
#define REPEAT_DELAY  200
#define IDT_TIMER 1

Then I wrote the handlers.

In the header file for the class, I added a new member variable, sent:

        UINT sent;

I changed the OnLButtonDown handler to read as follows:

void CAutoRepeatButton::OnLButtonDown(UINT nFlags, CPoint point) 
    sent = 0;
    CButton::OnLButtonDown(nFlags, point);

I start a timer, based on the initial delay, and zero the clicks-sent counter. I'll discuss the need for it a bit later.

In the OnTimer handler I added the indicated code:

void CAutoRepeatButton::OnTimer(UINT nIDEvent) 
    if( (GetState() & BST_PUSHED) == 0)
                                MAKELONG(GetDlgCtrlID(), BN_CLICKED), 

What is going on here? Well, for one thing, if you drag the mouse out of the button, it pops back up again. This is standard button behavior. So I looked around for the state that indicates this, and discovered that it is the BST_PUSHED state. So I check the state to see if that bit is set. If it is not, I do not want to generate a BN_CLICKED event. I decided that I also did not want to change the timer interval. So I just return directly if the button state is not "pushed".  If the button state is pushed, I reset the timer to the shorter interval (it is always permissible to do multiple SetTimer operations on the same timer; the timer is just restarted with the new interval). I generate a BN_CLICKED notification to the parent. The SendMessage just creates the same WM_COMMAND message as the button would have generated on an OnLButtonUp event. I then increment a counter of the number of items I have sent.

This all worked fine, except that if I did a normal OnLButtonUp by using the CButton superclass event, I always got an extra click. This is easy to see if you set the time constants to something large, like 1000 and 1000. You see the counter click into "27", release the button, and it reads "28". This Is Not Good.

So I added the following code to the OnLButtonUp handler, and removed the call to the superclass.

void CAutoRepeatButton::OnLButtonUp(UINT nFlags, CPoint point) 
    if(GetCapture() != NULL)
       { /* release capture */
	if(sent == 0 && (GetState() & BST_PUSHED) != 0)
                                       MAKELONG(GetDlgCtrlID(), BN_CLICKED), 
       } /* release capture */
    //CButton::OnLButtonUp(nFlags, point);

The obvious thing to do is to kill the timer. That's easy. But what I have to do is "fake out" the normal button behavior. So the first thing I did was to comment out the call on the superclass. Now, I knew that this would cause serious malfunction; in fact, if you were to do only this, you would find that the button never released capture. So I cheated, and forced the capture release myself. What surprised me was that the button actually pops back up and redraws properly, something that I was sure would not work and would force me to actually hand-code the whole thing. I got away with it, but I'm not comfortable with the idea.

Here's how it works.

First, an OnLButtonUp event isn't interesting if I just clicked the mouse down somewhere on the dialog, dragged it into the button area, and released it. So I only want to do this if I actually had capture. Hence the GetCapture() != NULL test. If I don't have capture, I don't need to do anything because the button wasn't active. If I had capture, I now release it, doing what the default OnLButtonUp  handler does. Now, if the user released the button before the INITIAL_DELAY interval, nothing has been sent, so I want to send something so a single fast click will actually be seen. Hence the use of the sent counter (it could have been a BOOL as well, but I decided to count rather than just mark as being sent. This is a gratuitous choice). But suppose the user clicked in the button, and within the INITIAL_DELAY time dragged the mouse out of the button and then released it? So I added in the test for the BST_PUSHED state, and only send a message to the parent if both conditions, nothing has already been sent and the button is actually pushed, are both true.

The views expressed in these essays are those of the author, and in no way represent, nor are they endorsed by, Microsoft.

Send mail to with questions or comments about this web site.
Copyright © 2001 All Rights Reserved


This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


About the Author

Joseph M. Newcomer
United States United States
PhD, Computer Science, Carnegie Mellon University, 1975
Certificate in Forensic Science and the Law, Duquesne University, 2008

Co-Author, [i]Win32 Programming[/i]

You may also be interested in...

Comments and Discussions

GeneralSimilar but slightly different Pin
Rod Stone13-Nov-03 23:53
memberRod Stone13-Nov-03 23:53 
QuestionAutofire function ? Pin
london115-Jun-03 1:12
memberlondon115-Jun-03 1:12 
AnswerRe: Autofire function ? Pin
Joseph M. Newcomer15-Jun-03 6:18
memberJoseph M. Newcomer15-Jun-03 6:18 
GeneralNormal button click doesn't work Pin
paulb5-Nov-02 16:54
memberpaulb5-Nov-02 16:54 
GeneralRe: Normal button click doesn't work Pin
Deuce10-Feb-03 20:21
memberDeuce10-Feb-03 20:21 
Generalthanks! Pin
lucy10-Jun-02 7:45
memberlucy10-Jun-02 7:45 
GeneralKeyboard is not working Pin
Davide Calabro15-Apr-02 22:35
memberDavide Calabro15-Apr-02 22:35 
GeneralThank you Pin
Nish Nishant15-Apr-02 19:49
sitebuilderNish Nishant15-Apr-02 19:49 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web02 | 2.8.181207.3 | Last Updated 15 Apr 2002
Article Copyright 2002 by Joseph M. Newcomer
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid