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

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

Panel.Chidrenのボトルネックについて

Canvasにせよ、Gridにせよ、みんなPanel継承しています。

ですから、Childrenに子コントロールを追加していくことになります。ChildrenはUIElementCollectionです。UIElementCollectionの中身を知っておくことは重要です。中身はVisualCollectionなのですが、これが実は結構曲者です。

http://referencesource.microsoft.com/#PresentationCore/src/Core/CSharp/System/Windows/Media/VisualCollection.cs#721

これがVisualCollection.Insert()の実装ですが、注目すべきはこの部分。

Visualに子を追加している様のソースを追跡すると、巡り巡ってこんな処理にたどり着きます。 

http://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/Panel.cs#1001

OnVisualChildrenChangedは、VisualTreeが変化するごとに呼び出されます。Panel.ZIndexの変更時と、VisualTreeに対する子の追加が行われる都度、といえばわかりやすいでしょうか。

ともかく、1つのパネルに追加するコントロール数は極力抑えるべきです。*1

 

場合によっては、パネル内にZIndex単位の層のパネルを内包して、その中に子を追加するなどするのが良いでしょう。ただし、その場合Invalidate系の処理は複雑になることがありますが。*2

あと、無難なところでは仮想化などが現実解でしょうか。

いずれにしても、通常のパネルに1000もの要素を追加していることがあるとするならば、何やら応答が遅い理由はこのあたりにあるのかもしれません。

*1:試しに3万程度の子をパネルに追加してみたら、30分くらい制御が返ってこなかったこともあります。

*2:これにはAffectsParentMeasure、AffectsParentArrangeなどは直上の親までしか伝わらない等の事情があります。