Chinaunix首页 | 论坛 | 博客
  • 博客访问: 9428298
  • 博文数量: 1227
  • 博客积分: 10026
  • 博客等级: 上将
  • 技术积分: 20273
  • 用 户 组: 普通用户
  • 注册时间: 2008-01-16 12:40
文章分类

全部博文(1227)

文章存档

2010年(1)

2008年(1226)

我的朋友

分类: C/C++

2008-04-23 22:02:39

简简单单:三个函数实现框架菜单自绘

作者:


  在看到的自绘菜单都是派生出一个新类,其实不用这么麻烦,添加三个函数即可实现框架菜单自绘,方便简单,易于维护。
  在MFC中,如果菜单带有MF_OWNERDRAW标志,程序就会调用OnDrawItem和OnMeasureItem函数来绘制菜单。
下面就让我们来动手吧!首先在CMainFrame响应三个消息,分别是:

WM_DRAWITEM:绘制菜单的样式

WM_MEASUREITEM:指定要绘制菜单的大小

WM_INITMENU:把框架菜单全部改成带MF_OWNERDRAW标志

  下面我帖出这三个函数代码,你不想改的话,把这三个函数的代码复制你的程序,编译一下看看你的程序菜单是不是变得很漂亮:)


void CMainFrame::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct) 

{

	LPMEASUREITEMSTRUCT& lpM=lpMeasureItemStruct;  //起个别名,好用一点

	if(lpM->CtlType==ODT_MENU){			  //判断是不是菜单要自绘

  	if(lpM->itemID!=ID_SEPARATOR)     //分别设定普通菜单和分隔栏的大小	

  	{

			lpM->itemHeight=20;    //分隔栏大小		

			lpM->itemWidth=150;	

  	}	

  	else	

 		{		

			lpM->itemHeight=1;     //普通菜单大小		

			lpM->itemWidth=150;	

		}

	}

}



void CMainFrame::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct) 

{

 	LPDRAWITEMSTRUCT& lpD=lpDrawItemStruct;  //起个别名,好用一点



	//判断是不是菜单自绘,因为按钮也可以自绘

	if(nIDCtl==0)							 

	{

		CDC* pDC=CDC::FromHandle(lpD->hDC);  //得到菜单的设备指针,用来绘制菜单

		pDC->SetBkMode(TRANSPARENT);

		CMenu menu;

		menu.Attach((HMENU)lpD->hwndItem);   //得到框架菜单对象



		CRect AllRgn(lpD->rcItem);			  //得到当前绘制的菜单选项项大小

		CRect FrontRgn(AllRgn.left,AllRgn.top,20,AllRgn.bottom);

		CBrush brushAll(RGB( 250,250,250 ));     //初始化画刷

		CBrush brushFront(RGB( 230,230,230 ));

		CBrush brushSel(RGB(148,170,214 ));

		CString strText;

  		menu.GetMenuString(lpD->itemID,strText,MF_BYCOMMAND);  //得到当前绘制的菜单选项文本

  

	  	if(lpD->itemID!=ID_SEPARATOR)   //菜单和分隔栏分别绘制

  		{

		      	if(lpD->itemAction & ODA_SELECT) //菜单选中时的样式

		      	{

	  			pDC->FillRect(AllRgn,&brushSel);   //绘制

	  			if(lpD->itemState &ODS_GRAYED) 

					//设定文本颜色(在最后才绘制出来)

	  				pDC->SetTextColor(RGB(194,194,194)); 

	  			else if(lpD->itemState & ODS_SELECTED)

  					pDC->SetTextColor(RGB(250,250,250));

  			}



			//菜单非选中时的样式

  			if(!((lpD->itemAction & ODA_SELECT) && (lpD->itemState & ODS_SELECTED)))  

  			{	

	  			pDC->FillRect(AllRgn,&brushAll);   //绘制

  				pDC->FillRect(FrontRgn,&brushFront);

  				if(lpD->itemState & ODS_GRAYED) 

	  				pDC->SetTextColor(RGB(194,194,194 )); 

  				else

					pDC->SetTextColor(RGB(66,110,180 )); 

	  		}	

		}

		else 

		  	pDC->FillRect(AllRgn,&brushFront);  //绘制分隔栏



		pDC->TextOut(AllRgn.left 30,AllRgn.top 5,strText);  //打印出字体

		menu.Detach();//分隔菜单句柄和对象(必要!)

	}

}



void CMainFrame::OnInitMenu(CMenu* pMenu) 

{

  	CMenu *pSubMenu;

  	UINT nCount,nSubCount,nID;

  	CString strText;

		nCount=pMenu->GetMenuItemCount();

  	for(UINT i=0;iGetSubMenu(i);

  		nSubCount=pSubMenu->GetMenuItemCount();

  		for(UINT j=0;jGetMenuItemID(j);



			//将框架菜单所有菜单都添加MF_OWNERDRAW标志

  			pSubMenu->ModifyMenu(j,MF_BYPOSITION|MF_OWNERDRAW,nID);  

  			pSubMenu->GetMenuString(j,strText,MF_BYPOSITION);

  		}	

  	}

}		
这样,你的程序就拥有了一个漂亮的菜单:) 这只是个初板而已。

建议改进:因为每次弹出菜单的时候都调用OnInitMenu,本来已改好的菜单就不必再改了,在OnInitMenu加一个全部变量标识菜单是否改好了,避免重复的修改菜单。那当然也可以在OnCreate中修改,不过你要确定你的菜单没有再添加新选项了。

缺点:不清楚为什么对"最近文件"那项不起作用,知道的还望告诉我一下。对子菜单的弹出菜单没有修改MF_OWNERDRAW,不过你可以增加一点代码遍历一下就OK了。这样一个简单菜单换肤就完成了,^_^
(参考了VCK的一些资料)

本文参考了 的一些资料,以及 MSDN 库:只列出部分
void CMainFrame::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct) 



lpMeasureItemStruct 是指向MEASUREITEMSTRUCT结构体的指针,其成员变量

UINT CtlType; // 要绘制的类型

UINT itemID; // 菜单选项ID

UINT itemWidth; //菜单选项宽度

UINT itemHeight; //菜单选项高度



void CMainFrame::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)



lpDrawItemStruct 是指向DRAWITEMSTRUCT结构体的指针,其成员变量

UINT CtlType; // 要绘制的类型

UINT itemID; // 菜单选项ID

UINT itemAction; // 菜单动作

UINT itemState; // 菜单选项的当前状态

HWND hwndItem; // 顶层菜单的句柄 

HDC hDC; // 绘制设备DC 

RECT rcItem; // 菜单选项的大小

DWORD itemData; // 附加自定义数据,由AppendMenu或InsertMenu或ModifyMenu的lpszNewItem指定

 

阅读(494) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~