学院首页>网络编程>其它编程>VB打造超酷个性化菜单(六)

VB打造超酷个性化菜单(六)

作者: 来源: 添加时间:2006-5-21 20:48:36
接上篇)

 

' 拦截菜单消息 (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下调试通过。

站内搜索