PowerBASIC之系统菜单的自绘方法

本文介绍了一种通过修改背景颜色和使用自定义图标来美化系统菜单的方法,并提供了详细的代码实现过程。

一、前言:

当你在单击窗口标题栏图标或点击鼠标右键时,系统会弹出一个默认菜单,如果在我们自己的程序中需要统一界面风格,则需要对这个弹出菜单进行些美化处理。

先上效果图,下面再演示实现方法:

二、实现方法:

1、首先初始化这个菜单,修改背景及添加MFT_OWNERDRAW类型风格。

初始化步骤可以是在%WM_INITDIALOG或者%WM_INITMENU消息中完成,我这里选择是在%WM_INITMENU时,对系统菜单进行初始化工作。

GLOBAL oldID AS LONG
GLOBAL oldText AS RECT
GLOBAL oldRSelect AS RECT
GLOBAL oldLSelect AS RECT
GLOBAL oldIcon AS DWORD 

CASE %WM_INITMENU
  LOCAL lpMenuInfo AS MENUINFO 
  LOCAL mii AS MENUITEMINFO
  LOCAL hMenu AS DWORD

  hMenu = GetSystemMenu ( CB.HNDL, %FALSE ) '取得系统菜单句柄
  '设置默认背景色
  lpMenuInfo.cbSize = SIZEOF(MENUINFO)
  lpMenuInfo.fMask = %MIM_BACKGROUND
  lpMenuInfo.hbrBack = CreateSolidBrush(RGB(46,46,46)) '背景色
  SetMenuInfo ( hMenu, lpMenuInfo )
  DrawMenuBar ( CB.HNDL )

  '设置菜单栏项目为自绘模式
  ZeroMemory BYVAL VARPTR(lpMenuInfo), SIZEOF(MENUINFO)    
  mii.cbSize = SIZEOF(mii)  
  mii.fMask = %MIIM_FTYPE OR %MIIM_BITMAP OR %MIIM_DATA   
  mii.fType = %MFT_OWNERDRAW
  mii.hbmpItem = %HBMMENU_CALLBACK 
  FOR i = 0 TO GetMenuItemCount ( hMenu ) - 1 
     IF i=0 THEN
        mii.dwItemData = LoadImage(%NULL, "icon\menu_restore_a.ico", %IMAGE_ICON, 16, 16, %LR_DEFAULTCOLOR OR %LR_CREATEDIBSECTION OR %LR_LOADFROMFILE )
     END IF
     IF i=3 THEN
        mii.dwItemData = LoadImage(%NULL, "icon\menu_min_a.ico", %IMAGE_ICON, 16, 16, %LR_DEFAULTCOLOR OR %LR_CREATEDIBSECTION OR %LR_LOADFROMFILE )
     END IF
     IF i=4 THEN
        mii.dwItemData = LoadImage(%NULL, "icon\menu_max_a.ico", %IMAGE_ICON, 16, 16, %LR_DEFAULTCOLOR OR %LR_CREATEDIBSECTION OR %LR_LOADFROMFILE )
     END IF
     IF i=6 THEN
        mii.dwItemData = LoadImage(%NULL, "icon\menu_exit_a.ico", %IMAGE_ICON, 16, 16, %LR_DEFAULTCOLOR OR %LR_CREATEDIBSECTION OR %LR_LOADFROMFILE )
     END IF  
     SetMenuItemInfo ( hMenu, i, %true, mii )
  NEXT i
  DrawMenuBar ( CB.HNDL ) 

  oldID = 0 
  oldIcon = 0
  ZeroMemory BYVAL VARPTR(oldText), SIZEOF(oldText)
  ZeroMemory BYVAL VARPTR(oldRSelect), SIZEOF(oldRSelect)
  ZeroMemory BYVAL VARPTR(oldLSelect), SIZEOF(oldLSelect)

  FUNCTION = %TRUE
  EXIT FUNCTION
  

