Which Events to use?

I have a DataGridView (dgv) on a Windows form. This dgv consists of several rows with 5 columns. The problem I'm having is not knowing which Events to fire.

The scenario is that if the cursor is in the 2nd column and the cell is empty and the User presses the Down/Up Arrow Keys, the program needs to display a message that the cell cannot be empty. I'm not sure what kind of logic to use to correctly handle this situation. Each cell only takes numeric data.

Who is Participating?

Improve company productivity with a Business Account.Sign Up

it_saigeConnect With a Mentor DeveloperCommented:
Don't try to get the Cell from the DataGridView properties.  Rather uses what the event gives you to get the cell.  The event also gives you the FormattedValue of the cell that is validating.  Consider this example -

Form1.vb -
Imports System.ComponentModel

Public Class Form1
	Private IsLoading As Boolean = False

	Private ReadOnly Products As BindingList(Of Product) = New BindingList(Of Product)() From
	  New Product() With {.ID = 1, .ProductName = "Milk", .Price = "2.34"},
	  New Product() With {.ID = 2, .ProductName = "Bread", .Price = "1.19"},
	  New Product() With {.ID = 3, .ProductName = "Eggs", .Price = "2.18"},
	  New Product() With {.ID = 4, .ProductName = "Butter", .Price = "3.14"},
	  New Product() With {.ID = 5, .ProductName = "Chips", .Price = "3.48"},
	  New Product() With {.ID = 6, .ProductName = "Salsa", .Price = "4.22"},
	  New Product() With {.ID = 7, .ProductName = "Bacon", .Price = "5.13"},
	  New Product() With {.ID = 8, .ProductName = "Sausage", .Price = "2.99"},
	  New Product() With {.ID = 9, .ProductName = "Peanut Butter", .Price = "2.56"},
	  New Product() With {.ID = 10, .ProductName = "Jelly", .Price = "2.99"}

	Private Sub OnLoad(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
		If Not IsLoading Then
			IsLoading = True
			DataGridSource.DataSource = Products
			DataGridView1.DataSource = DataGridSource
			IsLoading = False
		End If
	End Sub

	Private Sub OnCellValidating(ByVal sender As Object, ByVal e As DataGridViewCellValidatingEventArgs) Handles DataGridView1.CellValidating
		If Not IsLoading Then
			DataGridView1.Rows(e.RowIndex).ErrorText = ""

			If DataGridView1.Rows(e.RowIndex).IsNewRow Then Return

			If TypeOf DataGridView1.Columns(e.ColumnIndex) Is DataGridViewTextBoxColumn Then
				Dim tb As DataGridViewTextBoxColumn = DirectCast(DataGridView1.Columns(e.ColumnIndex), DataGridViewTextBoxColumn)

				If tb.Name.Equals(colQuantity.Name) Then
					Dim tempInteger As Integer

					If Not Integer.TryParse(e.FormattedValue.ToString(), tempInteger) Then
						e.Cancel = True
						DataGridView1.Rows(e.RowIndex).ErrorText = "The quantity must be an integer value."
					End If
				ElseIf tb.Name.Equals(colPrice.Name) Then
					Dim tempDecimal As Decimal

					If Not Decimal.TryParse(e.FormattedValue.ToString(), tempDecimal) OrElse tempDecimal < 0.0 Then
						e.Cancel = True
						DataGridView1.Rows(e.RowIndex).ErrorText = "The price must be a non-negative decimal value."
					End If
				End If
			End If
		End If
	End Sub

	Private Sub OnCellValidated(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView1.CellValidated
		TextBox1.Text = Products.Sum(Function(item) item.SubTotal).ToString("C2")
	End Sub
End Class

Class Product
	Private fQuantity As Integer = 0
	Private fPrice As Decimal = 0.0
	Public Property ID() As Integer
	Public Property ProductName() As String

	Public Property Quantity() As Integer
			Return fQuantity
		End Get
		Set(ByVal value As Integer)
			If Not value.Equals(fQuantity) Then
				fQuantity = value
			End If
		End Set
	End Property

	Public Property Price() As Decimal
			Return fPrice
		End Get
		Set(ByVal value As Decimal)
			If Not value.Equals(fPrice) Then
				fPrice = value
			End If
		End Set
	End Property

	Public ReadOnly Property SubTotal() As Decimal
			Return Quantity * Price
		End Get
	End Property
End Class

Open in new window

Form1.Designer.vb -
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Partial Class Form1
    Inherits System.Windows.Forms.Form

    'Form overrides dispose to clean up the component list.
    <System.Diagnostics.DebuggerNonUserCode()> _
    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
            If disposing AndAlso components IsNot Nothing Then
            End If
        End Try
    End Sub

    'Required by the Windows Form Designer
    Private components As System.ComponentModel.IContainer

    'NOTE: The following procedure is required by the Windows Form Designer
    'It can be modified using the Windows Form Designer.  
    'Do not modify it using the code editor.
    <System.Diagnostics.DebuggerStepThrough()> _
    Private Sub InitializeComponent()
		Me.components = New System.ComponentModel.Container()
		Me.DataGridView1 = New System.Windows.Forms.DataGridView()
		Me.colID = New System.Windows.Forms.DataGridViewTextBoxColumn()
		Me.colProductName = New System.Windows.Forms.DataGridViewTextBoxColumn()
		Me.colQuantity = New System.Windows.Forms.DataGridViewTextBoxColumn()
		Me.colPrice = New System.Windows.Forms.DataGridViewTextBoxColumn()
		Me.colSubTotal = New System.Windows.Forms.DataGridViewTextBoxColumn()
		Me.ProductSource = New System.Windows.Forms.BindingSource(Me.components)
		Me.Label1 = New System.Windows.Forms.Label()
		Me.TextBox1 = New System.Windows.Forms.TextBox()
		Me.DataGridSource = New System.Windows.Forms.BindingSource(Me.components)
		CType(Me.DataGridView1, System.ComponentModel.ISupportInitialize).BeginInit()
		CType(Me.ProductSource, System.ComponentModel.ISupportInitialize).BeginInit()
		CType(Me.DataGridSource, System.ComponentModel.ISupportInitialize).BeginInit()
		Me.DataGridView1.AllowUserToAddRows = False
		Me.DataGridView1.AllowUserToDeleteRows = False
		Me.DataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize
		Me.DataGridView1.Columns.AddRange(New System.Windows.Forms.DataGridViewColumn() {Me.colID, Me.colProductName, Me.colQuantity, Me.colPrice, Me.colSubTotal})
		Me.DataGridView1.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter
		Me.DataGridView1.Location = New System.Drawing.Point(13, 13)
		Me.DataGridView1.Name = "DataGridView1"
		Me.DataGridView1.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect
		Me.DataGridView1.Size = New System.Drawing.Size(566, 214)
		Me.DataGridView1.TabIndex = 0
		Me.colID.DataPropertyName = "ID"
		Me.colID.HeaderText = "ID"
		Me.colID.Name = "colID"
		Me.colID.Visible = False
		Me.colProductName.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill
		Me.colProductName.DataPropertyName = "ProductName"
		Me.colProductName.HeaderText = "Product Name"
		Me.colProductName.Name = "colProductName"
		Me.colProductName.ReadOnly = True
		Me.colProductName.Resizable = System.Windows.Forms.DataGridViewTriState.[True]
		Me.colProductName.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable
		Me.colQuantity.DataPropertyName = "Quantity"
		Me.colQuantity.HeaderText = "Quantity"
		Me.colQuantity.Name = "colQuantity"
		Me.colPrice.DataPropertyName = "Price"
		Me.colPrice.HeaderText = "Price"
		Me.colPrice.Name = "colPrice"
		Me.colSubTotal.DataPropertyName = "SubTotal"
		Me.colSubTotal.HeaderText = "SubTotal"
		Me.colSubTotal.Name = "colSubTotal"
		Me.colSubTotal.ReadOnly = True
		Me.Label1.AutoSize = True
		Me.Label1.Location = New System.Drawing.Point(434, 236)
		Me.Label1.Name = "Label1"
		Me.Label1.Size = New System.Drawing.Size(34, 13)
		Me.Label1.TabIndex = 1
		Me.Label1.Text = "Total:"
		Me.TextBox1.Location = New System.Drawing.Point(479, 233)
		Me.TextBox1.Name = "TextBox1"
		Me.TextBox1.ReadOnly = True
		Me.TextBox1.Size = New System.Drawing.Size(100, 20)
		Me.TextBox1.TabIndex = 2
		Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
		Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
		Me.ClientSize = New System.Drawing.Size(591, 265)
		Me.Name = "Form1"
		Me.Text = "Form1"
		CType(Me.DataGridView1, System.ComponentModel.ISupportInitialize).EndInit()
		CType(Me.ProductSource, System.ComponentModel.ISupportInitialize).EndInit()
		CType(Me.DataGridSource, System.ComponentModel.ISupportInitialize).EndInit()

	End Sub
	Friend WithEvents DataGridView1 As System.Windows.Forms.DataGridView
	Friend WithEvents Label1 As System.Windows.Forms.Label
	Friend WithEvents TextBox1 As System.Windows.Forms.TextBox
	Friend WithEvents DataGridSource As System.Windows.Forms.BindingSource
	Friend WithEvents ProductSource As System.Windows.Forms.BindingSource
	Friend WithEvents colID As System.Windows.Forms.DataGridViewTextBoxColumn
	Friend WithEvents colProductName As System.Windows.Forms.DataGridViewTextBoxColumn
	Friend WithEvents colQuantity As System.Windows.Forms.DataGridViewTextBoxColumn
	Friend WithEvents colPrice As System.Windows.Forms.DataGridViewTextBoxColumn
	Friend WithEvents colSubTotal As System.Windows.Forms.DataGridViewTextBoxColumn

End Class

Open in new window

Produces the following output -Initial startupAfter changing the quantity of milk.After changing the price of milk.Trying to change the price of milk to a negative number.-saige-
Use the CellValidating event.  Example code from MSDN:
Private Sub dataGridView1_CellValidating(ByVal sender As Object, _
    ByVal e _
    As DataGridViewCellValidatingEventArgs) _
    Handles dataGridView1.CellValidating

    Me.dataGridView1.Rows(e.RowIndex).ErrorText = "" 
    Dim newInteger As Integer 

    ' Don't try to validate the 'new row' until finished  
    ' editing since there 
    ' is not any point in validating its initial value. 
    If dataGridView1.Rows(e.RowIndex).IsNewRow Then Return 
    If Not Integer.TryParse(e.FormattedValue.ToString(), newInteger) _
        OrElse newInteger < 0 Then

        e.Cancel = True 
        Me.dataGridView1.Rows(e.RowIndex).ErrorText = "the value must be a non-negative integer" 

    End If 
End Sub

Open in new window

Fernando SotoRetiredCommented:
You could use the DataGridView.CellContentClick Event to determine which cell in which row was clicked and use the DataGridView.CellLeave Event to make sure the cell is not empty before leaving.
BlakeMcKennaAuthor Commented:

So far I think the CellValidating will work, however, I still need some tweeks. Here is what my CellValidating Event looks like:

    Private Sub dgvTension_CellValidating(sender As Object, e As DataGridViewCellValidatingEventArgs) Handles dgvTension.CellValidating
            Dim cl As DataGridViewCell = dgvTension.CurrentCell

            EH.ErrorMessage = String.Empty

            Select Case e.RowIndex
                Case 0
                    Select Case e.ColumnIndex
                        Case 2  'Output Cell (0 Load)
                            If cl.Value = String.Empty Then
                                gblnEmptyCell = True
                            End If
                    End Select
                Case Else
                    Select Case e.ColumnIndex
                        Case 1  'Output Cell 
                            If cl.Value = String.Empty Then
                                gblnEmptyCell = True
                            End If
                        Case 2

                    End Select
            End Select

            If gblnEmptyCell Then
                e.Cancel = True
                gblnEmptyCell = False
                EH.ErrorMessage = "Cell cannot be empty!" & "~I"
                'dgvTension.CurrentCell = dgvTension(iTensionColIDX, iTensionRowIDX)
            End If

        Catch ex As Exception
            EH.ErrorMessage = "frmCalibration_3/dgvTension_CellValidating() - " & ex.Message & "...Contact Engineering!" & "~E"
        End Try

        EH.ProcessMessages(Me, sbr, EH.ErrorMessage)
    End Sub

Open in new window

But here is my problem, when this event fires, the cl.Value has a string value of "" even though there is a numeric value in the cell so consequently it will execute the last If statement in the Event. I thought when the CellValidating Event fires...if there is data in a specific cell, it will show (I'm just declaring a variable as DataGridViewCell and assigning the CurrentCell to it).
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.