鼠标在列表框上滚动

Posted

技术标签:

【中文标题】鼠标在列表框上滚动【英文标题】:Mouse scroll on a listbox 【发布时间】:2016-01-20 21:57:42 【问题描述】:

我正在使用 Peter Thornton 的这段代码,它使我能够在用户表单列表框中滚动,但是每次我的鼠标穿过列表框(只是将光标移动到用户表单的另一部分,甚至没有点击)它“激活"列表框。有什么办法可以“阻止”这种情况发生吗?我的意思是,只有当我单击向下箭头打开列表框时,鼠标滚动才能起作用?

这是 Peter Thronton 的代码:

'''''' normal module code

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 FindWindow Lib "user32" _
                                        Alias "FindWindowA" ( _
                                                        ByVal lpClassName As String, _
                                                        ByVal lpWindowName As String) As Long

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
Private mCtl As MSForms.Control
Dim n As Long

Sub HookListBoxScroll(frm As Object, ctl As MSForms.Control)
Dim lngAppInst As Long
Dim hwndUnderCursor As Long
Dim tPT As POINTAPI
     GetCursorPos tPT
     hwndUnderCursor = WindowFromPoint(tPT.X, tPT.Y)
     If Not frm.ActiveControl Is ctl Then
             ctl.SetFocus
     End If
     If mListBoxHwnd <> hwndUnderCursor Then
             UnhookListBoxScroll
             Set mCtl = ctl
             mListBoxHwnd = hwndUnderCursor
             lngAppInst = GetWindowLong(mListBoxHwnd, GWL_HINSTANCE)
             ' PostMessage mListBoxHwnd, WM_LBUTTONDOWN, 0&, 0&
             If Not mbHook Then
                     mLngMouseHook = SetWindowsHookEx( _
                                                     WH_MOUSE_LL, AddressOf MouseProc, lngAppInst, 0)
                     mbHook = mLngMouseHook <> 0
             End If
     End If
End Sub

Sub UnhookListBoxScroll()
     If mbHook Then
                Set mCtl = Nothing
             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
Dim idx As Long
        On Error GoTo errH
     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
'                                Else
'                                        PostMessage mListBoxHwnd, WM_KEYDOWN, VK_DOWN, 0
'                                End If
'                                PostMessage mListBoxHwnd, WM_KEYUP, VK_UP, 0
                                If lParam.hwnd > 0 Then idx = -1 Else idx = 1
'                             idx = idx + mCtl.ListIndex
'                             If idx >= 0 Then mCtl.ListIndex = idx
                             idx = idx + mCtl.TopIndex
                             If idx >= 0 Then mCtl.TopIndex = idx
                                Exit Function
                     End If
             Else
                     UnhookListBoxScroll
             End If
     End If
     MouseProc = CallNextHookEx( _
                             mLngMouseHook, nCode, wParam, ByVal lParam)
     Exit Function
errH:
     UnhookListBoxScroll
End Function
'''''''' end normal module code

【问题讨论】:

HookListBoxScroll 过程中的“ctl.SetFocus”行是激活列表框的内容。如果您删除该部分并将其更改为“退出子”,那么这可能仅在列表框已激活时才有效。可能需要进行更多调整,因为我实际上并没有将此代码放入 Excel 工作簿中进行测试。 已在上面的链接中解决。不过谢谢大家的关注。 =) Excel 2010 UserForm - form does not scroll with Mouse Wheel的可能重复 【参考方案1】:

这会起作用 =)

Option Explicit

