[mfc] Rewrite a toolbar class with a drop-down menu

        CToolBarEx can easily create a toolbar with floating prompts, display relevant prompt information on the buttons, and add drop-down menus to button items.

Header file part:

// 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 section

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

Example:

// 创建一个派生自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;
};

Guess you like

Origin blog.csdn.net/qq_42608732/article/details/130755713
Recommended