Powershell/Forms - how to anchor listview within a tab control?

3.9k views Asked by At

I'm trying to anchor a listview within a tab page in Forms, such that the listview resizes along with the tab and the other controls are also anchored to allow this, but it looks like it's anchoring to the parent form, not the tab - example code:

Add-Type -AssemblyName System.Windows.Forms
#
$form = New-Object System.Windows.Forms.Form
$form.MinimumSize = '585,700'
$form.StartPosition = 'CenterScreen'
$form.MaximizeBox = $false
$form.CancelButton = $ExitButton
#Autoscaling settings
$form.AutoScale = $true
$form.AutoScaleMode = "Font"
$ASsize = New-Object System.Drawing.SizeF(7,15)
$form.AutoScaleDimensions = $ASsize
#
#
$MainTab = New-Object System.Windows.Forms.TabControl
$MainTab.Size = '540,465'
$MainTab.Location = '15,95'
$MainTab.Multiline = $True
$MainTab.Name = 'TabPage'
$MainTab.SelectedIndex = 0
$MainTab.Anchor = 'Top,Left,Bottom,Right'
#
$TabPage1 = New-Object System.Windows.Forms.TabPage
$Tabpage1.Name = 'TabPage1'
$Tabpage1.Padding = '5,5,5,5'
$Tabpage1.TabIndex = 1
$Tabpage1.Text = 'Host SSH'
$Tabpage1.UseVisualStyleBackColor = $True
$TabPage1.Enabled = $false
#
$ESXhostList = New-Object System.Windows.Forms.ListView
$ESXhostList.View = [System.Windows.Forms.View]::Details
$ESXhostList.Location = '10,15'
$ESXhostList.Size = '510,150'
$ESXhostList.Columns.Add('Host Name',420) | Out-Null
$ESXhostList.Columns.Add('SSH Status',80) | Out-Null
$ESXhostList.Anchor = 'Top,Left,Right'
#
$ConnectBtn = New-Object System.Windows.Forms.Button
$ConnectBtn.Location = '20,190'
$ConnectBtn.Size = '54,24'
$ConnectBtn.Text = 'Connect'
$ConnectBtn.BackgroundImageLayout = 'Center'
$ConnectBtn.Enabled = $true
$ConnectBtn.Anchor = 'Left,Bottom'
#
$TabPage1.Controls.AddRange(@($ESXhostList,$ConnectBtn))
################################################################################
# TabPage 2
################################################################################
$TabPage2 = New-Object System.Windows.Forms.TabPage
$Tabpage2.Name = 'TabPage2'
$Tabpage2.Padding = '5,5,5,5'
$Tabpage2.TabIndex = 2
$Tabpage2.Text = 'Datastores'
$Tabpage2.UseVisualStyleBackColor = $True
$TabPage2.Enabled = $false
#
$DSList = New-Object System.Windows.Forms.ListView
$DSList.View = [System.Windows.Forms.View]::Details
$DSList.Location ='10,15'
$DSList.Size = '510,150'
$DSList.Columns.Add('Name',160) | Out-Null
$DSList.Columns.Add('FreeGB',65) | Out-Null
#
$ConnectBtn2 = New-Object System.Windows.Forms.Button
$ConnectBtn2.Location = '20,190'
$ConnectBtn2.Size = '54,24'
$ConnectBtn2.Text = 'Connect'
$ConnectBtn2.BackgroundImageLayout = 'Center'
$ConnectBtn2.Enabled = $true
#
$TabPage2.Controls.AddRange(@($DSList,$ConnectBtn2))
#
#
$MainTab.Controls.AddRange(@($TabPage1,$TabPage2))
#
# Info/Logging Window
$ProgressLog = New-Object System.Windows.Forms.TextBox
$ProgressLog.Location = '15,570'
$ProgressLog.Size = '540,80'
$ProgressLog.Multiline = $true
$ProgressLog.Anchor = 'Left,Bottom,Right'
$ProgressLog.TabStop = $false
$ProgressLog.ScrollBars = "Vertical"
$ProgressLog.ReadOnly = $true
#
# Add all the Form controls
$form.Controls.AddRange(@($MainTab,$ProgressLog))
#
#End
# Show form
$form.ShowDialog() | Out-Null
$form.Dispose()

