1

I am trying to create a script that lets the user choose a server from a drop down list. Each server is mapped to a unique array which goes to a foreach loop. The loop cycles through the array and prints a check box on to the form with the value that is in the array. This works with no issue. The problem is when I select the different Server from the drop down list and click "Select Server" button the new values in the Array do not overwrite the existing values. In other words the check box values on the form are not updating with the new array values. What I would like to happen is when you click the "Select Server" button the check box values update to reflect the array values associated with their corresponding server.

Here is an example.

  1. Choose ServerA from drop down
  2. Select 'Select Server'
  3. The following check boxes will list out on to the form in checkbox's:@('Zero','One','Two','Three')
  4. Now if you click ServerB and select "Select Server" I would expect new check boxes to overwrite the existing check boxes with these values: @('0','1','2','3')

Unfortunately the values do not update. I need to have the array values update when the "Select Server" button is selected... Ive looked around at forums and have found some possible solutions but they all seems to fall short.

Thank you in advance.

function GenerateForm 
{   
$PC=
{
    $hostname = $dropdown.SelectedItem
    
    if ($hostname -eq "ServerA")

        { $CheckBoxLabels = @('Zero','One','Two','Three')
        }
    elseif($hostname -eq "ServerB")
        {
           $CheckBoxLabels = @('0','1','2','3')
     }
        
    $name = New-Object System.Windows.Forms.Label -Property @{
    Text = "Start Time"
    Location = "900, 220"
    ForeColor = "Black"
    Height = 22
    Width = 200
    }
    $form1.Controls.Add($hostname)
        
    $CheckBoxCounter = 1
    
    $CheckBoxes = foreach($Label in $CheckBoxLabels) 
    {   
        $CheckBox = New-Object System.Windows.Forms.CheckBox        
        $CheckBox.UseVisualStyleBackColor = $True
        $System_Drawing_Size = New-Object System.Drawing.Size
        $System_Drawing_Size.Width = 104
        $System_Drawing_Size.Height = 24
        $CheckBox.Size = $System_Drawing_Size
        $CheckBox.TabIndex = 2
    
        $CheckBox.Text = $Label
        
        $System_Drawing_Point = New-Object System.Drawing.Point
        $System_Drawing_Point.X = 27
        # Make sure to vertically space them dynamically, counter comes in handy
        $System_Drawing_Point.Y = 200 + (($CheckBoxCounter - 1) * 31) #Controls location on Y axis
        $CheckBox.Location = $System_Drawing_Point
        $CheckBox.DataBindings.DefaultDataSourceUpdateMode = 0

        # Give it a unique name based on our counter
        $CheckBox.Name = "CheckBox$CheckBoxCounter"

       $form1.Controls.Add($CheckBox)

        # return object ref to array
        $Global:CheckBox
        # increment our counter
        $CheckBoxCounter++
    }
    }

    $form1 = New-Object System.Windows.Forms.Form
    $form1.Text = "UCCE Log Collector - Version 2.0"
    $form1.Name = "form1"
    $form1.DataBindings.DefaultDataSourceUpdateMode = 0
    $System_Drawing_Size = New-Object System.Drawing.Size
    $System_Drawing_Size.Width = 1150
    $System_Drawing_Size.Height = 500
    $form1.ClientSize = $System_Drawing_Size

$dropdown = New-Object System.Windows.Forms.ListBox
$dropdown.Location = New-Object System.Drawing.Point(10,50)
$dropdown.Size = New-Object System.Drawing.Size(100,20)
$dropdown.Height = 80

[void] $dropdown.Items.Add('ServerA')
[void] $dropdown.Items.Add('ServerB')


$form1.Controls.Add($dropdown)

    ######### Select Server Button
    $SelectPC = New-Object System.Windows.Forms.Button
    $SelectPC.TabIndex = 4
    $SelectPC.Name = "SelectPC"
    $System_Drawing_Size = New-Object System.Drawing.Size
    $System_Drawing_Size.Width = 120
    $System_Drawing_Size.Height = 30
    $SelectPC.Size = $System_Drawing_Size
    $SelectPC.UseVisualStyleBackColor = $True
    $SelectPC.Text = "Select Server"
    $System_Drawing_Point = New-Object System.Drawing.Point
    $System_Drawing_Point.X = 0 # 0
    $System_Drawing_Point.Y = 150 #150
    $SelectPC.Location = $System_Drawing_Point
    $SelectPC.DataBindings.DefaultDataSourceUpdateMode = 0
    $SelectPC.add_Click($PC)

    $form1.Controls.Add($SelectPC)
    
        $result = $form1.ShowDialog()
    $result

}
GenerateForm
4
  • You're saying it works the first time you select a server but not after that? You never set the Checked or CheckState properties of any CheckBox. Each time you select a server you're adding new controls on top of existing controls without removing or reusing what's already there. I believe $CheckBox.Name = "CheckBox$CheckBoxCounter" will cause name collisions, too, because each time $CheckBoxCounter starts back at 1. Commented Nov 6, 2021 at 4:59
  • Correct, if I select ServerA or ServerB the check boxes will generate with their corresponding array. But if I then select a new server it doesn't replace the existing array on the form. Any chance you could share an example as to how I can fix this? Commented Nov 6, 2021 at 5:07
  • I see, you're still seeing the same Text from the first load. Instead of creating the CheckBoxes every time Click/$PC is invoked, you want to create them one time after $form1 is created (the same way you do with $dropdown and $SelectPC) and store them in separate variables or, even better, an array. Then when Click/$PC is invoked is when you'd merely set their Text and, I presume, Checked properties. Commented Nov 6, 2021 at 5:15
  • Could you please share an example I can work from? This would help me considerably. Commented Nov 6, 2021 at 5:53

