VB打造超酷个性化菜单(六)
(接上篇)
' 拦截菜单消息 (frmMenu 窗口入口函数) Function MenuWndProc(ByVal hwnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long Select Case Msg Case WM_COMMAND ' 单击菜单项 If MyItemInfo(wParam).itemType = MIT_CHECKBOX Then If MyItemInfo(wParam).itemState = MIS_CHECKED Then MyItemInfo(wParam).itemState = MIS_UNCHECKED Else MyItemInfo(wParam).itemState = MIS_CHECKED End If End If MenuItemSelected wParam Case WM_EXITMENULOOP ' 退出菜单消息循环(保留) Case WM_MEASUREITEM ' 处理菜单项高度和宽度 MeasureItem hwnd, lParam Case WM_MENUSELECT ' 选择菜单项 Dim itemID As Long itemID = GetMenuItemID(lParam, wParam And &HFF) If itemID <> -1 Then MenuItemSelecting itemID End If Case WM_DRAWITEM ' 绘制菜单项 DrawItem lParam End Select MenuWndProc = CallWindowProc(preMenuWndProc, hwnd, Msg, wParam, lParam) End Function
' 处理菜单高度和宽度 Private Sub MeasureItem(ByVal hwnd As Long, ByVal lParam As Long) Dim TextSize As Size, hdc As Long hdc = GetDC(hwnd) CopyMemory MeasureInfo, ByVal lParam, Len(MeasureInfo) If MeasureInfo.CtlType And ODT_MENU Then MeasureInfo.itemWidth = lstrlen(MyItemInfo(MeasureInfo.itemID).itemText) * (GetSystemMetrics(SM_CYMENU) / 2.5) + BarWidth If MyItemInfo(MeasureInfo.itemID).itemType <> MIT_SEPARATOR Then MeasureInfo.itemHeight = GetSystemMetrics(SM_CYMENU) Else MeasureInfo.itemHeight = 6 End If End If CopyMemory ByVal lParam, MeasureInfo, Len(MeasureInfo) ReleaseDC hwnd, hdc End Sub
' 绘制菜单项 Private Sub DrawItem(ByVal lParam As Long) Dim hPen As Long, hBrush As Long Dim itemRect As RECT, barRect As RECT, iconRect As RECT, textRect As RECT Dim i As Long CopyMemory DrawInfo, ByVal lParam, Len(DrawInfo) If DrawInfo.CtlType = ODT_MENU Then SetBkMode DrawInfo.hdc, TRANSPARENT ' 初始化菜单项矩形, 图标矩形, 文字矩形 itemRect = DrawInfo.rcItem iconRect = DrawInfo.rcItem textRect = DrawInfo.rcItem ' 设置菜单附加条矩形 With barRect .Left = 0 .Top = 0 .Right = BarWidth - 1 For i = 0 To GetMenuItemCount(hMenu) - 1 If MyItemInfo(i).itemType = MIT_SEPARATOR Then .Bottom = .Bottom + 6 Else .Bottom = .Bottom + MeasureInfo.itemHeight End If Next i .Bottom = .Bottom - 1 End With ' 设置图标矩形, 文字矩形 If BarStyle <> LBS_NONE Then iconRect.Left = barRect.Right + 2 iconRect.Right = iconRect.Left + 20 textRect.Left = iconRect.Right + 3 With DrawInfo ' 画菜单背景 itemRect.Left = barRect.Right hBrush = CreateSolidBrush(BkColor) FillRect .hdc, itemRect, hBrush DeleteObject hBrush
' 画菜单左边的附加条 Dim RedArea As Long, GreenArea As Long, BlueArea As Long Dim red As Long, green As Long, blue As Long Select Case BarStyle Case LBS_NONE ' 无附加条
Case LBS_SOLIDCOLOR ' 实色填充
hBrush = CreateSolidBrush(BarStartColor) FillRect .hdc, barRect, hBrush DeleteObject hBrush
Case LBS_HORIZONTALCOLOR ' 水平过渡色
BlueArea = Int(BarEndColor / &H10000) - Int(BarStartColor / &H10000) GreenArea = (Int(BarEndColor / &H100) And &HFF) - (Int(BarStartColor / &H100) And &HFF) RedArea = (BarEndColor And &HFF) - (BarStartColor And &HFF)
For i = 0 To BarWidth - 1 red = Int(BarStartColor And &HFF) + Int(i / BarWidth * RedArea) green = (Int(BarStartColor / &H100) And &HFF) + Int(i / BarWidth * GreenArea) blue = Int(BarStartColor / &H10000) + Int(i / BarWidth * BlueArea) hPen = CreatePen(PS_SOLID, 1, RGB(red, green, blue)) Call SelectObject(.hdc, hPen) Call MoveToEx(.hdc, i, 0, 0) Call LineTo(.hdc, i, barRect.Bottom) Call DeleteObject(hPen) Next i
Case LBS_VERTICALCOLOR ' 垂直过渡色
BlueArea = Int(BarEndColor / &H10000) - Int(BarStartColor / &H10000) GreenArea = (Int(BarEndColor / &H100) And &HFF) - (Int(BarStartColor / &H100) And &HFF) RedArea = (BarEndColor And &HFF) - (BarStartColor And &HFF)
For i = 0 To barRect.Bottom red = Int(BarStartColor And &HFF) + Int(i / (barRect.Bottom + 1) * RedArea) green = (Int(BarStartColor / &H100) And &HFF) + Int(i / (barRect.Bottom + 1) * GreenArea) blue = Int(BarStartColor / &H10000) + Int(i / (barRect.Bottom + 1) * BlueArea) hPen = CreatePen(PS_SOLID, 1, RGB(red, green, blue)) Call SelectObject(.hdc, hPen) Call MoveToEx(.hdc, 0, i, 0) Call LineTo(.hdc, barRect.Right, i) Call DeleteObject(hPen) Next i
Case LBS_IMAGE ' 图像
If BarImage.Handle <> 0 Then Dim barhDC As Long barhDC = CreateCompatibleDC(GetDC(0)) SelectObject barhDC, BarImage.Handle BitBlt .hdc, 0, 0, BarWidth, barRect.Bottom - barRect.Top + 1, barhDC, 0, 0, vbSrcCopy DeleteDC barhDC End If
End Select ' 画菜单项 If MyItemInfo(.itemID).itemType = MIT_SEPARATOR Then ' 画菜单分隔条(MIT_SEPARATOR) If MyItemInfo(.itemID).itemType = MIT_SEPARATOR Then itemRect.Top = itemRect.Top + 2 itemRect.Bottom = itemRect.Top + 1 itemRect.Left = barRect.Right + 5 Select Case SepStyle Case MSS_NONE ' 无分隔条 Case MSS_DEFAULT ' 默认样式 DrawEdge .hdc, itemRect, EDGE_ETCHED, BF_TOP Case Else ' 其它 hPen = CreatePen(SepStyle, 0, SepColor) hBrush = CreateSolidBrush(BkColor) SelectObject .hdc, hPen SelectObject .hdc, hBrush Rectangle .hdc, itemRect.Left, itemRect.Top, itemRect.Right, itemRect.Bottom DeleteObject hPen DeleteObject hBrush End Select End If Else If Not CBool(MyItemInfo(.itemID).itemState And MIS_DISABLED) Then ' 当菜单项可用时 If .itemState And ODS_SELECTED Then ' 当鼠标移动到菜单项时 ' 设置菜单项高亮范围 If SelectScope And ISS_ICON_TEXT Then itemRect.Left = iconRect.Left ElseIf SelectScope And ISS_TEXT Then itemRect.Left = textRect.Left - 2 Else itemRect.Left = .rcItem.Left End If ' 处理菜单项无图标或为CHECKBOX时的情况 If (MyItemInfo(.itemID).itemType = MIT_CHECKBOX Or MyItemInfo(.itemID).itemIcon = 0) And SelectScope <> ISS_LEFTBAR_ICON_TEXT Then itemRect.Left = iconRect.Left End If ' 画菜单项边框 Select Case EdgeStyle Case ISES_NONE ' 无边框 Case ISES_SUNKEN ' 凹进 DrawEdge .hdc, itemRect, BDR_SUNKENOUTER, BF_RECT Case ISES_RAISED ' 凸起 DrawEdge .hdc, itemRect, BDR_RAISEDINNER, BF_RECT Case Else ' 其它 hPen = CreatePen(EdgeStyle, 0, EdgeColor) hBrush = CreateSolidBrush(BkColor) SelectObject .hdc, hPen SelectObject .hdc, hBrush Rectangle .hdc, itemRect.Left, itemRect.Top, itemRect.Right, itemRect.Bottom DeleteObject hPen DeleteObject hBrush End Select ' 画菜单项背景 InflateRect itemRect, -1, -1 Select Case FillStyle Case ISFS_NONE ' 无背景 Case ISFS_HORIZONTALCOLOR ' 水平渐变色 BlueArea = Int(FillEndColor / &H10000) - Int(FillStartColor / &H10000) GreenArea = (Int(FillEndColor / &H100) And &HFF) - (Int(FillStartColor / &H100) And &HFF) RedArea = (FillEndColor And &HFF) - (FillStartColor And &HFF) For i = itemRect.Left To itemRect.Right - 1 red = Int(FillStartColor And &HFF) + Int((i - itemRect.Left) / (itemRect.Right - itemRect.Left + 1) * RedArea) green = (Int(FillStartColor / &H100) And &HFF) + Int((i - itemRect.Left) / (itemRect.Right - itemRect.Left + 1) * GreenArea) blue = Int(FillStartColor / &H10000) + Int((i - itemRect.Left) / (itemRect.Right - itemRect.Left + 1) * BlueArea) hPen = CreatePen(PS_SOLID, 1, RGB(red, green, blue)) Call SelectObject(.hdc, hPen) Call MoveToEx(.hdc, i, itemRect.Top, 0) Call LineTo(.hdc, i, itemRect.Bottom) Call DeleteObject(hPen) Next i Case ISFS_VERTICALCOLOR ' 垂直渐变色 BlueArea = Int(FillEndColor / &H10000) - Int(FillStartColor / &H10000) GreenArea = (Int(FillEndColor / &H100) And &HFF) - (Int(FillStartColor / &H100) And &HFF) RedArea = (FillEndColor And &HFF) - (FillStartColor And &HFF) For i = itemRect.Top To itemRect.Bottom - 1 red = Int(FillStartColor And &HFF) + Int((i - itemRect.Top) / (itemRect.Bottom - itemRect.Top + 1) * RedArea) green = (Int(FillStartColor / &H100) And &HFF) + Int((i - itemRect.Top) / (itemRect.Bottom - itemRect.Top + 1) * GreenArea) blue = Int(FillStartColor / &H10000) + Int((i - itemRect.Top) / (itemRect.Bottom - itemRect.Top + 1) * BlueArea) hPen = CreatePen(PS_SOLID, 1, RGB(red, green, blue)) Call SelectObject(.hdc, hPen) Call MoveToEx(.hdc, itemRect.Left, i, 0) Call LineTo(.hdc, itemRect.Right, i) Call DeleteObject(hPen) Next i Case ISFS_SOLIDCOLOR ' 实色填充 hPen = CreatePen(PS_SOLID, 0, FillStartColor) hBrush = CreateSolidBrush(FillStartColor) SelectObject .hdc, hPen SelectObject .hdc, hBrush Rectangle .hdc, itemRect.Left, itemRect.Top, itemRect.Right, itemRect.Bottom DeleteObject hPen DeleteObject hBrush End Select ' 画菜单项文字 SetTextColor .hdc, TextSelectColor DrawText .hdc, MyItemInfo(.itemID).itemText, -1, textRect, DT_SINGLELINE Or DT_LEFT Or DT_VCENTER ' 画菜单项图标 If MyItemInfo(.itemID).itemType <> MIT_CHECKBOX Then DrawIconEx .hdc, iconRect.Left + 2, iconRect.Top + (iconRect.Bottom - iconRect.Top + 1 - 16) / 2, MyItemInfo(.itemID).itemIcon, 16, 16, 0, 0, DI_NORMAL Select Case IconStyle Case IIS_NONE ' 无效果 Case IIS_SUNKEN ' 凹进 If MyItemInfo(.itemID).itemIcon <> 0 Then DrawEdge .hdc, iconRect, BDR_SUNKENOUTER, BF_RECT End If Case IIS_RAISED ' 凸起 If MyItemInfo(.itemID).itemIcon <> 0 Then DrawEdge .hdc, iconRect, BDR_RAISEDINNER, BF_RECT End If Case IIS_SHADOW ' 阴影 hBrush = CreateSolidBrush(RGB(128, 128, 128)) DrawState .hdc, hBrush, 0, MyItemInfo(.itemID).itemIcon, 0, iconRect.Left + 3, iconRect.Top + (iconRect.Bottom - iconRect.Top + 1 - 16) / 2 + 1, 0, 0, DST_ICON Or DSS_MONO DeleteObject hBrush DrawIconEx .hdc, iconRect.Left + 1, iconRect.Top + (iconRect.Bottom - iconRect.Top + 1 - 16) / 2 - 1, MyItemInfo(.itemID).itemIcon, 16, 16, 0, 0, DI_NORMAL End Select Else ' CHECKBOX型菜单项图标效果 If MyItemInfo(.itemID).itemState And MIS_CHECKED Then DrawIconEx .hdc, iconRect.Left + 2, iconRect.Top + (iconRect.Bottom - iconRect.Top + 1 - 16) / 2, MyItemInfo(.itemID).itemIcon, 16, 16, 0, 0, DI_NORMAL End If End If Else ' 当鼠标移开菜单项时 ' 画菜单项边框和背景(清除) If BarStyle <> LBS_NONE Then itemRect.Left = barRect.Right + 1 Else itemRect.Left = 0 End If hBrush = CreateSolidBrush(BkColor) FillRect .hdc, itemRect, hBrush DeleteObject hBrush ' 画菜单项文字 SetTextColor .hdc, TextEnabledColor DrawText .hdc, MyItemInfo(.itemID).itemText, -1, textRect, DT_SINGLELINE Or DT_LEFT Or DT_VCENTER ' 画菜单项图标 If MyItemInfo(.itemID).itemType <> MIT_CHECKBOX Then DrawIconEx .hdc, iconRect.Left + 2, iconRect.Top + (iconRect.Bottom - iconRect.Top + 1 - 16) / 2, MyItemInfo(.itemID).itemIcon, 16, 16, 0, 0, DI_NORMAL Else If MyItemInfo(.itemID).itemState And MIS_CHECKED Then DrawIconEx .hdc, iconRect.Left + 2, iconRect.Top + (iconRect.Bottom - iconRect.Top + 1 - 16) / 2, MyItemInfo(.itemID).itemIcon, 16, 16, 0, 0, DI_NORMAL End If End If End If Else ' 当菜单项不可用时 ' 画菜单项文字 SetTextColor .hdc, TextDisabledColor DrawText .hdc, MyItemInfo(.itemID).itemText, -1, textRect, DT_SINGLELINE Or DT_LEFT Or DT_VCENTER ' 画菜单项图标 If MyItemInfo(.itemID).itemType <> MIT_CHECKBOX Then DrawState .hdc, 0, 0, MyItemInfo(.itemID).itemIcon, 0, iconRect.Left + 2, iconRect.Top + (iconRect.Bottom - iconRect.Top + 1 - 16) / 2, 0, 0, DST_ICON Or DSS_DISABLED Else If MyItemInfo(.itemID).itemState And MIS_CHECKED Then DrawState .hdc, 0, 0, MyItemInfo(.itemID).itemIcon, 0, iconRect.Left + 2, iconRect.Top + (iconRect.Bottom - iconRect.Top + 1 - 16) / 2, 0, 0, DST_ICON Or DSS_DISABLED End If End If End If End If End With End If End Sub
' 菜单项事件响应(单击菜单项) Private Sub MenuItemSelected(ByVal itemID As Long) Debug.Print "鼠标单击了:" & MyItemInfo(itemID).itemText Select Case MyItemInfo(itemID).itemAlias Case "exit" Dim frm As Form For Each frm In Forms Unload frm Next End Select End Sub
' 菜单项事件响应(选择菜单项) Private Sub MenuItemSelecting(ByVal itemID As Long) Debug.Print "鼠标移动到:" & MyItemInfo(itemID).itemText End Sub
到此为止,我们就完成了菜单类的编写,且还包括一个测试窗体。现在,完整的工程里应该包括两个窗体:frmMain和frmMenu;一个标准模块:mMenu;一个类模块:cMenu。按F5编译运行一下,在窗体空白处单击鼠标右键。怎么样,出现弹出式菜单了吗?换个风格再试试。 看完这个系列的文章后,我想你应该已经对采用物主绘图技术的自绘菜单有了一定的了解,再看看MS Office 2003的菜单,其实也没什么难的嘛。 该程序在Windows XP、VB6下调试通过。 源代码下载地址:http://y365.com/ses518/soft/samplecsdn.zip
(全文完)
****************************************************************
* 转载请通知作者并注明出处,谢谢。
* 作者:goodname008(卢培培)
* 邮箱:goodname008@163.com
****************************************************************
相关链接: VB打造超酷个性化菜单(一) VB打造超酷个性化菜单(二) VB打造超酷个性化菜单(三)
VB打造超酷个性化菜单(四) VB打造超酷个性化菜单(五) VB打造超酷个性化菜单(六)
|