Hello, I recently setup my DataGridView to use the RowValidating event. However, whenever a user types something in to a NewRow (as to cause the DGV to create a new row and consider the current row's NewRow = false) and then hit escape to cancel the edit, the DataError exception goes nuts. It keeps spitting out "Index 4 does not exist" in what seems to be an infinite loop. Here is my RowValidating code: | | private void dgvMain_RowValidating(object sender, DataGridViewCellCancelEventArgs e) { if (dgvMain.Rows[e.RowIndex] != null && !dgvMain.Rows[e.RowIndex].IsNewRow) { string error = ""; if (dgvMain.Rows[e.RowIndex].Cells["CDS"].Value == null || dgvMain.Rows[e.RowIndex].Cells["CDS"].Value.ToString().Trim() == "") error += " * Site\n"; if (dgvMain.Rows[e.RowIndex].Cells["FirstName"].Value == null || dgvMain.Rows[e.RowIndex].Cells["FirstName"].Value.ToString().Trim() == "") error += " * First Name\n"; if (dgvMain.Rows[e.RowIndex].Cells["LastName"].Value == null || dgvMain.Rows[e.RowIndex].Cells["LastName"].Value.ToString().Trim() == "") error += " * Last Name\n"; if (dgvMain.Rows[e.RowIndex].Cells["Email"].Value == null || dgvMain.Rows[e.RowIndex].Cells["Email"].Value.ToString().Trim() == "") error += " * Email\n"; if (error != "") { MessageBox.Show("The following required fields were left blank:\n\n" + error, "Row Validation Error", MessageBoxButtons.OK, MessageBoxIcon.Error); e.Cancel = true; } } } |
Note: I have determined that any access to dgvMain.Rows[e.RowIndex] (EVEN TO CHECK IF ITS NULL) will cause the dataerror to go nuts when hitting the escape key. If I comment out any part that access dgvMain.Rows[e.RowIndex] in the RowValidating event, it works fine (but obviously doesn't validate like it should). Also, if I try to Watch anything in the debugger, it says the DGV has 6 rows (it should have had 6 before, 5 after you hit escape and cancel the edit). If I try to Watch dgvMain.Rows[4] it times out and then my debugging doesnt work anymore after that, but if I try to view any other row it works fine. This kind of seems like a bug in the framework, but I'm really not too sure. By the way, I'm using VS2005/.NET RC. Is there anyway I can get this to work, or to disable the ability to cancel editing with the Escape key? Thanks -Adam |
| Adam Plocher Friday, October 28, 2005 3:52 PM |
This is correct behavior, but kinda weird the way it comes across. The issue is that when you press Esc the row is actually removed already since you are escaping out of the cell edit. Since the edit in the cell was the only thing that was keeping the row from being around the row is in the process of being removed.
This isn't something that can be canceled (the removal of the row), but since focus is being moved to the next row, the RowValidating event is fired.
Anyway, displaying the MessageBox causes problems in this case because it "pauses" the processing of the row removal. In this paused state the row does not exist in the database view, so when we try to paint the row (as a result of the messagebox) we find that the row doesn't exist, so we raise the DataError event
The only thing you need to add to your code is a check to see if the row you are validating is dirty:
| | if (dataGridView1.Rows[e.RowIndex] != null && !dataGridView1.Rows[e.RowIndex].IsNewRow && dataGridView1.IsCurrentRowDirty) |
Hope this helps, -mark DataGridView Program Manager Microsoft |
| Mark Rideout Friday, November 11, 2005 10:07 PM |
I tried this out on the RTM version and didn't have any problems. Can you provide the actual steps that you used? I did this: 1) New WF app 2) Add a DGV and create 4 columns named CDS, FirstName, LastName & Email 3) Handle the RowValidating event and add the above code to it 4) Run 5) Type a value in the first cell (cause the DGV to create a new row and consider the current row's NewRow = false) 6) Hit ESC 7) Row is removed 8) Type a value in the first cell (same as 5) 9) Hit the down arrow or click out of the grid 10) Messsagebox from RowValidating event is shown.
Is this what you are seeing? -mark DataGridView Program manager Microsoft This post is provided "as-is" |
| Mark Rideout Friday, November 04, 2005 11:39 PM |
Does anyone know how I can work around this? This is a pretty big issue. The end-users are losing their data (if they don't commit it) whenever they hit escape because it's causing them to have to kill the program. I need to validate the rows, but it seems that removing the RowValidating event is the only way to fix this.. PLEASE HELP  TIA -Adam |
| Adam Plocher Tuesday, November 01, 2005 7:11 PM |
I tried this out on the RTM version and didn't have any problems. Can you provide the actual steps that you used? I did this: 1) New WF app 2) Add a DGV and create 4 columns named CDS, FirstName, LastName & Email 3) Handle the RowValidating event and add the above code to it 4) Run 5) Type a value in the first cell (cause the DGV to create a new row and consider the current row's NewRow = false) 6) Hit ESC 7) Row is removed 8) Type a value in the first cell (same as 5) 9) Hit the down arrow or click out of the grid 10) Messsagebox from RowValidating event is shown.
Is this what you are seeing? -mark DataGridView Program manager Microsoft This post is provided "as-is" |
| Mark Rideout Friday, November 04, 2005 11:39 PM |
Hi Mark, I'm trying my hardest to reproduce this problem in a test app but I can't. I have narrowed down the problem on my existing app though. On my main app, if I press escape it fires the RowValidating event - and the exact line of code that is causing the problem is the MessageBox.Show() method (if I comment out that one line, it works fine).
Here's the weird (or weirder) part though: on my test app, the RowValidating event never gets fired if you press escape to cancel the edit - which seems right. For some reason, my main app does fire that event. Mark, do you know what could cause a RowValidating event to fire? I have removed ALL other datagrid events except for RowValidating - and it's that one stupid messagebox that's actually the causing the problem within that event.
I will continue to try to find the answer for this and attempt to reproduce it in a test app if I can.
Thanks a lot -Adam |
| Adam Plocher Monday, November 07, 2005 7:28 PM |
Hi Mark, I was able to reproduce the problem in a simple test app.
First, it will have to be a data-driven DataGridView, my example uses the northwind database (although you will need to modify the connection string).
Create a blank Winforms project and drag a DataGridView control to the form.
In the forms Load event add the following code:
| |
DataTable tempData = new DataTable(); SqlConnection con = new SqlConnection("Persist Security Info=True;User ID=****;Password=****;Initial Catalog=NorthWind;Data Source=****"); SqlDataAdapter da = new SqlDataAdapter(new SqlCommand("SELECT * FROM Customers", con)); da.Fill(tempData); dataGridView1.DataSource = tempData;
|
Now, create a RowValidating event handler for the DGV with the following code: | |
private void dataGridView1_RowValidating(object sender, DataGridViewCellCancelEventArgs e) { if (dataGridView1.Rows[e.RowIndex] != null && !dataGridView1.Rows[e.RowIndex].IsNewRow) { string error = ""; if (dataGridView1.Rows[e.RowIndex].Cells["CompanyName"].Value == null || dataGridView1.Rows[e.RowIndex].Cells["CompanyName"].Value.ToString().Trim() == "") error += " * Company Name\n"; if (dataGridView1.Rows[e.RowIndex].Cells["ContactName"].Value == null || dataGridView1.Rows[e.RowIndex].Cells["ContactName"].Value.ToString().Trim() == "") error += " * Contact Name\n"; if (error != "") { MessageBox.Show("The following required fields were left blank:\n\n" + error, "Row Validation Error", MessageBoxButtons.OK, MessageBoxIcon.Error); e.Cancel = true; } } }
|
Note: I also added the System.Data.SqlClient namespace. If you run this app and try to enter data into a new row and press escape, it should generate a bunch of errors (via the DataError event). If you catch those errors, it will give you the index out of range errors. Thanks -Adam |
| Adam Plocher Monday, November 07, 2005 8:38 PM |
This is correct behavior, but kinda weird the way it comes across. The issue is that when you press Esc the row is actually removed already since you are escaping out of the cell edit. Since the edit in the cell was the only thing that was keeping the row from being around the row is in the process of being removed.
This isn't something that can be canceled (the removal of the row), but since focus is being moved to the next row, the RowValidating event is fired.
Anyway, displaying the MessageBox causes problems in this case because it "pauses" the processing of the row removal. In this paused state the row does not exist in the database view, so when we try to paint the row (as a result of the messagebox) we find that the row doesn't exist, so we raise the DataError event
The only thing you need to add to your code is a check to see if the row you are validating is dirty:
| | if (dataGridView1.Rows[e.RowIndex] != null && !dataGridView1.Rows[e.RowIndex].IsNewRow && dataGridView1.IsCurrentRowDirty) |
Hope this helps, -mark DataGridView Program Manager Microsoft |
| Mark Rideout Friday, November 11, 2005 10:07 PM |
Mark.. you rock, thanks so much, that helped a lot. I implemented it on several of my grids and it fixed that problem on all of them. A few of them are now giving me a "Current cell cannot be set to an invisible cell" exception, but I haven't yet taken the time to debug it and I don't think I'll have any problem fixing that.
Thanks again
|
| Adam Plocher Friday, November 11, 2005 10:38 PM |
Mark, I got one more scenario that I would like to run by you to see if you can help. Using the test code that I provided before, try changing the query to do a "SELECT TOP 0 * FROM Customers" as to create a databound DataGridView - only with 0 records.
Now try to edit a cell on the first row and press escape - everything works fine, the extra row goes away...
But if you try to edit a cell on the first row, then switch to another cell (on the same row) and begin editting it, now press escape twice - it creates an extra row. This doesn't seem to have anything to do with the RowValidating event. If I comment it out, it still behaves oddly.
Do you know of a fix for this behavior? At least it's not throwing a bunch of exceptions, but it would still be nice if there's a fix for this (so I don't have a few null rows added to my DB).
Thanks again. -Adam |
| Adam Plocher Monday, November 14, 2005 7:01 PM |
I'm having the same errors!
I'm having some trouble with my DataGridView object.
Here's what I've got:
1) A table with columns int pk PRIMARY KEY AUTOINCREMENT, nvarchar(16) cod NOT NULL, nvarchar(60) desc NOT NULL, int a, int b, int c.
2) A DataGridView bound to that table through a DataSet. The pk column is not show nor hidden.
I'm trying to error handle all faults caused by bad filled rows. I've already read the posts about the RowValidating and CellValidating events and they worked just fine.
Except...
I'm testing the DataGridView to ensure that all the ways of filling a row will be validated and will not raise any unhandled error.
But I've found some disturbing error: everytime I fill the initial column cod (it's marked as NOT NULL) and move to another column without filling nor editing it (it's just selected), if I press the ESCAPE key it raises an error through it's CancelEdit method, telling me that's it's not possible for the cod column to be NULL. Too bad the error closes the form.
I wonder if you could help me to handle such error.
Thanks a lot!
José Carlos Júnior. |
| José Carlos T. Santos Júnior Saturday, December 31, 2005 2:56 AM |
Does anyone know how to fix this? Thanks |
| Adam Plocher Tuesday, January 24, 2006 5:12 PM |
No. This is a known bug in the grid. We have not found a workaround yet.
-mark
DataGridView Program Manager
Microsoft
This post is provided "as-is"
|
| Mark Rideout Tuesday, January 24, 2006 6:36 PM |
Hello Adam and Mark,
I came across this problem and fixed it in my applicationby overriding the OnKeyDown event on the DataGridView. Andmaking sure the BindingSource.CancelEdit() method is called instead of the dataGridView.CancelEdit() which itseems to meends up calling EndEdit() somewhere down the line.
private void dataGridView1_KeyDown(object sender, KeyEventArgs e) {
if (e.KeyCode == Keys.Escape) {
dataGridView1BindingSource.CancelEdit(); e.Handled = true;
}
else { base.OnKeyDown(e); }
} |
| Bård-Erik Venheim Friday, February 03, 2006 1:53 AM |
Good looking out Erik, I will try this on Tuesday (when I'm back at work). Thanks a lot
|
| Adam Plocher Friday, February 03, 2006 3:16 AM |
Handling the KeyDown event or indeed any other event that takes place doesn't work for me.I can see the events being handled when I am on rows 2, 3, 4, etc but when I am on row 1, raise the DataError event of the dgv and then press escape there seems to be no way to stop the whole app bombing! (I am using VB.NET not C#)
I have tried all sorts of ways to intercept the exception but all of the exposed events are just ignored as far as I can see. Does anyone else have a workaround please? Any service packs due out soon? The dgv is unusable for me at the moment. |
| Sean2006 Thursday, February 23, 2006 8:07 AM |
Yes, I had prevented this bug with the same quick-method (handling keydown event of the datagridview ) but it's getting harder for me to stand all these unsolved bugs. I think I will go on with Java as I did before and offer you all to do the same :) |
| Bluehunter Friday, August 04, 2006 7:37 AM |
Hi Mark,
As you mentioned early, it is a known bug. Did you find any workaround now?
|
| cckdave Friday, September 22, 2006 12:34 AM |
After more
than one year from the signaling, released the SP1 Of The Framework 2.0, but
this bug of which does not know a workaround working, you did not solve it.
My best
compliments.
:-(
Mauro |
| Mauro Rogledi Thursday, December 28, 2006 9:45 AM |
Hi, I think I've found a workaround.
I have used the method AutoGenerateColumns of Datagridview set to false; In this way,
the datagridview does not create the columns alone, but we must be we to decide
what columns to show and what no.. Adding some
invisible columns is also possible, it is enough that they are not placed to
the first places of the datagridview. I enclose
example.
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Data.Sql; using System.Data.SqlClient;
namespace WindowsApplication2 { public class Form2 : Form { private System.Windows.Forms.DataGridView dataGridView1; private System.Windows.Forms.DataGridViewTextBoxColumn Column1; private System.Windows.Forms.DataGridViewTextBoxColumn Column2; private System.Windows.Forms.DataGridViewTextBoxColumn Column3;
DataTable dt; DataView dw; public Form2() { dt = new DataTable("MyTable"); dw = new DataView(dt); dt.Columns.Add("Column1"); dt.Columns.Add("Column2"); dt.Columns.Add("Column3");
InitializeComponent(); // // Column1 // this.Column1.DataPropertyName = "ProductId"; this.Column1.HeaderText = "Column1"; this.Column1.Name = "Column1"; this.Column1.Visible = false; // // Column2 // this.Column2.DataPropertyName = "Column2"; this.Column2.HeaderText = "Column2"; this.Column2.Name = "Column2"; // // Column3 // this.Column3.DataPropertyName = "Column3"; this.Column3.HeaderText = "Column3"; this.Column3.Name = "Column3";
// Here I've error this.dataGridView1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { this.Column1, this.Column2, this.Column3});
// If I remove this.Column1 // I haven't error //this.dataGridView1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { //this.Column2, //this.Column3});
// OR // Invert Column1 with Column2 in this way //this.dataGridView1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { //this.Column2, //this.Column1, //this.Column3});
dataGridView1.AutoGenerateColumns = false; dataGridView1.DataSource = dt; this.Validate(); }
private void InitializeComponent() { this.dataGridView1 = new System.Windows.Forms.DataGridView(); this.Column1 = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.Column2 = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.Column3 = new System.Windows.Forms.DataGridViewTextBoxColumn(); ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit(); this.SuspendLayout(); // // dataGridView1 //
this.dataGridView1.ColumnHeadersHeightSizeMode =
System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; this.dataGridView1.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter; this.dataGridView1.Location = new System.Drawing.Point(21, 12); this.dataGridView1.Name = "dataGridView1"; this.dataGridView1.Size = new System.Drawing.Size(296, 150); this.dataGridView1.TabIndex = 0; // // Column1 // this.Column1.Name = "Column1"; // // Column2 // this.Column2.Name = "Column2"; // // Column3 // this.Column3.Name = "Column3"; // // Form2 // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(453, 289); this.Controls.Add(this.dataGridView1); this.Name = "Form2"; this.Text = "Form2"; ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit(); this.ResumeLayout(false);
}
private System.ComponentModel.IContainer components = null;
/// <summary> /// Clean up any resources being used. /// </summary> /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } }
}
|
| Mauro Rogledi Sunday, January 07, 2007 4:12 PM |
I second the observations of Sean2006 in that under some circumstances, there is nothing that can be done to prevent the crash - no events that could be intercepted get fired at all. This is in managed C++, for those who want to know. We hope a fix will be coming soon.
Kamen |
| Kamen Friday, April 20, 2007 9:05 PM |
I managed to get a working work around by inherting the datagridview and overriding this method ... I also added some code for confirmation of row deleting here is the class code ... all you have to do is check the new CurrentEditCancelling if true then exit the rowvalidating sub else continue normally... hope this helps.... Also notice that I've made the enter key work like the tab key ....
Code Snippet
Public Class myDataGridView
Inherits DataGridView
Public Event DeletedRow(ByVal sender As Object, ByVal e As EventArgs)
Private _CancellingCurrentEdit As Boolean = False
Public ReadOnly Property CurrentEditCancelling() As Boolean
Get
Return _CancellingCurrentEdit
End Get
End Property
Protected Overrides Function ProcessCmdKey(ByRef msg As System.Windows.Forms.Message, ByVal keyData As System.Windows.Forms.Keys) As Boolean
Dim bReturn As Boolean = MyBase.ProcessCmdKey(msg, keyData)
If keyData = Keys.Enter Then
SendKeys.Send( "{TAB}")
Return True
ElseIf keyData = Keys.Delete Then
If Me.AllowUserToDeleteRows Then
If Not Me.CurrentRow Is Nothing Then
If (Me.CurrentRow.State And DataGridViewElementStates.Selected) = DataGridViewElementStates.Selected Then
If Not Me.CurrentRow.IsNewRow Then
If MessageBox.Show("Delete This Row?", "Confirm Delete", _
MessageBoxButtons.YesNo) = DialogResult.No Then
Return True
Else
_CancellingCurrentEdit = True
If Not IsNothing(Me.CurrentRow) Then Me.Rows.Remove(Me.CurrentRow)
_CancellingCurrentEdit = False
RaiseEvent DeletedRow(Me, New EventArgs)
End If
End If
Else
Return bReturn
End If
Else
Return bReturn
End If
End If
ElseIf keyData = Keys.Escape Then
If Not IsNothing(Me.DataSource) AndAlso TypeOf Me.DataSource Is BindingSource Then
_CancellingCurrentEdit = True
CType(Me.DataSource, BindingSource).CancelEdit()
_CancellingCurrentEdit = False
Return True
End If
End If
Return bReturn
End Function
End Class
|
| Mohammed Zedan Monday, July 30, 2007 2:22 PM |
How can the modified DataGridView be retrofitted to existing code? Can it be integrated into the designer?
I'm only a part time developer and have used a number of datagridviews bound to datatables and have just fallen over this bug. It only happened to me in the RowValidated event as I didn't try to access the row in validating unless it was dirty - will this modified class fix both events?
Interestingly, I only get the error in debug mode. If I ignore it and build my projects it just seems to work. |
| Jerry Frain1 Wednesday, August 08, 2007 10:01 AM |
Dear Mark, Has this bug been fixed? Please let me know. Thanks, N. |
| Offhands Friday, July 31, 2009 9:16 AM |