Using XAML/WPF in PowerShell, how do I populate a list box?
Alright, I figured out the answer. It turns out that when I was using .Add, I should have been specifying a PowerShell custom object as my overload, not a simple hashtable as I was doing before. When I changed my code to the following:
#Add DisplayMemberBindings for all columns<GridViewColumn Header="VMName" DisplayMemberBinding ="{Binding VMName}"/><GridViewColumn Header="Status" DisplayMemberBinding ="{Binding Status}"/><GridViewColumn Header="Other" DisplayMemberBinding ="{Binding Other}"/>
And then modify my Add statement as well:
$vmpicklistView.items.Add([pscustomobject]@{'VMName'='1';Status="Access Denied";Other="Yes"})
I'm able to populate my fields, like so
#Make Dummy Entries 1..15 | % {if ($_ % 2){$vmpicklistView.items.Add([pscustomobject]@{'VMName'="VM_$($_)";Status="Online";Other="Yes"})}else{$vmpicklistView.items.Add([pscustomobject]@{'VMName'="VM_$($_)";Status="Access Denied";Other="Yes"})}}
Why did I have to do this?
Here is my interpretation as to why this was needed. PowerShell Custom Objects provide an Object with Named values, which I can pluck out using bindings, while a hashtable is a collection of Key/Value pairs, not well suited for this purpose.
I hope this answer has helped anyone else who got stumped as I did!
WPF is generally used to bind to viewmodel object properties not dictionary or hashtable entries. There is a syntax to bind to dictionary entries which would make the GridViewColumn bindings look like this:
<GridViewColumn Header="VMName" DisplayMemberBinding ="{Binding [VMName]}"/><GridViewColumn Header="Status" DisplayMemberBinding ="{Binding [Status]}"/>
Note the use of square brackets to indicate that the value comes from an indexer.
As for why the "Other" column appears as "(Collection)", I believe the default binding is just going to display the object and WPF must render dictionaries and hashtables as "(Collection)"
Try adding a similar binding for Other:
<GridViewColumn Header="Other" DisplayMemberBinding ="{Binding [Other]}"/>
There is a much simpler way of doing this.
Say you have a setup similar to the question above
<ListView Name="Collections"> <ListView.View> <GridView> <GridViewColumn Header="Collection Name" DisplayMemberBinding="{Binding CollectionName}" /> <GridViewColumn Header="Collection Count" DisplayMemberBinding="{Binding CollectionCount}" /> </GridView> </ListView.View></ListView>
and in Powershell you are getting say the list of objects from some provider (in this case ConfigurationManager)
$DCs = Get-CMDeviceCollection | ? { $_.CollectionRules.RuleName -contains "OU Query" }
Now this out of the box has properties that do not align with my DisplayMemberBindings so we have to make a collection of objects that do so
$BindableDCs = $DCs | Select-Object -Property @{Name='CollectionName';Expression={$_.Name}}, @{Name='CollectionCount';Expression={$_.MemberCount}}
Now (assuming the code to load the XAML and discover the named elements and put them in powershell variables has been run)
$Collections.ItemsSource = $BindableDCs
Done.
Or if you want to do it the one liner powershell way
$Collections.ItemsSource = Get-CMDeviceCollection | ? { $_.CollectionRules.RuleName -contains "OU Query" } | Select-Object -Property @{Name='CollectionName';Expression={$_.Name}}, @{Name='CollectionCount';Expression={$_.MemberCount}}