Disabling Alt+F4 in a Powershell form - forms

I've written a powershell script which displays a form using System.Windows.Forms. I've already disabled the control box and all other ways that this form can be closed via the mouse. But I can't find a way of preventing the form closing by pressing Alt+F4.
i.e. Code snippet looks like this:
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
$objForm = New-Object System.Windows.Forms.Form
$objForm.Text = "Restart Required"
$objForm.Size = New-Object System.Drawing.Size(400,300)
$objForm.StartPosition = "CenterScreen"
$objForm.KeyPreview = $True
$objForm.Topmost = $True
$objForm.MinimizeBox = $false
$objForm.MaximizeBox = $false
$objForm.FormBorderStyle = "Fixed3d"
$objForm.ControlBox = $false
$objForm.ShowInTaskbar = $false
$objForm.Add_Shown({$objForm.Activate()})
[void] $objForm.ShowDialog()
Looking at MSDN, there are articles about overriding the FormClosing eventhandler in VB, C#, etc and . But I'm not sure how to implement similar logic into Powershell (if it's at all possible).

Set forms keypreview to true
$form1_KeyDown=[System.Windows.Forms.KeyEventHandler]{
#Event Argument: $_ = [System.Windows.Forms.KeyEventArgs]
if ($_.Alt -eq $true -and $_.KeyCode -eq 'F4') {
$script:altF4Pressed = $true;
}
}
$form1_FormClosing=[System.Windows.Forms.FormClosingEventHandler]{
#Event Argument: $_ = [System.Windows.Forms.FormClosingEventArgs]
if ($script:altF4Pressed)
{
if ($_.CloseReason -eq 'UserClosing') {
$_.Cancel = $true
$script:altF4Pressed = $false;
}
}
}

Related

Intermittent unexpected results when moving rows in a DataGridView

I have a basic script as per my code below. So the purpose of this is to be able to move records up and down, when pressing the up or down buttons. This works some of the time, but intermittently it will not move a record (but the selected row will still move). Please try out my code, and move the first record up and down a few times, I hope you will soon see what I mean.
Thank you
Adam
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Data")
# Main Form
$Form = New-Object System.Windows.Forms.Form
$Form.Size = New-Object System.Drawing.Size(510,450)
$Form.Text = "Test"
$Form.TopMost = $false
$Form.MinimizeBox = $false
$Form.MaximizeBox = $false
$Form.StartPosition = "CenterScreen"
$Form.FormBorderStyle = "FixedToolWindow"
# Datasource
$dt = New-Object System.Data.DataTable
[void]$dt.Columns.Add("Name")
[void]$dt.Columns.Add("Step")
[void]$dt.Rows.Add("Jack","1")
[void]$dt.Rows.Add("Jayden","2")
[void]$dt.Rows.Add("Dylan","3")
[void]$dt.Rows.Add("Ben","4")
# Datagridview
$DataGrid = New-Object System.Windows.Forms.DataGridView
$DataGrid.Location = New-Object System.Drawing.Size(10,10)
$DataGrid.Size = New-Object System.Drawing.Size(300,200)
$DataGrid.AllowUserToAddRows = $false
$DataGrid.DataSource = $dt
$DataGrid.DataBindings.DefaultDataSourceUpdateMode = 'OnPropertyChanged'
$DataGrid.AutoResizeColumns()
$Form.Controls.Add($DataGrid)
# Up Button
$upButton = New-Object System.Windows.Forms.Button
$upButton.Location = New-Object System.Drawing.Size(320,20)
$upButton.Size = New-Object System.Drawing.Size(90,40)
$upButton.Text = “Up”
$upButton.Add_Click({
moveRow -1
})
$Form.Controls.Add($upButton)
# Down Button
$downButton = New-Object System.Windows.Forms.Button
$downButton.Location = New-Object System.Drawing.Size(320,70)
$downButton.Size = New-Object System.Drawing.Size(90,40)
$downButton.Text = “Down”
$downButton.Add_Click({
moveRow 1
})
$Form.Controls.Add($downButton)
# Function to move record in DataGridView
function moveRow ($direction){
if ($DataGrid.SelectedRows[0] -ne $null){
$currentRow = $DataGrid.SelectedRows[0].Index
# Don't move up if you are at the top
if (($currentRow -eq 0) -and ($direction -eq -1)){
return
}
# Don't move down if you are at the bottom
elseif (($currentRow -eq ($DataGrid.Rows.Count - 1)) -and ($direction -eq 1)){
return
}
else {
# Get Current and New Values
$currentValue = $DataGrid.Rows[$currentRow].Cells["Step"].Value
$newRow = $currentRow + $direction
$newValue = $DataGrid.Rows[$newRow].Cells["Step"].Value
# Swap Values
$DataGrid.Rows[$currentRow].Cells["Step"].Value = $newValue
$DataGrid.Rows[$newRow].Cells["Step"].Value = $currentValue
# Sort and refresh DataGridView
$DataGrid.ClearSelection()
$DataGrid.Rows[$newRow].Selected = $true;
$DataGrid.Sort($DataGrid.Columns["Step"], "Ascending")
}
}
}
# Show Form
$Form.Add_Shown({$Form.Activate()})
[void] $Form.ShowDialog()

