Author Posts

November 21, 2013 at 7:46 am

Hey all. I'm working on a list box to choose a user from a provided list, in this case users having the last name of Smith for an example. I've referenced the technet article on creating a list box, and while it populates correctly the variable I'm storing the selected user in returns nothing. I'm assuming this is probably due to how I'm passing each user's sam account name to the list box but I'm not certain.

If anyone code provide some insight, I'd appreciate it. Code below.


$search_properties = "sAMAccountName", "sn"
$search = "OU=Test,DC=contoso,DC=com"
$lastname = "Smith"
$users = Get-ADUser -Filter * -SearchBase $search -Properties $search_properties |
Select-Object @{Name="sAM";Expression={$_.sAMAccountName}},
@{Name="Last Name";Expression={$_.sn}} |
Where {$_."Last Name" -eq $lastname}

[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")

$objForm = New-Object System.Windows.Forms.Form
$objForm.Text = "Select the user to remove"
$objForm.Size = New-Object System.Drawing.Size(300,200)
$objForm.StartPosition = "CenterScreen"

$objForm.KeyPreview = $True
$objForm.Add_KeyDown({if ($_.KeyCode -eq "Enter")
{$selection=$objListBox.SelectedItem;$objForm.Close()}})
$objForm.Add_KeyDown({if ($_.KeyCode -eq "Escape")
{$objForm.Close()}})

$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(75,120)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "OK"
$OKButton.Add_Click({$selection=$objListBox.SelectedItem;$objForm.Close()})
$objForm.Controls.Add($OKButton)

$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Size(150,120)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = "Cancel"
$CancelButton.Add_Click({$objForm.Close()})
$objForm.Controls.Add($CancelButton)

$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(10,20)
$objLabel.Size = New-Object System.Drawing.Size(280,20)
$objLabel.Text = "Please select a user:"
$objForm.Controls.Add($objLabel)

$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 = 80
$objListBox.Sorted = $True

Foreach ($user in $users) {
[void] $objListBox.Items.Add($user."sAM")
}

$objForm.Controls.Add($objListBox)

$objForm.Topmost = $True

$objForm.Add_Shown({$objForm.Activate()})
[void] $objForm.ShowDialog()

$selection

November 21, 2013 at 8:55 am

I don't know what the deal is with those sample articles on TechNet. This is the second one I've seen where they used the approach of trying to set a variable's value from inside the OK Button's Click event. This doesn't work, as written, due to variable scoping issues. You're just setting a variable named $selection inside that script block.

The quick and dirty fix for this is to use the script: scope modifier when assigning the variable:

$script:selection = $objListBox.SelectedItem

I don't actually like this method much, but it does work.

Here's how I would modify their sample code. (I would have modified yours instead, but copying and pasting it out of the forum post stuck everything on one giant line for some reason.)

[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") 

$objForm = New-Object System.Windows.Forms.Form 
$objForm.Text = "Select a Computer"
$objForm.Size = New-Object System.Drawing.Size(300,200) 
$objForm.StartPosition = "CenterScreen"

# Got rid of the block of code related to KeyPreview and KeyDown events.

$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(75,120)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "OK"

# Got rid of the Click event for the OK button, and instead just assigned its DialogResult property to OK.
$OKButton.DialogResult = [System.Windows.Forms.DialogResult]::OK

$objForm.Controls.Add($OKButton)

# Setting the form's AcceptButton property causes it to automatically intercept the Enter keystroke and
# treat it as clicking the OK button (without having to write your own KeyDown events).
$objForm.AcceptButton = $OKButton

$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Size(150,120)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = "Cancel"

# Got rid of the Click event for the Cancel button, and instead just assigned its DialogResult property to Cancel.
$CancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel

$objForm.Controls.Add($CancelButton)

# Setting the form's CancelButton property causes it to automatically intercept the Escape keystroke and
# treat it as clicking the OK button (without having to write your own KeyDown events).
$objForm.CancelButton = $CancelButton

$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(10,20) 
$objLabel.Size = New-Object System.Drawing.Size(280,20) 
$objLabel.Text = "Please select a computer:"
$objForm.Controls.Add($objLabel) 

$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 = 80

[void] $objListBox.Items.Add("atl-dc-001")
[void] $objListBox.Items.Add("atl-dc-002")
[void] $objListBox.Items.Add("atl-dc-003")
[void] $objListBox.Items.Add("atl-dc-004")
[void] $objListBox.Items.Add("atl-dc-005")
[void] $objListBox.Items.Add("atl-dc-006")
[void] $objListBox.Items.Add("atl-dc-007")

$objForm.Controls.Add($objListBox) 

$objForm.Topmost = $True

# Now, instead of having events in the form assign a value to a variable outside of their scope, the code that calls the dialog
# instead checks to see if the user pressed OK and selected something from the box, then grabs that value.

$result = $objForm.ShowDialog()
if ($result -eq [System.Windows.Forms.DialogResult]::OK -and $objListBox.SelectedIndex -ge 0)
{
    $selection = $objListBox.SelectedItem
    $selection

    # Do something with $selection
}

November 21, 2013 at 9:24 am

That makes perfect sense. Thanks!

July 10, 2014 at 11:29 am

Hi Dave,

Your previous answer helped me out too. Would you mind reworking this one as well? It seems to have a similar problem.

http://technet.microsoft.com/en-us/library/ff730941.aspx

[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

$objForm = New-Object System.Windows.Forms.Form
$objForm.Text = "Data Entry Form"
$objForm.Size = New-Object System.Drawing.Size(300,200)
$objForm.StartPosition = "CenterScreen"

$objForm.KeyPreview = $True
$objForm.Add_KeyDown({if ($_.KeyCode -eq "Enter")
{$x=$objTextBox.Text;$objForm.Close()}})
$objForm.Add_KeyDown({if ($_.KeyCode -eq "Escape")
{$objForm.Close()}})

$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(75,120)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "OK"
$OKButton.Add_Click({$x=$objTextBox.Text;$objForm.Close()})
$objForm.Controls.Add($OKButton)

$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Size(150,120)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = "Cancel"
$CancelButton.Add_Click({$objForm.Close()})
$objForm.Controls.Add($CancelButton)

$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(10,20)
$objLabel.Size = New-Object System.Drawing.Size(280,20)
$objLabel.Text = "Please enter the information in the space below:"
$objForm.Controls.Add($objLabel)

$objTextBox = New-Object System.Windows.Forms.TextBox
$objTextBox.Location = New-Object System.Drawing.Size(10,40)
$objTextBox.Size = New-Object System.Drawing.Size(260,20)
$objForm.Controls.Add($objTextBox)

$objForm.Topmost = $True

$objForm.Add_Shown({$objForm.Activate()})
[void] $objForm.ShowDialog()

$x

September 24, 2014 at 7:45 am

Thanks for this fix. I had problems with the same code too and was so glad to find this excellent fix!

September 24, 2014 at 8:20 am

No problem! Just a heads-up, these fixes have been published to TechNet and via a "Hey, Scripting Guy!" article last month: http://blogs.technet.com/b/heyscriptingguy/archive/2014/08/02/weekend-scripter-fixing-powershell-gui-examples.aspx

September 24, 2014 at 8:56 am

Thanks. Shame they don't have a link to that article in the original which would have saved me several hours of tearing my hair out earlier!

Much appreciate your help.

Jon

December 12, 2017 at 3:06 pm

Hi Dave,

Hope you are well.

Wondering if you can help, I am using the above script to prompt a user to select an App-V package from a folder and then to publish the selected App-V package. However how do I get $selection to run Add-AppvClientPackage | Publish-AppvClientPackage -global. I also need it to Enable-appv using as administrator.

Would appreciate any tips, help or suggestions.

Thanks