Search code examples
c#datagridviewbackcolor

Detecting BackColor change of the DataGridViewCell


I'm working on a class, let's call it MyDataGridView, derived from DataGridView that must be synchronized with the Excel file (i.e. when something changes in the DataGridView, I need to make the same changes to the Excel file).

To detect changes in the DataGridView I use events (for example, RowsAdded, ColumnRemoved, CellValueChanged, etc). But I have a problem with detecting cell whose BackColor have been changed.

The color is changed by the other programmer who uses my class. To do this, he can use the following code:

MyDataGridView myDataGridView;
// create and fill MyDataGridView...
myDataGridView.Rows[0].Cells[0].Style.BackColor = Color.AliceBlue;

My goal is to detect changes of BackColor property to change the Excel file.

To achieve this goal, I (unsuccessfully) tried to use several methods:

  1. CellStyleContentChanged event (problem: can't get the Cell itself from the event handler).

  2. CellFormatting event (problems: event rises so many times and I can't get the reason of its occurrence).

  3. CellStyleChanged event (problem: event only occurs when the Style property changes, but not Style.BackColor).

  4. overriding of DataGridViewCellStyle class (problem: I don't know how to correctly override this class and whether it is possible at all).

Code snippet that will help to reproduce my attempts:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        dataGridView1.CellStyleContentChanged += dataGridView1_CellStyleContentChanged;
        dataGridView1.CellFormatting += dataGridView1_CellFormatting;
    }

    // Goal
    private void Form1_Load(object sender, EventArgs e)
    {
        dataGridView1.Columns.Add("column1", "column1");
        dataGridView1.Columns.Add("column2", "column2");
        dataGridView1.Rows.Add("cell1", "cell2");

        // how to detect this?
        dataGridView1.Rows[0].Cells[0].Style.BackColor = Color.AliceBlue;
    }

    // Attempt #1
    void dataGridView1_CellStyleContentChanged(
        object sender, DataGridViewCellStyleContentChangedEventArgs e)
    {
        // how to get cell itself (rowIndex & columnIndex)?
    }

    // Attempt #2
    void dataGridView1_CellStyleChanged(object sender, DataGridViewCellEventArgs e)
    {
        // event only occurs when the Style property changes, but not Style.BackColor
    }

    // Attempt #3
    void dataGridView1_CellFormatting(
        object sender, DataGridViewCellFormattingEventArgs e)
    {
        // event rises so many times!
        // and how to get reason of formatting (i need to detect only color change)?
    }

    // Attempt #4
    // do i need something like this?
    public class MyDataGridView : DataGridView
    {
        public class MyDataGridViewRowCollection : DataGridViewRowCollection
        {
            public MyDataGridViewRowCollection(DataGridView _dgv) : base(_dgv) { }

            public class MyDataGridViewRow : DataGridViewRow
            {
                public class MyDataGridViewCellCollection : DataGridViewCellCollection
                {
                    public MyDataGridViewCellCollection(DataGridViewRow _dgvRow) :
                        base(_dgvRow) { }

                    public class MyDataGridViewCell : DataGridViewCell
                    {
                        private new MyDataGridViewCellStyle Style { get; set; }

                        public class MyDataGridViewCellStyle : DataGridViewCellStyle
                        {
                            public new Color BackColor
                            {
                                get
                                {
                                    return base.BackColor;
                                }
                                set
                                {
                                    base.BackColor = value;

                                    // TODO: changes in Excel
                                    // ...
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

I will be glad to any advice and answers!


Solution

  • Discussion with @TaW prompted me to use several events at once.

    We use CellStyleContentChanged event for detect Style.BackColor changes and CellFormatting event to get Cell, whose BackColor have been changed.

    Also we need some Collection to store changed colors, because these events don't occur sequentially (see log below).

    Remark!! This method only works for user-visible cells (DataGridView should not have scroll bars).

    public partial class Form1 : Form
    {
        string log = "";
        List<Color> changedBackColors = new List<Color>();
    
        public Form1()
        {
            InitializeComponent();
            this.dataGridView1.CellStyleContentChanged +=
                dataGridView1_CellStyleContentChanged;
            this.dataGridView1.CellFormatting +=
                dataGridView1_CellFormatting;
            this.Shown += Form1_Shown;
        }
    
        void Form1_Shown(object sender, EventArgs e)
        {
            log += "Change Cell[0,0] (color = blue)\r\n";
            this.dataGridView1.Rows[0].Cells[0].Style.BackColor = Color.Blue;
    
            log += "Change Cell[0,1] (color = red)\r\n";
            this.dataGridView1.Rows[0].Cells[1].Style.BackColor = Color.Red;
        }
    
        private void Form1_Load(object sender, EventArgs e)
        {
            this.dataGridView1.Columns.Add("column1", "column1");
            this.dataGridView1.Columns.Add("column1", "column1");
            this.dataGridView1.Rows.Add("cell1", "cell2");
        }
    
        private void dataGridView1_CellStyleContentChanged(
            object sender, DataGridViewCellStyleContentChangedEventArgs e)
        {
            log += string.Format(
                "CellStyleContentChanged occurs for Cell[?,?] (color = {0})\r\n",
                e.CellStyle.BackColor.Name);
            this.changedBackColors.Add(e.CellStyle.BackColor);
        }
    
        private void dataGridView1_CellFormatting(
            object sender, DataGridViewCellFormattingEventArgs e)
        {
            if (this.changedBackColors.Count > 0)
            {
                if (this.changedBackColors.Contains(e.CellStyle.BackColor))
                {
                    log += string.Format(
                        "CellFormatting occurs for Cell[{0},{1}] (color = {2})\r\n",
                         e.RowIndex, e.ColumnIndex, e.CellStyle.BackColor.Name);
    
                    this.changedBackColors.Remove(e.CellStyle.BackColor);
    
                    // TODO: change excel file
                    // ...
                }
            }
        }
    }
    

    Log:

    • Change Cell[0,0] (color = blue)
    • CellStyleContentChanged occurs for Cell[?,?] (color = Blue)
    • Change Cell[0,1] (color = red)
    • CellStyleContentChanged occurs for Cell[?,?] (color = Red)
    • CellFormatting occurs for Cell[0,0] (color = Blue)
    • CellFormatting occurs for Cell[0,1] (color = Red)