銀河鉄道

【PowerShell】フォルダ内のファイル一覧を表示する|FolderBrowserDialog

サムネイル
[PowerShell]Exportfile list
GUIで
フォルダを選択する

GUIでできるの?

WPF(Windows Presentation Foundation)を使えばできる

WPF window with PowerShell

Add-Type -AssemblyName PresentationFramework
Add-Type -AssemblyName PresentationCore
Add-Type -AssemblyName WindowsBase
Add-Type -AssemblyName System.Windows.Forms

# XAML 定義
# NOTE: ヒアドキュメント内の不正な文字(U+00A0など)を全て削除し、
# "<Window..."がヒアドキュメントの開始記号の直後から始まるように修正。
$xaml = @"
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        Title="フォルダ内ファイル一覧" Height="550" Width="700">
    <Grid Margin="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <StackPanel Orientation="Horizontal" Grid.Row="0" Margin="0,0,0,10">
            <Button Name="SelectFolderButton" Content="フォルダを選択" Width="120" Margin="0,0,10,0"/>
            <Button Name="CopyAllButton" Content="一覧をコピー" Width="120" Margin="0,0,10,0"/>
            <Button Name="SaveCsvButton" Content="CSV保存" Width="120"/>
        </StackPanel>

        <TextBlock Name="FolderPathText" Grid.Row="1" FontWeight="Bold" Foreground="DarkBlue" Margin="0,0,0,5"/>

        <TextBlock Name="TitleText" Grid.Row="2" Text="フォルダ内ファイル一覧をここに表示します" FontWeight="Bold" FontSize="14" Margin="0,0,0,5"/>

        <ListBox Name="FileListBox" Grid.Row="3" Margin="0,10,0,0" SelectionMode="Extended"/>
    </Grid>
</Window>
"@

# 読み込み
# XAMLパースエラーが解消され、ここから正常に実行される
$reader = New-Object System.Xml.XmlNodeReader ([xml]$xaml)
$window = [Windows.Markup.XamlReader]::Load($reader)

# コントロール取得 (以降のコードは前回の機能実現のための修正版を使用)
$selectButton = $window.FindName("SelectFolderButton")
$copyButton = $window.FindName("CopyAllButton")
$saveCsvButton = $window.FindName("SaveCsvButton")
$fileListBox = $window.FindName("FileListBox")
$folderPathText = $window.FindName("FolderPathText")
$titleText = $window.FindName("TitleText")

# ファイル情報保持用
$global:fileInfoList = @()

# 最初の表示場所
$desktopPath = [System.Environment]::GetFolderPath('Desktop')

# フォルダ選択処理を関数として定義
function Show-FolderBrowserDialog {
    param(
        [string]$InitialPath
    )

    $folderBrowser = New-Object System.Windows.Forms.FolderBrowserDialog
    # 初期パスとしてデスクトップを設定
    $folderBrowser.SelectedPath = $InitialPath

    if ($folderBrowser.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
        $selectedPath = $folderBrowser.SelectedPath
        $fileListBox.Items.Clear()
        $global:fileInfoList = @()

        # Get-ChildItemの結果を一時的に保持
        $items = Get-ChildItem -Path $selectedPath | Where-Object { $_.Name -ne "." -and $_.Name -ne ".." }

        # 1. フォルダを先に一覧に追加
        $folders = $items | Where-Object { $_.PSIsContainer } | Sort-Object Name
        foreach ($item in $folders) {
            $name = $item.Name
            $date = $item.LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss")
            $full = $item.FullName
            $display = "[フォルダ] $name (更新日時: $date)" 
            $fileListBox.Items.Add($display)
            $global:fileInfoList += [PSCustomObject]@{
                Name = $name
                LastWriteTime = $date
                FullPath = $full
                IsContainer = $true
            }
        }

        # 2. ファイルを後から一覧に追加
        $files = $items | Where-Object { -not $_.PSIsContainer } | Sort-Object Name
        foreach ($item in $files) {
            $name = $item.Name
            $date = $item.LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss")
            $full = $item.FullName
            $display = "$name (更新日時: $date)"
            $fileListBox.Items.Add($display)
            $global:fileInfoList += [PSCustomObject]@{
                Name = $name
                LastWriteTime = $date
                FullPath = $full
                IsContainer = $false
            }
        }
        
        $folderPathText.Text = "選択されたフォルダ: " + $selectedPath
        $titleText.Text = "フォルダ内ファイル一覧"
    }
}

# フォルダ選択イベント
$selectButton.Add_Click({
    Show-FolderBrowserDialog -InitialPath $desktopPath
})

# ウィンドウロード完了イベント: ウィンドウが表示された直後にフォルダ選択ダイアログを自動で開く
$window.Add_Loaded({
    Show-FolderBrowserDialog -InitialPath $desktopPath
})

