Cannot hide datetimepicker in datagridview control vb.net

Hi all.

I want to add a datetimepicker to a datagridview control in vb.net. I've looked online for some examples and found one that works but with one exception. If I click the the right side of the cell, the calendar icon appears and  I then click the down arrow the calendar pops up and I'm able to select the date. But, if I were to click the middle of the cell it defaults to today's date and if I don't click the calendar icon on the right and keep it selected to today's date then it doesn't hide the datetimepicker (ie. AddHandler dateTimePicker1.CloseUp, AddressOf DateTimePickerClose in my code below), and it won't clear the cell (i.e.w1.Cells.Item("ExpectedShipDate").Value = "" in my code below). It's as if the datetimepicker control is frozen.

I also attached a print screen, the last row is the one with the weird behavior. If you notice, the date is all the way to the left and the calendar icon with the down arrow still appears. If you click the icon and down arrow it pops up the calendar again and you can choose a date but the cell won't have that new selected value but rather the one that was there before. If you click the icon that is in the column to the right it'll clear the value of the cell (i.e.w1.Cells.Item("ExpectedShipDate").Value = "") but you'll still see the 2/13/2018 with the calendar icon in the cell.

Ideally, I just want to clear the cell (values and datetimepicker) when I click the "clear" icon to the right whenever the end user selects today's date by clicking in the middle of the cell instead of by selecting a date via the calendar icon and down arrow. It's as if you don't select the date using the calendar and down arrow column the datetimepicker1 cannot be hidden.

I even tried using InvalidateCell to force it to repaint the cell but that didn't work, you can still see the datetimepicker1 control.

Any help would be appreciated!

Private Sub DataGrid_InventoryAllocation_CellClick(sender As Object, e As DataGridViewCellEventArgs) Handles DataGrid_InventoryAllocation.CellClick
If Not e.RowIndex = -1 Then
            'Apply on column index in which you want to display DatetimePicker.
            If e.ColumnIndex = 15 Then  '15 is the ExpectedShipDate column
                'Initialize the dateTimePicker1.
                dateTimePicker1 = New DateTimePicker()
                'Adding the dateTimePicker1 into DataGridView.
                DataGrid_InventoryAllocation.Controls.Add(dateTimePicker1)
                ' Setting the format i.e. mm/dd/yyyy)
                dateTimePicker1.Format = DateTimePickerFormat.Short
                ' Create retangular area that represents the display area for a cell.
                Dim oRectangle As Rectangle = DataGrid_InventoryAllocation.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, True)
                ' Setting area for dateTimePicker1.
                dateTimePicker1.Size = New Size(oRectangle.Width, oRectangle.Height)
                ' Setting location for dateTimePicker1.
                dateTimePicker1.Location = New Point(oRectangle.X, oRectangle.Y)
                ' An event attached to dateTimePicker1 which is fired when any date is selected.
                AddHandler dateTimePicker1.TextChanged, AddressOf DateTimePickerChange
                ' An event attached to dateTimePicker1 which is fired when DateTimeControl is closed.
                AddHandler dateTimePicker1.CloseUp, AddressOf DateTimePickerClose
            End If
        End If

 If e.ColumnIndex = 16 And e.RowIndex <> -1 Then 'column 16 is the image column with the clear expectedshipdate cell icon
            'AddHandler dateTimePicker1.CloseUp, AddressOf DateTimePickerClose
            GridRow1.Cells.Item("ExpectedShipDate").Value = ""
              End If

End Sub

 Private Sub DateTimePickerChange(ByVal sender As Object, ByVal e As EventArgs)
        DataGrid_InventoryAllocation.CurrentCell.Value = dateTimePicker1.Text.ToString()
    End Sub

    Private Sub DateTimePickerClose(ByVal sender As Object, ByVal e As EventArgs)
        dateTimePicker1.Visible = False
    End Sub

Open in new window

printscreen.png
printmediaAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

