Search code examples
c#datagridviewvirtual

Virtual DataGridView with multi-colored rows causes CellValueNeeded to race


I have encountered a strange problem with my first attempt at a virtual DataGridView. The grid displays data from a DataTable, and rows are color coded based on the value of a status column. I use the event RowPostPaint to set the color of each row based on its status value.

This works great as long as the rows in the grid have the same color throughout all the columns. But when I introduce more than one color per row, the grid goes crazy and fires the CellValueNeeded event continuously and the CPU-usage goes up to 100%. The problem goes away as soon as the multi-colored row is scrolled out of view.

The DataGridView has no issues when it's in regular mode. However, I would really like to use a virtual DataGridView to achieve best performance. Does anyone know anything about this issue and how to avoid it?

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
using System.Diagnostics;

namespace VirtualDGV
{
    public partial class Form1 : Form
    {
        protected Color greenColor =  Color.FromArgb(200, 240, 140);
        protected Color redColor = Color.FromArgb(255, 100, 50);
        protected Color cyanColor = Color.FromArgb(175, 255, 225);
        protected Color pinkColor = Color.FromArgb(255, 200, 150);

        private DataTable data = null;

        public Form1()
        {
            InitializeComponent();

            LoadData2();

            dgv.VirtualMode = true;

            dgv.CellValueNeeded += new DataGridViewCellValueEventHandler(dgv_CellValueNeeded);
            dgv.RowPostPaint += new DataGridViewRowPostPaintEventHandler(dgv_RowPostPaint);
        }

        private void CreateDataTable()
        {
            if (data != null)
            {
                data.Clear();
                data.Dispose();
                data = null;
            }

            data = new DataTable();
            List<DataColumn> columns = new List<DataColumn>();
            columns.Add(new DataColumn("A", typeof(string)));
            columns.Add(new DataColumn("B", typeof(string)));
            columns.Add(new DataColumn("C", typeof(DateTime)));
            columns.Add(new DataColumn("D", typeof(DateTime)));
            columns.Add(new DataColumn("E", typeof(string)));
            columns.Add(new DataColumn("F", typeof(double)));
            columns.Add(new DataColumn("G", typeof(double)));
            columns.Add(new DataColumn("H", typeof(string)));
            columns.Add(new DataColumn("I", typeof(string)));
            columns.Add(new DataColumn("J", typeof(int)));
            columns.Add(new DataColumn("Status", typeof(int)));


            data.Columns.AddRange(columns.ToArray());
        }


        private void LoadData1()
        {
            CreateDataTable();

            data.Rows.Add("Abcdefghijklmnopqrstuvwxyz", "XYZ", DateTime.Now, DateTime.Now, "ZZZ", 110, 110, "", "", 250, 2);
            data.Rows.Add("Abcdefghijklmnopqrstuvwxyz", "XYZ", DateTime.Now, DateTime.Now, "ZZZ", 110, 110, "", "", 250, 0);
            data.Rows.Add("Abcdefghijklmnopqrstuvwxyz", "XYZ", DateTime.Now, DateTime.Now, "ZZZ", 110, 110, "", "", 250, 1);
            data.Rows.Add("Abcdefghijklmnopqrstuvwxyz", "XYZ", DateTime.Now, DateTime.Now, "ZZZ", 110, 110, "", "", 250, 5);
            data.Rows.Add("Abcdefghijklmnopqrstuvwxyz", "XYZ", DateTime.Now, DateTime.Now, "ZZZ", 110, 110, "", "", 250, 5);
            data.Rows.Add("Abcdefghijklmnopqrstuvwxyz", "XYZ", DateTime.Now, DateTime.Now, "ZZZ", 110, 110, "", "", 250, 5);
            data.Rows.Add("Abcdefghijklmnopqrstuvwxyz", "XYZ", DateTime.Now, DateTime.Now, "ZZZ", 110, 110, "", "", 250, 0);
            data.Rows.Add("Abcdefghijklmnopqrstuvwxyz", "XYZ", DateTime.Now, DateTime.Now, "ZZZ", 110, 110, "", "", 250, 0);
            data.Rows.Add("Abcdefghijklmnopqrstuvwxyz", "XYZ", DateTime.Now, DateTime.Now, "ZZZ", 110, 110, "", "", 250, 0);
            data.Rows.Add("Abcdefghijklmnopqrstuvwxyz", "XYZ", DateTime.Now, DateTime.Now, "ZZZ", 110, 110, "", "", 250, 0);
            data.Rows.Add("Abcdefghijklmnopqrstuvwxyz", "XYZ", DateTime.Now, DateTime.Now, "ZZZ", 110, 110, "", "", 250, 0);
            data.Rows.Add("Abcdefghijklmnopqrstuvwxyz", "XYZ", DateTime.Now, DateTime.Now, "ZZZ", 110, 110, "", "", 250, 0);

            dgv.RowCount = data.Rows.Count;
        }

