在没有虚拟化的情况下在 WPF DataGrid 上排序时性能不佳

发布于 2024-12-04 10:34:27 字数 8791 浏览 0 评论 0原文

我们有一个简单的 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;
         }

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

We have a simple WPF demo app with 1000 rows and 32 columns (see below for code).

We've done nothing special with it other than disable virtualization, which our users require as otherwise the scrolling is too sluggish (they do this all day long on large data sets, so responsiveness/snappiness is important for them).

The problem we have is if you click on one of the headers to sort the data, this will take ~ 20 seconds (on a 2x3GHz Core 2 Duo machine here). Is there any way to speed this up?

It seems as if it's reconstructing the entire visual tree when sorting, and it seems unnecessary. Any pointers to how to speed up this specific situation will be appreciated, even if it goes as far as compiling our own version of the grid.

Thanks.

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

UPDATE: I have tried AngelWPF's suggestion as follows:

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;
         }

Although the performance problem still exists, as it's rebuilding the grid.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

做个ˇ局外人 2024-12-11 10:34:27

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

为什么要放弃虚拟化?数据网格滚动缓慢的问题可以得到解决...就像这些帖子可能会有所帮助...WPF 数据网格性能, 缓慢且口吃的 WPF加载大量数据时的网格滚动(40 列,2000 行), http://www.codeproject.com/KB/WPF/WpfDataVirtualization.aspx

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

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

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

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

Without virtualization, there is no chance of sorting performance to improve!

Why to loose virtualization? The problem of sluggish datagrid scrolling can be addressed ... like these posts may help... WPF Datagrid Performance, Slow and Stuttery WPF Grid Scrolling when loaded with large amounts of data (40 columns, 2000 rows), http://www.codeproject.com/KB/WPF/WpfDataVirtualization.aspx.

Having said that there is one way to improve the sorting on a non-virtualized data grid.

  1. Handle the DataGrid.Sorting event and set e.Handled = true in its handler. This way datagrid wont perform the sorting.
  2. In the above handler by looking at the column name you will know the property that the column is representing or is bound to. So use that property name and use LINQ's AsQueryable() interface to sort. This will be fastest way to sort the ItemsSource of the datagrid.
  3. Set the sorted collection back to the ItemsSource.

We tried the above approach for a readonly non-virtualized datagrid that displayed variety of colors, effects and 30000 rows of 30 columns, and the result was astounding.

Queryable LINQ can do wonders!

困倦 2024-12-11 10:34:27

我在使用 DataGrid 时遇到了一个问题,在进行列排序、调整大小等操作后,它实际上需要几秒钟的时间来刷新,并在执行此操作时锁定窗口 UI(1000 行,5 列)。

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

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

  • 将包含元素的大小更改为相对大小 (*) 或
    固定值
  • 将DataGrid的MaxHeight和MaxWidth设置为较大的固定值
    比正常使用时可以得到的
  • 尝试另一种具有不同调整大小策略的容器类型(网格,
    DockPanel 等)

I had a problem with the DataGrid in which it took literally seconds to refresh after a column sort, resize etc. and locked up the window UI while it was doing so (1000 rows, 5 columns).

It came down to an issue with the WPF sizing calculations. I had it in a grid with the RowDefinition Height="Auto" which was causing the rendering system to try and recalculate the size of the DataGrid at runtime by measuring the size of each and every column and row, presumably by filling the whole grid (as I understand it). It is supposed to handle this intelligently somehow but in this case it was not.

A quick check to see if this is a related problem is to set the Height and Width properties of the DataGrid to a fixed size for the duration of the test, and try running again. If your performance is restored, a permanent fix may be among these options:

  • Change the sizes of the containing elements to be relative (*) or
    fixed values
  • Set MaxHeight and MaxWidth of the DataGrid to a fixed value larger
    than it could get in normal use
  • Try another container type with different resizing strategy (Grid,
    DockPanel, etc)
木格 2024-12-11 10:34:27

从更新的部分:

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

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

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

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

From the updated section:

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

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

Why don't you disable the WriteLine? That can't be good from a performance point of view. Additionally, is there a reason you are re-initializing the _rows variable to a new ObservableCollection? If possible, convert _rows to a property and then raise the NotifyPropertyChanged event when change the value.

Of course, you'd also have to change your data binding for the grid to this new property.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文