Manual Checkbox enable - winforms

I have a ComboBox control on my form. What I want is when I change an item in the ComboBox from one to another, the event was handled. It is important that when changing and not when choosing the same element. All this time I used ComboBox.Add_SelectionChangeCommitted($function), but soon I realized that the block that is also executed by the handler when the same (selected) item is selected from the list. A little digging in ComboBox events I am completely confused. Having tried several events (SelectedItemChanged, SelectedIndexChanged) I could never able to achieve the desired result.
An example of what I want to do and what should not be done several times when choosing the same element. When the block of code for Manual is executed several times, the contents of all TextBox are cleared, but I don’t want to.
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
$Form = New-Object system.Windows.Forms.Form
$Form.ClientSize = '420,240'
$Form.TopMost = $false
$Form.FormBorderStyle = 'Fixed3D'
$Form.MaximizeBox = $false
$ComboBox = New-Object system.Windows.Forms.ComboBox
$ComboBox.width = 391
$ComboBox.height = 47
#('Automatic (DHCP)','Manual Input') | ForEach-Object {[void] $ComboBox.Items.Add($_)}
$ComboBox.location = New-Object System.Drawing.Point(13,57)
$ComboBox.DropDownStyle = 'DropDownList'
$ComboBox.DrawMode = 'OwnerDrawFixed'
$Button = New-Object system.Windows.Forms.Button
$Button.text = "Set"
$Button.width = 60
$Button.height = 30
$Button.visible = $false
$Button.enabled = $false
$Button.location = New-Object System.Drawing.Point(336,126)
$CheckBox = New-Object system.Windows.Forms.CheckBox
$CheckBox.width = 65
$CheckBox.height = 15
$CheckBox.visible = $false
$CheckBox.Checked = $false
$CheckBox.enabled = $true
$CheckBox.location = New-Object System.Drawing.Point(16,210)
$TextBox1 = New-Object system.Windows.Forms.TextBox
$TextBox1.Name = "TextBox1"
$TextBox1.multiline = $false
$TextBox1.width = 40
$TextBox1.height = 20
$TextBox1.visible = $false
$TextBox1.enabled = $true
$TextBox1.location = New-Object System.Drawing.Point(81,100)
$TextBox2 = New-Object system.Windows.Forms.TextBox
$TextBox2.Name = "TextBox2"
$TextBox2.multiline = $false
$TextBox2.width = 40
$TextBox2.height = 20
$TextBox2.visible = $false
$TextBox2.enabled = $false
$TextBox2.location = New-Object System.Drawing.Point(81,208)
$Form.controls.AddRange(#($TextBox1,$TextBox2,$Button,$ComboBox,$CheckBox))
$global:ManualChecked = $null
$global:AutomaticChecked = $null
$ComboBox.Add_SelectionChangeCommitted($methodSelection)
$netwValues = New-Object 'System.Collections.Hashtable'
$methodSelection =
{
switch($ComboBox.Text)
{
"Manual Input"
{
$CheckBox.Visible = $Button.Visible = $true
$Button.Enabled = $false
ForEach ($control in $Form.controls)
{
if ($control -is [System.Windows.Forms.TextBox] )
{
$control.Visible = $control.Enabled = $true
$control.Clear()
if($netwValues.Count -gt 0)
{
$control.Text = $netwValues.Item($control.Name)
$netwValues.Remove($control.Name)
}
}
}
if($global:ManualChecked -eq 1)
{
$CheckBox.Checked = $true
$TextBox2.Enabled = $true
}
else
{
$CheckBox.Checked = $false
$TextBox2.Enabled = $false
}
}
"Automatic (DHCP)"
{
ForEach($control in $Form.controls)
{
if($control -is [System.Windows.Forms.TextBox] -or $control -is [System.Windows.Forms.CheckBox] -or $control -is [System.Windows.Forms.Button])
{
$control.Visible = $control.Enabled = $true
if($control -is [System.Windows.Forms.Label] -or $control -is [System.Windows.Forms.TextBox])
{
$control.Enabled = $false
if($control -is [System.Windows.Forms.TextBox])
{
if($control.Text.Length)
{
$netwValues.Add($control.Name,$control.Text)
$control.Clear()
}
}
}
}
}
if($global:AutomaticChecked -eq 1)
{
$CheckBox.Checked = $true
}
else
{
$CheckBox.Checked = $false
}
}
}
}

You can implement this logic yourself.
# Simple class for using as item for combobox instead of just string.
class CbItem
{
CbItem([string] $value)
{
$this.Value = $value
}
[string] $Value
[string] ToString()
{
return $this.Value
}
}
# Add instances of this class into combobox.
$ComboBox.Items.Add([CbItem]::new('Automatic (DHCP)'))
$ComboBox.Items.Add([CbItem]::new('Manual Input'))
# Create a variable for storing current combobox's item.
$global:CurrentCbItem = $null
# Add logic at the begining of handler for exiting if selected item is the same as the last one.
$methodSelection =
{
if ([Object]::ReferenceEquals($global:CurrentCbItem, $ComboBox.SelectedItem))
{
return
}
$global:CurrentCbItem = $ComboBox.SelectedItem
...
}

Related

Refresh Array checkboxes with Powershell

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.
Choose ServerA from drop down
Select 'Select Server'
The following check boxes will list out on to the form in checkbox's:#('Zero','One','Two','Three')
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
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.

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?
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()

PowerShell WinForm: Enable button if one or more checkboxes are checked otherwise disable button

Should be pretty straight forward but cant work out the logic. Below is the code I currently have which sort of works but the only problem is if more than one checkboxes are checked and you uncheck one the button disables, I need the button to remain enabled as long as there is one or more checkbox checked. I've also tried various convoluted If and Elseif statements but nothing I've tried changes this behaviour.
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
$Form = New-Object system.Windows.Forms.Form
$Form.ClientSize = '170,191'
$Form.text = "Scheduler"
$Form.TopMost = $false
$MonCheckBox = New-Object system.Windows.Forms.CheckBox
$MonCheckBox.text = "Monday"
$MonCheckBox.AutoSize = $false
$MonCheckBox.width = 95
$MonCheckBox.height = 20
$MonCheckBox.location = New-Object System.Drawing.Point(30,30)
$MonCheckBox.Font = 'Microsoft Sans Serif,10'
$TueCheckBox = New-Object system.Windows.Forms.CheckBox
$TueCheckBox.text = "Tuesday"
$TueCheckBox.AutoSize = $false
$TueCheckBox.width = 95
$TueCheckBox.height = 20
$TueCheckBox.location = New-Object System.Drawing.Point(30,50)
$TueCheckBox.Font = 'Microsoft Sans Serif,10'
$WedCheckBox = New-Object system.Windows.Forms.CheckBox
$WedCheckBox.text = "Wednesday"
$WedCheckBox.AutoSize = $false
$WedCheckBox.width = 95
$WedCheckBox.height = 20
$WedCheckBox.location = New-Object System.Drawing.Point(30,70)
$WedCheckBox.Font = 'Microsoft Sans Serif,10'
$ThuCheckBox = New-Object system.Windows.Forms.CheckBox
$ThuCheckBox.text = "Thursday"
$ThuCheckBox.AutoSize = $false
$ThuCheckBox.width = 95
$ThuCheckBox.height = 20
$ThuCheckBox.location = New-Object System.Drawing.Point(30,90)
$ThuCheckBox.Font = 'Microsoft Sans Serif,10'
$FriCheckBox = New-Object system.Windows.Forms.CheckBox
$FriCheckBox.text = "Friday"
$FriCheckBox.AutoSize = $false
$FriCheckBox.width = 95
$FriCheckBox.height = 20
$FriCheckBox.location = New-Object System.Drawing.Point(30,110)
$FriCheckBox.Font = 'Microsoft Sans Serif,10'
$SchedButton = New-Object system.Windows.Forms.Button
$SchedButton.text = "Schedule"
$SchedButton.width = 60
$SchedButton.height = 30
$SchedButton.location = New-Object System.Drawing.Point(30,134)
$SchedButton.Font = 'Microsoft Sans Serif,10'
$SchedButton.Autosize = $true
$SchedButton.Enabled = $false
$Form.controls.AddRange(#($MonCheckBox,$TueCheckBox,$WedCheckBox,$ThuCheckBox,$FriCheckBox,$SchedButton))
$MonCheckBox.add_CheckedChanged({$SchedButton.Enabled = $MonCheckBox.Checked})
$TueCheckBox.add_CheckedChanged({$SchedButton.Enabled = $TueCheckBox.Checked})
$WedCheckBox.add_CheckedChanged({$SchedButton.Enabled = $WedCheckBox.Checked})
$ThuCheckBox.add_CheckedChanged({$SchedButton.Enabled = $ThuCheckBox.Checked})
$FriCheckBox.add_CheckedChanged({$SchedButton.Enabled = $FriCheckBox.Checked})
[void]$Form.ShowDialog()
I would do something like this:
$Form.controls.AddRange(#($MonCheckBox, $TueCheckBox, $WedCheckBox, $ThuCheckBox, $FriCheckBox, $SchedButton))
Function Test-AnyButtonChecked {
if (
$MonCheckBox.Checked -or
$TueCheckBox.Checked -or
$WedCheckBox.Checked -or
$ThuCheckBox.Checked -or
$FriCheckBox.Checked
) {
$SchedButton.Enabled = $true
}
else {
$SchedButton.Enabled = $false
}
}
$MonCheckBox.add_CheckedChanged( { Test-AnyButtonChecked })
$TueCheckBox.add_CheckedChanged( { Test-AnyButtonChecked })
$WedCheckBox.add_CheckedChanged( { Test-AnyButtonChecked })
$ThuCheckBox.add_CheckedChanged( { Test-AnyButtonChecked })
$FriCheckBox.add_CheckedChanged( { Test-AnyButtonChecked })
[void]$Form.ShowDialog()

Build and respond to events on multiple check boxes

I am attempting to make a form with multiple check boxes, based on the array passed to the form creation function. I can calculate the correct location based on the count of what checkbox I am at is, but I am having trouble (I think) dealing with events. I have this for now (partial code, obviously)
$checkboxCount = 1
foreach ($year in $years) {
$checkbox = new-object System.Windows.Forms.checkbox
$checkbox.Size = new-object System.Drawing.Size(100,20)
$checkbox.Location = new-object System.Drawing.Size(10,($checkbox.Size.Height*$checkboxCount-10))
$checkbox.Text = "Revit $year"
$checkbox.Checked = $true
$Form.Controls.Add($checkbox)
$checkbox.Add_CheckStateChanged({
$results.$year = $checkbox.Checked
})
$checkboxCount ++
}
and the check boxes are created correctly, but when I return $results from the function they are all True. I am basing the code off of this, which works but with a static number of check boxes.
function checkbox_test{
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
$results = #{
one = $true
two = $true
}
$optionCount = 2
# Set the size of your form
$Form = New-Object System.Windows.Forms.Form
$Form | Format-List *
$form.FormBorderStyle = 'FixedDialog'
$form.ShowInTaskbar = $false
$Form.width = 300
$Form.height = 150
$Form.Text = ”Px Tools Updater”
# Set the font of the text to be used within the form
$Font = New-Object System.Drawing.Font("Times New Roman",12)
$Form.Font = $Font
# create your checkbox
$checkbox1 = new-object System.Windows.Forms.checkbox
$checkbox1.Location = new-object System.Drawing.Size(10,7)
$checkbox1.Size = new-object System.Drawing.Size(100,20)
$checkbox1.Text = "One"
$checkbox1.Checked = $true
$Form.Controls.Add($checkbox1)
# create your checkbox
$checkbox2 = new-object System.Windows.Forms.checkbox
$checkbox2.Location = new-object System.Drawing.Size(10,27)
$checkbox2.Size = new-object System.Drawing.Size(100,20)
$checkbox2.Text = "Two"
$checkbox2.Checked = $true
$Form.Controls.Add($checkbox2)
# Add an OK button
$OKButton = new-object System.Windows.Forms.Button
$OKButton.Location = new-object System.Drawing.Size(10,70)
$OKButton.Size = new-object System.Drawing.Size(60,30)
$OKButton.Text = "OK"
$OKButton.Add_Click({$Form.Close()})
$form.Controls.Add($OKButton)
#Add a cancel button
$CancelButton = new-object System.Windows.Forms.Button
$CancelButton.Location = new-object System.Drawing.Size(225,100)
$CancelButton.Size = new-object System.Drawing.Size(60,30)
$CancelButton.Text = "Cancel"
$CancelButton.Margin = 0
$CancelButton.Add_Click({$Form.Close()})
$form.Controls.Add($CancelButton)
$checkbox1.Add_CheckStateChanged({
$results.one = $checkbox1.Checked
})
$checkbox2.Add_CheckStateChanged({
$results.two = $checkbox2.Checked
})
# Activate the form
$Form.Add_Shown({$Form.Activate()})
[void] $Form.ShowDialog()
$results
}
I am not sure if I am going wrong with the way I am referencing the results hash table, or maybe the entire approach is wrong?
Edit: I had a thought, that $year is meaningless in the event handler, so I added
$checkbox.Name = $year
and revised the event handler to
$results.($checkbox.Name) = $checkbox.Checked
and
$results.($Self.Name) = $checkbox.Checked
But no joy with either. But what is weird is that using $self results in an odd extra key being added to $return. It has no key name, but the value matches the last change made to any checkbox.
EDIT #2: In further testing, I changed the handler to
$results.2019 = $checkbox.Checked
expecting that to mean any change results in that change applied to the 2019 key. not so. So I am thinking it relates to the way hash tables are passed and referenced and likely I am doing this all wrong. Perhaps worrisome is the fact that I can find tons of information on making check boxes react to and change other parts of the form, but so far nothing on just getting results back.
EDIT #3: OK, seems the answer (of sorts) is there is no need for event handlers, because I only really care about end state anyway. So, with some extra cleanup to also handle Cancel I have this, and it works. Still curious how, or if, I could interact directly with $results from an event handler though.
function checkbox_test{
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
$years = #('2016', '2017', '2018', '2019')
$optionCount = $years.Count
# Set the size of your form
$Form = New-Object System.Windows.Forms.Form
$form.FormBorderStyle = 'FixedDialog'
$form.ShowInTaskbar = $false
$Form.width = 300
$Form.height = ($years.Count * 30 + 50 + 40) #150
$Form.Text = ”Px Tools Updater”
# Set the font of the text to be used within the form
$Font = New-Object System.Drawing.Font("Times New Roman",12)
$Form.Font = $Font
# create Checkboxes
$checkboxCount = 1
foreach ($year in $years) {
$checkbox = new-object System.Windows.Forms.checkbox
$checkbox.Size = new-object System.Drawing.Size(100,20)
$checkbox.Location = new-object System.Drawing.Size(10,($checkbox.Size.Height*$checkboxCount-10))
$checkbox.Text = "Revit $year"
$checkbox.Name = $year
$checkbox.Checked = $true
$Form.Controls.Add($checkbox)
$checkboxCount ++
}
# Add an OK button
$OKButton = new-object System.Windows.Forms.Button
$OKButton.Size = new-object System.Drawing.Size(60,30)
$OKButton.Location = new-object System.Drawing.Size(10,($form.DisplayRectangle.Height - $OKButton.Size.Height - 10))
$OKButton.Text = "OK"
$OKButton.Add_Click({
$Form.DialogResult = [System.Windows.Forms.DialogResult]::OK
$Form.Close()
})
$form.Controls.Add($OKButton)
#Add a cancel button
$CancelButton = new-object System.Windows.Forms.Button
$CancelButton.Size = new-object System.Drawing.Size(60,30)
$CancelButton.Location = new-object System.Drawing.Size(($form.DisplayRectangle.Width - $CancelButton.Size.Width - 10),($form.DisplayRectangle.Height - $CancelButton.Size.Height - 10))
$CancelButton.Text = "Cancel"
$CancelButton.Add_Click({
$Form.Close()
})
$form.Controls.Add($CancelButton)
# Activate the form
$Form.Add_Shown({$Form.Activate()})
if ($Form.ShowDialog() -eq 'OK') {
$results = New-Object Collections.Specialized.OrderedDictionary
foreach ($control in $form.Controls) {
if ($years -contains $control.Name) {
$results.Add($control.Name, $control.Checked)
}
}
} else {
$results = $null
}
[void] $Form.Dispose
$results
}
#Call the function
$returned = checkbox_test
Foreach ($key in $returned.keys) {
Write-Host "[$key] $($returned.$key)!"
}
The foreach loop used to assign each checkbox event handler above effectively overwrites the previous one, and therefore you only capture the last one. (2019)
Instead, assign the event hander the way it's traditionally done in Windows Forms:
$results = New-Object Collections.Specialized.OrderedDictionary;
foreach ($year in $years) {
$checkbox = new-object System.Windows.Forms.checkbox
$Form.Controls.Add($checkbox);
$checkbox.Size = new-object System.Drawing.Size(100,20)
$checkbox.Location = new-object System.Drawing.Size(10,($checkbox.Size.Height*$checkboxCount-10))
$checkbox.Text = "Revit $year"
$checkbox.Name = $year
$checkbox.Checked = $true
$results.Add($year, $checkbox.Checked);
# HERE!
$checkbox.Add_CheckStateChanged({
# $this -eq sender, optionally $_ -eq EventArgs
$year = $this.name;
$results.$year = $this.Checked;
});
$checkboxCount++
}

How Do I Access CheckBoxes From a Function

I've created a form and dynamically added CheckBoxes and CheckBox names. How can I programmatically check one particular CheckBox from the Get-LicenseDetails function? Missing bracket has been added.
import-module MSOnline
Function Get-LicenseDetails {
Param ($upn)
$licenses = Get-MsolUser -UserPrincipalName $upn
ForEach ($license in $licenses.Licenses) {
If ($license.AccountSkuId -like '*ENTERPRISEPACK') {
$serviceName = $serviceStatus.ServicePlan.ServiceName
$checkBox.Checked = $true
}
}
}
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$System_Drawing_Point = New-Object System.Drawing.Point
Add-Type -AssemblyName System.Windows.Forms
$form = New-Object Windows.Forms.Form
$form.Text = "Office 365 Licensing"
$form.Name = "Form1"
$form.Size = New-Object Drawing.Size #(316, 510)
#SEARCH BUTTON
$searchBtn = New-Object System.Windows.Forms.Button
$System_Drawing_Point.X = 226
$System_Drawing_Point.Y = 38
$searchBtn.Location = $System_Drawing_Point
$searchBtn.add_click({Get-LicenseDetails "user.name#domain.com"})
$searchBtn.Size = New-Object System.Drawing.Size(67, 23)
$searchBtn.Text = "Click Me"
$form.Controls.Add($searchBtn)
#CHECKBOXES
$y = 80
$Services = (Get-MsolAccountSku | Where-Object {$_.SkuPartNumber -eq "ENTERPRISEPACK"}).ServiceStatus.ServicePlan.ServiceName
ForEach ($service in $Services) {
$checkbox = New-Object System.Windows.Forms.CheckBox
$checkbox.Text = $service
$checkbox.Name = "CheckBox_$service"
$checkbox.Size = New-Object System.Drawing.Size(260,17)
$checkbox.Location = New-Object System.Drawing.Size(10,$y)
$y += 25
$form.Controls.Add($checkbox)
}
$drc = $form.ShowDialog()
First of all, you are missing a square Bracket where you load the System.Drawing Assembly.
You could access the CheckBox in the Get-LicenseDetails bei either passing them to the function or by accessing them using $global:. However, I wouldn't pass GUI elements to that function. Instead I would create a model (new object) and pass that to the Get-LicenseDetails method.
OK, I've figured it out. I used these lines in the function:
$checkBoxName = "CheckBox_" + $serviceName
$checkbox = $form.Controls | Where-Object {$_.name -eq $checkBoxName}
$checkbox.Checked = $true

Resources