独自のTriggerBehaviorを作る方法 #win8dev_jp
先日の記事とアドベントカレンダーで連携した記事でビヘイビアーとトリガーアクションにしか触れていなくて"トリガー"について触れていませんでした。*1
そこで独自のトリガー(ここではMyTriggerBehaviorとします)を順を追って作成します。
TriggerBehaviorに必要な要素
まず、Windows ストアアプリ標準ライブラリに実装されているEventTriggerBhaviorとDataTriggerBehaviorからトリガーとは何かを見てみましょう。
オブジェクトブラウザーのスクショを並べて加工しました。
共通点がめちゃんこ少ないですね。ここで更に独自のビヘイビアーのクラス作成の初期状態を見てみましょう。
おわかりいただけただろうか。
Actionsプロパティを追加すればいいことがわかりますね。
Behaviorの作成
Actionsプロパティを追加する前にビヘイビアーとしての最低限を実装しておきます。
public DependencyObject AssociatedObject { get { return this.associatedObject; } } private DependencyObject associatedObject; public void Attach(DependencyObject associatedObject) { this.associatedObject = associatedObject; } public void Detach() { this.associatedObject = null; }
Actionsプロパティの実装
オブジェクトブラウザーによるとActionCollection型のプロパティでgetterのみです。XAMLで取り扱うので依存関係プロパティとして定義します。スニペットがあるのでpropdpでTabキー叩いてください。そしてsetterを消す。
public ActionCollection Actions { get { return (ActionCollection)GetValue(ActionsProperty); } } public static readonly DependencyProperty ActionsProperty = DependencyProperty.Register( "Actions", typeof(ActionCollection), typeof(MyTriggerBehavior), new PropertyMetadata(null));
とりあえずこんな状態。
ひとまず、ここまででトリガーアクションを配下に置けるTriggerBehaviorが出来ました。Blendでぽとぺたぽちぽちしてみると確認できます。
<Interactivity:Interaction.Behaviors> <local:MyTriggerBehavior> <local:MyTriggerBehavior.Actions> <Core:ChangePropertyAction/> <Core:InvokeCommandAction/> </local:MyTriggerBehavior.Actions> </local:MyTriggerBehavior> </Interactivity:Interaction.Behaviors>
Triggerの実装
何をトリガーにしてActionsが実行されるのかを定義します。ここでは親オブジェクトがButtonだと想定してClickイベントをトリガーとします。
private void RegisterTrigger() { var button = this.associatedObject as Button; if (button == null) { return; } button.Click += OnClick; } private void UnRegisterTrigger()[...] private void OnClick(object sender, RoutedEventArgs args) { foreach (var item in this.Actions) { var action = (IAction)item; action.Execute(sender, args); } }
RegisterとUnRegisterをAttachとDetachでそれぞれ呼んでください。OnClickではトリガーアクションを一つ一つ実行しています。色々漁ってるとMicrosoft.Xaml.Interactivity名前空間にInteraction.ExecuteActionsという補助があることに気づいたりして、簡単に書けます。
private void OnClick(object sender, RoutedEventArgs args) { Interaction.ExecuteActions(sender, this.Actions, args); }
実行
いよいよ実行です。先日と同じくMessageNotifyAction*2を置いて実行してみます。
<Button Content="トリガー"> <Interactivity:Interaction.Behaviors> <local:MyTriggerBehavior> <local:MyTriggerBehavior.Actions> <local:MessageNotifyAction Message="アクション実行"/> </local:MyTriggerBehavior.Actions> </local:MyTriggerBehavior> </Interactivity:Interaction.Behaviors> </Button>
何やら実行時例外がおきました。
collection propertyがnullだとおっしゃってます。最低限で突貫してきたツケです。collection propertyと言えばActionsですのでget時にきちんとインスタンスを返すようにします。
public ActionCollection Actions { get { var actionCollection = (ActionCollection)GetValue(ActionsProperty); if (actionCollection == null) { actionCollection = new ActionCollection(); SetValue(MyTriggerBehavior.ActionsProperty, actionCollection); } return actionCollection; } }
えいっ!
できました!
隠し味
一応、完成しましたがXAMLの書き味がよろしくないのでContentProperty属性を追加します。
[ContentProperty(Name = "Actions")] public class MyTriggerBehavior : DependencyObject, IBehavior
<local:MyTriggerBehavior> <local:MessageNotifyAction Message="アクション実行"/> </local:MyTriggerBehavior>
まとめ
IBehaviorの実装に加えて、ActionCollection型のプロパティ、実行の起点、つまり、トリガーを実装することで独自のTriggerBehaviorを作ることが出来ました。
gistに全文を置いときましたので参照してください。
おまけ
Visual StudioでALT + ドラッグで範囲選択できますよ。盲点。