[備忘録] PowerShellでExcelのようなグリッド画面を作成してみた

## はじめに

PowerShellでデータを扱う際、コンソールでの表示だけでは見づらいことがあります。そこで、Excelのようなグリッド形式でデータを表示・編集できる画面を作成する方法を備忘録として残します。

### 想定するユースケースの例
- ファイルから特定の情報を即席で抽出して確認したい場面――たとえばコピー直後などに使えそうだと思い、この記事の実装を試してみました。

- ログ解析や、LLMの出力データから特定の情報を頻繁に抜き出したいときにも便利です。

- ツールのインストールが難しい環境でも、PowerShellが使える場合には有効な手段になりそうです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/d3db512c-be3c-4cbf-922a-fda6c7088e82.png)


## 動作確認環境

- OS: Windows 11  
- PowerShell: 5.1  
- .NET Framework（Windows 標準搭載）

> **補足**: PowerShell 7 でも `Microsoft.PowerShell.GraphicalTools` をインストールすれば  
> 動作する見込みがありますが、筆者環境では未検証です。

## Out-GridViewコマンドレット

PowerShellには標準で`Out-GridView`というコマンドレットが用意されており、簡単にグリッド表示を実現できます。

### 基本的な使い方

```powershell
# プロセス一覧をグリッド表示
Get-Process | Out-GridView
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/1fb67076-2cf2-4b1f-b331-b20e1a9f9a56.png)


### フィルタリング機能付きグリッド

```powershell
# サービス一覧を表示（フィルタリング可能）
Get-Service | Out-GridView -Title "サービス一覧"
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/cdf935cf-5909-4d72-b98e-cb443a7455b9.png)

### 選択結果を変数に格納

```powershell
# 複数選択可能なグリッドから選択したアイテムを取得
$selectedItems = Get-Process | Out-GridView -PassThru -Title "プロセスを選択してください"
$selectedItems | ForEach-Object { Write-Host "選択されたプロセス: $($_.Name)" }
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/85e1cc4b-0acb-44aa-a53f-3c0c6ce732cf.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/ac80ec0d-c1dc-4fe8-962f-972eccc9822a.png)

## Windows Formsを使用したカスタムグリッド

より高度なカスタマイズが必要な場合は、Windows FormsのDataGridViewを使用します。

### 基本的なDataGridViewの実装

```powershell
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

# フォームの作成
$form = New-Object System.Windows.Forms.Form
$form.Text = "PowerShell データグリッド"
$form.Size = New-Object System.Drawing.Size(800, 600)
$form.StartPosition = "CenterScreen"

# DataGridViewの作成
$dataGridView = New-Object System.Windows.Forms.DataGridView
$dataGridView.Size = New-Object System.Drawing.Size(760, 520)
$dataGridView.Location = New-Object System.Drawing.Point(20, 20)
$dataGridView.AutoSizeColumnsMode = "Fill"

# データの準備
$data = @(
    [PSCustomObject]@{ID=1; 名前="田中太郎"; 部署="営業部"; 年齢=28}
    [PSCustomObject]@{ID=2; 名前="鈴木花子"; 部署="開発部"; 年齢=32}
    [PSCustomObject]@{ID=3; 名前="佐藤次郎"; 部署="総務部"; 年齢=45}
)

# データをDataGridViewにバインド
$dataGridView.DataSource = [System.Collections.ArrayList]$data

# フォームにDataGridViewを追加
$form.Controls.Add($dataGridView)

# フォームを表示
$form.ShowDialog()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/e10b1900-fc03-41b4-a7cb-90802145eea6.png)

### CSVファイルの読み込みと表示

