7

我正在使用 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 的解决方案就像一个魅力

  • 原来的:
    • 启动:35s,
    • 内存:393MB
    • 滚动:慢
  • 树视图:
    • 启动:18s,
    • 内存377MB,
    • 滚动:快速
  • pushpraj 的解决方案:
    • 启动:<1s,
    • 内存:20MB,
    • 滚动:快速
4

1 回答 1

19

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

例如

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

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

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


编辑

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

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>

代码

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

于 2014-08-27T14:56:08.220 回答