2

I am trying to write a PowerShell script that lets the user select a printer. The script below displays a menu of printers, but I can't figure out how to get the $selectPrinter variable out of the scope of the button.Add_Click block and into the rest of the script. This is probably easy for experts, but none of my experiments work. Is there an obvious answer that I'm missing? Here is the code:

# Load Windows Forms and drawing libraries
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

# Create the form
$form = New-Object System.Windows.Forms.Form
$form.Text = 'Select a Printer'
$form.Size = New-Object System.Drawing.Size(300,200)
$form.StartPosition = 'CenterScreen'

# Add a label
$label = New-Object System.Windows.Forms.Label
$label.Location = New-Object System.Drawing.Point(10,10)
$label.Size = New-Object System.Drawing.Size(280,20)
$label.Text = 'Please select a printer:'
$form.Controls.Add($label)

# Add a list box
$listBox = New-Object System.Windows.Forms.ListBox
$listBox.Location = New-Object System.Drawing.Point(10,40)
$listBox.Size = New-Object System.Drawing.Size(260,100)
$form.Controls.Add($listBox)

# Populate the list box with printer names
$printers = Get-Printer | Select-Object -ExpandProperty Name
foreach ($printer in $printers) {
    $listBox.Items.Add($printer)
}

# Add a button
$button = New-Object System.Windows.Forms.Button
$button.Location = New-Object System.Drawing.Point(10,150)
$button.Size = New-Object System.Drawing.Size(100,20)
$button.Text = 'OK'
$form.Controls.Add($button)

# Add button click event
$button.Add_Click({
    $selectedPrinter = $listBox.SelectedItem
    $form.Close()
})

# Show the form
$form.Add_Shown({$form.Activate()})
[void] $form.ShowDialog()

# Output the selected printer
if ($selectedPrinter) {
    Write-Host "Selected printer: $selectedPrinter"
} else {
    Write-Host "No printer selected."
}

pause
5
  • In short: Event handlers run in a child scope of the caller, so in order to create or update variables in the caller's scope, the latter must be targeted explicitly, typically with $script:variable = ...; without that, you'll implicitly create a block-local variable. See the linked duplicate for details. Commented Jan 19, 2024 at 2:06
  • 1
    @mklement0 - Thank you for that. I should have figured that out for myself from the Microsoft documentation, but I didn't. Commented Jan 19, 2024 at 2:51
  • Glad to hear it helped. It's actually not easy to find the PowerShell-specific angle on this, short of trial and error (which is how I discovered it). Commented Jan 19, 2024 at 2:58
  • @mklement0 - I also needed trial and error before I figured out that the $global: or $script: part of the variable only worked in the child scope, and that I didn't need it outside the child scope. At least I think that's what is the case... Commented Jan 19, 2024 at 3:04
  • Yes, in the script scope itself $script: is optional - but $global: is best avoided, because it affects the entire session, not just your script. More generally, the fact that the event-handler script block runs in a child scope means that if you want to target the caller's scope - which may or may not be the script scope - you need Set-Variable -Scope 1 ..., as discussed in the duplicate. Commented Jan 19, 2024 at 3:08

1 Answer 1

1

The answer turns out to be: replace $selectedPrinter with $global:selectedPrinter - and the script now works perfectly. Apologies for wasting bandwidth, and I hope the answer is useful to someone.

Sign up to request clarification or add additional context in comments.

2 Comments

@Santiago Squarzon - Thank you for fixing my typo!
That works - but using $global: is best avoided. See the linked duplicate for better solutions.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.