PowerShell Version 4.0 (.PS1 File) - Add Text from TextBox into an Array to be printed on the console - arrays

Please Note this is PowerShell 4.0. I don't think Convert-String is available to , but granted, answers for other versions of PowerShell are still worth the read.
Alright. I'm very new to PowerShell, but interestingly enough both through Google and StackOverFlow I haven't been able to find the answer to this; I'm just not sure where to go yet.
Please keep in mind that i want to launch the file example.ps1 from the PowerShell prompt.
Function Show_Form
{
Add-Type -AssemblyName System.Windows.Forms
$global:Y_Position = 10
$global:X_Position = 5
$Form = New-Object system.Windows.Forms.Form
$Form.Text = "Sample Form"
$Form.Width = 400
$Form.Height = 450
$Form.AutoScroll = $True
$Form.AutoSize = $True
$Form.AutoSizeMode = "GrowOnly"
$Form.MinimizeBox = $False
$Form.MaximizeBox = $False
$Label = New-Object System.Windows.Forms.Label
$Label.Text = "Please Fill in the Required Information:`n"
$Label.AutoSize = $True
$Form.Controls.Add($Label)
$LabelTextBox = New-Object System.Windows.Forms.Label
$LabelTextBox.Location = New-Object System.Drawing.Point(10,40)
$LabelTextBox.Text = $Text
$LabelTextBox.AutoSize = $True
$Form.Controls.Add($LabelTextBox)
$global:textBox = New-Object System.Windows.Forms.TextBox
$X_Position = $X_Position+90
$textBox.Location = New-Object System.Drawing.Point(10,40)
$textBox.Size = New-Object System.Drawing.Size(260,20)
$Form.Controls.Add($textBox)
$Okbutton = New-Object System.Windows.Forms.Button
$Okbutton.Location = New-Object System.Drawing.Size(250,570)
$Okbutton.Size = New-Object System.Drawing.Size(100,30)
$Okbutton.Text = "OK"
<#-----------------------------------#>
$global:Arr = #("lll", "ppp", 2)
$OkButton.Add_Click({[System.Windows.Forms.MessageBox]::Show($textBox.text, "My Dialog Box")})
$OkButton.Add_Click({Write-Host $textBox.text})
$global:Arr[0] = $textBox.text
$OkButton.Add_Click({Write-Host $Arr})
<#-----------------------------------#>
$Okbutton.Add_Click({$Form.Close()})
$Form.Controls.Add($Okbutton)
$Form.ShowDialog()
}
Show_Form
My problem is with these lines:
$global:Arr = #("lll", "ppp", 2)
$OkButton.Add_Click({[System.Windows.Forms.MessageBox]::Show($textBox.text, "My Dialog Box")})
$OkButton.Add_Click({Write-Host $textBox.text})
$global:Arr[0] = $textBox.text
$OkButton.Add_Click({Write-Host $Arr})
The Add_Click({Write-Host $textBox.text}) displays in the Console but when I add $textBox.text to an array (object array or otherwise) or replace another element with it then print the Array, it is always just blank.
.
And if I do the following (Note: Arr[0] AND Arr[1])
$global:Arr = #("lll", "ppp", 2)
$OkButton.Add_Click({[System.Windows.Forms.MessageBox]::Show($textBox.text, "My Dialog Box")})
$OkButton.Add_Click({Write-Host $textBox.text})
$global:Arr[0] = $textBox.text
$OkButton.Add_Click({Write-Host $Arr})
$global:Arr[1] = "$textBox.text"
$OkButton.Add_Click({Write-Host $Arr})
Both elements get written as the value System.Windows.Forms.TextBox, Text: .text
I'm wondering how to make $textBox.text display in the Console in the array?

The problem you face is simple.
You assumed that $global:Arr[1] = "$textBox.text" would be set the actual text value from when you click the Ok button in your form.
But actually, this section of code is called before $Form.ShowDialog(). Therefore, at that point in time, your textbox is indeed empty, which explains what you get.
If you want your global array to be set to the textbox text after the Ok button is pressed, you need to set the array value when the user click the button through the Add_Click event.
Like this:
$OkButton.Add_Click( { $global:Arr[0] = $textBox.text })
Additional note
If you're not familiar already with how debugging work in Powershell, I'd recommend you to look at some debugging tutorials.
You would have immediately seen that the script did go to your variable assignment before the form is even shown, which would have possibly clued you in on the situation.
--
You can see below a debug point on the assignment line. The execution is stopped before I had a chance to see or enter anything in the form, which indicates that the value clearly can't be the one from the form. Querying $textBox.text at that point from the console shows the value to be empty, as we would expect from a newly initialized form.
Some documentation: how to debug scripts (even though it says ISE, all of this should be good with VSCode too)

