views:

330

answers:

2

Ok, so I'm binding a DataGridView to a BindingSource in a background thread while a little, "Please Wait" model window keeps the user entertained. No problem.

However, I need to change some of the rows background colors based on the row's databounditem type. Like this:

for (int i = 0; i < dgItemMaster.Rows.Count; i++)
{
  if (dgItemMaster.Rows[i].DataBoundItem.GetType().Name == "Package")
  {
   dgItemMaster.Rows[i].DefaultCellStyle.BackColor = Color.PowderBlue;                    
  }
}

Programatically I can do this but it is enough rows that it will lock up the GUI while it is iterating the rows. I'm looking for ideas on the best way to deal with the situation.

This is what I'm doing now:

void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            dgItemMaster.DataSource = products;
            dgItemMaster.BeginInvoke((Action)(() =>
            {
                for (int i = 0; i < dgItemMaster.Rows.Count; i++)
                {
                    if (dgItemMaster.Rows[i].DataBoundItem.GetType().Name == "Package")
                    {
                        dgItemMaster.Rows[i].DefaultCellStyle.BackColor = Color.PowderBlue;
                    }
                    else if (dgItemMaster.Rows[i].DataBoundItem.GetType().Name == "PackageKit")
                    {
                        dgItemMaster.Rows[i].DefaultCellStyle.BackColor = Color.Pink;
                    }
                }
            }));
        }
+1  A: 

I would try running the code that changes the backcolor in the RowAdded event, this will be fired for each row as it's added to the Grid, no need to iterate through the whole list again.

http://msdn.microsoft.com/en-us/library/system.windows.forms.datagridview.rowsadded.aspx

Good luck.

Ricardo Villamil
+1  A: 

What is the volume of data here? For it to hang up the UI it must be non-trivial. One extreme answer would be to switch to virtual mode - but that is a lot of work.

If you simply don't want to hang the UI, perhaps just do the first x (20? 50?) rows immediately, and do the remainder in batches - essentially emulating DoEvents, just without the code-smell of DoEvents...

(untested)

        int startIndex = 0;
        Action action = null;
        action = () =>
        {   // only processes a batch of 50 rows, then calls BeginInvoke
            // to schedule the next batch
            int endIndex = startIndex + 50;
            if (endIndex > dgItemMaster.Rows.Count) endIndex = dgItemMaster.Rows.Count;

            if (startIndex > endIndex)
            {
                for (int i = startIndex; i < endIndex; i++)
                {
                    // process row i
                }

                startIndex = endIndex;
                this.BeginInvoke(action); // next iteration
            }                
        };

        // kick it off
        this.BeginInvoke(action);
Marc Gravell