在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
相关产品推荐
相关产品推荐