Related

How Can I change the color of the Text on TabPage?

What properties need to be applied to change the forecolor and backcolor of the text on Tabpage?
See Picture:
https://imgur.com/a/Su8aSg7
Here is my Code:
$TabControl_Main = New-Object System.Windows.Forms.TabControl
$TabControl_Main.Location = New-Object System.Drawing.Size(20,550)
$TabControl_Main.Size = New-Object System.Drawing.Size(850,270)
$form_MainForm.Controls.Add($TabControl_Main)
$TabPage1 = New-Object System.Windows.Forms.TabPage
$TabPage1.Location = New-Object System.Drawing.Size(20,550)
$TabPage1.Size = New-Object System.Drawing.Size(850,270)
$TabPage1.Text = "Processes"
$TabControl_Main.Controls.Add($TabPage1)
You have to create an event and draw the area. Here is some code based on this example in c#, credits #Fun Mun Pieng.
# assign a color for each tab
$PageColor = #{0 = "lightgreen";
1 = "yellow";
2 = "lightblue"}
# define the event
$tabControl_Drawing = {
param([object]$Sender, [System.EventArgs]$e)
$Background = new-object Drawing.SolidBrush $PageColor[$e.Index]
$Foreground = new-object Drawing.SolidBrush black
$tabGraphics = $e.Graphics
$tabBounds = $e.Bounds
$tabGraphics.FillRectangle($Background,$tabBounds)
$tabTextSize = $tabGraphics.MeasureString($sender.TabPages[$e.Index].text, $e.Font)
$tabGraphics.DrawString($Sender.TabPages[$e.Index].Text,$e.Font,$Foreground,$tabBounds.Left + ($tabBounds.Width - $tabTextSize.Width) / 2,$tabBounds.Top + ($tabBounds.Height -$tabTextSize.Height) / 2 +1)
$e.DrawFocusRectangle()
}
# add the event
$TabControl_Main.add_DrawItem($tabControl_Drawing)
A little easier to use is HotTrack:
$TabControl_Main.HotTrack = $true
You will see the effect when you execute your script with powershell instead of powershell ISE.
BackColor does nothing. To use the words of MSDN:
BackColor > This member is not meaningful for this control.
edit: added the code.

Powershell Windows Form listbox selection validation

