菜单自绘方面的知识 收藏
1要实现漂亮的界面菜单,必须要启动菜单项的自绘功能,所谓菜单的自绘,就是让菜单自己管理自己的显示效果,为此,首先要作的就是设置菜单项的风格为MF_OWNERDRAW(自绘制),设置菜单的自绘功能即可以通过CMenu类的AppendMenu()函数在菜单的初始阶段实现,也可以通过ModifyMenu()函数对已存在的菜单项进行类型修改。
具体的菜单的自绘是通过重载CMenu类的DrawItem()函数来实现的,这个函数根据各种菜单状态,处理当前菜单项中菜单图标、文字显示的功能。DrawItem()函数的原形为:virtual void DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct ),它参数为一个指向DRAWITEMSTRUCT结构的指针,DRAWITEMSTRUCT结构如下:
具体的菜单的自绘是通过重载CMenu类的DrawItem()函数来实现的,这个函数根据各种菜单状态,处理当前菜单项中菜单图标、文字显示的功能。DrawItem()函数的原形为:virtual void DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct ),它参数为一个指向DRAWITEMSTRUCT结构的指针,DRAWITEMSTRUCT结构如下:
typedef struct tagDRAWITEMSTRUCT { UINT CtlType; //控件类型; UINT CtlID; //组合框、列表框等控件的ID标识号; UINT itemID; //菜单项的ID标识号或列表框、组合框中某一项的索引值; UINT itemAction; //控件行为; UINT itemState; //控件状态; HWND hwndItem; //父窗口句柄或菜单句柄 HDC hDC; //控件对应的绘图设备句柄 RECT rcItem; //控件所占据的矩形区域 DWORD itemData; //列表框或组合框中某一项的值 } |
可以看出,上面的DRAWITEMSTRUCT结构包含了控件自绘时的各种信息。
其中,结构成员CtlType指定了控件的类型,其取值ODT_BUTTON表示按钮控件;ODT_COMBOBOX表示组合框控件;ODT_LISTBOX表示列表框控件;ODT_LISTVIEW表示列表视图控件;ODT_MENU菜单项;ODT_STATIC表示静态文本控件;ODT_TAB表示Tab控件。CtlID指定了自绘控件的ID值,而对于菜单项则不需要使用该成员。itemID表示菜单项ID,也可以表示列表框或者组合框中某项的索引值,对于一个空的列表框或组合框,该成员的值为-1。
itemAction指定绘制行为,其取值可以为下表中所示值的一个或者多个的联合:ODA_DRAWENTIRE表示整个控件都需要被绘制;ODA_FOCUS表示控件需要在获得或失去焦点时被绘制;ODA_SELECT表示控件需要在选中状态改变时被绘制。
itemState指定了当前绘制操作时所绘项的状态,例如,如果菜单项应该被灰显示,则可
以指定ODS_GRAYED状态标志。其取值可以为下表中所示值的一个或者多个的联合:ODS_CHECKED表示菜单项将被选中,该值只对菜单项有用;
ODS_COMBOBOXEDIT在自绘组合框控件中只绘制选择区域;
ODS_DEFAULT表示当前控件处于默认状态;
ODS_DISABLED表示控件将被禁止;
ODS_FOCUS表示控件需要输入焦点;
ODS_GRAYED表示控件需要被灰显示,该值只在绘制菜单时使用;
ODS_HOTLIGHT表示鼠标指针位于控件之上时控件会显示高亮颜(支持Windows 98/Me, Windows 2000/XP);
ODS_COMBOBOXEDIT在自绘组合框控件中只绘制选择区域;
ODS_DEFAULT表示当前控件处于默认状态;
ODS_DISABLED表示控件将被禁止;
ODS_FOCUS表示控件需要输入焦点;
ODS_GRAYED表示控件需要被灰显示,该值只在绘制菜单时使用;
ODS_HOTLIGHT表示鼠标指针位于控件之上时控件会显示高亮颜(支持Windows 98/Me, Windows 2000/XP);
ODS_SELECTED表示选中控件;hwndItem 指定了组合框、列表框和按钮等自绘控件的窗口句柄;如果自绘的对象时菜单项,则表示包含该菜单项的菜单句柄。hDC指定了绘制操作所使用的设备环境。 rcItem指定了将被绘制的矩形区域。这个矩形区域就是上面hDC的作用范围。系统会自动裁剪组合框、列表框或按钮等控件的自绘制区域以外的部分。也就是说rcItem中的坐标点(0,0)指的就是控件的左上角。但是系统不裁剪菜单项,所以在绘制菜单项的时候,必须先通过一定的换算得到该菜单项的位置,以保证绘制操作在我们希望的区域中进行。
itemData这个成员变量最为关键,菜单自绘时所需要的图标、文本等信息都是通过它获取的,至于它的具体值,是通过CMenu类的CMenu::AppendMenu()、CMenu::InSertMenu()、CMenu::ModifMenu()等函数的调用来传递的。
菜单自绘仅仅重载CMenu::DrawItem()函数是不够的,还需要重载CMenu:: MeasureItem()函数,在这个函数里面填充MEASUREITEMSTRUCT结构,通知Windows自绘控件的尺寸。该函数的原形为:
virtual void MeasureItem( LPMEASUREITEMSTRUCT lpMeasureItemStruct ); |
该函数的参数为一个指向MEASUREITEMSTRUCT结构的指针对象,该对象结构为:
typedef struct tagMEASUREITEMSTRUCT { UINT CtlType; //控件类型; UINT CtlID; //控件的ID识别号,它不包括菜单控件; UINT itemID; //菜单项的ID识别号; UINT itemWidth; //菜单项的宽度; UINT itemHeight; //菜单项的高度; DWORD itemData //自绘控件所需要的数据, } MEASUREITEMSTRUCT; |
上面这个结构中,成员变量CtlType等于ODT_COMBOBOX 时,表示当前控件为自绘型的
组合框,等于ODT_LISTBOX 时表示当前控件为自绘列表控制件,等于ODT_MENU 时表示当前控件为自绘菜单。对于组合框和列表框控件,成员变量itemData是通过相应的AddString()、InsertString()获取的, 对于菜单控件,成员变量itemData与DRAWITEMSTRUCT结构中的itemData是一致的。
菜单自绘时所需要的图标资源,可以预先定义,使用时直接装载,但是这种方法比较呆板,另外一种方法是通过搜索状态条上的按钮信息,如果当前按钮的ID识别号与某一菜单项的ID识别号一致,那末通过就将该按钮上的图标提取出来,作为菜单图标,如果菜单项与工具条上的所有按钮的ID都不相同,那么对该菜单项不画图标。至于菜单的文本信息,直接采用用户自定义菜单的文本就可以了
菜单自绘时所需要的图标资源,可以预先定义,使用时直接装载,但是这种方法比较呆板,另外一种方法是通过搜索状态条上的按钮信息,如果当前按钮的ID识别号与某一菜单项的ID识别号一致,那末通过就将该按钮上的图标提取出来,作为菜单图标,如果菜单项与工具条上的所有按钮的ID都不相同,那么对该菜单项不画图标。至于菜单的文本信息,直接采用用户自定义菜单的文本就可以了
上面这个结构中,成员变量CtlType等于ODT_COMBOBOX 时,表示当前控件为自绘型的组合框,等于ODT_LISTBOX 时表示当前控件为自绘列表控制件,等于ODT_MENU 时表示当前控件为自绘菜单。对于组合框和列表框控件,成员变量itemData是通过相应的AddString()、InsertString()获取的, 对于菜单控件,成员变量itemData与DRAWITEMSTRUCT结构中的itemData是一致的。 菜单自绘时所需要的图标资源,可以预先定义,使用时直接装载,但是这种方法比较呆板,另外一种方法是通过搜索状态条上的按钮信息,如果当前按钮的ID识别号与某一菜单项的ID识别号一致,那末通过就将该按钮上的图标提取出来,作为菜单图标,如果菜单项与工具条上的所有按钮的ID都不相同,那么对该菜单项不画图标。至于菜单的文本信息,直接采用用户自定义菜单的文本就可以了。 根据上面介绍的知识,本例定义一个CMemu类的子类CMemuEx类来实现菜单的自绘功能,该类不仅支持在菜单中显示图标、即时反映当前菜单项状态的功能,还支持在菜单中添加纵向位图。在具体实现过程中,CMemuEx类除了上述介绍的需要重载的DrawItem()、MeasureItem()等函数外,另外的一些主要成员函数如下: 1、void InitMenu(CMenu *pMenu,UINT uToolBar,CToolBar *pToolBar); 说明:这是CMenuEx类最主要的一个接口。该函数根据状态栏信息来初始化CMenuEx类; 2、void SetImageLeft(UINT idBmpLeft); 说明:这也是CMenuEx类中的一个重要的接口。调用CMenuEx类对象的SetImageLeft()可以实现菜单中的纵向位图(像Windows系统中的"开始"菜单),调用该函数时参数是位图的ID值。需要注意的是,目前CMenuEx类实现的是对主框架菜单设置纵向位图,对上下文菜单不适用,读者朋友可以稍加修改,自由的决定对何种菜单设置纵向位图。 3、void InitPopupMenu(CMenu *pMenu,UINT uToolBar,CToolBar *pToolBar) 说明:这个函数是为了处理上下文菜单的自绘而编写的,CMenuEx类的任一实例都只能调用InitMenu()、InitPopupMenu()这两个成员函数中的一个,不能一同使用。 4、int GetImageFromToolBar(UINT uToolBar, CToolBar *pToolBar,COLORREF crMask) 说明:这个函数用来从工具条上获取相应图标。 5、void ChangeStyle(CMenu *pMenu,CToolBar *pToolBar,BOOL bIsMainMenu) 说明:这个函数用来修改菜单pMenu的类型为"自绘制" 6、void DrawMenuItemImage(CDC *pDC,CRect &rect,BOOL bSelected,BOOL bChecked,BOOL bGrayed,BOOL bHasImage,LPMENUITEM lpItem); 说明:这个函数根据菜单的不同状态及包含的各种信息,对菜单进行绘制; 上述函数构成了CMenuEx类的主要骨架,该类还有一些实现辅助函数,它们分别是: 1、void SetHighLightColor(COLORREF crColor); //设置菜单搞亮显示; 2、void SetBackColor(COLORREF); //设置菜单的背景颜; 3、void SetTextColor(COLORREF); //设置菜单的文本颜; 4、void GrayString(CDC *pDC,const CString &str,const CRect rect); //显示灰菜单文本; 5、void TextMenu(CDC *pDC,CRect &rect,CRect rtText,BOOL bSelected,BOOL bGrayed,LPMENUITEM lpItem); //显示菜单上的文本; CMenuEx类在使用过程中,要注意对其初始化,对于主框架菜单,可以在CMainFrame类的WM_INITMENU消息响应函数中实现,为了激活菜单的自绘功能, 需要在CMainFrame类的WM_DRAWITEM和WM_MEASUREITEM中分别调用CMenuEx类的DrawItem()函数和MeasureItem()函数。对于上下文菜单的实现,有两种方法,一种方法是在项目视图类的WM_INITMENUPOPUP响应函数中调用InitPopupMenu()函数来修改菜单的类型,然后在WM_CONTEXTMENU响应函数中调用CMenu::TrackPopupMenu()函数显示上下文菜单;另一种方法是直接响应鼠标的右键单击消息,在响应函数中处理鼠标的初始化和显示。第二种方法处理上下文菜单比较常用,这方面的资料也很多,就不在赘述了。为了让读者朋友们更深入的了解上下文菜单的处理,例子中使用了第一种方法。 | ||||||
-- 作者:admin -- 发布时间:2005-10-17 9:20:00 -- 二、编程步骤 1、 启动Visual C++6.0生成一个单文档应用程序框架,去除文档支持,将应用程序命名为Hello; 2、 在CMainFrame类中添加CMenuEx类的成员变量m_menu; 3、 使用Class Wizard在CMainFrame类添加WM_INITMENU、WM_DRAWITEM、WM_MEASUREITEM消息响应函数、在ChildView类中添加WM_INITMENUPOPUP、WM_CONTEXTMENU消息响应函数; 4、 将需要显示图标的菜单的ID识别号与工具条上响应的按钮的ID识别号统一起来; 5、 在CChildView类中添加成员变量CToolBar * m_pToolBar和 CMenuEx m_menu。m_pToolBar对象主要是用来存放程序中的工具条,从而提供给上下文菜单m_menu自画时所需要的图标信息。 6、添加代码,编译运行程序。 三、程序代码
四、小结 到此为止,本例详细介绍了菜单自绘类CMenuEx的实现以及它在应用程序的具体使用方法,相信读者能够从中学习到菜单的自绘制机理。本例中的CMenuEx类稍加改动,就可以实现各种不同类型的菜单效果,如WindowsXP风格的菜单等。 | ||||||
-- 作者:admin -- 发布时间:2005-10-17 9:20:00 -- 一种漂亮的自绘菜单 以前还是菜鸟时就觉得QQ的菜单做得很漂亮,想着自已的程序如果有那种菜单多好。 现在积累了一定的知识,就自已设计了一个类似的菜单控件类。并把它发表出来供大家使用和参考,难免有不足的地方请高手不吝赐教! 菜单效果如下: 一、CMenuEx菜单类主要接口函数: 1、void InitMenu(CMenu *pMenu,UINT uToolBar,CToolBar *pToolBar); 说明:这是最主要的一个接口。如果要改变主窗口的菜单则应在主窗口的OnInitMenu(CMenu *pMenu)中调用该函数(如:m_menu.InitMenu(pMenu,IDR_MAINFRAME,&m_wndToolBar)),当然主窗口必须要有工具栏,才会产生菜单项位图。最后还必须重载主窗口的OnMeasureItem()和OnDrawItem()。并在两个函数中分别 调用菜单类的另外两个接口DrawItem()和MeasureItem(); 2、void SetHighLightColor(COLORREF crColor); 3、void SetBackColor(COLORREF); 4、void SetTextColor(COLORREF); 以上三个接口应该不用再说明 。 QQ的菜单采用的颜是 SELECTTEXT_COLOR RGB(0,0,127) TEXT_COLOR RGB(0,0,0) BK_COLOR RGB(143,167,207) 效果不错 5、void SetImageLeft(UINT idBmpLeft) 说明:这也是个重要的接口。要实现纵向位图(像“开始”菜单)。可以在适当的地方调用该函数。参数当然是位图的ID值。注意选择位图时要选好看一点的哦!要记得高宽的比例。 6、void InitPopupMenu(CMenu *pMenu,UINT uToolBar,CToolBar *pToolBar) 与InitMenu不同的是:InitMenu并不修改第一级菜单为自绘风格,而 该函数有包括第一级菜单. 但必须注意:该类的任一实例都只能调用这两个函数中的一个,不能一同使用!!! 二、CMenuEx类的具体使用步骤:2.1 用CMenuEx类加载主窗口菜单的具体步骤: 1、添加CMenuEx成员变量 CMenuEx m_menu 2、CMainFrame类添加WM_INITMENU消息,并在实现函数中加入代码: m_menu.InitMenu(pMenu,IDR_MAINFRAME,&m_wndToolBar); IDR_MAINFRAME是工具栏资源ID,m_wndToolBar是工具栏对象 3、CMainFrame类添加WM_DRAWITEM和WM_MEASUREITEM消息,并在实现函数分加入: if(!nIDCtl) m_menu.DrawItem(lpDrawItemStruct);if(!nIDCtl) m_menu.MeasureItem(lpMeasureItemStruct); 只须这三步就大功造成了! 但是有时你会发现菜单的位图错位了,这不是程序的错。 这是因为“工具栏位图”的个数与“工具栏按钮个数”不符, 你只需把不用的位图删掉就可以,或者添加对应的菜单项,反正使这两个数目一致就对了 4、如果你想要让你的菜单拥有“纵向位图”(像“开始”菜单),就必须在CMainFrame在OnCreate()中加入: m_menu.SetImageLeft(IDB_BITMAP1);//IDB_BITMAP1是指定位图 2.2 用CMenuEx类加载弹出菜单的具体步骤: 1、在CChildView类中加入成员变量 CMenuEx m_menu和CToolBar *m_pToolBar 为什么要加入m_pToolBar,这里做一下说明: 因为在CMenuEx的接口函数InitPopupMenu()中需要工具栏对象指针,而工具栏对象又偏偏是CMainFrame类的成员 所以设此指针变量指向工具栏对象 2、给CChildView类的成员m_pToolBar赋值。 当CView派生类由不得CxxxxDocTemplate产生时,赋值的地方比较特殊 请在CxxxWinApp类中的InitInstance()的最后加入代码: CMainFrame *pFrame=(CMainFrame *)m_pMainWnd; CChildView *pView=(CChildView *)pFrame->GetActiveView(); pView->m_pToolBar=&(pFrame->m_wndToolBar);//注意:原m_wndToolBar是私有变量,要改成public的哦! 当CView派生类是CMainFrame类的成员变量时,就很简单,直接在OnCreate()中设置 3、在CChildView的构造函数中加入设置弹出菜单的代码 m_menu.CreatePopupMenu(); m_menu.AppendMenu(0,ID_EDIT_UNDO,"撤消"); m_menu.AppendMenu(MF_SEPARATOR,0); m_menu.AppendMenu(0,ID_EDIT_COPY,"复制"); m_menu.AppendMenu(0,ID_EDIT_CUT,"剪切"); m_menu.AppendMenu(0,ID_EDIT_PASTE,"粘贴");//当菜单ID与工具栏按钮ID一样时就会显示位图 4、在CChildView的析构函数中加入代码: m_menu.DestroyMenu(); //释放资源 5、在CChildView类中加入WM_INITMENUPOPUP消息,并在其实现函数中加入代码: if(!bSysMenu) m_menu.InitPopupMenu(pPopupMenu,IDR_MAINFRAME,m_pToolBar); 6、在CChildView类中加入WM_CONTEXTMENU消息,并在其实现函数中加入弹出菜单的代码: m_menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,point.x,point.y,this); 好了!介绍完了。 大家可以在适当的地方(比如CMainFrame:OnCreate)中加入: m_menu.SetHighLightColor(RGB(0,0,127));m_menu.SetTextColor(RGB(0,0,0));m_menu.SetBackColor(RGB(143,167,207)); 来设置菜单颜,怎么样:) 欢迎大家多提建议 | ||||||
-- 作者:admin -- 发布时间:2005-10-17 9:24:00 -- 示例工程 CMenuEx.Zip 点击浏览该文件 | ||||||
-- 作者:admin -- 发布时间:2005-10-17 10:08:00 -- www.vckbase/document/listdoc.asp?mclsid=3&sclsid=303 | ||||||
-- 作者:admin -- 发布时间:2005-10-17 15:44:00 -- science/~corkum/BCMenu.html | ||||||
| ||||||
-- 作者:admin -- 发布时间:2005-10-17 17:49:00 -- 完美实现真彩自绘菜单 作者:阿福(geforce_zf) 下载源代码一、提出问题 在VCKBASE上读到《自绘菜单的实现》[作者:querw]。应用的我自己的正在进行的工程后发现效果不错,可是有存在许多问题。整个类的设计方面存在很多缺陷(先天,后天的),存在的主要问题如下: 1 当应用在多文档界面(MDI)中的时候,无法对系统自动添加菜单和文档模板菜单进行自绘(比如无法对文件->最近文件(MRU)菜单项中的文件列表就是系统自动添加)。原因是类内部没有对CMainFrame::OnInitPopupMenu()消息进行处理的函数, 因此不具备修改系统自动添加菜单项的功能。(BCMENU有这功能,而且工作的不错) 作者提到的 BCMENU 不用映射 WM_DRAWITEM 和 WM_MEASUREITEM 两个消息就能实现自画功能,实际上是错误的。不映射这两个重要的消息,即使能自绘,也是有问题的,不信看图。 菜单编辑器中的模菜单样 使用BCMENU并且映射了这两个消息后的执行情况 使用BCMENU没有映射两个消息的执行情况 原作者分析的自绘的是因为把主菜单(top-level menu)的子菜单都加载成弹出菜单(popupmenu),是不正确的。真正的原因是因为MFC框架会自动调用CMenu的两个虚拟函数MeasureItem()和OnDrawItem()。 因此,当CMenuEx派生于CMenu,并且重写这两个虚拟函数以后。 2 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 { ...... } ......} 3 当菜单项中含有子菜单(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);} 4 菜单编辑器中没有分割条菜单的菜单 原CMenuEx执行的模样 菜单编辑器中有分割条菜单的菜单 原CMenuEx执行的模样 代码不够简练,程序粒度划分不好,可读性差(不过比BCMENU的代码可读性强多了:))。 二、解决问题 针对以上遇到的问题,我参考BCMENU和原作者的CMenuEx,对CMenuEx类重新进行了组织,类定义如下: // 声明,因为下面的结构要用到 CMenuEx*,又不支持向后引用,又什么办法啊!class CMenuEx;//自绘菜单数据项结构,就是要传给系统的那个牛X的LPCTSTR指针所指向的东东class CMenuEx : public CMenu{ DECLARE_DYNAMIC( CMenuEx ) // Constructorpublic: CMenuEx(); virtual ~CMenuEx(); virtual BOOL DestroyMenu(); // Operationpublic: // 加载菜单操作 BOOL LoadMenu(UINT nIDResource); BOOL LoadMenu(LPCTSTR lpszResourceName); BOOL LoadMenu(HMENU hMenu); BOOL LoadMenu(CMenu & Menu); // 菜单项操作,如果当前菜单为主菜单(top-level)就调用相应的CMenu的操作。如果是弹出菜单, // 就将新加入的菜单项定义为自绘菜单 BOOL AppendMenu(UINT nFlags, UINT nIDNewItem = 0,LPCTSTR lpszNewItem = NULL); BOOL InsertMenu(UINT nPosition,UINT nFlags,UINT nIDNewItem=0,LPCTSTR lpszNewItem=NULL ); BOOL ModifyMenu(UINT nPosition,UINT nFlags,UINT nIDNewItem=0,LPCTSTR lpszNewItem=NULL ); BOOL RemoveMenu(UINT nPosition, UINT nFlags); // 加载菜单图像操作 //通过菜单索引表加载图像索引,此操作必须在设置过菜单图像后调用 void SetImageIndex(const UINT* nIDResource,UINT nIDCount); void LoadToolBar(const CToolBar* pToolBar);// 通过工具栏加载图像,和图像索引 // 取自绘菜单项的数据项 UINT GetMenuItemSize() const; LPMENUITEM GetMenuItem(UINT nPosition); // 取子菜单操作,如果位置nPosition存在子菜单,返回该子菜单指针 // 如果不存在子菜单,返回NULL CMenuEx* GetSubMenu(int nPosition); // 在当前菜单和所以子菜单中中寻相应ID // 如果到,返回ID所在菜单的指针,没到返回NULL CMenuEx* FindPopupMenuFromID(UINT nID); // Attributesprotected: // 指示为主菜单(top-level menu or menubar)还是弹出菜单(popupmenu) BOOL m_bPopupMenu; // 分割条的默认高度 int m_nSeparator; // 绘制菜单需要的颜 COLORREF m_crBackground; // 菜单背景 COLORREF m_crTextSelected; // 菜单项被选中时的文字颜 COLORREF m_crText; // 菜单项文字颜 COLORREF m_crLeft; // 菜单左侧的背景颜 COLORREF m_crSelectedBroder; // 菜单选中框的线条颜 COLORREF m_crSelectedFill; // 菜单选中框的填充颜 // 菜单项图像的尺寸 CSize m_szImage; CImageList* m_pImageList; // 菜单项正常的图像列表 CImageList* m_pDisabledImageList; // 菜单项禁用时的图像列表 CImageList* m_pHotImageList; // 菜单项被选中时的图像列表 protected: // 包含所有菜单项的数组 CArray m_MenuItemArr; public: // 设置颜操作 void SetTextSelectedColor(COLORREF color); void SetBackgroundColor(COLORREF color); void SetTextColor(COLORREF color); void SetLeftColor(COLORREF color); void SetSelectedBroderColor(COLORREF color); void SetSelectedFillColor(COLORREF color); // 设置图像列表操作 void SetImageList(CImageList* pImageList); void SetDisabledImageList(CImageList* pImageList); void SetHotImageList(CImageList* pImageList); // 设置当前菜单为主菜单还是弹出菜单 void SetPopupMenu(BOOL bPopupMenu); // Implementationpublic: // 绘制菜单项的虚拟函数,由MFC框架自动调用 virtual void DrawItem(LPDRAWITEMSTRUCT lpDIS); // 更新弹出菜单菜单项操作 // 因为有时候系统会通过菜单句柄插入一些非自绘菜单 // 该函数就是更新这些非自绘菜单为自绘菜单 void UpdatePopupMenu(); protected: // 绘制菜单项的辅助函数,想自己的菜单看上去更COOL,就拿他们开刀 void DrawBackground(CDC* pDC,CRect rect); void DrawMenuImage(CDC* pDC,CRect rect,LPDRAWITEMSTRUCT lpDIS); void DrawMenuText(CDC* pDC,CRect rect,LPDRAWITEMSTRUCT lpDIS); void DrawSelected(CDC* pDC,CRect rect,LPDRAWITEMSTRUCT lpDIS); // Static Memberpublic: // 在CMainFrame的OnMeasureItem()消息映射函数中调用它,用来测量所有菜单项尺寸 static void MeasureItem(LPMEASUREITEMSTRUCT lpMIS); // 在CMainFrame的OnInitPopupMenu()消息映射函数中调用它, // 用来更新系统自动添加的菜单项为自绘菜单 static void InitPopupMenu(CMenu* pPopupMenu,UINT nIndex,BOOL bSystem); };#endif // !defined(MENUEX_H) 三、实现方法 有了以上的强有力的武器,就可以对我们的程序下手了:)在MDI或SDI中使用CMenuEx的时候需要修改以下地方。 先将MenuEx.h和MenuEx.cpp添加到工程中,在CMainFrame中添加头文件,CMenuEx对象,用于存储菜单图像的CImageList对象和初始化菜单程序。 #include "MenuEx.h" // 添加头文件class CMainFrame : public CMDIFrameWnd{ ...public: HMENU InitMainFrameMenu(); // 初始化主菜单 HMENU InitImageTypeMenu(); // 初始化文档模板菜单 protected: // CMenuEx members CMenuEx m_menuMainFrame; // 主窗体没有打开任何文档时菜单 CMenuEx m_menuImageType; // 主窗体打开文档时菜单(文档模板菜单) protected: // CMenuEx\'\'s image list members CImageList m_imageMenu; // 菜单项正常的图像列表 CImageList m_imageMenuDisable; // 菜单项禁用时的图像列表 CImageList m_imageMenuHot; // 菜单项被选中时的图像列表 ...} 撰写菜单图像索引表,初始化菜单程序,初始化菜单图像列表程序, 和两个重要的消息映射函数CMainFrame::OnMeasureItem()和CMainFrame::OnInitPopupMenu()。 (什么?不会添加!,Clazard帮忙或许有点帮助了:)) // 声明,因为下面的结构要用到 CMenuEx*,又不支持向后引用,又什么办法啊!class CMenuEx;//自绘菜单数据项结构,就是要传给系统的那个牛X的LPCTSTR指针所指向的东东typedef struct tagMENUITEM{ CString strText; // 菜单名称 UINT nID; // 菜单ID号 // 分割条的ID是 0 // 子菜单的ID是 -1 CSize itemSize; // 菜单项的尺寸,不包括菜单图像的尺寸 CImageList* pImageList; // 菜单项的正常图像列表 CImageList* pDisabledImageList; // 菜单项的禁用图像列表 CImageList* pHotImageList; // 菜单项的选中图像列表 UINT nImageIndex; // 菜单项的图像列表索引,-1表示没有图像 BOOL bIsSubMenu; // 表示当前菜单项是否为子菜单项 CMenuEx* pSubMenu; // 如果是一般菜单,该值为NULL // 如果bIsSubMenu为TRUE,该值为指向子菜单项的CMenuEx*指针 } MENUITEM,*LPMENUITEM;///////////////////////////////////////////// 在ManiFram.cpp 中添加菜单图像索引表static UINT nMenuImageIndex[] ={ ID_FILE_OPEN, ID_FILE_SAVE, ID_FILE_PRINT, ID_EDIT_COPY, ID_EDIT_PASTE, ID_EDIT_UNDO, ID_EDIT_REDO, ID_APP_ABOUT, ID_IMAGE_LEVEL, ID_IMAGE_EQUALIZE, ID_IMAGE_SMOOTH, ID_IMAGE_SHARP, ID_IMAGE_SIZE, ID_IMAGE_RA, ID_IMAGE_HISTOGRAM, ID_ZOOMOUT, ID_ZOOMIN,};/////////////////////////////////////////////////////////////////////////////// 在ManiFram.cpp 中添加初始化菜单程序void CMainFrame::InitMenuImage(){ // 初始化菜单图像列表 CBitmap bm; m_imageMenu.Create(20, 20, TRUE | ILC_COLOR24, 9, 0); // 要问我IDB_SMALLMENUCOLOR是什么,当然是是真彩位图了,看图说话了 bm.LoadBitmap(IDB_SMALLMENUCOLOR); m_imageMenu.Add(&bm,(CBitmap*)NULL); bm.Detach(); // 还有IDB_SMALLMENUDISABLE m_imageMenuDisable.Create(20, 20, TRUE | ILC_COLOR24, 9, 0); bm.LoadBitmap(IDB_SMALLMENUDISABLE); m_imageMenuDisable.Add(&bm,(CBitmap*)NULL); bm.Detach(); // 还有IDB_SMALLMENUHOT m_imageMenuHot.Create(20, 20, TRUE | ILC_COLOR24, 9, 0); bm.LoadBitmap(IDB_SMALLMENUHOT); m_imageMenuHot.Add(&bm,(CBitmap*)NULL); bm.Detach(); }/*IDB_SMALLMENUCOLOR IDB_SMALLMENUHOT IDB_SMALLMENUDISABLE 5 当然,要通过资源编辑器的Import功能将他们导入到资源文件中,不过因为是真彩,所以不能用VC的图片编辑器编辑了。 告诉大家个敲门,我是用windows自带的画笔画的:) *//////////////////////////////////////////////////////////////////////////////// 在ManiFram.cpp 中添加初始化菜单图像列表程序int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct){ // 在CMainFrame::OnCreate中调用菜单图标初始化程序 。。。。。。 InitMenuImage(); 。。。。。。}/////////////////////////////////////////////////////////////////////////////HMENU CMainFrame::InitMainFrameMenu(){ //初始化主菜单 m_menuMainFrame.LoadMenu(IDR_MAINFRAME); { // 这只加载图像的一种方法,是一种两步方法,先加载图像列表 m_menuMainFrame.SetImageList(&m_imageMenu); m_menuMainFrame.SetDisabledImageList(&m_imageMenuDisable); m_menuMainFrame.SetHotImageList(&m_imageMenuHot); // 再通过菜单图像索引表为菜单加载图像索引, m_menuMainFrame.SetImageIndex(nMenuImageIndex, sizeof(nMenuImageIndex)/sizeof(UINT)); } // 也可以使用另外一种一步方法加载图像 /* // 假设MAINFRAM具有m_wndToolBar成员,并且已经设置了真彩位图 // 关于设置工具栏的真彩位图,请参考 www.vckbase/document/viewdoc/?id=576 //"CMDIFrameWnd::OnMeasureItem()",不对子菜单项的尺寸进行测量 // 害的我们只好映射这个函数了 CMDIFrameWnd::OnMeasureItem(nIDCtl, lpMeasureItemStruct); // 静态函数,看好了,别忘了写CMenuEx啊 CMenuEx::MeasureItem(lpMeasureItemStruct);} 6 在CXXXApp::InitInstance()中添加代码,XXX代表你自己的程序了 BOOL CXXXApp::InitInstance(){ ...... CMultiDocTemplate* pDocTemplate; pDocTemplate = new CMultiDocTemplate( IDR_IMAGETYPE, RUNTIME_CLASS(CImageDoc), RUNTIME_CLASS(CChildFrame), // custom MDI child frame RUNTIME_CLASS(CImageView)); AddDocTemplate(pDocTemplate); // create main MDI Frame window CMainFrame* pMainFrame = new CMainFrame; if (!pMainFrame->LoadFrame(IDR_MAINFRAME)) return FALSE; m_pMainWnd = pMainFrame; // 这些才是要添加的代码,别弄错了 // 初始化文档模板菜单 pDocTemplate->m_hMenuShared=pMainFrame->InitImageTypeMenu(); // 初始化主窗体菜单 pMainFrame->m_hMenuDefault=pMainFrame->InitMainFrameMenu(); // 更新,具体干什么没研究,反正不调用就出错了:) pMainFrame->OnUpdateFrameMenu(pMainFrame->m_hMenuDefault); // 要添加的代码到这结束 ......} 三、总结 说了这么多,也不知道大家看明白没有,没关系,先贴个图,大家看看效果再说了。 效果图一,使用图像索引表加载的小图标菜单 效果图一,工具条加载的大图标菜单 四、结束语 感谢querw和BCMenu的作者,没有他们的辛勤劳动,后人是没办法站在他们肩膀上的!由于程序写的匆忙,难免有不尽人意和错误的地方,欢迎大家任意修改源程序:) 要说这个菜单做的完美,那是吹牛,世界上哪有完美的东西啊 :) 只要自己觉得完美,就够了。 希望大家能从文章中学到点东西,就好。 | ||||||
-- 作者:admin -- 发布时间:2005-10-17 17:50:00 -- 老大 你还敢用完美2字。用了你的代码出现了一大堆内存泄漏!!!!! 看看DestroyMenu()里面,错误一大堆 for(int i = 0; i < m_MenuItemArr.GetSize(); i++) { MENUITEM *pMenuItem = m_MenuItemArr.GetAt(i); // destroy submenu if(pMenuItem->bIsSubMenu) pMenuItem->pSubMenu->DestroyMenu(); // remove menu item from array m_MenuItemArr.RemoveAt(i); // free menuitem struct delete pMenuItem; } 要这样改 for(int i = 0; i < m_MenuItemArr.GetSize(); i++) { MENUITEM *pMenuItem = m_MenuItemArr.GetAt(i); // destroy submenu if(pMenuItem->pSubMenu) { pMenuItem->pSubMenu->DestroyMenu(); delete pMenuItem->pSubMenu; // DestroyMenu()了以后就不用delete了嗦 } // remove menu item from array //m_MenuItemArr.RemoveAt(i); // 就算你要Remove也要--i一下啊 // free menuitem struct delete pMenuItem; } m_MenuItemArr.RemoveAll(); // 这里再RemoveAll ( cooljjyy 发表于 2005-4-23 20:52:00) | ||||||
-- 作者:admin -- 发布时间:2005-10-18 0:32:00 -- 告别图标失真的烦恼 示例代码运行效果图 自从和VC知识库认识之后我认识了不少朋友,有的象我一样走着苦苦探索的道路。其中有很多朋友问我在VC中如何保证256以上的图标加载后不失真。根据这些提问我也曾经到各个站点上寻求答案,结果是大失所望,虽然有介绍如何实现256以上的工具栏的文章,但是方法中大都采用加载一幅256以上的工具栏位图的方法。这样的方法存在一个麻烦就是:有什么简便好用的制图工具来做这样的位图呢?我虽然没有尝试过去寻这样的工具,但是我知道大家都希望有更直接的方法来使用256以上的图标,并且保持最终的效果不失真! 所以针对以上问题我研究了MSDN,费了九牛二虎的劲最终才"修成正果",哈哈!不过有快乐得和大家一起分享,希望我的努力没有白费,好了,废话少说,咱们立刻揭开它神秘的面纱吧。 一. 建立一个单文档工程(多文档也可)。 二. 在主框架CMainFrame类的头中定义一个CImageList对象。 三. 在主框架CMainFrame类的OnCreate处理中创建CImageList对象并且向其中添加你想要的图标。 其中CImageList的创建是本工程最最关键的一步,如果写错,那你就回到原处,结果是空空如也。哈哈! m_ilTB.Create(32, 32, TRUE | ILC_COLOR8, 4, 0); //设置ToolBar的图标列表 现在对这几个参数做个详细介绍如下: 1. 前两个参数指定图标的宽度和高度,即:图标的尺寸定义。 2. 第三个参数指定何如显示图标。ILC_ COLOR8说明以256调板来显示图标。而TRUE则指明了以透明方式来显示图标。那么两者进行"按位或"运算后的意义变为:以透明方式来显示256图标。 3. 第四和第五个参数则分别为:初始图标个数和新增图标时对象自动申请内存空间的步长。如果在大批量操作图标,并且需要不断的增删图标时,设置第五个参数可以改变程序的性能,如果第五个参数设置的比较适中则可以避免程序反复的申请和释放内存空间。 四. 最后的一步就是加载和添加图标资源了。 HICON hIcon = NULL;hIcon = (HICON)::LoadImage(::AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, 32, 32, 0); m_ilTB.Add(hIcon); 好了,一切就是这么简单,如果你还不清楚的话,那就打开工程看看吧,如你有什么问题也不要忘记来信告诉我哦!最后祝大家学习愉快,多多交流,多多进步,一切顺利! | ||||||
发表于 @ 2006年12月28日 10:06:00 | 评论( )| 举报| 收藏
旧一篇:OutLook样式类使用过程,弹出模式对话框时候,鼠标不能点击对话框按钮,的问题。 | 新一篇:解决了CListCtrl控件当字体改变后,自动调整每行的宽度的问题.
查看最新精华文章 请访问博客首页相关文章
发表评论
表 情:
评论内容:
用 户 名:
验 证 码:
重新获得验证码
热门招聘职位
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论