はじめに
オレオレ解釈の覚え書き その9
一番最初にやるべきだった変更通知プロパティについてまとめます。
タイトルには「ViewModel と~」とありますが、Model 層で使用される場合もあります。
本文
MVVM では TextBox.Text や CheckBox.IsChecked 等の依存関係プロパティに、ViewModel で定義された変更通知プロパティをバインドさせることで値を伝播することができます。つまり View の変更を ViewModel へ、ViewModel の変更を View へと、同期を取り合うということです。
今回は ViewModel に定義する変更通知プロパティについて紹介します。これは通常のプロパティ(いわゆる自動実装プロパティ)とは異なり、自身の値の変更を通知する機能を併せ持つプロパティを指します。原始的には INotifyPropertyChanged インターフェースを実装したクラス(これが ViewModel となります)を用意し、プロパティのセッターではバッキングフィールドを確認、値に変更があれば置き換えたうえで INotifyPropertyChanged.PropertyChanged イベントを発生させます。
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace TestApp.ViewModels
{
public class MainWindowViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
=> this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
private string _text;
public string Text
{
get => this._text;
set
{
if (this._text != value)
{
this._text = value;
this.OnPropertyChanged();
}
}
}
}
}
愚直に実装すると上記のようになりますが、プロパティの部分はともかくとして、変更通知のイベントを自前で処理することは少ないはずです。大半の MVVM フレームワークではこれらの機能を搭載した基底クラスが提供されており、Prism の BindableBase、Livet の NotificationObject、MVVM Light Toolkit の ObservableObject などがそれにあたります。
先の例を Prism を使用して書き換えると次のようになりスッキリします。
using Prism.Mvvm;
namespace TestApp.ViewModels
{
public class MainWindowViewModel : BindableBase
{
private string _text;
public string Text
{
get => this._text;
set => this.SetProperty(ref this._text, value);
}
}
}
なお View はコントロールのプロパティにバインドさせるだけです。Mode で同期の方向を、UpdateSourceTrigger でタイミングを制御することもできます。この辺りの仕様は .NET のリファレンスを確認してください。
<Window x:Class="TestApp.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:p="http://prismlibrary.com/"
p:ViewModelLocator.AutoWireViewModel="True">
<Grid>
<TextBox Text="{Binding Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</Window>
おわりに
今回は View と ViewModel をつなぐ仕組みの中でも、頻出で重要な機構である変更通知プロパティについてでした。
移譲コマンドは処理の移譲だけでしたが、この変更通知プロパティがあることで値を受け渡すことも可能になります。ここまで役者が揃ってくると、多くの処理を MVVM パターン上で構築できることに気づきます。とはいえ、構築できることと楽に実装できることの間には溝があり、正直今回の例にあるようにプロパティのセッターで一々変更を通知するのは面倒です。複数のプロパティの変更通知を絡める処理などが出てくると管理も大変になるでしょう。
次回は、このような変更通知やリアクティブプログラミングを強力にサポートするライブラリである ReactiveProperty について、表面だけサラッとご紹介します。