If I create an Excel VBA userform form which dynamically generates a variable number of controls, how can I determine the usable area of the form so I can resize the form to fit the controls?
A screenshot is attached below, followed by a code sample, but the last few lines of code do not work.
I am able to write my own auto-size code for UserForm_Initialize(), but this isn't possible if I can't find the usable width and height of the form. There is always a discrepancy which no doubt includes the sizes of the borders, shadows, titlebar, etc.
I do understand VBA is disorganized with various measurement scales such as pixels, twips, inches, etc. So it would be helpful if any answer specifies the scale being used, so I can call the correct conversion functions.
I did find this post about the VBA coordinate systems, which helps but it doesn't answer my question: https://www.reddit.com/r/vba/comments/1arzvx8/coordinate_systems_in_vba_userform_controls/
Private Sub UserForm_Initialize()
Dim ctls As MSForms.Controls
Dim ctl As MSForms.Control
Dim coll As VBA.Collection
Dim ctlCount As Integer
Dim colCount As Integer
Dim x As Single
Dim y As Single
Dim w As Single
Dim h As Single
Dim i As Integer
'Settings.
colCount = 5
w = 24
h = 24
'Get the objects.
Set ctls = Me.Controls
Set coll = New VBA.Collection
'Add the buttons to a collection.
ctlCount = ctls.Count
For i = 0 To ctlCount - 1
For Each ctl In ctls
If ctl.TabIndex = i Then
coll.Add ctl
End If
Next
Next
'Align the buttons.
i = 0
Do
If i < ctlCount Then
y = y + 1
Do
x = x + 1
If x <= colCount Then
i = i + 1
Set ctl = coll.Item(i)
ctl.Left = (x - 1) * w
ctl.Top = (y - 1) * h
ctl.Width = 24
ctl.Height = 24
Else
Exit Do
End If
Loop
x = 0
Else
Exit Do
End If
Loop
'Adjust the form size.
Me.Width = (x + 1) * w '### This does not work. ###
Me.Height = (y + 1) * h '### This does not work. ###
End Sub
As already written in the comments, the properties you are looking for are InsideHeight
and InsideWidth
.
I reworked you code a little bit and this is what I came up with. Note that I put the resizing into a separate routine so that you can call it whenever you are done with creating controls "on the fly".
Private Sub UserForm_Initialize()
CreateControls ... ' If you want to create some controls first
ResizeControls
End Sub
Private Sub ResizeControls
Const ColCount = 5
Const w As Double = 24
Const h As Double = 24
Dim i As Long
ReDim ctrlArr(0 To Me.Controls.Count - 1)
Dim ctrl As Control
For i = 0 To Me.Controls.Count - 1
Set ctrlArr(Me.Controls(i).TabIndex) = Me.Controls(i)
Next
Dim row As Long, col As Long
For i = LBound(ctrlArr) To UBound(ctrlArr)
Set ctrl = ctrlArr(i)
row = i \ ColCount
col = i Mod ColCount
Debug.Print i, row & "/" & col
With ctrl
' .Caption = i ' Just for test, will show the index as text
.Left = col * w
.Top = row * h
.Width = w
.Height = h
End With
Next
Dim rowcount As Long
rowcount = ((UBound(ctrlArr) - 1) \ ColCount) + 1
' Difference between width and usable width resp. height and usable height
Dim dx As Double, dy As Double
dx = Me.Width - Me.InsideWidth
dy = Me.Height - Me.InsideHeight
'Adjust the form size.
Me.Width = ColCount * w + dx
Me.Height = rowcount * h + dy
End Sub
Design Time (9 buttons, chaotic on purpose):
At runtime: