Powershell Winforms -如何在项目分组时按列排序列表视图



考虑以下内容。如果ListView包含未分组的项,则SortListView函数可以完美地工作。

Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
$form = New-Object System.Windows.Forms.Form
$form.Text = "Test"
$form.Size = '500,375'
$form.StartPosition = 'CenterScreen'
$form.MaximizeBox = $false
# List of sessions
$LogonList = New-Object System.Windows.Forms.ListView
$LogonList.View = 'Details'
$LogonList.Location = '10,20'
$LogonList.Size = '465,305'
$LogonList.FullRowSelect = $true
$LogonList.Columns.Add('UID',80) | Out-Null
$LogonList.Columns.Add('IPAddress',100) | Out-Null
$LogonList.Columns.Add('HostName',160) | Out-Null
$LogonList.Add_ColumnClick({SortListView $this $_.Column})
$form.Controls.Add($LogonList)
#
Function SortListView {
Param(
[System.Windows.Forms.ListView]$xsender,
$column
)
$Script:SortingDescending = !$Script:SortingDescending
$xsender.Sorting = 'none'
If (!$xsender.Groups) {
#
# No groups in ListView - sort whole list by clicked column property
#
$xsender.ShowGroups = $false
$temp = $xsender.Items | Foreach-Object { $_ }
$xsender.Items.Clear()
$xsender.Items.AddRange(($temp | Sort-Object -Descending:$script:SortingDescending -Property @{ Expression={ $_.SubItems[$column].Text } }))
}
Else {
#
# ListView is grouped, sort each group by clicked column property.
#
$xsender.ShowGroups = $true
$temp = $xsender.Items | Foreach-Object { $_ }
Write-Host "ListView groups:"
$temp | Group-Object -Property Group | ForEach-Object {
Write-Host $_.Name
}
$xsender.Items.Clear() 
}
}
#
$TestData = New-Object System.Collections.ArrayList
$TestData.Add([pscustomobject]@{UID='userjoe';IPAddress='192.168.150.14';Hostname='Workstation99'}) | Out-Null
$TestData.Add([pscustomobject]@{UID='userjoe';IPAddress='192.168.150.15';Hostname='Workstation100'}) | Out-Null
$TestData.Add([pscustomobject]@{UID='userjoe';IPAddress='192.168.150.16';Hostname='Workstation101'}) | Out-Null
$TestData.Add([pscustomobject]@{UID='userjoe';IPAddress='192.168.150.17';Hostname='Workstation102'}) | Out-Null
$TestData.Add([pscustomobject]@{UID='userdave';IPAddress='192.168.150.13';Hostname='Workstation104'}) | Out-Null
$TestData.Add([pscustomobject]@{UID='userdave';IPAddress='192.168.150.12';Hostname='Workstation105'}) | Out-Null
$TestData.Add([pscustomobject]@{UID='userdave';IPAddress='192.168.150.11';Hostname='Workstation106'}) | Out-Null
$TestData.Add([pscustomobject]@{UID='userdave';IPAddress='192.168.150.10';Hostname='Workstation107'}) | Out-Null
#
$TestData | Group-Object -Property UID | ForEach-Object {
$ThisEntry = New-Object System.Windows.Forms.ListViewGroup
$ThisEntry.Header = $_.Name
$LogonList.Groups.Add($ThisEntry) |Out-Null
$DuplicateUIDs = $_.Group | Sort-Object {$_.IPAddress} -Descending 
$DuplicateUIDs | ForEach-Object {
$Entry = New-Object System.Windows.Forms.ListViewItem('-') -ErrorAction Stop
$Entry.SubItems.Add([string]$_.IPAddress) | Out-Null
$Entry.SubItems.Add([string]$_.HostName) | Out-Null
$Entry.Group = $ThisEntry
# Add compiled object to ListView box
$LogonList.Items.Add($Entry) | Out-Null
}
}
$form.Activate()
$form.ShowDialog() | Out-Null
$form.Dispose()

我想编码相同的行为时,项目分组-在上面的例子中,单击列标题应按该列订购组内的所有组项目。我真的很难理解不同的对象/项目分配,并想出一个明智的解决方案——任何帮助都很感激。

如果你使用的是PowerShell 5或以上版本,你可以用一种更简单、更快捷的方式实现ListView排序:

首先,在代码的某个地方实现一个比较器接口(你可以在排序函数之前添加它):

class ListViewItemComparer : System.Collections.IComparer
{
[int]$col
[System.Windows.Forms.SortOrder]$order
ListViewItemComparer()
{
$this.col = 0
$this.order = [System.Windows.Forms.SortOrder]::Ascending
}
ListViewItemComparer([int]$column, [System.Windows.Forms.SortOrder]$sortOrder)
{
$this.col = $column
$this.order = $sortOrder
}
[int]Compare([object]$x, [object]$y)
{
$result = [String]::Compare( `
([System.Windows.Forms.ListViewItem]$x).SubItems[$this.col].Text,`
([System.Windows.Forms.ListViewItem]$y).SubItems[$this.col].Text);
if ($this.order -eq [System.Windows.Forms.SortOrder]::Ascending)
{
return $result
}
else
{
return -($result)
}
}
}

那么你的排序函数将通过设置ListViewItemSorter属性为一个新的比较器接口来实现:

Function SortListView {
Param(
[System.Windows.Forms.ListView]$xsender,
$column
)
$Script:SortingDescending = !$Script:SortingDescending
if ($Script:SortingDescending)
{
$xsender.Sorting = [System.Windows.Forms.SortOrder]::Descending
}
else
{
$xsender.Sorting = [System.Windows.Forms.SortOrder]::Ascending
}
$xsender.ListViewItemSorter = [ListViewItemComparer]::new($column, $xsender.Sorting)
}

无论是否有组,该方法都有效。

此处和此处提供更多信息

编辑:

在创建表单之前不要忘记这一行

Add-Type -AssemblyName System.Windows.Forms

如果你使用的是旧版本的PowerShell,用c#实现比较接口:

$compareCode = @"
using System;
using System.Collections;
using System.Windows.Forms;
namespace Tools
{
public class ListViewItemComparer : IComparer
{
private int col;
private SortOrder order;
public ListViewItemComparer()
{
col = 0;
order = SortOrder.Ascending;
}
public ListViewItemComparer(int column, SortOrder sortOrder)
{
col = column;
order = sortOrder;
}
public int Compare(object x, object y)
{
int result = String.Compare(
((ListViewItem)x).SubItems[col].Text,
((ListViewItem)y).SubItems[col].Text);
if (order == SortOrder.Ascending)
{
return result;
}
else
{
return -result;
}
}
}
}
"@
Add-Type -TypeDefinition $compareCode -ReferencedAssemblies System.Windows.Forms

然后在sort函数中调用接口(只更改这一行):

$xsender.ListViewItemSorter = [Tools.ListViewItemComparer]::new($column, $xsender.Sorting)

编辑2

PowerShell类选项需要在运行脚本之前调用Add-Type -AssemblyName System.Windows.Forms才能正常工作。这是由于PowerShell在执行前解析代码的方式。

#Call script
Add-Type -AssemblyName System.Windows.Forms
.MyFormScriptWithICompareClass.ps1

c#版本没有这个问题。

最新更新