C#+WPFチューニング戦記

C#とWPFで高速なコードと最適なシステムを書くためにやってきたいろいろな事を書いてみます。.NET Frameworkのソースコードを読み解きましょう。なお、ここに書かれているのは個人の見解であって何らかの団体や企業の見解を代表するものではありません。

2つの親子関係

タイトルのようなことを書くと、普通はロジカルツリーとビジュアルツリーというのが相場ですが、これは別の話です。
ビジュアルツリーは2つの繋がりを持って初めてビジュアルツリーなのです。

  1. AddVisualChild()で接続される、WPFの描画順序に関わるツリー
  2. VisualChildCountとGetVisualChildでつながる、いわゆるところのビジュアルツリー(通常、overrideするもの)

Panelに慣れてしまうとChildrenがあたかも直接取り持ってくれているように錯覚しがちです。
実は、両方つながっていないとMeasureもArrengeもOnRenderもまともに動きません。

そう、考えてみてください。
Panel.ZIndexで描画順序が決まり、Childrenは接続した順番を維持しているのです。
これはつまり2つの繋がりはまったく別個で、描画順序を入れ替えたいときは、ユーザーコード側からは一度RemoveVisualChildして、表示したい順序で接続しなおす必要があるのです。

WPF側からはMeasureパス、Arrangeパスの順序のためのGetVisualChildとVisualChildCountです。
ちなみにOnRenderはArrangeの中で直接呼ばれています。
しかし、なんとOnRenderは描画命令をバッファリングするだけ。その順序で描画されないという事実を知ったとき、描画順序はWPF内部の親子リレーションシップの順序に固定されているのだと理解しました。
そして、RecomputeZStateはそのリレーションシップを並び替える最大のボトルネックなのです。

でも、この話をすると、Panel.ZIndexに振り回されずにWPFを最適に動かす描画方法のヒントが見えてくるのではないでしょうか。
あと、ロジカルツリーなんてどこまで最適化しても(もっとはっきり書くとViewModel層をどこまで綺麗に書いても)、ビジュアルツリーの制御が速くならないことには全くもって速くならないということが分かるでしょう。