众所周知,MS Office 2003推出已经有一段时间了,但我们依然不会忘记Office XP刚刚推出时其令人耳目一新的菜单给我们留下的深刻印象。突起的悬浮式图标,不同寻常的菜单项填充方式,不仅让办公一族们赞不绝口,更让广大的程序员和编程爱好者对这种风格的菜单的制作产生了浓厚的兴趣。所以,在这篇文章里,我们就来好好地研究研究用VB怎么制作这种风格的菜单,在文章的最后,我将给出源代码的下载地址。事实上,在了解其原理以后,不论是用VB、VC还是Delphi,都能够制作出XP风格的菜单。不仅如此,我们还可以制作出更加充满个性的另类风格的菜单,比如3D立体风格、渐变风格、多彩风格等等。只有想不到的,没有做不到的。Follow me!
现在,我想有必要说一说我们现在要做的事情。事实上,我们只要做一个菜单类就行了。但谁都会明白,只做一个菜单类是不够的,我们需要一个程序,或者更详细的说,是一个窗体,来测试我们的菜单类。在我个人的开发过程中,我是先写的菜单类,后写的测试窗体,但为了让大家先领略一下写好的菜单类在应用时是多么的方便,所以让我们先来看看测试窗体: (1)打开VB,新建“标准EXE”工程。 (2)下面是窗体的控件:
其实就是在窗体上添加了一个Frame,然后在Frame里添加OptionButton控件数组,用来设置菜单风格,还有一个Label,上面只显示一行提示文字,非常简单。
(3)窗体代码:
Option Explicit 代码中创建了一个cMenu类的对象,我们的编程重点将会放在cMenu类上,上面的代码只是简单地调用cMenu。在后面的文章中,我们会看到其实cMenu有多达30个方法和属性供我们调用,它的Style属性只提供了5种内置风格,在实际应用中,我们可以利用cMenu类提供的方法和属性制作出各种各样风格的菜单,为自己的程序锦上添花。
(4)运行结果:
图1 图2 图3 图4 图5 这篇文章只是抛砖引玉,让大家先睹为快,提前体验一下这个菜单类的魅力。在下一篇中,我们将继续讨论个性化菜单的制作,不一样的是,我们的重点将是那个cMenu类。 :) 未完待续…
其实,漂亮的界面都是“画”出来的,菜单当然也不例外。既然是“画”出来的,就需要有窗体来接收“画”菜单这个消息,后面我们会看到,实际上不仅仅是“画”这个消息,一切关于这个菜单的消息都要有一个窗体来接收。如果你对消息不太了解,可以看看网上其它一些关于Windows消息机制的文章。不了解也没有关系,只要会使用就可以了,后面的文章给出了完整的源代码,而且文章的最后还给出了源代码的下载地址。
下面我们来创建接收消息的窗体:打开上次建好的工程,添加一个窗体,并将其名称设置为frmMenu(注意:这一步是必须的)。还记得上篇文章的最后一幅图吗?菜单左边那个黑底色的附加条,为了方便,将frmMenu的Picture属性设置成那幅图。到此,这个窗体就算OK了!对了,就这样,因为这个窗体仅仅是为了处理消息和存储那个黑底色的风格条,我们将会对它进行子类处理,处理消息的代码全部都放在了将在下一篇中详细介绍的标准模块中。 接下来添加一个类模块,并将其名称设置为cMenu,代码如下: '*************************************************************
'* 本类模块是一个菜单类, 提供了各种样式的菜单的制作方案 '* '* 版权: LPP软件工作室 '* 作者: 卢培培(goodname008) '* (******* 复制请保留以上信息 *******) '************************************************************* Option Explicit Private Declare Function TrackPopupMenu Lib "user32" (ByVal hMenu As Long, ByVal wFlags As Long, ByVal x As Long, ByVal y As Long, ByVal nReserved As Long, ByVal hwnd As Long, lprc As Any) As Long Public Enum MenuUserStyle ' 菜单总体风格 STYLE_WINDOWS STYLE_XP STYLE_SHADE STYLE_3D STYLE_COLORFUL End Enum Public Enum MenuSeparatorStyle ' 菜单分隔条风格 MSS_SOLID MSS_DASH MSS_DOT MSS_DASDOT MSS_DASHDOTDOT MSS_NONE MSS_DEFAULT End Enum Public Enum MenuItemSelectFillStyle ' 菜单项背景填充风格 ISFS_NONE ISFS_SOLIDCOLOR ISFS_HORIZONTALCOLOR ISFS_VERTICALCOLOR End Enum Public Enum MenuItemSelectEdgeStyle ' 菜单项边框风格 ISES_SOLID ISES_DASH ISES_DOT ISES_DASDOT ISES_DASHDOTDOT ISES_NONE ISES_SUNKEN ISES_RAISED End Enum Public Enum MenuItemIconStyle ' 菜单项图标风格 IIS_NONE IIS_SUNKEN IIS_RAISED IIS_SHADOW End Enum Public Enum MenuItemSelectScope ' 菜单项高亮条的范围 ISS_TEXT = &H1 ISS_ICON_TEXT = &H2 ISS_LEFTBAR_ICON_TEXT = &H4 End Enum Public Enum MenuLeftBarStyle ' 菜单附加条风格 LBS_NONE LBS_SOLIDCOLOR LBS_HORIZONTALCOLOR LBS_VERTICALCOLOR LBS_IMAGE End Enum Public Enum MenuItemType ' 菜单项类型 MIT_STRING = &H0 MIT_CHECKBOX = &H200 MIT_SEPARATOR = &H800 End Enum Public Enum MenuItemState ' 菜单项状态 MIS_ENABLED = &H0 MIS_DISABLED = &H2 MIS_CHECKED = &H8 MIS_UNCHECKED = &H0 End Enum Public Enum PopupAlign ' 菜单弹出对齐方式 POPUP_LEFTALIGN = &H0& ' 水平左对齐 POPUP_CENTERALIGN = &H4& ' 水平居中对齐 POPUP_RIGHTALIGN = &H8& ' 水平右对齐 POPUP_TOPALIGN = &H0& ' 垂直上对齐 POPUP_VCENTERALIGN = &H10& ' 垂直居中对齐 POPUP_BOTTOMALIGN = &H20& ' 垂直下对齐 End Enum ' 释放类 Private Sub Class_Terminate() SetWindowLong frmMenu.hwnd, GWL_WNDPROC, preMenuWndProc Erase MyItemInfo DestroyMenu hMenu End Sub ' 创建弹出式菜单 Public Sub CreateMenu() preMenuWndProc = SetWindowLong(frmMenu.hwnd, GWL_WNDPROC, AddressOf MenuWndProc) hMenu = CreatePopupMenu() Me.Style = STYLE_WINDOWS End Sub ' 插入菜单项并保存自定义菜单项数组, 设置Owner_Draw自绘菜单 Public Sub AddItem(ByVal itemAlias As String, ByVal itemIcon As StdPicture, ByVal itemText As String, ByVal itemType As MenuItemType, Optional ByVal itemState As MenuItemState) Static ID As Long, i As Long Dim ItemInfo As MENUITEMINFO ' 插入菜单项 With ItemInfo .cbSize = LenB(ItemInfo) .fMask = MIIM_STRING Or MIIM_FTYPE Or MIIM_STATE Or MIIM_SUBMENU Or MIIM_ID Or MIIM_DATA .fType = itemType .fState = itemState .wID = ID .dwItemData = True .cch = lstrlen(itemText) .dwTypeData = itemText End With InsertMenuItem hMenu, ID, False, ItemInfo ' 将菜单项数据存入动态数组 ReDim Preserve MyItemInfo(ID) As MyMenuItemInfo For i = 0 To UBound(MyItemInfo) If MyItemInfo(i).itemAlias = itemAlias Then Class_Terminate Err.Raise vbObjectError + 513, "cMenu", "菜单项别名相同." End If Next i With MyItemInfo(ID) Set .itemIcon = itemIcon .itemText = itemText .itemType = itemType .itemState = itemState .itemAlias = itemAlias End With ' 获得菜单项数据 With ItemInfo .cbSize = LenB(ItemInfo) .fMask = MIIM_DATA Or MIIM_ID Or MIIM_TYPE End With GetMenuItemInfo hMenu, ID, False, ItemInfo ' 设置菜单项数据 With ItemInfo .fMask = .fMask Or MIIM_TYPE .fType = MFT_OWNERDRAW End With SetMenuItemInfo hMenu, ID, False, ItemInfo ' 菜单项ID累加 ID = ID + 1 End Sub ' 删除菜单项 Public Sub DeleteItem(ByVal itemAlias As String) Dim i As Long For i = 0 To UBound(MyItemInfo) If MyItemInfo(i).itemAlias = itemAlias Then DeleteMenu hMenu, i, 0 Exit For End If Next i End Sub ' 弹出菜单 Public Sub PopupMenu(ByVal x As Long, ByVal y As Long, ByVal Align As PopupAlign) TrackPopupMenu hMenu, Align, x, y, 0, frmMenu.hwnd, ByVal 0 End Sub ' 设置菜单项图标 Public Sub SetItemIcon(ByVal itemAlias As String, ByVal itemIcon As StdPicture) Dim i As Long For i = 0 To UBound(MyItemInfo) If MyItemInfo(i).itemAlias = itemAlias Then Set MyItemInfo(i).itemIcon = itemIcon Exit For End If Next i End Sub ' 获得菜单项图标 Public Function GetItemIcon(ByVal itemAlias As String) As StdPicture Dim i As Long For i = 0 To UBound(MyItemInfo) If MyItemInfo(i).itemAlias = itemAlias Then Set GetItemIcon = MyItemInfo(i).itemIcon Exit For End If Next i End Function ' 设置菜单项文字 Public Sub SetItemText(ByVal itemAlias As String, ByVal itemText As String) Dim i As Long For i = 0 To UBound(MyItemInfo) If MyItemInfo(i).itemAlias = itemAlias Then MyItemInfo(i).itemText = itemText Exit For End If Next i End Sub ' 获得菜单项文字 Public Function GetItemText(ByVal itemAlias As String) As String Dim i As Long For i = 0 To UBound(MyItemInfo) If MyItemInfo(i).itemAlias = itemAlias Then GetItemText = MyItemInfo(i).itemText Exit For End If Next i End Function ' 设置菜单项状态
Public Sub SetItemState(ByVal itemAlias As String, ByVal itemState As MenuItemState) Dim i As Long For i = 0 To UBound(MyItemInfo) If MyItemInfo(i).itemAlias = itemAlias Then MyItemInfo(i).itemState = itemState Dim ItemInfo As MENUITEMINFO With ItemInfo .cbSize = Len(ItemInfo) .fMask = MIIM_STRING Or MIIM_FTYPE Or MIIM_STATE Or MIIM_SUBMENU Or MIIM_ID Or MIIM_DATA End With GetMenuItemInfo hMenu, i, False, ItemInfo With ItemInfo .fState = .fState Or itemState End With SetMenuItemInfo hMenu, i, False, ItemInfo Exit For End If Next i End Sub ' 获得菜单项状态 Public Function GetItemState(ByVal itemAlias As String) As MenuItemState Dim i As Long For i = 0 To UBound(MyItemInfo) If MyItemInfo(i).itemAlias = itemAlias Then GetItemState = MyItemInfo(i).itemState Exit For End If Next i End Function ' 属性: 菜单句柄 Public Property Get hwnd() As Long hwnd = hMenu End Property Public Property Let hwnd(ByVal nValue As Long) End Property ' 属性: 菜单附加条宽度 Public Property Get LeftBarWidth() As Long LeftBarWidth = BarWidth End Property Public Property Let LeftBarWidth(ByVal nBarWidth As Long) If nBarWidth >= 0 Then BarWidth = nBarWidth End If End Property ' 属性: 菜单附加条风格 Public Property Get LeftBarStyle() As MenuLeftBarStyle LeftBarStyle = BarStyle End Property Public Property Let LeftBarStyle(ByVal nBarStyle As MenuLeftBarStyle) If nBarStyle >= 0 And nBarStyle >= 4 Then BarStyle = nBarStyle End If End Property ' 属性: 菜单附加条图像(只有当 LeftBarStyle 设置为 LBS_IMAGE 时才有效) Public Property Get LeftBarImage() As StdPicture Set LeftBarImage = BarImage End Property Public Property Let LeftBarImage(ByVal nBarImage As StdPicture) Set BarImage = nBarImage End Property ' 属性: 菜单附加条过渡色起始颜色(只有当 LeftBarStyle 设置为 LBS_HORIZONTALCOLOR 或 LBS_VERTICALCOLOR 时才有效) ' 当 LeftBarStyle 设置为 LBS_SOLIDCOLOR (实色填充)时以 LeftBarStartColor 颜色为准 Public Property Get LeftBarStartColor() As Long LeftBarStartColor = BarStartColor End Property Public Property Let LeftBarStartColor(ByVal nBarStartColor As Long) BarStartColor = nBarStartColor End Property ' 属性: 菜单附加条过渡色终止颜色(只有当 LeftBarStyle 设置为 LBS_HORIZONTALCOLOR 或 LBS_VERTICALCOLOR 时才有效) ' 当 LeftBarStyle 设置为 LBS_SOLIDCOLOR (实色填充)时以 LeftBarStartColor 颜色为准 Public Property Get LeftBarEndColor() As Long LeftBarEndColor = BarEndColor End Property Public Property Let LeftBarEndColor(ByVal nBarEndColor As Long) BarEndColor = nBarEndColor End Property ' 属性: 菜单项高亮条的范围 Public Property Get ItemSelectScope() As MenuItemSelectScope ItemSelectScope = SelectScope End Property Public Property Let ItemSelectScope(ByVal nSelectScope As MenuItemSelectScope) SelectScope = nSelectScope End Property ' 属性: 菜单项可用时文字颜色 Public Property Get ItemTextEnabledColor() As Long ItemTextEnabledColor = TextEnabledColor End Property Public Property Let ItemTextEnabledColor(ByVal nTextEnabledColor As Long) TextEnabledColor = nTextEnabledColor End Property ' 属性: 菜单项不可用时文字颜色 Public Property Get ItemTextDisabledColor() As Long ItemTextDisabledColor = TextDisabledColor End Property Public Property Let ItemTextDisabledColor(ByVal nTextDisabledColor As Long) TextDisabledColor = nTextDisabledColor End Property ' 属性: 菜单项选中时文字颜色 Public Property Get ItemTextSelectColor() As Long ItemTextSelectColor = TextSelectColor End Property Public Property Let ItemTextSelectColor(ByVal nTextSelectColor As Long) TextSelectColor = nTextSelectColor End Property ' 属性: 菜单项图标风格 Public Property Get ItemIconStyle() As MenuItemIconStyle ItemIconStyle = IconStyle End Property Public Property Let ItemIconStyle(ByVal nIconStyle As MenuItemIconStyle) IconStyle = nIconStyle End Property ' 属性: 菜单项边框风格 Public Property Get ItemSelectEdgeStyle() As MenuItemSelectEdgeStyle ItemSelectEdgeStyle = EdgeStyle End Property Public Property Let ItemSelectEdgeStyle(ByVal nEdgeStyle As MenuItemSelectEdgeStyle) EdgeStyle = nEdgeStyle End Property ' 属性: 菜单项边框颜色 Public Property Get ItemSelectEdgeColor() As Long ItemSelectEdgeColor = EdgeColor End Property Public Property Let ItemSelectEdgeColor(ByVal nEdgeColor As Long) EdgeColor = nEdgeColor End Property ' 属性: 菜单项背景填充风格 Public Property Get ItemSelectFillStyle() As MenuItemSelectFillStyle ItemSelectFillStyle = FillStyle End Property Public Property Let ItemSelectFillStyle(ByVal nFillStyle As MenuItemSelectFillStyle) FillStyle = nFillStyle End Property ' 属性: 菜单项过渡色起始颜色(只有当 ItemSelectFillStyle 设置为 ISFS_HORIZONTALCOLOR 或 ISFS_VERTICALCOLOR 时才有效) ' 当 ItemSelectFillStyle 设置为 ISFS_SOLIDCOLOR (实色填充)时以 ItemSelectFillStartColor 颜色为准 Public Property Get ItemSelectFillStartColor() As Long ItemSelectFillStartColor = FillStartColor End Property Public Property Let ItemSelectFillStartColor(ByVal nFillStartColor As Long) FillStartColor = nFillStartColor End Property ' 属性: 菜单项过渡色终止颜色(只有当 ItemSelectFillStyle 设置为 ISFS_HORIZONTALCOLOR 或 ISFS_VERTICALCOLOR 时才有效) ' 当 ItemSelectFillStyle 设置为 ISFS_SOLIDCOLOR (实色填充)时以 ItemSelectFillStartColor 颜色为准 Public Property Get ItemSelectFillEndColor() As Long ItemSelectFillEndColor = FillEndColor End Property Public Property Let ItemSelectFillEndColor(ByVal nFillEndColor As Long) FillEndColor = nFillEndColor End Property ' 属性: 菜单背景颜色 Public Property Get BackColor() As Long BackColor = BkColor End Property Public Property Let BackColor(ByVal nBkColor As Long) BkColor = nBkColor End Property ' 属性: 菜单分隔条风格 Public Property Get SeparatorStyle() As MenuSeparatorStyle SeparatorStyle = SepStyle End Property Public Property Let SeparatorStyle(ByVal nSepStyle As MenuSeparatorStyle) SepStyle = nSepStyle End Property ' 属性: 菜单分隔条颜色 Public Property Get SeparatorColor() As Long SeparatorColor = SepColor End Property Public Property Let SeparatorColor(ByVal nSepColor As Long) SepColor = nSepColor End Property
' 属性: 菜单总体风格 这个类模块中包含了各种属性和方法及关于菜单的一些枚举类型,我想强调的有以下几点: 1、在CreateMenu方法中用SetWindowLong重新定义了frmMenu的窗口入口函数的地址,MenuWndProc是标准模块中的一个函数,就是处理消息的那个函数。 2、AddItem这个方法是添加菜单项的,使用一个叫做MyItemInfo的动态数组存储菜单项的内容,在“画”菜单项的时候要用到它。在AddItem方法的最后,将菜单项的fType设置成了MFT_OWNERDRAW,也就是物主绘图,这一步最关键,因为将菜单项设置成了Owner Draw,Windows将不会替我们写字,不会替我们画图标,一切都由我们自己来。 3、在PopupMenu方法中,调用了API函数中的TrackPopupMenu,看到第6个参数了吗?将处理菜单消息的窗口设置成了frmMenu,而我们又对frmMenu进行了子类处理,一切都在我们的掌握之中。 4、记得要在Class_Terminate中还原frmMenu的窗口入口函数的地址,并释放和菜单相关的资源。 好了,类模块已经OK了,大家可能对这个菜单类有了更多的了解,也看到了它的属性和方法。怎么样?还算比较丰富吧。如果觉得不够丰富的话,自己加就好了,呵呵。不过,最核心的部分还不在这里,而是在那个处理消息的函数,也就是MenuWndProc,它将完成复杂地“画”菜单的任务以及处理各种菜单事件。看看右边的滚动条,已经够窄了,下一篇再讨论吧。 :) 源代码下载:点击这里下载(28K, winzip压缩文件)
现在到了最关键,最精彩,也是最复杂的部分了。我们最关心的就是怎样“画”菜单,怎样处理菜单事件,在MenuWndProc这个处理消息的函数里,我们要处理如下消息:WM_COMMAND(单击菜单项),WM_MEASUREITEM(处理菜单高度和宽度),WM_MENUSELECT(选择菜单项),WM_DRAWITEM(绘制菜单项)。 打开上次建好的工程,添加一个标准模块,并将其名称设置为mMenu,代码如下: '*****************************************************
' 拦截菜单消息 (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
|
|