Option Explicit
Public Event SheetScroll( _
ByVal Sh As Worksheet, _
ByVal ScrollType As Long, _
ByVal ScrollDirection As Long, _
ByVal ScrolledRowsCount As Long, _
ByVal ScrolledColumnsCount As Long, _
ByRef UndoScroll As Boolean _
)
Private WithEvents MonitorSheetScroll As CommandBars
Private WithEvents Wb As Workbook
#If VBA7 Then
Private Declare PtrSafe Function SetThreadExecutionState Lib "Kernel32.dll" (ByVal esFlags As Long) As Long
Private Declare PtrSafe Function CreateMutex Lib "kernel32" Alias "CreateMutexA" (lpMutexAttributes As Any, ByVal bInitialOwner As Long, ByVal lpName As String) As LongPtr
Private Declare PtrSafe Function ReleaseMutex Lib "kernel32" (ByVal hMutex As LongPtr) As Long
Private Declare PtrSafe Function CloseHandle Lib "kernel32" (ByVal hObject As LongPtr) As Long
Private Declare PtrSafe Function SetProp Lib "user32" Alias "SetPropA" (ByVal hwnd As LongPtr, ByVal lpString As String, ByVal hData As LongPtr) As Long
Private Declare PtrSafe Function GetProp Lib "user32" Alias "GetPropA" (ByVal hwnd As LongPtr, ByVal lpString As String) As LongPtr
#Else
Private Enum LongPtr
[_]
End Enum
Private Declare Function SetThreadExecutionState Lib "Kernel32.dll" (ByVal esFlags As Long) As Long
Private Declare Function CreateMutex Lib "kernel32" Alias "CreateMutexA" (lpMutexAttributes As Any, ByVal bInitialOwner As Long, ByVal lpName As String) As LongPtr
Private Declare Function ReleaseMutex Lib "kernel32" (ByVal hMutex As LongPtr) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As LongPtr) As Long
Private Declare Function SetProp Lib "user32" Alias "SetPropA" (ByVal hwnd As LongPtr, ByVal lpString As String, ByVal hData As LongPtr) As Long
Private Declare Function GetProp Lib "user32" Alias "GetPropA" (ByVal hwnd As LongPtr, ByVal lpString As String) As LongPtr
#End If
Private lPrevRow As Long, lPrevCol As Long
Private Sub Class_Initialize()
Call PreventMultipleClassInstances
Set Wb = ThisWorkbook
Set MonitorSheetScroll = Application.CommandBars
Call MonitorSheetScroll_OnUpdate
End Sub
Private Sub Class_Terminate()
Call ReleasePreviousMutex
PreventSleepMode = False
Set Wb = Nothing
Set MonitorSheetScroll = Nothing
End Sub
Private Sub MonitorSheetScroll_OnUpdate()
Dim bool1 As Byte, bool2 As Byte, bool3 As Byte, bool4 As Byte, bool5 As Byte, bool6 As Byte
Dim lScrollType As Long, lScrollDirection As Long
Dim bUndo As Boolean
If Not ActiveWorkbook Is ThisWorkbook Then GoTo Xit
With Application.ActiveWindow.VisibleRange
bool1 = CBool(lPrevRow <> .Row And lPrevRow)
bool2 = CBool(lPrevCol <> .Column And lPrevCol)
bool3 = CBool(lPrevRow < .Row And lPrevRow)
bool4 = CBool(lPrevRow > .Row And lPrevRow)
bool5 = CBool(lPrevCol < .Column And lPrevCol)
bool6 = CBool(lPrevCol > .Column And lPrevCol)
lScrollType = (bool1 And 1&) + (bool2 And 2&)
lScrollDirection = (bool3 And 1&) + (bool4 And 2&) + (bool5 And 3&) + (bool6 And 4&)
If lScrollType Then
RaiseEvent SheetScroll(ActiveSheet, lScrollType, lScrollDirection, .Row - lPrevRow, .Column - lPrevCol, bUndo)
If bUndo Then
Set MonitorSheetScroll = Nothing
With Application.ActiveWindow
.ScrollRow = lPrevRow
.ScrollColumn = lPrevCol
End With
Set MonitorSheetScroll = Application.CommandBars
Exit Sub
End If
End If
lPrevRow = .Row
lPrevCol = .Column
End With
Xit:
PreventSleepMode = True
With Application.CommandBars.FindControl(Id:=2040&)
.Enabled = Not .Enabled
End With
End Sub
Private Sub PreventMultipleClassInstances()
Const ERROR_ALREADY_EXISTS = &HB7
Dim hMutex As LongPtr
'
hMutex = CreateMutex(ByVal 0&, 1&, "Mutex")
If Err.LastDllError = ERROR_ALREADY_EXISTS Then
Call ReleaseMutex(hMutex)
Call CloseHandle(hMutex)
Call ReleasePreviousMutex
MsgBox "No more than one *CScrollEvent* Class instance is allowed." & _
vbNewLine & vbNewLine & "Please, try again.", vbExclamation
End
Else
Call SetProp(Application.hwnd, "Mutex", hMutex)
End If
End Sub
Private Sub ReleasePreviousMutex()
Dim lPrevMutex As LongPtr
lPrevMutex = GetProp(Application.hwnd, "Mutex")
Call ReleaseMutex(lPrevMutex)
Call CloseHandle(lPrevMutex)
End Sub
Private Property Let PreventSleepMode(ByVal bPrevent As Boolean)
Const ES_SYSTEM_REQUIRED As Long = &H1
Const ES_DISPLAY_REQUIRED As Long = &H2
Const ES_AWAYMODE_REQUIRED = &H40
Const ES_CONTINUOUS As Long = &H80000000
If bPrevent Then
Call SetThreadExecutionState(ES_CONTINUOUS Or ES_DISPLAY_REQUIRED Or ES_SYSTEM_REQUIRED Or ES_AWAYMODE_REQUIRED)
Else
Call SetThreadExecutionState(ES_CONTINUOUS)
End If
End Property
Private Sub wb_SheetDeactivate(ByVal Sh As Object)
lPrevRow = 0&
lPrevCol = 0&
End Sub