自绘制菜单带图标
## 个性定制菜单带图标 ##
今天把程序托到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_1 + IDB_BMP_1));
//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. 以上代码发布时改动过,没有测试,如果有出入还请自行改制
http://pan.baidu.com/s/1pKX7ixd 44y4
简单办法将图标弄小写,直接设置图标吧。这样有个弊端,那就是设置的bmp不能透明化,但是自己重绘就可以透明了。
HBITMAP hBmpAbout = LoadBitmap(hInst,MAKEINTRESOURCE(IDB_BMP_ABOUT));
SetMenuItemBitmaps(hMenu, ID_HELP_ABOUT, MF_BYCOMMAND, hBmpAbout,hBmpAbout);
## 个性定制菜单带图标 ##
今天把程序托到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_MENU1 + IDB_BMP_MENU1)); //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);