zoukankan      html  css  js  c++  java
  • 自绘制菜单带图标

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

    今天把程序托到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);
                    // 还要判断是否有 有的话就要分开写了
                    WCHAR* lpFind = wcschr(wszString,L' ');
                    if (lpFind)
                    {
                        *lpFind = L'';
                        lpFind+=1 ;
                    }
        
        
                    DrawTextEx(hDc,wszString,wcslen(wszString),(LPRECT)&(rect),DT_LEFT,NULL);
                    // 有 的话
                    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);                // 还要判断是否有 有的话就要分开写了                WCHAR* lpFind = wcschr(wszString,L' ');                if (lpFind)                {                    *lpFind = L'';                    lpFind+=1 ;                }                        DrawTextEx(hDc,wszString,wcslen(wszString),(LPRECT)&(rect),DT_LEFT,NULL);                // 有 的话                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);

  • 相关阅读:
    为《理解C#中的System.In32和int:并非鸡和鸡蛋 》做个续
    Windows C++代码heap分析详解
    Windows 内存分析之路 How to use Resource Monitor
    给C++初学者的50个忠告(好文转载)
    The 32bit generalpurpose registers EAX, EBX, ECX, EDX, ESI, EDI, EBP, and ESP
    Exceptional C++ 精华代码—实现异常安全的Stack
    Windows开发的内功和招式
    Windows代码heap内存分析实战
    十分钟让你对C++ Traits大彻大悟
    使用Windows API PostThreadMessage进行线程间消息通信
  • 原文地址:https://www.cnblogs.com/witty/p/DRAW_MENU.html
Copyright © 2011-2022 走看看