DateTimePicker in DGV Problem

SteveInBeloit

Well-known member
Joined
May 22, 2006
Messages
132
Programming Experience
10+
I have added a DateTimePicker to my DataGridView ala this link that I found on this forum while searching:

http://msdn2.microsoft.com/en-us/library/7tas5c80.aspx

I have set the display and input format to: "MM/dd hh:mm tt"

When the form loads, I see the dates from the database in that format. Then when I edit them, I get the up/down arrows, and can edit, but as soon as I move off of this row, my time goes to 12:00 AM. If I edit the mm/dd, it stays as the value I put in, but the time always goes to 12:00 AM.

Any help appriciated.
 
it sounds like the .Value of the date time picker is being set to a date.. I've never used a DTP to pick a time, so I'm not sure how it handles it. Double check your code to ensure that nowhere do you set the value to a date only..


If that makes no sense, it may help you to understand that dates are usually stored as a number giving the total days since a point in time..

If the epoch is 01 Jan 1900 midnight, then a date of 02 Jan 1900 midday is stored as 1.5 - it is literally 1.5 days after the epoch

I think that somewhere the fractional part of your datetime is being lost.. the 0.5 that gives a time of 12 noon, is being truncated

Calling DateTime.Now might return us a value based on 237345.75 - meaning that 237,345 days have passed since the epoch, and it is 1800 hours time-wise. Calling DateTime.Now.Date would return us just 237,345.00 - truncating the number removes the time component

See if there is anywhere in the code where this happens
 
In the code from that link, it has two areas where it is setting that value.

VB.NET:
    Public Property EditingControlFormattedValue() As Object _
        Implements IDataGridViewEditingControl.EditingControlFormattedValue

        Get
            Return Me.Value.[U]ToShortDateString[/U]
        End Get

        Set(ByVal value As Object)
            If TypeOf value Is String Then
                Me.Value = DateTime.Parse(CStr(value))
            End If
        End Set

    End Property

    Public Function GetEditingControlFormattedValue(ByVal context _
        As DataGridViewDataErrorContexts) As Object _
        Implements IDataGridViewEditingControl.GetEditingControlFormattedValue

        Return Me.Value.[U]ToShortDateString[/U]

    End Function

I was hoping that when I typed me.value. the intellisence would show ToDateTimeString or something like that. It did not, it shows short and long date and time. I played with those and it is like it sounds, it would just return the time when I set it to LongTime, or just the date the way it is.
 
Damn, i was really hoping that the msdn article would be a bit more sophisticated. Let me read the article and check whether it's going to be workable in this case (we arent supposed to be messing around with parsing)
 
ToShortDateString seems the wrong thing to do; it's that which strips the time off. Change it to just ToString()

Additionally GetEditingControlFormattedValue calls the property; leave it alone. Dont replicate property code in there.

Your DTP isnt very useful if it doesnt let the user edit the time.. set it to have a custom format in the constructor:
VB.NET:
    public CalendarEditingControl() //constructor
    {
      this.Format = DateTimePickerFormat.Custom;
      this.CustomFormat = "MM/dd HH:mm:ss";
    }
Sorry for the C# - i'm not working on a machine that has VB right now.. its only 2 lines though.. just remove the semicolons

Your cell isnt formatted as you want it; its just a textbox, not a DTP, so we treat it like a string. Here is the constructor of the CalendarCell:
VB.NET:
    public CalendarCell()
      : base()
    {
      // Use our custom format.
      this.Style.Format = "MM/dd HH:mm:ss";
    }
 
That didn't seem to get it. I still just get the date changes, and not the time.

Here is the code, I have made the changes you suggested, (I think).

VB.NET:
Public Class CalendarColumn
    Inherits DataGridViewColumn

    Public Sub New()
        MyBase.New(New CalendarCell())
    End Sub

    Public Overrides Property CellTemplate() As DataGridViewCell
        Get
            Return MyBase.CellTemplate
        End Get
        Set(ByVal value As DataGridViewCell)

            ' Ensure that the cell used for the template is a CalendarCell.
            If (value IsNot Nothing) AndAlso _
                Not value.GetType().IsAssignableFrom(GetType(CalendarCell)) _
                Then
                Throw New InvalidCastException("Must be a CalendarCell")
            End If
            MyBase.CellTemplate = value

        End Set
    End Property

End Class

