问题
In the application I'm developing at the moment I'm using a datagridview to display data. To fill it, I've to press a button and a backgroundworker will start running, it will fill a datatable and when it's finished running it will use the datatable as the datasource for the datagrid. This works fine, the UI stays responsive et cetera. But now I've implemented coloring to the rows, depending on their values (Im still playing around with it, so any suggestions are welcome):
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);
}
}
}
}
}
}
}
}
This is giving me problems. Not because the coloring doesn't work, it does. But because it makes it hella slow. The first time it runs fine, but when i press the button again, it's slow as hell and the datagrid is flickering. I want this run as postprocess, so it's (or rather should) be run after the backgroundworker is completed. But when i call applycoloring from the RunWorkerCompleted event its just slow. What should I do to prvent this? How can I make sure the UI doesnt flicker while its executing a new query (while NOT LOSING the current data in the grid).
回答1:
Two suggestions:
- Try calling SuspendLayout() prior to the loop and ResumeLayout() after the loop. Most other controls call this BeginUpdate() and EndUpdate() (Listviews, Combobox's etc).
- Use VirtualMode on DataGridView if you're dealing with large amount of data.
回答2:
you can turn on double buffering.
VB:
Imports System.Reflection
put the following e.g. in 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)
For C#: go to:Fixing a slow scrolling DataGridView
Cheers
回答3:
I found another way to do the reflection double buffering for slow datagrids:
Create an extension method with some reflection to set double buffering on the datagrid:
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);
}
}
Then in the form initializer do:
this.dataGrid.DoubleBuffered(true);
This is very similar to Evolved's answer and credits go to Shweta Lodha: http://www.codeproject.com/Tips/390496/Reducing-flicker-blinking-in-DataGridView
回答4:
Try to call SuspendLayout before your updates. Do not forget to call ResumeLayout.
回答5:
I would highly recommend not to loop over the grid (double buffering might help but it is just "hiding" the real problem) since it produces a lot of rendering.
For purposes like this I use a eventhandler:
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;
}
}
Anytime your cell values change your grid gets automatically updates and changes the back color
回答6:
Turn on double buffering
List of namespaces required for the function to compile is:
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);
}
}
then initialize datagridview
dataGridView1.DoubleBuffered(true);
来源:https://stackoverflow.com/questions/2041782/how-to-prevent-rows-in-datagrid-from-flickering-while-application-is-running