You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在CMFCToolbar中添加多个CSliderCtrl按钮的消息与Tooltip问题

问题描述

我需要在MFCToolbar中添加多个滑块作为按钮,已成功将滑块显示在工具栏中,但无法将滑块位置变更消息传递至父工具栏或主框架类,也无法显示滑块当前位置的Tooltip。

我的实现步骤如下:

  • 从CSliderCtrl派生clsMySlider类,重载其OnHScroll方法
  • 从CMFCToolbarButton派生clsMySliderButton类,并在其中嵌入clsMySlider实例
  • 为clsMySliderButton添加Initialize方法,在该方法中创建clsMySlider实例
  • m_pParent和m_pToolbar分别为主框架窗口和MFC工具栏的CWnd*指针
  • 使用CMFCToolbar::ReplaceButton将工具栏的第1、2个按钮替换为2个clsMySliderButton实例
  • 在MainFrame窗口中重载OnHScroll方法,以捕获Windows发送至主框架的消息

相关代码片段:

void clsMySlider::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
    // TODO: 添加消息处理代码或调用默认实现
    ::PostMessage(m_pParent->GetSafeHwnd(), WM_SLIDER_HSCROLL, NULL, nPos);
    ::PostMessage(m_pToolbar->GetSafeHwnd(), WM_SLIDER_HSCROLL, NULL, nPos);

    CSliderCtrl::OnHScroll(nSBCode, nPos, pScrollBar);
}
void CMainFrame::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
    // TODO: 添加消息处理代码或调用默认实现
        int dummyVar=nPos;

    CFrameWndEx::OnHScroll(nSBCode, nPos, pScrollBar);
}

但问题在于clsMySlider或CMainFrame窗口中的OnHScroll从未被调用。

恳请帮忙解答:如何将滑块位置变更事件传递至父工具栏和主框架窗口,以及如何显示滑块当前位置的Tooltip?

附带类定义和初始化代码:

class clsMySlider : public CSliderCtrl
{
    DECLARE_DYNAMIC(clsMySlider)

public:
    clsMySlider(CWnd* parent, CMFCToolBar* toolbar);
    virtual ~clsMySlider();

protected:
    DECLARE_MESSAGE_MAP()

    CWnd* m_pParent;
    CMFCToolBar* m_pToolbar;
public:
    afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
    afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
};
class clsMySliderButton : public CMFCToolBarButton
{
public:
    clsMySliderButton ();
    virtual ~clsMySliderButton ();

public:
    CWnd* GetParentWnd();
    
    virtual BOOL CanBeDropped(CMFCToolBar* pToolbar) { return FALSE; }//自定义模式下用户无法调整按钮位置
    virtual BOOL CanBeStored() const { return FALSE; }//按钮无法作为拖放操作的一部分存储
    virtual BOOL CanBeStretched() const { return TRUE; }//自定义模式下按钮可拉伸
    virtual HWND GetHwnd();
    virtual const CRect GetInvalidateRect() const;
    virtual BOOL HaveHotBorder() const { return TRUE; }//选中按钮时不显示边框
    virtual BOOL OnBeforeDrag() const { return FALSE; }//禁用按钮拖拽
    virtual SIZE OnCalculateSize(CDC* pDC, const CSize& sizeDefault, BOOL bHorz);
    virtual void OnDraw(CDC* pDC,const CRect& rect,CMFCToolBarImages* pImages,BOOL bHorz = TRUE,BOOL bCustomizeMode = FALSE,BOOL bHighlight = FALSE,BOOL bDrawBorder = TRUE,BOOL bGrayDisabledButtons = TRUE);
    //virtual BOOL OnGetCustomToolTipText(CString& strToolTip);
    virtual void OnSize(int iSize);
    virtual BOOL SetACCData(CWnd* pParent, CAccessibilityData& data) { return FALSE; }//工具栏按钮不提供辅助功能数据
    virtual void SetStyle(UINT nStyle);
    BOOL Initialize(CRect r, CWnd* pParent, CMFCToolBar* pToolbar);
    virtual HBRUSH OnCtlColor(CDC* pDC, UINT nCtlColor)
    {
        CBrush b;
        b.CreateSolidBrush(NULL_BRUSH);
        return (HBRUSH)b.GetSafeHandle();
    }
    virtual BOOL OnUpdateToolTip(CWnd* pWndParent, int iButtonIndex, CToolTipCtrl& wndToolTip, CString& str)
    {
        if (!m_pSlider || iButtonIndex!=10)
            return FALSE;
        str.Format(L"Current Value=%d", m_pSlider->GetPos());
        return TRUE;
    }
    virtual BOOL OnToolHitTest(const CWnd* pWnd,TOOLINFO* pTI);
    virtual BOOL IsOwnerOf(HWND hwnd) 
    {
        if (!m_pSlider)
            return FALSE;
        return hwnd == m_pSlider->GetSafeHandle(); 
    }
    virtual void OnChangeParentWnd(CWnd* pWndParent)
    {

    }
private :
    clsMySlider* m_pSlider;
    CMFCToolTipCtrl* m_pTooltip;
    CWnd* m_pParent;

};
//在CMainFrame::OnCreate中
//CMFCToolBar       m_wndToolBar;
//clsMySliderButton m_ZoomSlider;
       CRect r;
    m_wndToolBar.GetItemRect(10, &r);
    r.right += r.Width() * 3;
    m_ZoomSlider.Initialize(r, this, &m_wndToolBar);
    m_wndToolBar.ReplaceButton(10, m_ZoomSlider);
    m_wndToolBar.UpdateButton(10);