Public Class CalendarCell
    Inherits DataGridViewTextBoxCell

    Public Sub New()
        ' Use the short date format.
        Me.Style.Format = "MM/dd hh:mm tt"
    End Sub

    Public Overrides Sub InitializeEditingControl(ByVal rowIndex As Integer, _
        ByVal initialFormattedValue As Object, _
        ByVal dataGridViewCellStyle As DataGridViewCellStyle)

        ' Set the value of the editing control to the current cell value.
        MyBase.InitializeEditingControl(rowIndex, initialFormattedValue, _
            dataGridViewCellStyle)

        Dim ctl As CalendarEditingControl = _
            CType(DataGridView.EditingControl, CalendarEditingControl)
        ctl.Value = CType(Me.Value, DateTime)

    End Sub

    Public Overrides ReadOnly Property EditType() As Type
        Get
            ' Return the type of the editing contol that CalendarCell uses.
            Return GetType(CalendarEditingControl)
        End Get
    End Property

    Public Overrides ReadOnly Property ValueType() As Type
        Get
            ' Return the type of the value that CalendarCell contains.
            Return GetType(DateTime)
        End Get
    End Property

    Public Overrides ReadOnly Property DefaultNewRowValue() As Object
        Get
            ' Use the current date and time as the default value.
            Return DateTime.Now
        End Get
    End Property

End Class

Class CalendarEditingControl
    Inherits DateTimePicker
    Implements IDataGridViewEditingControl

    Private dataGridViewControl As DataGridView
    Private valueIsChanged As Boolean = False
    Private rowIndexNum As Integer

    Public Sub New()
        Me.Format = DateTimePickerFormat.Custom
        Me.CustomFormat = "MM/dd hh:mm tt"
        Me.ShowUpDown = True
    End Sub

    Public Property EditingControlFormattedValue() As Object _
        Implements IDataGridViewEditingControl.EditingControlFormattedValue

        Get
            Return Me.Value.ToString
        End Get

        Set(ByVal value As Object)
            If TypeOf value Is String Then
                Me.Value = DateTime.Parse(CStr(value))
            End If
        End Set

    End Property

    Public Function GetEditingControlFormattedValue(ByVal context _
        As DataGridViewDataErrorContexts) As Object _
        Implements IDataGridViewEditingControl.GetEditingControlFormattedValue

        Return Me.Value.ToShortDateString

    End Function

    Public Sub ApplyCellStyleToEditingControl(ByVal dataGridViewCellStyle As _
        DataGridViewCellStyle) _
        Implements IDataGridViewEditingControl.ApplyCellStyleToEditingControl

        Me.Font = dataGridViewCellStyle.Font
        Me.CalendarForeColor = dataGridViewCellStyle.ForeColor
        Me.CalendarMonthBackground = dataGridViewCellStyle.BackColor

    End Sub

    Public Property EditingControlRowIndex() As Integer _
        Implements IDataGridViewEditingControl.EditingControlRowIndex

        Get
            Return rowIndexNum
        End Get
        Set(ByVal value As Integer)
            rowIndexNum = value
        End Set

    End Property

    Public Function EditingControlWantsInputKey(ByVal key As Keys, _
        ByVal dataGridViewWantsInputKey As Boolean) As Boolean _
        Implements IDataGridViewEditingControl.EditingControlWantsInputKey

        ' Let the DateTimePicker handle the keys listed.
        Select Case key And Keys.KeyCode
            Case Keys.Left, Keys.Up, Keys.Down, Keys.Right, _
                Keys.Home, Keys.End, Keys.PageDown, Keys.PageUp

                Return True

            Case Else
                Return False
        End Select

    End Function

    Public Sub PrepareEditingControlForEdit(ByVal selectAll As Boolean) _
        Implements IDataGridViewEditingControl.PrepareEditingControlForEdit

        ' No preparation needs to be done.

    End Sub

    Public ReadOnly Property RepositionEditingControlOnValueChange() _
        As Boolean Implements _
        IDataGridViewEditingControl.RepositionEditingControlOnValueChange

        Get
            Return False
        End Get

    End Property

    Public Property EditingControlDataGridView() As DataGridView _
        Implements IDataGridViewEditingControl.EditingControlDataGridView

        Get
            Return dataGridViewControl
        End Get
        Set(ByVal value As DataGridView)
            dataGridViewControl = value
        End Set

    End Property

    Public Property EditingControlValueChanged() As Boolean _
        Implements IDataGridViewEditingControl.EditingControlValueChanged

        Get
            Return valueIsChanged
        End Get
        Set(ByVal value As Boolean)
            valueIsChanged = value
        End Set

    End Property

    Public ReadOnly Property EditingControlCursor() As Cursor _
        Implements IDataGridViewEditingControl.EditingPanelCursor

        Get
            Return MyBase.Cursor
        End Get

    End Property

    Protected Overrides Sub OnValueChanged(ByVal eventargs As EventArgs)

        ' Notify the DataGridView that the contents of the cell have changed.
        valueIsChanged = True
        Me.EditingControlDataGridView.NotifyCurrentCellDirty(True)
        MyBase.OnValueChanged(eventargs)

    End Sub

