参考

https://github.com/Exathi/Powershell-WPF

簡単なハンズオンで学ぶWPFのデータバインディングからのMVVM

構成

/
├── CreateClassInstanceHelper.psm1          # PowerShellクラスのインスタンス生成を支援するモジュール
│   ├── $Script:powershell                 # PowerShellインスタンスを保持するグローバル変数
│   ├── $Script:body                       # クラスインスタンス生成用の関数定義を含む文字列
│   ├── function Initialize()              # PowerShellインスタンスの初期化とスクリプト実行関数
│   └── function New-UnboundClassInstance( # ランスペースに束縛されていないクラスインスタンスを生成
│       [Type] $type,                      # 生成するクラスの型
│       [object[]] $arguments              # コンストラクタの引数配列
│       )
│
├── WPFClassHelpers.psm1                   # WPFアプリケーション構築支援モジュール
│   ├── function New-WPFObject(            # XAMLからWPFオブジェクトを生成する関数
│   │   [string] $Path,                    # XAMLファイルのパス
│   │   [string] $Xaml,                    # XAML文字列
│   │   [string] $BaseUri                  # リソース参照の基準URI
│   │   )
│   └── class ThreadManager                # 非同期処理を管理するクラス
│       ├── $RunspacePool                  # PowerShellコマンドを実行するためのプール
│       ├── $DisposeTaskDelegate           # リソース解放用デリゲート
│       ├── constructor(                   # 初期化処理
│       │   [string]$FunctionName
│       │   )
│       ├── Dispose()                      # リソースの解放
│       ├── [object]Async(                 # 非同期実行メソッド(オーバーロード3種)
│       │   [scriptblock]$scriptblock,     # 実行するスクリプトブロック
│       │   [Delegate]$MethodToRunAsync,   # 実行するメソッド
│       │   [Delegate]$Callback            # 完了時のコールバック
│       │   )
│       └── [Delegate]CreateDelegate(      # デリゲート生成メソッド
│           [PSMethod]$Method,             # 対象メソッド
│           $Target                        # ターゲットオブジェクト
│           )
│
├── ViewModel.ps1                          # MVVMパターンのViewModelクラス定義
│   └── class MyViewModel : ViewModelBase  # UIとデータを仲介するクラス
│       ├── $SharedResource               # 共有リソース値
│       ├── $SharedResourceLock           # 共有リソースのロックオブジェクト
│       ├── $Jobs                         # 実行中ジョブのコレクション
│       ├── $JobsLock                     # ジョブコレクションのロックオブジェクト
│       ├── $AddTenSlowlyCommand          # 数値を10増やすコマンド
│       ├── $ExternalMethodCommand        # 外部メソッド実行コマンド
│       ├── $CmdletInMethodCommand        # Cmdlet実行コマンド
│       └── constructor()                 # 初期化処理
│
├── Views/                                # UI定義ファイル群
│   ├── Common.xaml                       # 共通リソース定義
│   │   ├── ResourceDictionary            # リソース辞書
│   │   ├── RightMarginConverter         # マージン計算用コンバーター
│   │   ├── Colors                       # 色定義
│   │   ├── Styles                       # スタイル定義
│   │   │   ├── TextBlockShowCase        # 表示用テキストブロック
│   │   │   ├── TextBlockLabel          # ラベル用テキストブロック
│   │   │   ├── TextBlockData           # データ表示用テキストブロック
│   │   │   ├── TitleBarButton         # タイトルバーボタン
│   │   │   └── TitleBarButtonRestore  # 復元ボタン
│   │   ├── ControlTemplates            # コントロールテンプレート
│   │   └── StreamGeometry             # 図形定義
│   │
│   ├── MainWindow.xaml                 # メインウィンドウ定義
│   │   └── Window                      # ウィンドウ要素
│   │       ├── Resources              # ウィンドウリソース
│   │       └── Grid                   # メインレイアウトグリッド
│   │
│   └── PartialWindow.xaml             # カスタムウィンドウ定義
│       └── local:PartialWindow        # カスタムウィンドウ要素
│           ├── Resources             # ウィンドウリソース
│           └── WindowChrome          # ウィンドウ装飾定義
│
└── SampleGUI.ps1                     # アプリケーションのエントリーポイント
    ├── モジュールのインポート          # 必要なモジュールの読み込み
    ├── $ThreadManager                # スレッド管理インスタンス
    ├── $MyViewModel                  # ViewModelインスタンス
    └── WPFウィンドウの初期化と表示     # UIの構築と表示

