This is very cool
Code
Option Explicit
Option Compare Text
'by cytop
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function ClientToScreen Lib "user32" _
(ByVal hwnd As Long, lpPoint As POINTAPI) As Long
Private Declare Function GetDeviceCaps Lib "Gdi32" (ByVal hDC As Long, ByVal _
nIndex As Long) As Long
Private Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function ReleaseDC Lib "user32" (ByVal hwnd As Long, ByVal _
hDC As Long) As Long
Private Declare Function SetCursorPos Lib "user32" (ByVal X As Long, ByVal Y As Long) As Long
Private Const LOGPIXELSX = 88
Private Const LOGPIXELSY = 90
Private Type POINTAPI
X As Long
Y As Long
End Type
Public Sub CenterMouseOver(f As Object, c As Object)
Dim P As POINTAPI
Dim lngHwnd As Long
'// Window handle - might be an idea to make sure caption
'// is not the default 'UserformX' to avoid possibility
'// of finding the wrong window - I'll tignten this
'// up later to eliminate that possibility if needed.
lngHwnd = FindWindow(vbNullString, f.Caption)
'// PointPerPixelX/Y is the key to this.
'// Find the mid point of the control based purely on the form
'// co-ordinates and convert based on the number of points
'// per pixel (X & Y are different - See constants declaration).
P.X = (c.Left + (c.Width \ 2)) / PointsPerPixelX
P.Y = (c.Top + (c.Height \ 2)) / PointsPerPixelY
'// Convert the control location baased on the userform
'// to an absolute Screen position based on 0,0 being
'// top left handcorner of the screen/
ClientToScreen lngHwnd, P
'// And simply put the cursor there
SetCursorPos P.X, P.Y
End Sub
Private Function PointsPerPixelX() As Double
Dim hDC As Long
'// Desktop Device Context
hDC = GetDC(0)
'// A point is 1/72th of an inch and LOGPIXELSX returns
'// the number of pixels per logical inch, so divided to give
'// the width of a pixel in Excel's userform coordinates
PointsPerPixelX = 72 / GetDeviceCaps(hDC, LOGPIXELSX)
ReleaseDC 0, hDC
End Function
Public Function PointsPerPixelY() As Double
'// Same as PointsPerPixelX
Dim hDC As Long
hDC = GetDC(0)
PointsPerPixelY = 72 / GetDeviceCaps(hDC, LOGPIXELSY)
ReleaseDC 0, hDC
End Function
Display More
and in the userform activate event
Code
'// Pass userform (Needed to get handle) and the
'// control. This works for any controi with a
'// Left/Top property
CenterMouseOver Me, ComboBox1
note
the code must go in the Activate event rather than the Initialize event.
It seems the userform does not really exist as an object (as far as Windows is concerned, at least) until after the Initialize event has completed - so the code will not work correctly in the Initialize event.
Once you get under the hood with the API you run into loads of little issues like this... All part of the way VBA is structured.