ネタ元:InfoQ: プロパティへのINotifyPropertyChanged注入することができるIL Weaving
昨今のデータバインド中心のUI開発では、INotifyPropertyChangedインターフェイスを実装し、プロパティの変更を通知する仕組みが必須です。
しかし、INotifyPropertyChangedインターフェイスの実装は、普通にやるには愚直にそれぞれのプロパティの変更を自前で判断して実装する必要があり、「面倒だ」というのが定説になりつつあります。
INotifyPropertyChanged 実装例
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
namespace WpfApplication1
{
public class MainWindowViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string myValue;
public string MyValue
{
get { return myValue; }
set
{
if (myValue != value)
{
myValue = value;
OnPropertyChanged("MyValue");
}
}
}
private string myText;
public string MyText
{
get { return myText; }
set
{
if (myText != value)
{
myText = value;
OnPropertyChanged("MyText");
}
}
}
private void OnPropertyChanged(string propertyName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
この手間を何とかしようと、多くの人たちが試行錯誤を続けてきました。
ある人はT4テンプレートで自動生成し、ある人はヘルパークラスを作成し、またある人はVS拡張のデザイナーを作ったりしています。
そんな中、今回登場したのが「Notify Property Weaver」です。
元記事を読むと、ILを書き換えて変更通知してくれるプロパティに変更してくれるツールのようです。さっそく試してみましょう。
まず、VS拡張の導入です。これは、元記事にあるリンクからVS Galleryからvsixをダウンロードしてもいいですし、VSの拡張機能マネージャーから検索すればそこからも導入可能です。
導入後、先ほどと同じになるように、ViewModelを次のように作成します。ただし、INotifyPropertyChangedは実装しますが、自動実装プロパティを使用します。
ViewModel
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
namespace WpfApplication1
{
public class MainWindowViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string MyValue { get; set; }
public string MyText { get; set; }
}
}
次に、[プロジェクト]メニュー→[NotifyPropertyWeaver]→[Configure]を選択します。
次のダイアログが表示されるので、そのまま[OK]ボタンをクリックします。
これで、プロパティの変更通知が自動で行われるようになります。
(ダイアログのなかではいろいろ設定できるようですが、まだよくわからないので今回はスルーします。)
それでは、実際に変更通知機構が働くか試してみましょう。
MainWindow.xaml、そしてコードビハインドを次のようにします。
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication1
{
/// <summary>
/// MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
this.DataContext = new MainWindowViewModel();
InitializeComponent();
}
private void MyButton_Click(object sender, RoutedEventArgs e)
{
var vm = this.DataContext as MainWindowViewModel;
vm.MyValue = "hoge!";
vm.MyText = "fuga!";
}
}
}
では実行してみます。まずは、次のようにウィンドウが表示されます。
ここで、[Press!]ボタンをクリックすると、コードビハインドのイベントハンドラーでVMに設定した値が画面に表示されます。
無事、変更通知機構が動いているようです。
なお、ビルドして生成されたアセンブリをIL Spyでのぞいてみると、次のようになっていました。
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace WpfApplication1
{
public class MainWindowViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string MyValue
{
[CompilerGenerated]
get
{
return this.k__BackingField;
}
[CompilerGenerated]
set
{
if (string.Equals(this.k__BackingField, value))
{
return;
}
this.k__BackingField = value;
this.OnPropertyChanged("MyValue");
}
}
public string MyText
{
[CompilerGenerated]
get
{
return this.k__BackingField;
}
[CompilerGenerated]
set
{
if (string.Equals(this.k__BackingField, value))
{
return;
}
this.k__BackingField = value;
this.OnPropertyChanged("MyText");
}
}
public virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
if (propertyChanged != null)
{
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
確かに、自動的にILに変更通知機構が追加されていることが確認できます。
自動でILを書き換えるため、ビルドに少し時間がかかる感じはしますし、自分で変更通知機構をカスタマイズしたいときにどうするか?などの問題はありますが、ViewModel作成の強い味方になってくれそうですね。