dragan10

依存性プロパティシステム - その深層(1)

2008-10-05 17:10:13

17日に依存性プロパティの話を始めましたが、そのときに書いたとおり、まだまだ書きたいことがあります。Silverlightのプログラミングの中心的なコンセプトは、伝統的に難易度の高いコンセプトとされてきました(まだリリースもされていない製品に伝統というのもなんですが)。これにはある程度、ちゃんと理由があります:カスタムコントロールを書いていないうちは、依存性プロパティのことは無視してもかまわないのです。それでは、無視するべきでない理由を三つ挙げてみましょう:

1.魅力的だから

2.内部動作が理解できるから

3.自分でカスタムコントロールを書くには欠かせないから

ええ、本当の理由は1です。とにかくおもしろいから、です。

依存性プロパティにアプローチするにはいくつかのやり方があります。多くの場合は、最初に何かやらなければならないこと(たとえば「このカスタムコントールに依存性プロパティをつなぐにはどうしたらいいんだろう?」といった)があって、現実的なアプローチをすることになり、それから依存性プロパティに関するシステム全体を理解しようとすることになります。今日のコラムの目的は、後者の段階に入っていくことです:システムが全体としてどうなっているのか調べていきましょう。

依存性プロパティを巡る冒険

まず最初に、いくつかの驚くべき事実を認めるところから始めましょう:

WPFとSilverlightを生み出した人たちは、根本に関わるような決定(私からすると、技術的に勇気がいることです)をいくつか下しました:

*彼ら曰く「今のプロパティは我々の要求を満たしていないから、下位のレイヤを損なわないようなやり方で、たくさん機能が追加できるような新しいレイヤをその上にかぶせよう」

*というわけで、依存性プロパティシステムは、根本的な拡張機能としてCLRに乗せられ、C++やJava、さらにはWPF登場以前からあるC#/VB.NETでもつかえるようになった

*そして、Silverlightの関係者は、単にきれいに一貫性を持たせようとするのではなく、Silverlightでの要求をふまえた上で、依存性プロパティシステムのどの部分を使い、どの部分を適合させ、どの部分を使わずにおくのかを決定した

これは議論を呼ぶ、ただし非常に理解できる決定でしたが、同時に依存性プロパティシステムを早い段階で理解しようとした人たちは頭痛を抱えることになりました。しかし、長い目で見れば利益をもたらす決定でもありました。

もちろん最大の問題は、特に現時点では何がどうなっているのかをはっきりさせるのがややむずかしいことです。時系列で見ると、今この時点について記述するのは大変を困難を伴います。

*WPFの本はすばらしいが、内容すべてがSilverlightにも当てはまるわけではない

*Beta2用のchmファイルやヘルプファイルも大変良いが、一部の内容はもう古くなっている

*changeドキュメントは役に立つが、すべてを網羅しているとは限らない

*依存性プロパティの完全なドキュメントには高い優先順位がついていない。依存性プロパティは上級者向けでわかりにくいものと考えられているので。

ということで、おもしろいことになっているわけです。

うまく説明できるように、(a)この話を数回に分けてポストし、(b)何を書いたか忘れないように、短い間に一連の依存性プロパティのポストをやってしまうように努力します!一連のポストには、すべて「依存性プロパティ」というタグを付けるので、必要に応じて検索したり読み飛ばしたりすることができるでしょう。

依存性プロパティが追加された理由

17日のポストで書いたとおり、WPFの設計者がすぐに気づいたことがあります。つまり、アニメーションができてデータバインド可能なクライアントサイドのアプリケーションを宣言的に書くには、通常のCLRのプロパティでは機能も拡張性も不足しているということです。必要なのは、実行時にプロパティの値を複数のソースからの入力(その時点での他のプロパティの値や、頻繁に変化するアニメーション関連の値など)に基づいて決定するシステムでした。

依存性プロパティの重要な価値は、登録済みの関連しているものに対して、自分の値が変わるたびに自動的に通知するプロパティを作れるところにあります。この、最初から用意されていて、面倒もなく、自動で動いてくれるオブザーバー・パターンの実装は非常に強力で、クライアントのプログラマの負担をとても小さくしてくれます(実際のところ、データバインドのシステムもこれに依存しています)。

[オブザーバーパターンについてはProgramming .NET 3.5でくわしく説明しています。非公認の抜粋はこちら]

これだけでも十分に価値があるでしょう。これはつまり、いくつものコントロールを依存性プロパティにバインドしておけば、そのプロパティの値が変わったら通知してもらえるというです。しかも一行もコードを書かずに、です。すごいでしょう!

コストと利益

私(と設計者達も明らかに)は、依存性プロパティは非常に役に立つと思っていますが、システムの勉強をしているだけとはいえ、引き替えになるものもあるという事実を無視するわけにはいかないでしょう。このポストでは、それが何なのか書くことにします。どんな風に役立つのかは、次のポストから書いていくことにしましょう。

その代価は・・・

 

