Slow performance in populating DataGridView with large data
我正在使用
我尝试将
请协助我。如何提高Grid的性能。
预先感谢,
维杰
如果您有大量的行,例如10,000或更多,
为避免性能泄漏-在数据绑定之前执行以下操作:
1 2 3 4 5 | dataGridView1.RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.EnableResizing; // or even better, use .DisableResizing. Most time consuming enum is DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders // set it to false if not needed dataGridView1.RowHeadersVisible = false; |
绑定数据后,可以重新启用它。
通常,关闭自动调整大小和双重缓冲有助于加快DataGridView的填充速度。检查DGV双缓冲是否正确打开:
1 2 3 4 5 6 7 | if (!System.Windows.Forms.SystemInformation.TerminalServerSession) { Type dgvType = dataGridView1.GetType(); PropertyInfo pi = dgvType.GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic); pi.SetValue(dataGridView1, value, null); } |
使用WinAPI WM_SETREDRAW消息禁用重绘也有帮助:
1 2 3 4 5 6 7 8 9 10 | // *** API Declarations *** [DllImport("user32.dll")] private static extern int SendMessage(IntPtr hWnd, Int32 wMsg, bool wParam, Int32 lParam); private const int WM_SETREDRAW = 11; // *** DataGridView population *** SendMessage(dataGridView1.Handle, WM_SETREDRAW, false, 0); // Add rows to DGV here SendMessage(dataGridView1.Handle, WM_SETREDRAW, true, 0); dataGridView1.Refresh(); |
如果不需要2向数据绑定或BindingSource提供的某些功能(过滤等),则可以考虑使用DataGridView.Rows.AddRange()方法一次性添加行。
带有示例的原始文章的链接:http://10tec.com/articles/why-datagridview-slow.aspx
确保您不自动调整列大小,这会提高性能。
即不要这样做:
我知道我要参加聚会很晚,但是最近我对DataGridView控件的自动调整大小太慢感到厌烦,并感到有人可能会从我的解决方案中受益。
我创建了这种扩展方法,用于手动测量和调整DataGridView中的列的大小。将AutoSizeColumnsMode设置为DataGridViewAutoSizeColumnsMode.None并在设置DataSource之后调用此方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | /// <summary> /// Provides very fast and basic column sizing for large data sets. /// </summary> public static void FastAutoSizeColumns(this DataGridView targetGrid) { // Cast out a DataTable from the target grid datasource. // We need to iterate through all the data in the grid and a DataTable supports enumeration. var gridTable = (DataTable)targetGrid.DataSource; // Create a graphics object from the target grid. Used for measuring text size. using (var gfx = targetGrid.CreateGraphics()) { // Iterate through the columns. for (int i = 0; i < gridTable.Columns.Count; i++) { // Leverage Linq enumerator to rapidly collect all the rows into a string array, making sure to exclude null values. string[] colStringCollection = gridTable.AsEnumerable().Where(r => r.Field<object>(i) != null).Select(r => r.Field<object>(i).ToString()).ToArray(); // Sort the string array by string lengths. colStringCollection = colStringCollection.OrderBy((x) => x.Length).ToArray(); // Get the last and longest string in the array. string longestColString = colStringCollection.Last(); // Use the graphics object to measure the string size. var colWidth = gfx.MeasureString(longestColString, targetGrid.Font); // If the calculated width is larger than the column header width, set the new column width. if (colWidth.Width > targetGrid.Columns[i].HeaderCell.Size.Width) { targetGrid.Columns[i].Width = (int)colWidth.Width; } else // Otherwise, set the column width to the header width. { targetGrid.Columns[i].Width = targetGrid.Columns[i].HeaderCell.Size.Width; } } } } |
尽管我当然绝不建议使用1000行填充DGV,但这种方法在产生与AutoResizeColumns方法非常相似的结果的同时,还带来了巨大的性能优势。
对于10k行:(10k行* 12列。)
AutoResizeColumns =?3000毫秒
FastAutoSizeColumns =?140毫秒
如果您不想覆盖DataGridView的虚拟模式所需的方法,则可以考虑使用Listview,这是另一种选择:
http://www.codeproject.com/Articles/16009/A-Much-Easier-to-Use-ListView
- It has a version (FastObjectListView) that can build a list of 100,000
objects in less than 0.1 seconds.- It has a version (DataListView)
that supports data binding, and another (FastDataListView) that
supports data binding on large (100,000+) data sets.
我不得不在几个地方禁用自动大小调整,以查看性能上的最大改进。就我而言,我为
之前,我必须禁用它们中的每一个
1 2 3 4 5 6 7 8 9 10 | dataGridView.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.None; dataGridView.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None; dataGridView.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing; // ... Bind the data here ... // Set the DataGridView auto-size modes back to their original settings. dataGridView.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells; dataGridView.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells; dataGridView.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize; |
当用户加载10000个项目或对其进行排序时,我的性能出现问题。
当我评论以下行时:
1 | this.dataEvents.AutoSizeRowsMode = System.Windows.Forms.DataGridViewAutoSizeRowsMode.AllCells; |
一切都变好了。
经过一段时间的故障排除后,我发现速度的主要问题仍然来自从非UI线程更改dataGridView。下面的代码已完全解决了我的datagridview从dataTable datasource
缓慢填充的问题
1 2 3 |
我遇到了同样的问题,我通过设置
解决了它
1 | AutoSizeRowsMode to DisplayedCellsExceptHeaders |
并为列设置相同的
@Bobby L的答案很好,但会阻止UI线程。这是我的修改,在将计算出的值应用于UI线程之前,先计算BackgroundWorker中列的宽度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | public partial class Form1 : Form { private BackgroundWorker _worker; public Form1() { InitializeComponent(); _worker = new BackgroundWorker(); _worker.DoWork += _worker_DoWork; _worker.RunWorkerCompleted += _worker_RunWorkerCompleted; } private void _worker_DoWork(object sender, DoWorkEventArgs e) { e.Result = GetAutoSizeColumnsWidth(dataGridView1); } private void _worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { SetAutoSizeColumnsWidth(dataGridView1, (int[])e.Result); } private int[] GetAutoSizeColumnsWidth(DataGridView grid) { var src = ((IEnumerable)grid.DataSource) .Cast<object>() .Select(x => x.GetType() .GetProperties() .Select(p => p.GetValue(x, null)?.ToString() ?? string.Empty) .ToArray() ); int[] widths = new int[grid.Columns.Count]; // Iterate through the columns. for (int i = 0; i < grid.Columns.Count; i++) { // Leverage Linq enumerator to rapidly collect all the rows into a string array, making sure to exclude null values. string[] colStringCollection = src.Where(r => r[i] != null).Select(r => r[i].ToString()).ToArray(); // Sort the string array by string lengths. colStringCollection = colStringCollection.OrderBy((x) => x.Length).ToArray(); // Get the last and longest string in the array. string longestColString = colStringCollection.Last(); // Use the graphics object to measure the string size. var colWidth = TextRenderer.MeasureText(longestColString, grid.Font); // If the calculated width is larger than the column header width, set the new column width. if (colWidth.Width > grid.Columns[i].HeaderCell.Size.Width) { widths[i] = (int)colWidth.Width; } else // Otherwise, set the column width to the header width. { widths[i] = grid.Columns[i].HeaderCell.Size.Width; } } return widths; } public void SetAutoSizeColumnsWidth(DataGridView grid, int[] widths) { for (int i = 0; i < grid.Columns.Count; i++) { grid.Columns[i].Width = widths[i]; } } } |
我认为您需要考虑在虚拟模式下使用数据网格。基本上,您可以预先设置网格的范围,然后根据需要覆盖" OnCellValueNeeded"。
您应该发现(尤其是仅用于1000左右的行)网格填充实际上是即时的。
祝你好运,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | Dim asrm = DataGridView1.AutoSizeRowsMode Dim ascm = DataGridView1.AutoSizeColumnsMode Dim chhs = DataGridView1.ColumnHeadersHeightSizeMode DataGridView1.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.None DataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None DataGridView1.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing DataGridView1.SuspendLayout() bs.SuspendBinding() DataGridView1.DataSource = Nothing For Each t As obj In lt_list bs.Add(t) Next DataGridView1.DataSource = bs DataGridView1.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.DisplayedCellsExceptHeaders DataGridView1.AutoSizeColumnsMode = ascm DataGridView1.ColumnHeadersHeightSizeMode = chhs bs.ResumeBinding() DataGridView1.ResumeLayout() |
16000个项目,
使用.DataSource <>没什么,需要16秒
.DataSource = Nothing
时为300毫秒