1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

値を変更したのに画面が更新されないのはなぜ?|WPF・MVVMのPropertyChangedを理解しよう

1
Posted at

前回は、

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選 実務でハマる!後悔しないための注意ポイントも解説

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?