# 一覧コピーイベント
$copyButton.Add_Click({
    if ($fileListBox.Items.Count -gt 0) {
        $text = ($fileListBox.Items | ForEach-Object { $_.ToString() }) -join "`r`n"
        [System.Windows.Clipboard]::SetText($text)
        [System.Windows.MessageBox]::Show("ファイル一覧をクリップボードにコピーしました。")
    } else {
        [System.Windows.MessageBox]::Show("コピーするファイルがありません。")
    }
})

# CSV保存イベント
$saveCsvButton.Add_Click({
    if ($global:fileInfoList.Count -gt 0) {
        $saveDialog = New-Object System.Windows.Forms.SaveFileDialog
        $saveDialog.Filter = "CSVファイル (*.csv)|*.csv"
        $saveDialog.FileName = "file_list.csv"
        if ($saveDialog.ShowDialog() -eq "OK") {
            $csvPath = $saveDialog.FileName
            $global:fileInfoList | Export-Csv -Path $csvPath -NoTypeInformation -Encoding UTF8
            [System.Windows.MessageBox]::Show("CSVファイルとして保存しました。`n`n$csvPath")
        }
    } else {
        [System.Windows.MessageBox]::Show("保存するファイル情報がありません。")
    }
})

# ダブルクリックでファイルを開く
$fileListBox.Add_MouseDoubleClick({
    $index = $fileListBox.SelectedIndex
    if ($index -ge 0 -and $index -lt $global:fileInfoList.Count) {
        $path = $global:fileInfoList[$index].FullPath
        Start-Process $path
    }
})

# 最前面表示
$window.Topmost = $true

# ウィンドウ表示
$window.ShowDialog()

主要ブロックの役割

  • Loadedイベントでフォルダ選択画面を出す
  • フォルダ選択
    • FolderBrowserDialogで選択 → Get-ChildItemで一括取得
    • フォルダ→ファイルの順にソート・整形してListBoxへ
    • $global:fileInfoListへ格納する
  • 一覧コピー
    • Clipboard.SetText()
  • CSV保存
    • Export-Csv -Encoding UTF8
  • ダブルクリック起動
    • Start-Processでファイル/フォルダを開く

その他のコマンドレット

Add-Type -AssemblyName

.NETクラスを利用可能にするもの

Loads an assembly into the PowerShell session, enabling the use of its .NET classes. In this script, it imports the core components of WPF and System.Windows.Forms for folder selection.

Get-ChildItem

指定された場所にあるアイテム(ファイルやフォルダなど)を取得する

A foundational PowerShell cmdlet that retrieves items (like files and folders) in a specified location.

| Where-Object | パイプラインから流れてきたオブジェクトを特定の条件でフィルタリング |
| Sort-Object | オブジェクトを指定したプロパティ(ここではName)で並べ替え |
| Export-Csv | [PSCustomObject]のリストをCSVファイルとして指定パスに書き出し |
| Start-Process | 指定されたファイルやアプリケーションを新しいプロセスとして起動 |
| New-Object System.Windows.Forms.FolderBrowserDialog | フォルダ選択ダイアログのインスタンスを生成 |
| [Windows.Markup.XamlReader]::Load() | XAML文字列からWPFのUI要素(ウィンドウ)を構築 |
| Add_Click({...}) | ボタンクリック時に実行する処理(スクリプトブロック)を登録 |
| [PSCustomObject]@{...} | カスタムプロパティを持つオブジェクトを生成しデータ格納 |

Diagram

[Start]
   |
   v
[Load XAML -> Window]
   |
   v
[Find Controls]
   |
   v
[OnLoaded] --(auto)--> [FolderBrowserDialog]
   |                          |
   |                          v
   |                   [Get-ChildItem]
   |                          |
   |             +------------+-------------+
   |             |                          |
   v             v                          v
[Copy All]   [ListBox Add Folders]     [ListBox Add Files]
   |             \                         /
   |              \                       /
   |               \                     /
   |                v                   v
   |            [$global:fileInfoList <- objects]
   |                         |
   |                         v
   |                    [Save CSV]
   |
   v
[DoubleClick -> Start-Process (open)]

Vocabulary

WPF|WPF
XAML|XAML
DataBinding|データバインディング
ListBox|リストボックス
ObservableCollection|オブザーバブルコレクション
Clipboard|クリップボード
Export-Csv|CSVエクスポート
BOM (Byte Order Mark)|バイト順マーク
STA (Single-Threaded Apartment)|単一スレッドアパートメント
FolderBrowserDialog|フォルダ選択ダイアログ

著者

author
月うさぎ

編集後記:
この記事の内容がベストではないかもしれません。