Storing variable from listbox

I'm attempting to gather user input and store it using $contacttype later on in my script. Originally I was using a simple text input, however I'm now trying to use a listbox to get user input instead.
Originally I was doing this:
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic') | Out-Null
$contacttype = [Microsoft.VisualBasic.Interaction]::InputBox("Enter the contact type", " ")
However, I'm now trying to use a listbox with something like:
$listBox = New-Object System.Windows.Forms.ListBox
$listBox.Location = New-Object System.Drawing.Point(10,40)
$listBox.Size = New-Object System.Drawing.Size(260,20)
$listBox.Height = 80
[void] $listBox.Items.Add("VPN")
[void] $listBox.Items.Add("Phone")
[void] $listBox.Items.Add("E-mail")
$form.Controls.Add($listBox)
$form.Topmost = $True
$result = $form.ShowDialog()
if ($result -eq [System.Windows.Forms.DialogResult]::OK)
{
$x = $listBox.SelectedItem
$x
}
How do I make sure $contacttype is populated with the output from the selected listbox item?
Per your answer, you just need to make sure you return your result to the $contacttype variable. However the code you provided wasn't complete, it didn't include the part that initiated $form or add an OK button to trigger the ok result.
Here's a complete version that I've also moved in to a function to show how you could make this a little more reusable:
Function Invoke-ListForm {
Param(
[string[]] $ListItem
)
$Form = New-Object system.Windows.Forms.Form
$listBox = New-Object System.Windows.Forms.ListBox
$listBox.Location = New-Object System.Drawing.Point(10,40)
$listBox.Size = New-Object System.Drawing.Size(260,20)
$listBox.Height = 80
ForEach ($Item in $ListItem) {
[void] $listBox.Items.Add($Item)
}
$listBox.Add_Click({ $listBox.SelectedItem })
$Form.Controls.Add($listBox)
$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Point(75,120)
$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)
$Form.Topmost = $True
$Result = $form.ShowDialog()
if ($result -eq [System.Windows.Forms.DialogResult]::OK)
{
$listBox.SelectedItem
}
}
$ContactType = Invoke-ListForm VPN,Phone,E-mail
$ContactType

Powershell date GUI strange behavior

