views:

238

answers:

4

Hello all,

This is what I'm currently using to implement a "Check all" feature on a bound DataGridView:

int max = ((DataTable)dataGridRes.DataSource).Rows.Count;
for (int i = 0; i < max; i++)
{
    if(((DataTable)dataGridRes.DataSource).Rows[i].Field<long>(0) == 0)
        ((DataTable)dataGridRes.DataSource).Rows[i].SetField(0, 1);
}

However this code is hopelessly slow. On a 625 row DataTable it takes aprox 5 seconds to complete on my computer. Very unacceptable.

What am I doing wrong? What better way can I use to do bulk edits on a DataGridView?

A: 

Second try.

  1. You are casting way too many times
  2. You are doing an if statement that doesn't matter

try something like:

foreach(DataRow r in ((DataTable)dataGridRes.DataSource).Rows)
{
    r.SetField(0, 1);
}
Jan Jongboom
Thanks Jan. It's still fantastically slow. The if statement actually helped speed up the process because I eventually noticed the bootleneck was on the setfield operation. However I found a way to speed it considerably. If if unbound the control, do the bulk edit and then rebind, the speed it gallops. Only problem is that when I'm rebinding the control the checkmark is removed and is replaced by 1s and 0s. I'm updating my question
Krugar
+1  A: 

This blog has what you need: DataGridView CheckBox Select All

Code posted for posterity:

public delegate void CheckBoxClickedHandler(bool state);
public class DataGridViewCheckBoxHeaderCellEventArgs : EventArgs
{
    bool _bChecked;
    public DataGridViewCheckBoxHeaderCellEventArgs(bool bChecked)
    {
     _bChecked = bChecked;
    }
    public bool Checked
    {
     get { return _bChecked; }
    }
}

class DatagridViewCheckBoxHeaderCell : DataGridViewColumnHeaderCell
{
    Point checkBoxLocation;
    Size checkBoxSize;
    bool _checked = false;
    Point _cellLocation = new Point();
    System.Windows.Forms.VisualStyles.CheckBoxState _cbState =
     System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal;

    public event CheckBoxClickedHandler OnCheckBoxClicked;

    public DatagridViewCheckBoxHeaderCell()
    {
    }

    protected override void Paint(System.Drawing.Graphics graphics,
     System.Drawing.Rectangle clipBounds,
     System.Drawing.Rectangle cellBounds,
     int rowIndex,
     DataGridViewElementStates dataGridViewElementState,
     object value,
     object formattedValue,
     string errorText,
     DataGridViewCellStyle cellStyle,
     DataGridViewAdvancedBorderStyle advancedBorderStyle,
     DataGridViewPaintParts paintParts)
    {
     base.Paint(graphics, clipBounds, cellBounds, rowIndex,
      dataGridViewElementState, value,
      formattedValue, errorText, cellStyle,
      advancedBorderStyle, paintParts);

     Point p = new Point();

     Size s = CheckBoxRenderer.GetGlyphSize(graphics,
      System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal);

     p.X = cellBounds.Location.X +
      (cellBounds.Width / 2) - (s.Width / 2);
     p.Y = cellBounds.Location.Y +
      (cellBounds.Height / 2) - (s.Height / 2);

      _cellLocation = cellBounds.Location;
      checkBoxLocation = p;
      checkBoxSize = s;
     if (_checked)
      _cbState = System.Windows.Forms.VisualStyles.
      CheckBoxState.CheckedNormal;
     else
      _cbState = System.Windows.Forms.VisualStyles.
      CheckBoxState.UncheckedNormal;
      CheckBoxRenderer.DrawCheckBox
      (graphics, checkBoxLocation, _cbState);
    }

    protected override void OnMouseClick(DataGridViewCellMouseEventArgs e)
    {
     Point p = new Point(e.X + _cellLocation.X, e.Y + _cellLocation.Y);
     if (p.X >= checkBoxLocation.X && p.X <=
      checkBoxLocation.X + checkBoxSize.Width
      && p.Y >= checkBoxLocation.Y && p.Y <=
      checkBoxLocation.Y + checkBoxSize.Height)
     {
     _checked = !_checked;
     if (OnCheckBoxClicked != null)
     {
      OnCheckBoxClicked(_checked);
      this.DataGridView.InvalidateCell(this);
     }
    }
    base.OnMouseClick(e);
  }
}

The code to add the checkbox to header and the corresponding event code is given below.

private void FormatGrid()
{
    DataView dv = new DataView();
    dv.Table = _loginDs.Tables[0];

    DataGridViewCheckBoxColumn chkbox = new DataGridViewCheckBoxColumn();
    DatagridViewCheckBoxHeaderCell chkHeader = new DatagridViewCheckBoxHeaderCell();
    chkbox.HeaderCell = chkHeader;
    chkHeader.OnCheckBoxClicked += new CheckBoxClickedHandler(chkHeader_OnCheckBoxClicked);
    _chkBoxGrid.Columns.Add(chkbox);

    DataGridViewTextBoxColumn uname = new DataGridViewTextBoxColumn();
    uname.HeaderText = "user";
    uname.Name = "username";
    uname.DataPropertyName = "username";
    _chkBoxGrid.Columns.Add(uname);

    _chkBoxGrid.DataSource = dv;
}

void chkHeader_OnCheckBoxClicked(bool state)
{
    foreach (DataGridViewRow row in _chkBoxGrid.Rows)
        row.Cells[0].Value = state;

}
0A0D
+1 Roboto. It does work. The only issue I had with it was that I found the solution overkill when it can be achieved in a much simpler way code-wise. Now, were my grid a few thousand records larger, I would definitely go this route. But topping at 1,000 records I don't feel the need fo this. I still saved it on my code snippets. Thanks.
Krugar
A: 

When editing multiple rows or columns in a DataTable, call BeginLoadData 1st and then call EndLoadData when you're done.

hjb417
-1. I'm sorry hjb417. But this obviously has nothing to do with BeginLoadData or EndLoadData.
Krugar
+1  A: 

I found a way to speed it up to an acceptable performance. Essentially unbind the control, do the update on the DataTable and rebind it.

DataTable dt = (DataTable)dataGridRes.DataSource;
dataGridRes.DataSource = null;

for (int i = 0; i < dt.Rows.Count; i++)
    dt.Rows[i].SetField(0, 1);

dataGridRes.DataSource = dt;
Krugar
I apologize for setting my own answer as the accepted one. But none of the other answers was satisfying on one case, or actually helped speed up the the performance on all other cases.
Krugar