Windows PowerShell Tip of the Week

Here’s a quick tip on working with Windows PowerShell. These are published every week for as long as we can come up with new tips. If you have a tip you’d like us to share or a question about how to do something, let us know.

Find more tips in the Windows PowerShell Tip of the Week archive.

Multi-Select List Boxes – And More!

Good new everyone: you can relax now, because the big day is finally here.

In last week’s tip we explained how you can use Windows PowerShell and the .NET Framework to create a dialog box that lets users make selections from a list box. After introducing the basic idea behind creating a list box we promised that, in this week’s tip, we’d show you a few additional things you can do to spruce up that list box; on top of that, we promised to show you how to create a multi-select list box. That was probably a mistake; after all, no doubt many of you haven’t been able to eat or sleep over the past week, impatiently waiting for the moment when we show you how to spruce up a list box. Well, now you can relax: it’s finally time to go beyond the basic list box.

Let’s start by showing you the code for creating a multi-select list box (in other words, a list box that enables you to select more than one item at a time). We’re not going to explain most of the code in any detail today; for that we’ll refer you to last week’s tip. But don’t worry: we will explain the code differences between a standard list box and a multi-select list box.

After all, we wouldn’t be able to eat or sleep if we failed to do that.

But first things first:

$x = @()

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

$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")
    {
        foreach ($objItem in $objListbox.SelectedItems)
            {$x += $objItem}
        $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(
   {
        foreach ($objItem in $objListbox.SelectedItems)
            {$x += $objItem}
        $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 make a selection from the list below:"
$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.SelectionMode = "MultiExtended"

[void] $objListbox.Items.Add("Item 1")
[void] $objListbox.Items.Add("Item 2")
[void] $objListbox.Items.Add("Item 3")
[void] $objListbox.Items.Add("Item 4")
[void] $objListbox.Items.Add("Item 5")

$objListbox.Height = 70
$objForm.Controls.Add($objListbox)
$objForm.Topmost = $True

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

$x

Interestingly enough, there are only two real differences between a standard list box and a multi-select list box: 1) in a multi-select list box you must assign a value to the SelectionMode property; and, 2) in a multi-select list box you must work with an array of selected items rather than a single selected item. Believe it or not, that’s all it takes to turn a standard list box into a multi-select list box.

Let’s start by assigning a value to the SelectionMode property:

$objListbox.SelectionMode = "MultiExtended"

As you can see, this isn’t the most complicated bit of coding you’ll ever have to do. In this case we’ve set the SelectionMode to MultiExtended. What does that mean? Well, for one thing, it means that you can select multiple items in the list box; all you have to do is click an item, then hold down the Ctrl key and click another item. (And another item and another item and ….) In addition, you can also click an item, hold down the Shift key and then click a second item; that will cause those two items, plus any items in between the two, to be selected. For example, let’s say we have the following list box (which we do):

Now, under MultiExtended mode, suppose you click on Item 1, then hold down the Shift key and click on Item 4. What’s going to happen? This is going to happen:

Nice, huh?

Alternatively, you can set the SelectionMode to MultiSimple. In that case, holding down the Shift key won’t select a range of items. Instead, under that mode the Shift key functions exactly like the Ctrl key: it enables you to select one additional item. Here’s what we get under MultiSimple mode when we click Item 1 and then hold down the Shift key and select Item 4:

Is that better or worse? Neither, really; it’s just different.

Note. And what if you don’t want a multi-select list box at all; what if you just want a plain-old select-one-item-only list box? That’s easy: just don’t bother to set the SelectionMode.

Incidentally, under the MultiExtended mode you can also use the arrow keys (with or without the Shift key) to select items in the list box. Give it a try and see for yourself.

Now, what about the code for determining which items were selected in the list box? Last week, when we talked about the standard list box, our code for grabbing the value of the selected item and then closing the dialog box was no more complicated than this:

$OKButton.Add_Click({$x=$objListbox.SelectedItem;$objForm.Close()})

That was easy because we never had to deal with more than a single item in the list box. With a multi-select list box, however, we can select more than one item. How are we going to handle that?

Well, here’s one way:

$OKButton.Add_Click(
   {
        foreach ($objItem in $objListbox.SelectedItems)
            {$x += $objItem}
        $objForm.Close()
   })

Before we explain how this bit of code works we should point out that, in the very first line of our script, we created an empty array named $x:

$x = @()

Why did we do that? Because we needed a place to store all the items that were chosen in the list box, and an array seemed as good a place as any.

Note. OK, technically that’s not true: we didn’t really need a place to store all the items chosen in the list box; we could just as easily work with the property value $objListbox.SelectedItems instead. We chose the array route because we thought it made it easier for people to visualize how the process works.

As for our little code block, we start out by creating a foreach loop that loops through all the items in the SelectedItems property (which, as the name implies, is a multi-valued property that contains each item selected in the list box):

foreach ($objItem in $objListbox.SelectedItems)

Inside this loop we then use the following line of code to add each item to the array $x:

$x += $objItem

Once we’ve finished looping through all the selected items we call the Close method and then close the form. As soon as the form is closed, we echo back the value of $x. That’s what the last line of code in the script does:

$x

Suppose we selected items 1, 3, and 4 in the list box. In that case, we should get back the following:

Item 1
Item 3
Item 4

By the way, you can use this same approach with a standard list box as well; the only difference is that the SelectedItems property will never have more than one value. It’s entirely up to you.

And there you have it: the multi-select list box. Now, what about sprucing up your list boxes?

Well, to be honest, there really isn’t all that much you can do to spruce up a list box. (For that you’d need to use a checked list box or a list view, two items we’ll cover in future tips.) One thing you can do, however, is specify a default item that will automatically be selected each time the list box is displayed. For example, suppose we want Item 2 to be the default item. That’s fine; the following line of code will take care of that for us. Simply pop this bit of code into the script right after you’ve added all the items to the list box:

$objListBox.SelectedItem = "Item 2"

Now run the script and display the list box. You should see this:

Not bad, huh?

Another property you might find useful is the Sorted property. When we created our list box we provided all the list items in alphabetical order:

[void] $objListbox.Items.Add("Item 1")
[void] $objListbox.Items.Add("Item 2")
[void] $objListbox.Items.Add("Item 3")
[void] $objListbox.Items.Add("Item 4")
[void] $objListbox.Items.Add("Item 5")

That’s nice, but it’s something that might not always be possible; for example, if you pull information from a text file, there’s no guarantee that information will be in alphabetical order. If you want to, you can use the Sort-Object cmdlet to sort those values before you add them to the list box. Alternatively, you can set the Sorted property to True and let the list box take care of all that for you:

$objListBox.Sorted = $True

Suppose we had used this block of code to add items to the list box, a block in which the items are not sorted alphabetically:

[void] $objListbox.Items.Add("Item 5")
[void] $objListbox.Items.Add("Item 2")
[void] $objListbox.Items.Add("Item 4")
[void] $objListbox.Items.Add("Item 1")
[void] $objListbox.Items.Add("Item 3")

What does that mean? That means that our list box will reflect the order in which those items were added:

OK, that’s not so good, is it? But that’s all right; notice the difference when we set the Sorted property to True:

Cool.

Last (and probably least) it’s possible to change the foreground and background colors of the list box, as well as the font used for displaying each item. Would you ever want to do this? Well, that’s up to you. If you are interested, however, then these lines of code should help:

$objListBox.BackColor = "cyan"
$objListBox.Forecolor = "blue"
$objListBox.Font = "Arial Black"

Here’s the resulting dialog box:

Like we said, we’ll leave it up to you to decide whether this is even worth doing.

That’s all we have time for this week; we’ll see everyone again next week. What tip will we be presenting next week? Well, we’re going to show you something realy cool, we’re going to – you know what: maybe it’s best if we don’t say any more. After all, you need to get some sleep this week.