こんにちは、働くC#プログラマーのさんさめです。
前回、C#コードから
VisualTreeの祖先要素を取得する方法を書きました。
今回は逆に、
VisualTreeの子孫要素を取得する方法を解説します。
祖先要素を取得するAPIと同様に、
VisualTreeHelperクラスにGetChild
というメソッドがあるのですが…

Helperを名乗る割に使いにくいんだってば
さらに残念なことに
GetChildは「何番目の子孫要素を取得するか」
を引数に要求するため、
使うときの面倒さがGetParentの比ではありません。
というわけで、前回に引き続き、
さんさめが実際に働く上で用いている
GetChildのラッパー実装を紹介します。
直近の子孫要素を取得するラッパーメソッド
さて、やはりというかなんというか、
子孫要素を辿るときも、
「型を指定」して「最初に見つかるもの」
が欲しいことが大半です。
この傾向は祖先要素の取得よりもさらに顕著です。
VisualTreeの子孫要素を取得したい時は、
ほとんどの場合、
「Templateになっているものにアクセスして
ごにょごにょしたい」ケースだからですね。
- ListBoxに内包されているScrollViewer
- DataGridに内包されている表左上のButton
- DataGridに内包されているDataGridColumnHeadersPresenter
- DataGridに内包されている…
といったものです。

ほとんどDataGridやないかい
他にもWPFに慣れてくると思い始める
「こいつがこう動いてくれればなぁ…」
というケースでは大抵使えます。
というわけでコードサンプルです。
public static class VisualTreeHelperWrapper
{
public static T FindDescendant<T>(this DependencyObject depObj)
where T : DependencyObject
{
if (depObj == null) { return null; }
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
var result = (child as T) ?? FindDescendant<T>(child);
if (result != null) { return result; }
}
return null;
}
}
使用例は以下の通りです。

VisualTreeを辿っているので、
xaml上には記述していないScrollViewerを
取得できていることが分かります。
子要素を辿る場合は実行タイミングに注意が必要
ちなみに、先ほどの使用例では、
しれっとLoadedイベントを購読したうえで使用していました。
なぜなら、InitializeComponentの直後だと
まだTemplateの適用が済んでいないため、
ScrollViewerが存在しないからです。

そのため、先ほどの使用例ではあえて
Loadedイベントを購読しています。
ただし、Loadedイベントは
View構築後以外にも結構頻繁に飛んでくるので
一回だけ必要な処理を行いたいのであれば、
すぐさま購読解除した方が良いですね。
VisualTreeの子孫要素を辿るヘルパーメソッドの実際の使用例
このヘルパーメソッドを使って、
さんさめが実務上で
非常に役立てている機能が1つあります。
それは、
【WPF】ScrollViewerを入れ子にした時の挙動をいい感じにする
です。
記事のサンプルコードをよく見ると使っています。
サンプルコードなのでメソッド名が違いますが(笑)
ScrollViewerは直接配置することが少ないので、
上記のようにVisualTreeを勝手に辿ってくれると、
xaml記述をとっても省力化できます。
VisualTreeの子孫要素を列挙するメソッドはあんまり使わない
さて、前回の記事の対比でいくと、
次は子孫要素をすべて列挙するメソッドが
でてくるところですが…。

実は列挙バージョンは
使ったことないので不要説アリ
ということなんですね。
正直、子孫を列挙してどうこうしたいケースには
まだ遭遇していません。
もし、「こういうときにやりたくなったよ!」
というものがあれば教えていただければ幸いです。
まとめ
まとめです。
- VisualTreeの子要素を取得する公式APIは、
プリミティブで使いづらい - 子孫要素の中から指定した型を取得する方法を紹介
- 列挙として返す方法もできなくはないが、
さんさめは使いたくなるケースに遭遇したことがない
最後までお読みいただき、ありがとうございました。
関連記事
子要素ではなく、親要素を辿りたいこともあります。
C#コードで親要素を辿りたいなら、
VisualTreeHelper.GetParentをラップした
メソッドを作ると良いでしょう。
【WPF】Viewの祖先要素をC#コードから取得する方法
で実際のラッパーメソッドを紹介しています。
コメント