In the above example, the only difference between the tabs is that I'm trying to anchor the listview and button controls in the first tab but there's no anchoring in the second tab.

Here's Tab 1 - the listview is outside the bounds of the tab instead of being anchored within it and the button is completely off the tab area

Incorrect anchoring

Here's the identical Tab 2 with no anchoring. Correct layout but no anchoring therefore resizing form does not resize the listview or reposition the button:

Correct layout but no anchoring therefore resizing form not working

Can anyone explain how to do this, as everything I read says that anchoring the tab then anchoring controls within it should work?

4

There are 4 answers

0
secondplace On BEST ANSWER

The reason it is not working has to do with the order you add controls to the control collection. You are adding your ListView and Button before adding the TabPage. The ListView then does not have any information on the width of the TabPage. The Anchor Parameter will then cause trouble since it is based on local coordinates within the TabPage.

You either add each control object right after the parameters are set:

$MainTab.Controls.Add($TabPage1)

Or if you want to keep your array and add them at once you'll have to move your $TabPage2 Parameters right after the $TabPage1 Parameters and then add them both together right away before any other controls:

$MainTab.Controls.AddRange(@($TabPage1,$TabPage2))

Additionally your TabControl needs the local coordinates reset to the top-left corner of the Tab or else the the Anchor will position your objects on the invisible position grid.

This is why you had the following problem:

TabPage1 controls now reposition/resize but I had to use some really funky location and size values:

Add the following Parameter to $MainTab to fix this:

$MainTab.AutoSize = $true
1
thepip3r On

Here is what I'm working with:

Add-Type -AssemblyName System.Windows.Forms
#
$form = New-Object System.Windows.Forms.Form
$form.MinimumSize = '585,700'
$form.StartPosition = 'CenterScreen'
$form.MaximizeBox = $false
$form.CancelButton = $ExitButton
#Autoscaling settings
$form.AutoScale = $true
$form.AutoScaleMode = "Font"
$ASsize = New-Object System.Drawing.SizeF(7,15)
$form.AutoScaleDimensions = $ASsize
#
#
$MainTab = New-Object System.Windows.Forms.TabControl
$MainTab.Size = '540,465'
$MainTab.Location = '15,95'
$MainTab.Multiline = $True
$MainTab.Name = 'TabPage'
$MainTab.SelectedIndex = 0
$MainTab.Anchor = 'Top,Left,Bottom,Right'
#
$TabPage1 = New-Object System.Windows.Forms.TabPage
$Tabpage1.Name = 'TabPage1'
$Tabpage1.Padding = '5,5,5,5'
$Tabpage1.TabIndex = 1
$Tabpage1.Text = 'Host SSH'
$Tabpage1.UseVisualStyleBackColor = $True
$TabPage1.Enabled = $false
#
$ESXhostList = New-Object System.Windows.Forms.ListView
$ESXhostList.View = [System.Windows.Forms.View]::Details
$ESXhostList.Location = '10,15'
$ESXhostList.Size = '510,150'
$ESXhostList.Columns.Add('Host Name',420) | Out-Null
$ESXhostList.Columns.Add('SSH Status',80) | Out-Null
$ESXhostList.Anchor = 'Top,Left,Right'
#
$ConnectBtn = New-Object System.Windows.Forms.Button
$ConnectBtn.Location = '20,190'
$ConnectBtn.Size = '54,24'
$ConnectBtn.Text = 'Connect'
$ConnectBtn.BackgroundImageLayout = 'Center'
$ConnectBtn.Enabled = $true
$ConnectBtn.Anchor = 'Left,Bottom'
#
$TabPage1.Controls.AddRange(@($ESXhostList,$ConnectBtn))
################################################################################
# TabPage 2
################################################################################
$TabPage2 = New-Object System.Windows.Forms.TabPage
$Tabpage2.Name = 'TabPage2'
$Tabpage2.Padding = '5,5,5,5'
$Tabpage2.TabIndex = 2
$Tabpage2.Text = 'Datastores'
$Tabpage2.UseVisualStyleBackColor = $True
$TabPage2.Enabled = $false
#
$DSList = New-Object System.Windows.Forms.ListView
$DSList.View = [System.Windows.Forms.View]::Details
$DSList.Location ='10,15'
$DSList.AutoSize = [System.Windows.Forms.AutoSizeMode]::GrowAndShrink
$DSList.Columns.Add('Name',160) | Out-Null
$DSList.Columns.Add('FreeGB',65) | Out-Null
$DSList.Anchor = 'Top,Left,Right'
#
$ConnectBtn2 = New-Object System.Windows.Forms.Button
$ConnectBtn2.Location = '10, 120'
$ConnectBtn2.Size = '54,24'
$ConnectBtn2.Text = 'Connect'
#$ConnectBtn2.BackgroundImageLayout = 'Center'
$ConnectBtn2.Enabled = $true
#$ConnectBtn2.Anchor = 'Left,Bottom'
#
$TabPage2.Controls.AddRange(@($DSList,$ConnectBtn2))
#
#
$MainTab.Controls.AddRange(@($TabPage1,$TabPage2))
#
# Info/Logging Window
$ProgressLog = New-Object System.Windows.Forms.TextBox
$ProgressLog.Location = '15,570'
$ProgressLog.Size = '540,80'
$ProgressLog.Multiline = $true
$ProgressLog.Anchor = 'Left,Bottom,Right'
$ProgressLog.TabStop = $false
$ProgressLog.ScrollBars = "Vertical"
$ProgressLog.ReadOnly = $true
#
# Add all the Form controls
$form.Controls.AddRange(@($MainTab,$ProgressLog))
#
#End
# Show form
$form.ShowDialog() | Out-Null
$form.Dispose()