```powershell
# まず、サンプルデータをCSVファイルとして保存
$sampleData = @(
    [PSCustomObject]@{ID=1; 名前="田中太郎"; 部署="営業部"; 年齢=28}
    [PSCustomObject]@{ID=2; 名前="鈴木花子"; 部署="開発部"; 年齢=32}
    [PSCustomObject]@{ID=3; 名前="佐藤次郎"; 部署="総務部"; 年齢=45}
)

# CSVファイルとして保存
$csvPath = "$env:TEMP\employee_data.csv"
$sampleData | Export-Csv -Path $csvPath -NoTypeInformation -Encoding UTF8

Write-Host "サンプルCSVファイルを作成しました: $csvPath"

# CSVファイルを読み込んでグリッド表示する関数
function Show-CSVInGrid {
    param(
        [string]$FilePath
    )
    
    Add-Type -AssemblyName System.Windows.Forms
    Add-Type -AssemblyName System.Drawing
    
    # CSVデータの読み込み
    $csvData = Import-Csv -Path $FilePath -Encoding UTF8
    
    # フォームの作成
    $form = New-Object System.Windows.Forms.Form
    $form.Text = "CSV Viewer - $(Split-Path $FilePath -Leaf)"
    $form.Size = New-Object System.Drawing.Size(800, 600)
    $form.StartPosition = "CenterScreen"
    
    # DataGridViewの作成
    $dataGridView = New-Object System.Windows.Forms.DataGridView
    $dataGridView.Size = New-Object System.Drawing.Size(760, 520)
    $dataGridView.Location = New-Object System.Drawing.Point(20, 20)
    $dataGridView.AutoSizeColumnsMode = "Fill"
    $dataGridView.AllowUserToAddRows = $false
    
    # データテーブルの作成
    $dataTable = New-Object System.Data.DataTable
    
    # カラムの追加
    $csvData[0].PSObject.Properties | ForEach-Object {
        $dataTable.Columns.Add($_.Name) | Out-Null
    }
    
    # データの追加
    foreach ($row in $csvData) {
        $dataRow = $dataTable.NewRow()
        $row.PSObject.Properties | ForEach-Object {
            $dataRow[$_.Name] = $_.Value
        }
        $dataTable.Rows.Add($dataRow)
    }
    
    # DataGridViewにデータをバインド
    $dataGridView.DataSource = $dataTable
    
    # スタイル設定
    $dataGridView.AlternatingRowsDefaultCellStyle.BackColor = [System.Drawing.Color]::LightGray
    $dataGridView.ColumnHeadersDefaultCellStyle.Font = New-Object System.Drawing.Font("MS UI Gothic", 10, [System.Drawing.FontStyle]::Bold)
    
    # フォームにDataGridViewを追加
    $form.Controls.Add($dataGridView)
    
    # フォームを表示
    $form.ShowDialog()
}

# 作成したCSVファイルを表示
Show-CSVInGrid -FilePath $csvPath
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/dfadfcf5-495d-4596-8986-09ac4c1a68b7.png)


### 編集可能なグリッドとデータの保存

```powershell
function Show-EditableGrid {
    Add-Type -AssemblyName System.Windows.Forms
    Add-Type -AssemblyName System.Drawing
    
    # フォームの作成
    $form = New-Object System.Windows.Forms.Form
    $form.Text = "編集可能なデータグリッド"
    $form.Size = New-Object System.Drawing.Size(900, 650)
    $form.StartPosition = "CenterScreen"
    
    # DataGridViewの作成
    $dataGridView = New-Object System.Windows.Forms.DataGridView
    $dataGridView.Size = New-Object System.Drawing.Size(860, 550)
    $dataGridView.Location = New-Object System.Drawing.Point(20, 20)
    $dataGridView.AutoSizeColumnsMode = "Fill"
    $dataGridView.AllowUserToAddRows = $true
    $dataGridView.AllowUserToDeleteRows = $true
    
    # 初期データ
    $script:dataList = [System.Collections.ArrayList]@(
        [PSCustomObject]@{商品名="ノートPC"; 価格=98000; 在庫=15}
        [PSCustomObject]@{商品名="マウス"; 価格=2500; 在庫=50}
        [PSCustomObject]@{商品名="キーボード"; 価格=8000; 在庫=30}
    )
    
    $dataGridView.DataSource = $script:dataList
    
    # 保存ボタン
    $saveButton = New-Object System.Windows.Forms.Button
    $saveButton.Text = "CSVに保存"
    $saveButton.Size = New-Object System.Drawing.Size(100, 30)
    $saveButton.Location = New-Object System.Drawing.Point(780, 580)
    
    $saveButton.Add_Click({
        $saveDialog = New-Object System.Windows.Forms.SaveFileDialog
        $saveDialog.Filter = "CSV files (*.csv)|*.csv|All files (*.*)|*.*"
        $saveDialog.DefaultExt = "csv"
        
        if ($saveDialog.ShowDialog() -eq "OK") {
            $script:dataList | Export-Csv -Path $saveDialog.FileName -NoTypeInformation -Encoding UTF8
            [System.Windows.Forms.MessageBox]::Show("保存しました", "情報")
        }
    })
    
    # コントロールをフォームに追加
    $form.Controls.Add($dataGridView)
    $form.Controls.Add($saveButton)
    
    # フォームを表示
    $form.ShowDialog()
}

# 実行
Show-EditableGrid
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/cded32a0-9735-4690-be03-72b7edfebb4f.png)

保存ボタンで出力できる

## WPFを使用したモダンなグリッド

より洗練されたUIが必要な場合は、WPFのDataGridを使用します。