printmediaAuthor Commented:
Hi All. After doing some more research I found a way to make this work, I had to tweak my code a bit. Here it is should anyone ever run into this issue in the future:

Private Sub DataGrid_InventoryAllocation_CellClick(sender As Object, e As DataGridViewCellEventArgs) Handles DataGrid_InventoryAllocation.CellClick

If Not e.RowIndex = -1 Then
            Select Case e.ColumnIndex
                Case 15
                    '//Adding DateTimePicker control into DataGridView   
                    DataGrid_InventoryAllocation.Controls.Add(dateTimePicker1)
                    '// Setting the format (i.e. 02/07/2017 - mm/dd/yyyy)
                    dateTimePicker1.Format = DateTimePickerFormat.Short
                    '// It returns the retangular area that represents the Display area for a cell  
                    Dim oRectangle = DataGrid_InventoryAllocation.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, True)
                    '//Setting area for DateTimePicker Control  
                    dateTimePicker1.Size = New Size(oRectangle.Width, oRectangle.Height)
                    '// Setting Location
                    dateTimePicker1.Location = New Point(oRectangle.X, oRectangle.Y)
                          '// Now make it visible  
                    dateTimePicker1.Visible = True
                  
                    AddHandler dateTimePicker1.ValueChanged, AddressOf dateTimePicker1_ValueChanged
                Case 16
                    GridRow1.Cells.Item("ExpectedShipDate").Value = ""
            End Select
        End If
        ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    End Sub

Private Sub dateTimePicker1_ValueChanged(sender As Object, e As System.EventArgs)
              DataGrid_InventoryAllocation.CurrentCell.Value = dateTimePicker1.Text.ToString()
    End Sub