クラス図

classDiagram
    %% ViewModels フォルダ
    namespace ViewModelsフォルダ {
        class ViewModelBase {
            +PropertyChanged : Event
            +add_PropertyChanged(handler) : void
            +remove_PropertyChanged(handler) : void
            +RaisePropertyChanged(propertyName) : void
            
            note "UIの更新通知の基盤となるクラス
            - プロパティの変更を検知してUIに通知
            - データバインディングの基本機能を提供
            - すべてのViewModelの親クラス"
        }

        class ActionCommand {
            +CanExecute(parameter) : bool
            +Execute(parameter) : void
            +RaiseCanExecuteChanged() : void
            -$Action : Action
            -$ActionObject : object
            -$CanExecuteAction : Func
            -$ThreadManager : ThreadManager
            -$Workers : int
            -$Throttle : int
            
            note "UIのアクション(ボタンクリックなど)を処理
            - 非同期処理のサポート
            - スロットリング制御
            - 実行可否の動的制御
            - マルチスレッド対応"
        }
    }

    %% Helpers フォルダ
    namespace Helpers {
        class ThreadManager {
            +Dispose() : void
            +Async(methodToRunAsync, callback) : Task
            +CreateDelegate(method, target) : Delegate
            -$SharedPoolVars : object
            -$RunspacePool : RunspacePool
            
            note "非同期処理の管理クラス
            - PowerShellランスペースの管理
            - スレッドプールの制御
            - 非同期タスクの実行と監視
            - リソースの適切な解放"
        }
    }

    %% 継承関係
    ActionCommand --|> ViewModelBase : 継承
    ActionCommand --|> System.Windows.Input.ICommand : 実装
    ThreadManager --|> System.IDisposable : 実装

    %% 依存関係
    ActionCommand ..> ThreadManager : 使用
classDiagram
    class ViewModelBase {
        +PropertyChanged : Event
        +add_PropertyChanged(handler) : void
        +remove_PropertyChanged(handler) : void
        +RaisePropertyChanged(propertyName) : void
        %% ViewModelBase (ビュー・モデル・ベース): 
        %% プロパティの変更を通知するための基礎クラスです。
        %% INotifyPropertyChangedインターフェースを実装しており、
        %% UIにプロパティの変更を通知します。
    }

    class ActionCommand {
        +CanExecute(parameter) : bool
        +Execute(parameter) : void
        +RaiseCanExecuteChanged() : void
        -$Action : Action
        -$ActionObject : object
        -$CanExecuteAction : Func
        -$ThreadManager : ThreadManager
        -$Workers : int
        -$Throttle : int
        -$InvokeCanExecuteChangedDelegate : Delegate
        -$Dispatcher : Dispatcher
        %% ActionCommand (アクション・コマンド): 
        %% WPFのコマンドパターンを実装し、UIアクションを処理するクラスです。
        %% ViewModelBaseを継承し、ICommandインターフェースを実装しています。
    }

    class ThreadManager {
        +Dispose() : void
        +Async(methodToRunAsync, callback) : Task
        +CreateDelegate(method, target) : Delegate
        -$SharedPoolVars : object
        -$DisposeTaskDelegate : Delegate
        -$RunspacePool : RunspacePool
        %% ThreadManager (スレッド・マネージャー): 
        %% 非同期処理や並列処理を管理するクラスです。
        %% IDisposableインターフェースを実装しており、リソースの解放を行います。
    }

    ActionCommand --|> ViewModelBase : 継承
    ActionCommand --|> System.Windows.Input.ICommand
    ThreadManager --|> System.IDisposable
classDiagram

    class New-UnboundClassInstance {
        +Initialize() : void
        +New-UnboundClassInstance(type, arguments) : object
        %% New-UnboundClassInstance (ニュー・アンバウンド・クラス・インスタンス): 
        %% PowerShellクラスのインスタンスを作成するためのヘルパークラスです。
        %% ランスペースに束縛されないインスタンスを作成します。
    }

    class SampleGUI {
        +MainWindow : Window
        +PartialWindow : Window
        %% SampleGUI (サンプルGUI): 
        %% GUIアプリケーションのエントリーポイントとなるクラスです。
        %% MainWindowやPartialWindowを表示します。
    }

