【実務・中級編】【中級者・実務】VB.NETによるWPFでの画面遷移フレームワーク設計:UserControl動的切替とパラメータ受渡し – Visual Basic (VB / VB.NET)解析バイブル

スポンサーリンク
スポンサーリンク

Windowを乱立させるな。WPFにおける「Single Window / Multi-View」アーキテクチャの極意

業務アプリケーションの開発において、多くの開発者が陥る致命的な罠がある。それは、「新しい画面を開くたびに、新しい Window インスタンスを生成して `Show()` する」という設計だ。

もし君が、ボタンをクリックするたびに `frmMaster.Show()` や `frmInput.Show()` を連発しているとしたら、今すぐその設計を捨ててほしい。それは、メモリ管理を放棄し、状態の整合性を破壊し、ユーザーに「使いにくい」という直感的な拒絶を与える、低品質な設計の入り口だ。

プロフェッショナルが構築すべきは、「一つのメインウィンドウ(Shell)の中に、UserControlを動的に差し替えていく」、いわゆる Single Window / Multi-View アーキテクチャである。

本稿では、VB.NETとWPFを用い、保守性が高く、かつ画面間でのパラメータ受け渡しを安全に行うための「画面遷移フレームワーク」の設計パターンを伝授する。

1. なぜ Window ではなく UserControl なのか?

なぜ、Windowを増やすのが「非効率」なのか。理由は3つある。

1. 状態管理の破綻: 複数のWindowが開くと、「どの画面が最新のデータを保持しているか」の管理が極めて困難になる。
2. リソースの浪費: Windowオブジェクトは、UserControlに比べて極めて重い。大量の画面遷移が発生する業務アプリでは、メモリリークの温床となる。
3. UX(ユーザー体験)の低下: 画面が重なり合い、タスクバーがウィンドウだらけになる設計は、モダンな業務アプリケーションとしては極めて不格好である。

我々が目指すべきは、「メインウィンドウは器(Container)に過ぎず、中身(Content)をViewModelの切り替えによって制御する」設計である。

2. アーキテクチャの設計図

このフレームワークを実現するために、以下の3つの要素を分離して設計する。

1. View (UserControl): 見た目。ロジックを持たない。
2. ViewModel: 画面のロジックとデータ。画面遷移の指令を出す。
3. MainViewModel (Navigator): 現在表示されているViewModelを保持し、Viewの切り替えを司る「司令塔」。

3. 実装:堅牢な画面遷移フレームワーク

それでは、実際にプロダクションレベルで利用可能なコードを実装していく。

3.1 基盤となる ViewModelBase

まず、すべてのViewModelの親となる基盤を作る。`INotifyPropertyChanged` の実装は、WPFにおける「データと表示の同期」の生命線だ。

Imports System.ComponentModel
Imports System.Runtime.CompilerServices

''' <summary>
''' すべてのViewModelの基底クラス。
''' プロパティ変更通知を確実に実装する。
''' </summary>
Public MustInherit Class ViewModelBase
    Implements INotifyPropertyChanged

    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

    ''' <summary>
    ''' プロパティ変更を通知する。
    ''' </summary>
    Protected Sub OnPropertyChanged(<CallerMemberName> Optional propertyName As String = Nothing)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
    End Sub
End Class

3.2 画面遷移の司令塔:MainViewModel

ここが心臓部だ。`CurrentViewModel` というプロパティを持ち、これを書き換えることで、表示される画面を切り替える。

''' <summary>
''' アプリケーション全体の司令塔。
''' 画面(ViewModel)の切り替えを管理する。
''' </summary>
Public Class MainViewModel
    Inherits ViewModelBase

    Private _currentViewModel As ViewModelBase

    ''' <summary>
    ''' 現在表示されているViewModel。
    ''' このプロパティが書き換わると、WPFのContentControlが自動的にViewを切り替える。
    ''' </summary>
    Public Property CurrentViewModel As ViewModelBase
        Get
            Return _currentViewModel
        End Get
        Set(value As ViewModelBase) Implements INotifyPropertyChanged.PropertyChanged
            _currentViewModel = value
            OnPropertyChanged()
        End Set
    End Property

    ''' <summary>
    ''' 画面遷移を実行する。
    ''' </summary>
    ''' <param name="newViewModel">遷移先のViewModel</param>
    Public Sub NavigateTo(newViewModel As ViewModelBase)
        ' ここで、遷移時のログ出力や、リソースの解放処理などを挟むことができる
        CurrentViewModel = newViewModel
    End Sub
End Class

3.3 安全なパラメータ受け渡し:IParameterReceiver

「画面遷移時にIDを渡したい」という要求は必ず出る。しかし、ViewModelを強引にキャストしてプロパティに値を突っ込むのは、結合度を高める悪手だ。
インターフェースを用いて、「パラメータを受け取れる能力を持つViewModel」として定義するのがスマートだ。

''' <summary>
''' パラメータを安全に受け取るためのインターフェース。
''' </summary>
Public Interface IParameterReceiver
    ''' <summary>
    ''' 画面表示前に呼び出される初期化メソッド。
    ''' </summary>
    ''' <param name="parameter">遷移時に渡したいデータ(Object型で汎用性を持たせる)</param>
    Sub Initialize(parameter As Object)
