7

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).

H H
  • 263,252
  • 30
  • 330
  • 514
Oxymoron
  • 1,382
  • 4
  • 20
  • 40
  • in addition to suspendlayout(), also avoid all those nested loops (I cannot know what that really does, but seems too convoluted to be honestly needed). Also all those .ToString()'s are probably killing some of performance. – Pasi Savolainen Jan 11 '10 at 12:51
  • You can probably remove the first foreach loop as it seems to do nothing and only increased the number of loops significantly!!! foreach (DataGridViewRow dataGridRow in dataGridView1.Rows) – Kurru Jan 11 '10 at 13:19
  • You're totally right, I've removed 2 foreach loops. The standard coloring doesn't have to be in a separate loop + the top most foreach is utterly useless :) I'll look into the ToString() remark :) – Oxymoron Jan 14 '10 at 08:14
  • My problem is similar but my scenario is different. The DataGridView does not stop flickering when the size of the data being displayed is just exactly the same as the vertical size of the DataGridview. I solved this by setting DataGridView..AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.None; – Ronald Ramos Jul 27 '19 at 21:31

6 Answers6

19

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

Evolved
  • 599
  • 6
  • 5
  • I found if I used the RowPostPaint Event, it caused flashing every couple of seconds, this solutions fixed that. – Wize May 15 '14 at 14:06
6

Two suggestions:

  1. Try calling SuspendLayout() prior to the loop and ResumeLayout() after the loop. Most other controls call this BeginUpdate() and EndUpdate() (Listviews, Combobox's etc).
  2. Use VirtualMode on DataGridView if you're dealing with large amount of data.
Martijn Laarman
  • 13,476
  • 44
  • 63
6

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

Wouter Simons
  • 2,856
  • 1
  • 19
  • 15
3

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);
Ramgy Borja
  • 2,330
  • 2
  • 19
  • 40
1

Try to call SuspendLayout before your updates. Do not forget to call ResumeLayout.

Arthur
  • 7,939
  • 3
  • 29
  • 46
1

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

Lennart
  • 11
  • 4