首页 > 解决方案 > 如何将 2 个不同的数组放在列表视图的不同列中

问题描述

我正在尝试创建一个包含 2 个单独列的列表视图。第一列应显示文件夹名称列表。第二列应显示文件夹中特定文件的最后修改日期。

数组 1 = 文件夹列表
数组 2 = 文件夹列表中文件的修改日期

我能够创建数组并列出文件夹名称,但是在使用文件夹中文件的修改日期填充第二列时遇到问题。“echo $filedate.lastwritetime”正确显示了所有最后修改的日期,但是当我尝试在 ListView 列中显示它时,所有行只显示 1 个日期。

$path = C:\Test\

$folders = Get-ChildItem $path

foreach ($folder in $folders) {
    $filedate = Get-ChildItem -Path $path\$folder\"Last Backed Up.txt"
    echo $filedate.LastWriteTime
}

[void]$listView.Columns.Add("Folder Name", 150);
[void]$listView.Columns.Add("Last Modified Date", 150);

$folders | ForEach-Object {
    $row = New-Object System.Windows.Forms.ListViewItem($_.Name)   
    [void]$row.SubItems.Add($filedate.LastWriteTime.Tostring())
    [void]$listView.Items.Add($row)
}

$form.Controls.Add($listview)

$form.Topmost = $true

$result = $form.ShowDialog()

标签: arrayspowershelllistview

解决方案


不需要有两个数组并遍历$folders数组两次。这可以在一个循环中完成。

由于不确定所有文件夹实际上都包含一个名为 的文件Last Backed Up.txt,因此我建议您执行以下操作。对您来说有趣的部分是$folders | ForEach-Object {..}循环,因为您已经准备好表单。为了清楚起见,代码完整:

$path = 'C:\Test'

# get an array of subfolder objects in the root folder $path
$folders = Get-ChildItem -Path $path -Directory  # should we add -Recurse to also dig into any subfolder?

# build your form
Add-Type -AssemblyName System.Windows.Forms
$form = New-Object System.Windows.Forms.Form 
$form.Text = "Backup Folders"
$form.Size = New-Object System.Drawing.Size(600,400) 
$form.StartPosition = "CenterScreen"
$form.TopMost = $true

$listView = New-Object System.Windows.Forms.ListView
$listView.Location = New-Object System.Drawing.Point(10,40) 
$listView.Size = New-Object System.Drawing.Size(560,280) 
$listView.Anchor = 'Top,Right,Bottom,Left'
$listView.View = 'Details'
$listView.FullRowSelect = $true
$listView.GridLines = $true

# add two columns to the listView
[void]$listView.Columns.Add("Folder Name", 150)
[void]$listView.Columns.Add("Last Modified Date", 150)

# iterate through the list of folders 
$folders | ForEach-Object {
    # try to get the LastWriteTime of the file 'Last Backed Up.txt' inside this folder
    $file = Get-Item -Path (Join-Path -Path $_.FullName -ChildPath 'Last Backed Up.txt') -ErrorAction SilentlyContinue
    if ($file) { $date = $file.LastWriteTime.Tostring() } else { $date = '' }

    # create a new row for the listview
    $row = New-Object System.Windows.Forms.ListViewItem( $_.Name )     # first colum:  the folder name
    [void]$row.SubItems.Add($date)                                     # second colum: the file LastWriteTime
    [void]$listView.Items.Add($row)
}

$form.Controls.Add($listview)

# add an event handler on the listbox to do something with the selected item
$listView.Add_Click({
    # here put your code to perform some action with the selected subfolder
    $selected = $listView.SelectedItems[0].Text
    $lastModified = $listView.SelectedItems[0].SubItems[1].Text
    # for demo, simply show a messagebox
    if ([string]::IsNullOrWhiteSpace($lastModified)) {
        $message = "Subfolder '$selected' was never backed up"
    }
    else {
        $message = "Subfolder '$selected' was last backed up on $lastModified"
    }
    [System.Windows.Forms.MessageBox]::Show($message, "Backup")
})

[void]$form.ShowDialog()

# we're done, so remove the form from memory
$form.Dispose()  

PS 在您的代码中,所有行仅显示一个日期这一事实是因为您$filedate在第一个循环的每次迭代中都重置了变量。因此,只有最后一个文件被捕获在变量中。


更新


因为您希望能够像您评论的那样对列表视图中的项目进行排序,所以我将代码更新如下。

