www.pudn.com > Outlook 97样式的菜单.zip > Menus.cls
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "Menus"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit
Private picMenu As PictureBox
Private picCache As PictureBox
Private colMenus As New Collection
Private mlButtonHeight As Long
Private mlMenuPrev As Long
Private mlMenuCur As Long
Private mbNumberOfMenusChanged As Boolean
#If USE_WING Then
Private Declare Function WinGBitBlt Lib "wing32.dll" (ByVal hDestDC As Long, ByVal X As Long, ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long) As Long
#Else
Private Declare Function BitBlt Lib "gdi32" (ByVal hDestDC As Long, ByVal X As Long, ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long) As Long
#End If
Const SRCCOPY = &HCC0020
Const PIXELS_PER_BITBLT = 1
Const TYPE_UP = 1
Const TYPE_DOWN = -1
' add a new Menu to the collection
' Parameters: sCaption Caption of the Menu
' lIndex Location of the Menu in Menus collection
Public Function Add(ByVal sCaption As String, lIndex As Long, ByVal picMenu As Object) As VMenu
Dim newMenu As New VMenu
On Error Resume Next
With newMenu
.Caption = sCaption
.Index = lIndex
Set .Control = picMenu
.ButtonHeight = mlButtonHeight
End With
' add the item to the collection specified by lIndex
' note, if there is nothing in the collection, just add it
' if there is nothing in the collection or we are adding it at then end, just add it
' elseif we are inserting in the first position, add it BEFORE
' else add it AFTER the previous item
If colMenus.Count = 0 Then
colMenus.Add newMenu
ElseIf lIndex = colMenus.Count + 1 Then
colMenus.Add newMenu
ElseIf lIndex = 1 Then
colMenus.Add newMenu, , 1
Else
colMenus.Add newMenu, , , lIndex - 1
End If
Set Add = newMenu
End Function
' delete the Menu from the collection
' Parameters: lIndex Index of the collection member
Public Sub Delete(lIndex As Long)
On Error Resume Next
colMenus.Remove lIndex
End Sub
' return the object of the Menu in the collection
' Parameters: lIndex Index of the collection member
Public Property Get Item(lIndex As Variant) As VMenu
On Error Resume Next
If lIndex > 0 Then
Set Item = colMenus(lIndex)
End If
End Property
' return the number of Menus in the collection
Public Function Count() As Long
On Error Resume Next
Count = colMenus.Count
End Function
' move a Menu to a new location
' Parameters: lCurIndex the current location
' lNewIndex the new location
Public Sub MoveMenu(lCurIndex As Long, lNewIndex As Long)
' under construction
End Sub
' move a MenuItem to a new location
' Parameters: lCurIndex the current location
' lNewIndex the new location
Public Sub MoveMenuItem(lCurIndex As Long, lNewIndex As Long)
' undex construction
End Sub
Public Property Get Caption(lIndex As Long) As String
On Error Resume Next
Caption = colMenus(lIndex).Caption
End Property
Public Property Let Caption(lIndex As Long, sNewValue As String)
On Error Resume Next
colMenus(lIndex).Caption = sNewValue
End Property
Public Property Get ButtonHeight() As Long
On Error Resume Next
ButtonHeight = mlButtonHeight
End Property
Public Property Let ButtonHeight(ByVal lNewValue As Long)
On Error Resume Next
mlButtonHeight = lNewValue
End Property
Public Property Set Menu(oNewValue As PictureBox)
On Error Resume Next
Set picMenu = oNewValue
End Property
Public Property Set Cache(oNewValue As PictureBox)
On Error Resume Next
Set picCache = oNewValue
End Property
Public Property Let MenuCur(lNewValue As Long)
On Error Resume Next
mlMenuCur = lNewValue
End Property
' Procedure: Paint
' This is the main procedure that paints our control
' It handles repaints as well as well as changes of the
' current menu
' Since we can move several menus at once, the code for
' this is done here in the collection of menus rather then
' the the menu class itself. However, the painting of the
' MenuItems is done in the MenuItem class itself.
Public Sub Paint()
On Error Resume Next
If mlMenuPrev = 0 Then ' first time paint
mlMenuPrev = mlMenuCur
End If
If mlMenuPrev = mlMenuCur Then
Repaint
ElseIf mlMenuPrev < mlMenuCur Then ' user selected a menu after the previously selected menu
ReselectDown
Else ' user selected a menu before the previously selected menu
ReselectUp
End If
DrawIcons
SetMenuButtonsHotSpot
mlMenuPrev = mlMenuCur ' save this menu as the next previous menu
End Sub
' repaint the menu as is - no changes were made
' support subroutine for Paint
Private Sub Repaint()
Dim l As Long
Dim lMax As Long
Dim lResult As Long
Dim hDestDC As Long
Dim hSrcDC As Long
Dim sCaption As String
Dim lWidth As Long
Dim lHeight As Long
On Error Resume Next
' setup variables
lMax = colMenus.Count
With picMenu
' if we just changed the number of menus then
' we need to clear the control first
If mbNumberOfMenusChanged Then
.Cls
mbNumberOfMenusChanged = False
End If
hDestDC = .hdc
.ScaleMode = vbPixels
.ForeColor = vbButtonText
lWidth = CLng(.ScaleWidth)
lHeight = CLng(.ScaleHeight)
End With
hSrcDC = picCache.hdc
If lMax = 0 Or hDestDC = 0 Or hSrcDC = 0 Then
Exit Sub
End If
' first, paint the menus up to the currently select one
For l = 1 To mlMenuCur
With picMenu
' draw the button
#If USE_WING Then
lResult = WinGBitBlt(hDestDC, 0, _
(l - 1) * mlButtonHeight, _
lWidth, _
mlButtonHeight, _
hSrcDC, 0, 0)
#Else
lResult = BitBlt(hDestDC, 0, _
(l - 1) * mlButtonHeight, _
lWidth, _
mlButtonHeight, _
hSrcDC, 0, 0, SRCCOPY)
#End If
' print the caption
sCaption = colMenus.Item(l).Caption
.CurrentX = (lWidth \ 2) - (.TextWidth(sCaption) \ 2)
.CurrentY = (l - 1) * mlButtonHeight + 2
picMenu.Print sCaption
End With
Next
' now, paint the menus below the currently seleted one (from the bottom up)
For l = lMax To mlMenuCur + 1 Step -1
With picMenu
' draw the button
#If USE_WING Then
lResult = WinGBitBlt(hDestDC, 0, _
lHeight - (lMax - l + 1) * mlButtonHeight, _
lWidth, _
mlButtonHeight, _
hSrcDC, 0, 0)
#Else
lResult = BitBlt(hDestDC, 0, _
lHeight - (lMax - l + 1) * mlButtonHeight, _
lWidth, _
mlButtonHeight, _
hSrcDC, 0, 0, SRCCOPY)
#End If
' print the caption
sCaption = colMenus.Item(l).Caption
.CurrentX = (lWidth \ 2) - (.TextWidth(sCaption) \ 2)
.CurrentY = lHeight - (lMax - l + 1) * mlButtonHeight + 2
picMenu.Print sCaption
End With
Next
End Sub
' the new current menu is further down on the menu than the previous one
' we need to move the menus up from the previous menu + 1 to the new current menu
' support subroutine for Paint
Private Sub ReselectDown()
Dim lStartY As Long
Dim lStopY As Long
Dim lTopOfGroupY As Long
Dim lPixelCount As Long
Dim lResult As Long
Dim lMax As Long
Dim hDestDC As Long
Dim hSrcDC As Long
Dim lWidth As Long
Dim bFirst As Boolean
On Error Resume Next
' setup variables
bFirst = True
lMax = colMenus.Count
With picMenu
hDestDC = .hdc
.ScaleMode = vbPixels
.ForeColor = vbButtonText
lWidth = .ScaleWidth
lStopY = mlMenuPrev * mlButtonHeight
lStartY = .ScaleHeight - (lMax - mlMenuCur) * mlButtonHeight
lTopOfGroupY = .ScaleHeight - (lMax - mlMenuPrev) * mlButtonHeight
End With
hSrcDC = picCache.hdc
If lMax = 0 Or hDestDC = 0 Or hSrcDC = 0 Then
Exit Sub
End If
Do
#If USE_WING Then
lResult = WinGBitBlt(hDestDC, 0, _
lStopY, _
lWidth, _
lStartY - lStopY - lPixelCount - PIXELS_PER_BITBLT - ((Not (bFirst)) * PIXELS_PER_BITBLT), _
hDestDC, 0, lStopY + PIXELS_PER_BITBLT)
#Else
lResult = BitBlt(hDestDC, 0, _
lStopY, _
lWidth, _
lStartY - lStopY - lPixelCount - PIXELS_PER_BITBLT - ((Not (bFirst)) * PIXELS_PER_BITBLT), _
hDestDC, 0, lStopY + PIXELS_PER_BITBLT, SRCCOPY)
#End If
If bFirst Then
#If USE_WING Then
lResult = WinGBitBlt(hDestDC, 0, _
lStartY - PIXELS_PER_BITBLT, _
lWidth, _
PIXELS_PER_BITBLT, _
hSrcDC, 0, mlButtonHeight + 3)
#Else
lResult = BitBlt(hDestDC, 0, _
lStartY - PIXELS_PER_BITBLT, _
lWidth, _
PIXELS_PER_BITBLT, _
hSrcDC, 0, mlButtonHeight + 3, SRCCOPY)
bFirst = False
#End If
End If
lPixelCount = lPixelCount + PIXELS_PER_BITBLT
Loop Until lTopOfGroupY - ((lPixelCount + 1) * PIXELS_PER_BITBLT) <= lStopY
' make sure the group is in it's correct final position
#If USE_WING Then
lResult = WinGBitBlt(hDestDC, 0, _
lStopY, _
lWidth, _
lStartY - lStopY - 1 - lPixelCount - (Not (bFirst) * PIXELS_PER_BITBLT), _
hDestDC, 0, lTopOfGroupY - lPixelCount)
#Else
lResult = BitBlt(hDestDC, 0, _
lStopY, _
lWidth, _
lStartY - lStopY - 1 - lPixelCount - (Not (bFirst) * PIXELS_PER_BITBLT), _
hDestDC, 0, lTopOfGroupY - lPixelCount, SRCCOPY)
#End If
End Sub
' the new current menu is further up on the menu than the previous one
' we need to move the menus down from the current menu + 1 to the previous menu
' support subroutine for Paint
Private Sub ReselectUp()
Dim lStartY As Long
Dim lStopY As Long
Dim lBottomOfGroupY As Long
Dim lPixelCount As Long
Dim lResult As Long
Dim lMax As Long
Dim hDestDC As Long
Dim hSrcDC As Long
Dim lWidth As Long
Dim bFirst As Boolean
On Error Resume Next
' setup variables
bFirst = True
lMax = colMenus.Count
With picMenu
hDestDC = .hdc
.ScaleMode = vbPixels
.ForeColor = vbButtonText
lWidth = .ScaleWidth
lStartY = (mlMenuCur) * mlButtonHeight
lStopY = .ScaleHeight - (lMax - mlMenuPrev) * mlButtonHeight
lBottomOfGroupY = mlMenuPrev * mlButtonHeight
End With
hSrcDC = picCache.hdc
If lMax = 0 Or hDestDC = 0 Or hSrcDC = 0 Then
Exit Sub
End If
Do
#If USE_WING Then
lResult = WinGBitBlt(hDestDC, 0, _
lStartY + lPixelCount + PIXELS_PER_BITBLT + ((Not (bFirst)) * PIXELS_PER_BITBLT), _
lWidth, _
lStopY - lStartY - lPixelCount - PIXELS_PER_BITBLT - ((Not (bFirst)) * PIXELS_PER_BITBLT), _
hDestDC, 0, lStartY + lPixelCount + ((Not (bFirst)) * PIXELS_PER_BITBLT))
#Else
lResult = BitBlt(hDestDC, 0, _
lStartY + lPixelCount + PIXELS_PER_BITBLT + ((Not (bFirst)) * PIXELS_PER_BITBLT), _
lWidth, _
lStopY - lStartY - lPixelCount - PIXELS_PER_BITBLT - ((Not (bFirst)) * PIXELS_PER_BITBLT), _
hDestDC, 0, lStartY + lPixelCount + ((Not (bFirst)) * PIXELS_PER_BITBLT), SRCCOPY)
#End If
If bFirst Then
#If USE_WING Then
lResult = WinGBitBlt(hDestDC, 0, _
lStartY + (lPixelCount * PIXELS_PER_BITBLT), _
lWidth, _
PIXELS_PER_BITBLT, _
hSrcDC, 0, mlButtonHeight + 3)
#Else
lResult = BitBlt(hDestDC, 0, _
lStartY + (lPixelCount * PIXELS_PER_BITBLT), _
lWidth, _
PIXELS_PER_BITBLT, _
hSrcDC, 0, mlButtonHeight + 3, SRCCOPY)
#End If
bFirst = False
End If
lPixelCount = lPixelCount + PIXELS_PER_BITBLT
Loop Until lBottomOfGroupY + lPixelCount >= lStopY
' make sure the group is in it's correct final position
#If USE_WING Then
lResult = WinGBitBlt(hDestDC, 0, _
lStopY - (mlMenuPrev - mlMenuCur) * mlButtonHeight - PIXELS_PER_BITBLT, _
lWidth, _
(mlMenuPrev - mlMenuCur) * mlButtonHeight + PIXELS_PER_BITBLT, _
hDestDC, 0, lStartY + lPixelCount - PIXELS_PER_BITBLT)
#Else
lResult = BitBlt(hDestDC, 0, _
lStopY - (mlMenuPrev - mlMenuCur) * mlButtonHeight - PIXELS_PER_BITBLT, _
lWidth, _
(mlMenuPrev - mlMenuCur) * mlButtonHeight + PIXELS_PER_BITBLT, _
hDestDC, 0, lStartY + lPixelCount - PIXELS_PER_BITBLT, SRCCOPY)
#End If
End Sub
' draw the icons for the currently select menu
' support subroutine for Paint
Private Sub DrawIcons()
On Error Resume Next
colMenus.Item(mlMenuCur).PaintItems IconStart(), mlMenuCur, ClipY(), colMenus.Count
End Sub
' support subroutine for Paint
Private Sub SetMenuButtonsHotSpot()
Dim lIndex As Long
Dim lMax As Long
Dim VMenu As VMenu
On Error Resume Next
lMax = colMenus.Count
For Each VMenu In colMenus
With VMenu
lIndex = .Index
If lIndex <= mlMenuCur Then ' the menu is at the top of the control
.ButtonTop = (lIndex - 1) * mlButtonHeight
Else ' the menu is at the bottom of the control
.ButtonTop = picMenu.ScaleHeight - (lMax - lIndex + 1) * mlButtonHeight
End If
End With
Next
End Sub
' determines if the mouse was clicked in a menu button
' returns the index of the menu clicked
' if no menu clicked, returns 0
Public Function IsMenuButtonClicked(ByVal ptX As Long, ByVal ptY As Long) As Long
Dim VMenu As VMenu
On Error Resume Next
For Each VMenu In colMenus
With VMenu
If .IsMenuSelected(ptX, ptY) Then
IsMenuButtonClicked = .Index
Exit Function
End If
End With
Next
End Function
Public Property Let NumberOfMenusChanged(ByVal bNewValue As Boolean)
On Error Resume Next
mbNumberOfMenusChanged = bNewValue
End Property
Public Property Get TotalMenuItems() As Long
Dim VMenu As VMenu
Dim lTotal As Long
On Error Resume Next
For Each VMenu In colMenus
lTotal = lTotal + VMenu.MenuItemCount
Next
TotalMenuItems = lTotal
End Property
' Process mouse events
' Note that even if we get a hit, we must process all code
' The individual objects handle their own paints depending what
' the mouse is doing.
Public Function MouseProcess(ByVal iMousePosition As Integer, ByVal X As Long, ByVal Y As Long, Optional lHitType As Long) As Long
Dim lResult As Long
Dim MenuItems As MenuItems
Const HIT_TYPE_MENU_BUTTON = 1
Const HIT_TYPE_MENUITEM = 2
Const HIT_TYPE_UP_ARROW = 3
Const HIT_TYPE_DOWN_ARROW = 4
Const SCROLL_UP = 100
Const SCROLL_DOWN = -100
Const MOUSE_UP = 1
Const MOUSE_DOWN = -1
Const MOUSE_MOVE = 0
' first test for a MenuButtonHit
If iMousePosition = MOUSE_DOWN Then
lResult = IsMenuButtonClicked(X, Y)
'if lResult is non-zero we have a hit
' so tell the calling procedure and return
If lResult <> 0 Then
lHitType = HIT_TYPE_MENU_BUTTON
MouseProcess = lResult
End If
End If
' test for a MenuItemHit
' don't do the test if we got a MenuButtonHit
If lResult = 0 Then
Set MenuItems = colMenus.Item(mlMenuCur).MenuItems()
lResult = MenuItems.MouseProcess(iMousePosition, X, Y)
If iMousePosition = MOUSE_UP And lResult > 0 Then
lHitType = HIT_TYPE_MENUITEM
MouseProcess = lResult
End If
End If
' test for arrow buttons
lResult = colMenus.Item(mlMenuCur).MouseProcessForArrows(iMousePosition, X, Y)
If lResult <> 0 Then
Select Case lResult
Case SCROLL_UP
ScrollUp
lHitType = HIT_TYPE_UP_ARROW
Case SCROLL_DOWN
ScrollDown
lHitType = HIT_TYPE_DOWN_ARROW
End Select
End If
End Function
Private Sub ScrollUp()
Dim lStartY As Long
Dim lStopY As Long
Dim lTopOfGroupY As Long
Dim lPixelCount As Long
Dim lResult As Long
Dim lMax As Long
Dim hDestDC As Long
Dim hSrcDC As Long
Dim lWidth As Long
Dim lPixel As Long
Const PIXELS_TO_SCROLL = 64
Const PIXELS_PER_BITBLT_S = 2
On Error Resume Next
With colMenus.Item(mlMenuCur)
.TopMenuItem = .TopMenuItem - 1
' the upbutton is visible, hide it so it doesn't scroll
.HideButton TYPE_UP, mlMenuCur
End With
' setup variables
lMax = colMenus.Count
With picMenu
hDestDC = .hdc
.ScaleMode = vbPixels
.ForeColor = vbButtonText
lWidth = .ScaleWidth
lStartY = mlMenuCur * mlButtonHeight
lStopY = .ScaleHeight - (lMax - mlMenuCur) * mlButtonHeight
End With
hSrcDC = picCache.hdc
If lMax = 0 Or hDestDC = 0 Or hSrcDC = 0 Then
Exit Sub
End If
For lPixel = 1 To PIXELS_TO_SCROLL \ PIXELS_PER_BITBLT_S
' move the screen up
#If USE_WING Then
lResult = WinGBitBlt(hDestDC, 0, _
lStartY + PIXELS_PER_BITBLT_S, _
lWidth, _
lStopY - lStartY - 2, _
hDestDC, 0, lStartY)
#Else
lResult = BitBlt(hDestDC, 0, _
lStartY + PIXELS_PER_BITBLT_S, _
lWidth, _
lStopY - lStartY - 2, _
hDestDC, 0, lStartY, SRCCOPY)
#End If
' repaint the background
#If USE_WING Then
lResult = WinGBitBlt(hDestDC, 0, _
lStartY, _
lWidth, _
PIXELS_PER_BITBLT_S, _
hSrcDC, 0, mlButtonHeight + 3)
#Else
lResult = BitBlt(hDestDC, 0, _
lStartY, _
lWidth, _
PIXELS_PER_BITBLT_S, _
hSrcDC, 0, mlButtonHeight + 3, SRCCOPY)
#End If
lPixelCount = lPixelCount + PIXELS_PER_BITBLT_S
Next
DrawIcons
SetMenuButtonsHotSpot
End Sub
Private Sub ScrollDown()
Dim lStartY As Long
Dim lStopY As Long
Dim lTopOfGroupY As Long
Dim lPixelCount As Long
Dim lResult As Long
Dim lMax As Long
Dim hDestDC As Long
Dim hSrcDC As Long
Dim lWidth As Long
Dim lPixel As Long
Const PIXELS_TO_SCROLL = 64
Const PIXELS_PER_BITBLT_S = 2
On Error Resume Next
With colMenus.Item(mlMenuCur)
.TopMenuItem = .TopMenuItem + 1
' the down button is visible, hide it so it doesn't scroll
.HideButton TYPE_DOWN, colMenus.Count - mlMenuCur
End With
' setup variables
lMax = colMenus.Count
With picMenu
hDestDC = .hdc
.ScaleMode = vbPixels
.ForeColor = vbButtonText
lWidth = .ScaleWidth
lStopY = mlMenuCur * mlButtonHeight
lStartY = .ScaleHeight - (lMax - mlMenuCur) * mlButtonHeight
End With
hSrcDC = picCache.hdc
If lMax = 0 Or hDestDC = 0 Or hSrcDC = 0 Then
Exit Sub
End If
For lPixel = 1 To PIXELS_TO_SCROLL \ PIXELS_PER_BITBLT_S
' move the screen up
#If USE_WING Then
lResult = WinGBitBlt(hDestDC, 0, _
lStopY, _
lWidth, _
lStartY - lStopY, _
hDestDC, 0, lStopY + PIXELS_PER_BITBLT_S)
#Else
lResult = BitBlt(hDestDC, 0, _
lStopY, _
lWidth, _
lStartY - lStopY, _
hDestDC, 0, lStopY + PIXELS_PER_BITBLT_S, SRCCOPY)
#End If
' repaint the background
#If USE_WING Then
lResult = WinGBitBlt(hDestDC, 0, _
lStartY - PIXELS_PER_BITBLT_S, _
lWidth, _
PIXELS_PER_BITBLT_S, _
hSrcDC, 0, mlButtonHeight + 3)
#Else
lResult = BitBlt(hDestDC, 0, _
lStartY - PIXELS_PER_BITBLT_S, _
lWidth, _
PIXELS_PER_BITBLT_S, _
hSrcDC, 0, mlButtonHeight + 3, SRCCOPY)
#End If
lPixelCount = lPixelCount + PIXELS_PER_BITBLT_S
Next
' redraw the icons
DrawIcons
SetMenuButtonsHotSpot
End Sub
Private Function IconStart() As Long
Dim l As Long
Dim lIconStart As Long
On Error Resume Next
' calculate the offset for our first icon
For l = 1 To mlMenuCur - 1
lIconStart = lIconStart + colMenus.Item(l).MenuItemCount
Next
IconStart = lIconStart + colMenus.Item(mlMenuCur).TopMenuItem - 1
End Function
Private Function ClipY() As Long
On Error Resume Next
' calculate the clipping area where the menu bottoms start at the bottom of picmenu
With picMenu
.ScaleMode = vbPixels
ClipY = .ScaleHeight - ((colMenus.Count) - mlMenuCur) * mlButtonHeight
End With
End Function