Search code examples
winformspowershelllayoutautosize

how to create dynamic layout since AutoSize in winforms doesn't work?


trying to create simple win form in PowerShell. there will be some automatically calculated checkbox and i'm struggling with sizes - seems that 'AutoSize' is a bullsh*t and values returned are from nowhere. can anyone please help and suggest how you create dynamic positioning? based on which values?

Add-Type -AssemblyName System.Drawing
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()

$nrOfNIC = 2
$nrOfDisks = 3
$nrOfPIP = 1

$vShift = 20
$allChkb = 1


$chkForm = New-Object system.Windows.Forms.Form
$chkForm.text = "Remove Resources"
$chkForm.Font = New-Object System.Drawing.Font('Microsoft Sans Serif', 10)
$chkForm.AutoSize = $true
$chkForm.StartPosition = 'CenterScreen'
$chkForm.FormBorderStyle = 'Fixed3D'
$chkForm.Icon = [System.Drawing.SystemIcons]::Question
$chkForm.Topmost = $true
$chkForm.MaximizeBox = $false

$chkVMBox = new-object System.Windows.Forms.GroupBox
#$chkVMBox.MinimumSize = New-Object System.Drawing.Size(180,100) 
$chkVMBox.AutoSize = $true
$chkVMBox.Location = New-Object System.Drawing.Point(10,10)
$chkVMBox.Text = 'VM resources'
#$chkVMBox.Anchor = 'left,top'
#$lastControl = $chkVMBox

if($nrOfDisks -gt 0) {

    $chkVMDisks = new-object System.Windows.Forms.GroupBox
    #$chkVMDisks.MinimumSize = New-Object System.Drawing.Size(180,20) 
    $chkVMDisks.Location = New-Object System.Drawing.Point(10,20)
    $chkVMDisks.Text = 'DISKs'
    #$chkVMDisks.Anchor = 'left,top'

    for($disk=0;$disk -lt $nrOfDisks;$disk++) {

        $chkbDisk = New-Object System.Windows.Forms.Checkbox 
        $chkbDisk.Location = New-Object System.Drawing.Point(10, ($vShift+($disk*$vShift)) ) 
        #$chkbDisk.Anchor = 'left,top'
        $chkbDisk.AutoSize = $true
        $chkbDisk.Text = "disk $disk"
        $chkbDisk.TabIndex = $allChkb++
        $chkVMDisks.Controls.Add($chkbDisk)
        #$allChkb++
    }
    $chkVMBox.Controls.Add($chkVMDisks)
    #$lastControl=$chkVMDisks
}

if($nrOfNIC -gt 0) {
    #$vLocation = $lastControl.Bottom+$shift
    $vLocation = $chkVMDisks.Bottom+$shift

    $chkVMNICs = new-object System.Windows.Forms.GroupBox
    #$chkVMNICs.MinimumSize = New-Object System.Drawing.Size(180,20) 
    $chkVMNICs.AutoSize = $true
    $chkVMNICs.Location = New-Object System.Drawing.Point(10,$vLocation)
    $chkVMNICs.Text = 'NICs'
    #$chkVMNICs.Anchor = 'left,top'

    for($nic = 0;$nic -lt $nrOfNIC; $nic++) {
        $chkbNIC = New-Object System.Windows.Forms.Checkbox 
        $chkbNIC.Location = New-Object System.Drawing.Point(10, ($vShift+($nic*$vShift)) ) 
        #$chkbNIC.Anchor = 'left,top'
        $chkbNIC.AutoSize = $true
        $chkbNIC.Text = "nic $nic"
        $chkbNIC.TabIndex = $allChkb++
        $chkVMNICs.Controls.Add($chkbNIC)
        #$allChkb++
    }
    $chkVMBox.Controls.Add($chkVMNICs)
    #$lastControl = $chkVMNICs
}

    #$vLocation = $lastControl.Bottom+$shift
    $vLocation = $chkVMNICs.Bottom+$shift
    $chkbVMdiag = New-Object System.Windows.Forms.Checkbox 
    $chkbVMdiag.Location = New-Object System.Drawing.Point(10,$vLocation) 
    #$chkbVMdiag.Anchor = 'left,top'
    $chkbVMdiag.AutoSize = $true
    $chkbVMdiag.Text = "Boot Diagnostics"
    $chkbVMdiag.TabIndex = $allChkb++
    $chkVMBox.Controls.Add($chkbVMdiag)
    
    
    $vLocation = $chkVMBox.bottom + 40
    $btOK = New-Object System.Windows.Forms.Button
    $btOK.Location = New-Object System.Drawing.Size(15,$vLocation)
    $btOK.Size = New-Object System.Drawing.Size(70,20)
    $btOK.Text = "OK"
    $btOK.DialogResult = [System.Windows.Forms.DialogResult]::OK
    #$btOK.Anchor = 'left,bottom'

    $btCancel = New-Object System.Windows.Forms.Button
    $btCancel.Location = New-Object System.Drawing.Size(95,$vLocation)
    $btCancel.Size = New-Object System.Drawing.Size(70,20)
    $btCancel.Text = "Cancel"
    $btCancel.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
    #$btCancel.Anchor = 'right,bottom'     

