How to update GUI when it's running - winforms

I have a script that runs a Windows form and a textbox in it.
My question is: when the GUI is running and presented to the user, I change the content of the textbox, how can I refresh the GUI and see the changes?
$formText = New-Object 'System.Windows.Forms.Form'
$richtextbox1 = New-Object 'System.Windows.Forms.RichTextBox'
$buttonFind = New-Object 'System.Windows.Forms.Button'
$textboxFind = New-Object 'System.Windows.Forms.TextBox'
$buttonCopy = New-Object 'System.Windows.Forms.Button'
$buttonExit = New-Object 'System.Windows.Forms.Button'
$InitialFormWindowState = New-Object 'System.Windows.Forms.FormWindowState'
#endregion Generated Form Objects
#region FindFunction
function FindText
{
if ($textboxFind.Text.Length -eq 0)
{
return
}
$index = $richtextbox1.Find($textboxFind.Text,$richtextbox1.SelectionStart+ $richtextbox1.SelectedText.Length,[System.Windows.Forms.RichTextBoxFinds]::None)
if ($index -ge 0)
{
$richtextbox1.Select($index,$textboxFind.Text.Length)
$richtextbox1.ScrollToCaret()
#$richtextbox1.Focus()
}
else
{
$index = $richtextbox1.Find($textboxFind.Text,0,$richtextbox1.SelectionStart,[System.Windows.Forms.RichTextBoxFinds]::None)
#
if ($index -ge 0)
{
$richtextbox1.Select($index,$textboxFind.Text.Length)
$richtextbox1.ScrollToCaret()
#$richtextbox1.Focus()
}
else
{
$richtextbox1.SelectionStart = 0
}
}
}
#endregion
$formText_Load = {
#TODO: Initialize Form Controls here
}
$buttonExit_Click = {
#TODO: Place custom script here
$formText.Close()
}
$buttonLoad_Click = {
Load-Text
}
$buttonCopy_Click = {
#The following requires STA mode
# if ($textbox1.Text.Length -gt 0)
# {
# [System.Windows.Forms.Clipboard]::SetText($textbox1.Text)
# }
#Alternative - Does not require STA
$richtextbox1.SelectAll() #Select all the text
$richtextbox1.Copy() #Copy selected text to clipboard
$richtextbox1.Select(0,0); #Unselect all the text
}
$textboxFind_TextChanged = {
$buttonFind.Enabled = $textboxFind.Text.Length -gt 0
}
$buttonFind_Click = {
#TODO: Place custom script here
FindText
}
#################################################
# Customize LoadText Function
#################################################
$Form_StateCorrection_Load = {
#Correct the initial state of the form to prevent the .Net maximized form issue
$formText.WindowState = $InitialFormWindowState
}
$Form_StoreValues_Closing = {
#Store the control values
$script:MainForm_richtextbox1 = $richtextbox1.Text
$script:MainForm_textboxFind = $textboxFind.Text
}
$Form_Cleanup_FormClosed = {
#Remove all event handlers from the controls
try
{
$buttonFind.remove_Click($buttonFind_Click)
$textboxFind.remove_TextChanged($textboxFind_TextChanged)
$buttonCopy.remove_Click($buttonCopy_Click)
$buttonExit.remove_Click($buttonExit_Click)
$formText.remove_Load($formText_Load)
$formText.remove_Load($Form_StateCorrection_Load)
$formText.remove_Closing($Form_StoreValues_Closing)
$formText.remove_FormClosed($Form_Cleanup_FormClosed)
}
catch { Out-Null <# Prevent PSScriptAnalyzer warning #> }
}
$formText.Controls.Add($richtextbox1)
$formText.Controls.Add($buttonFind)
$formText.Controls.Add($textboxFind)
$formText.Controls.Add($buttonCopy)
$formText.Controls.Add($buttonExit)
$formText.AcceptButton = $buttonFind
$formText.AutoScaleDimensions = '10, 20'
$formText.AutoScaleMode = 'Font'
$formText.ClientSize = '828, 594'
$formText.Margin = '8, 8, 8, 8'
$formText.Name = 'formText'
$formText.StartPosition = 'CenterScreen'
$formText.Text = 'Wake On Lan'
$formText.add_Load($formText_Load)
$formtext.TopMost = $True
$richtextbox1.Anchor = 'Top, Bottom, Left, Right'
$richtextbox1.BackColor = 'Window'
$richtextbox1.Font = 'Courier New, 12pt'
$richtextbox1.HideSelection = $False
$richtextbox1.Location = '20, 55'
$richtextbox1.Margin = '5, 5, 5, 5'
$richtextbox1.Name = 'richtextbox1'
$richtextbox1.ReadOnly = $True
$richtextbox1.Size = '784, 467'
$richtextbox1.TabIndex = 6
$richtextbox1.WordWrap = $False
#
# buttonFind
#
$buttonFind.Anchor = 'Top, Right'
$buttonFind.Enabled = $False
$buttonFind.Location = '748, 1'
$buttonFind.Margin = '5, 5, 5, 5'
$buttonFind.Name = 'buttonFind'
$buttonFind.Size = '60, 35'
$buttonFind.TabIndex = 5
$buttonFind.Text = '&Find'
$buttonFind.UseVisualStyleBackColor = $True
$buttonFind.add_Click($buttonFind_Click)
#
# textboxFind
#
$textboxFind.Anchor = 'Top, Right'
$textboxFind.Location = '420, 15'
$textboxFind.Margin = '8, 8, 8, 8'
$textboxFind.Name = 'textboxFind'
$textboxFind.Size = '316, 26'
$textboxFind.TabIndex = 4
$textboxFind.add_TextChanged($textboxFind_TextChanged)
#
# buttonCopy
#
$buttonCopy.Anchor = 'Bottom'
$buttonCopy.Location = '20, 540'
$buttonCopy.Margin = '5, 5, 5, 5'
$buttonCopy.Name = 'buttonCopy'
$buttonCopy.Size = '125, 35'
$buttonCopy.TabIndex = 3
$buttonCopy.Text = '&Copy'
$buttonCopy.UseVisualStyleBackColor = $True
$buttonCopy.add_Click($buttonCopy_Click)
#
# buttonExit
#
$buttonExit.Anchor = 'Bottom, Right'
$buttonExit.Location = '683, 540'
$buttonExit.Margin = '5, 5, 5, 5'
$buttonExit.Name = 'buttonExit'
$buttonExit.Size = '125, 35'
$buttonExit.TabIndex = 2
$buttonExit.Text = 'E&xit'
$buttonExit.UseVisualStyleBackColor = $True
$buttonExit.add_Click($buttonExit_Click)
$formText.ResumeLayout()
#endregion Generated Form Code
$Results = "1","2"
#---------------------------------------------
$richtextbox1.Text = foreach ($Result in $Results) {
($Result+[Environment]::NewLine)
}
#Save the initial state of the form
$InitialFormWindowState = $formText.WindowState
#Init the OnLoad event to correct the initial state of the form
$formText.add_Load($Form_StateCorrection_Load)
#Clean up the control events
$formText.add_FormClosed($Form_Cleanup_FormClosed)
#Store the control values when form is closing
$formText.add_Closing($Form_StoreValues_Closing)
#Show the Form
$formText.ShowDialog()

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.

Powershell. Accessing variable outside a Runspace instance

I wrote a small application using WPF and Runspaces and ran into some difficulties.
$Runspace = [runspacefactory]::CreateRunspace()
$Runspace.ApartmentState = "STA"
$Runspace.ThreadOptions = "ReuseThread"
$Runspace.Open()
$code = {
#Build the GUI
[xml]$xaml = #"
$xaml_code
"#
$syncHash = [hashtable]::Synchronized(#{ })
$reader = (New-Object System.Xml.XmlNodeReader $xaml)
$syncHash.Window = [Windows.Markup.XamlReader]::Load($reader)
function RunspaceScript {
param($syncHash, $Servers, $TargetBox)
$syncHash.Host = $host
$Runspace = [runspacefactory]::CreateRunspace()
$Runspace.ApartmentState = "STA"
$Runspace.ThreadOptions = "ReuseThread"
$Runspace.Open()
$Runspace.SessionStateProxy.SetVariable("syncHash", $syncHash)
$Runspace.SessionStateProxy.SetVariable("Servers", $Servers)
$Runspace.SessionStateProxy.SetVariable("TargetBox", $TargetBox)
$code = {
Function Add-OutputBoxLine {
Param ([string]$Message,[string]$Color = "Black")
$syncHash.Window.Dispatcher.invoke(
[action]{
$RichTextRange = New-Object System.Windows.Documents.TextRange($syncHash.$TargetBox.Document.ContentEnd, $syncHash.$TargetBox.Document.ContentEnd)
$RichTextRange.Text = "$Message`r`n"
$RichTextRange.ApplyPropertyValue([System.Windows.Documents.TextElement]::ForegroundProperty, "$Color")
$syncHash.$TargetBox.ScrollToCaret()
}
)
}
$syncHash.Window.Dispatcher.invoke(
[action]{ $syncHash.$TargetBox.Clear(); $syncHash.Start_Switchover.IsEnabled = $False })
if (!$az_connection) {
$az_connection = Connect-AzAccount
}
}
$PSinstance = [powershell]::Create().AddScript($Code)
$PSinstance.Runspace = $Runspace
$PSinstance.Runspace.Name = "SwitchOver"
$job = $PSinstance.BeginInvoke()
}
# Click Actions
$syncHash.Start_Switchover.Add_Click(
{
RunspaceScript -syncHash $syncHash -Servers $syncHash.Servers.Text -TargetBox "Process_Output"
})
$syncHash.Window.ShowDialog()
$Runspace.Close()
$Runspace.Dispose()
}
$PSinstance1 = [powershell]::Create().AddScript($Code)
$PSinstance1.Runspace = $Runspace
$job = $PSinstance1.BeginInvoke()
I want to access variable $az_connection which is inside a runspace which is inside a function "RunspaceScript" from previous execution of that function.
Any ideas how it can be achieved?
P.S. I was forced to remove some lines from the script code here because of the rules, don't try to copy and run the code, it will not work.
Probably the easiest way is to add it to $syncHash - $syncHash.AzConnection = $az_connection. Otherwise you need to return it as output from your scriptblock and then pull the output once the AsyncResult completes.
function RunspaceScript {
param($syncHash, $Servers, $TargetBox)
$syncHash.Host = $host
$Runspace = [runspacefactory]::CreateRunspace()
$Runspace.ApartmentState = 'STA'
$Runspace.ThreadOptions = 'ReuseThread'
$Runspace.Open()
$Runspace.SessionStateProxy.SetVariable('syncHash', $syncHash)
$Runspace.SessionStateProxy.SetVariable('Servers', $Servers)
$Runspace.SessionStateProxy.SetVariable('TargetBox', $TargetBox)
$code = {
Function Add-OutputBoxLine {
Param ([string]$Message, [string]$Color = 'Black')
$syncHash.Window.Dispatcher.invoke(
[action] {
$RichTextRange = New-Object System.Windows.Documents.TextRange($syncHash.$TargetBox.Document.ContentEnd, $syncHash.$TargetBox.Document.ContentEnd)
$RichTextRange.Text = "$Message`r`n"
$RichTextRange.ApplyPropertyValue([System.Windows.Documents.TextElement]::ForegroundProperty, "$Color")
$syncHash.$TargetBox.ScrollToCaret()
}
)
}
$syncHash.Window.Dispatcher.invoke(
[action] { $syncHash.$TargetBox.Clear(); $syncHash.Start_Switchover.IsEnabled = $False })
if (!$az_connection) {
$az_connection = Connect-AzAccount
}
## Output the connection from your scriptblock ##
$az_connection
## OR just add it to your synchronized hashtable ##
$syncHash.AzConnection = $az_connection
}
$PSinstance = [powershell]::Create().AddScript($Code)
$PSinstance.Runspace = $Runspace
$PSinstance.Runspace.Name = 'SwitchOver'
$job = $PSinstance.BeginInvoke()
## Wait for the $code scriptblock to finish ##
While (-not $job.IsCompleted) {
Start-Sleep -Seconds 1
}
## Pass the job to EndInvoke() to receive the output ##
$az_connection = $PSinstance.EndInvoke($job)
## Or access it from your synchronized hashtable ##
$syncHash.AzConnection
}

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

Manual Checkbox enable

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
...
}

Trying to set properties on checkboxes in a for loop

I've been trying to find this by myself but I'm not entirely sure even how to phrase the question. Essentially what I'm doing here is taking an array of Names, and from that creating a checkbox for each name. Edit: I thought the code snippet below was working too but it looks like this might not be actually working?
New-object System.Windows.Forms.CheckBox | New-Variable -Name ("checkbox" + $_)
The main problem I'm having is this: How do I actually set the properties on these variable named checkboxes once they've been created? Essentially how to get the variable name for each iteration, then do the $checkbox.margin and such settings. I've posted my entire script below, it's very WIP and I know there's some other stuff that's weird but I'm planning to sort that out once I can fix this.
$form = New-Object System.Windows.Forms.Form
$flowlayoutpanel = New-Object System.Windows.Forms.FlowLayoutPanel
$buttonOK = New-Object System.Windows.Forms.Button
$usernames = "andrew", "beth", "charlie", "dave", "james", "george"
$totalvalues = ($usernames.count)
$formsize = 85 + (30 * $totalvalues)
$flowlayoutsize = 10 + (30 * $totalvalues)
$buttonplacement = 40 + (30 * $totalvalues)
$form_Load = {
0..$totalvalues|%{
New-object System.Windows.Forms.CheckBox | New-Variable -Name ("checkbox" + $_)
('$checkbox' + $_).Margin = '10, 8, 0, 0'
$checkbox.Margin = '10, 8, 0, 0'
$checkbox.Name = 'checkbox' + $_
$checkbox.Size = '200, 22'
$checkbox.Text = "" + $usernames[$_]
$checkbox.TextAlign = 'MiddleLeft'
$flowlayoutpanel.Controls.Add($checkbox)
}
}
$form.Controls.Add($flowlayoutpanel)
$form.Controls.Add($buttonOK)
$form.AcceptButton = $buttonOK
$form.AutoScaleDimensions = '8, 17'
$form.AutoScaleMode = 'Font'
$form.ClientSize = "500 , $formsize"
$form.FormBorderStyle = 'FixedDialog'
$form.Margin = '5, 5, 5, 5'
$form.MaximizeBox = $False
$form.MinimizeBox = $False
$form.Name = 'form1'
$form.StartPosition = 'CenterScreen'
$form.Text = 'Form'
$form.add_Load($form_Load)
$flowlayoutpanel.BorderStyle = 'FixedSingle'
$flowlayoutpanel.Location = '48, 13'
$flowlayoutpanel.Margin = '4, 4, 4, 4'
$flowlayoutpanel.Name = 'flowlayoutpanel1'
$flowlayoutpanel.Size = "400, $flowlayoutsize"
$flowlayoutpanel.TabIndex = 1
$buttonOK.Anchor = 'Bottom, Right'
$buttonOK.DialogResult = 'OK'
$buttonOK.Location = "383, $buttonplacement"
$buttonOK.Margin = '4, 4, 4, 4'
$buttonOK.Name = 'buttonOK'
$buttonOK.Size = '100, 30'
$buttonOK.TabIndex = 0
$buttonOK.Text = '&OK'
$form.ShowDialog()
Replace your foreacheach loop with this code
$form_Load = {
foreach($user in $usernames){
$DynamicCheckBox = New-Variable -Name ("checkbox" + $user)
$DynamicCheckBox = New-object System.Windows.Forms.CheckBox
$DynamicCheckBox.Margin = '10, 8, 0, 0'
$DynamicCheckBox.Name = 'checkbox' + $_
$DynamicCheckBox.Size = '200, 22'
$DynamicCheckBox.Text = "" + $user
$DynamicCheckBox.TextAlign = 'MiddleLeft'
$flowlayoutpanel.Controls.Add($DynamicCheckBox)
}
}

Resources