我在表单上有两个并排的列表框,它们模拟 Excel 屏幕,左侧冻结在适当的位置。
第一个列表框将显示销售日期和客户名称。
第二个列表框将显示各种详细信息,因此可以水平滚动,而客户名称不会消失。
我设法使ListBox1.TopIndex = ListBox2.TopIndex。但只有当我单击它、选择一个项目或将鼠标移到它上面时才会发生这种情况。简而言之,使用任何可用的事件来激活该命令行。
每当用户向上或向下滚动列表框但没有滚动事件时,我都需要发生这种情况。
现在,当我向下滚动 listbox2 时,listbox1 保持静止。当我单击 listbox2 上的某个项目时,listbox1 会刷新并与其对齐。
Windows API 在 VBA 中仍然非常有用,可以满足更复杂项目的需求。要在列表框中滚动,请使用以下代码(对于 Excel 32 位)。我只是警告说,使用“Windows 挂钩”可能会带来意外或不可预见的不稳定,有必要评估在给定项目中使用此资源是否合适。
在列表框中移动选择的代码是“MouseProc”。其他处理从 Windows 到 Userform/ListBox 的消息拦截,以及何时开始或结束这些拦截(Hook/Unhook)。
'Put or edit these events on the UserForm:
Private Sub ListBox1_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
fnHookListBoxScroll
End Sub
Private Sub UserForm_Deactivate()
fnUnhookListBoxScroll
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
fnUnhookListBoxScroll
End Sub
'++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Put these codes on a standard module:
Option Explicit
Private Type POINTAPI
X As Long
Y As Long
End Type
Private Type MOUSEHOOKSTRUCT
pt As POINTAPI
hwnd As Long
wHitTestCode As Long
dwExtraInfo As Long
End Type
Private Declare Function GetWindowLong Lib "user32.dll" _
Alias "GetWindowLongA" ( _
ByVal hwnd As Long, _
ByVal nIndex As Long) As Long
Private Declare Function SetWindowsHookEx Lib "user32" _
Alias "SetWindowsHookExA" ( _
ByVal idHook As Long, _
ByVal lpfn As Long, _
ByVal hmod As Long, _
ByVal dwThreadId As Long) As Long
Private Declare Function CallNextHookEx Lib "user32" ( _
ByVal hHook As Long, _
ByVal nCode As Long, _
ByVal wParam As Long, _
lParam As Any) As Long
Private Declare Function UnhookWindowsHookEx Lib "user32" ( _
ByVal hHook As Long) As Long
Private Declare Function PostMessage Lib "user32.dll" _
Alias "PostMessageA" ( _
ByVal hwnd As Long, _
ByVal wMsg As Long, _
ByVal wParam As Long, _
ByVal lParam As Long) As Long
Private Declare Function WindowFromPoint Lib "user32" ( _
ByVal xPoint As Long, _
ByVal yPoint As Long) As Long
Private Declare Function GetCursorPos Lib "user32.dll" ( _
ByRef lpPoint As POINTAPI) As Long
Private Const WH_MOUSE_LL As Long = 14
Private Const WM_MOUSEWHEEL As Long = &H20A
Private Const HC_ACTION As Long = 0
Private Const GWL_HINSTANCE As Long = (-6)
Private Const WM_KEYDOWN As Long = &H100
Private Const WM_KEYUP As Long = &H101
Private Const VK_UP As Long = &H26
Private Const VK_DOWN As Long = &H28
Private Const WM_LBUTTONDOWN As Long = &H201
Private mLngMouseHook As Long
Private mListBoxHwnd As Long
Private mbHook As Boolean
Sub fnHookListBoxScroll()
Dim lngAppInst As Long
Dim hwndUnderCursor As Long
Dim tPT As POINTAPI
GetCursorPos tPT
DoEvents
hwndUnderCursor = WindowFromPoint(tPT.X, tPT.Y)
DoEvents
If mListBoxHwnd <> hwndUnderCursor Then
fnUnhookListBoxScroll
DoEvents
mListBoxHwnd = hwndUnderCursor
DoEvents
lngAppInst = GetWindowLong(mListBoxHwnd, GWL_HINSTANCE)
DoEvents
PostMessage mListBoxHwnd, WM_LBUTTONDOWN, 0&, 0&
DoEvents
If Not mbHook Then
mLngMouseHook = SetWindowsHookEx( _
WH_MOUSE_LL, AddressOf MouseProc, lngAppInst, 0)
DoEvents
mbHook = mLngMouseHook <> 0
DoEvents
End If
DoEvents
End If
DoEvents
End Sub
Sub fnUnhookListBoxScroll()
If mbHook Or mLngMouseHook = 0 Then
UnhookWindowsHookEx mLngMouseHook
mLngMouseHook = 0
mListBoxHwnd = 0
mbHook = False
End If
End Sub
Private Function MouseProc( _
ByVal nCode As Long, ByVal wParam As Long, _
ByRef lParam As MOUSEHOOKSTRUCT) As Long
On Error GoTo errH 'Resume Next
If (nCode = HC_ACTION) Then
If WindowFromPoint(lParam.pt.X, lParam.pt.Y) = mListBoxHwnd Then
If wParam = WM_MOUSEWHEEL Then
MouseProc = True
If lParam.hwnd > 0 Then
PostMessage mListBoxHwnd, WM_KEYDOWN, VK_UP, 0
DoEvents
Else
PostMessage mListBoxHwnd, WM_KEYDOWN, VK_DOWN, 0
DoEvents
End If
PostMessage mListBoxHwnd, WM_KEYUP, VK_UP, 0
DoEvents
Exit Function
End If
Else
fnUnhookListBoxScroll
End If
End If
MouseProc = CallNextHookEx( _
mLngMouseHook, nCode, wParam, ByVal lParam)
Exit Function
errH:
fnUnhookListBoxScroll
End Function
*** 重要:***
如果您使用的是 Excel 64 位(在 64 位 Windows 下),则正确使用以下代码:
除了“声明”之外,还有代码本身的其他重大变化,请注意 POINTAPI 类型的巨大变化。有必要创建一个专用函数来正确传递相应的数据。
'Put or edit these events on the UserForm:
Option Explicit
Private Sub ListBox1_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal x As Single, ByVal Y As Single)
fnHookListBoxScroll
End Sub
Private Sub UserForm_Deactivate()
fnUnhookListBoxScroll
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
fnUnhookListBoxScroll
End Sub
'++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
'Put these codes on a standard module:
Option Explicit
Public Type POINTAPI
x As Long
Y As Long
End Type
Private Type MOUSEHOOKSTRUCT
pt As POINTAPI
hwnd As Long
wHitTestCode As Long
dwExtraInfo As Long
End Type
Private Declare PtrSafe Function GetWindowLong Lib "user32.dll" _
Alias "GetWindowLongA" ( _
ByVal hwnd As LongPtr, _
ByVal nIndex As Long) As Long
Declare PtrSafe Function SetWindowsHookEx Lib "user32" _
Alias "SetWindowsHookExA" ( _
ByVal idHook As Long, _
ByVal lpfn As LongPtr, _
ByVal hmod As LongPtr, _
ByVal dwThreadId As Long) As LongPtr
Declare PtrSafe Function CallNextHookEx Lib "user32" _
( _
ByVal hHook As LongPtr, _
ByVal ncode As Long, _
ByVal wParam As LongPtr, _
lParam As Any) As LongPtr
Declare PtrSafe Function UnhookWindowsHookEx Lib "user32" ( _
ByVal hhk As LongPtr) As Long
Declare PtrSafe Function PostMessage Lib "user32" Alias _
"PostMessageA" (ByVal hwnd As LongPtr, _
ByVal wMsg As Long, _
ByVal wParam As LongPtr, _
ByVal lParam As LongPtr) As Long
Declare PtrSafe Function WindowFromPoint Lib "user32" _
( _
ByVal Point As LongLong) As LongPtr
Declare PtrSafe Function GetCursorPos Lib "user32" _
(lpPoint As POINTAPI) As Long
Declare PtrSafe Sub CopyMemory Lib "kernel32" _
Alias "RtlMoveMemory" ( _
Destination As Any, _
Source As Any, _
ByVal Length As LongPtr)
Private Const WH_MOUSE_LL As Long = 14
Private Const WM_MOUSEWHEEL As Long = &H20A
Private Const HC_ACTION As Long = 0
Private Const GWL_HINSTANCE As Long = (-6)
Private Const WM_KEYDOWN As Long = &H100
Private Const WM_KEYUP As Long = &H101
Private Const VK_UP As Long = &H26
Private Const VK_DOWN As Long = &H28
Private Const WM_LBUTTONDOWN As Long = &H201
Private mLngMouseHook As LongPtr
Private mListBoxHwnd As LongPtr
Private mbHook As Boolean
' Copies a POINTAPI into a LongLong. For an API requiring a ByVal POINTAPI parameter,
' this LongLong can be passed in instead. Example API's include WindowFromPoint,
' ChildWindowFromPoint, ChildWindowFromPointEx, DragDetect, and MenuItemFromPoint.
Function PointToLongLong(Point As POINTAPI) As LongLong
Dim ll As LongLong
Dim cbLongLong As LongPtr
cbLongLong = LenB(ll)
' make sure the contents will fit
If LenB(Point) = cbLongLong Then
CopyMemory ll, Point, cbLongLong
End If
PointToLongLong = ll
End Function
Sub fnHookListBoxScroll()
Dim lngAppInst As Long
Dim hwndUnderCursor As LongPtr
Dim tPT As POINTAPI
GetCursorPos tPT
DoEvents
hwndUnderCursor = WindowFromPoint(PointToLongLong(tPT))
DoEvents
If mListBoxHwnd <> hwndUnderCursor Then
fnUnhookListBoxScroll
DoEvents
mListBoxHwnd = hwndUnderCursor
DoEvents
lngAppInst = GetWindowLong(mListBoxHwnd, GWL_HINSTANCE)
DoEvents
PostMessage mListBoxHwnd, WM_LBUTTONDOWN, 0&, 0&
DoEvents
If Not mbHook Then
mLngMouseHook = SetWindowsHookEx( _
WH_MOUSE_LL, AddressOf MouseProc, lngAppInst, 0)
DoEvents
mbHook = mLngMouseHook <> 0
DoEvents
End If
DoEvents
End If
DoEvents
End Sub
Sub fnUnhookListBoxScroll()
If mbHook Or mLngMouseHook = 0 Then
UnhookWindowsHookEx mLngMouseHook
mLngMouseHook = 0
mListBoxHwnd = 0
mbHook = False
End If
End Sub
Private Function MouseProc( _
ByVal ncode As Long, ByVal wParam As Long, _
ByRef lParam As MOUSEHOOKSTRUCT) As LongPtr
On Error GoTo errH 'Resume Next
If (ncode = HC_ACTION) Then
If WindowFromPoint(PointToLongLong(lParam.pt)) = mListBoxHwnd Then
If wParam = WM_MOUSEWHEEL Then
MouseProc = True
If lParam.hwnd > 0 Then
PostMessage mListBoxHwnd, WM_KEYDOWN, VK_UP, 0
DoEvents
Else
PostMessage mListBoxHwnd, WM_KEYDOWN, VK_DOWN, 0
DoEvents
End If
PostMessage mListBoxHwnd, WM_KEYUP, VK_UP, 0
DoEvents
Exit Function
End If
Else
fnUnhookListBoxScroll
End If
End If
MouseProc = CallNextHookEx( _
mLngMouseHook, ncode, wParam, ByVal lParam)
Exit Function
errH:
fnUnhookListBoxScroll
End Function
这里的一个简单选项(但当我遇到同样的问题时我没有立即想到)是使用单独的 ScrollBar 控件,其事件连接到两个 ListBox 控件。
您必须弄清楚如何连接所有内容,这可能取决于列表框是否显示一定数量的项目或可扩展。然而,它拥有所有事件。
我的 Excel 应该有什么配置才能使代码正常工作?因为复制和粘贴会损坏文件并自动关闭并损坏