        private void LoadData2()
        {
            CreateDataTable();
            data.Rows.Add("Abcdefghijklmnopqrstuvwxyz", "XYZ", DateTime.Now, DateTime.Now, "ZZZ", 110, 110, "", "", 250, 0);
            data.Rows.Add("Abcdefghijklmnopqrstuvwxyz", "XYZ", DateTime.Now, DateTime.Now, "ZZZ", 110, 110, "", "", 250, 9);
            data.Rows.Add("Abcdefghijklmnopqrstuvwxyz", "XYZ", DateTime.Now, DateTime.Now, "ZZZ", 110, 110, "", "", 250, 0);
            data.Rows.Add("Abcdefghijklmnopqrstuvwxyz", "XYZ", DateTime.Now, DateTime.Now, "ZZZ", 110, 110, "", "", 250, 0);
            data.Rows.Add("Abcdefghijklmnopqrstuvwxyz", "XYZ", DateTime.Now, DateTime.Now, "ZZZ", 110, 110, "", "", 250, 0);
            data.Rows.Add("Abcdefghijklmnopqrstuvwxyz", "XYZ", DateTime.Now, DateTime.Now, "ZZZ", 110, 110, "", "", 250, 0);
            data.Rows.Add("Abcdefghijklmnopqrstuvwxyz", "XYZ", DateTime.Now, DateTime.Now, "ZZZ", 110, 110, "", "", 250, 0);
            data.Rows.Add("Abcdefghijklmnopqrstuvwxyz", "XYZ", DateTime.Now, DateTime.Now, "ZZZ", 110, 110, "", "", 250, 0);
            data.Rows.Add("Abcdefghijklmnopqrstuvwxyz", "XYZ", DateTime.Now, DateTime.Now, "ZZZ", 110, 110, "", "", 250, 0);
            data.Rows.Add("Abcdefghijklmnopqrstuvwxyz", "XYZ", DateTime.Now, DateTime.Now, "ZZZ", 110, 110, "", "", 250, 0);
            data.Rows.Add("Abcdefghijklmnopqrstuvwxyz", "XYZ", DateTime.Now, DateTime.Now, "ZZZ", 110, 110, "", "", 250, 0);
            data.Rows.Add("Abcdefghijklmnopqrstuvwxyz", "XYZ", DateTime.Now, DateTime.Now, "ZZZ", 110, 110, "", "", 250, 0);
            data.Rows.Add("Abcdefghijklmnopqrstuvwxyz", "XYZ", DateTime.Now, DateTime.Now, "ZZZ", 110, 110, "", "", 250, 0);

            dgv.RowCount = data.Rows.Count;
        }


        protected void SetRowColor(DataGridViewRow row, int status, int dateColumn, int amountColumn)
        {
            switch (status)
            {
                case 10:
                    for (int i = 1; i < row.Cells.Count; i++)
                        row.Cells[i].Style.BackColor = greenColor;

                    row.Cells[dateColumn].Style.BackColor = Color.FromArgb(255, 170, 130);
                    break;

                case 11:
                    row.Cells[dateColumn].Style.BackColor = Color.FromArgb(255, 170, 130);
                    row.Cells[amountColumn].Style.BackColor = greenColor;
                    break;

                case 1:
                    for (int i = 1; i < row.Cells.Count; i++)
                        row.Cells[i].Style.BackColor = greenColor;
                    break;

                case 2:
                    for (int i = 1; i < row.Cells.Count; i++)
                        row.Cells[i].Style.BackColor = greenColor;

                    row.Cells[amountColumn].Style.BackColor = pinkColor;
                    break;

                case 3:
                    for (int i = 1; i < row.Cells.Count; i++)
                        row.Cells[i].Style.BackColor = greenColor;

                    row.Cells[dateColumn].Style.BackColor = Color.FromArgb(255, 170, 130);
                    break;

                case 4:
                    row.Cells[dateColumn].Style.BackColor = Color.FromArgb(255, 170, 130);
                    row.Cells[amountColumn].Style.BackColor = Color.FromArgb(255, 200, 150);
                    break;

                case 5:
                    for (int i = 1; i < row.Cells.Count; i++)
                        row.Cells[i].Style.BackColor = redColor;
                    break;

                case 6:
                    for (int i = 1; i < row.Cells.Count; i++)
                        row.Cells[i].Style.BackColor = cyanColor;
                    break;

                case 7:
                    row.Cells[amountColumn].Style.BackColor = greenColor;
                    break;

                case 8:
                    for (int i = 1; i < row.Cells.Count; i++)
                        row.Cells[i].Style.BackColor = cyanColor;
                    break;

                case 9:
                    for (int i = 1; i < row.Cells.Count; i++)
                        row.Cells[i].Style.BackColor = greenColor;

                    row.Cells[dateColumn].Style.BackColor = redColor;
                    row.Cells[amountColumn].Style.BackColor = redColor;
                    break;

            }
        }


        private void dgv_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
        {
            //Debug.WriteLine(e.RowIndex + ", " + e.ColumnIndex);

            e.Value = data.Rows[e.RowIndex][e.ColumnIndex];
        }

        private void dgv_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e)
        {
            SetRowColor(dgv.Rows[e.RowIndex], (int)data.Rows[e.RowIndex]["Status"], 4, 7);
        }
    }
}

Solution

  • Handle CellFormatting event instead of RowPostPaint like this:

    private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
    {
        if (e.RowIndex == 1 && e.ColumnIndex == 2)
        {
            e.CellStyle.ForeColor = Color.Red;
        }
    }
    

    Use exclusively the CellStyle property and don't touch dgv[col, row].Style properties because will fire the event again.