它现在响应单击列标题以动态排序。
最初,列表视图显示在“文件夹名称”列上按升序排序的项目。
如果您单击“上次修改日期”列标题,它将按显示的日期对项目进行排序。再次单击同一列标题会将排序从升序切换到降序,反之亦然。

$path = 'C:\Test'

# get an array of subfolder objects in the root folder $path
$folders = Get-ChildItem -Path $path -Directory  # should we add -Recurse to also dig into any subfolder?

# build your form
Add-Type -AssemblyName System.Windows.Forms
$form = New-Object System.Windows.Forms.Form 
$form.Text = "Backup Folders"
$form.Size = New-Object System.Drawing.Size(600,400) 
$form.StartPosition = "CenterScreen"
$form.TopMost = $true

$listView = New-Object System.Windows.Forms.ListView
$listView.Location = New-Object System.Drawing.Point(10,40) 
$listView.Size = New-Object System.Drawing.Size(560,280) 
$listView.Anchor = 'Top,Right,Bottom,Left'
$listView.View = 'Details'
$listView.FullRowSelect = $true
$listView.GridLines = $true
$listView.Sorting = [System.Windows.Forms.SortOrder]::None  # IMPORTANT

# add two columns to the listView
[void]$listView.Columns.Add("Folder Name", 150)
[void]$listView.Columns.Add("Last Modified Date", 150)

# iterate through the list of folders initially sort the items by FolderName (column 1) ascending
# $items will be an array of objects to help sorting whatever is displayed in the listview
$items = $folders | ForEach-Object {
    # try to get the LastWriteTime of the file 'Last Backed Up.txt' inside this folder
    $file = Get-Item -Path (Join-Path -Path $_.FullName -ChildPath 'Last Backed Up.txt') -ErrorAction SilentlyContinue
    # emit an object to be collected in the $items array
    [PsCustomObject]@{
        'FolderName' = $_.Name
        'Modified'   = if ($file) { $file.LastWriteTime }  # a real DateTime object here for accurate sorting
    }
} | Sort-Object FolderName

# create three script-scoped variables to keep score of the current sort
$script:sortColumn     = 0
$script:sortDescending = $false
$script:sortProperty   = @('FolderName', 'Modified')[$script:sortColumn]

# fill the listview for the first time
$items | ForEach-Object {
    # create a new row for the listview
    $row = New-Object System.Windows.Forms.ListViewItem( $_.FolderName )     # first colum:  the folder name
    if ($_.Modified) { [void]$row.SubItems.Add($_.Modified.ToString()) }     # second colum: the file LastWriteTime
    [void]$listView.Items.Add($row)
}

# add an event handler for when the user clicks on one of the column headers
$listView.Add_ColumnClick({
    param($sender, $e)  # $sender is the listview itself, $e is ColumnClickEventArgs
    if ($e.Column -eq $script:sortColumn) {
        # click on the currently sorted column --> reverse sort
        $script:sortDescending = $script:sortDescending -xor $true
    }
    else {
        $script:sortColumn     = $e.Column
        $script:sortDescending = $false
        $script:sortProperty   = @('FolderName', 'Modified')[$script:sortColumn]
    }

    # now sort the $items array on property $script:sortProperty in the 
    $temp = $items | Sort-Object $script:sortProperty -Descending:$script:sortDescending
    # redraw the listview items
    # using SuspendLayout() method before and ResumeLayout() after adding the items increases the performance
    $listView.SuspendLayout()
    $listView.Items.Clear()
    $temp | ForEach-Object {
        # create a new row for the listview
        $row = New-Object System.Windows.Forms.ListViewItem( $_.FolderName )     # first colum:  the folder name
        if ($_.Modified) { [void]$row.SubItems.Add($_.Modified.ToString()) }     # second colum: the file LastWriteTime
        [void]$listView.Items.Add($row)
    }
    $listView.ResumeLayout()
    $temp = $null
})

# add an event handler on the listbox to do something with the selected item
$listView.Add_Click({
    # here put your code to perform some action with the selected subfolder
    $selected = $listView.SelectedItems[0].Text
    $lastModified = $listView.SelectedItems[0].SubItems[1].Text
    # for demo, simply show a messagebox
    if ([string]::IsNullOrWhiteSpace($lastModified)) {
        $message = "Subfolder '$selected' was never backed up"
    }
    else {
        $message = "Subfolder '$selected' was last backed up on $lastModified"
    }
    [System.Windows.Forms.MessageBox]::Show($message, "Backup")
})

$form.Controls.Add($listview)

[void]$form.ShowDialog()

# we're done, so remove the form from memory
$form.Dispose()

推荐阅读