ListView
每当我实施分组时,我都会遇到主要的性能问题。我在 StackOverflow 上发现了一些类似的问题,但似乎没有任何帮助!
这是我目前的情况(我已经简化了我的项目,所以噪音更少):
我有一个ContentControl
和一个ListView
一样的孩子。ListView
绑定到一个ObservableCollection
,它最初是空的。随着时间的推移,对象被添加到集合中(在此示例中,每 10 秒使用 500 个项目添加DispatcherTimer
)。会的大小ObservableCollection
会有所不同,但该系列最终可能会超过 25,000 件。
当ObservableCollection
小于 2000(不是确切的数字)时,列大小调整如下所示:
但是,随着更多对象添加到 中ObservableCollection
,性能会显着下降(您需要向下滚动才能发生这种情况)。
这最终将导致Application
锁定。
我认为可以使用解决问题Virtualization
,所以我尝试使用以下方法:
<ListView x:Name="ListView1"
Style="{DynamicResource lvStyle}"
VirtualizingPanel.IsVirtualizingWhenGrouping="True"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.IsContainerVirtualizable="True"
ScrollViewer.IsDeferredScrollingEnabled="True">
但是,似乎没有任何效果!
更不用说,VirtualizingPanel.IsVirtualizingWhenGrouping="True"
导致ListView
完全锁定。
我还研究了Paul McClean 出色的数据虚拟化,但是它不处理分组。
问题: 在对 a 中的项目进行分组时ListView
,有没有办法调整列的大小而不会显着影响应用程序的性能?
理想情况下,我想减少内存开销,所以我完全支持实现某种异步解决方案。
代码:
XAML:
<ContentControl x:Class="ListViewDemo.MainWindow"
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:ListViewDemo"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:dat="clr-namespace:System.Windows.Data;assembly=PresentationFramework"
xmlns:Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Classic"
mc:Ignorable="d"
d:DesignHeight="400" d:DesignWidth="300">
<ContentControl.Resources>
<Style x:Key="lvStyle" TargetType="{x:Type ListView}">
<Setter Property="VirtualizingStackPanel.IsVirtualizing" Value="True"/>
<Setter Property="VirtualizingStackPanel.VirtualizationMode" Value="Recycling"/>
<Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="True"/>
<Setter Property="ListView.ItemsSource" Value="{Binding}"/>
<Setter Property="ListView.View">
<Setter.Value>
<GridView>
<GridViewColumn Header="Name">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Date">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Date}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Desc">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Desc}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</Setter.Value>
</Setter>
</Style>
</ContentControl.Resources>
<Grid>
<ListView x:Name="ListView1"
Style="{DynamicResource lvStyle}"
VirtualizingPanel.IsVirtualizingWhenGrouping="True"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.IsContainerVirtualizable="True"
ScrollViewer.IsDeferredScrollingEnabled="True">
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<DockPanel>
<Border DockPanel.Dock="Top">
<TextBlock x:Name="groupItem"
Text="{Binding ItemCount, StringFormat={}({0} Results)}"></TextBlock>
</Border>
<ItemsPresenter DockPanel.Dock="Bottom"></ItemsPresenter>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
</Grid>
</ContentControl>
代码隐藏:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
namespace ListViewDemo
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : ContentControl
{
private ObservableCollection<Event> eventCollection = new ObservableCollection<Event>();
public MainWindow()
{
InitializeComponent();
DispatcherTimer dispatcherTimer = new DispatcherTimer();
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Interval = new TimeSpan(0, 0, 10);
dispatcherTimer.Start();
ListView1.ItemsSource = eventCollection;
ListView1.Items.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Descending));
ListView1.Items.SortDescriptions.Add(new SortDescription("Date", ListSortDirection.Descending));
ListView1.Items.GroupDescriptions.Add(new PropertyGroupDescription("Seconds"));
}
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
for(var i = 0; i < 500; i++){
eventCollection.Add(new Event
{
Name = string.Format("Name_{0}", eventCollection.Count),
Date = DateTime.Now.ToString("MM.dd.yy HH:mm"),
Seconds = Convert.ToInt32(DateTime.Now.ToString("ss")),
Desc = "Description"
});
}
}
public class Event
{
public string Name { get; set; }
public string Date { get; set; }
public int Seconds { get; set; }
public string Desc { get; set; }
}
}
}