前回は、
DataContextは
「Bindingがデータを探す基準になるもの」
という話を書きました。
例えば、
<TextBlock Text="{Binding UserName}" />
と書くと、
BindingはDataContextに設定されたViewModelから
UserName
を探します。
これで、
ViewModelの値を画面に表示する仕組み
までは分かりました。
でも、ここで次の疑問が出てきます。
ViewModel側で、
UserName = "田中";
と値を変更したとします。
値は確かに変わっています。
では、
なぜ画面側の表示も変わるのでしょうか?
逆に、
値を変更したのに、画面が更新されない……
ということがあるのは、なぜでしょうか。
今回は、その間をつないでいる
PropertyChanged
について整理してみます。
値を変えただけでは画面は知らない
例えば、ViewModelにこんなプロパティがあるとします。
public string UserName { get; set; }
そして、
UserName = "田中";
と値を変更します。
この時、
ViewModelの中では確かに、
UserName = "田中"
になっています。
でも、
ViewModelの値が変わったからといって、
画面が自動的に
「あ、UserNameが変わったんだ!」
と知るわけではありません。
つまり、
ViewModel
UserName = "田中"
↓
値は変わった
↓
でもViewは
変更されたことを知らない
という状態です。
Bindingがあれば自動で更新されるんじゃないの?
ここは、最初に勘違いしやすいところだと思います。
前回までの記事を読むと、
BindingがViewとViewModelをつないでいるなら、値が変われば勝手に画面も変わるのでは?
と思うかもしれません。
でも、
BindingはViewとデータを結び付ける仕組みです。
値が変更されたことそのものを知らせるには、
別の仕組みが必要になります。
そこで登場するのが、
PropertyChanged
です。
PropertyChangedとは?
PropertyChangedを一言でいうと、
「プロパティの値が変わったことを知らせる仕組み」
です。
例えば、
UserNameが変わる
↓
PropertyChanged
「UserNameが変わったよ!」
↓
Bindingが変更を知る
↓
画面表示が更新される
というイメージです。
前回までの記事の言い方に合わせるなら、
DataContextが、
「ここを見て」
Bindingが、
「UserNameが欲しい」
とするなら、
PropertyChangedは、
「UserNameが変わったよ!」
と知らせる役割です。
実際のコードを見てみよう
PropertyChangedのコードは、
初めて見ると少し複雑に見えます。
なので、
いきなり全部理解しようとしなくても大丈夫です。
まずは普通のプロパティから見てみます。
private string _userName;
public string UserName
{
get => _userName;
set
{
_userName = value;
}
}
これでも、
UserNameの値そのものは変更できます。
ただし、これだけでは画面に、
「UserNameが変わったよ」
とは伝えていません。
そこで、
値を変更したあとに通知を追加します。
public string UserName
{
get => _userName;
set
{
_userName = value;
OnPropertyChanged(nameof(UserName));
}
}
今回、一番見てほしいのはこの一行です。
OnPropertyChanged(nameof(UserName));
これは、
「UserNameの値が変わりました!」
と通知するための処理です。
OnPropertyChangedは何をしているの?
では、
OnPropertyChanged は何をしているのでしょうか。
例えば、こんなコードです。
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(
this,
new PropertyChangedEventArgs(propertyName));
}
急に難しく見えますよね。
でも今回は、
細かい書き方まで覚えなくても大丈夫です。
まず理解しておきたいのは、
UserNameを変更する
↓
OnPropertyChangedを呼ぶ
↓
「UserNameが変わったよ」
と通知する
↓
Bindingが変更を知る
↓
画面表示が更新される
という流れです。
INotifyPropertyChangedって何?
WPF・MVVMでは、
この変更通知を行うために
INotifyPropertyChanged
という仕組みを使うことがよくあります。
全体を書くと、例えばこんな形です。
public class MainViewModel : INotifyPropertyChanged
{
private string _userName;
public string UserName
{
get => _userName;
set
{
_userName = value;
OnPropertyChanged(nameof(UserName));
}
}
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(
this,
new PropertyChangedEventArgs(propertyName));
}
}
コードだけを見ると、
少し難しく感じるかもしれません。
でも、最初は全部を暗記する必要はありません。
まずは、
「ViewModelの値を変えたあと、変更したことを通知している」
という役割を理解しておけば十分だと思います。
PropertyChangedがないとどうなる?
例えば、
public string UserName { get; set; }
だけでも、
画面を最初に表示した時には、
値が表示されることがあります。
すると、
「ちゃんとBindingできているから問題ない」
と思ってしまいます。
でも、その後に
UserName = "田中";
と変更しても、
変更通知がなければ、
画面はその変更を知ることができません。
つまり、
最初の画面表示
↓
UserNameが表示される
↓
UserNameを変更
↓
ViewModelの値は変わる
↓
でも変更を通知していない
↓
画面は古い値のまま
となります。
これが、
「値はちゃんと変わっているのに、なぜ画面に反映されないの?」
というハマりにつながります。
なぜPropertyChangedを使うの?
もし、
PropertyChangedのような変更通知の仕組みがなければ、
値が変わるたびに、
UserNameTextBlock.Text = UserName;
のように、
自分で画面を書き換える方法も考えられます。
でも、
ViewModel側で値を変更するたびに、
View側の表示まで自分で更新するようになると、
ViewとViewModelが強く結び付いてしまいます。
そこで、
ViewModelは、
「値が変わったよ」
と通知する。
そして、
その値を画面へ反映する部分はBindingに任せる。
こうすることで、
ViewとViewModelの役割を分けやすくなります。
これもMVVMでPropertyChangedを使う大きな理由の一つです。
デバッグするときはどこを見る?
例えば、
「ViewModelのUserNameは『田中』になっているのに、画面には変更前の名前が表示されている」
という不具合があったとします。
そんな時は、
次のように確認してみます。
UserNameは変更された?
↓
YES
PropertyChangedは通知された?
↓
YES
Bindingは正しい?
↓
YES
画面は更新された?
特に、
ViewModelの値は正しいのに、画面だけ変わらない
という場合は、
PropertyChangedが通知されているかを確認すると、
原因を絞り込みやすくなります。
##ここまでの「繋ぐ仕組み」を整理してみる
ここまで、
・Event
・Command
・Binding
・DataContext
・PropertyChanged
を一つずつ紹介してきました。
ただし、
これらが、
Event
↓
Command
↓
DataContext
↓
Binding
↓
PropertyChanged
という順番で、
いつも一本道に動いているわけではありません。
それぞれ、
違うものを繋ぐための役割
を持っています。
大きく分けると、
こんなイメージです。
【操作と処理をつなぐ】
ユーザー操作
│
├─ Event
│ 「何か起きたよ」
│
└─ Command
「この処理を実行するよ」
【データと画面をつなぐ】
View
│
├─ Binding
│ 「この値を使いたい」
│
├─ DataContext
│ 「このデータを基準に探してね」
│
└─ PropertyChanged
「その値が変わったよ」
│
ViewModel
それぞれの役割を整理すると、
| 仕組み | 役割 |
|---|---|
| Event | 「何かが起きた」ことを知らせる |
| Command | 実行する処理をViewModel側につなぐ |
| Binding | Viewとデータを結び付ける |
| DataContext | Bindingがデータを探す基準になる |
| PropertyChanged | プロパティの値が変わったことを知らせる |
最初は、
全部バラバラの仕組みに見えるかもしれません。
でも、
「何と何を繋ぐための仕組みなのか?」
という視点で見ると、
それぞれの役割が少し分かりやすくなります。
不具合が起きた時も「役割」で考えてみる
それぞれの役割が分かってくると、
不具合が起きた時にも、
どこから調べればいいのか考えやすくなります。
例えば、
ボタンを押しても処理が動かない
なら、
EventやCommandを確認してみる。
ViewModelに値はあるのに画面に表示されない
なら、
BindingやDataContextを確認してみる。
値は変わっているのに画面だけ更新されない
なら、
PropertyChangedを確認してみる。
もちろん、
実際の不具合はこれだけで解決できるとは限りません。
でも、
「まずどこから調べればいいのか」
という最初の当たりを付けやすくなります。
コードを一つずつ覚えるよりも、
それぞれの仕組みが
何のために存在しているのか
を理解しておくことが、
実際の開発では役に立つのではないかと思います。
まとめ
今回は、
PropertyChanged
について紹介しました。
覚えておきたいポイントは4つです。
✅ ViewModelの値を変更しただけでは、画面は変更されたことを知らない
✅ BindingはViewとデータを結び付ける仕組み
✅ PropertyChangedは「値が変わったこと」を通知する仕組み
✅ 値は変わっているのに画面が更新されない時は、変更通知を確認してみる
そして、
Event、Command、Binding、DataContext、PropertyChanged。
それぞれの書き方を覚えるだけではなく、
「何と何を繋ぐための仕組みなのか?」
という視点で見てみると、
WPF・MVVMのコードも少し追いやすくなると思います。
📚 WPF・MVVM基礎シリーズ
第1回
✅ 新人・初級PGが最初に覚えた方がいいこと|言語より先に理解したいプログラムの仕組み
第2回
✅ WPF・MVVMで最初に覚えておきたいこと|プログラムは「データ」と「処理」だけでは動かない
第3回
✅ ボタンを押したらなぜ処理が動くの?|WPF・MVVMのEventを理解しよう
第4回
✅ [ボタンを押したらなぜ画面が切り替わるの?|WPF・MVVMのCommandを理解しよう](https://qiita.com/k-shimaoka-dev/items/e41fa6cd06177106ed40)
第5回
✅ なぜViewModelの値が画面に表示されるの?|WPF・MVVMのBindingを理解しよう
第6回
✅Bindingは何を見ているの?|WPF・MVVMのDataContextを理解しよう
📖 あわせて読みたい
WPFのDataContextでハマるポイント3選【MVVM】実務で迷子にならないための整理
WPFで後から辛くなるバインディングの典型パターン3選 実務でハマる!後悔しないための注意ポイントも解説