virtualizing nested containers (virtualizingstackp

2019-07-23 05:28发布

问题:

I've kind of coded myself into a pickle on this one. I am writing a custom WPF control which is similar to the TreeListView described in This MSDN article as well as many other places on the net. A pretty big pile of this thing is custom at this point, and it's meeting my goals fairly well, except on the virtualization front. My overriden TreeView and TreeViewItem templates both use VirtualizingStackPanels to present their items, and I've verified that all of this is getting created as expected. The virtualization works correctly on root level items (only the UI elements for those currently visible in the ScrollViewer are cooked up), and the TreeView stuff takes care of not generating the elements for collapsed nodes. The problem comes when expanding a node -- all the elements are cooked up for every child in the node, even the thousands that are offscreen.

It seemed to me like all I needed to do was somehow set the scrollowner property of the inner nested VirtualizingStackPanels to the same main scrollview that the root level VSP gets hooked up to by default, but I read an MSFT poster here saying that this will not work.

Unfortunately this thing is slow as mud without the virtualization occurring, so I need to come up with some sort of solution. Any suggestions would be greatly appreciated.

回答1:

I know this is an old question, but was still relevant for me.

In my case I thought a TreeView wouldn't cut it because I need exactly two layers, and the types of items displayed are different between the two layers. Also, I'm refactoring a list of Expanders so I was thinking more one-dimensionally.

But then I realized that you can customize the ItemTemplate of the TreeView to include your own HierarchicalDataTemplate, and that you can customize the ItemTemplate of that HierarchicalDataTemplate with your own tailored DataTemplate... Presto! Exactly two layers with different things in each!

So my point is that the TreeView is flexible enough that you should try not to create your own custom control.

Here's what I did:

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>

The Person class:

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

The Expense class:

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

Here's how it looks:

I inspected it with Snoop to prove that it is UI Virtualizing. Here's the number of loaded TreeViewItems when the app is small:

...And here's the number of loaded TreeViewItems when the app is fullscreen (it keeps going beyond this snippet, but you get the idea):

Now it's just the simple matter of styling things to make the nested layer look how I want!

Edit: I just verified that the TreeView virtualizes all its layers, not just the first layer.



回答2:

I had a link to a list of what not to do with a virtualizing stackpanel, but for some reason the page comes up blank. Here's another page that talks a little about it:

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

and even links to the one im talking about, but its always blank. if you look on that page its the link to Mark Shurmer's blog. Here's the link if you'd like to try it:

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

Bea Stollnitz also has some articles on it that might help:

Part 1 Part 2 Part 3

If you post some of your code, someone might be able to unravel the spaghetti a bit to help you get to a better implementation.

EDIT: Found a link that might work (thank you archive.org!!!): http://web.archive.org/web/20080104163725/http://itknowledgeexchange.techtarget.com/wpf/listview-is-it-really-too-slow/