DependencyObjectから子要素をたどってBehavior、Actionをみつける
それで何が嬉しいかっていうとActionからサスペンドした時に、Actionを探して復帰することで、簡単に処理を継続できるとかとか。具体的にはFileOpenPicker
用のアクションを作った時とか。
Windows ストアアプリは直接コマンドへ、Windows Phoneは復帰後にページからActionをみつけてコマンドへ。
子要素をたどる
まず、子要素をたどるにはVisualTreeHelper
を使います。しかし、低級すぎて使いにくいので拡張メソッドを用意するといいです。
public static IEnumerable<DependencyObject> Children(this DependencyObject obj) { if (obj == null) throw new ArgumentNullException("obj"); var childrenCount = VisualTreeHelper.GetChildrenCount(obj); for (int i = 0; i < childrenCount; i++) { yield return VisualTreeHelper.GetChild(obj, i); } } public static IEnumerable<DependencyObject> Descendants(this DependencyObject obj) { if (obj == null) throw new ArgumentNullException("obj"); foreach (var child in obj.Children()) { yield return child; foreach (var grandChild in child.Descendants()) { yield return grandChild; } } }
これでpage.Descendants()
なんてやると子要素をごっそり取れます。
Behavior、Actionをみつける
Behavior、Actionは表示要素ではないので子要素の中に入っていません。ここではInteraction.GetBehaviors
を使います。
要素を引数にして関連づけられたBehaviorCollection
が取得できます。ここまでくれば何とかなる!こちらも拡張メソッドを定義します。
public static T FindAction<T>(this DependencyObject obj) where T: IAction { return obj .Descendants() .SelectMany(Interaction.GetBehaviors) .SelectMany(x => { var eventTrigger = x as EventTriggerBehavior; if (eventTrigger != null) { return eventTrigger.Actions; } var dataTrigger = x as DataTriggerBehavior; if (dataTrigger != null) { return dataTrigger.Actions; } return null; }) .OfType<T>() .SingleOrDefault(); }
Behaviorをみつける場合はSelectMany(Interaction.GetBehaviors).OfType
でいいです。Actionをみつける場合はActions
をもつBehaviorから展開する必要があるので、ここではSDKに含まれているBehaviorを対象としました。