classDiagram

    class MainWindow {
        +MainGrid : Grid
        +TitleBar : UIElement
        +JobGrid : DataGrid
        %% MainWindow (メイン・ウィンドウ): 
        %% アプリケーションのメインウィンドウを定義するクラスです。
        %% UI要素やレイアウトを含みます。
    }

    class PartialWindow {
        +MainGrid : Grid
        +TitleBar : UIElement
        +JobGrid : DataGrid
        %% PartialWindow (パーシャル・ウィンドウ): 
        %% 特定のUIレイアウトを持つウィンドウを定義するクラスです。
    }

    class Common {
        +Styles : ResourceDictionary
        +Resources : ResourceDictionary
        %% Common (コモン): 
        %% アプリケーション全体で使用されるスタイルやリソースを定義するクラスです。
    }

フローチャート

flowchart TD
    A[アプリケーション開始] --> B[New-UnboundClassInstanceで<br>クラスインスタンス作成<br>CreateClassInstanceHelper.psm1]

    %% 各ステップの説明
    B -->|PowerShellクラスの<br>インスタンスを作成| C[ViewModelBaseの<br>プロパティ変更通知設定<br>ViewModel.ps1]
    C -->|プロパティ変更をUIに通知| D[ActionCommandの<br>コマンド設定]
    D -->|UIアクションを処理| E[ThreadManagerで<br>非同期処理設定]
    E -->|非同期処理を管理| F[SampleGUIでGUI表示]
    F -->|GUIアプリケーションの<br>エントリーポイント| G[MainWindowの<br>UI要素設定]
    G -->|メインウィンドウの設定| H[PartialWindowの<br>UI要素設定]
    H -->|特定のUIレイアウトの設定| I[アプリケーション終了]
flowchart TD
    A[アプリケーション開始] --> B

    subgraph SampleGUI.ps1
    B[SampleGUI.ps1 メイン処理]
    B --> C[モジュールとアセンブリの読み込み]
    C --> D[New-WPFObject]
    D --> E[ウィンドウの表示]
    end

    subgraph CreateClassInstanceHelper.psm1
    F[New-UnboundClassInstance]
    end

    subgraph ViewModel.ps1
    G[ViewModelBase クラス]
    H[ActionCommand クラス]
    I[ThreadManager クラス]
    J[RightMarginConverter クラス]
    end

    subgraph WPFClassHelpers.psm1
    K[New-WPFObject]
    end

    subgraph XAML ファイル
        subgraph MainWindow.xaml
        L[MainWindow]
        end

        subgraph PartialWindow.xaml
        M[PartialWindow]
        end

        subgraph Common.xaml
        N[共通リソース]
        end
    end

    B --> F
    B --> G
    B --> H
    B --> I
    B --> J
    D --> K
    E --> L
    E --> M
    L --> N
    M --> N

    subgraph UI コンポーネント
    O[メニュー]
    P[グループボックス]
    Q[DataGrid]
    end

    M --> O
    M --> P
    M --> Q

    R[アプリケーション終了]
    E --> R

SampleGUI.ps1

        flowchart TB
            Start([開始]) --> ModuleImport[モジュールのインポート<br>using module/namespace]
            
            subgraph Init[初期化処理]
                ModuleImport --> AssemblyLoad[アセンブリの動的読み込み<br>Add-Type]
                AssemblyLoad --> ModuleLoad[モジュールの読み込み<br>Import-Module/ドット演算子]
                ModuleLoad --> ThreadMgr[ThreadManager<br>インスタンス作成]
                ThreadMgr --> ViewModel[ViewModel<br>インスタンス作成]
                ViewModel --> CreateBtn[ボタンの作成]
            end

            Init --> EnableSync[Jobsコレクションの<br>同期有効化]

            subgraph WindowSetup[ウィンドウ設定]
                EnableSync --> CreateWPF[WPFオブジェクト作成<br>New-WPFObject]
                CreateWPF --> SetContext[DataContext設定]
            end

            WindowSetup --> ShowDialog[ダイアログ表示]
            ShowDialog --> WaitClose{ウィンドウが<br>閉じられる?}
            WaitClose -->|No| ShowDialog
            WaitClose -->|Yes| ShowResult[ジョブ結果の表示<br>Format-Table]
            ShowResult --> End([終了])

            %% コメントアウトされている処理を点線で表示
            style EventHandler stroke-dasharray: 5 5
            WaitClose -.-> EventHandler[/Closingイベントハンドラ/]
            EventHandler -.-> Dispose[ThreadManagerの<br>Dispose]

