CMenu自绘

本文介绍了如何创建CMenu的派生类,并详细讲解了如何重写DrawItem和MeasureItem函数,实现菜单项的自定义绘制效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、创建一个CMenu类的派生类,并重写DrawItem(LPDRAWITEMSTRUCT lpDS)、MeasureItem(LPMEASUREITEMSTRUCT lpMS)函数:

如下:

#pragma once
#include "afxwin.h"
class CMyMenu :public CMenu
{
public:
	CMyMenu(void);
	~CMyMenu(void);
private:
	struct SItem{
		HICON hIcon;
		char szText[32];
		int nHeight;
	};
	CMap<WORD,WORD,SItem,SItem> m_map;//菜单Id与对应的节点信息


public:
	virtual void DrawItem(LPDRAWITEMSTRUCT lpDS);
	virtual void MeasureItem(LPMEASUREITEMSTRUCT lpMS);
	BOOL AppendMenu(UINT_PTR nIDNewItem = 0,LPCTSTR lpszNewItem = NULL ,HICON hIcon = NULL,UINT nFlags = 0);
};


#include "StdAfx.h"
#include "MyMenu.h"


CMyMenu::CMyMenu(void)
{
}


CMyMenu::~CMyMenu(void)
{
}


void CMyMenu::DrawItem(LPDRAWITEMSTRUCT lpDS)
{
	CDC* pDC = CDC::FromHandle(lpDS->hDC);

	CRect rect = lpDS->rcItem;

	CString strText;

	SItem &sItem = m_map[lpDS->itemID];

	if(lpDS->itemID) strText = sItem.szText;

	//选中状态菜单项:画边线和浅蓝色填充
	if((lpDS->itemState & ODS_SELECTED)&&(lpDS->itemAction & ODA_SELECT))
	{
		pDC->FillSolidRect(&rect,::GetSysColor(COLOR_MENU));
		rect.DeflateRect(1,1);
		CPen pen(PS_SOLID,1,GetSysColor(COLOR_HIGHLIGHT));
		CBrush br(RGB(182,189,210));
		pDC->SelectObject(&pen);
		pDC->SelectObject(br);
		pDC->Rectangle(rect);
	}
	else//非选中状态:普通背景填充
	{
		pDC->FillSolidRect(&rect,GetSysColor(COLOR_MENU));

		if (rect.Height() == 10)//如果是分隔条菜单:画两条线
		{
			CPen pen1(PS_SOLID,1,GetSysColor(COLOR_BTNSHADOW));
			pDC->SelectObject(&pen1);
			pDC->MoveTo(rect.left+2,rect.top+4);
			pDC->LineTo(rect.right-2,rect.top+4);

			CPen pen2(PS_SOLID,1,GetSysColor(COLOR_HOTLIGHT));
			pDC->SelectObject(&pen2);
			pDC->MoveTo(rect.left+2,rect.top+5);
			pDC->LineTo(rect.right-2,rect.top+5);

			return;
		}
		rect.DeflateRect(1,1);
	}

	//画图标
	pDC->DrawIcon(rect.left+1,rect.top+1,sItem.hIcon);

	pDC->SetBkMode(TRANSPARENT);
    rect.left+=35;
	pDC->DrawText(strText,rect,DT_LEFT|DT_VCENTER|DT_SINGLELINE);
	
}


void CMyMenu::MeasureItem(LPMEASUREITEMSTRUCT lpMS)
{
	lpMS->itemWidth = 80;//菜单项的宽度

	lpMS->itemHeight = m_map[lpMS->itemID].nHeight;//每个菜单项的高度
}

BOOL CMyMenu::AppendMenu(UINT_PTR nIDNewItem,LPCTSTR lpszNewItem,HICON hIcon,UINT nFlags)
{
	//将菜单的文字和图标信息按照ID进行登记,以便自绘时使用

	SItem sItem = {hIcon};

	if(lpszNewItem) strcpy(sItem.szText,lpszNewItem);

	if(nFlags & MF_SEPARATOR) sItem.nHeight = 10;
	else sItem.nHeight = 35;

	//根据ID添加到map中
	m_map[nIDNewItem] = sItem;

	//每个插入的菜单项都自动添加自绘属性
	return CMenu::AppendMenu(nFlags|MF_OWNERDRAW,nIDNewItem,lpszNewItem);

}

2、在主对话框的视图类的OnContextMenu(CWnd* pWnd, CPoint point)函数中进行弹出式菜单的操作:

void CMenu自绘View::OnContextMenu(CWnd* pWnd, CPoint point)
{
	CMyMenu Menu;
	Menu.CreatePopupMenu();

	HICON hIcon = AfxGetApp()->LoadIcon(IDI_ICON1);
	Menu.AppendMenu(ID_EDIT_UNDO,"撤销",hIcon);

	//hIcon = AfxGetApp()->LoadIcon(IDI_ICON2);
	Menu.AppendMenu(ID_EDIT_CUT,"剪切",hIcon);

	//hIcon = AfxGetApp()->LoadIcon(IDI_ICON3);
	Menu.AppendMenu(ID_FILE_SAVE,"保存",hIcon);

	//hIcon = AfxGetApp()->LoadIcon(IDI_ICON4);
	Menu.AppendMenu(ID_FILE_SAVE_AS,"另存为",hIcon);

	Menu.AppendMenu(0,NULL,0,MF_SEPARATOR);

	Menu.TrackPopupMenu(TPM_LEFTALIGN,point.x,point.y,this);
}

如图:


//所有的菜单(包括顶层框架菜单与弹出式菜单)项均会触发OnInitMenu函数,分以下几种情况: // 一 顶层框架菜单:就是文件,编辑等这种菜单,框架菜单在主窗口显示后并不会自动调用OnInitMenu函数, // 必须手动设置框架菜单的自标志,否则在主窗口显示后框架菜单仍然为非自状态 // 二 系统菜单:这种情况比较特殊,需要注意,分以下几种情况: // (1) 主窗口显示时在标题栏上点右键弹出的菜单,此时进入到OnInitMenu函数中后,通过遍历发现 // 这些菜单项仍然是框架菜单项,也就是文件,编辑等,而不是最小化,最大化等系统菜单。 // (2) 主窗口未最小化时(不管主窗口是前台窗口还是非前台窗口),在任务栏的程序按钮上点右键弹出的菜单, // 此时进入到OnInitMenu函数中后,通过遍历发现这些菜单项仍然是框架菜单项,也就是文件,编辑等,而不是最小化,最大化等系统菜单。 // (3) 主窗口最化小时,在任务栏的程序按钮上点右键弹出的菜单,此时进入到OnInitMenu函数中后,发现该菜单才是系统菜单,最小化,最大化等。 // 三 弹出式菜单:所有弹出式菜单都会触发OnInitMenu函数处理 //因为框架菜单已经在OnInitMenu函数之前进行了强制设置自标志,所以这里不再考虑是否将框架菜单进行过滤掉,当进入到ModifyMenuStyle函数中后,会发现该 //菜单已经设置了自标志,就不再处理了。 //这里只判断是否系统菜单,并对其进行过滤,不自系统菜单。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小米的修行之路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值