添付ビヘイビアとは
MVVMモデルを採用したWPFにおいて、Viewの状態変化に伴って実行される処理を定義したい場合に使用されるテクニックです。
MVVMの思想上View、つまりコードビハインドに処理を書くのはご法度ですが、
Viewで完結する処理をViewModelに書くのもあまりいいとは言えません。
そこで、WPFの添付プロパティを自作する処理にまとめてコードビハインドに書きたかった処理を記載することで
Viewのビヘイビア(= 振る舞い)を表現するテクニックになります。
イメージ
サンプルコード
実際に作成してみたビヘイビアがこちら。
usingSystem.Windows;usingSystem.Windows.Input;namespaceWPF.Views.Behaviors{internalclassMessageDialogBehavior{// 1. DependencyPropertyインスタンスを生成publicstaticreadonlyDependencyPropertyShowMessageProperty=DependencyProperty.RegisterAttached("ShowMessage",typeof(bool),typeof(MessageDialogBehavior),newPropertyMetadata(false,OnShowMessage));// 2. Getterを作成publicstaticboolGetShowMessage(DependencyObjecttarget)=>(bool)target.GetValue(ShowMessageProperty);// 3. Setterを作成publicstaticvoidSetShowMessage(DependencyObjecttarget,boolvalue)=>target.SetValue(ShowMessageProperty,value);// 4. callbackメソッドを定義privatestaticvoidOnShowMessage(DependencyObjectsender,DependencyPropertyChangedEventArgse){varelement=senderasUIElement;if(element==null){return;}varnewValue=(bool)e.NewValue;if((bool)e.NewValue){element.MouseRightButtonDown+=ShowMessage;}else{element.MouseRightButtonDown-=ShowMessage;}}privatestaticvoidShowMessage(objectsender,MouseButtonEventArgse){MessageBox.Show("Behavior Sample.");}}}添付プロパティで Trueを指定したコントロールで右クリックするとポップアップが表示されるものになります。
1. DependencyPropertyインスタンスを生成
まずは DependencyProperty.RegisterAttached()を利用してDependencyPropertyインスタンスを 生成します。DependencyProperty.RegisterAttached()が要求する4つの引数には次の内容を記載します。
| 引数 | |
|---|---|
| 第1引数 | プロパティ名を string指定します。この値が実際にXAMLで指定するプロパティ名になります。 |
| 第2引数 | プロパティの型を Typeで指定します。 |
| 第3引数 | プロパティを所有する型を Typeで指定します。今回の場合は自分自身である MessageDialogBehaviorになります。 |
| 第4引数 | メタデータを指定します。 実際の値としては PropertyMetadataのインスタンスになります。 |
PropertyMetadataについて
DependencyPropertyインスタンスの生成に必要な PropertyMetadataインスタンスですが、
デフォルトコンストラクタの他に4種類のオーバーロードで用意されていて、状況に応じて使い分けることになります。
各コンストラクタの引数は以下の通りです。
// ※一部省略namespaceSystem.Windows{publicclassPropertyMetadata{publicPropertyMetadata();publicPropertyMetadata(objectdefaultValue);publicPropertyMetadata(PropertyChangedCallbackpropertyChangedCallback);publicPropertyMetadata(objectdefaultValue,PropertyChangedCallbackpropertyChangedCallback);publicPropertyMetadata(objectdefaultValue,PropertyChangedCallbackpropertyChangedCallback,CoerceValueCallbackcoerceValueCallback);}}今回使用したのはこのコンストラクタです。
publicPropertyMetadata(objectdefaultValue,PropertyChangedCallbackpropertyChangedCallback);添付プロパティのデフォルト値と、値が変更された際のcallbackを指定しています。
※callbackがどのように使用されるかは後述
2. Getterを作成
添付プロパティの値を取得するためのメソッドを定義します。
引数の DependencyObjectの GetValue()メソッドの戻り値を
「1. DependencyPropertyインスタンスを生成」でRegisterAttached()の第2引数に指定した
型でキャストして返します。
※第2引数はプロパティの型でした
メソッド名について
Getterのメソッド名は必ず Get + プロパティ名とする必要があります。
今回の場合だとプロパティ名を ShowMessageとしているので、Geeterのメソッド名は GetShowMessageとなります。
3. Setterを作成
添付プロパティの値を設定するためのメソッドを定義します。
引数の DependencyObjectの SetValue()メソッドを使用して設定を行います。
メソッド名について
Getterと同じく、Setterのメソッド名も必ず Set + プロパティ名とする必要があります。
そのため今回の場合だと、メソッド名は SetShowMessageとなります。
4. callbackメソッドを定義
値が変更されたときに実行するcallbackメソッドを定義しています。
これは「1. DependencyPropertyインスタンスを生成」で登場したPropertyMetadataコンストラクタの第2引数として使用されています。
この中で少し独特だなと感じたのはこの部分でした。
if((bool)e.NewValue){element.MouseRightButtonDown+=ShowMessage;}else{element.MouseRightButtonDown-=ShowMessage;}DependencyPropertyChangedEventArgsの eで変更前と変更後の値を取得できるのですが
今回は特に何かしらの処理で値を変更するといった処理は行っていません。
にもかかわらず、変更前・変更後どちらからも値が取得できるようになっていました。
デバッグして確認してみたところ、どうやら値を変更するような処理を行っていなくても
変更前の値である e.OldValueにはデフォルト値が設定されているようでした。
で、このデフォルト値がどこで指定されているかというと...
publicstaticreadonlyDependencyPropertyShowMessageProperty=DependencyProperty.RegisterAttached("ShowMessage",typeof(bool),typeof(MessageDialogBehavior),newPropertyMetadata(false,OnShowMessage)// ここの第1引数);DependencyPropertyインスタンス作成時に指定していました!
初期値がfalseなので実質添付プロパティでTrueを指定するとcallbackメソッドが実行されるとなっています。
View側の記述
このビヘイビアを実際に使用するためのXAMLと使用結果は以下の通りです。
<Windowx:Class="WPFP.Views.BehaviorSample"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:local="clr-namespace:WPFP.Views"xmlns:b="clr-namespace:WPFP.Views.Behaviors"mc:Ignorable="d"Title="BehaviorSample"Height="450"Width="800"><StackPanel><TextBlockText="Trueにするとメッセージボックスが表示される"Margin="5"FontSize="20"b:MessageDialogBehavior.ShowMessage="True"/><TextBlockText="falseだとメッセージボックスが表示されない"Margin="5"FontSize="20"b:MessageDialogBehavior.ShowMessage="False"/></StackPanel></Window>実行結果はこんな感じ。
![]() |
|---|