Trying to find the blog article I used for an AD tree searcher form I created where it explained very well the alignment, docking, and autofilling issues in Winforms. Will add if I can.

1
Scepticalist On

Ok, I managed to get the desired result like this purely by trial and error. TabPage1 controls now reposition/resize bnut I had to use some really funky location and size values:

Add-Type -AssemblyName System.Windows.Forms -ErrorAction Stop

#
$form = New-Object System.Windows.Forms.Form
$form.MinimumSize = '585,700'
$form.StartPosition = 'CenterScreen'
$form.MaximizeBox = $false
$form.CancelButton = $ExitButton
#Autoscaling settings
$form.AutoScale = $true
$form.AutoScaleMode = "Font"
$ASsize = New-Object System.Drawing.SizeF(7,15)
$form.AutoScaleDimensions = $ASsize
#
# Parent Tab Control
$MainTab = New-Object System.Windows.Forms.TabControl
$MainTab.Size = '540,465'
$MainTab.Location = '15,15'
$MainTab.Name = 'TabPage'
$MainTab.SizeMode = 'FillToRight'
$MainTab.Anchor = 'Top,Left,Bottom,Right'

################################################################################
# TabPage 1
################################################################################
$TabPage1 = New-Object System.Windows.Forms.TabPage
$Tabpage1.Padding = '5,5,5,5'
$Tabpage1.Text = 'Host SSH'
$TabPage1.Parent
#
$ESXhostList = New-Object System.Windows.Forms.ListView
$ESXhostList.View = [System.Windows.Forms.View]::Details
$ESXhostList.Location = '10,15'
$ESXhostList.Size = '180,-50'
$ESXhostList.Columns.Add('Host Name',420) | Out-Null
$ESXhostList.Columns.Add('SSH Status',80) | Out-Null
$ESXhostList.Anchor = 'Top,Left,Bottom,Right'

#
$ConnectBtn = New-Object System.Windows.Forms.Button
$ConnectBtn.Location = '10,-20'
$ConnectBtn.Size = '54,24'
$ConnectBtn.Text = 'Connect'
$ConnectBtn.Anchor = 'Left,Bottom'
#
$TabPage1.Controls.AddRange(@($ESXhostList,$ConnectBtn))
################################################################################
# TabPage 2
################################################################################
$TabPage2 = New-Object System.Windows.Forms.TabPage
$Tabpage2.Padding = '5,5,5,5'
$Tabpage2.Text = 'Datastores'
#
$DSList = New-Object System.Windows.Forms.ListView
$DSList.View = [System.Windows.Forms.View]::Details
$DSList.Location ='10,15'
$DSList.Size = '510,150'
$DSList.Columns.Add('Name',160) | Out-Null
$DSList.Columns.Add('FreeGB',80) | Out-Null
# $DSList.Anchor = 'Top,Left,Right'

