sortable column headings like out-gridview?

Aug 31, 2011 at 7:28 PM

Is there a way to create sortable column headings in a new-gridview like with the out-gridview cmdlet?

Coordinator
Sep 1, 2011 at 5:50 AM
Edited Sep 1, 2011 at 6:49 PM

 

$Items = Get-Process
$tableHeaders = @{ HandleCount = "Handles"; NonpagedSystemMemorySize = "NPM";
 PagedMemorySize = "PM"; WorkingSet ="WS"; VirtualMemorySize = "VM";
 TotalProcessorTime ="CPU Time"; Id = "Id"; ProcessName = "Process Name" } ScrollViewer -Margin 5 -Row 1 { ListView -SelectionMode Extended -ItemsSource $Items -Name SelectedItems ` -FontFamily "Consolas, Courier New" -View { GridView -Columns { ## tableHeaders is a hashtable with keys that are property names and values that are labels foreach($h in $tableHeaders.GetEnumerator()) { GridViewColumn -Header $h.Value -DisplayMember { Binding $h.Key } } } } -On_SelectionChanged { if($selectedItems.SelectedItems.Count -gt 0) { $SelectFTList | Set-UIValue -value ( $selectedItems.SelectedItems | ForEach-Object { $_.OriginalItem } ) } else { $SelectFTList | Set-UIValue -value ( $selectedItems.Items | ForEach-Object { $_.OriginalItem } ) } } -On_Loaded { ## Default output, in case you close the window without selecting anything $SelectFTList | Set-UIValue -value ( $selectedItems.Items | ForEach-Object { $_.OriginalItem } ) } # -On_MouseDoubleClick { Close-Control $parent } } -On_Load { Add-EventHandler -Input $SelectedItems -SourceType GridViewColumnHeader -EventName Click { if($_.OriginalSource -and $_.OriginalSource.Role -ne "Padding") { ## We need to sort by a PROPERTY of the objects in the gridview, in our example, we can just use the path that we used for binding ... $Sort = $_.OriginalSource.Column.DisplayMemberBinding.Path.Path $direction = if($Sort -eq $lastSort) { "Descending" } else { "Ascending" } $lastSort = $Sort $view = [System.Windows.Data.CollectionViewSource]::GetDefaultView( $SelectedItems.ItemsSource ) $view.SortDescriptions.Clear() $view.SortDescriptions.Add(( New-Object System.ComponentModel.SortDescription $Sort, $direction )) $view.Refresh() } } } -Show

It's basically a matter of adding an event handler to the GridViewColumnHeader(s).  Note that when you see $SelectedItems we are talking about the actual ListView (that's where we bind the Click event).

Sep 1, 2011 at 5:12 PM

This looks promising, but with wpk v1.0 and showui v1.1 my add-eventhandler does not have an -input or a -sourcetype parameter.  It looks like this:

Add-EventHandler [-Object] <Object> [-EventName] <String> [[-Handler] <ScriptBlock>] [-PassThru] [<CommonParameters>]

Is the somewhere else I should be getting add-eventhandler?

 

Coordinator
Sep 1, 2011 at 6:44 PM
Edited Sep 1, 2011 at 6:49 PM

Ah fooey.  That's the second time I did that this week -- that functionality was added to source last month, but for various reasons we didn't get a monthly release out.  Anyway. Here's what you have to do in 1.1:

 

$Items = Get-Process
$tableHeaders = @{ HandleCount = "Handles"; NonpagedSystemMemorySize = "NPM";
 PagedMemorySize = "PM"; WorkingSet ="WS"; VirtualMemorySize = "VM";
 TotalProcessorTime ="CPU Time"; Id = "Id"; ProcessName = "Process Name" } ScrollViewer -Margin 5 -Row 1 -Name SelectList { ListView -SelectionMode Extended -ItemsSource $Items -Name SelectedItems ` -FontFamily "Consolas, Courier New" -View { GridView -Columns { ## tableHeaders is a hashtable with keys that are property names and values that are labels foreach($h in $tableHeaders.GetEnumerator()) { GridViewColumn -Header $h.Value -DisplayMember { Binding $h.Key } } } } -On_SelectionChanged { if($selectedItems.SelectedItems.Count -gt 0) { $SelectList | Set-UIValue -value ( $selectedItems.SelectedItems | ForEach-Object { $_.OriginalItem } ) } else { $SelectList | Set-UIValue -value ( $selectedItems.Items | ForEach-Object { $_.OriginalItem } ) } } -On_Loaded { ## Default output, in case you close the window without selecting anything $SelectList | Set-UIValue -value ( $selectedItems.Items | ForEach-Object { $_.OriginalItem } ) } # -On_MouseDoubleClick { Close-Control $parent } } -On_Load { [System.Windows.RoutedEventHandler]$EventHandler = { Initialize-EventHandler $ErrorActionPreference = 'stop' ###### START EVENT HANDLER if($_.OriginalSource -and $_.OriginalSource.Role -ne "Padding") { ## We need to sort by a PROPERTY of the objects in the gridview, in our example, we can just use the path that we used for binding ... $Sort = $_.OriginalSource.Column.DisplayMemberBinding.Path.Path $direction = if($Sort -eq $lastSort) { "Descending" } else { "Ascending" } $lastSort = $Sort $view = [System.Windows.Data.CollectionViewSource]::GetDefaultView( $SelectedItems.ItemsSource ) Write-Verbose "Sort: $Sort ($direction)" $view.SortDescriptions.Clear() $view.SortDescriptions.Add(( New-Object System.ComponentModel.SortDescription $Sort, $direction )) $view.Refresh() } ###### END EVENT HANDLER trap { . Write-WPFError $_ continue } } ## Hook up to a RoutedEvent from the grid headers by handling it on the ListView $SelectedItems.AddHandler( [System.Windows.Controls.GridViewColumnHeader]::ClickEvent, $EventHandler) } -Show

I should note: the whole point here is just:  Add a SortDescription (and Refresh the view). 

The way people expect for it to work is when they click the column header, so you want to add an event handler to the GridViewColumnHeader's Click event. 

It's a little hard to *find* the GridViewColumnHeader(s), so we just set the event handler (for THAT event) on the ListView itself, and since it's a RoutedEvent, it bubbles up and you can handle it there.