bmt22033
bmt22033

Reputation: 7240

Fastest way to set backcolor for WinForms DataGridView rows

I have a WinForms DataGridView that is bound to a BindingSource which, in turn, is bound to a BindingList of 100,000 objects.

BindingList<MyObject> myObjectList = new BindingList<MyObject>();
BindingSource bindingSourceForMyObjects = new BindingSource();

bindingSourceForMyObjects.DataSource = myObjectList;
dataGridViewMyObjects.DataSource = bindingSourceForMyObjects;

I have an event handler hooked up to my DataGridView's CellValueChanged event that contains the following code:

dataGridViewMyObjects.Rows[e.RowIndex].DefaultCellStyle.BackColor = Color.Red;
dataGridViewMyObjects.Rows[e.RowIndex].DefaultCellStyle.ForeColor = Color.White;

So when my user changes a row, this event handler fires and changes the row to white on red to indicate that the data has changed. This works great but I also have some cases where I need to programatically change the underlying list and I'd like those changes to reflect in the DataGridView, as well. To accomplish that, my object class implements INotifyPropertyChanged and I have an event handler hooked up to the ListChanged event of my BindingSource. The code in that event handler looks like this:

if (e.ListChangedType == ListChangedType.ItemChanged)
{
    dataGridViewMyObjects.Rows[e.NewIndex].DefaultCellStyle.BackColor = Color.Red;
    dataGridViewMyObjects.Rows[e.NewIndex].DefaultCellStyle.ForeColor = Color.White;
}

This is also working so if I programatically modify 50 of the objects, the DataGridView rows get updated as well. However, as I said before, I have 100,000 objects that I'm dealing with and if I need to modify more than ~800 objects, it can take a while because of all the calls to OnPropertyChanged() in my object. In the absolute worst case, if I need to modify all 100,000 objects, it can take nearly 1 minute for this to happen.

Inside of my object class, I have a boolean variable which I use to avoid triggering the OnPropertyChanged() call when I'm programatically doing "bulk" updates (> 800 objects). This makes updating the object properties very fast but the corresponding rows in the DataGridView don't get their forecolor/backcolor values updated anymore since the two-way binding is being bypassed. I know which rows correspond to the objects that were modified and I've tried looping through them and updating the ForeColor/BackColor values but again, it takes nearly a minute for this operation to complete.

I've tried wrapping that loop in...

dataGridViewMyObjects.SuspendLayout();
// loop here
dataGridViewMyObjects.ResumeLayout();

but that didn't seem to make a difference in performance. Is there a faster way to set the ForeColor/BackColor for lots of rows or is the speed I'm seeing simply a matter of the size of the data that I'm working with?

Upvotes: 2

Views: 1475

Answers (1)

LarsTech
LarsTech

Reputation: 81620

One thing to try is to tell windows to stop painting your control while you loop through your changes. From How do I suspend painting for a control and its children?

class DrawingControl
{
    [DllImport("user32.dll")]
    public static extern int SendMessage(IntPtr hWnd, Int32 wMsg, bool wParam, Int32 lParam);

    private const int WM_SETREDRAW = 11; 

    public static void SuspendDrawing( Control parent )
    {
        SendMessage(parent.Handle, WM_SETREDRAW, false, 0);
    }

    public static void ResumeDrawing( Control parent )
    {
        SendMessage(parent.Handle, WM_SETREDRAW, true, 0);
        parent.Refresh();
    }
}

Then your code would look like this:

DrawingControl.SuspendDrawing(dataGridViewMyObjects);
// loop here
DrawingControl.ResumeDrawing(dataGridViewMyObjects);

Upvotes: 3

Related Questions