When I run this code, you'll see that the first write host will show today's date, but the last 2 will show the default date. Does anyone know why?
The code came from Microsoft, so I'm confused as to why it won't work?
https://technet.microsoft.com/en-us/library/ff730942.aspx
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
[DateTime] $dtmDate = New-Object DateTime;
[string] $dt = "";
$objForm = New-Object Windows.Forms.Form
$objForm.Text = "Select a Date, then press the enter key"
$objForm.Size = New-Object Drawing.Size #(350,190)
$objForm.StartPosition = "CenterScreen"
$objForm.KeyPreview = $True
$objForm.Add_KeyDown({
if ($_.KeyCode -eq "Enter")
{
$dtmDate = $objCalendar.SelectionStart
#$dt = $objCalendar.SelectionStart.ToShortDateString();
Write-Host $dtmDate;
$objForm.Close()
}
})
$objForm.Add_KeyDown({
if ($_.KeyCode -eq "Escape")
{
$objForm.Close()
}
})
$objCalendar = New-Object System.Windows.Forms.MonthCalendar
$objCalendar.ShowTodayCircle = $False
$objCalendar.MaxSelectionCount = 1
$objForm.Controls.Add($objCalendar)
$objForm.Topmost = $True
$objForm.Add_Shown({$objForm.Activate()})
[void] $objForm.ShowDialog()
Write-Host "Date selected: $dtmDate"
if ($dtmDate)
{
Write-Host "Date selected: $dtmDate"
}
Change $dtmDate to $global:dtmDate.
The reason is you are assigning the value when the event is triggered so its declared and initialized only locally. Out of the event the variable is only declared. By using $global you are changing the scope if the variable to all script.
Btw. never trust Microsoft documentation

grey out a textbox until enabled by checkbox

I'm creating a script and I want the user to mark certain checkboxes to enable txtboxes.
When the user presses the chexbox, the textbox next to it will be enabled. If they don't, then they cannot insert text into it.
Right now it doesn't work, has someone an idea how to change it?
Thanks for your help!
Here is the part of my script with the checkbox and the textbox:
#creating the whole form
$objForm = New-Object System.Windows.Forms.Form
$objForm.Text = "Ofir`s script"
$objForm.Size = New-Object System.Drawing.Size(480,240)
$objForm.StartPosition = "CenterScreen"
#This creates the TextBox1
$objTextBox1 = New-Object System.Windows.Forms.TextBox
$objTextBox1.Location = New-Object System.Drawing.Size(300,40)
$objTextBox1.Size = New-Object System.Drawing.Size(140,150)
$objTextBox1.TabIndex = 3
$objTextBox1.text = Dsp.z
$objForm.Controls.Add($objTextBox1)
#This creates a checkbox for textbox1
$objDsp2Checkbox = New-Object System.Windows.Forms.Checkbox
$objDsp2Checkbox.Location = New-Object System.Drawing.Size(280,40)
$objDsp2Checkbox.Size = New-Object System.Drawing.Size(150,20)
$objDsp2Checkbox.TabIndex = 0
$objForm.Controls.Add($objDsp2Checkbox)
#changing the file name
if ($objDsp2Checkbox.Checked -eq $true)
{
$objTextBox1.Enabled = $true
}
elseif ($objDsp2Checkbox.Checked -eq $false)
{
$objTextBox1.Enabled = $false
}
#makes the form appear on top of the screen
$objForm.Topmost = $True
$objForm.Add_Shown({$objForm.Activate()})
[void] $objForm.ShowDialog()
You need to assign the bit of code that enables/disables the text box to an event handler on the checkbox object. Most likely the Click event.
$objDsp2Checkbox_OnClick = {
if ($objDsp2Checkbox.Checked -eq $true)
{
$objTextBox1.Enabled = $true
}
elseif ($objDsp2Checkbox.Checked -eq $false)
{
$objTextBox1.Enabled = $false
}
}
$objDsp2Checkbox.Add_Click($objDsp2Checkbox_OnClick)
http://social.technet.microsoft.com/wiki/contents/articles/25911.how-to-add-a-powershell-gui-event-handler-part-1.aspx
https://msdn.microsoft.com/en-us/library/system.windows.forms.textbox(v=vs.110).aspx

Better way to validate when not using a function ValidateSet

