Need a faster DataGridView bulk edit method
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).R开发者_JS百科ows[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?
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;
}
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;
Second try.
- You are casting way too many times
- 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);
}
When editing multiple rows or columns in a DataTable, call BeginLoadData 1st and then call EndLoadData when you're done.
精彩评论