解决方案

一、修复滑块滚动消息不触发的问题

1. 补全消息映射实现

clsMySlider的消息映射必须在.cpp文件中明确实现,否则重载的OnHScroll不会被系统调用:

IMPLEMENT_DYNAMIC(clsMySlider, CSliderCtrl)

BEGIN_MESSAGE_MAP(clsMySlider, CSliderCtrl)
    ON_WM_HSCROLL()
    ON_WM_VSCROLL()
END_MESSAGE_MAP()

2. 调整消息传递逻辑

放弃自定义消息WM_SLIDER_HSCROLL,改用MFC原生消息传递机制,或者通过按钮类转发消息:

  • 在clsMySlider中添加clsMySliderButton* m_pOwnerButton成员,构造时传入按钮指针
  • 修改OnHScroll方法,让按钮作为中间层通知主框架:
void clsMySlider::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
    CSliderCtrl::OnHScroll(nSBCode, nPos, pScrollBar);
    if (m_pOwnerButton && m_pOwnerButton->GetParentWnd())
    {
        // 向主框架发送滚动消息,携带滑块ID和当前位置
        m_pOwnerButton->GetParentWnd()->PostMessage(WM_HSCROLL, MAKELONG(nSBCode, GetDlgCtrlID()), (LPARAM)GetSafeHwnd());
    }
}

3. 确认主框架消息映射

在CMainFrame.cpp中确保OnHScroll的消息映射已注册:

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWndEx)
    ON_WM_HSCROLL()
    // 其他消息映射...
END_MESSAGE_MAP()

二、实现滑块Tooltip实时显示

1. 修正Tooltip更新逻辑

去掉OnUpdateToolTip中硬编码的按钮索引判断,适配多个滑块场景:

virtual BOOL OnUpdateToolTip(CWnd* pWndParent, int iButtonIndex, CToolTipCtrl& wndToolTip, CString& str)
{
    if (!m_pSlider)
        return FALSE;
    str.Format(L"当前值=%d", m_pSlider->GetPos());
    return TRUE;
}

2. 关联滑块与Tooltip控件

在clsMySliderButton::Initialize中,手动将滑块注册到工具栏的Tooltip控件:

BOOL clsMySliderButton::Initialize(CRect r, CWnd* pParent, CMFCToolBar* pToolbar)
{
    m_pParent = pParent;
    // 给滑块分配唯一ID,比如IDC_SLIDER_1、IDC_SLIDER_2
    m_pSlider = new clsMySlider(pParent, pToolbar);
    m_pSlider->Create(TBS_HORZ | WS_VISIBLE | WS_CHILD, r, pToolbar, IDC_SLIDER_1);

    // 启用工具栏Tooltip并注册滑块
    pToolbar->EnableToolTips(TRUE);
    CToolTipCtrl& tooltip = pToolbar->GetToolTipCtrl();
    
    TOOLINFO ti = {0};
    ti.cbSize = sizeof(TOOLINFO);
    ti.hwnd = pToolbar->GetSafeHwnd();
    ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
    ti.uId = (UINT_PTR)m_pSlider->GetSafeHwnd();
    ti.lpszText = LPSTR_TEXTCALLBACK;
    tooltip.SendMessage(TTM_ADDTOOL, 0, (LPARAM)&ti);

    return TRUE;
}

3. 实时更新Tooltip文本

在滑块滚动时,主动触发Tooltip更新:

void clsMySlider::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
    CSliderCtrl::OnHScroll(nSBCode, nPos, pScrollBar);
    // ...其他消息传递逻辑
    // 更新Tooltip
    if (m_pOwnerButton)
    {
        CMFCToolBar* toolbar = dynamic_cast<CMFCToolBar*>(m_pOwnerButton->GetParentWnd());
        if (toolbar)
        {
            toolbar->GetToolTipCtrl().UpdateTipText(L"", this);
        }
    }
}

三、其他注意事项

  • 每个滑块必须分配唯一的控件ID,避免与其他控件冲突
  • 确保clsMySliderButton::IsOwnerOf能正确识别滑块窗口,防止消息被工具栏拦截
  • 滑块创建时的父窗口必须设为工具栏,保证消息能正常向上传递

内容的提问来源于stack exchange,提问作者R1349

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 03:35:56