I'm attempting to make a function that will allow me to popup a please wait message run some more script then close the popup
Function Popup-Message {
param ([switch]$show,[switch]$close)
Add-Type -AssemblyName System.Windows.Forms
# Build Form
$objForm = New-Object System.Windows.Forms.Form
$objForm.Text = "Test"
$objForm.Size = New-Object System.Drawing.Size(220,100)
# Add Label
$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(80,20)
$objLabel.Size = New-Object System.Drawing.Size(100,20)
$objLabel.Text = "Hi there!"
$objForm.Controls.Add($objLabel)
If ($show)
{
$objForm.Show() | Out-Null
$global:test = "Show"
}
If ($close)
{
# destroy form
$objForm.Close() | Out-Null
$global:test = "Close"
}
}
I can then get the popup to display by:
Popup-Message -show
At this point I can see the $test variable as Show
But when I try to close the window with:
Popup-Message -close
But the popup window will not close If I look at $test again it will show as Close
I'm assuming this has something to do with keeping the function in the Global Scope but I can't figure out how to do this with the form
kpogue's helpful answer explains your problem well and offers a solution that is effective, but suboptimal due to relying on global variables.
Let me suggest a different approach, where you use a function to simply define the form and return that definition, on which you can call the .Show()
and .Close()
methods as needed, but note that the .Show()
method is overridden via Add-Member
to include a call to [System.Windows.Forms.Application]::DoEvents()
, so as to ensure that the form is properly drawn.
function New-PopUpForm {
Add-Type -AssemblyName System.Windows.Forms
# Create the form.
$objForm = New-Object System.Windows.Forms.Form -Property @{
Text = "Test"
Size = New-Object System.Drawing.Size 220, 100
StartPosition = 'CenterScreen' # Center on screen.
FormBorderStyle = 'FixedSingle' # fixed-size form
# Remove interaction elements (close button, system menu).
ControlBox = $false
}
# Create a label...
$objLabel = New-Object System.Windows.Forms.Label -Property @{
Location = New-Object System.Drawing.Size 80, 20
Size = New-Object System.Drawing.Size 100, 20
Text = "Hi there!"
}
# ... and add it to the form.
$objForm.Controls.Add($objLabel)
# Override the `.Show()` method to include
# a [System.Windows.Forms.Application]::DoEvents(), so as
# to ensure that the form is properly drawn.
$objForm | Add-Member -Force -Name Show -MemberType ScriptMethod -Value {
$this.psbase.Show() # Call the original .Show() method.
# Activate the form (focus it).
$this.Activate()
[System.Windows.Forms.Application]::DoEvents() # Ensure proper drawing.
}
# Since this form is meant to be called with .Show() but without
# a [System.Windows.Forms.Application]::DoEvents() *loop*, it
# it is best to simply *hide* the cursor (mouse pointer), so as not
# to falsely suggest that interaction with the form is possible
# and so as not to end up with a stuck "wait" cursor (mouse pointer) on
# the first instantiation in a session.
[System.Windows.Forms.Cursor]::Hide()
# Return the form.
return $objForm
}
You can then use the function as follows:
$form = New-PopupForm
$form.Show()
# ...
$form.Close()
Note:
Once you call .Close()
, the form instance stored in $form
is disposed of and cannot be reused - simply call New-PopupForm
again to create a new instance.
If the PowerShell session running your script exits, any pop-up windows created in the session close automatically.
Caveats:
Note that, due to use of the .Show()
method (without additional effort), the user won't be able to interact with the pop-up window, notably not even in order to move the window or close it manually.
ControlBox = $false
above removes the window's close button and system menu so as to make it obvious that no interaction is possible.[System.Windows.Forms.Cursor]::Hide()
serves the same purpose.
.Show()
, as discussed in the last bullet point.By contrast, .ShowDialog()
would allow interaction, but blocks further execution of your script until the window is closed by the user.
If you need to combine the two approaches - allowing the user to interact with the window while continuing to do processing in your PowerShell script - you need to call [System.Windows.Forms.Application]::DoEvents()
in a loop, as shown in this answer.
[System.Windows.Forms.Cursor]::Hide()
call from the function.