2015/04/13追記 Win7 64bitマシンで、プロジェクトの対象のフレームワークを.Net4.5として、マシンには.Net4.5.2をインストールした状態で、仮想化したTreeViewの要素をExpandしたときにスクロール位置が狂う(Expandした要素やその子供が範囲外に隠れる)現象を確認しています。 恐らく https://connect.microsoft.com/VisualStudio/feedback/details/783812/c-wpf-treeview-virtualized-issue-with-treeview-scrolling-on-item-expanded と同様の現象です。
2015/05/24追記 スクロールバーをマウス操作している時に、UIが完全にフリーズする現象が発生しました。https://connect.microsoft.com/VisualStudio/feedback/details/790241/mousescrooll-freezes-virtualizingpanel-if-isvirtualizingwhengrouping-and-scrollunit-pixel-set で言及されているように、`VirtualizingPanel.ScrollUnit="Item"`と指定したところ、今のところは再発していません。
背景
- WPFでDataGridとTreeViewを組み合わせたコントロールはどうすれば実現できるかググる。
- MSDNでTreeListViewという自作コントロールのサンプルや解説を見つけたので触ってみる。
- スクロールバーがついていなかったので、見つけた記事を参考にScrollViewerをControlTemplateに組み込んでみる。(余談ですが、この内容だと幅が足りない時に縦スクロールバーが隠れてしまいます。※2015/04/09追記 ScrollViewer with non scrolling area (fixed header) の方法だとうまく行きました。)
- ふと試しに1ノードの下に1000の子要素を設定してExpandしてみると、数秒かかることが判明。UIの仮想化を試してみる。
- StyleやTemplateを未指定のTreeViewでは VirtualizingStackPanel.IsVirtualizing="True" を指定すると軽快な動作になる。けれど、2〜3で触っていたTreeListViewでは効果がなかった。
原因
Livetで始めるWPF(ざっくり)入門で、コントロールからControlTemplateを抜き出す方法を紹介されていたことを思い出し、TreeViewのTemplateを抜き出すと次の内容がありました(要所のみ抜粋)。
<ControlTemplate x:Key="TreeViewControlTemplate1" TargetType="{x:Type TreeView}"> <Border ...> <ScrollViewer x:Name="_tv_scrollviewer_" ...> <ItemsPresenter /> </ScrollViewer> </Border> <ControlTemplate.Triggers> ... <Trigger Property="VirtualizingPanel.IsVirtualizing" Value="True"> <Setter Property="CanContentScroll" TargetName="_tv_scrollviewer_" Value="True" /> </Trigger> </ControlTemplate.Triggers> </ControlTemplate>
どう見ても大事なTriggerが抜け落ちていました。というわけでTriggerを追加(ScrollViewerを2つ設置しているので両方共設定)すると、UIの仮想化に成功し、Expandが一瞬で済むようになりました。
なお、MSDNのOptimizing Performance: Controlsのページを見ると
The following is a list of conditions that disable UI virtualization.
...
Setting CanContentScroll to false.
とあり、かつScrollViewer.CanContentScrollのデフォルト値はfalseでした。
教訓:ドキュメントをちゃんと読まないとバチが当たる。