如何防止数据网格中的行在应用程序运行时闪烁
Posted
技术标签:
【中文标题】如何防止数据网格中的行在应用程序运行时闪烁【英文标题】:How to prevent rows in datagrid from flickering while application is running 【发布时间】:2011-01-03 17:58:16 【问题描述】:在我正在开发的应用程序中,我正在使用 datagridview 来显示数据。要填充它,我必须按下一个按钮,后台工作程序将开始运行,它将填充一个数据表,当它完成运行时,它将使用数据表作为数据网格的数据源。这工作正常,用户界面保持响应等等。 但是现在我已经根据它们的值实现了对行的着色(我还在玩它,所以欢迎提出任何建议):
private void ApplyColoring()
if (dataGridView1.DataSource != null)
foreach (DataGridViewRow dataGridRow in dataGridView1.Rows)
// hardmap a color to a column
IDictionary<Int32, Color> colorDictionary = new Dictionary<Int32, Color>();
colorDictionary.Add( 7, Color.FromArgb(194, 235, 211));
colorDictionary.Add( 8, Color.Salmon);
colorDictionary.Add( 9, Color.LightBlue);
colorDictionary.Add(10, Color.LightYellow);
colorDictionary.Add(11, Color.LightGreen);
colorDictionary.Add(12, Color.LightCoral);
colorDictionary.Add(13, Color.Blue);
colorDictionary.Add(14, Color.Yellow);
colorDictionary.Add(15, Color.Green);
colorDictionary.Add(16, Color.White);
foreach (DataGridViewRow gridRow in dataGridView1.Rows)
foreach (DataGridViewCell cell in gridRow.Cells)
if (colorDictionary.Keys.Contains(cell.ColumnIndex))
// standard background
cell.Style.BackColor = Color.FromArgb(194, 235, 211);
IList<String> checkedValues = new List<String>();
// first we loop through all the rows
foreach (DataGridViewRow gridRow in dataGridView1.Rows)
IDictionary<String, Int32> checkedVal = new Dictionary<String, Int32>();
// then we loop through all the data columns
int maxCol = dnsList.Count + 7;
for (int columnLoop = 7; columnLoop < maxCol; columnLoop++)
string current = gridRow.Cells[columnLoop].Value.ToString();
for (int checkLoop = 7; checkLoop < maxCol; checkLoop++)
string check = gridRow.Cells[checkLoop].Value.ToString();
if (!current.Equals(check))
if (checkedVal.Keys.Contains(current))
gridRow.Cells[columnLoop].Style.BackColor = colorDictionary[checkedVal[current]];
else
gridRow.Cells[columnLoop].Style.BackColor = colorDictionary[columnLoop];
checkedVal.Add(current, columnLoop);
这给我带来了麻烦。不是因为着色不起作用,而是它起作用。但是因为它让它变慢了。第一次运行良好,但是当我再次按下按钮时,速度慢得要命,数据网格在闪烁。 我希望这个作为后处理运行,所以它(或者更确切地说应该)在后台工作完成后运行。 但是当我从 RunWorkerCompleted 事件中调用 applycoloring 时,它的速度很慢。我应该怎么做才能防止这种情况?如何确保 UI 在执行新查询时不会闪烁(同时不会丢失网格中的当前数据)。
【问题讨论】:
除了suspendlayout()之外,还要避免所有那些嵌套循环(我不知道它到底做了什么,但看起来太复杂了,真的不需要)。此外,所有这些 .ToString() 可能会降低一些性能。 您可能可以删除第一个 foreach 循环,因为它似乎什么都不做,只会显着增加循环数!!! foreach (DataGridViewRow dataGridRow in dataGridView1.Rows) 你说得对,我已经删除了 2 个 foreach 循环。标准着色不必在单独的循环中 + 最上面的 foreach 完全没用:) 我会研究 ToString() 评论:) 我的问题类似,但我的情况不同。当显示的数据大小与 DataGridview 的垂直大小完全相同时,DataGridView 不会停止闪烁。我通过设置 DataGridView..AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.None; 解决了这个问题 【参考方案1】:你可以打开双缓冲。
VB:
Imports System.Reflection
输入以下内容,例如在 Form_Load 中
Dim systemType As Type = DataGridView1.GetType()
Dim propertyInfo As PropertyInfo = systemType.GetProperty("DoubleBuffered", BindingFlags.Instance Or BindingFlags.NonPublic)
propertyInfo.SetValue(DataGridView1, True, Nothing)
对于 C#:转到:Fixing a slow scrolling DataGridView
干杯
【讨论】:
我发现如果我使用 RowPostPaint 事件,它会导致每隔几秒闪烁一次,这个解决方案解决了这个问题。【参考方案2】:两个建议:
-
尝试在循环之前调用 SuspendLayout() 并在循环之后调用 ResumeLayout()。大多数其他控件将此称为 BeginUpdate() 和 EndUpdate()(列表视图、组合框等)。
如果您要处理大量数据,请在 DataGridView 上使用 VirtualMode
数据。
【讨论】:
【参考方案3】:我找到了另一种为慢速数据网格进行反射双缓冲的方法:
创建一个带有一些反射的扩展方法以在数据网格上设置双缓冲:
public static class DataGridViewExtensioncs
public static void DoubleBuffered(this DataGridView dgv, bool setting)
var dgvType = dgv.GetType();
var pi = dgvType.GetProperty("DoubleBuffered",
BindingFlags.Instance | BindingFlags.NonPublic);
pi.SetValue(dgv, setting, null);
然后在表单初始化器中做:
this.dataGrid.DoubleBuffered(true);
这与 Evolved 的答案非常相似,并且归功于 Shweta Lodha: http://www.codeproject.com/Tips/390496/Reducing-flicker-blinking-in-DataGridView
【讨论】:
【参考方案4】:开启双缓冲
要编译的函数所需的命名空间列表是:
using System;
using System.Reflection;
using System.Windows.Forms;
public static class ExtensionMethods
public static void DoubleBuffered(this DataGridView dgv, bool setting)
Type dgvType = dgv.GetType();
PropertyInfo pi = dgvType.GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic);
pi.SetValue(dgv, setting, null);
然后初始化datagridview
dataGridView1.DoubleBuffered(true);
【讨论】:
【参考方案5】:尝试在更新之前调用 SuspendLayout。不要忘记调用 ResumeLayout。
【讨论】:
【参考方案6】:我强烈建议不要在网格上循环(双缓冲可能会有所帮助,但它只是“隐藏”真正的问题),因为它会产生大量渲染。
出于这样的目的,我使用事件处理程序:
dataGridView1.CellValueChanged += new DataGridViewCellEventHandler(dataGridView1_CellValueChanged);
private void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
if (dataGridView1.Columns[5].Index == e.ColumnIndex && e.RowIndex >= 0 && dataGridView1[5, e.RowIndex].Value.ToString() != "0")
e.CellStyle.BackColor = Color.PaleGreen;
只要您的单元格值发生更改,您的网格就会自动更新并更改背景颜色
【讨论】:
以上是关于如何防止数据网格中的行在应用程序运行时闪烁的主要内容,如果未能解决你的问题,请参考以下文章
如何防止自定义 UITableViewCells 在取消选择时闪烁白色?