超シンプルに、JSONファイルを読み込んでビューに表示するPowershellアプリをxamlを活用して作ってみる

PS1ファイル

# Requires -Version 5.1
param(
    [string]$JsonPath = ".\tasks.json"
)

# ===== STA でなければ自動再起動 =====
if ([Threading.Thread]::CurrentThread.ApartmentState -ne 'STA') {
    $argsList = @('-sta','-NoProfile','-ExecutionPolicy','Bypass','-File', $PSCommandPath)
    if ($PSBoundParameters.Count) { $PSBoundParameters.GetEnumerator() | % { $argsList += @("-$($_.Key)","$($_.Value)") } }
    Start-Process -FilePath (Get-Command powershell).Source -ArgumentList $argsList | Out-Null
    exit
}

# ===== WPF 必要アセンブリ =====
Add-Type -AssemblyName PresentationCore, PresentationFramework, WindowsBase -ErrorAction SilentlyContinue
Add-Type -AssemblyName System.Windows.Forms -ErrorAction SilentlyContinue

# ===== XAML ロード =====
$XamlPath = Join-Path $PSScriptRoot 'SimpleView.xaml'
if (-not (Test-Path -LiteralPath $XamlPath)) {
    throw "XAML が見つかりません。$XamlPath"
}
[xml]$xaml = Get-Content -LiteralPath $XamlPath -Raw -Encoding UTF8
$reader = New-Object System.Xml.XmlNodeReader $xaml
$Window = [Windows.Markup.XamlReader]::Load($reader)

# ===== UI 参照 =====
function Find-Ui([string]$name) {
    $el = $Window.FindName($name)
    if (-not $el) { $el = [System.Windows.LogicalTreeHelper]::FindLogicalNode($Window, $name) }
    return $el
}
$TxtJsonPath = Find-Ui 'TxtJsonPath'
$BtnBrowse   = Find-Ui 'BtnBrowse'
$BtnLoad     = Find-Ui 'BtnLoad'
$BtnClose    = Find-Ui 'BtnClose'
$Grid        = Find-Ui 'Grid'
$TxtCount    = Find-Ui 'TxtCount'

function Read-JsonData([string]$Path) {
    if (-not (Test-Path -LiteralPath $Path)) { return @() }
    try {
        $raw = Get-Content -LiteralPath $Path -Raw -Encoding UTF8
        $doc = $raw | ConvertFrom-Json

        # 1) 配列ならそのまま
        if ($doc -is [System.Collections.IEnumerable] -and -not ($doc -is [string])) {
            return @($doc)
        }

        # 2) オブジェクト内に“配列プロパティ”があればそれを使う(例: TASK_DATA)
        $arrProp = ($doc.PSObject.Properties | Where-Object {
            $_.Value -is [System.Collections.IEnumerable] -and -not ($_.Value -is [string])
        } | Select-Object -First 1)

        if ($arrProp) { return @($arrProp.Value) }

        # 3) どれでもなければ、そのオブジェクト1件を表示
        return @($doc)
    }
    catch {
        [System.Windows.MessageBox]::Show("JSON読み込みに失敗: $($_.Exception.Message)","Error","OK","Error") | Out-Null
        return @()
    }
}


function Bind-Data([object[]]$items) {
    # AutoGenerateColumns=True なので配列のままバインドでOK
    $Grid.ItemsSource = $null
    $Grid.ItemsSource = $items
    $TxtCount.Text = [string](@($items).Count)
    $Window.Title = "JSON Viewer - $(@($items).Count) rows"
}

# ===== 参照ボタン =====
$BtnBrowse.Add_Click({
    $ofd = New-Object System.Windows.Forms.OpenFileDialog
    $ofd.Filter = "JSON (*.json)|*.json|All Files (*.*)|*.*"
    $ofd.Multiselect = $false
    if ($TxtJsonPath.Text -and (Test-Path $TxtJsonPath.Text)) {
        $ofd.InitialDirectory = Split-Path -Path $TxtJsonPath.Text -Parent
    }
    if ($ofd.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
        $TxtJsonPath.Text = $ofd.FileName
    }
})

# ===== 読み込みボタン =====
$BtnLoad.Add_Click({
    $path = $TxtJsonPath.Text
    $items = Read-JsonData -Path $path
    Bind-Data -items $items
})

# ===== 閉じる =====
$BtnClose.Add_Click({ $Window.Close() })

# 既定パスが指定されていれば自動表示
if ($JsonPath) {
    $TxtJsonPath.Text = (Resolve-Path -LiteralPath $JsonPath -ErrorAction SilentlyContinue).Path
    if ($TxtJsonPath.Text) {
        Bind-Data -items (Read-JsonData -Path $TxtJsonPath.Text)
    }
}

# ===== 起動 =====
$null = $Window.ShowDialog()