クラスの中の新しい要素について学び、しっかり把握しなければなりません-依存性プロパティは、これまではなかった下位層のシステムとやりとりします。加えて、あなたが使っているプロパティの中には、その新しい依存性プロパティのラッパに過ぎないものすらあります。それはどういうことかというと、プロパティの値になっているのがメンバー変数やデータベースの値や演算結果ではなく、依存性プロパティだということです。そのことを心にとめておきましょう。

コードで見ると次のようになります:

 public static readonly DependencyProperty ValuableProperty =
  DependencyProperty.Register(
  "Valuable",
  typeof( bool ),
  typeof( MyCustomControl ),
  new PropertyMetadata( new PropertyChangedCallback(   
      MyCustomControl.OnValuablePropertyChanged ) ) );

 

とても標準的なgetとsetですが、背後にある変数にアクセスするのにGetValueとSetValueを使い、ValuablePropertyという依存性プロパティ(これはイディオムで、CLRのプロパティ名+Property=依存性プロパティ名です。この場合は、Valueable + Property = ValuableProperyですね)にgetやsetをしているところが違っています。

依存性プロパティの宣言そのものは、なんともややこしい感じです。

 

public static readonly DependencyProperty ValuableProperty =

  DependencyProperty.Register(

  "Valuable",

  typeof( bool ),

  typeof( MyCustomControl ),

  new PropertyMetadata( new PropertyChangedCallback(   

      MyCustomControl.OnValuablePropertyChanged ) ) );

一つ一つ見ていきましょう。最初の行ではオブジェクト(これは実際にはDependencyPropertyへの参照です)をpublicとして宣言しています:これはstaticでreadonlyでなければならず、その型はDependencyPropertyで名前(識別子)はValuablePropertyです。

この参照を、DependencyPropatyクラスのstaticなRegisterメソッドの返値を参照するように設定します。Registerメソッドは4つの引数を取ります:

1.依存性プロパティのラッパの名前

2.登録する依存性プロパティの型

3.依存性プロパティを登録するオブジェクトの型

4.コールバック

コールバックの型はPropertyMetaDataです。いろいろなMedaDataがDependencyPropertyに用意されている世界を創造することもできますが、現時点でSilverlightに用意されているのはただ一つ、コールバックだけです。

PropertyMetaDataのコンストラクタは、PropertyChangedCallback型のオブジェクトを取ります。このPropertyChangedCallback型のオブジェクトは、有効な依存性プロパティのプロパティ値が変化するたびに呼ばれます。このオブジェクトに、呼び出すメソッド(すなわちコールバックです)への参照を渡してやるわけです。

これはすなわちどういうことかというと、他に対して、実際にはDependencyPropertyが背後控えたCLRプロパティ(Valuable)を公開してやるということです。そしてそのDependencyPropertyは、有効なプロパティの値が変化したら、OnValuablePropertyChangedメソッドをコールバックするのです。

コールバックメソッドは2つの引数を取ります:

*DependencyObject(コントロール)

*DependencyPropertyChangedEventArgs型のオブジェクト

典型的には、最初の引数をプロパティを持っているコントロールの型にキャストして、DependencyPropertyChangedEventArgs型のオブジェクトのNewValueプロパティを変更されたDependencyPropertyの型へキャストします。これで、依存性プロパティの値の変化に基づいて、好きな動作をさせることができるようになります。

 

   1: public class MyCustomControl : Control

   2: {

   3:  

   4:    public static  readonly DependencyProperty

   5:       ValuableProperty = DependencyProperty.Register(

   6:        "Valuable",

   7:        typeof( bool ),

   8:        typeof( MyCustomControl ),

   9:        new PropertyMetadata( new PropertyChangedCallback( MyCustomControl.OnValuablePropertyChanged ) ) );

  10:  

  11:    public bool Valuable

  12:    {

  13:       get { return (bool) GetValue( ValuableProperty );}

  14:       set { setValue( ValuableProperty, value );}

  15:    }

  16:  

  17:    private static void OnValuablePropertyChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )

  18:    {

  19:       MyCustomControl control = d as MyCustomControl;

  20:       bool b = (bool) e.NewValue;

  21:    }

  22: }

  23:  

これでほとんど終わりです。ただし、なんでこんなことをしたのかあなたが頭をひねっているであろうことをのぞけば。ということで、ここからさらに詳しく、このシステムの利点が何なのかを調べていきましょう。いったんここを通り過ぎることができれば、どんないいことが待っているでしょうか?

あわてず待っていてくださいね。さらに詳しい説明をお楽しみに。

-jesse

 (原文はこちら


※このエントリは ブロガーにより投稿されたものです。朝日インタラクティブ および ZDNet Japan編集部の見解・意向を示すものではありません。
  • 新着記事
  • 特集
  • ブログ
このサイトでは、利用状況の把握や広告配信などのために、Cookieなどを使用してアクセスデータを取得・利用しています。 これ以降ページを遷移した場合、Cookieなどの設定や使用に同意したことになります。
Cookieなどの設定や使用の詳細、オプトアウトについては詳細をご覧ください。
[ 閉じる ]