2、初始化菜单项规格。在菜单收到MFT_OWNERDRAW标识通知后,便可以接收到 %WM_DRAWITEM 及 %WM_MEASUREITEM 消息,其中%WM_MEASUREITEM 消息下完成菜单项宽、高的定义。

CASE %WM_MEASUREITEM
  LOCAL pdis AS MEASUREITEMSTRUCT PTR
  pdis = CB.LPARAM
  IF @pdis.CtlType = %ODT_MENU THEN
      @pdis.itemWidth  = 161
      @pdis.itemHeight = 20
  END IF

  FUNCTION = %TRUE
  EXIT FUNCTION 

3、完成上述两个步骤后,我既可以开始在 %WM_DRAWITEM消息体下,去绘制自己设定的样式风格了(直接在HDC上绘制即可)。

CASE %WM_DRAWITEM 
  LOCAL hvBrush AS DWORD
  LOCAL lpdis AS DRAWITEMSTRUCT PTR
  lpdis = CB.LPARAM
  IF @lpdis.CtlType = %ODT_MENU THEN
     LOCAL hFont AS DWORD
     LOCAL ItemCount AS LONG  
     LOCAL MenuWidth AS LONG
     LOCAL MenuHigh AS LONG     
     LOCAL rcRight AS RECT
     LOCAL rcLeft AS RECT
     LOCAL rcRSelect AS RECT
     LOCAL rcLSelect AS RECT 

     hMenu = @lpdis.hwndItem
     MenuWidth = @lpdis.rcItem.right-@lpdis.rcItem.left
     MenuHigh = @lpdis.rcItem.bottom-@lpdis.rcItem.top  
     ItemCount = GetMenuItemCount ( hMenu )

     hFont = GetStockObject( %DEFAULT_GUI_FONT ) '菜单字体
     SelectObject ( @lpdis.hdc, hFont )
     SetBkMode @lpdis.hdc, %TRANSPARENT 
     SetTextColor @lpdis.hdc, RGB(112,112,112) 
  
     '-------------------------------
     '绘制左MenuBar
     '-------------------------------
     SetRect(rcLeft, 0, @lpdis.rcItem.top, 28, @lpdis.rcItem.bottom)
     hvBrush = CreateSolidBrush(RGB(113,96,232))
     FillRect(@lpdis.hdc,rcLeft,hvBrush)
     DeleteObject hvBrush

     SELECT CASE @lpdis.itemID
         CASE %SC_RESTORE
             DrawIconEx(@lpdis.hdc, 7, 2, @lpdis.itemData, 0, 0, 0, %NULL, %DI_NORMAL OR %DI_COMPAT )
         CASE %SC_MINIMIZE
             DrawIconEx(@lpdis.hdc, 7, 62, @lpdis.itemData, 0, 0, 0, %NULL, %DI_NORMAL OR %DI_COMPAT )
         CASE %SC_MAXIMIZE
             DrawIconEx(@lpdis.hdc, 7, 82, @lpdis.itemData, 0, 0, 0, %NULL, %DI_NORMAL OR %DI_COMPAT )
         CASE %SC_CLOSE
             DrawIconEx(@lpdis.hdc, 7, 122, @lpdis.itemData, 0, 0, 0, %NULL, %DI_NORMAL OR %DI_COMPAT )
     END SELECT   

     '-------------------------------
     '绘制右文本区
     '-------------------------------
     SetRect(rcRSelect, 30, @lpdis.rcItem.top, MenuWidth, @lpdis.rcItem.bottom)
     SetRect(rcRight, 32, @lpdis.rcItem.top, MenuWidth, @lpdis.rcItem.bottom)
     hvBrush = CreateSolidBrush(RGB(46,46,46)) '背景色
     FillRect(@lpdis.hdc,rcRight,hvBrush)
     DeleteObject hvBrush

     GetMenuString( hMenu, @lpdis.itemID, czText, 256, %MF_BYCOMMAND )
     ReplaceHotkeys(VARPTR(czText),LEN(czText))
     DrawText(@lpdis.hdc, czText, -1, rcRight, %DT_SINGLELINE OR %DT_LEFT OR %DT_VCENTER )    

     '-------------------------------
     '绘制分割线
     '-------------------------------
     IF @lpdis.itemID = 0 THEN
        hvBrush = CreateSolidBrush(RGB(112,112,112))                    
        SetRect(rc,30,@lpdis.rcItem.top+10,@lpdis.rcItem.right,@lpdis.rcItem.top+11)
        FillRect(@lpdis.hdc,rc,hvBrush)
        DeleteObject hvBrush
     END IF 

     '-------------------------------
     '绘制选择菜单项时的鼠标移动效果
     '-------------------------------
     IF @lpdis.itemAction = %ODA_SELECT AND @lpdis.itemID > 0 THEN
        '--绘制左半部选项条--
        hvBrush = CreateSolidBrush(RGB(255,0,0))
        FillRect(@lpdis.hdc,rcRSelect,hvBrush)
        DeleteObject hvBrush
        '--绘制右半部选项条--
        hvBrush = CreateSolidBrush(BGR(220,240,120))
        FillRect(@lpdis.hdc,rcLeft,hvBrush)
        DeleteObject hvBrush
        '--绘制文本--
        SetTextColor @lpdis.hdc, RGB(255,255,255)
        GetMenuString( hMenu, @lpdis.itemID, czText, 256, %MF_BYCOMMAND )
        DrawText( @lpdis.hdc, czText, -1, rcRight, %DT_SINGLELINE OR %DT_LEFT OR %DT_VCENTER ) 
        '--绘制选项条左部图标-- 
        SELECT CASE @lpdis.itemID
            CASE %SC_RESTORE
                DrawIconEx(@lpdis.hdc, 7, 2, @lpdis.itemData, 0, 0, 0, %NULL, %DI_NORMAL OR %DI_COMPAT )
            CASE %SC_MINIMIZE
                DrawIconEx(@lpdis.hdc, 7, 62, @lpdis.itemData, 0, 0, 0, %NULL, %DI_NORMAL OR %DI_COMPAT )
            CASE %SC_MAXIMIZE
                DrawIconEx(@lpdis.hdc, 7, 82, @lpdis.itemData, 0, 0, 0, %NULL, %DI_NORMAL OR %DI_COMPAT )
            CASE %SC_CLOSE
                DrawIconEx(@lpdis.hdc, 7, 122, @lpdis.itemData, 0, 0, 0, %NULL, %DI_NORMAL OR %DI_COMPAT )
        END SELECT 

        '--恢复鼠标移动后,上一个选项的背景及文本--
        hvBrush = CreateSolidBrush(RGB(46,46,46)) '背景色
        FillRect(@lpdis.hdc,oldRSelect,hvBrush)
        DeleteObject hvBrush
        hvBrush = CreateSolidBrush(RGB(113,96,232)) '
        FillRect(@lpdis.hdc,oldLSelect,hvBrush)
        DeleteObject hvBrush
        '--绘制文本--
        SetTextColor @lpdis.hdc, RGB(112,112,112)
        GetMenuString( hMenu, oldID, czText, 256, %MF_BYPOSITION )
        DrawText( @lpdis.hdc, czText, -1, oldText, %DT_SINGLELINE OR %DT_LEFT OR %DT_VCENTER )
        '--绘制选项条左部移动后上一个图标-- 
        SELECT CASE oldID
            CASE 0
                DrawIconEx(@lpdis.hdc, 7, 2, oldIcon, 0, 0, 0, %NULL, %DI_NORMAL OR %DI_COMPAT )
            CASE 3
                DrawIconEx(@lpdis.hdc, 7, 62, oldIcon, 0, 0, 0, %NULL, %DI_NORMAL OR %DI_COMPAT )
            CASE 4
                DrawIconEx(@lpdis.hdc, 7, 82, oldIcon, 0, 0, 0, %NULL, %DI_NORMAL OR %DI_COMPAT )
            CASE 6
                DrawIconEx(@lpdis.hdc, 7, 122, oldIcon, 0, 0, 0, %NULL, %DI_NORMAL OR %DI_COMPAT )
        END SELECT  

        oldID = GetMenuItemPos(hMenu,@lpdis.itemID)
        oldIcon = @lpdis.itemData 
        CopyRect(oldText,rcRight)
        CopyRect(oldRSelect,rcRSelect)
        CopyRect(oldLSelect,rcLeft)                   
     END IF

     DeleteObject hFont      
    
  END IF  

  FUNCTION = %TRUE
  EXIT FUNCTION