End Interface

4. 実践:具体的な画面の実装例

では、実際に「一覧画面」から「詳細画面」へ、IDを渡して遷移する流れを作ろう。

4.1 詳細画面のViewModel

''' <summary>
''' 詳細表示画面のロジック。
''' </summary>
Public Class DetailViewModel
    Inherits ViewModelBase
    Implements IParameterReceiver

    Private _targetId As Integer
    Public Property TargetId As Integer
        Get
            Return _targetId
        End Get
        Set(value As Integer) Implements INotifyPropertyChanged.PropertyChanged
            _targetId = value
            OnPropertyChanged()
        End Set
    End Property

    ' IParameterReceiverの実装
    Public Sub Initialize(parameter As Object) Implements IParameterReceiver.Initialize
        ' 型安全性を確保するためのガード
        If TypeOf parameter Is Integer Then
            Me.TargetId = DirectCast(parameter, Integer)
        Else
            Throw New ArgumentException("Invalid parameter type for DetailViewModel.")
        End If
    End Sub
End Class

4.2 メインウィンドウのXAML構成

WPFの強力な機能である `DataTemplate` を使い、「どのViewModelの時に、どのUserControlを表示するか」を宣言的に定義する。これが、コードを汚さずにViewを切り替える魔法だ。

<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:YourAppNamespace"
        Title="Enterprise Business System" Height="450" Width="800">
    
    <Window.Resources>
        <!-- ViewModelとViewの紐付け(DataTemplate) -->
        <!-- これにより、CurrentViewModelに特定のクラスが入ると、自動的に対応するUserControlが表示される -->
        <DataTemplate DataType="{x:Type local:HomeViewModel}">
            <local:HomeUserControl />
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:DetailViewModel}">
            <local:DetailUserControl />
        </DataTemplate>
    </Window.Resources>

    <Grid>
        <!-- ContentControlが、CurrentViewModelの内容を動的に表示する -->
        <ContentControl Content="{Binding CurrentViewModel}" />
    </Grid>
</Window>

5. プロフェッショナルとしての注意点

このフレームワークを実務に投入する際、以下の3点に細心の注意を払え。

① メモリリークへの警戒

`UserControl` を切り替えても、古いViewModelがイベントハンドラを握り続けていると、ガベージコレクション(GC)が効かず、メモリが肥大化する。画面を離れる際(Unload時)に、イベントの購読を解除する設計を徹底せよ。

② データベース連携の責務分離

ViewModelの中で直接 `SqlConnection` を生成してはいけない。それは「単体テスト不能」なコードへの第一歩だ。必ず Repository パターン や Service クラス を介し、ViewModelは「データの要求」と「データの表示」に専念させろ。

③ パラメータ受け渡しの型安全性

`IParameterReceiver` で `Object` 型を使用しているが、実務では `DirectCast` 時の例外がバグの原因になる。可能であれば、専用の `NavigationParameter` クラスを定義し、その中に必要な情報をカプセル化して渡すのが、最も堅牢な設計である。

結論

Windowを乱発する設計は、技術的負債の積み立てに過ぎない。
`ContentControl` と `DataTemplate`、そして `Interface` による疎結合な設計をマスターすること。それが、大規模な業務アプリケーションを、バグなく、高速に、そして美しく構築するための唯一の道だ。

この設計パターンを君のプロジェクトに導入し、真の「アーキテクト」への一歩を踏み出してほしい。

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