【问题标题】:Poor Performance when Sorting on WPF DataGrid without Virtualization在没有虚拟化的情况下在 WPF DataGrid 上排序时性能不佳
【发布时间】:2011-11-16 00:28:31
【问题描述】:

我们有一个简单的 WPF 演示应用程序,它有 1000 行和 32 列(代码见下文)。

除了禁用虚拟化之外,我们没有做任何特别的事情,我们的用户需要这样做,否则滚动太慢(他们整天在大型数据集上这样做,因此响应性/快速性对他们来说很重要)。

我们遇到的问题是,如果您单击其中一个标题对数据进行排序,这将需要大约 20 秒(此处为 2x3GHz Core 2 Duo 机器)。有什么办法可以加快速度吗?

排序的时候好像是在重构整个可视化树,好像没必要。任何有关如何加快这种特定情况的指针都将受到赞赏,即使它可以编译我们自己的网格版本。

谢谢。

<Window x:Class="WpfGridTest1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <DataGrid Name="dataGrid" VirtualizingStackPanel.IsVirtualizing="False">

        </DataGrid>
    </Grid>
</Window>

using System.Collections.Generic;

namespace WpfGridTest1
{
    public partial class MainWindow
    {
        public MainWindow()
        {
            InitializeComponent();

            List<Row> rows = new List<Row>();

            for (int i = 0; i < 1000; i++)
            {
                Row row = new Row
                              {
                                  Column0 = i,
                                  Column1 = i,
                                  Column2 = i,
                                  Column3 = i,
                                  Column4 = i,
                                  Column5 = i,
                                  Column6 = i,
                                  Column7 = i,

                                  Column8 = i,
                                  Column9 = i,
                                  Column10 = i,
                                  Column11 = i,
                                  Column12 = i,
                                  Column13 = i,
                                  Column14 = i,
                                  Column15 = i,

                                  Column16 = i,
                                  Column17 = i,
                                  Column18 = i,
                                  Column19 = i,
                                  Column20 = i,
                                  Column21 = i,
                                  Column22 = i,
                                  Column23 = i,

                                  Column24 = i,
                                  Column25 = i,
                                  Column26 = i,
                                  Column27 = i,
                                  Column28 = i,
                                  Column29 = i,
                                  Column30 = i,
                                  Column31 = i
                              };
                rows.Add(row);
            }

            dataGrid.ItemsSource = rows;
        }
    }
}



using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;

namespace WpfGridTest1
{
    class Row : INotifyPropertyChanged
    {
        private double column0 ;
        private double column1 ;
        private double column2 ;
        private double column3 ;
        private double column4 ;
        private double column5 ;
        private double column6 ;
        private double column7 ;

        private double column8 ;
        private double column9 ;
        private double column10;
        private double column11;
        private double column12;
        private double column13;
        private double column14;
        private double column15;

        private double column16;
        private double column17;
        private double column18;
        private double column19;
        private double column20;
        private double column21;
        private double column22;
        private double column23;

        private double column24;
        private double column25;
        private double column26;
        private double column27;
        private double column28;
        private double column29;
        private double column30;
        private double column31;

        public double Column0  { get { return column0 ; } set { column0  = value; NotifyPropertyChanged("Column0 "); } }
        public double Column1  { get { return column1 ; } set { column1  = value; NotifyPropertyChanged("Column1 "); } }
        public double Column2  { get { return column2 ; } set { column2  = value; NotifyPropertyChanged("Column2 "); } }
        public double Column3  { get { return column3 ; } set { column3  = value; NotifyPropertyChanged("Column3 "); } }
        public double Column4  { get { return column4 ; } set { column4  = value; NotifyPropertyChanged("Column4 "); } }
        public double Column5  { get { return column5 ; } set { column5  = value; NotifyPropertyChanged("Column5 "); } }
        public double Column6  { get { return column6 ; } set { column6  = value; NotifyPropertyChanged("Column6 "); } }
        public double Column7  { get { return column7 ; } set { column7  = value; NotifyPropertyChanged("Column7 "); } }

        public double Column8  { get { return column8 ; } set { column8  = value; NotifyPropertyChanged("Column8 "); } }
        public double Column9  { get { return column9 ; } set { column9  = value; NotifyPropertyChanged("Column9 "); } }
        public double Column10 { get { return column10; } set { column10 = value; NotifyPropertyChanged("Column10"); } }
        public double Column11 { get { return column11; } set { column11 = value; NotifyPropertyChanged("Column11"); } }
        public double Column12 { get { return column12; } set { column12 = value; NotifyPropertyChanged("Column12"); } }
        public double Column13 { get { return column13; } set { column13 = value; NotifyPropertyChanged("Column13"); } }
        public double Column14 { get { return column14; } set { column14 = value; NotifyPropertyChanged("Column14"); } }
        public double Column15 { get { return column15; } set { column15 = value; NotifyPropertyChanged("Column15"); } }

        public double Column16 { get { return column16; } set { column16 = value; NotifyPropertyChanged("Column16"); } }
        public double Column17 { get { return column17; } set { column17 = value; NotifyPropertyChanged("Column17"); } }
        public double Column18 { get { return column18; } set { column18 = value; NotifyPropertyChanged("Column18"); } }
        public double Column19 { get { return column19; } set { column19 = value; NotifyPropertyChanged("Column19"); } }
        public double Column20 { get { return column20; } set { column20 = value; NotifyPropertyChanged("Column20"); } }
        public double Column21 { get { return column21; } set { column21 = value; NotifyPropertyChanged("Column21"); } }
        public double Column22 { get { return column22; } set { column22 = value; NotifyPropertyChanged("Column22"); } }
        public double Column23 { get { return column23; } set { column23 = value; NotifyPropertyChanged("Column23"); } }