ViewModel.ps1

flowchart TB
    subgraph MyViewModel[MyViewModelクラス]
        direction TB
        Init[初期化処理] --> Props[プロパティ定義]
        Props --> |初期化| SharedRes[共有リソース管理]
        Props --> |初期化| Jobs[ジョブ管理]
        Props --> |初期化| Calc[計算サービス]
        
        subgraph Commands[コマンド管理]
            Delegates[デリゲート定義] --> CmdBinding[コマンドバインディング]
            CmdBinding --> AddTen[AddTenSlowlyCommand]
            CmdBinding --> External[ExternalMethodCommand]
            CmdBinding --> Cmdlet[CmdletInMethodCommand]
        end
    end

    subgraph PartialWindow[PartialWindowクラス]
        direction TB
        Constructor[コンストラクタ] --> SystemCmd[システムコマンド<br>バインディング]
        
        SystemCmd --> ShowMenu[ShowSystemMenu]
        SystemCmd --> Minimize[MinimizeWindow]
        SystemCmd --> Maximize[MaximizeWindow]
        SystemCmd --> Restore[RestoreWindow]
        SystemCmd --> Close[CloseWindow]
        
        subgraph WindowOps[ウィンドウ操作]
            ShowMenu --> MenuPopup{システムメニュー<br>表示}
            Minimize --> MinState{最小化}
            Maximize --> MaxState{最大化}
            Restore --> NormalState{通常サイズ}
            Close --> CloseWin{ウィンドウを<br>閉じる}
        end
    end

    style MyViewModel fill:#f9f,stroke:#333,stroke-width:2px
    style PartialWindow fill:#bbf,stroke:#333,stroke-width:2px
    style Commands fill:#ffe,stroke:#333,stroke-width:1px
    style WindowOps fill:#efe,stroke:#333,stroke-width:1px

WPFClassHelpers.psm1

flowchart TB
    subgraph New-WPFObject[New-WPFObject関数]
        direction TB
        Start([開始]) --> CheckParams{パラメータ<br>チェック}
        
        CheckParams -->|Path指定| ReadFile[ファイルから<br>XAMLを読み込み]
        CheckParams -->|Xaml指定| UseXaml[XAMLを<br>直接使用]
        
        subgraph ParseProcess[XAMLパース処理]
            ReadFile --> CheckDynamic{Dynamic<br>モード?}
            UseXaml --> CheckDynamic
            
            CheckDynamic -->|Yes| SetupContext[ParserContextの<br>設定]
            SetupContext --> ParseWithContext[XAMLReaderで<br>パース]
            
            CheckDynamic -->|No| ParseDirect[直接パース]
        end
        
        ParseWithContext --> ReturnObj[WPFオブジェクト<br>を返す]
        ParseDirect --> ReturnObj
        ReturnObj --> End([終了])
    end

    subgraph ThreadManager[ThreadManagerクラス]
        direction TB
        InitTM[初期化] --> CreatePool[RunspacePool作成]
        CreatePool --> SetupAsync[非同期処理設定]
        
        subgraph AsyncOps[非同期操作]
            SetupAsync --> ExecAsync[Async実行]
            ExecAsync --> CreateTask[タスク作成]
            CreateTask --> SetCallback[コールバック設定]
        end
        
        SetCallback --> Cleanup[クリーンアップ]
    end

    style New-WPFObject fill:#f9f,stroke:#333,stroke-width:2px
    style ThreadManager fill:#bbf,stroke:#333,stroke-width:2px
    style ParseProcess fill:#efe,stroke:#333,stroke-width:1px
    style AsyncOps fill:#ffe,stroke:#333,stroke-width:1px