Click here to Skip to main content
15,881,588 members
Articles / Desktop Programming / MFC
Alternative
Article

An AutoRepeat Button Class

Rate me:
Please Sign up or sign in to vote.
5.00/5 (3 votes)
12 Apr 2017CPOL3 min read 11.5K   493   3   4
This is an alternative for "An AutoRepeat Button Class"

Introduction

I am in need of an autorepeat button. A button that when the user presses and holds it down will fire multiple BN_CLICKED notifications. I did a search and found Joseph M. Newcomer's article "An AutoRepeat Button Class". While the code presented in the article worked exactly as advertised, it had one major flaw. It did not work when the user pressed the space bar, it only worked with the left mouse button. The comments attached to the article provided some ideas on how to fix it, but none were ideal. I took the ideas presented in the article and some of the comments and came up with the code I am sharing here.

Using the Code

The best way to get the button control to do what I needed it to do was to subclass the CButton control and override its OnLButtonDownOnLButtonUpOnKeyDown, and OnKeyUp message handlers. I added four bool member variables that act as flags to control what has happened and what has to happen next when the control is used. The first two variables are KeyPress and MousePress that  control whether it is the mouse button or the space bar that started the timer. The next one is TimerActive that prevents the timer from being restarted when the key press auto-repeats. The last one is MessageSent that controls whether a BN_CLICK message should be sent when the button is released.

C++
void CAutoRepeatButton::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    if (!MousePress)    // Only if not already activated by the mouse
    {
        if (VK_SPACE == nChar && !TimerActive)
        {
            SetTimer(TIMERID, InitialTime, NULL);
            TimerActive = true;
            KeyPress = true;
        }

        CButton::OnKeyDown(nChar, nRepCnt, nFlags);
    }
}

void CAutoRepeatButton::OnLButtonDown(UINT nFlags, CPoint point)
{
    if (!KeyPress)  // Only if not already activated with the space bar
    {
        if (!TimerActive)
        {
            SetTimer(TIMERID, InitialTime, NULL);
            TimerActive = true;
            MousePress = true;
        }

        CButton::OnLButtonDown(nFlags, point);
    }
}

When the button is pressed with either the left mouse button or the space bar, we first check that the button is not already activated by the other one. Then we check if the timer is not already started and start it with the initial delay time if it was not. Finally, place a call to the base class handler to do the default handler which captures the mouse and draws the button as depressed.

C++
void CAutoRepeatButton::OnTimer(UINT_PTR nIDEvent)
{
    if (nIDEvent == TIMERID)
    {
        if (BST_PUSHED == (BST_PUSHED & GetState()))
        {
            if (!MessageSent)
            {
                SetTimer(TIMERID, RepeatTime, NULL);
                MessageSent = true;
            }
            Parent->SendMessage(WM_COMMAND, MAKELPARAM(GetDlgCtrlID(), BN_CLICKED), (WPARAM)m_hWnd);
        }
    }
    else
    {
        CButton::OnTimer(nIDEvent);
    }
}

When the timer fires, we first check the state of the button. If the mouse was used to start the timer and then it is moved off of the button, the button will no longer be pushed so we will not want the BN_CLICKED messages to be sent. But we do want them to continue if the mouse moves back onto the button. After the initial time delay, the timer is reset to the repeat time and a BN_CLICKED message is sent.

C++
void CAutoRepeatButton::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    if (VK_SPACE == nChar && KeyPress)
    {
        KillTimer(TIMERID);
        if (MessageSent)
        {
            ReleaseCapture();   // CButton::OnKeyDown captures the mouse
            SetState(0);        // Redraw button as not pushed
        }
        else
        {
            CButton::OnKeyUp(nChar, nRepCnt, nFlags);
        }
        TimerActive = false;
        KeyPress = false;
        MessageSent = false;
    }
}

void CAutoRepeatButton::OnLButtonUp(UINT nFlags, CPoint point)
{
    if (MousePress)
    {
        KillTimer(TIMERID);
        if (MessageSent)
        {
            ReleaseCapture();
            SetState(0);
        }
        else
        {
            CButton::OnLButtonUp(nFlags, point);
        }
        TimerActive = false;
        MousePress = false;
        MessageSent = false;
    }
} 

When the button is released, the timer is stopped. And then, depending on whether the button was held down long enough to generate a BN_CLICKED message, we either simply release the mouse capture and draw the button as not pressed or we call the base class handler to fire off a BN_CLICKED message. Then the flags are reset for the next time the button is pressed.

C++
void CAutoRepeatButton::OnLButtonDblClk(UINT nFlags, CPoint point)
{
    OnLButtonDown(nFlags, point);
}

The last thing to handle is if the user double clicks and then holds the button down. I simply call OnLButtonDown to treat the double click as a second single click. If this is not done, then the timer is not started.

C++
void CAutoRepeatButton::SetTimes(UINT Initial, UINT Repeat)
{
    InitialTime = Initial;
    RepeatTime = Repeat;
}

Finally, as a convenience function, I added the ability to set the initial and repeating delay time interval.

History

  • April 13, 2017 - Published on CodeProject

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
President
Canada Canada
Father of two, brother of two, child of two.
Spouse to one, uncle to many, friend to lots.
Farmer, carpenter, mechanic, electrician, but definitely not a plumber.
Likes walks with the wife, board games, card games, travel, and camping in the summer.
High school graduate, college drop-out.
Hobby programmer who knows C++ with MFC and the STL.
Has dabbled with BASIC, Pascal, Fortran, COBOL, C#, SQL, ASM, and HTML.
Realized long ago that programming is fun when there is nobody pressuring you with schedules and timelines.

Comments and Discussions

 
QuestionSeeking help with CFileEditCtrl.cpp Pin
Member 1332065220-Jul-17 13:37
Member 1332065220-Jul-17 13:37 
AnswerRe: Seeking help with CFileEditCtrl.cpp Pin
PJ Arends2-Aug-17 7:39
professionalPJ Arends2-Aug-17 7:39 
GeneralMy vote of 5 Pin
chaz-chance18-Apr-17 2:46
chaz-chance18-Apr-17 2:46 
GeneralRe: My vote of 5 Pin
PJ Arends18-Apr-17 11:48
professionalPJ Arends18-Apr-17 11:48 

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.