WPF

【WPF】バインドプロパティの定義と使い分け

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)
	{
	    // なんかやる
	}

必要な定義としては、

  1. DependencyPropertyを使用して依存関係プロパティの実体を定義
    (WPFに対して依存関係プロパティとしてValue1を登録しています。)
  2. 依存関係プロパティへのアクセサとしてのプロパティを定義
    (ソース上はこのアクセサに対して値の参照、設定を行います。)
  3. 各種コールバック関数を定義
    (必須ではありません。必要な場合のみ定義してください。)

となります。各引数の詳細は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";

必要な実装としては、

  1. 変更通知処理の実装
    PropertyChangedEventHandlerを該当プロパティ名を引数にしてコールします。

    ※各ViewModelに変更通知処理を実装するのは無駄なので、ベースとなるViewModelクラスに実装し他のViewModelはそれを継承するようにした方が良いです。

  2. バインドソースとなるCLRプロパティの定義
    set関数にて、ローカル変数に値格納後、変更通知処理をコールします。これでビューに対して変更通知が飛び画面に反映されます。

なお、変更通知処理定義先のクラスにはINotifyPropertyChangedが継承されている必要があります。

まとめ

基本的に

  • カスタムコントロールなど画面内で使用するUIを作成する場合は、親画面からXAMLで指定するプロパティは依存関係プロパティで定義する。
  • それ以外はCLRプロパティで定義する。

バインドソース用のプロパティは必要に応じて外からIntelliSenseで見えないように隠すと良いでしょう。

今回はデータバインドで使用する2種類のプロパティとその記述方法について解説しました。勉強中の身ですので、誤りや不備等ありましたらご指摘いただけますと幸いです。

COMMENT

メールアドレスが公開されることはありません。

CAPTCHA