1 Answer 1

1

Per the request in the comments to accommodate a variable number of properties for each host, first we'll create a Panel to hold our CheckBoxes and add it to our Form...

# Create a Panel to contain the dynamic collection of CheckBoxes
$HostPropertiesPanel = New-Object -TypeName 'System.Windows.Forms.Panel' -Property @{
    # To illustrate the changing Size of the Panel
    BackColor = [System.Drawing.Color]::GreenYellow
    Location  = New-Object -TypeName 'System.Drawing.Point' -Property @{
        X = 27
        Y = 200
    }
    Name      = 'HostPropertiesPanel'
    Size      = [System.Drawing.Size]::Empty
}
$form1.Controls.Add($HostPropertiesPanel)

This eliminates the need to keep track of the CheckBoxes ourselves since we'll know they're all contained within this Panel. The Size is initially Empty since there's no CheckBoxes to display until a host is selected.

Inside of Click/$PC we'll then change how our host properties are defined...

# Set $hostProperties to a Hashtable array for the corresponding value of $hostname
# The IsChecked values are arbitrary for demonstration purposes
#TODO: Replace if...elseif with a switch statement; see "help about_Switch"
$hostProperties = if ($hostname -eq "ServerA") {
    @{ Label = 'Zero';  IsChecked = $false },
    @{ Label = 'One';   IsChecked = $true },
    @{ Label = 'Two';   IsChecked = $true },
    @{ Label = 'Three'; IsChecked = $false }
}
elseif ($hostname -eq "ServerB") {
    @{ Label = '0'; IsChecked = $true },
    @{ Label = '1'; IsChecked = $false },
    @{ Label = '2'; IsChecked = $false },
    @{ Label = '3'; IsChecked = $true }
}
elseif ($hostname -eq "ServerC") {
    @{ Label = 'A'; IsChecked = $true },
    @{ Label = 'B'; IsChecked = $true },
    @{ Label = 'C'; IsChecked = $false }
}
elseif ($hostname -eq "ServerD") {
    # Create a property (Hashtable) for each day of the week
    [Enum]::GetNames([DayOfWeek]) | ForEach-Object -Process {
        @{
            Label     = $_
            # Check the box if the day name has an odd number of vowels
            IsChecked = [Regex]::Matches($_, '[aeiou]').Count % 2 -eq 1
        }
    }
}
else {
    # Oops!  A host with no properties defined was selected...
}

Instead of using one array to store labels and another to store CheckBox states, we get one array of Hashtables based on which host has been selected and store it in $hostProperties.

Before we create the new CheckBoxes we must remove any existing ones from $HostPropertiesPanel and resize it based on the Length of the selected host's properties...

