wpf 提高 StackPanel 中巨大 ListBox 的性能?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25530354/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 12:25:24  来源:igfitidea点击:

Improve performance for huge ListBox in StackPanel?

wpflistboxvirtualizingstackpanelui-virtualization

提问by jayars

I am using a StackPanel to layout several controls vertically (ie, Title, sub titles, listbox, separator, listbox, etc).

我使用 StackPanel 来垂直布局几个控件(即标题、副标题、列表框、分隔符、列表框等)。

The StackPanel is a child of a ScrollViewer to ensure its content is always scrollable.

StackPanel 是 ScrollViewer 的子项,以确保其内容始终可滚动。

One of the controls in the StackPanel is a ListBox.

StackPanel 中的控件之一是 ListBox。

Its ItemsSource is data bound to a huge collection, and a complex DataTemplate is used to realise each item.

它的ItemsSource是绑定到一个庞大集合的数据,每个item都使用了一个复杂的DataTemplate来实现。

Unfortunately, I'm getting really poor performance (high cpu/memory) with it.

不幸的是,我的性能非常差(高 CPU/内存)。

I tried

我试过

  • setting the ListBox's ItemsPanel to a VirtualizingStackPanel, and
  • overriding its ControlTemplate to only an ItemsPresenter (remove the ListBox's ScrollViewer).
  • 将 ListBox 的 ItemsPanel 设置为 VirtualizingStackPanel,以及
  • 将其 ControlTemplate 覆盖为仅 ItemsPresenter(删除 ListBox 的 ScrollViewer)。

But there were no difference in performances. I'm guessing the StackPanel gives its internal children infinite height during measure?

但在表演上并没有什么不同。我猜 StackPanel 在测量过程中给它内部的孩子无限的高度?

When I replaced the ScrollViewer and StackPanel with other panels/layouts (e.g, Grid, DockPanel) and the performance improves significantly, which leads me to believe the bottleneck, as well as solution, is in virtualization.

当我用其他面板/布局(例如,Grid、DockPanel)替换 ScrollViewer 和 StackPanel 并且性能显着提高时,这让我相信瓶颈以及解决方案在于虚拟化。

Is there any way for me to improve the cpu/memory performance of this view?

我有什么办法可以提高这个视图的 CPU/内存性能吗?

enter image description here

在此处输入图片说明

[Update 1]

[更新1]

Original Sample project: http://s000.tinyupload.com/index.php?file_id=29810707815310047536

原始示例项目:http: //s000.tinyupload.com/index.php?file_id=29810707815310047536

[Update 2]

[更新2]

I tried restyling/templating TreeView/TreeViewItems to come up with the following example. It still takes a long time to start/same,high memory usage. But once loaded, scrolling feels a lot more responsive than the original sample.

我尝试重新设计/模板化 TreeView/TreeViewItems 以提出以下示例。启动/相同仍然需要很长时间,内存使用率高。但是一旦加载,滚动感觉比原始样本响应要快得多。

Wonder if there's any other way to further improve the start up time/memory usage?

想知道是否还有其他方法可以进一步提高启动时间/内存使用率?

Restyled TreeView project: http://s000.tinyupload.com/index.php?file_id=00117351345725628185

重新设计的 TreeView 项目:http://s000.tinyupload.com/index.php?file_id=00117351345725628185

[Update 2]

[更新2]

pushpraj's solution works like a charm

pushpraj 的解决方案就像一个魅力

  • Original:
    • Startup: 35s,
    • Memory: 393MB
    • Scrolling: Slow
  • TreeView:
    • Startup: 18s,
    • Memory 377MB,
    • Scrolling: Fast
  • pushpraj's solution:
    • Startup: <1s,
    • Memory: 20MB,
    • Scrolling: Fast
  • 原来的:
    • 启动:35s,
    • 内存:393MB
    • 滚动:慢
  • 树视图:
    • 启动:18s,
    • 内存 377MB,
    • 滚动:快速
  • pushpraj 的解决方案:
    • 启动:<1s,
    • 内存:20MB,
    • 滚动:快速

回答by pushpraj

you may perhaps limit the maximum size of the huge list box and enable Virtualization

您可能会限制巨大列表框的最大大小并启用 Virtualization

eg

例如

<ListBox MaxHeight="500" 
         VirtualizingPanel.IsVirtualizing="true" 
         VirtualizingPanel.VirtualizationMode="Recycling" />

this will enable the ListBox to load a few items only and will enable a scrollbar on listbox to scroll to rest of the items if needed.

这将使 ListBox 仅加载几个项目,并在需要时启用列表框上的滚动条以滚动到其余项目。

at the same time setting VirtualizationModeto Recyclingwill help you to reuse the complex data templates thus eliminating the need of re creating them again for every item.

同时设置VirtualizationModeRecycling将帮助您重用复杂的数据模板,从而无需为每个项目重新创建它们。



EDIT

编辑

here is a solution based on your sample, I have used CompositeCollectionwith Virtualizationto achieve the desired.

这是基于您的示例的解决方案,我已经使用CompositeCollectionVirtualization来实现所需的。

xaml

xml

<Grid xmlns:sys="clr-namespace:System;assembly=mscorlib"
      xmlns:l="clr-namespace:PerfTest">
    <Grid.Resources>
        <DataTemplate DataType="{x:Type l:Permission}">
            <StackPanel Orientation="Horizontal">
                <CheckBox />
                <TextBlock Text="{Binding Name}" />
                <Button Content="+" />
                <Button Content="-" />
                <Button Content="..." />
            </StackPanel>
        </DataTemplate>
        <CompositeCollection x:Key="data">
            <!-- Content 1 -->
            <TextBlock Text="Title"
                       FontSize="24"
                       FontWeight="Thin" />
            <!-- Content 2 -->
            <TextBlock Text="Subtitle"
                       FontSize="16"
                       FontWeight="Thin" />
            <!-- Content 3 -->
            <CollectionContainer Collection="{Binding DataContext, Source={x:Reference listbox}}" />
            <!-- Content 4 -->
            <TextBlock Text="User must scroll past the entire list box before seeing this"
                       FontSize="16"
                       FontWeight="Thin"
                       Padding="5"
                       TextWrapping="Wrap"
                       Background="#99000000"
                       Foreground="White" />
        </CompositeCollection>
    </Grid.Resources>
    <ListBox x:Name="listbox"
             VirtualizingPanel.IsVirtualizing="True"
             VirtualizingPanel.VirtualizationMode="Recycling"
             ScrollViewer.HorizontalScrollBarVisibility="Disabled"
             ItemsSource="{StaticResource data}" />
</Grid>

code

代码

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        var items = new ObservableCollection<Permission>();
        foreach (var i in Enumerable.Range(0, 10000).Select(i => new Permission() { Name = "Permission " + i }))
        { items.Add(i); }
        DataContext = items;
    }
}

public class Permission
{
    public string Name { get; set; }
}

since we can not create data template for string so I changed the string collection to Permissioncollection. I hope in your real project it would be something similar.

由于我们无法为字符串创建数据模板,所以我将字符串集合更改为Permission集合。我希望在你的真实项目中它会是类似的。

give this a try and see if this is close to what you need.

尝试一下,看看这是否接近您的需要。

note: you may safely ignore if there is any designer warning on Collection="{Binding DataContext, Source={x:Reference listbox}}"

注意:如果有任何设计师警告,您可以放心地忽略 Collection="{Binding DataContext, Source={x:Reference listbox}}"