本文へスキップ
BecomeCoder

C#デザインパターンコース · 第3章 現代の設計パターン · レッスン23

MVVM と INotifyPropertyChanged

ブラウザで完結

導入

WPF・MAUI・Blazor などのUIは、「データが変わったら画面が自動で更新される」しくみ(データバインディング)で作ります。その中心が MVVM(Model・View・ViewModel)と、変更を通知する INotifyPropertyChanged です。

図解

flowchart LR
    V["View(画面)"] <-->|バインド| VM["ViewModel<br/>画面用の状態+通知"]
    VM --> M["Model(業務データ)"]
    VM -.->|PropertyChanged| V
    style VM fill:#e1f5fe

ViewModel はプロパティが変わるたびに PropertyChanged イベントを発火します。Viewはそれを購読していて、通知を受けると表示を更新します(今回はイベント購読を自分で書いて動きを再現します)。

サンプル

using System.ComponentModel;

var vm = new CounterViewModel();
// View の代わりに「変わったら知らせて」と購読しておく
vm.PropertyChanged += (_, e) => Console.WriteLine($"更新通知: {e.PropertyName} = {vm.Count}");

vm.Increment();   // 更新通知: Count = 1
vm.Increment();   // 更新通知: Count = 2

class CounterViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;

    private int _count;
    public int Count
    {
        get => _count;
        private set { _count = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count))); }
    }
    public void Increment() => Count++;
}
  • ViewModelは「画面用の状態」と「変更通知」を持つ
  • INotifyPropertyChangedPropertyChanged を、値が変わるたびに発火する
  • Viewはそれを購読し、通知が来たら表示を更新する(=双方向バインディング)

やってみよう

Name プロパティを増やし、変更時に同じように PropertyChanged を発火してみましょう。プロパティ名 nameof(Name) が通知に乗ることを確認できます。

演習

using System.ComponentModel;

class TempViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;
    private int _celsius;
    public int Celsius
    {
        get => _celsius;
        // TODO: 値を設定し、PropertyChanged を nameof(Celsius) で発火してください
        set { ___ }
    }
}

var vm = new TempViewModel();
vm.PropertyChanged += (_, e) => Console.WriteLine($"changed: {e.PropertyName}");
vm.Celsius = 25;   // changed: Celsius
  • 期待される出力: changed: Celsius
ヒント1を見る

_celsius = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Celsius)));

ヒント2を見る

?.Invoke は購読者がいないとき(null)でも安全に呼べる書き方です

まとめ

  • MVVMはViewとModelの間にViewModel(画面用状態+通知)を置く
  • INotifyPropertyChanged で「変わったよ」をViewに通知する
  • WPF・MAUI・Blazorのデータバインディングの心臓

次回: 読み取りと書き込みを分ける CQRS です。

実際に動かしてみよう

本文のサンプルや演習のコードは、コードブロック右上の「コピー」ボタンでコピーして、下のエディタに貼り付ければそのまま実行できます。

C# — ブラウザ内で実行

ブラウザ内でC#を動かす環境を読み込みます(初回のみ数秒)。
スクロールして表示された時点でも自動で読み込まれます。