ここまでにも名前だけはたびたび出てきているが、WPFでは「依存関係プロパティ(dependency property)」という、通常のプロパティ(区別のために、WPFの文脈においては「CLRプロパティ」と呼ぶことがある)とは異なる「値の保持機構」を持っている。
依存関係プロパティは以下のような用途を想定して作られている(Figure 3)。
これらは、いずれも「ほかの要素の値に依存してプロパティの値を決定する機構」といえる。依存関係プロパティという名前はここから来ている。
リソース、スタイル、および、データ・バインディングに関する詳細は次回以降で説明する。今回は、依存関係プロパティの定義および利用方法について説明していく(包含継承の説明を含む)。
●依存関係プロパティの定義
まずList 5に、依存関係プロパティを定義するクラスの最低限の実装例を示す。
public class Point : DependencyObject
{
public int X
{
get { return (int)GetValue(XProperty); }
set { SetValue(XProperty, value); }
}
public static readonly DependencyProperty XProperty =
DependencyProperty.Register(
"X", typeof(int), typeof(Point),
new UIPropertyMetadata(0));
}
Public Class Point
Inherits DependencyObject
Public Property X() As Integer
Get
Return CType(GetValue(XProperty), Integer)
End Get
Set(ByVal value As Integer)
SetValue(XProperty, value)
End Set
End Property
Public Shared ReadOnly XProperty As DependencyProperty = _
DependencyProperty.Register( _
"X", GetType(Integer), GetType(Point),
New UIPropertyMetadata(0))
End Class
依存関係プロパティの定義には、DependencyPropertyクラス(System.Windows名前空間)を用いる。ただし、DependencyPropertyクラスのインスタンス自身は、プロパティそのものというよりは辞書のキーのようなもので、実際に値を保持するのはDependencyObjectクラス(System.Windows名前空間)(の子クラス)になる。
依存関係プロパティは以下のようにして定義する(Figure 4)。
実際の値の読み書きはDependencyObjectクラスのSetValue/GetValueメソッドを通して行う。また、内部的にSetValue/GetValueメソッドを呼び出すだけのCLRプロパティ(この例の場合、Xプロパティ)も定義しておく。ただし、このCLRプロパティ内ではSetValue/GetValueメソッド呼び出し以外の処理を行ってはならない。依存関係プロパティは、必ずしもこのCLRプロパティを通して呼ばれるわけではなく、データ・バインディングなどを行うとSetValue/GetValueメソッドが直接呼び出される場合があるため、CLRプロパティ内に記述した処理は行われないことがある。
●添付プロパティの場合
依存関係プロパティの仕組みは、添付プロパティを実現するためにも利用される。依存関係プロパティを添付プロパティとして利用したい場合には、List 6に示すように、Registerメソッドの代わりにRegisterAttachedメソッドを用いた登録を行う。
public class MyCanvas
{
public static int GetX(DependencyObject obj)
{
return (int)obj.GetValue(XProperty);
}
public static void SetX(DependencyObject obj, int value)
{
obj.SetValue(XProperty, value);
}
public static readonly DependencyProperty XProperty =
DependencyProperty.RegisterAttached(
"X", typeof(int), typeof(MyCanvas),
new UIPropertyMetadata(0));
}
Public Class MyCanvas
Public Shared Function GetX(ByVal obj As DependencyObject) As Integer
Return CType(obj.GetValue(XProperty), Integer)
End Function
Public Shared Sub SetX(ByVal obj As DependencyObject, ByVal value As Integer)
obj.SetValue(XProperty, value)
End Function
Public Shared ReadOnly XProperty As DependencyProperty = _
DependencyProperty.RegisterAttached( _
"X", GetType(Integer), GetType(MyCanvas),
New UIPropertyMetadata(0))
End Class
また、添付プロパティの場合には、CLRプロパティの代わりに「Setプロパティ名」「Getプロパティ名」(上記のコードの例では「GetX」「SetX」)という名前の静的メソッドを定義しておく。通常の依存関係プロパティの場合と同様、これらのSet/Getメソッドの内部では、DependencyObject.SetValue/DependencyObject.GetValueメソッド呼び出し以外の処理を行ってはならない。
●オーナー・クラスの追加
ほかのクラスで定義された既存の依存関係プロパティを自作のクラスでも利用したい場合、DependencyProperty.AddOwnerメソッドを用いることで、WPFに登録情報を追加できる。例えば、標準のTextBoxクラスで定義されいてるText依存関係プロパティを、自作のMyControlクラスでも利用したい場合、List 7に示すような記述を行う。
using System.Windows;
using System.Windows.Controls;
public partial class MyControl : UserControl
{
public static DependencyProperty TextProperty =
TextBox.TextProperty.AddOwner(typeof(MyControl));
}
Public Class MyControl
Public Shared TextProperty As DependencyProperty =
TextBox.TextProperty.AddOwner(GetType(MyControl))
End Class
この仕組みは例えば、ComboBoxクラス(System.Windows.Controls名前空間)などのIsSelected依存関係プロパティなどで利用されている。ComboBoxクラスの子要素となるComboBoxItemクラスに対してIsSelected依存関係プロパティを設定する場合、本来なら、「<ComboBoxItem ComboBox.IsSelected="true"/>」というように、添付プロパティとして設定する必要があるが、IsSelected依存関係プロパティはオーナー・クラスの追加の仕組みを使ってComboBoxItemクラス側にも定義されていて、「<ComboBoxItem IsSelected="true"/>」と書くことができる。
●依存関係プロパティの意義
WPFが、CLRプロパティではなく、わざわざ依存関係プロパティのような仕組みを利用する意義は、以下のような点にある。
(1)パフォーマンス
依存関係プロパティは一種の辞書構造になっているが、これはデータ・バインディングのパフォーマンスの向上や、添付プロパティの実現のために利用されている。
CLRプロパティを用いてデータ・バインディングのような仕組みを実現するためには、リフレクションに頼ることになる。一般に、リフレクションの利用は著しくパフォーマンスを低下させることがある。プロパティの値を辞書的に持てば、リフレクションの利用を避けることができ、パフォーマンスの向上が見込める。
また、辞書的に値を持つことで、添付プロパティ(=要素自身ではなく、親要素で利用する値を保持する機構)も実現可能である。
(2)メタデータの保持
依存関係プロパティは、次節で説明するようなメタデータを持つことができる。
●メタデータ
メタデータはPropertyMetadataクラス(System.Windows名前空間)、もしくは、その子クラスのインスタンスとして定義する。利用場面に応じて以下の3つのうちのいずれかを用いる(いずれもSystem.Windows名前空間に所属するクラス)。
PropertyMetadataクラスは、WPFに限らず幅広い用途で利用することを想定したもので、メタデータとして最低限の情報を持っている。PropertyMetadataクラスの持っているプロパティのうち、主要なものを以下に示す。
UIPropertyMetadataクラスは、UI要素向けのメタデータで、PropertyMetadataクラスの持つ情報に加えて、アニメーションの可否を表すIsAnimationProhibitedプロパティを持つ。
FrameworkPropertyMetadataクラスは、WPF用のメタデータで、UIPropertyMetadataクラスの持つ情報に加えて、WPF固有の豊富な機能向けの情報を持っている。FrameworkPropertyMetadataクラスのプロパティのうち、主要なものを以下に示す。
●値の優先順位
依存関係プロパティを利用する際、包含継承やスタイルなどを併用すると、複数の個所から値が設定されることになる。この場合、値の設定元には優先順位があり、順位の高いものが依存関係プロパティの値に反映される。優先順位は以下のとおりである(上に行くほど優先度が高い)。
最後に、ルーティング・イベントについて解説する。
Copyright© Digital Advantage Corp. All Rights Reserved.