## 个性定制菜单带图标 ## 今天把程序托到xp虚拟机,打算看下在xp的表现,发现菜单的小图标16*16的显示3/4左右,很丑,便有了此篇文章 ## 实现方法 ## 有点像Listview的item重绘(自绘)实现。3点 1. 设置菜单为MF_OWNERDRAW风格,(自绘风格) 2. 处理WM_MEASUREITEM消息来设置菜单ITEM的高度 3. 处理WM_DRAWITEM消息来绘制图表文字。。 是不是和listview item重绘如出一辙呀!(有空分享下listview的重绘) ### 设置菜单为MF_OWNERDRAW风格 ### 1. 循环遍历菜单的每个item设置风格 void ModifyMenuOwnDraw(HMENU hMen) { HMENU hSubMenu = NULL ; int iMenuIdx = 0 ; WCHAR wszString[MAX_PATH]; int iMenuItemCount = 0; while ((hSubMenu = GetSubMenu(hMen,iMenuIdx))) { iMenuItemCount = GetMenuItemCount(hSubMenu); for (int i=0;i < iMenuItemCount;i++) { ModifyMenu(hSubMenu,i,MF_BYPOSITION|MF_OWNERDRAW,NULL,NULL); } iMenuIdx ++; } } 2. 调用时机 我在WM_CREATE消息处理中调用的 3. 效果 每个menu的item都变成小白框了 ### 处理WM_MEASUREITEM消息来设置菜单ITEM的高度 ### 1. 增加 WM_MEASUREITEM 处理函数 void OnMeasureItem(HWND hwnd, MEASUREITEMSTRUCT * lpMeasureItem) { if (lpMeasureItem->CtlType == ODT_MENU ) { // 有ID是正常项,没ID的是分割线 if ( lpMeasureItem->itemID) { lpMeasureItem->itemHeight =24;// GetSystemMetrics(SM_CYMENU); } else { // 分割线就矮点 lpMeasureItem->itemHeight = 3; } lpMeasureItem->itemWidth = 120 ; } } 2. 效果 每个menu的item都变成长条白框了 ###处理WM_DRAWITEM消息来绘制图表文字### 1. 添加 WM_DRAWITEM 处理函数 void OnDrawItem(HWND hwnd,const DRAWITEMSTRUCT * lpDrawItem) { HDC hDc = lpDrawItem->hDC; MENUITEMINFO miinfo ={0} ; WCHAR wszString[MAX_PATH]; RECT rect; memcpy(&rect,&(lpDrawItem->rcItem),sizeof(RECT)); // 这里来判断是自绘的BUTTON 、listview 、MENU 等,可以看看ODT_MENU的定义,就能看到有多少种了 if (lpDrawItem->CtlType == ODT_MENU ) { HBRUSH hBrush ; // 菜单的item有id的是正常选项,没ID的就是分割线;分开对待 if (lpDrawItem->itemID) { // 对于选中的item 我们要 画个大红框 if (lpDrawItem->itemState & ODS_SELECTED ) { hBrush =CreateSolidBrush(RGB(255,20,147)); } else { // 没选中的 要 在绘制回原色 hBrush =CreateSolidBrush(GetSysColor(COLOR_MENU)); } FrameRect(hDc,&rect,hBrush); DeleteBrush(hBrush); }else { // 分割线我们就 只画根大杠杠 SetDCBrushColor(hDc,getsyscolor(COLOR_MENUTEXT)); rect.top +=1; rect.bottom -= 1; Rectangle(hDc,rect.left,rect.top,rect.right,rect.bottom); } if (lpDrawItem->itemID) { // draw img int iAlign = (rect.bottom -rect.top - 16)/2 ; int x = rect.left + iAlign ; int y = rect.top + iAlign ; BITMAP bmp; HDC hdcMem = CreateCompatibleDC(hDc); HBITMAP hBmp = LoadBitmap(hInst,MAKEINTRESOURCE(lpDrawItem->itemID - ID_FILE_LISTENER + IDB_BMP_LISTENER)); //HBITMAP hBmp = (HBITMAP)LoadImage(NULL, "1.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE|LR_CREATEDIBSECTION); GetObject(hBmp, sizeof(BITMAP), &bmp); SelectObject(hdcMem, hBmp); BitBlt(hDc, x, y, 16, 16, hdcMem, 0, 0, SRCCOPY); DeleteDC(hdcMem); DeleteObject(hBmp); //draw text // 要绘制字的先获取下高度 TEXTMETRIC tm; GetTextMetrics(hDc,&tm); // 计算下字所在的rect 这里要是字体上下居中 int iAlignTop = (rect.bottom -rect.top-tm.tmHeight)/2 ; iAlignTop = iAlignTop > 0 ? iAlignTop : -iAlignTop ; rect.top += iAlignTop; rect.bottom -= iAlignTop ; // 留出绘制的 图标 位置来 rect.left+=24; // 先获取字符串 GetMenuString((HMENU)lpDrawItem->hwndItem,lpDrawItem->itemID,wszString,MAX_PATH,MF_BYCOMMAND); // 还要判断是否有 \t 有的话就要分开写了 WCHAR* lpFind = wcschr(wszString,L‘\t‘); if (lpFind) { *lpFind = L‘\0‘; lpFind+=1 ; } DrawTextEx(hDc,wszString,wcslen(wszString),(LPRECT)&(rect),DT_LEFT,NULL); // 有 \t的话 if (lpFind) { rect.left += 50 ; rect.right -= 10; DrawTextEx(hDc,lpFind,wcslen(lpFind),(LPRECT)&(rect),DT_RIGHT,NULL); } } } } 2. 以上代码发布时改动过,没有测试,如果有出入还请自行改制 - 实在想偷懒的,还是设置小图标吧,大小12*12的在xp下不会不适应的。
HBITMAP hBmpShow = LoadBitmap(hInst,MAKEINTRESOURCE(IDB_BMP_SHOW));
SetMenuItemBitmaps(hTrayMenu, ID_NOTIFY_SHOWXMAN, MF_BYCOMMAND, hBmpShow,hBmpShow);
时间: 2024-10-17 09:17:06