Search code examples
powershellwinformspowershell-5.1

How do I reference a ListBox item


Sometimes things (PowerShell?) gets too smart on me...

In this case, I would like change the Text property of a WinForms ListBox item selected by its index (not the SelectedItems). The point here is that $ListBox.Items[$CurrentIndex] appears to be a [string] type rather than an [Item] type...
For this I have created a mcve (with a context menu) out of my bigger project:

using namespace System.Windows.Forms
$Form = [Form]@{ StartPosition = 'CenterScreen' }
$ListBox = [ListBox]@{}
@('one', 'two', 'three').ForEach{ $Null = $ListBox.Items.Add($_) }
$Form.Controls.Add($ListBox)
$ListBox.ContextMenuStrip = [ContextMenuStrip]@{}
$Context = $ListBox.ContextMenuStrip.Items.Add('ToUpper')
$ListBox.Add_MouseDown({ param($s, $e) 
    if ($e.Button -eq 'Right') { $Script:CurrentIndex = $ListBox.IndexFromPoint($e.Location) }
})
$Context.Add_Click({ param($s, $e)
    if ($Null -ne $CurrentIndex) {
        $Text = $ListBox.Items[$CurrentIndex]
        Write-Host "I want to change text ""$Text"" of the item #$CurrentIndex to: ""$($Text.ToUpper())"""
        # $ListBox.Items.Item[$CurrentIndex] = $Text.ToUpper()
    }
})
$Form.ShowDialog()

How can I change the text of e.g. $ListBox.Items[1] ("two") to e.g. "TWO"?
I am actually not sure whether this is PowerShell related issue (similar to #16878 Decorate dot selected Xml strings (leaves) with XmlElement methods where I would have an option to use the SelectNodes() method) or related to WinForms itself: The answer might be in Gwt Listbox item reference to an Object in which case I have no clue how translate this to PowerShell.


Solution

  • You could modify the stored item value with $ListBox.Items.Item($CurrentIndex) = $Text.ToUpper() - but modifying a collection item in place is not going to trigger re-drawing the ListBox control, so it'll look like no change took place.

    Instead, modify the collection itself - remove the old entry and insert a new one at the same position:

    $Context.Add_Click({ param($s, $e)
        if ($Null -ne $CurrentIndex) {
            $Text = $ListBox.Items[$CurrentIndex]
            # Remove clicked item
            $ListBox.Items.RemoveAt($CurrentIndex)
            # Insert uppercased string value as new item at the same index
            $ListBox.Items.Insert($CurrentIndex, $Text.ToUpper())
        }
    })
    

    This will cause the owning control to refresh and the change will be reflected in the GUI immediately