Option Explicit
Public Event OnFocus(ByVal ActiveCtrl As MSForms.Control)
Private WithEvents FocusMonitoringRoutine As CommandBars
Private Type POINTAPI
X As Long
Y As Long
End Type
#If VBA7 Then
#If Win64 Then
Private Declare PtrSafe Function WindowFromPoint Lib "user32" (ByVal Point As LongPtr) As LongPtr
Private Declare PtrSafe Function AccessibleObjectFromPoint Lib "oleacc" (ByVal arg1 As LongLong, ppacc As Any, pvarChild As Variant) As Long
Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As LongLong)
#Else
Private Declare PtrSafe Function WindowFromPoint Lib "user32" (ByVal xPoint As Long, ByVal yPoint As Long) As LongPtr
Private Declare PtrSafe Function AccessibleObjectFromPoint Lib "oleacc" (ByVal lX As Long, ByVal lY As Long, ppacc As IAccessible, pvarChild As Variant) As Long
#End If
Private Declare PtrSafe Function IUnknown_GetWindow Lib "shlwapi" Alias "#172" (ByVal pIUnk As IUnknown, ByVal hUF As LongPtr) As Long
Private Declare PtrSafe Function GetCursorPos Lib "user32.dll" (lpPoint As POINTAPI) As Long
Private Declare PtrSafe Function GetActiveWindow Lib "user32" () As LongPtr
Private Declare PtrSafe Function GetAncestor Lib "user32" (ByVal hUF As LongPtr, ByVal gaFlags As Long) As LongPtr
Private Declare PtrSafe Function GetAsyncKeyState Lib "user32" (ByVal vKey As Long) As Integer
#Else
Private Enum LongPtr
[_]
End Enum
Private Declare Function WindowFromPoint Lib "user32" (ByVal xPoint As Long, ByVal yPoint As Long) As LongPtr
Private Declare Function AccessibleObjectFromPoint Lib "oleacc" (ByVal lX As Long, ByVal lY As Long, ppacc As IAccessible, pvarChild As Variant) As Long
Private Declare Function IUnknown_GetWindow Lib "shlwapi" Alias "#172" (ByVal pIUnk As IUnknown, ByVal hUF As LongPtr) As Long
Private Declare Function GetCursorPos Lib "user32.dll" (lpPoint As POINTAPI) As Long
Private Declare Function GetActiveWindow Lib "user32" () As LongPtr
Private Declare Function GetAncestor Lib "user32" (ByVal hUF As LongPtr, ByVal gaFlags As Long) As LongPtr
Private Declare Function GetAsyncKeyState Lib "user32" (ByVal vKey As Long) As Integer
#End If
Private oForm As UserForm
' ______________________________________ PUBLIC METHODS ___________________________________
Public Sub BeginMonitoring(ByVal UForm As UserForm)
Set oForm = UForm
Set FocusMonitoringRoutine = Application.CommandBars
Call FocusMonitoringRoutine_OnUpdate
End Sub
Public Sub ForceFocus(ByVal Ctrl As MSForms.Control)
On Error Resume Next
With Ctrl
.Visible = False
.Visible = True
.SetFocus
End With
End Sub
' ______________________________________ PRIVATE ROUTINES ___________________________________
Private Sub FocusMonitoringRoutine_OnUpdate()
Const S_OK = 0&
Const CHILDID_SELF = 0&
Const GA_ROOT = 2&
Const ROLE_SYSTEM_GROUPING = &H14&
Static bFormActivated As Boolean
Static oPrevActiveCtrl As Object
Dim hwnd As LongPtr
Dim oIAcc As IAccessible, lRole As Long
Dim tCurPos As POINTAPI
Call GetCursorPos(tCurPos)
#If Win64 Then
Dim lPtr As LongLong, hWinUnderMouse As LongLong
Call CopyMemory(lPtr, tCurPos, LenB(tCurPos))
hWinUnderMouse = WindowFromPoint(lPtr)
Call CopyMemory(lPtr, tCurPos, LenB(lPtr))
If AccessibleObjectFromPoint(lPtr, oIAcc, CHILDID_SELF) = S_OK Then
#Else
Dim hWinUnderMouse As Long
hWinUnderMouse = WindowFromPoint(tCurPos.X, tCurPos.Y)
If AccessibleObjectFromPoint(tCurPos.X, tCurPos.Y, oIAcc, CHILDID_SELF) = S_OK Then
#End If
lRole = oIAcc.accRole(CHILDID_SELF)
End If
Call IUnknown_GetWindow(oForm, VarPtr(hwnd))
If GetActiveWindow = hwnd Then
If bFormActivated = False Then
bFormActivated = True
If lRole = ROLE_SYSTEM_GROUPING Then
RaiseEvent OnFocus(oPrevActiveCtrl)
Else
RaiseEvent OnFocus(RealActiveControl)
End If
SetTabStops True
End If
Else
If bFormActivated Then
bFormActivated = False
Set oPrevActiveCtrl = RealActiveControl
SetTabStops False
End If
End If
If GetAncestor(hWinUnderMouse, GA_ROOT) <> hwnd And GetAsyncKeyState(VBA.vbKeyLButton) Then
bFormActivated = False
Set oPrevActiveCtrl = RealActiveControl
SetTabStops False
End If
With Application.CommandBars.FindControl(ID:=2040)
.Enabled = Not .Enabled
End With
End Sub
Private Sub SetTabStops(ByVal bStop As Boolean)
Dim oCtrl As MSForms.Control
For Each oCtrl In oForm.Controls
oCtrl.TabStop = bStop
Next oCtrl
End Sub
Private Function RealActiveControl() As MSForms.Control
Dim oControl As MSForms.Control
On Error Resume Next
Set oControl = oForm.ActiveControl
Do
Set oControl = CallByName(oControl, "ActiveControl", VbGet)
Loop Until TypeName(oControl) <> "Frame"
Set RealActiveControl = oControl
End Function