65.9K
CodeProject 正在变化。 阅读更多。
Home

自动重复按钮类

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2017年4月13日

CPOL

3分钟阅读

viewsIcon

12710

downloadIcon

570

这是“自动重复按钮类”的替代方案

引言

我需要一个自动重复按钮。当用户按下并按住该按钮时,它会触发多个 BN_CLICKED 通知。我搜索了一下,找到了 Joseph M. Newcomer 的文章 "一个自动重复按钮类"。虽然文章中提供的代码完全按照广告宣传的那样工作,但它有一个主要的缺陷。当用户按下空格键时,它不起作用,它只适用于鼠标左键。文章附带的评论提供了一些关于如何修复它的想法,但没有一个是理想的。我采用了文章和一些评论中提出的想法,并提出了我在这里分享的代码。

Using the Code

让按钮控件执行我需要它做的事情的最佳方法是子类化 CButton 控件,并覆盖它的 OnLButtonDownOnLButtonUpOnKeyDownOnKeyUp 消息处理程序。我添加了四个 bool 成员变量,它们充当标志来控制发生了什么以及控件使用时接下来必须发生什么。前两个变量是 KeyPressMousePress,它们控制是鼠标按钮还是空格键启动了计时器。下一个是 TimerActive,它防止在按键自动重复时重新启动计时器。最后一个是 MessageSent,它控制在释放按钮时是否应发送 BN_CLICK 消息。

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);
    }
}

当用鼠标左键或空格键按下按钮时,我们首先检查该按钮是否已经被另一个按钮激活。然后,我们检查计时器是否尚未启动,如果未启动,则以初始延迟时间启动它。最后,调用基类处理程序来执行默认处理程序,该处理程序捕获鼠标并将按钮绘制为已按下。

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);
    }
}

当计时器触发时,我们首先检查按钮的状态。如果使用鼠标启动计时器,然后将其移开按钮,则按钮将不再被按下,因此我们不希望发送 BN_CLICKED 消息。但是,如果鼠标移回按钮,我们希望它们继续。在初始时间延迟之后,计时器将重置为重复时间并发送 BN_CLICKED 消息。

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;
    }
} 

释放按钮时,计时器会停止。然后,根据按钮是否被按住足够长的时间来生成 BN_CLICKED 消息,我们要么简单地释放鼠标捕获并将按钮绘制为未按下,要么调用基类处理程序以触发 BN_CLICKED 消息。然后,这些标志会被重置,以便下次按下按钮。

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

要处理的最后一件事是如果用户双击然后按住按钮。我只需调用 OnLButtonDown 将双击视为第二次单击。如果不这样做,则不会启动计时器。

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

最后,作为一个方便的功能,我添加了设置初始和重复延迟时间间隔的能力。

历史

  • 2017 年 4 月 13 日 - 发布在 CodeProject 上
© . All rights reserved.