functionpowershellpopupglobal-functions

Create a popup message in Powershell


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


Solution

  • 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:

    Caveats: