自由テーマ

Tech

PowerShellでWPF GUIを開発する際、XAMLによる宣言的UI定義とデータバインディングの活用は、コードの可読性と保守性を向上させる。これにより、UIとロジックの分離が促進され、開発効率とメンテナンス性が向上する。

XAMLによるUI定義の基本

WPFでは、GUIのレイアウトやコントロールの定義をXAML(Extensible Application Markup Language)で行うことが推奨される。これにより、UIの構造が視覚的に理解しやすくなり、C#やPowerShellといったコードビハインドからUI定義が分離される。

まず、基本的なWPFウィンドウのXAMLファイルを定義する。

<!-- MainWindow.xaml -->
<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="PowerShell WPF Demo" Height="200" Width="300">
    <StackPanel Margin="10">
        <TextBlock Text="入力してください:" Margin="0,0,0,5"/>
        <TextBox x:Name="InputTextBox" Width="200" HorizontalAlignment="Left" Margin="0,0,0,10"/>
        <Button Content="表示" Width="80" HorizontalAlignment="Left"/>
        <TextBlock x:Name="OutputTextBlock" Margin="0,10,0,0" FontWeight="Bold"/>
    </StackPanel>
</Window>

PowerShellからこのXAMLを読み込み、ウィンドウを表示する。

# PowerShellスクリプト
Add-Type -AssemblyName PresentationFramework, PresentationCore, WindowsBase, System.Xaml

$xamlPath = ".\MainWindow.xaml"
$xaml = [System.IO.File]::ReadAllText($xamlPath)
$reader = New-Object System.Xml.XmlNodeReader($xaml)
$window = [System.Windows.Markup.XamlReader]::Load($reader)

# ウィンドウ内のコントロールへのアクセス
# $inputTextBox = $window.FindName("InputTextBox")
# $outputTextBlock = $window.FindName("OutputTextBlock")

$window.ShowDialog() | Out-Null

データバインディングの導入

データバインディングは、UI要素のプロパティとデータオブジェクトのプロパティを関連付けるメカニズムである。これにより、データ変更時にUIが自動的に更新され、UI操作時にデータが更新される。DataContextプロパティがバインディングのソースとなるデータオブジェクトを指定する。

ViewModelの作成

PowerShellでデータバインディングのソースとなるオブジェクト(ViewModel)を作成する。変更通知をUIに伝達するためには、INotifyPropertyChangedインターフェースを実装する必要がある。

# INotifyPropertyChangedを実装したViewModelクラス定義
Add-Type -TypeDefinition @"
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;

public class MyViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private string _inputText;
    public string InputText
    {
        get { return _inputText; }
        set
        {
            if (_inputText != value)
            {
                _inputText = value;
                OnPropertyChanged();
            }
        }
    }

    private string _displayText;
    public string DisplayText
    {
        get { return _displayText; }
        set
        {
            if (_displayText != value)
            {
                _displayText = value;
                OnPropertyChanged();
            }
        }
    }

    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
"@

XAMLの更新とDataContextの設定

XAMLを更新し、TextBoxTextBlockにデータバインディングを設定する。Text="{Binding PropertyName}"の形式で指定する。

<!-- MainWindow.xaml (データバインディング対応版) -->
<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="PowerShell WPF Demo" Height="200" Width="300">
    <StackPanel Margin="10">
        <TextBlock Text="入力してください:" Margin="0,0,0,5"/>
        <TextBox Text="{Binding InputText, UpdateSourceTrigger=PropertyChanged}" Width="200" HorizontalAlignment="Left" Margin="0,0,0,10"/>
        <Button Content="表示" Width="80" HorizontalAlignment="Left" Click="Button_Click"/>
        <TextBlock Text="{Binding DisplayText}" Margin="0,10,0,0" FontWeight="Bold"/>
    </StackPanel>
</Window>

PowerShellスクリプトでViewModelインスタンスを作成し、ウィンドウのDataContextに設定する。ボタンクリックイベントはコードビハインドで処理する。

# PowerShellスクリプト (データバインディング対応版)
Add-Type -AssemblyName PresentationFramework, PresentationCore, WindowsBase, System.Xaml

# MyViewModelクラス定義は上記Add-Typeで実行済みとする

$xamlPath = ".\MainWindow.xaml"
$xaml = [System.IO.File]::ReadAllText($xamlPath)
$reader = New-Object System.Xml.XmlNodeReader($xaml)
$window = [System.Windows.Markup.XamlReader]::Load($reader)

# ViewModelのインスタンスを作成
$viewModel = New-Object MyViewModel
$viewModel.InputText = "初期値" # 初期値の設定

# ウィンドウのDataContextにViewModelを設定
$window.DataContext = $viewModel

# ボタンクリックイベントハンドラを定義
$button = $window.FindName("Button") # XAMLにx:Name="Button"を追加するか、FindNameで探す
# 今回のXAMLではx:Nameが無いので、StackPanelの子要素からButtonを探す
$button = $window.Content.Children | Where-Object { $_ -is [System.Windows.Controls.Button] }

# XAMLのClick属性をButton_Clickにしているため、動的にイベントハンドラを追加する
# ここでは匿名デリゲートでイベントを処理する
$button.Add_Click({
    $viewModel.DisplayText = $viewModel.InputText
})

$window.ShowDialog() | Out-Null

データバインディングの流れ

データバインディングの基本的な流れは以下の通りである。

graph LR
    A["UI要素 (TextBox.Text)"] -->|Binding| B(ViewModel.InputText)
    B -->|INotifyPropertyChanged| C["UI要素 (TextBlock.Text)"]
    D["ユーザー入力"] --> A
    B -->|データ変更| C

この構成により、UI要素のTextプロパティがViewModelInputTextプロパティにバインドされる。TextBoxへの入力は自動的にInputTextへ反映され(UpdateSourceTrigger=PropertyChanged)、InputTextが変更されるとINotifyPropertyChangedを通じてUIに通知され、必要に応じてTextBlockDisplayTextプロパティが更新される。ボタンクリック時には、InputTextの値をDisplayTextに代入することで、TextBlockに表示が反映される。

まとめ

PowerShellでWPF GUIを構築する際にXAMLとデータバインディングを組み合わせることで、UIとビジネスロジックの分離が実現し、コードの保守性、可読性、テスト容易性が向上する。INotifyPropertyChangedを実装したViewModelを使用することで、UIの自動更新が可能となり、より宣言的で効率的なGUI開発が可能となる。

ライセンス:本記事のテキスト/コードは特記なき限り CC BY 4.0 です。引用の際は出典URL(本ページ)を明記してください。
利用ポリシー もご参照ください。

コメント

タイトルとURLをコピーしました