分类:
2009-11-13 11:12:54
图标菜单设计方案
方案分析 在MFC中提供了CMenu类用于进行菜单设计,默认情况下,CMenu中的菜单项是不能够显示图标或位图等信息的。但是在设计应用程序界面时,通常需要实现该功能。其实,设计图标菜单并不像想象中的那么困难,可以有两种方法实现图标菜单。第一种方法是使用CMenu类提供的SetMenuItemBitmaps方法,另一种方法是重载CMenu类的DrawItem方法和MeasureItem方法。相对而言,第一种方法比较简单,容易实现,但是菜单效果并不理想,第二种方法略微复杂一些,但是用户可以随意设置自己想要的图标菜单。图1、图2分别显示了这两种方法设计的图标菜单。 图1 图标菜单自绘设计方案一 图2 图标菜单自绘设计方案二 实施过程 1 使用SetMenuItemBitmaps方法设计图标菜单 (1)创建一个基于对话框的工程,向对话框中添加Picture控件。 (2)在工作区的“Resource View”选项中创建一个菜单资源,如图3所示。 图3 菜单资源设计 (3)在对话框类中定义一个CMenu对象和一个CBitmap对象。 CMenu m_Menu; CBitmap bmp; (4)在对话框初始化时加载菜单资源,遍历菜单项,将菜单项与位图关联。 BOOL CIconMenuDlg::On { CDialog::On ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { CString strAboutMenu; strAboutMenu.LoadString(IDS_ABOUTBOX); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } SetIcon(m_hIcon, TRUE); SetIcon(m_hIcon, FALSE); m_Menu.LoadMenu(IDR_MAINMENU); SetMenu(&m_Menu); int ItemCount = m_Menu.GetMenuItemCount(); int BmpIndex = IDB_BITMAP1; for (int i = 0 ; i< ItemCount; i++) { CMenu* pSubMenu = m_Menu.GetSubMenu(i); if (pSubMenu != NULL) { int SubCount = pSubMenu->GetMenuItemCount(); for (int j = 0 ; j < SubCount; j++) { bmp.LoadBitmap(BmpIndex); pSubMenu->SetMenuItemBitmaps(j,MF_BYPOSITION,&bmp,NULL); BmpIndex++; bmp.Detach(); } } } return TRUE; } 2 重载DrawItem方法和MeasureItem方法设置图标菜单 (1)创建一个基于对话框的工程,在对话框中添加Picture控件。 (2)定义一个菜单项结构,记录菜单项的附加信息,系统将会为每个菜单项关联一个菜单项结构对象。 struct CMenuItemInfo { CString m_ItemText; //菜单项文本 int m_IconIndex; //菜单项索引 int m_ItemID; //菜单标记 -2顶层菜单,-1弹出式菜单,0分隔条,其他普通菜单 }; (3)从CMenu类派生一个子类CIconMenu,在CIconMenu类中定义如下成员变量。 CMenuItemInfo m_ItemLists[MAX_MENUCOUNT]; //菜单项信息 int m_index; //临时索引 int m_iconindex; CImageList m_imagelist; //存储菜单项图标 (4)在CIconMenu类的构造函数中创建图像列表控件,并向图像列表控件中添加图标。 CIconMenu::CIconMenu() { m_index= 0; m_iconindex= 0; //创建图像列表 m_imagelist.Create(16,16,ILC_COLOR24|ILC_MASK,0,0); //添加图标 m_imagelist.Add(AfxGetApp()->LoadIcon(IDI_ICON1)); m_imagelist.Add(AfxGetApp()->LoadIcon(IDI_ICON2)); m_imagelist.Add(AfxGetApp()->LoadIcon(IDI_ICON3)); m_imagelist.Add(AfxGetApp()->LoadIcon(IDI_ICON4)); m_imagelist.Add(AfxGetApp()->LoadIcon(IDI_ICON5)); m_imagelist.Add(AfxGetApp()->LoadIcon(IDI_ICON6)); m_imagelist.Add(AfxGetApp()->LoadIcon(IDI_ICON7)); m_imagelist.Add(AfxGetApp()->LoadIcon(IDI_ICON8)); m_imagelist.Add(AfxGetApp()->LoadIcon(IDI_ICON9)); m_imagelist.Add(AfxGetApp()->LoadIcon(IDI_ICON10)); } (5)在CIconMenu类的析构函数中释放图像列表控件资源。 CIconMenu::~CIconMenu() { m_imagelist.Detach(); } (6)向CIconMenu类中添加AttatchMenu方法,将CIconMenu对象关联到菜单资源上。 BOOL CIconMenu::AttatchMenu(HMENU m_hmenu) { this->Attach(m_hmenu); return TRUE; } (7)向CIconMenu类中添加ChangeMenuItem方法,利用递归遍历每一个菜单项,为菜单项关联CMenuItemInfo类对象。 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_menu->GetMenuString(i,m_ItemLists[m_index].m_ItemText,MF_BYPOSITION); int m_itemID = m_menu->GetMenuItemID(i); if (m_itemID==-1 && m_Toped) { m_itemID = -2;//顶层菜单 }; m_ItemLists[m_index].m_ItemID = m_itemID; 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)向CIconMenu类中添加DrawItemText方法,在指定的区域绘制菜单项文本。 /************************************************************* 功能描述: 绘制菜单项文本 参数说明: m_pdc标识画布对象,str标识菜单文本,m_rect标识菜单区域 *************************************************************/ void CIconMenu::DrawItemText(CDC* m_pdc,LPSTR str,CRect m_rect) { m_rect.DeflateRect(20,0); m_pdc->DrawText(str,m_rect,DT_SINGLELINE|DT_VCENTER|DT_LEFT); } void CIconMenu::DrawTopMenu(CDC* m_pdc,CRect m_rect,BOOL m_selected ) { if (m_selected) { m_pdc->SelectStockObject(BLACK_PEN); m_pdc->Rectangle(&m_rect); m_rect.DeflateRect(1,1); m_pdc->FillSolidRect(m_rect,RGB(150, 185, 255)); } else { CRect rect; AfxGetMainWnd()->GetClientRect(rect); rect.top = m_rect.top; rect.bottom = m_rect.bottom; rect.left= 400; rect.right +=4; m_pdc->FillSolidRect(&rect,RGB(200,187, 255)); m_pdc->FillSolidRect(&m_rect,RGB(200,187, 255)); } } (9)向CIconMenu类中添加DrawTopMenu方法,绘制顶层菜单。 void CIconMenu::DrawTopMenu(CDC* m_pdc,CRect m_rect,BOOL m_selected ) { if (m_selected) { m_pdc->SelectStockObject(BLACK_PEN); m_pdc->Rectangle(&m_rect); m_rect.DeflateRect(1,1); m_pdc->FillSolidRect(m_rect,RGB(150, 185, 255)); } else { CRect rect; AfxGetMainWnd()->GetClientRect(rect); rect.top = m_rect.top; rect.bottom = m_rect.bottom; rect.left= 400; rect.right +=4; m_pdc->FillSolidRect(&rect,RGB(200,187, 255)); m_pdc->FillSolidRect(&m_rect,RGB(200,187, 255)); } } (10)向CIconMenu类中添加DrawSeparater方法,绘制菜单的分隔条。 void CIconMenu::DrawSeparater(CDC* m_pdc,CRect m_rect) { if (m_pdc != NULL) { m_pdc->Draw3dRect(m_rect,RGB(255,0,0),RGB(0,0,255)); } } (11)向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->Rectangle(m_rect); m_rect.DeflateRect(1,1); int r1,g1,b1; //读取渐变起点的颜色值 r1 = GetRValue(m_fromcolor); g1 = GetGValue(m_fromcolor); b1 = GetBValue(m_fromcolor); int r2,g2,b2; //读取渐变终点的颜色值 r2 = GetRValue(m_tocolor); g2 = GetGValue(m_tocolor); b2 = GetBValue(m_tocolor); float r3,g3,b3;//菜单区域水平方向每个点RGB值应该变化的度(范围) r3 = ((float)(r2-r1)) / (float)(m_rect.Height()); g3 = (float)(g2-g1)/(float)(m_rect.Height()); b3 = (float)(b2-b1)/(float)(m_rect.Height()); COLORREF r,g,b;//菜单区域水平方向每个点的颜色值 CPen* m_oldpen ; for (int i = m_rect.top;i { r = r1+(int)r3*(i-m_rect.top); g = g1+(int)g3*(i-m_rect.top); b = b1+ (int)b3*(i-m_rect.top); CPen m_pen (PS_SOLID,1,RGB(r,g,b)); m_oldpen = m_pdc->SelectObject(&m_pen); m_pdc->MoveTo(m_rect.left,i); m_pdc->LineTo(m_rect.right,i); } m_pdc->SelectObject(m_oldpen); } else { m_pdc->FillSolidRect(m_rect,RGB(0x000000F9, 0x000000F8, 0x000000F7)); } } (12)向CIconMenu类中添加DrawMenuIcon方法,将图像列表中的图像绘制到菜单项中。 void CIconMenu::DrawMenuIcon(CDC* m_pdc,CRect m_rect,int m_icon ) { m_imagelist.Draw(m_pdc,m_icon,CPoint(m_rect.left+2,m_rect.top+4),ILD_TRANSPARENT); } (13)重载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; break; } } } } (14)重载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); //m_dc.Attach(lpStruct->hDC); CString str = ((CMenuItemInfo*)(lpStruct->itemData))->m_ItemText; LPSTR m_str = str.GetBuffer(str.GetLength()); int m_itemID = ((CMenuItemInfo*)(lpStruct->itemData))->m_ItemID; 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); DrawMenuIcon(m_dc,m_rect,m_itemicon); break; } } } } (15)处理对话框类的WM_DRAWITEM消息,调用CMenu类的DrawItem方法。 void CDrawMenuDlg::On { m_Menu.DrawItem(lpDrawItemStruct); // CDialog::On } (16)处理对话框类的WM_MEASUREITEM消息,调用CMenu类的MeasureItem方法。 void CDrawMenuDlg::On { m_Menu.MeasureItem(lpMeasureItemStruct); // CDialog::On } (17)在对话框初始化时加载菜单资源,修改菜单项信息。 BOOL CDrawMenuDlg::On { CDialog::On ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { CString strAboutMenu; strAboutMenu.LoadString(IDS_ABOUTBOX); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } SetIcon(m_hIcon, TRUE); SetIcon(m_hIcon, FALSE); m_Menu.AttatchMenu(LoadMenu(NULL,MAKEINTRESOURCE(IDR_MAINMENU))); m_Menu.ChangeMenuItem(&m_Menu,TRUE); SetMenu(&m_Menu); return TRUE; |