I have a performance problem with the (WPF Toolkit) DataGrid. It contains about 1.000 rows (only eight columns) and scrolling is horribly slow and laggy. Also the initial load of the Window containing the DataGrid takes 5-10 seconds.
我对(WPF Toolkit) DataGrid有性能问题。它包含了大约1000行(只有8列),滚动速度非常慢,而且很慢。此外,包含DataGrid的窗口的初始负载需要5-10秒。
I did some research (using google and *) but couldn't find anything besides the advice to turn on UI virtualization. But even after explictly enabling that scrolling continues to be awfully slow.
我做了一些研究(使用谷歌和*),但是除了打开UI虚拟化的建议之外,我没有找到其他的东西。但即使在明确地支持滚动之后,滚动仍然非常缓慢。
My DataGrid is bound to an ICollectionView / CollectionViewSource. It's is defined in XAML like this (the columns are explicitly defined, not auto generated):
我的DataGrid绑定到ICollectionView / CollectionViewSource。它是在XAML中定义的(列是显式定义的,而不是自动生成的):
<tk:DataGrid x:Name="dataGrid"
ItemsSource="{Binding Path=Bookings}"
AutoGenerateColumns="False"
Grid.Row="1"
EnableRowVirtualization="True"
EnableColumnVirtualization="True"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling">
...
</tk:DataGrid>
The DataContext for the entire Window is set to an instance of the class containing the ICollectionView the DataGrid is bound to.
整个窗口的DataContext设置为包含DataGrid绑定到的ICollectionView的类的实例。
Every blog or forum post I found was praising the DataGrid's performance so I'm quite obviously doing something seriously wrong. Since I'm quite new to WPF in general and especially to the DataGrid I've no clue of how to improve this. Does anybody have some advice for me? What's your experience with the DataGrid? What am I doing wrong?
我发现的每一个博客或论坛都在赞扬DataGrid的表现,所以我显然做错了什么。由于我对WPF很陌生,尤其是对DataGrid,我不知道如何改进它。有人对我有什么建议吗?你对DataGrid有什么经验?我做错了什么?
Edit: Just followed this question's advice to set the Width of all columns to "Auto". That did not change the bad scrolling performance. Also I'm not using DataGridTemplateColumns (just some DataGridTextColumns and two DataGridComboBoxColumns).
编辑:按照这个问题的建议,将所有列的宽度设置为“自动”。这并没有改变糟糕的滚动性能。我也没有使用DataGridTemplateColumns(只是一些datagridtextcolumn和两个DataGridComboBoxColumns)。
Edit2: I used Snoop to look at my app. What I see suggests that virtualization is indeed working (only 19 rows, not a thousand). But every row contains 52 elements, so those add up to more than thousand elements. Might that be a / the problem?
我使用Snoop查看我的应用。我看到的显示虚拟化确实在工作(只有19行,不是1000行)。但是每一行包含52个元素,所以这些加起来超过了1000个元素。这是问题吗?
Thanks a lot!
谢谢!
6 个解决方案
#1
18
The DataGrid has an Attached property, ScrollViewer.CanContentScroll, that manages this behavior. To get smooth scrolling you'll need to set it to False.
DataGrid有一个附加属性ScrollViewer。CanContentScroll管理这个行为。要获得平滑滚动,需要将其设置为False。
#2
6
After finally making the time to build my application against an up-to-date version of WPF the scrolling problem seems completely gone. So if anyone still uses the toolkit version of the DataGrid just "update" to the version included in the framework and you should be fine.
在花时间在最新版本的WPF上构建应用程序之后,滚动问题似乎完全消失了。因此,如果有人仍然使用DataGrid的工具包版本,只需“更新”到框架中包含的版本,那么您应该没问题。
#3
4
I am using .NET 4.0 and still get the scroll performance problem. What I did is - disabled virtualization. I set EnableRowVirtualization to 'false' in the DataGrid. This considerably improved the scroll performance.
我正在使用。net 4.0,但仍然存在滚动性能问题。我所做的是禁用虚拟化。我在DataGrid中将EnableRowVirtualization设为“false”。这大大提高了滚动性能。
I would suggest to not assume that whatever is being offered by WPF is useful in all the situations.
我建议不要假设WPF提供的任何东西在所有情况下都是有用的。
#4
3
What container does your datagrid live in? For example - if you put it in a scrollviewer, the datagrid will grow to display every row, thus effectively disabling virtualization (and the scrollviewer will make it appear normal while this happens). Make sure that the datagrid size is bounded.
您的datagrid所在的容器是什么?例如,如果您将它放在一个scrollviewer中,那么datagrid将会增长到显示每一行,从而有效地禁用虚拟化(当这种情况发生时,scrollviewer将使它看起来正常)。确保datagrid大小是有界的。
It really does sound like a virtalization thing, if this advice doesn't work run your app through a profiler to make sure virtualization is happening.
它听起来确实像一个病毒化的东西,如果这个建议不能通过一个分析器运行你的应用程序来确保虚拟化正在发生。
Edit: Here is an example of how to use snoop (or mole I guess) to quickly see if the virtualization is working. http://blogs.msdn.com/jgoldb/archive/2008/03/25/quick-tips-to-improve-wpf-app-memory-footprint.aspx
编辑:这里有一个如何使用snoop(或者我猜是mole)快速查看虚拟化是否在工作的例子。http://blogs.msdn.com/jgoldb/archive/2008/03/25/quick-tips-to-improve-wpf-app-memory-footprint.aspx
#5
2
You could try adding the items one by one (or row by row) in the datagrid and updating the UI thread after each addition. That way, the user sees the loading take place and it doesn't seem like the application is doing nothing. See here a more detailed description of this method
您可以尝试在datagrid中逐一添加条目(或逐行添加),并在每次添加后更新UI线程。这样,用户就可以看到正在进行加载,而应用程序似乎并不是什么都不做。请参见这里对该方法的更详细描述
#6
2
As far as initial loading goes, I found it neccessary to extend the public API to dramatically improve large # of column loading - we're talking minutes to under a second. That said, I have similar issues with scrolling performance, even 500+ columns is really slow to scroll.
就初始加载而言,我发现有必要扩展公共API,以显著改善大量的列加载——我们在几分钟内就能完成。也就是说,我对滚动性能也有类似的问题,即使500多列也很慢。
Setting columns within my derived datagrid:
在派生的datagrid中设置列:
var columns = new DataGridColumnCollection(true, dataGrid);
for (int i = 0; i < pivotTable.DetailsColumnCount; i++)
{
if (!pivotTable.NullColumns.Contains(i))
{
columns.Add(new PivotDetailColumn(pivotTable, i));
}
}
columns.ForceUpdate();
dataGrid.Columns = columns;
dataGrid.ItemsSource =
Enumerable.Range(0, pivotTable.DetailsRowCount)
.Where(i => !pivotTable.NullRows.Contains(i)) // Only non null rows
.ToList();
Fixes to DataGridColumnCollection:
DataGridColumnCollection修复:
public class DataGridColumnCollection : ObservableCollection<DataGridColumn>
{
private bool _DeferColumnChangeUpdates = false;
public DataGridColumnCollection(bool deferColumnChangeUpdates, DataGrid dataGridOwner)
: this(dataGridOwner)
{
_DeferColumnChangeUpdates = deferColumnChangeUpdates;
}
public DataGridColumnCollection(DataGrid dataGridOwner)
{
Debug.Assert(dataGridOwner != null, "We should have a valid DataGrid");
DisplayIndexMap = new List<int>(5);
_dataGridOwner = dataGridOwner;
RealizedColumnsBlockListForNonVirtualizedRows = null;
RealizedColumnsDisplayIndexBlockListForNonVirtualizedRows = null;
RebuildRealizedColumnsBlockListForNonVirtualizedRows = true;
RealizedColumnsBlockListForVirtualizedRows = null;
RealizedColumnsDisplayIndexBlockListForVirtualizedRows = null;
RebuildRealizedColumnsBlockListForVirtualizedRows = true;
}
#region Protected Overrides
public void ForceUpdate()
{
if (DisplayIndexMapInitialized)
{
UpdateDisplayIndexForNewColumns(this, 0);
}
InvalidateHasVisibleStarColumns();
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
if (!_DeferColumnChangeUpdates)
{
if (DisplayIndexMapInitialized)
{
UpdateDisplayIndexForNewColumns(e.NewItems, e.NewStartingIndex);
}
InvalidateHasVisibleStarColumns();
}
break;
#1
18
The DataGrid has an Attached property, ScrollViewer.CanContentScroll, that manages this behavior. To get smooth scrolling you'll need to set it to False.
DataGrid有一个附加属性ScrollViewer。CanContentScroll管理这个行为。要获得平滑滚动,需要将其设置为False。
#2
6
After finally making the time to build my application against an up-to-date version of WPF the scrolling problem seems completely gone. So if anyone still uses the toolkit version of the DataGrid just "update" to the version included in the framework and you should be fine.
在花时间在最新版本的WPF上构建应用程序之后,滚动问题似乎完全消失了。因此,如果有人仍然使用DataGrid的工具包版本,只需“更新”到框架中包含的版本,那么您应该没问题。
#3
4
I am using .NET 4.0 and still get the scroll performance problem. What I did is - disabled virtualization. I set EnableRowVirtualization to 'false' in the DataGrid. This considerably improved the scroll performance.
我正在使用。net 4.0,但仍然存在滚动性能问题。我所做的是禁用虚拟化。我在DataGrid中将EnableRowVirtualization设为“false”。这大大提高了滚动性能。
I would suggest to not assume that whatever is being offered by WPF is useful in all the situations.
我建议不要假设WPF提供的任何东西在所有情况下都是有用的。
#4
3
What container does your datagrid live in? For example - if you put it in a scrollviewer, the datagrid will grow to display every row, thus effectively disabling virtualization (and the scrollviewer will make it appear normal while this happens). Make sure that the datagrid size is bounded.
您的datagrid所在的容器是什么?例如,如果您将它放在一个scrollviewer中,那么datagrid将会增长到显示每一行,从而有效地禁用虚拟化(当这种情况发生时,scrollviewer将使它看起来正常)。确保datagrid大小是有界的。
It really does sound like a virtalization thing, if this advice doesn't work run your app through a profiler to make sure virtualization is happening.
它听起来确实像一个病毒化的东西,如果这个建议不能通过一个分析器运行你的应用程序来确保虚拟化正在发生。
Edit: Here is an example of how to use snoop (or mole I guess) to quickly see if the virtualization is working. http://blogs.msdn.com/jgoldb/archive/2008/03/25/quick-tips-to-improve-wpf-app-memory-footprint.aspx
编辑:这里有一个如何使用snoop(或者我猜是mole)快速查看虚拟化是否在工作的例子。http://blogs.msdn.com/jgoldb/archive/2008/03/25/quick-tips-to-improve-wpf-app-memory-footprint.aspx
#5
2
You could try adding the items one by one (or row by row) in the datagrid and updating the UI thread after each addition. That way, the user sees the loading take place and it doesn't seem like the application is doing nothing. See here a more detailed description of this method
您可以尝试在datagrid中逐一添加条目(或逐行添加),并在每次添加后更新UI线程。这样,用户就可以看到正在进行加载,而应用程序似乎并不是什么都不做。请参见这里对该方法的更详细描述
#6
2
As far as initial loading goes, I found it neccessary to extend the public API to dramatically improve large # of column loading - we're talking minutes to under a second. That said, I have similar issues with scrolling performance, even 500+ columns is really slow to scroll.
就初始加载而言,我发现有必要扩展公共API,以显著改善大量的列加载——我们在几分钟内就能完成。也就是说,我对滚动性能也有类似的问题,即使500多列也很慢。
Setting columns within my derived datagrid:
在派生的datagrid中设置列:
var columns = new DataGridColumnCollection(true, dataGrid);
for (int i = 0; i < pivotTable.DetailsColumnCount; i++)
{
if (!pivotTable.NullColumns.Contains(i))
{
columns.Add(new PivotDetailColumn(pivotTable, i));
}
}
columns.ForceUpdate();
dataGrid.Columns = columns;
dataGrid.ItemsSource =
Enumerable.Range(0, pivotTable.DetailsRowCount)
.Where(i => !pivotTable.NullRows.Contains(i)) // Only non null rows
.ToList();
Fixes to DataGridColumnCollection:
DataGridColumnCollection修复:
public class DataGridColumnCollection : ObservableCollection<DataGridColumn>
{
private bool _DeferColumnChangeUpdates = false;
public DataGridColumnCollection(bool deferColumnChangeUpdates, DataGrid dataGridOwner)
: this(dataGridOwner)
{
_DeferColumnChangeUpdates = deferColumnChangeUpdates;
}
public DataGridColumnCollection(DataGrid dataGridOwner)
{
Debug.Assert(dataGridOwner != null, "We should have a valid DataGrid");
DisplayIndexMap = new List<int>(5);
_dataGridOwner = dataGridOwner;
RealizedColumnsBlockListForNonVirtualizedRows = null;
RealizedColumnsDisplayIndexBlockListForNonVirtualizedRows = null;
RebuildRealizedColumnsBlockListForNonVirtualizedRows = true;
RealizedColumnsBlockListForVirtualizedRows = null;
RealizedColumnsDisplayIndexBlockListForVirtualizedRows = null;
RebuildRealizedColumnsBlockListForVirtualizedRows = true;
}
#region Protected Overrides
public void ForceUpdate()
{
if (DisplayIndexMapInitialized)
{
UpdateDisplayIndexForNewColumns(this, 0);
}
InvalidateHasVisibleStarColumns();
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
if (!_DeferColumnChangeUpdates)
{
if (DisplayIndexMapInitialized)
{
UpdateDisplayIndexForNewColumns(e.NewItems, e.NewStartingIndex);
}
InvalidateHasVisibleStarColumns();
}
break;