I'm looking for a better way to validate a "response" from a user for my script. I know you can use a function to validate this but I want it to be more interactive.
$DPString = #"
Enter users department.
Valid choices are;
"Accounts","Claims","Broker Services","Underwriting","Compliance","HR","IT","Developmet","Legal" and "Legal Underwriting"
"#
$Department = Read-Host "$DPString"
do
{
Switch ($Department)
{
Accounts { $DepBool = $true }
Claims { $DepBool = $true }
"Broker Services" { $DepBool = $true }
Underwriting { $DepBool = $true }
Compliance { $DepBool = $true }
"Legal Underwriting" { $DepBool = $true }
Legal { $DepBool = $true }
HR { $DepBool = $true }
IT { $DepBool = $true }
Development { $DepBool = $true }
Default { $DepBool = $false }
}
if ($DepBool -eq $true)
{
$DepLoop = $false
}
else {
$Department = Read-Host "Please enter a valid Department"
$DepLoop = $true
}
}
while ($DepLoop)
It's not clear in what context the user is being prompted for input, but I would favour a list of valid parameters being passed in on the command line. I would make them mutually exclusive using Parameter Sets.
However, if this is a part of a larger script which prompts for input part way through, then it might be appropriate to prompt for input using the windows API for displaying an input box. Here's a link which describes this approach in more detail Creating a Custom Input Box.
Although it feels wrong to display UI from powershell, I understand that there are times when this is desirable, using the link above here's an implementation of a ListBox, you simply pass it a string array and it returns the selected value:
<#
.SYNOPSIS
Displays a Windows List Control and returns the selected item
.EXAMPLE
Get-ListBoxChoice -Title "Select Environment" -Prompt "Choose an environment" -Options #("Option 1","Option 2")
This command displays a list box containing two options.
.NOTES
There are two command buttons OK and Cancel, selecting OK will return the selected option, whilst
Cancel will return nothing.
.RELATED LINKS
http://technet.microsoft.com/en-us/library/ff730941.aspx
#>
Function Get-ListBoxChoice
{
[cmdletbinding()]
param
(
[Parameter(Mandatory=$true)]
[string] $Title,
[Parameter(Mandatory=$true)]
[string] $Prompt,
[Parameter(Mandatory=$true)]
[string[]] $Options
)
Write-Verbose "Get-ListBoxChoice"
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
$uiForm = New-Object System.Windows.Forms.Form
$uiForm.Text = $Title
$uiForm.FormBorderStyle = 'Fixed3D'
$uiForm.MaximizeBox = $false
$uiForm.Size = New-Object System.Drawing.Size(300,240)
$uiForm.StartPosition = "CenterScreen"
$uiForm.KeyPreview = $True
$uiForm.Add_KeyDown({if ($_.KeyCode -eq "Enter")
{$chosenValue=$objListBox.SelectedItem;$uiForm.Close()}})
$uiForm.Add_KeyDown({if ($_.KeyCode -eq "Escape")
{$uiForm.Close()}})
$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(75,160)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "OK"
$OKButton.Add_Click({$chosenValue=$objListBox.SelectedItem;$uiForm.Close()})
$uiForm.Controls.Add($OKButton)
$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Size(150,160)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = "Cancel"
$CancelButton.Add_Click({$uiForm.Close()})
$uiForm.Controls.Add($CancelButton)
$uiLabel = New-Object System.Windows.Forms.Label
$uiLabel.Location = New-Object System.Drawing.Size(10,20)
$uiLabel.Size = New-Object System.Drawing.Size(280,20)
$uiLabel.Text = $Prompt
$uiForm.Controls.Add($uiLabel)
$objListBox = New-Object System.Windows.Forms.ListBox
$objListBox.Location = New-Object System.Drawing.Size(10,40)
$objListBox.Size = New-Object System.Drawing.Size(260,20)
$objListBox.Height = 120
$Options | % {
[void] $objListBox.Items.Add($_)
}
$uiForm.Controls.Add($objListBox)
$uiForm.Topmost = $True
$uiForm.Add_Shown({$uiForm.Activate()})
[void] $uiForm.ShowDialog()
$chosenValue
}
If they're running at least V3, you can use Out-Gridview:
$Departments = #(
[PSCustomObject]#{Name = 'Accounts';Description = 'Accounting Department'}
[PSCustomObject]#{Name = 'Claims';Description = 'Claims Department'}
[PSCustomObject]#{Name = 'Broker';Description = 'Broker Services'}
)
$GridParams = #{
Title = "Select a department, and press 'OK', or 'Cancel' to quit."
OutPutMode = 'Single'
}
$Department = $Departments | Out-Gridview #GridParams
If ($Department)
{ #Do stuff }