WPFでMVVM構造のアプリケーションを作成する場合、データバインドで使用するプロパティは『依存関係プロパティ』と『CLRプロパティ』の2種類が主な定義方法となります。たまにWPFに触るとどっちが何だっけ?となるので、本記事では使い分けとそれぞれの記述方法について整理しました。
- WPFでバインドプロパティの使い分け、記述方法
プロパティの定義方法の使い分け
依存関係プロパティとCLRプロパティのどちらとして定義するかは、バインド元となるか先となるかで判断します。以下のルールに従ってください。
ルール
バインドターゲットとなる プロパティ |
依存関係プロパティとして定義する。 (DependencyPropertyを使用して定義) |
---|---|
バインドソースとなる プロパティ |
CLRプロパティとして定義する。 (通常のプロパティとして定義。変更通知の実装は必要) |
『依存関係プロパティ』とは、要はバインドターゲットにできるプロパティのことです。
このため、バインドターゲットにするプロパティは必ず『依存関係プロパティ』としてDependencyPropertyで定義する必要があります。
逆に、バインドターゲットにならないプロパティは『CLRプロパティ』として定義します。バインドソースとする場合は変更通知も実装します。変更通知を実装しないとプロパティの値が変わってもビュー(画面)に反映されないため、バインドソースとして機能しません。
『データバインド』とは、『依存関係プロパティ』にプロパティを紐づけることのようです。
このため、バインドターゲットは必ず『依存関係プロパティ』である必要があります。
バインドターゲットとなるかどうかで『依存関係プロパティ』と『CLRプロパティ』のどちらで定義するかが必然的に決まります。
依存関係プロパティの定義
依存関係プロパティはコードビハインド(.xaml.cs/.xaml.vb)に記述します。
ViewModelに依存関係プロパティを記述することはありません。(記述してもビューへの自動反映等動きません)
例)[SampleControl.xaml.cs]
public partial class SampleControl : DependencyObject
{
// 1.依存関係プロパティの定義
public static readonly DependencyProperty Value1Property =
DependencyProperty.Register("Value1",
typeof(string),
typeof(MainWindow),
new FrameworkPropertyMetadata("defaultValue1", new PropertyChangedCallback(OnValue1Changed)));
// 2.依存関係プロパティへのアクセサプロパティを提供する
public string Value1
{
get { return (string)GetValue(Value1Property); }
set { SetValue(Value1Property, value); }
}
// 3.依存関係プロパティが変更されたとき呼ばれるコールバック関数の定義
private static void OnValue1Changed(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
// なんかやる
}
必要な定義としては、
- DependencyPropertyを使用して依存関係プロパティの実体を定義
(WPFに対して依存関係プロパティとしてValue1を登録しています。)
- 依存関係プロパティへのアクセサとしてのプロパティを定義
(ソース上はこのアクセサに対して値の参照、設定を行います。)
- 各種コールバック関数を定義
(必須ではありません。必要な場合のみ定義してください。)
となります。各引数の詳細はWPFのドキュメントを参照してください。
なお、定義先のクラスにはDependencyObjectが継承されている必要があります。
CLRプロパティの定義
CLRプロパティはViewModelに記載します。
プロパティのset処理にてビュー(画面)に対して変更通知を行う必要があります。
例)[SampleWindowViewModel.cs]
class SampleWindowViewModel :
INotifyPropertyChanged
{
// 1. 変更通知処理の実装
// 引数の[CallerMemberName]はこの関数をコールした関数名がそのまま入ります。
// ⇒ つまり、引数propertyNameにはNotifyPropertyChangedをコールしたプロパティの名前が入ってくる
// ここでは"Value2"。
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
// 2. バインドソースとなるCLRプロパティの定義
public string Value2
{
get { return lValue2; }
set {
lValue2 = value;
NotifyPropertyChanged(); // ← 変更通知
}
}
private string lValue2 = "Value2";
必要な実装としては、
- 変更通知処理の実装
PropertyChangedEventHandlerを該当プロパティ名を引数にしてコールします。※各ViewModelに変更通知処理を実装するのは無駄なので、ベースとなるViewModelクラスに実装し他のViewModelはそれを継承するようにした方が良いです。
- バインドソースとなるCLRプロパティの定義
set関数にて、ローカル変数に値格納後、変更通知処理をコールします。これでビューに対して変更通知が飛び画面に反映されます。
なお、変更通知処理定義先のクラスにはINotifyPropertyChangedが継承されている必要があります。
まとめ
基本的に
- カスタムコントロールなど画面内で使用するUIを作成する場合は、親画面からXAMLで指定するプロパティは依存関係プロパティで定義する。
- それ以外はCLRプロパティで定義する。
バインドソース用のプロパティは必要に応じて外からIntelliSenseで見えないように隠すと良いでしょう。
今回はデータバインドで使用する2種類のプロパティとその記述方法について解説しました。勉強中の身ですので、誤りや不備等ありましたらご指摘いただけますと幸いです。