End Class

Then from the designer, I went to the DGV, edit columns, and the CalandarColumn shows up under ColumnType.
Thanks for looking.
 
Here is the code, I have made the changes you suggested, (I think).

VB.NET:
    Public Property EditingControlFormattedValue() As Object _
        Implements IDataGridViewEditingControl.EditingControlFormattedValue

        Get
            Return Me.Value.ToString 'you changed this.. good :)
        End Get

        Set(ByVal value As Object)
            If TypeOf value Is String Then
                Me.Value = DateTime.Parse(CStr(value))
            End If
        End Set

    End Property

[B][COLOR="Red"]    Public Function GetEditingControlFormattedValue(ByVal context _
        As DataGridViewDataErrorContexts) As Object _
        Implements IDataGridViewEditingControl.GetEditingControlFormattedValue

        Return [U]Me.Value.ToShortDateString[/U] 'MS wrote this lame bit of code

    End Function[/COLOR][/B]

I did say not to do this.. Though I cant fault you directly.. The C# example in MSDN is different to the VB example in this one tiny regard. In MSDN it seems MS copy and pasted the

Return Me.Value.ToShortDateString

Line. In the C# example, the Get() function returns the value fo the property:
VB.NET:
    // Implements the IDataGridViewEditingControl.EditingControlFormattedValue 
    // property.
    public object EditingControlFormattedValue
    {
        get
        {
            return this.Value.ToString(); //you changed this.. good :)
        }
        set
        {
            if (value is String)
            {
                this.Value = DateTime.Parse((String)value);
            }
        }
    }

[B][COLOR="SeaGreen"]    // Implements the 
    // IDataGridViewEditingControl.GetEditingControlFormattedValue method.
    public object GetEditingControlFormattedValue(
        DataGridViewDataErrorContexts context)
    {
        return [U]EditingControlFormattedValue[/U]; //if MS had done this in VB I think you wouldnt have had an error
    }[/COLOR][/B]
MS = lamers ;)

Needless to say, pasting code in that does the same thing is bad, because it means you have to change it in multiple places. Edit the VB code so that Get...() returns the property value, otherwise youre still suffering a ToShortDateString snipping your time off

If you still dont get any joy.. take their C# code and run it through a converter to VB.. As C# I know it works :)
 
Yes!!!! That did it!!

I cannot thank you enough for helping me through this. It is a great learning experience for me. I really enjoy doing this, but it is certainly quite a bit different from ACCESS developement, or COBOL in the old days.

I have read more in the last three weeks than in the previous six months....

Thanks again,
Steve
 
Hi,
I ran into a little bit of a problem when I tried to add a new record via the DGV. This bit of code failed:

VB.NET:
    Public Overrides Sub InitializeEditingControl(ByVal rowIndex As Integer, _
        ByVal initialFormattedValue As Object, _
        ByVal dataGridViewCellStyle As DataGridViewCellStyle)

        ' Set the value of the editing control to the current cell value.
        MyBase.InitializeEditingControl(rowIndex, initialFormattedValue, _
            dataGridViewCellStyle)

        Dim ctl As CalendarEditingControl = _
            CType(DataGridView.EditingControl, CalendarEditingControl)
            ctl.Value = [U]CType(Me.Value, DateTime)[/U]
    End Sub

me.value was Null. At first I thought since below in the code:

VB.NET:
    Public Overrides ReadOnly Property DefaultNewRowValue() As Object
        Get
            ' Use the current date and time as the default value.
            Return DateTime.Now
        End Get
    End Property

sets ctl.value to now, just skip the offending line if me.value is dbnull. When I did that, and then hit the add new row button on the navigator, I don't get the null exception anymore, but you have to click on the cell three times, and it will show todays date. But then if you tab on to the next cell, the date cell remains empty. You have to actually click on it three times to show the date, then click on the days or months, change it, and then change it back to today, then go on and it will save it.

Do you know what I would do to get it to just keep todays date unless they change it?

Thanks
 
yeah, rather than buggering about with the editor controls that may or may not ever touch the cell, you should set the myDataSet.MyDataTable.MyCOlumnName.DefaultValue = DateTime.Now just before you call AddNew() :)
 
It might actually be better to show the checkbox on the datetime picker and If the value is null, untick the box.. You know.. put some custom handling in.. you are, after all, extending the DTP (oo inheritance) - code the extra functionality in

Or use a Null capable DateTimePicker - they do exist, its just that MS' DTP isnt Null capable
 
Thanks for the references. I am looking through them, trying to become more familiar with them, and everything else. I did add

datatable.columns("myCol").defaultvalue = datetime.now

to the event I get when I double click the '+' button on the nav bar. Seemed to do the trick.

On to the next problem!

Thanks
 
Back
Top