Get-ChildItem with a user interface that accepts pipeline input

Yesterday I published a little PowerShell function that allows you to select files visually. It's a wrapper around Get-ChildItem. Check the post.

Bernd Kriszio suggested that the first argument should be accepted from pipeline too. So, here is the second version.

Examples

The examples from previous post work as expected. And there is a little bit more to show you.

For each directory in m:\temp\blog a form is shown. Selected files are sent to the pipeline

[1]: gci m:\temp\blog | 
	? { $_.PsIsContainer } | 
	Select-File -rec | copy-item -Destination c:\temp\

For each directory (m:\temp\blog and m:\backup) form is shown. Again, the selected files are sent to the pipeline.

[2]: 'm:\temp\blog','m:\backup' | select-file

Code

function Select-File {
    param(
        [Parameter(Position=0,Mandatory=$true, ValueFromPipeline=$true,  
            ValueFromPipelineByPropertyName=$true)]
        [Alias("PsPath")]
        [string]$LiteralPath,
        [Parameter(Position=1,Mandatory=$false)][string]$filter,
        [Parameter()][switch]$recurse,
        [Parameter()][switch]$hideDir,
        [Parameter(Mandatory=$false)][string]$sortProperty='Name'
    )
    begin {
        Add-Type -Assembly System.Windows.Forms
        $global:SelectedItems = @()
        
    }
    process {
        $p = @{LiteralPath=$LiteralPath}
        if ($filter) { $p['filter'] = $filter }
        if ($recurse) { $p['recurse'] = $true }
        $files = @(Get-ChildItem @p | ? {!$_.PsIsContainer})
        
        if ($files.Length -eq 0) {
            #[Void][System.Windows.Forms.MessageBox]::Show("There is no item to select in $LiteralPath")
            return
        }
        $form = new-object Windows.Forms.Form  
        $form.Text = "Pick the files"
        $form.Size = new-object Drawing.Size @(500,600)  

        $panel = new-object System.Windows.Forms.ListView
        $panel.Dock = 'Fill'
        $panel.CheckBoxes = $true
        $panel.View = 'Details'
        $panel.FullRowSelect = $true
        [void]$panel.Columns.Add('', -1, 'Left')
        [void]$panel.Columns.Add('Dir', $(if($hideDir){0}else{-1}), 'Left')
        [void]$panel.Columns.Add('Name', -1, 'Left')
        [void]$panel.Columns.Add('Size', -1, 'Right')
        [void]$panel.Columns.Add('Modified', -1, 'Left')
        [void]$panel.Columns.Add('FullPath', 0, 'Left')
        $form.Controls.Add($panel)
            
        $dir = (Resolve-Path $LiteralPath).Path
        $dirE = [regex]::escape($dir)
        $items = @{}
        $files | 
            Sort $sortProperty |
            % { 
                $items[$_.FullName] = $_
                $cb = New-Object Windows.Forms.ListViewItem
                $cb.Checked = $false
                [void]$cb.SubItems.Add(($_.FullName -replace "($dirE).*",'$1'))    #directory
                [void]$cb.SubItems.Add(($_.FullName -replace "$dirE\\?(.*)",'$1')) #file
                [void]$cb.SubItems.Add($_.Length)                                  #length
                [void]$cb.SubItems.Add($_.LastWriteTime.ToString('yyyy-MM-dd HH:mm:ss')) #modified
                [void]$cb.SubItems.Add($_.FullName)                                #helper full path
                [void]$panel.Items.Add($cb)
            }
    
        $ok = new-object System.Windows.Forms.Button 
        $ok.add_Click({  
            $form.close()
        })  
        $ok.BackColor = [System.Drawing.Color]::Green
        $ok.Text      = "Ok"
        $ok.Dock      = [Windows.Forms.DockStyle]::Bottom
        $form.Controls.Add($ok)
    
        $Form.Add_Shown({$form.Activate()})   
        [void]$form.showdialog()  
        
        $panel.Items | 
            ? { $_.Checked } | 
            % { 
                $key = $_.SubItems[5].Text
                Write-Output $items[$key]
            }
    }
}

Now, when I think about the usage – originally I needed to select files only. However, why not to be able to select directories as well? Is it worthy spending my time? Maybe..

Download

Meta: 2010-04-02, Pepa