I have a program for editing a database. The program has, amongt others, a DataGridView with rows from the database, and a TreeView that applies a filter to the DataGridView. It also has a separate DataGridView that only displays added, modified and deleted items.
Because there are thousands of rows in the database, and they belong to different categories (maintained by the TreeView), a user can double click a field in one of the DataGridViews to find the row. When that happens, the program searches for a corresponding node in the TreeView, and if it finds it, the node is selected:
// Search for component when user double clicks it
private void dgvFindComponent(object sender, DataGridViewCellEventArgs e)
{
try
{
searching = true; // Row validation would cause problems while doing a search
if (e.ColumnIndex >= 0 && e.RowIndex >= 0) // Ignore headers
{
// Find category
DataGridViewRow findRow = ((DataGridView)sender).CurrentRow;
DataRowView findRowView = ((DataRowView)findRow.DataBoundItem);
int cellIndex = ((DataGridView)sender).CurrentCellAddress.X;
TreeNode[] findNodes = trvCategories.Nodes.Find(findRow.Cells["Part Type"].Value.ToString() + "\\", true);
if (findNodes.Length != 0)
{
trvCategories.SelectedNode = findNodes[0];
}
// Clear filter
btnClearFilter_Click(null, null);
// Find component
foreach (DataGridViewRow row in dgvEditor.Rows)
{
if (row.DataBoundItem != null && ((DataRowView)row.DataBoundItem).Row == findRowView.Row)
{
dgvEditor.Focus();
dgvEditor.CurrentCell = row.Cells[cellIndex];
break;
}
}
}
searching = false;
}
catch (Exception ex) { debugOutput(ex); }
}
This part works fine. As you can see, I use a "searching" variable to stop row validation, which would make the problem to appear less often - but still in some situations.
What happens is that when a node is found and selected, a rowFilter is applied to the DataGridView, and this (I presume) triggers a RowValidating event. In this event, the rowIndex that is sent via the DataGridViewCellCancelEventArgs is larger than the number of rows currently in the DataGridView - altough Rows.Count returns a large enough value. Because of this, an IndexOutOfRangeException is thrown. The RowValidating code is:
private void dgvEditor_RowValidating(object sender, DataGridViewCellCancelEventArgs e)
{
try
{
//if (!searching)
{
DataGridViewRow row = ((DataGridView)sender).Rows[e.RowIndex];
// Auto-fill some fields. Only on new (uncomitted) items.
if (row.DataBoundItem != null && ((DataRowView)row.DataBoundItem).Row.RowState == DataRowState.Detached || ((DataRowView)row.DataBoundItem).Row.RowState == DataRowState.Added)
{
// Set Part Type for new components from TreeView
if (row.Cells["Part Type"].Value.ToString() == String.Empty)
{
row.Cells["Part Type"].Value = getCurrentCategory();
}
// If no Created Date is set, use todays date
if (row.Cells["Created Date"].Value.ToString() == string.Empty)
{
row.Cells["Created Date"].Value = DateTime.Now.ToString("yyyy-MM-dd");
}
// If no revision is set, use a default
if (row.Cells["Revision"].Value.ToString() == string.Empty)
{
row.Cells["Revision"].Value = "p1_r1.0";
}
// If no initials are set, use value from textbox.
if (row.Cells["Created By"].Value.ToString() == string.Empty)
{
row.Cells["Created By"].Value = txtInitials.Text.ToUpper();
}
}
// Error on empty compulsory fields
dgvSetErrors(row);
}
}
catch (Exception ex) { debugOutput(ex); }
}<br/>
I have commented out the if (!searching) to make the problem appear more often.
My problems are:
1: I can't find a way to check if the row is valid. The problem appears when checking row.DataBoundItem.
2: Even though i have a try ... catch around the whole code, I get standard error messages stating that I should catch the exception myself.
edit: Problem 2 was solved by creating a dummyhandler for the DataError event... Doesn't DataGridView follow the standard way of handling exceptions?