- Visual C++程序开发范例宝典(软件工程师典藏版)
- 刘志铭 李贺 高茹编著
- 1109字
- 2020-06-27 11:09:44
1.2 弹出菜单应用实例
在程序中使用弹出式菜单能够方便用户操作。本节通过几个典型实例介绍各种弹出式菜单的设计。
实例004 在控件上单击右键弹出带菜单
本实例可以方便操作、提高效率
实例位置:光盘\mingrisoft\01\004
实例说明
在许多应用软件中,当用户单击鼠标右键时,会弹出一个快捷菜单,用户可以通过快捷菜单方便地进行各种操作。本实例实现了弹出式菜单的功能,效果如图1.4所示。
技术要点
实现弹出式菜单非常简单,只需要处理WM_CONTEXTMENU消息就可以了。在其消息处理函数(默认为OnContextMenu)中调用菜单的TrackPopupMenu方法即可在指定位置弹出菜单。
实现过程
(1)新建一个基于对话框的应用程序。
(2)在对话框中添加List Control控件,设置控件属性,如图1.5所示。
图1. 4 在控件上单击右键弹出菜单
图1.5 列表控件属性
(3)处理对话框的WM_CONTEXTMENU消息,代码如下:
void CPopManuDlg::OnContextMenu(CWnd* pWnd, CPoint point) { CMenu m_popmenu; //定义菜单对象 m_popmenu.LoadMenu(IDR_POPMENU); //加载菜单资源 CMenu*m_submenu=m_popmenu.GetSubMenu(0); //获得子菜单 m_submenu->TrackPopupMenu(TPM_LEFTBUTTON|TPM_LEFTALIGN,point.x,point.y,this); //显示弹出菜单 m_popmenu.DestroyMenu(); }
举一反三
根据本实例,读者可以:
设计系统托盘菜单。
实例005 个性化的弹出菜单
这是一个可以提高基础技能的实例
实例位置:光盘\mingrisoft\01\005
图1.6 个性化的弹出菜单
实例说明
网上许多软件的弹出式菜单非常漂亮。本实例设计了一个漂亮的弹出式菜单,效果如图1.6所示。
技术要点
设计弹出式菜单与设计普通的菜单一样,需要从CMenu类派生一个子类,然后改写MeasureItem方法设置菜单项大小;改写DrawItem方法根据当前状态绘制菜单。设计思路可以参考实例002带图标的程序菜单。
实现过程
(1)新建一个基于对话框的应用程序。
(2)CMenu派生一个子类CIconMenu,定义一个菜单项结构CMenuItemInfo,代码如下:
/************************************* CMenuItemInfo结构用于记录菜单项信息 *************************************/ struct CmenuItemInfo { CString m_ItemText; //菜单项文本 int m_IconIndex; //菜单项索引 int m_ItemID; //菜单标记 -2顶层菜单,-1弹出式菜单,0分隔条,其他普通菜单 };
(3)在CIconMenu类中定义如下成员变量:
CMenuItemInfo m_ItemLists[MAX_MENUCOUNT]; //菜单项信息 int m_index; //临时索引 int m_iconindex; //图像索引 BOOL m_isdrawtitle; //是否重绘标题 CFont m_titlefont; //标题字体
(4)向CIconMenu类中添加DrawItemText方法,绘制菜单项文本,代码如下:
/************************************************************* 功能描述: 绘制菜单项文本 参数说明: m_pdc标识画布对象,str标识菜单文本,m_rect标识菜单区域 *************************************************************/ void CIconMenu::DrawItemText(CDC* m_pdc,LPSTR str,CRect m_rect) { m_rect.DeflateRect(40,0,0,0); //调整文本绘制区域 m_pdc->DrawText(str,m_rect,DT_SINGLELINE|DT_VCENTER|DT_LEFT); //绘制菜单项文本 }
(5)向CIconMenu类中添加DrawComMenu方法,绘制普通的菜单,代码如下:
void CIconMenu::DrawComMenu(CDC* m_pdc,CRect m_rect,COLORREF m_fromcolor,COLORREF m_tocolor, BOOL m_selected ) { if(m_selected) //如果选中菜单项 { m_pdc->SelectStockObject(BLACK_PEN); //设置黑色画笔 m_rect.DeflateRect(25,1,0,2); //调整菜单项区域 m_pdc->Rectangle(m_rect); //绘制矩形 CBitmap m_bitmap; m_bitmap.LoadBitmap(IDB_LEFTBITMAP); //加载位图资源 BITMAP m_size; m_bitmap.GetBitmap(&m_size); //获得位图数据 CDC m_memdc; m_memdc.CreateCompatibleDC(m_pdc); //创建内存兼容设备上下文 CGdiObject* m_oldobject; m_oldobject=m_memdc.SelectObject(&m_bitmap); //选入位图 m_pdc->StretchBlt(m_rect.left+1,m_rect.top+1,m_rect.Width()-2,m_rect.Height()-2,&m_memdc,0,0, m_size.bmWidth, m_size.bmHeight,SRCCOPY); //绘制位图 m_bitmap.DeleteObject(); } else { m_pdc->FillSolidRect(m_rect,RGB(0x000000F9,0x000000F8,0x000000F7)); //填充菜单项背景 } }
(6)向CIconMenu类中添加DrawSeparater方法,绘制分隔条,代码如下:
void CIconMenu::DrawSeparater(CDC* m_pdc,CRect m_rect) { if (m_pdc != NULL) { m_rect.DeflateRect(25,0,0,0); //设置绘制区域 m_pdc->Draw3dRect(m_rect,RGB(255,0,0),RGB(0,0,255)); //绘制滚动条 } }
(7)向CIconMenu类中添加ChangeMenuItem方法,修改菜单项的风格,代码如下:
BOOL CIconMenu::ChangeMenuItem(CMenu* m_menu,BOOL m_Toped) { if (m_menu != NULL) { int m_itemcount=m_menu->GetMenuItemCount(); //获得菜单项数量 for (int i=0;i<m_itemcount;i++) { m_menu->GetMenuString(i,m_ItemLists[m_index].m_ItemText,MF_BYPOSITION);//获得菜单项文本 int m_itemID=m_menu->GetMenuItemID(i); //获得菜单项ID if (m_itemID==-1 && m_Toped) { m_itemID=-2; //顶层菜单 }; m_ItemLists[m_index].m_ItemID=m_itemID; //保存菜单项ID if (m_itemID>0) { m_ItemLists[m_index].m_IconIndex=m_iconindex; //保存菜单项索引 m_iconindex+=1; //设置菜单项索引增加 } m_menu->ModifyMenu(i,MF_OWNERDRAW|MF_BYPOSITION |MF_STRING, m_ItemLists[m_index].m_ItemID,(LPSTR)&(m_ItemLists[m_index])); //修改菜单风格 m_index+=1; CMenu*m_subMenu=m_menu->GetSubMenu(i); //获得子菜单 if (m_subMenu) { ChangeMenuItem(m_subMenu); //递归修改菜单项 } } } return TRUE; }
(8)改写CMenu类的MeasureItem方法,设置菜单项大小,代码如下:
void CIconMenu::MeasureItem( LPMEASUREITEMSTRUCT lpStruct ) { if (lpStruct->CtlType==ODT_MENU) { lpStruct->itemHeight=ITEMHEIGHT; //设置菜单项高度 lpStruct->itemWidth=ITEMWIDTH; //设置菜单项宽度 CMenuItemInfo* m_iteminfo; m_iteminfo=(CMenuItemInfo*)lpStruct->itemData; //获得菜单项数据 lpStruct->itemWidth = ((CMenuItemInfo*)lpStruct->itemData)->m_ItemText.GetLength()*10;//设置菜单项高度 switch(m_iteminfo->m_ItemID) { case 0: //分隔条 { lpStruct->itemHeight=1; //设置分隔条高度为1 break; } } } }
(9)改写CMenu类的DrawItem方法,根据菜单状态绘制菜单,代码如下:
void CIconMenu::DrawItem( LPDRAWITEMSTRUCT lpStruct ) { if (lpStruct->CtlType==ODT_MENU) { if(lpStruct->itemData==NULL) return; unsigned int m_state=lpStruct->itemState; //获得菜单项状态 CDC*m_dc=CDC::FromHandle(lpStruct->hDC); //获得设备上下文 CString str= ((CMenuItemInfo*)(lpStruct->itemData))->m_ItemText; //获得菜单项文本 LPSTR m_str = str.GetBuffer(str.GetLength()); int m_itemID=((CMenuItemInfo*)(lpStruct->itemData))->m_ItemID; //获得菜单项ID int m_itemicon=((CMenuItemInfo*)(lpStruct->itemData))->m_IconIndex; //获得菜单项索引 CRect m_rect=lpStruct->rcItem; //获得菜单项区域 m_dc->SetBkMode(TRANSPARENT); //设置背景透明 switch(m_itemID) { case-2: //顶层菜单 { DrawTopMenu(m_dc,m_rect,(m_state&ODS_SELECTED)||(m_state&0x0040));//0x0040==ODS_HOTLIGHT DrawItemText(m_dc,m_str,m_rect); //绘制菜单项文本 break; } case-1: //弹出菜单 { DrawItemText(m_dc,m_str,m_rect); //绘制菜单项文本 break; } case 0: //分隔条 { DrawSeparater(m_dc,m_rect); //绘制分隔条 break; } default: //普通菜单 { DrawComMenu(m_dc,m_rect,0xfaa0,0xf00ff,m_state&ODS_SELECTED); //绘制菜单背景 DrawItemText(m_dc,m_str,m_rect); //绘制菜单项文本 DrawMenuTitle(m_dc,m_rect,"明日科技有限公司"); //绘制菜单左侧标题 break; } } } }
举一反三
根据本实例,读者可以:
开发具有背景颜色的菜单。
实例006 任务栏托盘弹出菜单
本实例是一个提高效率、人性化的程序
实例位置:光盘\mingrisoft\01\006
实例说明
在安装完瑞星、金山词霸等软件时,在系统的任务栏中会显示一个托盘图标。用户用鼠标右键单击托盘图标,就会弹出一个快捷菜单。本实例将实现一个任务栏托盘菜单,效果如图1.7所示。
图1.7 任务栏托盘弹出菜单
技术要点
要设计任务栏托盘菜单,需要使用Shell_NotifyIcon函数。该函数语法如下:
WINSHELLAPI BOOL WINAPI Shell_NotifyIcon( DWORD dwMessage, PNOTIFYICONDATA pnid );
参数说明:
● dwMessage:表示发送的消息值,其可选值如下。
■ NIM_ADD:表示添加图标到任务栏。
■ NIM_DELETE:表示从任务栏区域删除一个图标。
■ NIM_MODIFY:表示修改任务栏区域的一个图标。
● pnid:是NOTIFYICONDATA结构指针。
NOTIFYICONDATA结构定义如下:
typedef struct _NOTIFYICONDATA { DWORD cbSize; HWND hWnd; UINT uID; UINT uFlags; UINT uCallbackMessage; HICON hIcon; char szTip[64]; } NOTIFYICONDATA, *PNOTIFYICONDATA;
参数说明:
● cbSize确定NOTIFYICONDATA结构的大小。
● hWnd表示接收任务栏菜单消息的窗口句柄。
● uID表示任务栏图标标识符。
● uFlags确定NOTIFYICONDATA结构中哪些成员是合法的。
● uCallbackMessage表示应用程序定义的消息标识符,系统将要发送该消息到hWnd表示的窗口。
● hIcon表示添加、修改或删除的图标句柄。
● szTip是工具提示文本。
实现过程
(1)新建一个基于对话框的应用程序。
(2)从CMenu类派生一个子类CIconMenu,改写DrawItem方法和MeasureItem方法重新绘制菜单。
(3)在对话框类中定义一个NOTIFYICONDATA结构变量m_traydata,在OnInitDialog方法中初始化m_traydata。
m_traydata.cbSize=sizeof(NOTIFYICONDATA); //设置结构大小 m_traydata.hIcon=AfxGetApp()->LoadIcon(IDI_TRAYICON); //加载图标资源 m_traydata.hWnd=m_hWnd; //设置窗口句柄 char *m_str="系统管理"; //声明字符串 //strlen +1表示将空字符复制到目标字符串中 strncpy(m_traydata.szTip,m_str,strlen(m_str)+1); //复制字符串 m_traydata.uCallbackMessage=WM_TRARMESSAGE; //设置回传消息 m_traydata.uFlags=NIF_ICON|NIF_MESSAGE|NIF_TIP; //设置合法参数
(4)在对话框的OnSysCommand方法中判断用户是否单击了“最小化”按钮,如果是,隐藏对话框,调用Shell_NotifyIcon函数向任务栏中添加图标,代码如下:
void CTrayPopMenuDlg::OnSysCommand(UINT nID, LPARAM lParam) { if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else if((nID&0xFFF0)==SC_MINIMIZE) //如果是最小化消息 { ShowWindow(SW_HIDE); //隐藏窗口 Shell_NotifyIcon(NIM_ADD,&m_traydata); //添加系统托盘图标 } else { CDialog::OnSysCommand(nID, lParam); } }
(5)在对话框的消息映射部分添加映射宏。
ON_MESSAGE(WM_TRARMESSAGE,OnTrayMessage)
(6)向对话框中添加OnTrayMessage方法,代码如下:
void CTrayPopMenuDlg::OnTrayMessage(WPARAM wParam, LPARAM lParam) { if(lParam==WM_LBUTTONDOWN) //如果在托盘图标上单击鼠标左键 { ShowWindow(SW_RESTORE); //恢复显示窗口 } else if(lParam==WM_RBUTTONDOWN) //如果在托盘图标上单击鼠标右键 { CPoint m_point; ::GetCursorPos(&m_point); //获得鼠标当前位置 CIconMenu*m_submenu=(CIconMenu*)m_menu.GetSubMenu(0); //获得子菜单 m_submenu->TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTBUTTON, m_point.x,m_point.y,AfxGetApp()->m_pMainWnd,TPM_LEFTALIGN); //显示弹出菜单 } }
举一反三
根据本实例,读者可以:
修改和删除系统托盘菜单。