# Remove all existing CheckBoxes from the Panel
$HostPropertiesPanel.Controls.Clear()
# Resize the Panel to accommodate the new count of host properties
$HostPropertiesPanel.Size = New-Object -TypeName 'System.Drawing.Size' -Property @{
    Width  = 104
    Height = if ($hostProperties.Length -gt 0) {
        ($hostProperties.Length - 1) * 31 + 24
    }
    else {
        0
    }
}

Then we just need to tweak the code that creates and configures each CheckBox...

for ($index = 0; $index -lt $hostProperties.Length; $index++) {
    $CheckBox = New-Object System.Windows.Forms.CheckBox
    $CheckBox.UseVisualStyleBackColor = $True
    $System_Drawing_Size = New-Object System.Drawing.Size
    $System_Drawing_Size.Width = 104
    $System_Drawing_Size.Height = 24
    $CheckBox.Size = $System_Drawing_Size
    $CheckBox.TabIndex = 2

    $CheckBox.Text = $hostProperties[$index].Label
    $CheckBox.Checked = $hostProperties[$index].IsChecked

    $System_Drawing_Point = New-Object System.Drawing.Point
    $System_Drawing_Point.X = 0
    # Make sure to vertically space them dynamically, counter comes in handy
    $System_Drawing_Point.Y = $index * 31 #Controls location on Y axis
    $CheckBox.Location = $System_Drawing_Point
    $CheckBox.DataBindings.DefaultDataSourceUpdateMode = 0

    # Give it a unique name based on our counter
    $CheckBox.Name = "CheckBox$index"

    $HostPropertiesPanel.Controls.Add($CheckBox)
}

The key change is to retrieve the Text and Checked values for the new CheckBox using the current of $index in $hostProperties.

The final change is to update the list of host names...

$dropdown.Items.AddRange(
    @('ServerA', 'ServerB', 'ServerC', 'ServerD', 'ServerX')
)
$form1.Controls.Add($dropdown)

The full script then looks like this...

