WPF Toolkit DataGrid滚动性能问题 - 为什么?

时间:2009-11-09 22:52:30

标签: c# wpf performance datagrid

我的(WPF Toolkit)DataGrid存在性能问题。它包含大约1.000行(只有8列),滚动速度非常慢且滞后。另外,包含DataGrid的Window的初始加载需要5-10秒。

我做了一些研究(使用谷歌和StackOverflow)但除了打开UI虚拟化的建议之外找不到任何东西。但即使在明确表示滚动仍然非常缓慢之后。

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

整个Window的DataContext设置为包含DataGrid绑定的ICollectionView的类的实例。

我发现的每篇博客或论坛帖子都赞扬了DataGrid的表现,所以我显然做了一些严重的错误。由于我对WPF很新,特别是对于DataGrid,我不知道如何改进它。 有人对我有什么建议吗?您对DataGrid的体验如何?我做错了什么?

编辑:按照this问题的建议,将所有列的宽度设置为“自动”。这确实改变了糟糕的滚动性能。另外,我没有使用DataGridTemplateColumns(只是一些DataGridTextColumns和两个DataGridComboBoxColumns)。

Edit2:我用Snoop查看我的应用程序。我所看到的表明虚拟化确实在起作用(只有19行,而不是一千行)。但每行包含52个元素,因此这些元素总计超过千个元素。可能是一个问题?

非常感谢!

7 个答案:

答案 0 :(得分:18)

DataGrid有一个附加属性 ScrollViewer.CanContentScroll ,用于管理此行为。要获得平滑滚动,您需要将其设置为 False

答案 1 :(得分:6)

在最终花时间针对WPF的最新版本构建我的应用程序后,滚动问题似乎完全消失了。因此,如果有人仍然使用DataGrid的工具包版本只是“更新”到框架中包含的版本,那么你应该没问题。

答案 2 :(得分:4)

我使用的是.NET 4.0,但仍然遇到了滚动性能问题。我所做的是 - 禁用虚拟化。我在DataGrid中将EnableRowVirtualization设置为'false'。这大大提高了滚动性能。

我建议不要假设WPF提供的任何内容在所有情况下都有用。

答案 3 :(得分:3)

您的数据网格包含哪些容器?例如 - 如果您将其放在滚动查看器中,则数据网格将增长以显示每一行,从而有效地禁用虚拟化(并且滚动查看器将使其在发生这种情况时显示正常)。确保数据网格大小有界。

它确实听起来像一个虚拟化的东西,如果这个建议不起作用通过分析器运行你的应用程序,以确保虚拟化正在发生。

编辑:这是一个如何使用snoop(或者我猜的是)来快速查看虚拟化是否正常工作的示例。 http://blogs.msdn.com/jgoldb/archive/2008/03/25/quick-tips-to-improve-wpf-app-memory-footprint.aspx

答案 4 :(得分:2)

您可以尝试在datagrid中逐个(或逐行)添加项目,并在每次添加后更新UI线程。 这样,用户看到加载发生,似乎应用程序什么都不做。 请参阅here此方法的更详细说明

答案 5 :(得分:2)

就初始加载而言,我发现扩展公共API以显着改善大量列加载是必要的 - 我们谈论的时间不到一秒钟。也就是说,我在滚动性能方面遇到了类似的问题,即使500多列的滚动速度也很慢。

在我的派生数据网格中设置列:

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();

修复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;

答案 6 :(得分:0)

我发现将列的宽度设置为Auto(默认值!)会在有许多单元格时引入很大的垂直滚动滞后。在我的情况下,切换到固定宽度很有帮助,尽管在有许多列的情况下仍然存在一些可检测到的滞后。

如果您担心用户不喜欢您选择的宽度,则可以在网格上设置CanUserResizeColumns="True"