我有一个DataGrid绑定到一个可观察的对象列表。如果我还有一个绑定到该列表的ItemsControl,则排序性能(通过单击DataGrid标头)非常糟糕(对于下面的示例,大约几秒钟)。当ItemsControl未绑定到同一列表时,则排序是即时的。
以下是一些展示此行为的示例代码
namespace LargeDataGridViewTest
{
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainPresenter();
}
}
public class MainPresenter : INotifyPropertyChanged
{
private readonly ObservableCollection<Item> _items = new ObservableCollection<Item>();
public IEnumerable<Item> Items { get { return _items; } }
public MainPresenter()
{
for (var i = 0; i < 10000; i++)
_items.Add(new Item());
}
}
public class Item : INotifyPropertyChanged
{
public int Random { get; private set; }
private static readonly Random Rand = new Random();
public Item()
{
Random = Rand.Next(0, 1000000);
}
}
}
和相应的XAML
<Window.Resources>
<DataTemplate DataType="{x:Type LargeDataGridViewTest:MainPresenter}">
<DockPanel>
<DataGrid ItemsSource="{Binding Items}"/>
<!--ListBox ItemsSource="{Binding Items}"/-->
<ItemsControl ItemsSource="{Binding Items}"/>
</DockPanel>
</DataTemplate>
</Window.Resources>
<ContentPresenter Content="{Binding}"/>
如果我使用ListBox代替ItemsControl,那么排序性能很好。如果我使用ListBox但通过更改ItemsPanelTemplate来访问它的底层ItemsControl,则性能再次变坏。
如果我获取列表的浅表副本(引用相同的项目)并将ItemsControl绑定到该列表,则性能再次正常。
通过EQATEC探查器运行速度较慢的ItemsControl绑定和快速ListBox绑定,除顶级应用时间外,没有显示性能差异。
有谁知道这里发生了什么?
修改
部分答案似乎是ItemsControls没有虚拟化,因此必须绘制所有项目而不仅仅是可见项目。在这种情况下,为什么在DataGrid排序时重新绘制所有ItemsControl项(即使ItemsControl绑定模式是OneTime)?我怎样才能阻止这对DataGrid排序的性能产生影响?
答案 0 :(得分:0)
我怀疑这是因为默认情况下DataGrid
和ListBox
都会对其项进行虚拟化,而ItemsControl
则不会。
这意味着DataGrid
和ListBox
仅创建在屏幕上可见的UI对象,而ItemsControl
将创建所有10,000个UI对象。
要修复效果,virtualize your ItemsControl
。这是所需的基本部分,但请查看链接的问题以获取更多详细信息。
<ItemsControl
VirtualizingStackPanel.IsVirtualizing="True" <!-- needed -->
ScrollViewer.CanContentScroll="True" <!-- needed -->
... >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel /> <!-- needed -->
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Template>
<ControlTemplate>
<ScrollViewer> <!-- needed -->
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
答案 1 :(得分:0)
虚拟化是关键!
默认情况下,ItemsControls不是虚拟化的,当数据网格对Items
集合进行排序时,默认集合视图将被排序,同样的排序也适用于另一个ItemsControl
。但是ItemsControl
没有实现虚拟化,因此它不仅对项目进行排序,而且还在项目容器上呈现它们。 ListBox
表现更好,因为默认情况下在其中实施了虚拟化。
为此,快速修复将模拟ListBox
看起来像ItemsControl
。你可以通过摆脱列表框的选择颜色来做到这一点。
<ListBox ItemsSource="{StaticResource MyData}" DisplayMemberPath="ID">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}"
Color="Transparent"/>
</Style.Resources>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Foreground" Value="Black"/>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>