Виртуализация вложенных контейнеров (virtualizingstackpanel) на одной родительской полосе прокрутки в WPF

Я как бы закодировал себя в этом. Я пишу пользовательский элемент управления WPF, который похож на TreeListView описано в этой статье MSDN, а также во многих других местах в сети. На данный момент довольно большая куча этой вещи - обычай, и она вполне соответствует моим целям, за исключением виртуализации. Мой переопределенный TreeView а также TreeViewItem оба шаблона используют VirtualizingStackPanel s, чтобы представить свои предметы, и я убедился, что все это создается, как и ожидалось. Виртуализация работает правильно на элементах корневого уровня (только элементы пользовательского интерфейса для тех, которые в настоящее время видны в ScrollViewer приготовлены), а TreeView вещи заботятся о том, чтобы не генерировать элементы для свернутых узлов. Проблема возникает при расширении узла - все элементы готовятся для каждого дочернего узла, даже тысячи, которые находятся за пределами экрана.

Мне казалось, что все, что мне нужно было сделать - это установить свойство scrollowner для внутреннего вложенного элемента. VirtualizingStackPanel s к тому же основному представлению прокрутки, к которому VSP корневого уровня подключен по умолчанию, но я прочитал плакат MSFT, в котором говорится, что это не будет работать.

К сожалению, эта штука медленна, как грязь, без виртуализации, поэтому мне нужно найти какое-то решение. Любые предложения будут ценны.

2 ответа

Я знаю, что это старый вопрос, но он все еще был актуален для меня.

В моем случае я подумал TreeView Я бы не обрезал его, потому что мне нужно ровно два слоя, и типы отображаемых элементов различаются между двумя слоями. Также я занимаюсь рефакторингом списка ExpanderТак что я думал более одномерно.

Но потом я понял, что вы можете настроить ItemTemplate из TreeView включить свой собственный HierarchicalDataTemplateи что вы можете настроить ItemTemplate того, что HierarchicalDataTemplate с вашими собственными DataTemplate... Престо! Ровно два слоя с разными вещами в каждом!

Так что я хочу сказать, что TreeView достаточно гибок, чтобы вы не пытались создать свой собственный элемент управления.

Вот что я сделал:

XAML:

<Page x:Class="Foo.Views.TreePage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      xmlns:local="clr-namespace:Foo.Views"
      xmlns:viewModel="clr-namespace:Foo.ViewModels"
      mc:Ignorable="d" 
      d:DesignHeight="300" d:DesignWidth="300"
      Title="Tree page"
      d:DataContext="{d:DesignInstance Type=viewModel:TreeModel}">
    <TreeView
        VirtualizingPanel.IsVirtualizing="True"
        VirtualizingPanel.VirtualizationMode="Recycling"
        ScrollViewer.CanContentScroll="True"
        VirtualizingPanel.ScrollUnit="Pixel"
        ItemsSource="{Binding Values}"><!-- IList<Person> -->
        <TreeView.ItemTemplate><!-- Template for first layer, which has a HierarchicalDataTemplate so that this layer will expand -->
            <HierarchicalDataTemplate
                ItemsSource="{Binding Expenses}"><!-- IList<Expense> -->
                <HierarchicalDataTemplate.ItemTemplate><!-- Template for the second layer, which has a DataTemplate instead of HierarchicalDataTemplate so that this layer won't expand -->
                    <DataTemplate>
                        <TextBlock Text="{Binding Amount}"/><!-- Expense amount in dollars -->
                    </DataTemplate>
                </HierarchicalDataTemplate.ItemTemplate>

                <TextBlock Text="{Binding Name}"/><!-- Person name -->
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
    </TreeView>
</Page>

Person учебный класс:

public class Person
{
    public string Name { get; set; }
    public List<Expense> Expenses { get; set; }
}

Expense учебный класс:

public class Expense
{
    public double Amount { get; set; }
}

Вот как это выглядит:

Скриншот приведенного выше кода в действии

Я проверил это с помощью Snoop, чтобы доказать, что это виртуализация пользовательского интерфейса. Вот количество загруженных TreeViewItemКогда приложение маленькое:

Снимок экрана, показывающий количество элементов, когда приложение маленькое

... а вот количество загруженных TreeViewItemКогда приложение работает в полноэкранном режиме (оно выходит за рамки этого фрагмента, но вы поняли):

Снимок экрана, показывающий, что в полноэкранном режиме приложения больше элементов

Теперь нужно просто стилизовать вещи так, чтобы вложенный слой выглядел так, как я хочу!

Изменить: я только что подтвердил, что TreeView виртуализирует все его слои, а не только первый.

У меня была ссылка на список того, чего не следует делать с виртуализированной стековой панелью, но по какой-то причине страница оказалась пустой. Вот еще одна страница, которая немного говорит об этом:

http://www.designerwpf.com/2008/02/12/listview-and-listbox-performance-issues/

и даже ссылки на тот, о котором я говорю, но он всегда пуст. если вы посмотрите на эту страницу, это ссылка на блог Марка Шурмера. Вот ссылка, если вы хотите попробовать это:

http://itknowledgeexchange.techtarget.com/wpf/listview-is-it-really-too-slow/

У Bea Stollnitz также есть несколько статей, которые могут помочь:

Часть 1 Часть 2 Часть 3

Если вы опубликуете часть своего кода, кто-то может немного разгадать спагетти, чтобы помочь вам получить лучшую реализацию.

РЕДАКТИРОВАТЬ: нашел ссылку, которая может работать (спасибо archive.org!!!): http://web.archive.org/web/20080104163725/http://itknowledgeexchange.techtarget.com/wpf/listview-is-it-really-too-slow/

Другие вопросы по тегам