```powershell
Add-Type -AssemblyName PresentationFramework
Add-Type -AssemblyName PresentationCore
Add-Type -AssemblyName WindowsBase

[xml]$xaml = @"
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="WPF データグリッド" Height="600" Width="900">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="50"/>
        </Grid.RowDefinitions>
        
        <DataGrid Name="dataGrid" Grid.Row="0" Margin="10"
                  AutoGenerateColumns="True"
                  CanUserAddRows="True"
                  CanUserDeleteRows="True"
                  GridLinesVisibility="All"
                  AlternatingRowBackground="#F0F0F0"/>
        
        <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right" Margin="10">
            <Button Name="loadButton" Content="データ読込" Width="100" Margin="5"/>
            <Button Name="saveButton" Content="保存" Width="100" Margin="5"/>
        </StackPanel>
    </Grid>
</Window>
"@

$reader = (New-Object System.Xml.XmlNodeReader $xaml)
$window = [Windows.Markup.XamlReader]::Load($reader)

# コントロールの取得
$dataGrid = $window.FindName("dataGrid")
$loadButton = $window.FindName("loadButton")
$saveButton = $window.FindName("saveButton")

# サンプルデータ
$employees = @(
    [PSCustomObject]@{社員ID="E001"; 氏名="山田太郎"; 部署="営業部"; 入社年=2018}
    [PSCustomObject]@{社員ID="E002"; 氏名="田中花子"; 部署="開発部"; 入社年=2020}
    [PSCustomObject]@{社員ID="E003"; 氏名="佐藤健"; 部署="人事部"; 入社年=2015}
)

$dataGrid.ItemsSource = $employees

# イベントハンドラ
$loadButton.Add_Click({
    [System.Windows.MessageBox]::Show("データ読込機能を実装してください", "情報")
})

$saveButton.Add_Click({
    [System.Windows.MessageBox]::Show("保存機能を実装してください", "情報")
})

$window.ShowDialog()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/8befbe23-2620-4f0d-b16b-506980d6629f.png)

## 実用的な Tips（簡易まとめ）

参考情報からの備忘録で自分では未検証です。興味ありましたら関係ありそうな参考情報を参照ください。

1. **大量データを高速表示**

   * WinForms `DataGridView` では `VirtualMode = $true` + `CellValueNeeded` 実装で桁違いに軽く

2. **カラムカスタマイズ**

   * 読み取り専用: `$dg.Columns["ID"].ReadOnly = $true`
   * 自動幅調整: `$dg.AutoResizeColumns()`

3. **セル書式・色分け**

   * 通貨書式: `$dg.Columns["価格"].DefaultCellStyle.Format = "C0"`
   * 条件付き色付け (在庫 < 10): `CellFormatting` イベントで `BackColor = LightPink`

4. **クリップボード連携**

   ```powershell
   Get-Clipboard | ConvertFrom-Csv | Out-GridView -Title "Clipboard CSV"
   ```

5. **ビューアとして待機 (-Wait)**

   ```powershell
   Get-EventLog -LogName System -Newest 500 |
       Out-GridView -Title "最新 500 件の System ログ" -Wait
   ```

6. **GUI が使えない環境の代替**

   * `Format-Table | more` でページング表示
   * VS Code PowerShell Notebook のテーブル表示も有効

---

## まとめ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/d705d7ed-a2ac-4e93-8696-d6458fd196a0.png)


PowerShellでExcelのようなグリッド画面を作成する方法をいくつか紹介しました。

- Out-GridView: 最も簡単で、基本的な表示・フィルタリングには十分
- Windows Forms: より細かいカスタマイズが可能で、編集機能も実装しやすい
- WPF: モダンなUIを実現でき、データバインディングが強力

用途に応じて適切な方法を選択することで、PowerShellでも使いやすいデータ管理ツールを作成できます。

## 参考リンク

- [Out-GridView (Microsoft Docs)](https://docs.microsoft.com/ja-jp/powershell/module/microsoft.powershell.utility/out-gridview)
- [DataGridView クラス (Microsoft Docs)](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.datagridview)
- [WPF DataGrid (Microsoft Docs)](https://docs.microsoft.com/ja-jp/dotnet/desktop/wpf/controls/datagrid)

- [大量データを高速表示](https://posh-able.com/tag/powershell/#:~:text=%2A%20Get)

https://learn.microsoft.com/en-us/dotnet/desktop/winforms/controls/implementing-virtual-mode-wf-datagridview-control

https://learn.microsoft.com/en-us/dotnet/desktop/winforms/controls/how-to-make-columns-read-only-in-the-windows-forms-datagridview-control

https://learn.microsoft.com/en-us/previous-versions/dotnet/netframework-4.0/dwhawy9k(v=vs.100)?WT.mc_id=DT-MVP-5003235

https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/get-clipboard?view=powershell-7.5

https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/out-gridview?view=powershell-7.5

https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/out-host?view=powershell-7.5