#
$ConnectBtn2 = New-Object System.Windows.Forms.Button
$ConnectBtn2.Location = '20,190'
$ConnectBtn2.Size = '54,24'
$ConnectBtn2.Text = 'Connect'
#
$TabPage2.Controls.AddRange(@($DSList,$ConnectBtn2))
#
$MainTab.Controls.AddRange(@($TabPage1,$TabPage2))
#
# Info/Logging Window
$ProgressLog = New-Object System.Windows.Forms.TextBox
$ProgressLog.Location = '15,570'
$ProgressLog.Size = '540,80'
$ProgressLog.Multiline = $true
$ProgressLog.Anchor = 'Left,Bottom,Right'
$ProgressLog.TabStop = $false
$ProgressLog.ScrollBars = "Vertical"
$ProgressLog.ReadOnly = $true
#
# Add all the Form controls
$form.Controls.AddRange(@($MainTab,$ProgressLog))
#
#
#End
# Show form
$form.ShowDialog() | Out-Null
$form.Dispose()

But can anyone explain why it works like this and what's happening?

0
TToth On

It's an older question but I managed to figure it out. You only have to explicitly set the Size of the TabPage itself and nothing more. For the default windows drawing style, the Size paramater is as follows: System.Drawing.Size(WidthOfParentTabControl - 8, HeightOfParentTabControl - 28)

Tested under PS 5.1

Edit: added some more Controls to demonstrate it better

Here is the full code, just save it and run:

using namespace System.Windows.Forms
using namespace System.Drawing
using namespace System.Collections.Generic

Add-Type -AssemblyName System.Windows.Forms

[System.Windows.Forms.Application]::EnableVisualStyles()

#region Initialize Components
    #Mainwindow
    $Form = New-Object System.Windows.Forms.Form
    $Form.ClientSize = New-Object Point(1280,720)
    $Form.text = "Resolution"
    $Form.TopMost = $false

    #region MainTabControl
    [TabControl] $tabControl = New-Object TabControl
    $tabControl.Location = New-Object Point(12, 12)
    #Size(1248, 668) so I have a 12-12-12-12 "margin" on the tabcontrol
    $tabControl.Size = New-Object Size(1256, 696)
    $tabControl.Anchor = "top,right,bottom,left"
    #endregion

    #region Sample Tab
    [TabPage] $tabSample = New-Object TabPage
    $tabSample.Text = "Sample Tab"
    $tabSample.TabIndex = 0
    #Size(WParent - 8, HParent - 28)
    $tabSample.Size = New-Object Size(1248, 668)

        #region Controls
        [ListBox] $lbSample = New-Object ListBox
        [Button] $bSample = New-Object Button
        [Label] $lSample = New-Object Label

        $lbSample.Anchor = "left,bottom"
        $lbSample.Size = New-Object Size(800, 600)
        $lbSample.Location = New-Object Point(12, 50)

        $bSample.Anchor = "top,left,bottom,right"
        $bSample.Location = New-Object Point(12, 12)
        $bSample.Size = New-Object Size("80, 30")
        $bSample.Text = "Resizing button"

        $lSample.Text = "Anchor (right,bottom)"
        $lSample.Anchor = "right,bottom"
        $lSample.Location = New-Object Point(1150, 630)
        #endregion

    $tabSample.Controls.Add($lbSample)
    $tabSample.Controls.Add($bSample)
    $tabSample.Controls.Add($lSample)
    #endregion

    #Adding tabs to MainTabControl
    $tabControl.Controls.Add($tabSample)

    $tabControl.SelectedIndex = 0;
    $Form.Controls.Add($tabControl)
#endregion

[void]$Form.ShowDialog()