VB打造超酷个性化菜单(六) (接上篇) ' 拦截菜单消息 (frmMenu 窗口入口函数) ' 处理菜单高度和宽度 ' 绘制菜单项 Case LBS_SOLIDCOLOR ' 实色填充 hBrush = CreateSolidBrush(BarStartColor) Case LBS_HORIZONTALCOLOR ' 水平过渡色 BlueArea = Int(BarEndColor / &H10000) - Int(BarStartColor / &H10000) For i = 0 To BarWidth - 1 Case LBS_VERTICALCOLOR ' 垂直过渡色 BlueArea = Int(BarEndColor / &H10000) - Int(BarStartColor / &H10000) For i = 0 To barRect.Bottom Case LBS_IMAGE ' 图像 If BarImage.Handle <> 0 Then End Select ' 菜单项事件响应(单击菜单项) ' 菜单项事件响应(选择菜单项) 到此为止,我们就完成了菜单类的编写,且还包括一个测试窗体。现在,完整的工程里应该包括两个窗体:frmMain和frmMenu;一个标准模块:mMenu;一个类模块:cMenu。按F5编译运行一下,在窗体空白处单击鼠标右键。怎么样,出现弹出式菜单了吗?换个风格再试试。
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 ' 无附加条
FillRect .hdc, barRect, hBrush
DeleteObject hBrush
GreenArea = (Int(BarEndColor / &H100) And &HFF) - (Int(BarStartColor / &H100) And &HFF)
RedArea = (BarEndColor And &HFF) - (BarStartColor And &HFF)
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
GreenArea = (Int(BarEndColor / &H100) And &HFF) - (Int(BarStartColor / &H100) And &HFF)
RedArea = (BarEndColor And &HFF) - (BarStartColor And &HFF)
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
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
' 画菜单项
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
看完这个系列的文章后,我想你应该已经对采用物主绘图技术的自绘菜单有了一定的了解,再看看MS Office 2003的菜单,其实也没什么难的嘛。
该程序在Windows XP、VB6下调试通过。
源代码下载地址:http://y365.com/ses518/soft/samplecsdn.zip