'This will compile in 32 bit Excel only

Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
                            (ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long

Private Declare Function GetForegroundWindow Lib "user32" () As Long

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
                       (ByVal Destination As Long, ByVal Source As Long, ByVal Length 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 Type POINTAPI
  x As Long
  Y As Long
End Type

Private Type MSLLHOOKSTRUCT  'Will Hold the lParam struct Data
  pt As POINTAPI
  mouseData As Long  ' Holds Forward\Backward flag
  flags As Long
  time As Long
  dwExtraInfo As Long
End Type

Private Const HC_ACTION = 0
Private Const WH_MOUSE_LL = 14
Private Const WM_MOUSEWHEEL = &H20A
Private Const GWL_HINSTANCE = (-6)

Public Const nMyControlTypeNONE = 0
Public Const nMyControlTypeUSERFORM = 1
Public Const nMyControlTypeFRAME = 2
Public Const nMyControlTypeCOMBOBOX = 3
Public Const nMyControlTypeLISTBOX = 4

Private hhkLowLevelMouse As Long
Private udtlParamStuct As MSLLHOOKSTRUCT

Public myGblUserForm As UserForm
Public myGblControlObject As Object
Public iGblControlType As Integer

Public myGblUserFormControl As Object

Function GetHookStruct(ByVal lParam As Long) As MSLLHOOKSTRUCT
' VarPtr returns address; LenB returns size in bytes.
  CopyMemory VarPtr(udtlParamStuct), lParam, LenB(udtlParamStuct)
  GetHookStruct = udtlParamStuct
End Function

Function LowLevelMouseProc _
         (ByVal nCode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

  'Avoid XL crashing if RunTime error occurs due to Mouse fast movement

  Dim iDirection As Long

  On Error Resume Next
  '    \\ Unhook & get out in case the application is deactivated
  If GetForegroundWindow <> FindWindow("ThunderDFrame", myGblUserForm.Caption) Then
    UnHook_Mouse
    Exit Function
  End If
  If (nCode = HC_ACTION) Then
    If wParam = WM_MOUSEWHEEL Then

      iDirection = GetHookStruct(lParam).mouseData
      Call ProcessMouseWheelMovement(iDirection)

      '\\ Don't process Default WM_MOUSEWHEEL Window message
      LowLevelMouseProc = True
    End If

    Exit Function
  End If
  LowLevelMouseProc = CallNextHookEx(0, nCode, wParam, ByVal lParam)
End Function

Sub Hook_Mouse()
' Statement to maintain the handle of the hook if clicking outside of the control.
' There isn't a Hinstance for Application, so used GetWindowLong to get handle.
  If hhkLowLevelMouse < 1 Then
    hhkLowLevelMouse = SetWindowsHookEx(WH_MOUSE_LL, AddressOf LowLevelMouseProc, _
      GetWindowLong(FindWindow("ThunderDFrame", myGblUserForm.Caption), GWL_HINSTANCE), 0)
  End If
End Sub

Sub UnHook_Mouse()
  If hhkLowLevelMouse <> 0 Then
    UnhookWindowsHookEx hhkLowLevelMouse
    hhkLowLevelMouse = 0
  End If
End Sub

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'UserForm MouseWheel Code
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Sub ProcessMouseWheelMovement(ByVal iDirection As Long)
  'This processes MouseWheel Scrolls
  '
  'Thank You Mathieu Plante from July 2004

  Dim i As Long
  Dim iMultiplier As Long

  'Debug.Print iDirection, iGblControlType, Now()


  Select Case iGblControlType


    ''''''''''''''''''''''''''''''''''''''''''''''''
    'UserForm Mouse Scroll
    ''''''''''''''''''''''''''''''''''''''''''''''''
    Case nMyControlTypeUSERFORM

      iMultiplier = 3

      If iDirection > 0 Then

        For i = 1 To iMultiplier
          myGblControlObject.Scroll fmScrollActionNoChange, fmScrollActionLineUp
        Next i
      Else
        For i = 1 To iMultiplier
          myGblControlObject.Scroll fmScrollActionNoChange, fmScrollActionLineDown
        Next i
      End If


    ''''''''''''''''''''''''''''''''''''''''''''''''
    'Frame Mouse Scroll
    ''''''''''''''''''''''''''''''''''''''''''''''''
    Case nMyControlTypeFRAME

      iMultiplier = 5

      If iDirection > 0 Then

        For i = 1 To iMultiplier
          myGblControlObject.Scroll fmScrollActionNoChange, fmScrollActionLineUp
        Next i
      Else
        For i = 1 To iMultiplier
          myGblControlObject.Scroll fmScrollActionNoChange, fmScrollActionLineDown
        Next i
      End If


    ''''''''''''''''''''''''''''''''''''''''''''''''
    'ComboBox Mouse Scroll
    ''''''''''''''''''''''''''''''''''''''''''''''''
    Case nMyControlTypeCOMBOBOX

      With myGblControlObject
        '\\ if rolling forward increase Top index by 1 to cause an Up Scroll
        If iDirection > 0 Then
          .TopIndex = .TopIndex - 1
        Else  '\\ if rolling backward decrease Top index by 1 to cause a Down Scroll
          .TopIndex = .TopIndex + 1
        End If
      End With
      'Debug.Print "Top Index = " & myGblControlObject.TopIndex

    ''''''''''''''''''''''''''''''''''''''''''''''''
    'Listbox Mouse Scroll
    ''''''''''''''''''''''''''''''''''''''''''''''''
    Case nMyControlTypeLISTBOX

      With myGblControlObject
        '\\ if rolling forward increase Top index by 1 to cause an Up Scroll
        If iDirection > 0 Then
          .TopIndex = .TopIndex - 1
        Else  '\\ if rolling backward decrease Top index by 1 to cause a Down Scroll
          .TopIndex = .TopIndex + 1
        End If
      End With
      'Debug.Print "Top Index = " & myGblControlObject.TopIndex

  End Select

End Sub

感谢 LJMetzer 来自 Excel 论坛。

【讨论】:

这个不能在我的Excel上编译,有人知道原因吗? @Bandoleras 你必须在 64 位机器上(比如我)。 Check out this *** question/answer 关于如何调整声明的函数以在 64 位环境中工作。

以上是关于鼠标在列表框上滚动的主要内容,如果未能解决你的问题,请参考以下文章

如何在滚动框上创建缓慢的滚动效果?

在组合框和列表框上为 ListFillRange 使用表格列

通过userform列表框上的名称管理器的动态范围

如何在 MFC 中垂直同步两个列表控件

使用带有排序组合框和页面组合框上的许多项目的列表视图

文件拖放在列表框上不起作用