I have created a powershell script using windows forms containing list boxes to run a cmd command (helps eliminate user-error and expedites process a bit). The only problem I run into is the command will still attempt to run even if an item is not selected on one of the forms I have populate. This can cause the script to simply not run, or it can cause mass amounts of data to be downloaded (the script pulls log files from a server, the listbox helps narrow down the data to be pulled). Is there a way to create error checking for the listbox that will essentially say "hey, you didn't select anything!" before continuing?
Thanks!
Edit (example of first list box):
$form = New-Object System.Windows.Forms.Form
$form.Text = "Select a production environment"
$form.Size = New-Object System.Drawing.Size(190,250)
$form.StartPosition = "CenterScreen"
$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Point(10,180)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "OK"
$OKButton.DialogResult = [System.Windows.Forms.DialogResult]::OK
$form.AcceptButton = $OKButton
$form.Controls.Add($OKButton)
$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Point(85,180)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = "Cancel"
$CancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$form.CancelButton = $CancelButton
$form.Controls.Add($CancelButton)
$label = New-Object System.Windows.Forms.Label
$label.Location = New-Object System.Drawing.Point(10,20)
$label.Size = New-Object System.Drawing.Size(280,20)
$label.Text = "Please select a production environment:"
$form.Controls.Add($label)
$listBox = New-Object System.Windows.Forms.ListBox
$listBox.Location = New-Object System.Drawing.Point(10,40)
$listBox.Size = New-Object System.Drawing.Size(150,20)
$listBox.Height = 140
[void] $listBox.Items.Add("server1")
[void] $listBox.Items.Add("server2")
[void] $listBox.Items.Add("server3")
$form.Controls.Add($listBox)
$form.Topmost = $True
$result = $form.ShowDialog()
if ($result -eq [System.Windows.Forms.DialogResult]::OK)
{
$Prod = $listBox.SelectedItem
$Prod
}
There might be a way to perform this kind of validation using Windows Forms methods, but this uses PowerShell.
Perform $result = $form.ShowDialog() in a Do..Until or Do..While loop and continue showing the dialogue until the user has selected at least one item, and pressed OK - or the user exits the dialogue - else, show a warning and show the dialogue again.
Here is the relevant PowerShell code:
do
{
$result = $form.ShowDialog()
if ($ListBox.SelectedIndices.Count -lt 1 -and $result -eq [System.Windows.Forms.DialogResult]::OK)
{
Write-Warning 'Nothing was selected, please select a server.'
}
}
until (($result -eq [System.Windows.Forms.DialogResult]::OK -and $listBox.SelectedIndices.Count -ge 1) -or $result -ne [System.Windows.Forms.DialogResult]::OK)
Of course, you can replace Write-Warning with whatever you want, such as a message box.
PS: You can use the AddRange method to add an array of items to the $listBox collection, like so: [void] $listBox.Items.AddRange(#("server1", "server2", "server3"))

In PowerShell and Windows Forms, how to capture user-entered data using multiple dynamically created input controls

I have a script I need to use for multiple parameter data collection, as follows:
function Build-FormPanel($FormTitle){
Add-Type -Assembly System.Windows.Forms ## Load the Windows Forms assembly
## Create the main form
$form = New-Object Windows.Forms.Form
$form.FormBorderStyle = "FixedToolWindow"
$form.Text = $FormTitle
$form.AutoScroll = $True
$form.StartPosition = "CenterScreen"
$form.Width = 740 ; $form.Height = 480 # Make the form wider
#Add Buttons- ## Create the button panel to hold the OK and Cancel buttons
$buttonPanel = New-Object Windows.Forms.Panel
$buttonPanel.Size = New-Object Drawing.Size #(400,40)
$buttonPanel.Dock = "Bottom"
$cancelButton = New-Object Windows.Forms.Button
$cancelButton.Top = $buttonPanel.Height - $cancelButton.Height - 10; $cancelButton.Left = $buttonPanel.Width - $cancelButton.Width - 10
$cancelButton.Text = "Cancel"
$cancelButton.DialogResult = "Cancel"
$cancelButton.Anchor = "Right"
## Create the OK button, which will anchor to the left of Cancel
$okButton = New-Object Windows.Forms.Button
$okButton.Top = $cancelButton.Top ; $okButton.Left = $cancelButton.Left - $okButton.Width - 5
$okButton.Text = "Ok"
$okButton.DialogResult = "Ok"
$okButton.Anchor = "Right"
## Add the buttons to the button panel
$buttonPanel.Controls.Add($okButton)
$buttonPanel.Controls.Add($cancelButton)
## Add the button panel to the form
$form.Controls.Add($buttonPanel)
## Set Default actions for the buttons
$form.AcceptButton = $okButton # ENTER = Ok
$form.CancelButton = $cancelButton # ESCAPE = Cancel
return $form
}
$LeftMargin = 25
$BottomMargin = 30
$i = 0
$form = Build-FormPanel "Please update server configurations"
foreach($param in $hash){#Where $hash is an "dictionary" of key/value pairs
$k = $param.Key
$v = $param.Value
$lblValue = New-Object System.Windows.Forms.Label
$lblValue.Text = $k+":"
$lblValue.Top = 20*$i ; $lblValue.Left = $LeftMargin; $lblValue.Width=150 ;$lblValue.AutoSize = $true
$form.Controls.Add($lblValue) # Add to Form
#
$txtValue = New-Object Windows.Forms.TextBox
$txtValue.Top = 20*$i; $txtValue.Left = 160; $txtValue.Width = 320;
$txtValue.Text = $v
$form.Controls.Add($txtValue) # Add to Form
$i++
}
$form.Topmost = $True
$form.Add_Shown( { $form.Activate(); } )
$result = $form.ShowDialog()
if($result -eq "OK")
{
$j = 0;
foreach($param in $hash){
${"txtValue_$j"}.Text
$j++
}
}
else {Write-Host "Cancel"}
Basically, this works OK to display the form and inputs. But after submission, I am unable to capture all the user inputs. Only the last input value is captured, obviously because the variables get overwritten in the loop.
How can I achieve capturing the data as described?
The issue as you have mentioned is because of the its getting overwritten.
I can give you a logical set off.
YOu can use it in a loop and in the loop , you store all the data either in array or if its dynamic then you can use arraylist by using
New-Object System.Collections.ArrayList
. But recommended is to create PSCustomObject , store in that and add that to the arraylist each time.
Finally you can get the output captured in the arraylist.
Further you can try making the arraylist Global so that it will be available for the entire script.
Hope it helps.

PowerShell Forms - Vertical Progress Bar

This is regarding Windows Forms in PowerShell and the System.Windows.Forms.ProgressBar.
I am looking all over and cannot find anything that allows the progress bar to fill vertically. I've considered alternatives (such as resizing a label with a background color), but I prefer to use something that already has a class if possible. I could have sworn I had seen something out there like this before, even if it wasn't a true progress bar. I am not using it to track progress, but more for CPU usage, RAM usage, and drive space for server statuses. This is for a useful GUI for a quick report of servers from a dropdown list, something where I don't have to open another complete shell session (I have enough shells open as it is between O365 eDiscovery, data analysis, and other needs). Thanks for any suggestions in advance.
Here is a very good C# answer How do I make a winforms progress bar move vertically in C#?
It overrides the CreateParams method to set the PBS_VERTICAL flag in Style.
To make it work in PowerShell you will unfortunately have to use a bit of C# code.
This works for me:
$type = #'
using System;
using System.Windows.Forms;
public class VerticalProgressBar : ProgressBar {
protected override CreateParams CreateParams {
get {
CreateParams cp = base.CreateParams;
cp.Style |= 0x04;
return cp;
}
}
}
'#
Add-Type -TypeDefinition $type -ReferencedAssemblies System.Drawing,System.Data,System.Windows.Forms
$userForm = New-Object System.Windows.Forms.Form
$userForm.Text = "$title"
$userForm.Size = New-Object System.Drawing.Size(230,300)
$userForm.StartPosition = "CenterScreen"
$userForm.AutoSize = $False
$userForm.MinimizeBox = $False
$userForm.MaximizeBox = $False
$userForm.SizeGripStyle= "Hide"
$userForm.WindowState = "Normal"
$userForm.FormBorderStyle="Fixed3D"
$progressbar = New-Object 'VerticalProgressBar'
$progressbar.Location = New-Object System.Drawing.Point(180, 50);
$progressbar.Width = 20
$progressbar.Height = 200
$userForm.Controls.Add($progressbar)
$TrackBar = New-Object 'System.Windows.Forms.TrackBar'
$TrackBar.Location = New-Object System.Drawing.Point(10, 10);
$TrackBar.Width = 200
$TrackBar.add_ValueChanged({$progressbar.Value = $this.value*10})
$userForm.Controls.Add($TrackBar)
$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(10,220)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "OK"
$OKButton.Add_Click({$userForm.Close()})
$userForm.Controls.Add($OKButton)
$userForm.ShowIcon = $False
$userForm.Add_Shown({$userForm.Activate()})
$userForm.AcceptButton = $OKButton
[void] $userForm.ShowDialog()

Powershell: Selecting DataGridView Row

So far I have this code.
$form = New-Object System.Windows.Forms.Form
$form.Size = New-Object System.Drawing.Size(900,600)
$dataGridView = New-Object System.Windows.Forms.DataGridView
$dataGridView.Size=New-Object System.Drawing.Size(800,400)
$go = New-Object System.Windows.Forms.Button
$go.Location = New-Object System.Drawing.Size(300,450)
$go.Size = New-Object System.Drawing.Size(75,23)
$go.text = "Select"
$form.Controls.Add($go)
$form.Controls.Add($dataGridView)
$dataGridView.ColumnCount = 4
$dataGridView.ColumnHeadersVisible = $true
$dataGridView.Columns[0].Name = "Name"
$dataGridView.Columns[1].Name = "ID"
$dataGridView.Columns[2].Name = "Description"
$dataGridView.Columns[3].Name = "Memory"
$dataGridView.Columns[0].width = 240
get-process | foreach {
$dataGridView.Rows.Add($_.Name,$_.ID,$_.Description,$_.WorkingSet) | out-null
}
$go.Add_Click({
$selectedRow = $dataGridView.CurrentRowIndex
write-host $selectedRow
})
[void]$form.ShowDialog()
It simply gets the Process Name, ID, etc. properties and puts them into pre-defined headers in a DataGridView.
My problem is that I want to see the row I've clicked on via $selectedRow = $dataGridView.CurrentRowIndex and output it to the console. Instead, when the 'Select' button is pushed, a blank string is output to the terminal.
You can also get the row index with:
$dataGridView.CurrentCell.RowIndex
or
$dataGridView.SelectedRows[0].Index
You may also want to set the grid MultiSelect property to $false. Currently it allows multiple rows selection. Another thing to consider is setting the SelectionMode property to 'FullRowSelect'. When the grid is populated the first column is selected, not the whole row.
Change
$selectedRow = $dataGridView.CurrentRowIndex
to
$selectedRow = $dataGridView.CurrentRow.Index

Resources