function GenerateForm {
    $PC = {
        $hostname = $dropdown.SelectedItem
        # Set $hostProperties to a Hashtable array for the corresponding value of $hostname
        # The IsChecked values are arbitrary for demonstration purposes
        #TODO: Replace if...elseif with a switch statement; see "help about_Switch"
        $hostProperties = if ($hostname -eq "ServerA") {
            @{ Label = 'Zero';  IsChecked = $false },
            @{ Label = 'One';   IsChecked = $true },
            @{ Label = 'Two';   IsChecked = $true },
            @{ Label = 'Three'; IsChecked = $false }
        }
        elseif ($hostname -eq "ServerB") {
            @{ Label = '0'; IsChecked = $true },
            @{ Label = '1'; IsChecked = $false },
            @{ Label = '2'; IsChecked = $false },
            @{ Label = '3'; IsChecked = $true }
        }
        elseif ($hostname -eq "ServerC") {
            @{ Label = 'A'; IsChecked = $true },
            @{ Label = 'B'; IsChecked = $true },
            @{ Label = 'C'; IsChecked = $false }
        }
        elseif ($hostname -eq "ServerD") {
            # Create a property (Hashtable) for each day of the week
            [Enum]::GetNames([DayOfWeek]) | ForEach-Object -Process {
                @{
                    Label     = $_
                    # Check the box if the day name has an odd number of vowels
                    IsChecked = [Regex]::Matches($_, '[aeiou]').Count % 2 -eq 1
                }
            }
        }
        else {
            # Oops!  A host with no properties defined was selected...
        }

        # Don't execute any layout logic until all changes are complete
        $HostPropertiesPanel.SuspendLayout()
        # Remove all existing CheckBoxes from the Panel
        $HostPropertiesPanel.Controls.Clear()
        # Resize the Panel to accommodate the new count of host properties
        $HostPropertiesPanel.Size = New-Object -TypeName 'System.Drawing.Size' -Property @{
            Width  = 104
            Height = if ($hostProperties.Length -gt 0) {
                ($hostProperties.Length - 1) * 31 + 24
            }
            else {
                0
            }
        }

        for ($index = 0; $index -lt $hostProperties.Length; $index++) {
            $CheckBox = New-Object System.Windows.Forms.CheckBox
            $CheckBox.UseVisualStyleBackColor = $True
            $System_Drawing_Size = New-Object System.Drawing.Size
            $System_Drawing_Size.Width = 104
            $System_Drawing_Size.Height = 24
            $CheckBox.Size = $System_Drawing_Size
            $CheckBox.TabIndex = 2

            $CheckBox.Text = $hostProperties[$index].Label
            $CheckBox.Checked = $hostProperties[$index].IsChecked

            $System_Drawing_Point = New-Object System.Drawing.Point
            $System_Drawing_Point.X = 0
            # Make sure to vertically space them dynamically, counter comes in handy
            $System_Drawing_Point.Y = $index * 31 #Controls location on Y axis
            $CheckBox.Location = $System_Drawing_Point
            $CheckBox.DataBindings.DefaultDataSourceUpdateMode = 0

            # Give it a unique name based on our counter
            $CheckBox.Name = "CheckBox$index"

            $HostPropertiesPanel.Controls.Add($CheckBox)
        }

        # All changes are complete, so resume layout logic
        $HostPropertiesPanel.ResumeLayout()
    }

    $form1 = New-Object System.Windows.Forms.Form
    $form1.Text = "UCCE Log Collector - Version 2.0"
    $form1.Name = "form1"
    $form1.DataBindings.DefaultDataSourceUpdateMode = 0
    $System_Drawing_Size = New-Object System.Drawing.Size
    $System_Drawing_Size.Width = 1150
    $System_Drawing_Size.Height = 500
    $form1.ClientSize = $System_Drawing_Size

    $dropdown = New-Object System.Windows.Forms.ListBox
    $dropdown.Location = New-Object System.Drawing.Point(10, 50)
    $dropdown.Size = New-Object System.Drawing.Size(100, 20)
    $dropdown.Height = 80

    $dropdown.Items.AddRange(
        @('ServerA', 'ServerB', 'ServerC', 'ServerD', 'ServerX')
    )
    $form1.Controls.Add($dropdown)

    ######### Select Server Button
    $SelectPC = New-Object System.Windows.Forms.Button
    $SelectPC.TabIndex = 4
    $SelectPC.Name = "SelectPC"
    $System_Drawing_Size = New-Object System.Drawing.Size
    $System_Drawing_Size.Width = 120
    $System_Drawing_Size.Height = 30
    $SelectPC.Size = $System_Drawing_Size
    $SelectPC.UseVisualStyleBackColor = $True
    $SelectPC.Text = "Select Server"
    $System_Drawing_Point = New-Object System.Drawing.Point
    $System_Drawing_Point.X = 0 # 0
    $System_Drawing_Point.Y = 150 #150
    $SelectPC.Location = $System_Drawing_Point
    $SelectPC.DataBindings.DefaultDataSourceUpdateMode = 0
    $SelectPC.add_Click($PC)

    $form1.Controls.Add($SelectPC)

    # Create a Panel to contain the dynamic collection of CheckBoxes
    $HostPropertiesPanel = New-Object -TypeName 'System.Windows.Forms.Panel' -Property @{
        # To illustrate the changing Size of the Panel
        BackColor = [System.Drawing.Color]::GreenYellow
        Location  = New-Object -TypeName 'System.Drawing.Point' -Property @{
            X = 27
            Y = 200
        }
        Name      = 'HostPropertiesPanel'
        Size      = [System.Drawing.Size]::Empty
    }
    $form1.Controls.Add($HostPropertiesPanel)

    $name = New-Object System.Windows.Forms.Label -Property @{
        Text      = "Start Time"
        Location  = "900, 220"
        ForeColor = "Black"
        Height    = 22
        Width     = 200
    }
    $form1.Controls.Add($name)

    $result = $form1.ShowDialog()
    $result
}
GenerateForm

An alternative approach, particularly for a large number of host properties, would be to replace the Panel and its contents with a CheckedListBox.

Sign up to request clarification or add additional context in comments.

5 Comments

Wow! Thank you Lance!! I would not have figured this out without your expertise.
You're welcome. I've edited my answer so that $PC sets $checkBox.Checked in a way that will be more straightforward when it comes time to deal with non-sample data.
Lance I just have one last question. The code always produces 4 check boxes. I need it to dynamically create check boxes based on how many values are in the Array "CheckBoxLabels". I can't seem to get it to update accordingly. Can you help with this please 🙏
I have updated my answer to work with a variable number of properties per host.
Thank you Lance! I really appreciate your help and response. You've helped me immensely.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.