I am working to fill datagridview manually. So there is no datasource or databindings.
I kept AllowUserToAddRows
to True
and AllowUserToDeleteRows
to True
MY WORK AND CODE
I tweaked to work my datagridview as Enter key goes to next cell to the right and not down.
Private WithEvents ec As DataGridViewTextBoxEditingControl
And,
Protected Overrides Function ProcessCmdKey(ByRef msg As Message, ByVal keyData As Keys) As Boolean
If keyData = Keys.Tab AndAlso DgvGrid.EditingControl IsNot Nothing Then
Return True
Else
Return MyBase.ProcessCmdKey(msg, keyData)
End If
Return Nothing
End Function
Also,
Private Sub dgvGrid_KeyDown(sender As Object, e As KeyEventArgs) Handles DgvGrid.KeyDown
Try
Dim iCol = DgvGrid.CurrentCell.ColumnIndex
Dim iRow = DgvGrid.CurrentCell.RowIndex
Select Case e.KeyCode
Case Keys.Return
e.Handled = True
If iCol = DgvGrid.Columns.Count - 1 Then
If DgvGrid.Rows(iRow).Cells("TCODE").Value = "" Then
MessageBox.Show("ENTER DETAILS AGAIN", MessageBoxButtons.OK, MessageBoxIcon.Warning)
DgvGrid.CurrentCell = DgvGrid("TREATMENT", iRow)
Else
DgvGrid.CurrentCell = DgvGrid("TREATMENT", iRow 1)
End If
Else
If iCol = 2 Then '#TREATMENT
If DgvGrid.Rows(iRow).Cells("TCODE").Value = "" Then
'CalculateData
Else
DgvGrid.CurrentCell = DgvGrid(iCol 1, iRow)
End If
ElseIf iRow = DgvGrid.Rows.Count - 1 Then
DgvGrid.Rows.Add() '------------- ISSUE IS HERE
'DgvGrid.BeginEdit(True)
Else
DgvGrid.CurrentCell = DgvGrid(iCol 1, iRow)
End If
End If
End Select
If e.Shift AndAlso e.KeyCode = Keys.Delete Then
If DgvGrid.SelectedCells.Count > 0 Then
DgvGrid.Rows.RemoveAt(DgvGrid.CurrentCell.RowIndex)
End If
End If
Catch ex1 As System.InvalidOperationException
MessageBox.Show("CANNOT DELETE LAST ROW.", "BONNY SOFTWARE", MessageBoxButtons.OK, MessageBoxIcon.Error)
Catch ex As Exception
MessageBox.Show("TAKE A PICTURE" & Environment.NewLine & "ERROR : " & ex.Message, "BONNY SOFTWARE", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub
Last,
Private Sub ec_PreviewKeyDown(ByVal sender As Object, ByVal e As PreviewKeyDownEventArgs) Handles ec.PreviewKeyDown
Try
Dim iCol = DgvGrid.CurrentCell.ColumnIndex
Dim iRow = DgvGrid.CurrentCell.RowIndex
Select Case e.KeyCode
Case Keys.Return
e.Handled = True
If iCol = DgvGrid.Columns.Count - 1 Then
If DgvGrid.Rows(iRow).Cells("TCODE").Value = "" Then
MessageBox.Show("ENTER DETAILS AGAIN", MessageBoxButtons.OK, MessageBoxIcon.Warning)
DgvGrid.CurrentCell = DgvGrid("TREATMENT", iRow)
Else
DgvGrid.CurrentCell = DgvGrid("TREATMENT", iRow 1)
End If
Else
If iCol = 2 Then '#TREATMENT
If DgvGrid.Rows(iRow).Cells("TCODE").Value = "" Then
'CalculateData
Else
DgvGrid.CurrentCell = DgvGrid(iCol 1, iRow)
End If
ElseIf iRow = DgvGrid.Rows.Count - 1 Then
DgvGrid.Rows.Add() '------------- ISSUE IS HERE
'DgvGrid.BeginEdit(True)
Else
DgvGrid.CurrentCell = DgvGrid(iCol 1, iRow)
End If
End If
End Select
If e.Shift AndAlso e.KeyCode = Keys.Delete Then
If DgvGrid.SelectedCells.Count > 0 Then
DgvGrid.Rows.RemoveAt(DgvGrid.CurrentCell.RowIndex)
End If
End If
Catch ex1 As System.InvalidOperationException
MessageBox.Show("CANNOT DELETE LAST ROW.", "BONNY SOFTWARE", MessageBoxButtons.OK, MessageBoxIcon.Error)
Catch ex As Exception
MessageBox.Show("TAKE A PICTURE" & Environment.NewLine & "ERROR : " & ex.Message, "BONNY SOFTWARE", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub
Mind that Datagridview's KeyDown
and ec_PreviewKeyDown
event are the same as I want same effect when cellEdit is active.
ISSUE
When I am at last column of the last row and try to add a row with code DgvGrid.Rows.Add()
then the row is added above or might be something else happens leaving the above row blank as shown in image below
But If I edit cell and type something then a new uncommitted row is created automatically and it works fine when Enter/Return is pressed. Shown in Image Below
But even if the datagridviewCell is in EditMode, a new uncommitted row won't be generated. It generates only when I type something or change the value.
If I can only get the uncommitted row without editing will do my work.
Any other approach/suggestion is accepted. Thanks
CodePudding user response:
In my small tests, one thing I am curious about is “how” the row was added into the grid in the first picture?...
Specifically, the top first grid… it looks odd in one sense that… “where is the new row?” … Taking into account that the grids AllowUserToAdd
rows property is set to true
… then the user clearly did not “type” the data into the grid.
So, we can only assume the data was programmatically added to the grid? And… the ONLY way I could get the grid to look as shown at the top, is if I programmatically added the data to the individual cells in the grids “new” and only row… something like…
DgvGrid.Rows(0).Cells(0).Value = “SomeValue”….
This may well display in the last row as we can see, however, there is a problem with manually adding the data to the grids “NEW” row … And in a nut shell… you don’t want to do that.
You can see from your example, that if you “add” a new row sometime “AFTER” we added the data to the “new” row, then, that row with the data we previously added is STILL in the grids “new” row… and below the row that was just added… as your picture shows.
Without getting technical, the grids “NEW” row is a “special” row and is not really a row you want to add data to programmatically. Example; if you type a single character into a cell in the last “new” row of a grid… then you will notice that as soon as you type a single character into a cell, then the grid “automatically creates” a new row and the row you just typed into is no longer the “new” row. When your code adds the data manually to the “new” row, the grid does not get triggered to create a “new” row. That is what YOUR code needs to do… i.e., add a new row.
Basically, the code that is adding the data to the grid … is doing it wrong. Whatever that code does, it is clear that it should ADD a new row first as opposed to programmatically adding the data to the grids “new” row.
In addition, I have seen this before such that you want to change the behavior of the grid’s selection to move right as opposed to down when the user presses the “Enter” key. And I will not deny, that it is a little trickier than it may appear. The code below moves the selection right and adds a new row if needed as shown below in action.
Below is my approach to this such that if the user presses the “Enter” key, that the selection will move to the cell on the right as opposed to down, IN addition… if the selection is in the LAST cell of the LAST “new” row, regardless if the cell is in edit mode or not, and the user presses the “Enter” key, then, a new row is created. I would not necessarily do this as the code will keep adding “empty” rows if the user repeatedly presses the enter key on the last “new” row. But I am guessing you want this behavior.
To simplify things, I used SendKeys
to “move” the selection as opposed to setting the grids CurrentCell
row and column indexes. In other words, we already know that if the user presses the “Enter” key then the grid’s “normal” selection/CurrentCell will move “DOWN” to the next cell. So, we simply need to send a “Tab” key, then an “UP” arrow key to get the selection where we want on the right.
Obviously, we need to check for boundary issues like the last column and last “new” row. In the case of the last column, the grid’s selection would simply move down and we only need to send the “HOME” key to get the selection to the first cell on that row.
If the selection is on the last “new” row AND last column, then we can ASSUME that there is NO data in any of the cells on that row… if there was data that the user typed into a cell on that row, then this row would NOT be the last row. So, the user would have to of pressed the “Enter” key multiple times WITHOUT typing any text into any of the cells… Or as you are doing… add the data to the “new” row programmatically. We will assume that the code is not doing this as it currently does and instead “adds” the row properly as it should. Otherwise, the new row will be on top as you show.
SO given this and to simplify the code, we will make a method that moves the selection to the cell on the right when the user presses the “Enter” key.
This method will be called by BOTH the GRIDS KeyDown
event when a cell is NOT in “edit mode” and the TextBox
cells PreviewKeyDown
event when a cell IS in “edit mode.” This method will assume ONE thing only… The user just pressed the ENTER key. It is not important if a cell was in edit mode or not.
This MoveGridSelection
method may look something like below with a walkthrough after.
Private Sub MoveGridSelection()
If DgvGrid.CurrentCell IsNot Nothing Then
If (DgvGrid.CurrentCell.ColumnIndex < DgvGrid.Columns.Count - 1) Then
If (Not (DgvGrid.CurrentCell.RowIndex = DgvGrid.NewRowIndex)) Then
SendKeys.Send("{Up}")
End If
SendKeys.Send("{Tab}")
Else ' current cell is LAST column
If (DgvGrid.CurrentCell.RowIndex = DgvGrid.NewRowIndex) Then
' last cell on last 'new' row - do nothing and stay there
' Or set the selection back to the top
' Or add a New row as below ..? Not a good idea IMO
DgvGrid.Rows.Add()
End If
SendKeys.Send("{Home}")
End If
End If
End Sub
Walkthrough: If the grids CurrentCell
column index is less than the number of columns, then… typically we could send a “Tab” and “UP” key to move right… UNLESS the cell is in the last “new” row… in which case we just want to send the “Tab” key to move right and do not send the “UP” key. So, we check if the row is NOT the “new” row, then we send the additional “UP” key.
If the grids CurrentCell
column index IS the last column when the user presses the enter key, then the grid’s selection will move “DOWN” so we only need to send the “HOME” key to move the selection to the first cell in that row… UNLESS the selection is already on the last “new” row. In that case we have to make a decision, we could move the selection to the top left or do nothing OR add a new row. In this case the code adds a new row as I assume this is what you are wanting.
So again, we check if the row is the last “new” row and if it IS, we add a new row to the grid. After the row is added we send the “HOME” key to set the selection as the first cell in the “new” row.
We will be able to use this method in a couple of places using a couple of the grid events. Below is a brief summary of the grid events we will use and why we need to use them.
First is the GRID’s “KeyDown” event. This event is specifically for when a cell is NOT in “edit mode” and the user presses the “Enter” key.
Private Sub DgvGrid_KeyDown(sender As Object, e As KeyEventArgs) Handles DgvGrid.KeyDown
If e.KeyCode = Keys.Enter Then
MoveGridSelection()
Else
If e.Shift AndAlso e.KeyCode = Keys.Delete Then
DeleteCurrentRow()
End If
End If
End Sub
Next, we need to capture when the “Enter” key is pressed and a cell IS in “edit mode.” As I am confident you are already aware... the grids KeyDown
event will NOT fire when the user “edits” a cell and this makes sense in that the grid’s editor is capturing the keys for the cell.
Typically, we would use the grids EditingControlShowing
event to help. When it fires, a cell is going into “edit mode.” We could cast the grids cell to a generic TextBox
and then subscribe to that text boxes KeyDown
event. This is how one would prevent the user from typing alpha characters into a cell that is numeric. Unfortunately, even in that case the KeyDown
event will NOT fire if the “Enter” key is pressed. Fortunately, the TextBoxes
PreviewKeyDown
event WILL fire when the “Enter” key is pressed.
SO, we need the grids EditingControlShowing
event to wire up this event when a cell goes into “edit mode.” To help, we will create a global TextBox
variable called EditedCell
and when a cell goes into “edit mode,” we will cast that cell to the global text box, wire up the text boxes PreviewKeyDown
event and then wait until the user presses the “Enter” key. It may look something like…
Private WithEvents EditedCell As TextBox = Nothing
Private Sub DgvGrid_EditingControlShowing(sender As Object, e As DataGridViewEditingControlShowingEventArgs) Handles DgvGrid.EditingControlShowing
EditedCell = CType(e.Control, TextBox)
RemoveHandler EditedCell.PreviewKeyDown, AddressOf EditedCell_PreviewKeyDown
AddHandler EditedCell.PreviewKeyDown, AddressOf EditedCell_PreviewKeyDown
End Sub
Next, we need to implement this EditedCell_PreviewKeyDown
event. Like the grids KeyDown
event, we just need to check and see if the “Enter” key was pressed and if it was then we call our previous method to move the selection. In addition, since the user pressed the “Enter” key we also know that the cell will be ending its edit mode, so we no longer need the global text box or its event. So, we unwire the event and set the text box to null
. If we do not do this, then the event may fire unexpectedly.
Private Sub EditedCell_PreviewKeyDown(sender As Object, e As PreviewKeyDownEventArgs) Handles EditedCell.PreviewKeyDown
If (e.KeyCode.Equals(Keys.Enter)) Then
MoveGridSelection()
RemoveHandler EditedCell.PreviewKeyDown, AddressOf EditedCell_PreviewKeyDown
EditedCell = Nothing
Else
If e.Shift AndAlso e.KeyCode = Keys.Delete Then
DeleteCurrentRow()
End If
End If
End Sub
This should be all we need to get the selection to move right instead of down.
And some missing code to delete the selected row if the Shift-Del keys are pressed.
Private Sub DeleteCurrentRow()
If DgvGrid.SelectedCells.Count > 0 Then
If Not DgvGrid.CurrentCell.RowIndex = DgvGrid.NewRowIndex Then
DgvGrid.Rows.RemoveAt(DgvGrid.CurrentCell.RowIndex)
End If
End If
End Sub
Lastly, I have tested this, however, I am confident that I may have missed something obvious. So please let me know.