【入門編】VB.NETのINotifyPropertyChangedインターフェイス実装:MVVMパターンを意識したWindows Forms向け軽量データバインディングの構築 – Visual Basic (VB / VB.NET)解析バイブル

スポンサーリンク

こんにちは!普段はシステム開発の現場で、VB.NETやC#を使ったアーキテクチャの設計や、泥臭いレガシーシステムの近代化に奮闘しているエンジニアです。

「マクロの記録や、ボタンをダブルクリックしてエディタに直接コードを書くスタイルから抜け出したい」
「フォームとロジックがスパゲッティのように絡み合って、修正するたびに別の場所が壊れる恐怖から解放されたい」

そんな風に悩んでいませんか?よく分かります。Windows Forms(WinForms)は歴史が長い分、適当にコードを書くとすぐに「画面とロジックがガチガチに結ばれた(密結合)」状態になってしまいます。

今回は、WPFなどで主流の「MVVM(Model-View-ViewModel)」の設計思想をWindows Formsに輸入し、`INotifyPropertyChanged` インターフェイスを使って「プロパティの変更をUIに自動で伝える」軽量かつ極上のデータバインディング基盤を構築する方法を解説します。

ここをクリアすれば、あなたのVB.NETプログラミングの視野は一気に広がります。一緒に本質をマスターしていきましょう!

—

1. なぜ「画面とロジックの分離」が必要なのか?

従来のWindows Forms開発でやりがちなのが、こんなコードです。

‘ 【やってはいけない!アンチパターン】
Private Sub btnSave_Click(sender As Object, e As EventArgs) Handles btnSave.Click
‘ 画面のテキストボックスから直接値を取る
Dim userName As String = txtUserName.Text

‘ バリデーションとビジネスロジックがここに直書き…
If String.IsNullOrEmpty(userName) Then
MessageBox.Show(“名前を入力してください!”)
Return
End If

‘ データベース保存処理…
‘ 画面の別のラベルを書き換える
lblStatus.Text = “保存しました”
End Sub

この書き方だと、「画面のコントロール名(`txtUserName`など)が変わっただけでコード全体が修正対象になる」「単体テストが極めて困難になる」という致命的な問題が発生します。

解決策:ViewModelの導入

画面(View)は「表示する」、ロジック(ViewModel)は「データを保持し、状態を管理する」という役割分担(関心の分離)を行います。ここで重要になるのが、「データが変わったら、自動的に画面にそれを伝える仕組み」です。それが `INotifyPropertyChanged` です。

—

2. 魔法のインターフェイス `INotifyPropertyChanged` とは?

`INotifyPropertyChanged` は、「私のプロパティの値が変わりましたよ!」と外部(この場合は画面のコントロール)に通知するための共通ルール(契約)です。

これを実装したクラス(ViewModel)のプロパティ値が書き換わった瞬間、WinFormsのデータバインディング機能がそれを検知し、ボタン一つ書くことなく自動的に画面の表示を更新してくれます。

実装の全体像(プレーンで美しいViewModel)

まずは、ボイラープレート(お決まりのコード)を減らすための基底クラスを作り、それを継承したViewModelをVB.NETで実装してみましょう。

Imports System.ComponentModel
Imports System.Runtime.CompilerServices

‘ ==========================================
‘ 1. 変更通知機能を持つベースクラス
‘ ==========================================
Public MustInherit Class ViewModelBase
Implements INotifyPropertyChanged

‘ プロパティ変更時に発生するイベント
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

‘ 変更を通知するヘルパーメソッド
‘ により、呼び出し元のプロパティ名を自動取得します(VB.NETの便利な機能!)
Protected Sub OnPropertyChanged( Optional propertyName As String = “”)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub

‘ 値の更新と変更通知を同時に行う安全なセッター
Protected Function SetProperty(Of T)(ByRef storage As T, value As T, Optional propertyName As String = “”) As Boolean
‘ 値が同じなら何もしない(パフォーマンス最適化と無限ループ防止)
If EqualityComparer(Of T).Default.Equals(storage, value) Then
Return False
End If

storage = value
OnPropertyChanged(propertyName)
Return True
End Function
End Class

> ここがプロの知見:
> `SetProperty` メソッドの中で値の比較(`Equals`)を行っています。これを行わないと、同じ値が代入された時にも無限にイベントが発火し、パフォーマンスの低下や予期せぬバグの温床になります。地味ですが非常に重要なテクニックです。

—

3. 実践:ViewModelとWindows Formsをバインドする

では、先ほどのベースクラスを継承した具体的なViewModelを作成し、実際のWindows Formからデータバインディングで結びつけてみましょう。

ステップ1:ViewModelの作成

‘ ==========================================
‘ 2. ユーザー画面専用のViewModel
‘ ==========================================
Public Class UserViewModel
Inherits ViewModelBase

Private _userName As String = “初期ユーザー”
Private _welcomeMessage As String = “”

