Reputation: 1111
I want to display UserForm right under the button that is placed in cell so it can simulate some popup window (just like dropdown list).
I tried many solutions over the net and none of them worked. The main problem is I'm not able to get the absolute screen location of cell or button on sheet.
Upvotes: 1
Views: 5202
Reputation: 11
this modifaction makes it work with Panes if you freeze certain rows and columns :
Public Sub FormShow(ByVal objForm As Object, ByVal Rng As Range)
Dim L As Single, T As Single
If ActiveWindow.FreezePanes Then
L = ActiveWindow.Panes(GetPanesIndex(Rng)).PointsToScreenPixelsX(Rng.Left)
T = ActiveWindow.Panes(GetPanesIndex(Rng)).PointsToScreenPixelsY(Rng.Top + Rng.Height)
Else
L = ActiveWindow.ActivePane.PointsToScreenPixelsX(Rng.Left)
T = ActiveWindow.ActivePane.PointsToScreenPixelsY(Rng.Top + Rng.Height)
End If
ConvertPixelsToPoints L, T
With objForm
.StartUpPosition = 0
.Left = L
.Top = T
.Show
End With
End Sub
Function GetPanesIndex(ByVal Rng As Range) As Integer
Dim sr As Long: sr = ActiveWindow.SplitRow
Dim sc As Long: sc = ActiveWindow.SplitColumn
Dim r As Long: r = Rng.Row
Dim c As Long: c = Rng.Column
Dim Index As Integer: Index = 1
Select Case True
Case sr = 0 And sc = 0: Index = 1
Case sr = 0 And sc > 0 And c > sc: Index = 2
Case sr > 0 And sc = 0 And r > sr: Index = 2
Case sr > 0 And sc > 0 And r > sr: If c > sc Then Index = 4 Else Index = 3
Case sr > 0 And sc > 0 And c > sc: If r > sr Then Index = 4 Else Index = 2
End Select
GetPanesIndex = Index
End Function
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
FormShow UserForm1, Target
SetForegroundWindow (Application.hWnd)
' aktivates Application window
' so Cellselection by key is possible
' -> Userform moves with Arrow keys not only mouse selection
End Sub
Upvotes: 1
Reputation: 3322
Try this in new module:
Option Explicit
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 GetDeviceCaps Lib "gdi32" ( _
ByVal hDC As Long, _
ByVal nIndex As Long) As Long
Const LOGPIXELSX = 88
Const LOGPIXELSY = 90
Const TWIPSPERINCH = 1440
Sub ConvertPixelsToPoints(ByRef x As Single, ByRef y As Single)
Dim hDC As Long
Dim RetVal As Long
Dim XPixelsPerInch As Long
Dim YPixelsPerInch As Long
hDC = GetDC(0)
XPixelsPerInch = GetDeviceCaps(hDC, LOGPIXELSX)
YPixelsPerInch = GetDeviceCaps(hDC, LOGPIXELSY)
RetVal = ReleaseDC(0, hDC)
x = x * TWIPSPERINCH / 20 / XPixelsPerInch
y = y * TWIPSPERINCH / 20 / YPixelsPerInch
End Sub
Sub FormShow(ByVal objForm As Object, ByVal Rng As Range)
Dim L As Single, T As Single
L = ActiveWindow.ActivePane.PointsToScreenPixelsX(Rng.Left)
T = ActiveWindow.ActivePane.PointsToScreenPixelsY(Rng.Top + Rng.Height)
ConvertPixelsToPoints L, T
With objForm
.StartUpPosition = 0
.Left = L
.Top = T
.Show
End With
End Sub
Sub test()
FormShow UserForm1, ActiveCell
End Sub
To test it add BeforeRightClick
event:
Private Sub Worksheet_BeforeRightClick(ByVal Target As Range, Cancel As Boolean)
FormShow UserForm1, Target
Cancel = True
End Sub
Now if you Right Click
any cell in this worksheet the UserForm1
will show under this cell.
Notes:
Upvotes: 2
Reputation: 17627
You would use something of this logic:
Sub SO()
With UserForm1
.StartUpPosition = 0
.Top = Application.Top + (ActiveSheet.Shapes(Application.Caller).Top + 170)
.Left = Application.Left + (ActiveSheet.Shapes(Application.Caller).Left + 25)
.Show
End With
End Sub
And your button would call the sub SO()
Upvotes: 2