Private Sub DataGrid_InventoryAllocation_CellLeave(sender As Object, e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGrid_InventoryAllocation.CellLeave
        dateTimePicker1.Visible = False
    End Sub

Open in new window

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
it_saigeDeveloperCommented:
This is a known bug.  You have to use a SendKeys hack; e.g. -

Form1.vb -
Imports System.Reflection
Imports System.Runtime.CompilerServices

'' Converted from a previous EE_PAQ - https://www.experts-exchange.com/questions/28974717/Validating-Date-and-Time-inside-Datagridview-in-C.html
Public Class Form1
	Private IsSendingKeys As Boolean = False

	Protected Overrides Function ProcessCmdKey(ByRef msg As Message, keyData As Keys) As Boolean
		Select Case keyData & Keys.KeyCode
			Case Keys.Enter, Keys.Tab
				DataGridView1.Focus()
		End Select
		Return MyBase.ProcessCmdKey(msg, keyData)
	End Function

	Private Sub OnLoad(sender As Object, e As EventArgs) Handles MyBase.Load
		DataGridView1.DataSource = (From i In Enumerable.Range(0, 10) Select New With {.ID = i, .FirstName = $"FirstName{i}", .StartDate = DateTime.MinValue, .StartTime = DateTime.MinValue, .EndDate = DateTime.MinValue, .EndTime = DateTime.MinValue}).ToList()
	End Sub

	Private Sub OnCellClick(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellClick
		If TypeOf sender Is DataGridView Then
			Dim grid = CType(sender, DataGridView)
			If grid.Rows.Count > 0 Then
				Dim dtp As DateTimePicker = Nothing
				Dim columns = {"startdate", "starttime", "enddate", "endtime"}
				Dim column = grid.Columns(e.ColumnIndex).Name
				If (columns.Any(Function(x) x.Equals(column, StringComparison.OrdinalIgnoreCase))) Then
					dtp = New DateTimePicker()
					grid.Controls.Add(dtp)
					dtp.Format = If(column.IndexOf("time", StringComparison.OrdinalIgnoreCase) > -1, DateTimePickerFormat.Time, DateTimePickerFormat.Short)
					If dtp.Format.Equals(DateTimePickerFormat.Time) Then
						dtp.ShowUpDown = True
					End If
					Dim rectangle = grid.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, True)
					dtp.Size = New Size(rectangle.Width, rectangle.Height)
					dtp.Location = New Point(rectangle.X, rectangle.Y)
					AddHandler dtp.Leave, AddressOf OnLeave
					AddHandler dtp.Validating, AddressOf OnValidating
					dtp.Visible = True
				End If
			End If
		End If
	End Sub

	Private Sub OnLeave(sender As Object, e As EventArgs)
		If TypeOf sender Is DateTimePicker Then
			Dim dtp = CType(sender, DateTimePicker)
			If dtp IsNot Nothing AndAlso Not String.IsNullOrWhiteSpace(dtp.Text) Then
				Try
					IsSendingKeys = True
					SendKeys.SendWait("{RIGHT}")
					SendKeys.SendWait("{LEFT}")
				Finally
					IsSendingKeys = False
				End Try
				dtp.Visible = False
			End If
		End If
	End Sub

	Private Sub OnValidating(sender As Object, e As EventArgs)
		If TypeOf sender Is DateTimePicker Then
			Dim dtp = CType(sender, DateTimePicker)
			Dim grid = CType(dtp.Parent, DataGridView)
			If grid.Rows.Count > 0 Then
				Dim row = grid.Rows(grid.CurrentCell.RowIndex)
				If row.DataBoundItem IsNot Nothing Then
					Dim data = row.DataBoundItem.Cast(New With {.ID = -1, .FirstName = "", .StartDate = DateTime.MinValue, .StartTime = DateTime.MinValue, .EndDate = DateTime.MinValue, .EndTime = DateTime.MinValue})
					Dim flags = BindingFlags.NonPublic Or BindingFlags.Instance
					Dim formats = {"<{0}>i_Field", "<{0}>"}
					For Each [property] In data.GetType().GetProperties()
						If [property].Name.Equals(grid.Columns(grid.CurrentCell.ColumnIndex).Name, StringComparison.OrdinalIgnoreCase) Then
							Dim names = formats.Select(Function(x) String.Format(x, [property].Name)).ToList()
							Dim field = data.GetType().GetFields(flags).First(Function(f) names.Contains(f.Name))
							If field IsNot Nothing Then
								field.SetValue(data, dtp.Value)
							End If
						End If
					Next
				End If
			End If
			dtp.Visible = False
		End If
	End Sub
End Class

Module Extensions
	<Extension()>
	Public Function Cast(Of T)(source As Object, [type] As T) As T
		Return CType(source, T)
	End Function
End Module

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)
		Try
			If disposing AndAlso components IsNot Nothing Then
				components.Dispose()
			End If
		Finally
			MyBase.Dispose(disposing)
		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.DataGridView1 = New System.Windows.Forms.DataGridView()
		CType(Me.DataGridView1, System.ComponentModel.ISupportInitialize).BeginInit()
		Me.SuspendLayout()
		'
		'DataGridView1
		'
		Me.DataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize
		Me.DataGridView1.Dock = System.Windows.Forms.DockStyle.Fill
		Me.DataGridView1.Location = New System.Drawing.Point(0, 0)
		Me.DataGridView1.Name = "DataGridView1"
		Me.DataGridView1.Size = New System.Drawing.Size(531, 290)
		Me.DataGridView1.TabIndex = 0
		'
		'Form1
		'
		Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
		Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
		Me.ClientSize = New System.Drawing.Size(531, 290)
		Me.Controls.Add(Me.DataGridView1)
		Me.Name = "Form1"
		Me.Text = "Form1"
		CType(Me.DataGridView1, System.ComponentModel.ISupportInitialize).EndInit()
		Me.ResumeLayout(False)

	End Sub

	Friend WithEvents DataGridView1 As DataGridView
End Class

Open in new window


-saige-
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Visual Basic.NET

From novice to tech pro — start learning today.