'-------------------------------
'取消快捷键定义字母下划线
'-------------------------------
SUB ReplaceHotkeys(BYVAL s AS BYTE PTR, BYVAL k AS LONG)
    DIM i AS INTEGER
    FOR i = 0 TO k
        IF @s[i] = 38 THEN
           @s[i] = 9
        END IF
    NEXT i

END SUB

'-------------------------------
'根据ID返回序号
'-------------------------------
FUNCTION GetMenuItemPos(BYVAL hMenu AS DWORD, ItemID AS DWORD) AS LONG
    LOCAL i AS LONG
    LOCAL ItemCount AS LONG
    ItemCount = GetMenuItemCount ( hMenu ) - 1
    FOR i=0 TO ItemCount
        IF GetMenuItemID(hMenu,i)=ItemID THEN
            FUNCTION = i
            EXIT FUNCTION
        END IF
    NEXT i
END FUNCTION 

 

 三、自此一个具有个性化的标题菜单就绘制完成了。由于水平有限代码未必严谨,仅供大家交流学习。

从VC++项目中的菜单资源建立结构相同的自弹出式菜单,原理和步骤如下: (1)CMenu::LoadMenu读入菜单资源; (2)CImageList::Create读入工具栏位图; (3)CMenu::CreatePopupMenu和CMenu::AppendMenu拷贝菜单资源,建立弹出式菜单。其中CMenu::AppendMenu第1个参数设置成MF_OWNERDRAW(自), 第四个参数设置成一个附加结构的指针,包括菜单项文字和位图索引等信息。通过这个结构,在自制时,可以获取对应的菜单项文字和位图位置索引,其中位图保存在第(2)步中的CImageList变量中; (4)在对右鼠标键的响应函数里,使用CMenu::TrackPopupMenu启动显示弹出式菜单; (5)在弹出式菜单的拥有者窗口(CxxxView)里,处理WM_MEASUREITEM消息和WM_DRAWITEM消息,分别调用CMenuEx::MeasureItem和CMenuEx::DrawItem, 分别用来定义菜单项的尺寸,对菜单项进行自; (6)在自函数CMenuEx::DrawItem里,通过每个菜单项的附加结构lpDIS->itemData,获得其文字和位图索引,然后分别使用CDC::DrawText和CImageList::Draw,画出该菜单项的文字和位图,从而实现自制。 程序在VC6下编译通过。 没有处理的地方:如果菜单项状态是checked或者radio,程序没做处理。另外,弹出式菜单的激活/禁止时,不会自动触发其拥有者窗口的ON_UPDATE_COMMAND_UI宏。不过,可以处理owner窗口的WM_INITMEMUPOPUP消息(在弹出式菜单的每个子菜单弹出时,都会发出此消息),为每个子菜单项单独生成一个CCmdUI对象,调用其CCmdUI::DoUpdate函数,来手动触发ON_UPDATE_COMMAND_UI宏中对应的消息处理函数,使得菜单项能够根据应用环境进行激活和禁止。详见博客: http://oliver.zheng.blog.163.com/blog/static/14241159520143210595266/
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值