Home > Back-end >  How to add a uncommitted blank row in Datagridview?
How to add a uncommitted blank row in Datagridview?

Time:06-15

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

ISSUE IMAGE

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

IMAGE WITH REQD SOLUTION

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?...

enter image description here

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.

enter image description here

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.

  • Related