简体   繁体   中英

WPF TreeView Virtualization performance with hidden items

I have a TreeView control where I'm allowing users to filter the items of the tree based on a keyword. I have VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling" on my tree and its ItemsSource is databound. My TreeViewItem 's Visibility is set as follows:

<Setter Property="Visibility" Value="{Binding IsVisible, Converter={StaticResource BooleanToVisibilityConverter}}"/>

The tree performs really great when it's not filtered, but I'm seeing a big performance hit when loading a tree item that has a big number of hidden items. From what I can see in VS's Diagnostic Tools, it seems to me that My TreeViewItem tries to load it's invisible child items even if they are not visible and this is causing both a memory and a CPU hit with a big number of invisible items loaded. Has anyone experienced this before? Is there a way I can override the default logic of realizing the virtualized tree items so hidden items don't get loaded?

This behavior is expected.

Let's say you have 10000 items in your collection. First 5000 have IsVisible set to false . With UI virtualization enabled, containers are generated until they fill available space. So you end up with 5000 collapsed TreeViewItem s (they take no space) plus a couple that fill the available space. I hope you see where the problem lies.

I think your best bet is to use Live Shaping (available in WPF 4.5 ). The basic idea is that invisible items are filtered out from the source collection view, so that containers are not generated for those items.

In a nutshell, instead of

<TreeView ItemsSource="{Binding Items}">
</TreeViewitem>

you could use the following setup:

<TreeView>
    <FrameworkElement.Resources>
        <CollectionViewSource x:Key="Items"
                              Source="{Binding Items}"
                              Filter="Items_Filter"
                              IsLiveFilteringRequested="True"
                              xmlns:sys="clr-namespace:System;assembly=mscorlib">
            <CollectionViewSource.LiveFilteringProperties>
                <sys:String>IsVisible</sys:String>
            </CollectionViewSource.LiveFilteringProperties>
        </CollectionViewSource>
    </FrameworkElement.Resources>
    <ItemsControl.ItemsSource>
        <Binding Source="{StaticResource Items}" />
    </ItemsControl.ItemsSource>
</TreeView>

and

private void Items_Filter(object sender, FilterEventArgs e)
{
    var item = (YourItemType)e.Item;
    e.Accepted = item.IsVisible;
}

You'll also have to apply same trick to HierarchicalDataTemplate.ItemsSource . What this does is setup a collection view that listens to changes in IsVisible property and re-applies the filter when necessary (if you're using WPF version prior to 4.5 you'll have to do it manually).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM