導入
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は「画面用の状態」と「変更通知」を持つ
INotifyPropertyChangedのPropertyChangedを、値が変わるたびに発火する- 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 です。