【mfc】重写一个带有下拉菜单的工具栏类
CToolBarEx 可以方便地创建一个带有悬浮提示的工具栏,并在按钮上显示相关的提示信息,同时可以给按钮项添加下拉菜单。
头文件部分:
// ToolBarEx.h
#pragma once
#include <afxtoolbar.h>
#include <afxwin.h>
#include <map>
class CToolBarEx : public CToolBar
{
public:
CToolBarEx();
virtual ~CToolBarEx();
void AddButton(int nID, const CString& strName, const CString& strTip);
void AddDropDownItem(int nParentID, int nID, const CString& strName, const CString& strTip);
void Apply();
protected:
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
virtual BOOL PreTranslateMessage(MSG* pMsg) override;
DECLARE_MESSAGE_MAP()
private:
std::vector<int> _tbButtonId; // 存储按钮的ID
std::vector<CString> _tbButtonName; // 存储按钮的名称
std::vector<CString> _tbButtonTip; // 存储按钮的提示信息
std::map<int, CMenu> _tbDropDownMenu; // 存储下拉菜单的父按钮ID和菜单对象
CToolTipCtrl m_toolTipCtrl; // 工具提示控件对象
void CreateDropDownMenu(); // 创建下拉菜单
void ShowDropDownMenu(int nButtonIndex); // 显示下拉菜单
int HitTest(const CPoint& point) const; // 判断鼠标位置是否在按钮上
bool m_bDropDownMenuVisible; // 下拉菜单是否可见
};
.cpp部分
// ToolBarEx.cpp
#include "pch.h" // #include "stdafx.h"
#include "ToolBarEx.h"
BEGIN_MESSAGE_MAP(CToolBarEx, CToolBar)
ON_WM_MOUSEMOVE()
ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()
CToolBarEx::CToolBarEx()
: m_bDropDownMenuVisible(false)
{
}
CToolBarEx::~CToolBarEx()
{
}
void CToolBarEx::AddButton(int nID, const CString& strName, const CString& strTip)
{
_tbButtonId.push_back(nID);
_tbButtonName.push_back(strName);
_tbButtonTip.push_back(strTip);
}
void CToolBarEx::AddDropDownItem(int nParentID, int nID, const CString& strName, const CString& strTip)
{
// 如果父按钮对应的下拉菜单不存在,则创建一个
if (!_tbDropDownMenu.count(nParentID))
{
CMenu menu;
menu.CreatePopupMenu();
_tbDropDownMenu[nParentID].Detach();
_tbDropDownMenu[nParentID].Attach(menu.Detach());
}
// 在父按钮的下拉菜单中添加菜单项
_tbDropDownMenu[nParentID].AppendMenu(MF_STRING, nID, strName);
_tbButtonTip.push_back(strTip);
}
void CToolBarEx::Apply()
{
// 设置按钮和菜单项
const UINT* nBtnId = reinterpret_cast<const UINT*>(_tbButtonId.data());
SetButtons(nBtnId, static_cast<int>(_tbButtonId.size()));
for (size_t i = 0; i < _tbButtonId.size(); ++i)
{
SetButtonText(static_cast<int>(i), _tbButtonName[i]);
}
SetSizes(CSize(90, 30), CSize(16, 16));
// 创建并设置工具提示控件
if (m_toolTipCtrl.GetSafeHwnd() == nullptr)
{
m_toolTipCtrl.Create(this, TTS_ALWAYSTIP);
}
for (size_t i = 0; i < _tbButtonId.size(); ++i)
{
CRect rect;
GetItemRect(static_cast<int>(i), &rect);
m_toolTipCtrl.AddTool(this, _tbButtonTip[i], rect, _tbButtonId[i]);
}
CreateDropDownMenu(); // 创建下拉菜单
}
void CToolBarEx::OnMouseMove(UINT nFlags, CPoint point)
{
int nButtonIndex = HitTest(point);
if (nButtonIndex != -1 && _tbDropDownMenu.count(_tbButtonId[nButtonIndex]))
{
if (!m_bDropDownMenuVisible)
{
m_bDropDownMenuVisible = true;
ShowDropDownMenu(nButtonIndex);
}
}
else
{
if (m_bDropDownMenuVisible)
{
bool bPressedButton = false;
for (int i = 0; i < _tbButtonId.size(); ++i)
{
if (GetButtonStyle(i) == TBBS_PRESSED)
{
bPressedButton = true;
break;
}
}
if (!bPressedButton)
{
m_bDropDownMenuVisible = false;
// 隐藏所有按钮的下拉菜单
for (auto& dropDownMenu : _tbDropDownMenu)
{
int nButtonIndex = GetToolBarCtrl().CommandToIndex(dropDownMenu.first);
if (nButtonIndex >= 0)
{
SetButtonStyle(nButtonIndex, TBBS_BUTTON);
}
}
}
}
}
CToolBar::OnMouseMove(nFlags, point);
}
void CToolBarEx::OnLButtonDown(UINT nFlags, CPoint point)
{
int nButtonIndex = HitTest(point);
if (nButtonIndex != -1 && _tbDropDownMenu.count(_tbButtonId[nButtonIndex]))
{
if (!m_bDropDownMenuVisible)
{
SetButtonStyle(nButtonIndex, TBBS_PRESSED);
m_bDropDownMenuVisible = true;
ShowDropDownMenu(nButtonIndex);
}
}
else
{
int nPressedButtonIndex = -1;
for (int i = 0; i < _tbButtonId.size(); ++i)
{
if (GetButtonStyle(i) == TBBS_PRESSED)
{
nPressedButtonIndex = i;
break;
}
}
if (nPressedButtonIndex != -1)
{
SetButtonStyle(nPressedButtonIndex, TBBS_BUTTON);
m_bDropDownMenuVisible = false;
}
CToolBar::OnLButtonDown(nFlags, point);
}
}
void CToolBarEx::CreateDropDownMenu()
{
// 创建下拉菜单
for (const auto& dropDownMenu : _tbDropDownMenu)
{
CMenu menu;
menu.CreatePopupMenu();
for (UINT i = 0; i < dropDownMenu.second.GetMenuItemCount(); ++i)
{
CString strMenuName;
UINT nID = dropDownMenu.second.GetMenuItemID(i);
dropDownMenu.second.GetMenuString(i, strMenuName, MF_BYPOSITION);
menu.AppendMenu(MF_STRING, nID, strMenuName);
}
int nButtonIndex = GetToolBarCtrl().CommandToIndex(dropDownMenu.first);
if (nButtonIndex >= 0)
{
CRect rect;
GetItemRect(nButtonIndex, &rect);
ClientToScreen(&rect);
_tbDropDownMenu[nButtonIndex].Detach();
_tbDropDownMenu[nButtonIndex].Attach(menu.Detach());
}
}
}
void CToolBarEx::ShowDropDownMenu(int nButtonIndex)
{
CRect rect;
GetItemRect(nButtonIndex, &rect);
ClientToScreen(&rect);
_tbDropDownMenu[_tbButtonId[nButtonIndex]].TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON, rect.left, rect.bottom, AfxGetMainWnd());
}
int CToolBarEx::HitTest(const CPoint& point) const
{
CRect rect;
for (int i = 0; i < _tbButtonId.size(); ++i)
{
GetItemRect(i, &rect);
if (rect.PtInRect(point))
{
return i;
}
}
return -1;
}
BOOL CToolBarEx::PreTranslateMessage(MSG* pMsg)
{
if (m_toolTipCtrl.GetSafeHwnd() != nullptr)
{
m_toolTipCtrl.RelayEvent(pMsg);
}
return CToolBar::PreTranslateMessage(pMsg);
}
示例:
// 创建一个派生自CFrameWnd的主窗口类(例如CMainFrame)
class CMainFrame : public CFrameWnd
{
public:
CMainFrame()
{
// 创建并添加ToolBarEx对象
m_toolBar.Create(this, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS);
m_toolBar.AddButton(ID_BUTTON1, _T("Button 1"), _T("Tooltip for Button 1"));
m_toolBar.AddButton(ID_BUTTON2, _T("Button 2"), _T("Tooltip for Button 2"));
m_toolBar.AddDropDownItem(ID_BUTTON2, ID_MENU_ITEM1, _T("Menu Item 1"), _T("Tooltip for Menu Item 1"));
m_toolBar.Apply();
// 将ToolBarEx对象附加到主窗口的工具栏
AddToolBar(&m_toolBar);
}
private:
CToolBarEx m_toolBar;
};
本文作者:香菇0_0
本文链接:https://www.cnblogs.com/Xiang-gu/p/18601776
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步