Ktouth Brand. on Web

け〜くんこと K.Ktouth のだらだらした日常と突発的に作るプログラムや読み物とかの雑多サイト



[2008年01月18日]

依存プロパティの使い道

2008年01月18日 09:36更新 筆者:K.Ktouth

Visual Studio 2008 Professional Edition アップグレード(AA)

WPFを利用するにあたり、クラスのプロパティの実装方法として「フィールドプロパティ」「依存プロパティ」「添付プロパティ」の3つの方法が提示されています。
んが。
対象クラスが持っていない(状況に依存する)プロパティを追加できる添付プロパティは用途が理解できたんですが、クラスの外から見た場合、フィールドプロパティと依存プロパティの差が実装手段以外にあるように見えなかったんですね。いくつかの制限が増えるだけじゃないのか、と。

昨日ちょっとしたクラスを設計していて、その辺をようやく理解。
つまり依存・添付プロパティの最大のメリットは「データ連結の対象プロパティに指定できる」って事なんですね。納得。
軽くまとめると……

  • フィールドプロパティ

    C#言語、ひいてはCLR環境における標準仕様のプロパティの実装方法。CLR標準なので言語やランタイムバージョンに依存しない。
    シンプルなコードで実装が可能。
    データ連結のソースとしては指定可能だが、対象にする事は出来ない。

  • 依存プロパティ

    WPFで提供されている新しい手法のプロパティの実装方法。
    ライブラリの一部としての提供なので言語には依存しないが、CLR3.0以上(WPF/WF)が必要。
    言語でのサポートは特にないので、コードが若干複雑で定型的な記述が必要になる。
    データ連結の対象にする事が出来る。
    依存プロパティを持つクラスは DependencyObject 型から派生していないといけない。依存プロパティ情報をstaticで持つ。

  • 添付プロパティ

    特定の状況に依存する情報をオブジェクトに「後から追加する」というプロパティの実装方法。
    基本部分は依存プロパティに準ずる。依存プロパティよりは複雑な記述が増える。
    データ連結の対象にする事が出来る。
    主にヘルパーメソッドと依存プロパティ情報をstaticで持つ。

こんな感じかな?

依存プロパティと INotifyPropertyChanged インターフェイス

WPFで利用するデータクラスは INotifyPropertyChanged インターフェイスを持つのが普通だけど、依存プロパティと組み合わせるときにはコードの記述の仕方が変わります。

以下、フィールドと依存でのプロパティの実装の仕方の違い。

class Foo : DependencyObject, INotifyPropertyChanged
{
 public static readonly BarProperty = DependencyProperty.Register(
  "Bar", typeof(int), typeof(Foo), new PropertyMetaData(OnBarPropertyChanged)
 );

 private static void OnBarPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
 {
  if (!e.NewValue.Equals(e.OldValue))
  {
   Foo value = (Foo)sender;
   value.OnPropertyChanged("Foo");
  }
 }

 public int Bar
 {
  get { return (int)GetValue(BarProperty); }
  set { SetValue(BarProperty, value); }
 }

 private int _Buzz;
 public int Buzz
 {
  get { return _Buzz; }
  set
  {
   if (_Buzz != value)
   {
    _Buzz = value;
    OnPropertyChanged("Buzz");
   }
  }
 }

 public event PropertyChangedEventHandler PropertyChanged;
 protected void OnPropertyChanged(string name)
 {
  if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(name)); }
 }
}

Barが依存プロパティ、Buzzがフィールドプロパティの例。
Buzzはsetメソッドの中で値の変更後にOnPropertyChangedを呼び出すコードを記述していますが、依存プロパティのそれらの処理はsetコードの中では行いません。
# そもそもBarプロパティの実装は、コード記述の簡便化のためのラッパーでしかない。

で、どうするかというと DependencyProperty 定義時にイベントハンドラを追加し、そのイベントハンドラをstatic メソッドとして定義します。
イベント引数には NewValue と OldValue もありますので、今回は同値でない場合のみにPropertyChangedイベントハンドラを呼び出すようにしました。
値の検証などはまた別のメソッドを実装し、定義時の引数を追加する必要があります。

これで、即値を与えても良し、Binding オブジェクトを生成してデータ連結をしても良しの万能プロパティが使用可能になります。
ただし、FrameworkElement クラスの持つ SetBinding メソッドはありませんので、データを連結するときには BindingOperation.SetBinding メソッドを利用します。この場合は DataContext をたぐる機能はないはずなので、Binding オブジェクトには必ずソースを指定する必要があります。(未確認)

本日のリンク元
アンテナ
その他のリンク元
検索