$chkForm.AcceptButton = $btOK
$chkForm.CancelButton = $btCancel
$chkForm.Controls.AddRange(@($chkVMBox, $btOK, $btCancel))

[void]$chkForm.ShowDialog()

effect: effect of code

  • there is some big padding on the right of the main form
  • in check box list - there is additional space in first, double the space in second control
  • there is some padding under last chkbox
  • chkVMBox (main box) returns 'size = 100' 'bottom = 110'and buttons which uses this value are actually under other controls and invisible.

seems that ALL VALUES returned with 'autosize' are bullsh*t. so how do you create dynamic layout, not knowing sizes/number of elements upfront?


Solution

    • there is some big padding on the right of the main form
    • in check box list - there is additional space in first, double the space in second control
    • there is some padding under last chkbox

    Set .AutoSizeMode = 'GrowAndShrink' as well as .MinimumSize properties along with the .AutoSize.

    • buttons … are actually under other controls and invisible

    Button positions are derived from $chkVMBox.bottom; use $chkForm.Controls.Add($chkVMBox) before computing vertical location for buttons and remove $chkVMBox from $chkForm.Controls.AddRange(…)

    • to create dynamic layout, not knowing sizes/number of elements upfront:

    I'd try calculating horizontal and vertical positions and (minimal) sizes of controls relative to an invariant; for instance, those are computed from $chkForm.Font in the following code (aspiration apparently inconsistent:), see the $hShift and $vShift variables, their values and usage.

    Add-Type -AssemblyName System.Drawing
    Add-Type -AssemblyName System.Windows.Forms
    [System.Windows.Forms.Application]::EnableVisualStyles()
    Remove-Variable chk* -ErrorAction SilentlyContinue
    $nrOfNIC = 2
    $nrOfDisks = 3
    $nrOfPIP = 1
    $allChkb = 1
    
    $chkForm = New-Object System.Windows.Forms.Form
    $chkForm.text = "Remove Resources"
    $chkForm.Font = New-Object System.Drawing.Font('Microsoft Sans Serif', 10)
    $hShift  = $chkForm.Font.SizeInPoints
    $vShift  = $chkForm.Font.Height * 1.5  ### arbitrary ad hoc coefficient
    
    $chkForm.StartPosition = 'CenterScreen'
    $chkForm.FormBorderStyle = 'Fixed3D'
    $chkForm.Icon = [System.Drawing.SystemIcons]::Question
    $chkForm.Topmost = $true
    $chkForm.AutoSize = $true
    $chkForm.AutoSizeMode = 'GrowAndShrink'
    $chkForm.MinimumSize = [System.Drawing.Size]::new(100,100)
    # $chkForm.AutoScaleMode = 'Font'
    $chkForm.MaximizeBox = $false
    $chkForm.MinimizeBox = $false
    
    $chkVMBox = new-object System.Windows.Forms.GroupBox
    $chkVMBox.AutoSizeMode = 'GrowAndShrink'
    $chkVMBox.AutoSize = $true
    $chkVMBox.Location = New-Object System.Drawing.Point(10,10)
    $chkVMBox.Text = 'VM resources'
    
    if($nrOfDisks -gt 0) {
    
        $chkVMDisks = new-object System.Windows.Forms.GroupBox
        $chkVMDisks.Location = New-Object System.Drawing.Point($hShift,( $vShift+10))
        $chkVMDisks.Text = 'DISKs'
        $chkVMDisks.AutoSize = $true
        $chkVMDisks.MinimumSize = [System.Drawing.Size]::new(($chkVMBox.Size.Width - $hShift),$vShift)
        $chkVMDisks.AutoSizeMode = 'GrowAndShrink'
    
        for($disk=0;$disk -lt $nrOfDisks;$disk++) {
    
            $chkbDisk = New-Object System.Windows.Forms.Checkbox 
            $chkbDisk.Location = New-Object System.Drawing.Point($hShift, ($vShift*($disk+1)))
            $chkbDisk.AutoSize = $true
            $chkbDisk.Text = "disk $disk"
            $chkbDisk.TabIndex = $allChkb++
            $chkVMDisks.Controls.Add($chkbDisk)
            #$allChkb++
        }
        $chkVMBox.Controls.Add($chkVMDisks)
        #$lastControl=$chkVMDisks
    }
    
    if($nrOfNIC -gt 0) {
        #$vLocation = $lastControl.Bottom+$hShift
        $vLocation = $chkVMDisks.Bottom + $vShift
    
        $chkVMNICs = new-object System.Windows.Forms.GroupBox
        $chkVMNICs.AutoSize = $true
        $chkVMNICs.MinimumSize = [System.Drawing.Size]::new(($chkVMBox.Size.Width - $hShift),$vShift)
        $chkVMNICs.AutoSizeMode = 'GrowAndShrink'
        $chkVMNICs.Location = New-Object System.Drawing.Point($hShift,$vLocation)
        $chkVMNICs.Text = 'NICs'
    
        for($nic = 0;$nic -lt $nrOfNIC; $nic++) {
            $chkbNIC = New-Object System.Windows.Forms.Checkbox 
            $chkbNIC.Location = New-Object System.Drawing.Point($hShift, ($vShift*($nic+1)) ) 
            # $chkbNIC.Anchor = 'left,top'
            $chkbNIC.AutoSize = $true
            $chkbNIC.Text = "nic $nic"
            $chkbNIC.TabIndex = $allChkb++
            $chkVMNICs.Controls.Add($chkbNIC)
            #$allChkb++
        }
        $chkVMBox.Controls.Add($chkVMNICs)
        #$lastControl = $chkVMNICs
    }
    
        #$vLocation = $lastControl.Bottom+$hShift
        $vLocation = $chkVMNICs.Bottom + $vShift
        $chkbVMdiag = New-Object System.Windows.Forms.Checkbox 
        $chkbVMdiag.Location = New-Object System.Drawing.Point($hShift,$vLocation) 
        $chkbVMdiag.AutoSize = $true
        $chkbVMdiag.Text = "Boot Diagnostics"
        $chkbVMdiag.TabIndex = $allChkb++
        $chkVMBox.Controls.Add($chkbVMdiag)
        
    $chkForm.Controls.Add($chkVMBox)
        
        $vLocation = $chkVMBox.bottom + 20
        $btOK = New-Object System.Windows.Forms.Button
        $btOK.Location = New-Object System.Drawing.Point(15,$vLocation)
        # $btOK.Size = New-Object System.Drawing.Size(70,20)
        $btOK.AutoSize = $true
        $btOK.Text = "OK"
        $btOK.DialogResult = [System.Windows.Forms.DialogResult]::OK
    
        $btCancel = New-Object System.Windows.Forms.Button
        $btCancel.Location = New-Object System.Drawing.Point(95,$vLocation)
        # $btCancel.Size = New-Object System.Drawing.Size(70,20)
        $btCancel.AutoSize = $true
        $btCancel.Text = "Cancel"
        $btCancel.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
    
    $chkForm.AcceptButton = $btOK
    $chkForm.CancelButton = $btCancel
    $chkForm.Controls.AddRange(@($btOK, $btCancel))
    
    [void]$chkForm.ShowDialog()
    $chkForm.Dispose()