|
-
Jan 6th, 2016, 10:39 AM
#1
Thread Starter
Addicted Member
[RESOLVED] VB.net DataGridView CellContentClick and CellValueChanged
I have a DataGridView with and editable textcolumn and editable checkbox. I attempting to write to the database as soon as the user edits, but the method seems to fire randomly between the two:
Code:
Private Sub DGVSMT_CellContentClick(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridSMT.CellContentClick
'This method attempts to convert the formatted, user-specified value to the underlying cell data type.
DataGridSMT.CommitEdit(DataGridViewDataErrorContexts.Commit)
'If the value is successfully committed, the CellValueChanged event occurs
End Sub
Private Sub DGVSMT_CellValueChanged(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridSMT.CellValueChanged
If e.ColumnIndex = 8 Then
'do stuff if checkbox clicked
End If
If e.ColumnIndex = 7 Then
'do stuff if text field updated
End If
End Sub
Sometimes when I'm editing the column 7 textbox the e.ColumnIndex = 8 checkbox IF statement will fire (but the checkbox itself in column 8 is not updated). I've tried editing the text field and hitting enter, or just clicking off the text cell to let it update. It's intermittent, sometime it works as expected, sometimes it fires the e.ColumnIndex = 8 IF statement.
Any ideas?
-
Jan 6th, 2016, 11:03 AM
#2
Re: VB.net DataGridView CellContentClick and CellValueChanged
Read the remarks section for CellValueChanged
https://msdn.microsoft.com/en-us/lib...v=vs.110).aspx
Specifically
In the case of check box cells, however, you will typically want to handle the change immediately. To commit the change when the cell is clicked, you must handle the DataGridView.CurrentCellDirtyStateChanged event. In the handler, if the current cell is a check box cell, call the DataGridView.CommitEdit method and pass in the Commit value.
-
Jan 6th, 2016, 11:07 AM
#3
Thread Starter
Addicted Member
Re: VB.net DataGridView CellContentClick and CellValueChanged
Yes, I read that already but don't understand it. Do you have an example?
I am handling the DataGridView.CommitEdit already. How/Where do I handle the CurrentCellDirtyStateChanged event? How do I isolate a checkbox column from a text column other than by columnIndex. Why does it randomly choose between the two?
Edit: I have been experimenting with these methods and they seem to work well and good when you only have one editable column. It's when you introduce two different types of columns that it randomly choose between the two. I tried to isolate them by identifying the columnIndex. Is there a different way to isolate the columns or edit events?
Last edited by Fedaykin; Jan 6th, 2016 at 12:29 PM.
-
Jan 11th, 2016, 11:05 PM
#4
Thread Starter
Addicted Member
Re: VB.net DataGridView CellContentClick and CellValueChanged
I continue to struggle with this and every forum out there quotes the same:
https://msdn.microsoft.com/en-us/lib...v=vs.110).aspx
Specifically
In the case of check box cells, however, you will typically want to handle the change immediately. To commit the change when the cell is clicked, you must handle the DataGridView.CurrentCellDirtyStateChanged event. In the handler, if the current cell is a check box cell, call the DataGridView.CommitEdit method and pass in the Commit value.
But this does not resolve the issue of multiple editable columns. The e.ColumnIndex remains 'stuck' on the first column integer edited. I tried setting the e.ColumnIndex to a variable and then setting that variable to -1 at the end of the method, however the CellContentClick Handler will only remember the first column. If the fist column you edit is column 0, then all subsequent columns edited will also fire 0 no matter which column is selected. Only disposing the DataGridView and recalling it will make it forget the last column fired.
This appears to be a bug because the question has been asked many times and I've yet to see a resolution. Everyone just points to the same post quoted above.
-
Jan 12th, 2016, 11:27 AM
#5
Re: VB.net DataGridView CellContentClick and CellValueChanged
 Originally Posted by Fedaykin
I continue to struggle with this and every forum out there quotes the same:
...
But this does not resolve the issue of multiple editable columns.
If the information in the link (and its linked references) that Kleinma provided does not solve the stated problem then I see only two possibilities.- The problem is not as you have stated.
- Your implementation of the proposed solution is incorrect.
Since you have not shown us your implementation, we have no idea what you have tried.
-
Jan 12th, 2016, 12:36 PM
#6
Thread Starter
Addicted Member
Re: VB.net DataGridView CellContentClick and CellValueChanged
The implementation is the original post with code. Am I missing something?
-
Jan 12th, 2016, 01:21 PM
#7
Re: VB.net DataGridView CellContentClick and CellValueChanged
 Originally Posted by Fedaykin
The implementation is the original post with code. Am I missing something?
Are you missing something? I'd say so.
You ask for help. Kleinma gives you a link to investigate and even directs you to the relevant part that describes how to do what you asked with an example but you claim it does not work.
Where have you shown your implementation of that solution? It is not in the first post, because if it was I would not be making this reply nor my prior one.
-
Jan 12th, 2016, 11:57 PM
#8
Thread Starter
Addicted Member
Re: VB.net DataGridView CellContentClick and CellValueChanged
Ahhh.. you'd like me to give you the code example that is listed in the link? I thought I had made it clear that I tried the code in the example in my earlier post. But here is the actual code for posterity:
In this example 'SMTOrder' is the textbox field (column 7 in my datagridview) and 'FLY' is the checkbox field (column 8 in my datagridview).
Code:
Sub DataGridSMT_CurrentCellDirtyStateChanged( _
ByVal sender As Object, ByVal e As EventArgs) _
Handles DataGridView1.CurrentCellDirtyStateChanged
If DataGridSMT.IsCurrentCellDirty Then
DataGridSMT.CommitEdit(DataGridViewDataErrorContexts.Commit)
End If
End Sub
' If a check box cell is clicked, this event handler disables
' or enables the button in the same row as the clicked cell.
Public Sub DataGridSMT_CellValueChanged(ByVal sender As Object, _
ByVal e As DataGridViewCellEventArgs) _
Handles DataGridSMT.CellValueChanged
If DataGridSMT.Columns(e.ColumnIndex).Name = "SMTOrder" Then
'do stuff
MessageBox.Show("You edited SMT.")
DataGridSMT.Invalidate()
End If
If DataGridSMT.Columns(e.ColumnIndex).Name = "FLY" Then
'do stuff
MessageBox.Show("You edited Fly.")
'DataGridSMT.Invalidate()
End If
End Sub
Column 8 fires as the DataGridView populates for every checkbox in the grid which is fine for testing purposes, we can always remove the handler while the grid loads at a later date.
Column 7 fires successfully when the column 7 textfield is edited. No trouble here.
Column 8 does not fire when the checkbox is clicked by the user. <-THIS IS THE ISSUE WITH THIS METHOD
I have tried commenting out the DataGridSMT.Invalidate() and leaving it in place, the result is the same.
You can substitute the CurrentCellDirtyStateChanged with CellContentClick as I described above and you get the same column index firing every time you click a column regardless of what column it is.
I am of course open to using any other method that might get the job done, or splitting this into two different methods if needed to handle the checkbox and textfield separately if they can be separated. I've tried every combination I can think of and all have similar results of not firing or firing incorrectly.
-
Jan 13th, 2016, 12:02 PM
#9
Re: VB.net DataGridView CellContentClick and CellValueChanged
 Originally Posted by Fedaykin
Ahhh.. you'd like me to give you the code example that is listed in the link? I thought I had made it clear that I tried the code in the example in my earlier post. But here is the actual code for posterity:
You may think that I'm being a jerk to not take you at your word on this, but how else am I or anyone else here to know what it is that you have done? We see it it all the time here where a poster is asked to try something and the immediate and totally useless comment of "it doesn't work" is all we receive back.
In your case, you are claiming that a well known and documented method to achieve what you claim you want to do fails to work. That in itself raises suspicions that the implementation is faulty and hence the reason that I requested that you show your code.
 Originally Posted by Fedaykin
...
Column 8 fires as the DataGridView populates for every checkbox in the grid which is fine for testing purposes, we can always remove the handler while the grid loads at a later date.
This should not be happening if the DGV is databound and the underlying data source is updated.
Is this a unbound DGV? It does not matter in the long run, but it is good information to know as you may need to add
Code:
If e.RowIndex = -1 Then Exit Sub
as the first statement in the dgv.CellValueChange event handler to prevent errors on form creation.
 Originally Posted by Fedaykin
...
Column 8 does not fire when the checkbox is clicked by the user. <-THIS IS THE ISSUE WITH THIS METHOD
The only way that I know for this to happen is if this column is set to ReadOnly.
When you state "does not fire" do you mean that the CellValueChanged eventhandler is not entered or that the desired "If-Then" block is not entered?
Give this code a try and observe the the Output windows while interacting with the DGV.
Code:
Private Sub dgv_CellValueChanged(ByVal sender As Object, ByVal e As DataGridViewCellEventArgs) Handles dgv.CellValueChanged
If e.RowIndex = -1 Then Exit Sub
Dim dgvSender As DataGridView = CType(sender, DataGridView)
Console.WriteLine("Cell[{0},{1}] changed. Column Name is: {2}. Values stored in datarow are:", e.RowIndex, e.ColumnIndex, dgvSender.Columns(e.ColumnIndex).Name)
For Each item As DataGridViewCell In dgvSender.Rows(e.RowIndex).Cells
Console.WriteLine(vbTab & vbTab & If(item.Value, "Nothing").ToString)
Next
End Sub
Private Sub dgv_CurrentCellDirtyStateChanged(ByVal sender As Object, ByVal e As EventArgs) Handles dgv.CurrentCellDirtyStateChanged
Dim dgvSender As DataGridView = CType(sender, DataGridView)
If dgvSender.IsCurrentCellDirty AndAlso TypeOf dgvSender.CurrentCell Is DataGridViewCheckBoxCell Then
Console.WriteLine("Committing checkbox in column: {0}, row {1}", dgvSender.CurrentCell.ColumnIndex, dgvSender.CurrentCell.RowIndex)
dgvSender.CommitEdit(DataGridViewDataErrorContexts.Commit)
End If
End Sub
-
Jan 13th, 2016, 03:38 PM
#10
Thread Starter
Addicted Member
Re: VB.net DataGridView CellContentClick and CellValueChanged
Thank you for the detailed reply. I don't think anyone is a jerk, I'm thankful for the fact that you cared enough to get involved. Just a simple misunderstanding.
This should not be happening if the DGV is databound and the underlying data source is updated.
Is this a unbound DGV? It does not matter in the long run, but it is good information to know as you may need to add
The DGV is populated by a pivot table that is created in the SQL database at form load (SELECT INTO...), the database table is then dropped after it populates the DGV. The information is so dynamic that it is 'old' as soon as it's populated. We only need to detect the user edit in the two columns of the DGV and then we have INSERT statements to handle the edited information based on the work order number captured in the DGV.
When the DGV is created it is sorted by date ascending, then it is set to readonly and then two columns are set to readonly= false:
Code:
For Each DGVC As DataGridViewColumn In DGV.Columns
DGVC.ReadOnly = True 'Set the whole datagridview to Readyonly
Next
'Make only certain columns editable
If MenuForm.ManChk.Checked = True Then
DGV.Columns("FLY").ReadOnly = False 'Now make only this column editable
DGV.Columns("SMTOrder").ReadOnly = False 'Now make only this column editable
End If
The only way that I know for this to happen is if this column is set to ReadOnly.
When you state "does not fire" do you mean that the CellValueChanged eventhandler is not entered or that the desired "If-Then" block is not entered?
In this case I mean the desired If-Then block is not entered. The textcolumn 'SMTOrder' is entered as coded. The 'FLY' checkboxcolumn 'FLY' is not entered as coded.
I will try your code now and report back, although I don't see how both the checkbox column AND textbox column will be handled in your example.
-
Jan 13th, 2016, 03:53 PM
#11
Thread Starter
Addicted Member
Re: VB.net DataGridView CellContentClick and CellValueChanged
I found the bug in my code! But not a complete resolution:
Code:
Sub DataGridSMT_CurrentCellDirtyStateChanged( _
ByVal sender As Object, ByVal e As EventArgs) _
Handles DataGridSMT.CurrentCellDirtyStateChanged '<--------THIS LINE WAS POINTING TO DataGridView1 instead of DataGridSMT!
If DataGridSMT.IsCurrentCellDirty Then
DataGridSMT.CommitEdit(DataGridViewDataErrorContexts.Commit)
End If
End Sub
' If a check box cell is clicked, this event handler disables
' or enables the button in the same row as the clicked cell.
Public Sub DataGridSMT_CellValueChanged(ByVal sender As Object, _
ByVal e As DataGridViewCellEventArgs) _
Handles DataGridSMT.CellValueChanged
If DataGridSMT.Columns(e.ColumnIndex).Name = "SMTOrder" Then
'do stuff
MessageBox.Show("You edited SMT.")
DataGridSMT.Invalidate()
End If
If DataGridSMT.Columns(e.ColumnIndex).Name = "FLY" Then
'do stuff
MessageBox.Show("You edited Fly.")
DataGridSMT.Invalidate()
End If
End Sub
I was handling the incorrect DGV in the code I posted earlier. I have fixed this to point to the correct DGV and the textcolumn and checkbox column IF-Then statements execute as expected, however the textcolumn IF-Then statement fires after every character typed into the box. I need it to only fire when the edit is complete, which tells me maybe I need to handle a separate CellEndEdit for this specific column? Is there a better way?
Last edited by Fedaykin; Jan 13th, 2016 at 03:56 PM.
-
Jan 13th, 2016, 05:42 PM
#12
Thread Starter
Addicted Member
Re: VB.net DataGridView CellContentClick and CellValueChanged
Okay, finally got this working. Thank you for everyone's input.
I could only make this work by handling the checkbox column and textbox columns separately.
I originally thought I could separate them with simple If-Then calling out which e.ColumnIndex had been edited, but this is not the case.
Code:
' In your form load event remove the handler then add it back after the form loads
' to avoid firing the checkboxes as the database populates them in your DataGridView.
' Removing the handler is especially useful if you are inserting and populating a checkbox column after grid creation.
RemoveHandler DataGridSMT.CellValueChanged, AddressOf DataGridSMT_CellValueChanged
'Load your DataGridView
AddHandler DataGridSMT.CellValueChanged, AddressOf DataGridSMT_CellValueChanged
'These next two subs handle the checkbox user click:
Private Sub DataGridSMT_CurrentCellDirtyStateChanged(ByVal sender As Object, ByVal e As EventArgs) Handles DataGridSMT.CurrentCellDirtyStateChanged
'<--Note that e is EventArgs, not DataGridViewCellEventArgs!--->
If DataGridSMT.IsCurrentCellDirty Then
DataGridSMT.CommitEdit(DataGridViewDataErrorContexts.Commit) 'This commits the checkbox and automatically calls the CellValueChanged event
End If
End Sub
'Second sub for handling checkbox user click
Private Sub DataGridSMT_CellValueChanged(ByVal sender As Object, ByVal e As DataGridViewCellEventArgs) Handles DataGridSMT.CellValueChanged
If DataGridSMT.Columns(e.ColumnIndex).Name = "CheckBoxColumnName" Then
'do stuff
MessageBox.Show("You edited your checkbox column.")
End If
End Sub
'This single sub handles the textbox column user edit
Private Sub SMT_CallEndEdit(ByVal sender As Object, ByVal e As DataGridViewCellEventArgs) Handles DataGridSMT.CellEndEdit '<--Note e is DataGridViewCellArgs, not EventArgs
If DataGridSMT.Columns(e.ColumnIndex).Name = "TextBoxColumnName" Then
Messagebox.Show("You edited the textbox.")
End If
End Sub
Also, just in case anyone else can benefit from it. If you both a right click context menu as well as the need to edit individual text cells you'll want to use something like this:
Code:
Private Sub DataGridSMT_CellClick(ByVal sender As Object, ByVal e As MouseEventArgs) Handles DataGridSMT.MouseDown
If e.Button = Windows.Forms.MouseButtons.Right Then
Dim hit As DataGridView.HitTestInfo = DataGridSMT.HitTest(e.X, e.Y)
With DataGridSMT
.SelectionMode = DataGridViewSelectionMode.FullRowSelect
.MultiSelect = False 'Prevents Multiple rows from being selected
DataGridSMT.Rows(hit.RowIndex).Selected = True
DataGridSMT.ContextMenuStrip.Show(Me.DataGridSMT, New Point(hit.RowIndex, hit.ColumnIndex))
End With
ElseIf e.Button = Windows.Forms.MouseButtons.Left Then
Dim hit As DataGridView.HitTestInfo = DataGridSMT.HitTest(e.X, e.Y)
With DataGridSMT
.SelectionMode = DataGridViewSelectionMode.CellSelect
.MultiSelect = False 'Prevents Multiple rows from being selected
End With
End If
End Sub
-
Jan 14th, 2016, 04:38 PM
#13
Thread Starter
Addicted Member
Re: VB.net DataGridView CellContentClick and CellValueChanged
EDIT: I had to further refine the code to prevent the CellValueChanged from firing twice. Apparently it fires once when the checkbox is Dirty and again when it is clean after the commit. To fix this I just set the DataGridView.CurrentCell = Nothing after the first fire. Since you've set the current cell to nothing, you also need to check for nothing (If IsNothing(DataGridView1.CurrentCell) Then Exit Sub)
EDIT: I also had to get very specific about the type of column that is edited by the user by testing for TypeOf DataGridView Cell before allowing the DataGridSMT.CommitEdit(DataGridViewDataErrorContexts.Commit) to fire.
EDIT: Finally, you need to test that the current cell is being edited when the CellValueChanged fires
My complete code that works is here:
Code:
'1 of 2 subs to update a DataGridView1 checkbox click back to the database.
Private Sub DataGridView1_CurrentCellDirtyStateChanged(ByVal sender As Object, ByVal e As EventArgs) Handles DataGridView1.CurrentCellDirtyStateChanged
If IsNothing(DataGridView1.CurrentCell) Then Exit Sub
If DataGridView1.IsCurrentCellDirty And TypeOf DataGridView1.CurrentCell Is DataGridViewCheckBoxCell Then
DataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit) 'This automatically fires the CellValueChanged event in the sub below:
End If
End Sub
'2 of 2 subs that updates a DataGridView1 checkbox click
'<--- Note that e is defined as DataGridViewCellArgs, Not EventArgs --->
Private Sub DataGridView1_CellValueChanged(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView1.CellValueChanged
If e.ColumnIndex = 8 And DataGridView1.IsCurrentCellInEditMode = True Then
'do code to UPDATE database
End If
DataGridView1.CurrentCell = Nothing
End Sub
'1 of 1 sub that handles the TextBoxColumn edits
Private Sub Kit_CallEndEdit(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellEndEdit
If IsNothing(DataGridView1.CurrentCell) Then Exit Sub
If If e.ColumnIndex = 7 And TypeOf DataGridView1.CurrentCell Is DataGridViewTextBoxCell Then
'do code to UPDATE database
End If
End Sub
Enjoy!
Last edited by Fedaykin; Jan 14th, 2016 at 11:00 PM.
Tags for this Thread
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|