以下是小编精心整理的简简单单:三个函数实现框架菜单自绘,本文共3篇,供大家参考借鉴,希望可以帮助到有需要的朋友。

篇1:简简单单:三个函数实现框架菜单自绘
简简单单:三个函数实现框架菜单自绘
作者:无心_所爱
在VCKBASE看到的自绘菜单都是派生出一个新类,其实不用这么麻烦,添加三个函数即可实现框架菜单自绘,方便简单,易于维护,
在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 ));} } elsepDC->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的一些资料)
本文参考了 VCKBASE 的一些资料,以及 MSDN 库:只列出部分
void CMainFrame.:OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruc
篇2:完美实现真彩自绘菜单
完美实现真彩自绘菜单
作者:阿福(geforce_zf)
下载源代码
一、提出问题
在VCKBASE上读到《自绘菜单的实现》[作者:querw],应用的我自己的正在进行的工程后发现效果不错,可是有存在许多问题。整个类的设计方面存在很多缺陷(先天,后天的),存在的主要问题如下:
当应用在多文档界面(MDI)中的时候,无法对系统自动添加菜单和文档模板菜单进行自绘(比如无法对文件->最近文件(MRU)菜单项中的文件列表就是系统自动添加)。原因是类内部没有对CMainFrame.:OnInitPopupMenu()消息进行处理的函数, 因此不具备修改系统自动添加菜单项的功能。(BCMENU有这功能,而且工作的不错)
作者提到的 BCMENU 不用映射 WM_DRAWITEM 和 WM_MEASUREITEM 两个消息就能实现自画功能,实际上是错误的。不映射这两个重要的消息,即使能自绘,也是有问题的,不信看图。
菜单编辑器中的模菜单样
使用BCMENU并且映射了这两个消息后的执行情况
使用BCMENU没有映射两个消息的执行情况
原作者分析的自绘的是因为把主菜单(top-level menu)的子菜单都加载成弹出菜单(popupmenu),是不正确的。真正的原因是因为MFC框架会自动调用CMenu的两个虚拟函数MeasureItem()和OnDrawItem()。 因此,当CMenuEx派生于CMenu,并且重写这两个虚拟函数以后。
1、MFC框架调用的GetMenu()->MeasureItem()就相当于调用了CMenuEx::MeasureItem(),从而实现自绘菜单控件尺寸的测量。2、MFC框架调用GetMenu()->DrawItem()就相当于调用了CMenuEx::DrawItem()来实现自绘菜单控件的自绘操作(不懂??,这正是C++的虚拟的妙用,指向派生类对象的基类指针可以调用派生类的虚拟函数,多么伟大的发明,谁想出来的???)。与子菜单是否为弹出菜单(popupmenu)没有什么关系,
以下是摘自WINCORE.CPP的一段程序,也就是WM_MEASUREITEM消息的默认流向的地方,相信大家会从中看出一些端倪。
void CWnd::OnMeasureItem(int /*nIDCtl*/, LPMEASUREITEMSTRUCT lpMeasureItemStruct){ if (lpMeasureItemStruct->CtlType == ODT_MENU) { ...... // 如果没有主菜单 if (pThreadState->m_hTrackingWindow == m_hWnd) { ...... } else { // 如果有主菜单 pMenu = GetMenu(); // 找到窗体的主菜单,注意,pMenu的是CMenu* 类型 } // 在当前菜单中寻找ID匹配的菜单项 pMenu = _AfxFindPopupMenuFromID(pMenu, lpMeasureItemStruct->itemID); if (pMenu != NULL)// 如果找到,就调用MeasureItem() // 这就是所谓的基类指针指向派生类对象,可以调用派生类虚拟函数的情况了 pMenu->MeasureItem(lpMeasureItemStruct); else TRACE1(“Warning: unknown WM_MEASUREITEM for menu item 0x%04X.\n”, lpMeasureItemStruct->itemID); } else { ...... } ......}
当菜单项中含有子菜单(submenu),而不含有分割条的时候,子菜单项的高度不可调。原因为原CMenuEx程序中将分割条的原COMMAND ID(0)改为菜单项的COMMADN ID(-1), 以欺骗MFC框架调用CMenuEx::MeasureItem()来计算子菜单项(submenu)的高度。(很令我失望,这也是促使我自己动手重写该类的原因之一。不信看程序,看图)
摘录自原CMenuEx.cpp第546-560行
if(uID == 0) //分隔符{ ::AppendMenu(hNewMenu,MF_SEPARATOR,0,NULL); ...... // 注意,就是下面那个-1,把分割条的ID从0改到-1, // 从而是MFC框架误以为找到了ID为-1的菜单项,并且测量了它的尺寸 // 而实际上ID为-1的菜单项是不可能被void CWnd::OnMeasureItem()找到的 ::ModifyMenu(hNewMenu,i,MF_BYPOSITION | MF_OWNERDRAW,-1,(LPCTSTR)pMenuItem);}
菜单编辑器中没有分割条菜单的菜单
原CMenuEx执行的模样
篇3:用VC++实现自绘按钮控制.net
四川 曾 志 Microsoft Windows 以其丰富一致的图形用户界面,简单灵便的操作,被广大用户所接受,对话框就是其中一个非常重要的界面形式,并且Windows为其应用程序的 开发 者提供了相当丰富的界面资源,许多控制可以方便地加入对话框中。 但当我们需要编写一
四川 曾 志
MicrosoftWindows以其丰富一致的图形用户界面,简单灵便的操作,被广大用户所接受。对话框就是其中一个非常重要的界面形式,并且Windows为其应用程序的开发者提供了相当丰富的界面资源,许多控制可以方便地加入对话框中。但当我们需要编写一个商业应用,需要一个更友好的图形用户界面时,就会发现资源的贫乏。
Visual C++提供了一个CBitmap Button类在一定程序上缓解了增加新资源的要求。但当我们要求在程序中能动态地改变图形时,我们就不得不考虑放弃CBitmapButton类,而坐下来自己做一点工作了。
一个可行的办法就是重载CButton类(即按钮控制)。这样对这块区域的大小、位置和ID号都可以通过AppStadio方便地制作。当然这样作最大的好处还是在于充分利用C++的特性把绘制控制的代码封装了起来,既避免了重复性开发,又使程序变得更简洁,增强了代码的可读性。
现在我们就通过创建一个名叫CColorButton的类来学习这种方法。虽然这是一个功能非常简单的类,通过调用成员函数ChangeColor可改变其颜色,鼠标点中时则高亮边框表示选中。但利用这种思路我们只需添加几个数据成员和重载一个函数就可以方便地实现你所需要的功能。
在创建这个类之前,我们必须先了解WM_DRAWITEM消息。当按钮、组合框、列表框或菜单的某一视觉状况发生变化时,系统就会发送一条WM_DRAWITEM消息给这些控制的拥有者窗口。这个消息的wParam指出这个控制的 id 号,而IParam则是一个指向DRAWITEMSTRUCT结构的指针,该结构存放有关要绘制的项的信息以及绘制所需的类型。DRAWITEM?STRUCT结构具有如下格式。
typedef struct tagDRAWITEM?STRUCT{
UINT CtlType; // 控制类型
UINT CtlID;// 控制的ID号
UNIT itemID;//菜单项的索引
UINT itemAction;// 说明需要的绘图操作
UINT itemState; // 指明绘图后的可见状态
HWND hwndItem; // 控制的窗口句柄
HDC hDC; // 相关的设备环境
RECT rcItem;//被画控制的边框
DWORD itemData;// 指定与菜单项相联系的应用程序定义的32位值
}DRAWITEMSTRUCT;
其中itemAction 和 itemState决定了需要的绘图操作。itemAction 说明需要的绘图操作,可为下列值中的一个或多个;
值 含 义
ODA_DRAWENTIRE 需要重来全部控制时
ODA_FOCUS 获得或失去输入焦点
ODA_SELECT 选择状态改变
itemState指明当前绘图动作发生之后,项的可见状态。下面是状态标志:
值 含 义
ODS_CHECKD 只用于菜单中
ODS_DISABLE 该项被屏蔽
ODS_FOCUS 该项具有输入焦点
ODS_GRAYED 只用于菜单中
ODS_SELECT 该项处于被选中状态
(上) □成都 曾志
用VC++实现自绘按钮控制
利用VC++编程会发现,当按钮控制接收到WM-DRMAWITEM消息时会调用Cbotton类的DrawItem函数。因此我们要做的就是利用C++的多态性通过重载CButton类的Drawitem函数来响应MW-DRAWITEM消息。
下面我们就实际构造一个CCol?orButton类。
class ccolorButton:public CButton
{ private:
COLORREF m-color:
public:
CColorButton():CButton(),m-color(0){}; //构造函数
void ChangeColor(COLORREF color); //改变颜色
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct
);//重载的函数
};
//重载的虚函数
void CColorButton::DrawItem(LPDRAWITEMSTRUCT lpDrawIt
emtruct)
{
CDC dc;
dc.Attach(lpDrawItemStruct->hDC); //得到绘制的设备环境CDC
VERIFY( lpDrawItemStruct->CtlType==ODT-BUTTON);
if (lpDrawItemStruct->itemAction & ODA-DRAWENTIRE)
{
//重绘整个控制
CBrush brush(m-Color);
dc.FillRect(&(lpDrawItemstruct->reItem),&brush)
;
} if ((lpDrawItemStruct->itemstate & ODS-SELECTED
) &&
(lpDrawItemStruct->itemAction &
(ODA-SELECT | ODA-DRAWENTIRE))) { //选中了本控制===>高亮边框
COLORREF fc=RGB(255-GetRvalue(m-color), 255-GetG
Value(m-color), 255-GetBValue(m-color));
CBrush brush(fc);
dc.FrameRect(&(lpDrawItemStruct->rcItem),&brush
);
} if (!(lpDrawItemStruct->itemState & ODS-SELECT
ED) &&
(lpDrawItemStruct->itemAction & ODA-SELECT)){
//控制的选中状态结束===>去掉边框
CBrush brush(m-color);
dc.FrameRect(&lpDrawItemStruct->rcItem,&brush);
} dc.Detach();
} //用于改变颜色的成员函数
void CColorButton::ChangeColor(COLORREF color) { CR
ect rect;
m-color=color;
GetClientRect(&rect);
}
m-colorChangeColor(COLORREF color)color,WM-DRAWITEM上面代码中数据成员m-color和来保存按钮的颜色,
ChangeColor(COLORREF color)函数负责改变按钮颜色值为color,然后通过使控制的客户区无效而激发WM-DRAWITEM消息。现在这个按钮控制类就算搭好了。下面我们把它加入到对话框中来试验一下(中) 用VC++实现自绘按钮控制1.首先通过AppWizard创建一个单文档的应用。
2.紧接着启动AppStadio创建一个对话框。添加一个按钮控制,
并将其ID设置为 IDC-COLORBUTTON。最后一定要记住将push Button Properties对话框中的Owner Draw检查框置上检查标志。
3.在AppStadio内运行ClassWiz?zand来产生CTestDialog类。然后在CTestdialog类中加入数据成员,在CTestDialog类说明加入如下的private型数据成员:
private:
ccolorButton m-ColorButton;
4.现在剩下的问题是到底要怎样才能使m-ColorButton的DrawItem函数能响应系统发往ID值为IDC-COLORBUTTON的按钮控制的WM-DRAWITEM消息。这时就要用到CWnd类的成员函数BOOL CWnd::Subcla
ssDlgItem(UINT nID,CWnd *pParent)。通过调用这个函数,我们可以动态地接管从对话框模板产生的控制,并把它隶属于CWnd对象。即用当前的CWnd对象接管发向隶属于pParent的ID号为nID的控制的一切消息。对于按钮控制而言,它把当前的按钮控制的位置和大小也清成和nID对应的按钮控制一样。于是我们对CTestDialog的源文件进行如下的编辑:
BOOL CTestDialog::OnInitDialog()
{ CDialog::OnInitDialog();
//TODO:Add extra initialization here
m-ColorButton.SubclassDlgtem(IDC-COLORBUTTON,this)
;//接管消息
m-ColorButton.ChangeColor(RGB(255,0,0);//设置为红色(可设为任何颜色)
return(TRUE);
}
接着通过ClassWizzard在CTestDialog中加入一个响应鼠标点击IDC-COLORBUTTON按钮的消息的函数:
void CTestDialog::OnColerbutton()
{ //TODO:Add your control notification handler code
here
int r=int(((float)rand()/RAND-MAX)*255
int g=int(((float)rand()/RAND-MAX)*255
int b=int(((float)rand()/RAND-MAX)*255
m-ColorButton.ChangeColor(RGB(r,g,b));
}
5.最后,利用ClassWizzard为View加入一个响应WM-LBUTTONDOWN的函数,以便激活对话框。请按如下代码对其进行编辑。
void CTestView::OnLButton?Down(UINT nflags,POINT point)
{ CTestDialog dlg;
dlg.Domodal();
}
6.编译并测试该程序。当鼠标在落视窗中时,按下鼠标左键应能弹出一个对话框。在对话框中的红色矩形区域内按下鼠标左键就会使其边框变成高亮状态,若在这块区域内释放左键则这城区域就会改变颜色且颜色是随机的。
只需更改DrawItem函数中的重绘代码,就可以得到自己的需要图形按钮。
原文转自:www.ltesting.net
文档为doc格式