XAMLファイル

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="JSON Viewer" Height="520" Width="880" Background="#0F0F10"
        WindowStartupLocation="CenterScreen">
  <Window.Resources>
    <Style TargetType="TextBlock">
      <Setter Property="Foreground" Value="White"/>
    </Style>
    <Style TargetType="Label">
      <Setter Property="Foreground" Value="White"/>
    </Style>
  </Window.Resources>

  <Grid Margin="12">
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto"/>
      <RowDefinition Height="*"/>
      <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>

    <!-- ファイル指定行 -->
    <DockPanel Grid.Row="0" LastChildFill="True" Margin="0,0,0,8">
      <Label Content="JSON Path:" VerticalAlignment="Center" Margin="0,0,8,0"/>
      <!-- ★ Text を空 or 属性自体を削除 -->
      <TextBox x:Name="TxtJsonPath" MinWidth="400" Margin="0,0,8,0" Text=""/>
      <Button x:Name="BtnBrowse" Content="参照" Width="80" Margin="0,0,8,0"/>
      <Button x:Name="BtnLoad" Content="読み込み" Width="100"/>
    </DockPanel>

    <DataGrid x:Name="Grid"
          Grid.Row="1"
          AutoGenerateColumns="True"
          CanUserAddRows="False"
          CanUserDeleteRows="False"
          GridLinesVisibility="Horizontal"
          Background="#15161B"
          Foreground="White"
          RowBackground="#1E1F25"
          AlternatingRowBackground="#20242A"
          BorderBrush="#2A2D33" BorderThickness="1"
          HeadersVisibility="All"
          ColumnHeaderHeight="28"
          ScrollViewer.CanContentScroll="True"
          ScrollViewer.VerticalScrollBarVisibility="Auto"
          ScrollViewer.HorizontalScrollBarVisibility="Auto">

      <!-- ヘッダーをダークテーマ化 -->
      <DataGrid.ColumnHeaderStyle>
        <Style TargetType="DataGridColumnHeader">
          <Setter Property="Background" Value="#1A1D22"/>
          <Setter Property="Foreground" Value="#C2C7CF"/>
          <Setter Property="FontWeight" Value="SemiBold"/>
          <Setter Property="BorderBrush" Value="#2A2D33"/>
          <Setter Property="BorderThickness" Value="0,0,0,1"/>
          <Setter Property="HorizontalContentAlignment" Value="Center"/>
        </Style>
      </DataGrid.ColumnHeaderStyle>

      <!-- セルスタイルも合わせて調整 -->
      <DataGrid.CellStyle>
        <Style TargetType="DataGridCell">
          <Setter Property="Background" Value="Transparent"/>
          <Setter Property="BorderBrush" Value="#2A2D33"/>
          <Setter Property="BorderThickness" Value="0,0,0,1"/>
          <Style.Triggers>
            <Trigger Property="IsSelected" Value="True">
              <Setter Property="Background" Value="#223047"/>
              <Setter Property="Foreground" Value="White"/>
            </Trigger>
          </Style.Triggers>
        </Style>
      </DataGrid.CellStyle>
    </DataGrid>


    <!-- フッター(件数) -->
    <DockPanel Grid.Row="2" Margin="0,8,0,0">
      <TextBlock Text="Rows:" VerticalAlignment="Center"/>
      <TextBlock x:Name="TxtCount" Margin="6,0,0,0" VerticalAlignment="Center"/>
      <TextBlock Text="(AutoGenerateColumns=True:JSONキー=列ヘッダー)"
                 Margin="16,0,0,0" Opacity="0.7" VerticalAlignment="Center"/>
      <Button x:Name="BtnClose" Content="閉じる" Width="90" DockPanel.Dock="Right"/>
    </DockPanel>
  </Grid>
</Window>

JSON※おためしデータ

{
    "TASK_DATA": [
        {
            "task_id": "1",
            "タスク名": "TASK1",
            "タスク_URL": "TASK_URL1",
            "発生日": "2025/10/11",
            "期日": "2025/10/14",
            "ステータス" : "対応中"
        },
        {
            "task_id": "2",
            "タスク名": "TASK2",
            "タスク_URL": "TASK_URL2",
            "発生日": "2025-10-01",
            "期日": "2025/10/15",
            "ステータス" : "完了"
        },
        {
            "task_id": "3",
            "タスク名": "TASK3",
            "タスク_URL": "TASK_URL3",
            "発生日": "2025-10-04",
            "期日": "2025/10/19",
            "ステータス" : "受付"
        }
    ]
}

まとめ

XAMLでレイアウトを作成する練習もかねて、

超シンプルに、JSONファイルを読み込んでビューに表示するアプリをPowershellで作成してみました。

初めから特定のJSONファイルを読み込ませた状態でビューの起動をするように機能修正するも良し、アイテムをクリックしたらイベントが発火するように機能追加するも良し。

夢が広がりますね。

スポンサーリンク

-IT関連
-,