我正在使用一个 BindingSource
控件 (参考链接) 来填充我的 DataGridView
控件。约有1000多条记录在其中进行填充,我正在使用线程来完成这个过程。在这种情况下,DataGridView
的性能非常慢。
我尝试将 DoubleBuffered
属性设置为 true,RowHeadersWidthSizeMode
禁用,AutoSizeColumnsMode
设置为 none。但还是出现了同样的行为。
我该如何提高表格的性能?
我正在使用一个 BindingSource
控件 (参考链接) 来填充我的 DataGridView
控件。约有1000多条记录在其中进行填充,我正在使用线程来完成这个过程。在这种情况下,DataGridView
的性能非常慢。
我尝试将 DoubleBuffered
属性设置为 true,RowHeadersWidthSizeMode
禁用,AutoSizeColumnsMode
设置为 none。但还是出现了同样的行为。
我该如何提高表格的性能?
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 的双缓冲是否已正确开启:
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消息禁用重绘也会有所帮助:
// *** 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();
如果您不需要双向数据绑定或BindingSource提供的某些功能(如过滤等),则可以考虑使用 DataGridView.Rows.AddRange() 方法一次添加多行。
源文章链接及示例: http://10tec.com/articles/why-datagridview-slow.aspx
确保您不要自动调整列大小,这可以提高性能。
也就是说,不要这样做:
Datagridview.Columns[I].AutoSizeMode = DataGridViewAutoSizeColumnMode.xxxxx;
我知道我来晚了,但最近我对DataGridView控件的自动调整大小速度感到非常不满,所以我觉得有人会从我的解决方案中受益。
我为手动测量和调整DataGridView列大小创建了这个扩展方法。将AutoSizeColumnsMode设置为DataGridViewAutoSizeColumnsMode.None,并在设置DataSource后调用此方法。
/// <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;
}
}
}
}
虽然我绝对不建议在DGV中填充1000+行,但这种方法可以大大提高性能,同时产生与AutoResizeColumns方法非常相似的结果。
对于10k行:(10k行*12列)
AutoResizeColumns=约3000毫秒
FastAutoSizeColumns=约140毫秒
为了获得更好的性能提升,我不得不在几个地方禁用自动调整大小。在我的情况下,我启用了 AutoSizeRowsMode
, AutoSizeColumnsMode
, 和 ColumnHeadersHeightSizeMode
的自动调整大小模式。因此,在将数据绑定到 DataGridView
之前,我需要禁用每个自动调整大小模式:
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;
ColumnHeadersHeightSizeMode
,其他选项已经在此处禁用。此页面提供的所有其他解决方案都没有效果或使初始启动时的加载更糟(但仍然始终在1秒内完成)。 - ygoe将AutoSizeColumnsMode设置为None,将AutoSizeRowsMode设置为DisplayedCells,这对我有所帮助。
this.dataEvents.AutoSizeRowsMode = System.Windows.Forms.DataGridViewAutoSizeRowsMode.AllCells;
一切变得很好。
for (int z = 0; z < dataGridView1.Columns.Count; z++)
{
dataGridView1.Columns[z].AutoSizeMode = DataGridViewAutoSizeColumnMode.None;
}
... Code where I change the content of dataGridView1 in a loop ...
for (int z = 0; z < dataGridView1.Columns.Count; z++)
{
dataGridView1.Columns[z].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
}
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];
}
}
}