【问题标题】:Improve performance for huge ListBox in StackPanel?提高 StackPanel 中巨大 ListBox 的性能?
【发布时间】:2014-08-27 14:50:55
【问题描述】:

我正在使用 StackPanel 垂直布局多个控件(即标题、子标题、列表框、分隔符、列表框等)。

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

StackPanel 中的一个控件是 ListBox。

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

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

我试过了

  • 将 ListBox 的 ItemsPanel 设置为 VirtualizingStackPanel,并且
  • 仅将其 ControlTemplate 覆盖为 ItemsPresenter(删除 ListBox 的 ScrollViewer)。

但是在性能上没有区别。我猜 StackPanel 在测量期间会为其内部子级提供无限高度?

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

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

[更新 1]

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

[更新 2]

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

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

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

[更新 2]

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

  • 原文:
    • 启动:35 秒,
    • 内存:393MB
    • 滚动:慢
  • 树视图:
    • 启动时间:18 秒,
    • 内存377MB,
    • 滚动:快速
  • pushpraj 的解决方案:
    • 启动:
    • 内存:20MB,
    • 滚动:快速

【问题讨论】:

    标签: wpf listbox virtualizingstackpanel ui-virtualization


    【解决方案1】:

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

    例如

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

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

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


    编辑

    这是基于您的示例的解决方案,我使用 CompositeCollectionVirtualization 来达到预期效果。

    xaml

    <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>
    

    代码

    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; }
    }
    

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

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

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

    【讨论】:

    • 修复列表框的最大高度会导致出现 2 个滚动条(参见 i.imgur.com/s1MJ6BO.png?1
    • 确实如此,但同时虚拟化只能在固定高度或定义最大高度的情况下工作,不能无限高度工作。您可能会使用复合集合来组合堆栈面板中的所有项目,并将所有项目显示在启用虚拟化的列表框中。你可以分享代码,我会尽力帮助你。
    • 更新为包含示例项目
    • @jsjslim 我更新了我的答案以满足您的需求。现在它加载速度非常快,没有额外的滚动条。
    • 谢谢!启动速度非常快,内存使用率低。我肯定需要开始探索 .NET/WPF 附带的其他类。
    猜你喜欢
    • 1970-01-01
    • 2020-09-04
    • 1970-01-01
    • 2012-09-04
    • 2011-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多