‘ 画面のテキストボックスと双方向バインドされるプロパティ
Public Property UserName As String
Get
Return _userName
End Get
Set(value As String)
‘ 値を設定し、変更があれば自動でUIに通知する
SetProperty(_userName, value)

‘ UserNameが変わったら、連動してWelcomeMessageも更新する
Me.WelcomeMessage = $”こんにちは、{_userName}さん!”
End Set
End Property

‘ 画面のラベルと単方向(読み取り専用)でバインドされるプロパティ
Public Property WelcomeMessage As String
Get
Return _welcomeMessage
End Get
Set(value As String)
SetProperty(_welcomeMessage, value)
End Set
End Property

End Class

ステップ2:Windows Form(View)側での結びつけ

フォーム側では、コントロールに直接文字を入れるのではなく、「データソース」としてViewModelを割り当て(バインド)ます。

‘ ==========================================
‘ 3. Windows Form (View) のコード
‘ ==========================================
Public Class MainForm

‘ ViewModelのインスタンスを保持
Private _viewModel As New UserViewModel()

Private Sub MainForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
‘ フォーム起動時にデータバインディングを設定する

‘ ① TextBoxの Text プロパティ と ViewModelの UserName プロパティを双方向(DataSourceUpdateMode.OnPropertyChanged)で結ぶ
txtUserName.DataBindings.Add(“Text”, _viewModel, NameOf(_viewModel.UserName), True, DataSourceUpdateMode.OnPropertyChanged)

‘ ② Labelの Text プロパティ と ViewModelの WelcomeMessage プロパティを結ぶ
lblWelcome.DataBindings.Add(“Text”, _viewModel, NameOf(_viewModel.WelcomeMessage), True, DataSourceUpdateMode.OnValidation)

‘ 初回メッセージの初期化を反映させる
_viewModel.UserName = “ゲスト”
End Sub

‘ ボタンが押されたらViewModelのデータを取得・検証する(ロジックはビューに書かない!)
Private Sub btnAction_Click(sender As Object, e As EventArgs) Handles btnAction.Click
‘ 画面のコントロールではなく、ViewModelの状態を参照する
MessageBox.Show($”現在のViewModelの状態: {_viewModel.UserName}”, “通知”)
End Sub

End Class

—

4. この設計がもたらす圧倒的なメリット

ここまでのコードを実装して実行してみると、テキストボックスに文字を打ち込んだ瞬間、もう一つのラベルの文字がリアルタイムで変化するのが分かります。

ここで、イベント駆動開発における重要なパラダイムシフトが起きています。

  • 従来のやり方: テキスト変更イベント(`TextChanged`)の中で「ラベルの文字を書き換えて…」と指示(命令)していた。
  • 今回のやり方: 「データ(ViewModel)のこの値が変わったよ」と通知しただけで、UIが勝手に追従して表示を更新した。

このアプローチを取ることで、画面のレイアウトやコントロールの配置を変更しても、ビジネスロジックやViewModelのコードを1行も書き換える必要がなくなります。これが「保守性の高い美しいアーキテクチャ」の正体です。

—

5. 陥りやすい罠とエラー回避の極意

現場でこの仕組みを導入する際、初心者が必ずと言っていいほどハマる「落とし穴」がいくつかあります。プロからのアドバイスとして共有しておきます。

⚠️ 注意点1:クロススレッド例外

バックグラウンドスレッド(非同期処理など)から `_viewModel.UserName = “xxx”` を書き換えると、`InvalidOperationException`(クロススレッド操作エラー)が発生します。
対策: UIスレッド以外から変更通知やプロパティ変更を行う場合は、`InvokeRequired`などを経由してUIスレッドに処理を marshalling(委譲)する必要があります。

⚠️ 注意点2:メモリリーク(イベントの解除忘れ)

長期間起動し続けるメインフォーム等であれば問題ありませんが、モーダルダイアログなどを何度も開閉する設計において、ViewModelとViewが強固に結びつきすぎると、ガベージコレクション(GC)が働かずにメモリリークを起こすことがあります。
対策: 必要に応じて `DataBindings.Clear()` を呼び出すか、デザイナではなくコード側できれいにライフサイクルを管理することを意識してください。

—

まとめ:Visual Basic (VB.NET) の基本はバッチリです!

今回は、VB.NETによる `INotifyPropertyChanged` の実装と、Windows Formsにおける軽量MVVMパターンについて解説しました。

  • ViewModelBase を用意して共通の変更通知ロジックをカプセル化する。
  • `SetProperty` を活用して無駄な変更発火を防ぎ、コードをスマートに保つ。
  • `DataBindings.Add` を使って、コントロールとプロパティを優雅に同期させる。

「ただ動くだけのコード」から、「美しく、変更に強く、テストしやすいコード」へ。この壁を越えたあなたなら、どんなに複雑なWindows Formsアプリケーションの改修や設計も、自信を持って楽しむことができるはずです。

ここをクリアできれば、VB.NETの基本、そしてオブジェクト指向の本質はもうバッチリです。ぜひ明日の開発現場で試してみてくださいね!

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