DataGridView逐行设置DefaultCellStyle运行异常缓慢问题排查
为什么你的DataGridView样式设置这么慢?
首先,这种速度肯定不正常——处理5000行花几十分钟完全是可以优化的,核心问题出在你直接操作DataGridView的UI行对象,以及一些可以避免的重复开销上。
慢的核心原因
你当前的代码是遍历DataTable的行,然后直接操作dataGridView1.Rows[r]这个UI控件对象。哪怕用了SuspendLayout(),每次访问DataGridView的Row集合都会触发底层的UI控件逻辑,5000次这样的操作累积起来开销巨大。另外,你每次循环都重复访问dbInterface.ds.Tables["articles"],这也会增加不必要的属性访问开销。
优化方案:从一次性全量处理改为按需绘制
最有效的优化是利用DataGridView的RowPrePaint事件,它只会在绘制可见行的时候触发,不需要一次性处理所有5000行,性能会提升几个数量级。
步骤1:提前定义样式(作为类级变量)
把样式对象移到类的顶部,避免每次函数调用都重新创建:
private DataGridViewCellStyle _groupCellStyle; private DataGridViewCellStyle _subTotCellStyle; // 在窗体构造函数或者初始化方法里初始化样式 public YourForm() { InitializeComponent(); InitCellStyles(); } private void InitCellStyles() { _groupCellStyle = new DataGridViewCellStyle(); _subTotCellStyle = new DataGridViewCellStyle(); _groupCellStyle.Font = new Font(dataGridView1.Font, FontStyle.Bold); _groupCellStyle.BackColor = Color.FromArgb(235, 235, 235); _subTotCellStyle.BackColor = Color.FromArgb(155, 155, 255); }
步骤2:注册RowPrePaint事件,按需设置样式
取消原来的formatRows()函数,改用事件处理:
// 在窗体加载或者数据绑定完成后注册事件 private void YourForm_Load(object sender, EventArgs e) { dataGridView1.RowPrePaint += DataGridView1_RowPrePaint; // 这里加上你的数据绑定逻辑 } private void DataGridView1_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e) { // 跳过新行(如果启用了AllowUserToAddRows) if (e.RowIndex == dataGridView1.NewRowIndex) return; // 缓存DataTable,避免重复访问 var dt = dbInterface.ds.Tables["articles"]; short flgGroup = dt.Rows[e.RowIndex].Field<short>("FLGGroup"); DataGridViewCellStyle targetStyle = null; if (flgGroup == 2) targetStyle = _groupCellStyle; else if (flgGroup == 0) targetStyle = _subTotCellStyle; if (targetStyle != null) { dataGridView1.Rows[e.RowIndex].DefaultCellStyle = targetStyle; } }
步骤3:开启DataGridView的性能优化开关
再给DataGridView加上几个性能提升的设置,进一步加速:
// 在窗体初始化时添加这些设置 private void InitDataGridViewSettings() { // 关闭自动列宽(如果不需要动态调整) dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None; // 关闭自动行高 dataGridView1.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.None; // 关闭行头(如果不需要) dataGridView1.RowHeadersVisible = false; // 开启双缓冲(通过反射设置,因为它是保护属性) typeof(DataGridView).InvokeMember( "DoubleBuffered", BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic, null, dataGridView1, new object[] { true } ); }
如果你坚持要一次性设置所有行
如果必须一次性处理完所有行,那至少要优化循环逻辑,减少UI交互开销:
private void FormatRowsOptimized() { var dt = dbInterface.ds.Tables["articles"]; int rowCount = dt.Rows.Count; dataGridView1.SuspendLayout(); // 关闭重绘 dataGridView1.Visible = false; for (int r = 0; r < rowCount; r++) { short flgGroup = dt.Rows[r].Field<short>("FLGGroup"); if (flgGroup == 2) dataGridView1.Rows[r].DefaultCellStyle = _groupCellStyle; else if (flgGroup == 0) dataGridView1.Rows[r].DefaultCellStyle = _subTotCellStyle; } dataGridView1.Visible = true; dataGridView1.ResumeLayout(); }
这里加上dataGridView1.Visible = false可以避免循环过程中的UI重绘,比单纯用SuspendLayout()效果更好。
最后说两句
5000行36列的DataGridView本身不算特别大,优化后应该能在几秒内完成样式设置。优先用RowPrePaint事件的方案,因为它是按需处理,内存和性能都更友好。
内容的提问来源于stack exchange,提问作者MonkeyH
相关产品推荐
相关产品推荐