        public double Column24 { get { return column24; } set { column24 = value; NotifyPropertyChanged("Column24"); } }
        public double Column25 { get { return column25; } set { column25 = value; NotifyPropertyChanged("Column25"); } }
        public double Column26 { get { return column26; } set { column26 = value; NotifyPropertyChanged("Column26"); } }
        public double Column27 { get { return column27; } set { column27 = value; NotifyPropertyChanged("Column27"); } }
        public double Column28 { get { return column28; } set { column28 = value; NotifyPropertyChanged("Column28"); } }
        public double Column29 { get { return column29; } set { column29 = value; NotifyPropertyChanged("Column29"); } }
        public double Column30 { get { return column30; } set { column30 = value; NotifyPropertyChanged("Column30"); } }
        public double Column31 { get { return column31; } set { column31 = value; NotifyPropertyChanged("Column31"); } }


        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }
    }
}

更新:我尝试过 AngelWPF 的建议如下:

private void dataGrid_Sorting(object sender, System.Windows.Controls.DataGridSortingEventArgs e)
        {
            e.Handled = true;

            IQueryable<Row> iqueryable = _rows.AsQueryable();

            var v = iqueryable.OrderBy(row => row.Column0);

            foreach (Row row in v)
                System.Diagnostics.Debug.WriteLine("Row " + row.Column0);

            _rows = new ObservableCollection<Row>(v.ToList());

            dataGrid.ItemsSource = _rows;
         }

虽然性能问题仍然存在,因为它正在重建网格。

【问题讨论】:

  • Virtulization Mode 设置为Recycling 至少有助于对我的应用程序中的滚动问题进行排序,同时仍然降低内存使用量:VirtualizationMode="Recycling"

标签: wpf performance sorting datagrid


【解决方案1】:

我遇到了 DataGrid 的问题,在该问题中,在列排序、调整大小等之后需要几秒钟才能刷新,并且在执行此操作时锁定了窗口 UI(1000 行,5 列)。

这归结为 WPF 大小计算的问题。我将它放在 RowDefinition Height="Auto" 的网格中,这导致渲染系统在运行时尝试通过测量每一列和每一行的大小来重新计算 DataGrid 的大小,大概是通过填充整个网格(据我了解)。它应该以某种方式智能地处理这个问题,但在这种情况下它不是。

快速检查这是否是相关问题是在测试期间将 DataGrid 的 Height 和 Width 属性设置为固定大小,然后再次尝试运行。如果您的性能得到恢复,则可以使用以下选项进行永久性修复:

  • 将包含元素的大小更改为相对 (*) 或 固定值
  • 将 DataGrid 的 MaxHeight 和 MaxWidth 设置为更大的固定值 比正常使用时更容易
  • 尝试使用不同大小调整策略的其他容器类型(网格、 DockPanel 等)

【讨论】:

    【解决方案2】:

    来自更新部分:

    foreach (Row row in v)
           System.Diagnostics.Debug.WriteLine("Row " + row.Column0);
    
    _rows = new ObservableCollection<Row>(v.ToList());
    

    为什么不禁用 WriteLine?从性能的角度来看,这不是很好。此外,您是否有理由将 _rows 变量重新初始化为新的 ObservableCollection?如果可能,将 _rows 转换为属性,然后在更改值时引发 NotifyPropertyChanged 事件。

    当然,您还必须将网格的数据绑定更改为这个新属性。

    【讨论】:

    • 谢谢 - 但是这些建议并不能解决我们的问题。我们基本上希望它在用户单击列标题时停止重建网格,但仍对其进行排序。
    【解决方案3】:

    没有虚拟化,排序性能就没有机会提升!

    为什么要放松虚拟化?可以解决数据网格滚动缓慢的问题......像这些帖子可能会有所帮助......WPF Datagrid PerformanceSlow and Stuttery WPF Grid Scrolling when loaded with large amounts of data (40 columns, 2000 rows)http://www.codeproject.com/KB/WPF/WpfDataVirtualization.aspx

    话虽如此,有一种方法可以改进非虚拟化数据网格的排序。

    1. 处理DataGrid.Sorting 事件并在其处理程序中设置e.Handled = true。这样 datagrid 不会执行排序。
    2. 在上述处理程序中,通过查看列名,您将知道该列所代表或绑定到的属性。因此,使用该属性名称并使用 LINQ 的 AsQueryable() 接口进行排序。这将是对数据网格的ItemsSource 进行排序的最快方法。
    3. 将排序后的集合设置回ItemsSource

    我们尝试了上述方法,用于显示各种颜色、效果和 30000 行 30 列的只读非虚拟化数据网格,结果令人震惊。

    可查询的 LINQ 可以创造奇迹!

    【讨论】:

    • 嗨,你能把这个应用到上面的例子中吗?谢谢。
    • 这就像AngelWPF教你钓鱼,你要求他给你钓些鱼。他给了你所有你需要的东西 (+1),现在你要求他为你实现它。
    • 我已按照建议的说明进行操作,并正在尝试以下代码(请参阅原始问题中的更新)。但是,当 itemsource 发生变化时,它似乎仍然会重新创建可视化树。
    猜你喜欢
    • 2014-08-02
    • 2015-02-15
    • 2016-10-23
    • 1970-01-01
    • 2017-11-30
    • 2011-09-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-01
    相关资源
    最近更新 更多