8]データ・バインドは便利で強力だが、バインドするデータにはプロパティごとにPropertyChangedイベントを発火させるコードが必要になり、数が増えると面倒だ。これを楽に実装する方法を考える。Insider.NET連載目次データ・バインドは便利で強力な仕組みだが、バインドするデータにはプロパティごとにPropertyChangedイベントを発火させるコードを書かねばならない。そのため、バインドするプロパティが10個・20個と増えてくると、その全てについてこのコードを書かねばならなくなる。この記述は嫌になる作業だ。何とかならないだろうか?そこで本稿では、前回で作成したClockクラスをリファクタリングして、PropertyChangedイベントを楽に実装する方法を考えてみることにする。2012)が必要である。これらを準備するには、第1回のTIPSを参考にしてほしい。8.0(無償)が必要となる。●リファクタリング前のClockクラスまず、前回で作成したClockクラスを再掲する。バインドするプロパティが3つあり、PropertyChangedイベントを発火させるコードも3箇所ある。PropertyChangedイベントを発火させるコードもプロパティに応じて3箇所ある((1)、(2)、(3))。上のコードではプロパティのsetアクセサ(=setter)(VBでは「Setプロパティ・プロシージャ」)がprivate/Privateになっているが、そうでないときは一般に次のコードのようにPropertyChangedイベントを発火させるコードをsetter内に記述する。setterに記述する際には、プロパティに変化がなければイベントを発火させてはいけないので、if文での切り分けも必要になる。SubPropertyChangedイベントを発火させるコードをsetter内に移した(上:C#、下:VB)上のコードを見ると、setter内の記述はほとんど同じである。このような冗長なコーディングを毎回行うのは苦痛以外の何物でもないだろう。そこで以下では、Clockクラスをリファクタリングして、クリーンなコードにしていくことにする。●Clockクラスをリファクタリングするまず、PropertyChangedイベントを発火させるコードを、OnPropertyChangedメソッドに切り出してみよう。次のコードのようになる。切り出したついでに、C#のコードでめったに起きるわけではないバグの修正も行った(コメント参照)。......PropertyChangedイベントを発火させるコードを、OnPropertyChangedメソッドに切り出した(上:C#、下:VB)次に、setterの中身を全部切り出してSetPropertyメソッドにしてみよう。NowTimeプロパティのsetterからコードを切り出すと次のようになる。SubNowTimeプロパティのsetterをSetPropertyメソッドに切り出した(上:C#、下:VB)なお、ジェネリックにすることを見越して、if文でobject.Equalsメソッドを使っている。このSetPropertyメソッドは、ジェネリックを使って次のコードのように書き直せば、他のプロパティからも使えるようになる。これで似たコードを何度も記述する手間がかなり削減されるはずだ。......ジェネリックを使ってSetPropertyメソッドを書き直した(上:C#、下:VB)これでsetterの記述は1行だけになった。引数にプロパティ名を渡すところは、書き間違えてしまうことがありそうだ。そこで、CallerMemberName属性(System.Runtime.CompilerServices名前空間)を使うと呼び出し元のメンバ名が取得できるので、これを使うことで、プロパティ名を引数として渡さなくて済むようになる(次のコード)。CallerMemberName属性を使うには既定値の指定が必要なので、ここではnull/Nothingとした。......CallerMemberName属性を使って、メソッド名の引数をなくした(上:C#、下:VB)これでリファクタリングは完了だ。カスタム属性を作ってsetterの記述を簡略化することも可能ではあろうが、そうすると呼び出しのオーバーヘッドが増えてしまうので、ここまででとどめておく。●BindableBaseクラスを使うこれから作るデータ・クラスにも上記のリファクタリングの結果を利用するならば、次のメンバを切り出して継承元となるクラス(=親クラス)を作っておくとよい。Subデータ・クラスを作るための親クラスに切り出すメンバー(上:C#、下:VB)しかし実は、まさにそのような用途で使える親クラスがプロジェクト・テンプレートに用意されている。(XAML)」テンプレートのCommonフォルダに入っている「BindableBase」クラスがそれだ*1。8のプロジェクト・テンプレートには入っていないが、Windowsストア・アプリのものを簡単に移植して利用できる。BindableBaseクラスが含まれていないプロジェクト・テンプレートを使うときは、前回で説明したように、そのプロジェクトに(新しいプロジェクト項目として)[基本ページ]を追加することでCommonフォルダに自動生成される。BindableBaseクラスでは、SetPropertyメソッドがvoidではなくbool/Boolean型の値を返すようになっているが、基本的に同じである(プロパティに変化がありOnPropertyChangedが呼び出されればtrue/True、そうでなければfalse/Falseが返される)。前述のリファクタリングが完了したClockクラスを、BindableBaseクラスを継承するように書き直すと次のコードのようになる。ClassBindableBaseクラスを継承して書き直したClockクラス(上:C#、下:VB)冒頭に載せたリファクタリング前のClockクラスと見比べてほしい。簡潔な記述になったことが分かるだろう。●まとめデータ・バインドに使うデータのクラスは、BindableBaseクラスを継承して作成するとよい。本稿で示したリファクタリングの成果をまとめたものが、BindableBaseクラスなのだ。プロジェクトには含まれていないが、Windowsストア・アプリのものを移植すればよい。メールマガジンをご購読ください。申し込みページへITmediaはアイティメディア株式会社の登録商標です。