自绘制菜单带图标

## 个性定制菜单带图标 ##

今天把程序托到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);

posted @ 2016-06-16 00:16  zhenw0  Views(1117)  Comments(0Edit  收藏  举报