Click here to Skip to main content
15,884,176 members
Articles / Programming Languages / Visual Basic
Article

A nullable DateTimePicker that is easy to write and easy to use

Rate me:
Please Sign up or sign in to vote.
4.55/5 (16 votes)
15 May 2008CPOL3 min read 77.8K   2.1K   43   13
With Visual Studio 2008, it is easy to create a nullable version of Microsoft's DateTimePicker.

Introduction

The same old story: I needed a date-time picker that could display a blank value, and I did not want to have to train my users that a grayed-out date with no check in the box really means there is no date. After looking through the many controls here at CodeProject, I got a bit dismayed. I made my own stab at it using VB and Visual Studio 2008, and I got something I liked without having to do a lot of work.

Calendar2.JPG

Using the code

The base control itself is pretty straightforward: a UserControl containing a MaskedEditBox and a Panel. The MaskedEditBox is masked for a short date, and the user can type in a date or clear the control. Docked on the inside right edge of the MaskedEditBox is a Panel control that acts as a button. If the user clicks on it, an extended version of VS 2008's MonthCalendar control pops up. If the MaskedEditBox has a date, that date will be pre-selected on the pop-up. Selecting a date will copy that date into the MaskedEditBox, or the user can click on the "Clear date" button which clears the MaskedEditBox. Either choice will close the pop-up. The user also has the option of closing the pop-up without changing the date, by clicking on the "Close" button.

The code defining the pop-up is embedded in the control as a private class. Writing it was very straightforward: I created a regular Form, added a MonthCalendar and two Label controls, set various properties to my liking, then copied the InitializeComponent code into the class' New method. This was the result, after cleaning it up a bit:

VB
Protected Class Calendar
          Inherits System.Windows.Forms.Form

    Private MyPicker As NullableDateTimePicker
    Private WithEvents Label1 As Label
    Private WithEvents Label2 As Label
    Private WithEvents MonthCalendar1 As MonthCalendar

    Public Sub New(ByRef Picker As NullableDateTimePicker)
        MyPicker = Picker

        Me.MonthCalendar1 = New MonthCalendar
        Me.Label1 = New Label
        Me.Label2 = New Label

        Me.SuspendLayout()
        '
        'MonthCalendar1
        '
        Me.MonthCalendar1.Location = New Point(0, 0)
        Me.MonthCalendar1.Margin = New Padding(0)
        Me.MonthCalendar1.MaxSelectionCount = 1
        Me.MonthCalendar1.Name = "MonthCalendar1"
        Me.MonthCalendar1.ShowTodayCircle = False
        Me.MonthCalendar1.TabIndex = 0
        '
        'Label1
        '
        Me.Label1.Font = New Font("Microsoft Sans Serif", 8.25!, _
            FontStyle.Underline, GraphicsUnit.Point, CType(0, Byte))
        Me.Label1.ForeColor = SystemColors.HotTrack
        Me.Label1.Location = New Point(2, 163)
        Me.Label1.Name = "Label1"
        Me.Label1.Size = New Size(55, 13)
        Me.Label1.TabIndex = 1
        Me.Label1.Text = "Clear date"
        '
        'Label2
        '
        Me.label2.AutoSize = True
        Me.label2.Font = New Font("Microsoft Sans Serif", 8.25!, _
            FontStyle.Underline, GraphicsUnit.Point, CType(0, Byte))
        Me.label2.ForeColor = System.Drawing.SystemColors.HotTrack
        Me.label2.Location = New System.Drawing.Point(192, 163)
        Me.label2.Name = "Label2"
        Me.label2.Size = New System.Drawing.Size(33, 13)
        Me.label2.TabIndex = 2
        Me.label2.Text = "Close"
        '
        'CalendarPopup
        '
        Me.AutoScaleDimensions = New SizeF(6.0!, 13.0!)
        Me.AutoScaleMode = AutoScaleMode.Font
        Me.ClientSize = New Size(228, 184)
        Me.ControlBox = False
        Me.Controls.Add(Me.Label1)
        Me.Controls.Add(Me.Label2)
        Me.Controls.Add(Me.MonthCalendar1)
        Me.FormBorderStyle = FormBorderStyle.FixedToolWindow
        Me.MaximizeBox = False
        Me.MinimizeBox = False
        Me.Name = "CalendarPopup"
        Me.ShowIcon = False
        Me.ShowInTaskbar = False
        Me.StartPosition = FormStartPosition.Manual
        Me.ResumeLayout(False)
    End Sub

    Public Property SelectedDate() As Date?
        Get
            If Information.IsDate(MonthCalendar1.SelectionStart) Then
                Return MonthCalendar1.SelectionStart
            Else
                 Return Nothing
            End If
        End Get
        Set(ByVal value As Date?)
            If Information.IsDate(value) Then
                MonthCalendar1.SetDate(Convert.ToDateTime(value))
            Else
                MonthCalendar1.SetDate(DateTime.Now)
            End If
        End Set
    End Property

    Private Sub Label1_Click(ByVal sender As Object, ByVal e As EventArgs) _
    Handles Label1.Click
        'Clear
        MyPicker.Value = Nothing
        Me.Close()
    End Sub

    Private Sub Label2_Click(ByVal sender As Object, ByVal e As EventArgs) _
    Handles Label2.Click
        'Close
        Me.Close()
    End Sub

    Private Sub MonthCalendar1_DateSelected(ByVal sender As Object, _
                ByVal e As DateRangeEventArgs) _
                Handles MonthCalendar1.DateSelected
        MyPicker.Value = e.Start
        Me.Close()
    End Sub
End Class

The NullableDateTimePicker has a private variable, Cal, which serves as the control's instance of the pop-up form. When the control's button is clicked, Cal is instantiated, if necessary, set to either the value in the masked edit box (if it is a valid date) or the current date (if it is not), and displayed as a modal form.

Calendar1.JPG

VB
Private Sub Button1_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs) _
            Handles Button1.MouseDown
    If Cal Is Nothing Then Cal = New Calendar(Me)
    If Cal.Visible Then Exit Sub

    If Information.IsDate(MaskedTextBox1.Text) Then
        Cal.SelectedDate = Convert.ToDateTime(MaskedTextBox1.Text)
    Else
        Cal.SelectedDate = Nothing
    End If

    Cal.Location = Button1.PointToScreen(New Point(0, 19)) 
    Cal.ShowDialog()
End Sub

Getting the date out of the control is pretty straightforward. I have implemented HasDate, which indicates whether or not the control is displaying a valid date, Text, which returns the Text property of the masked edit box, and Value which returns a Nullable(Of Date) value (abbreviated in VB 2008 as Date?).

VB
Public ReadOnly Property HasDate() As Boolean
    Get
        Return Information.IsDate(MaskedTextBox1)
    End Get
End Property

Public Overrides Property Text() As String
    Get
        Return MaskedTextBox1.Text
    End Get
    Set(ByVal value As String)
        MaskedTextBox1.Text = value
    End Set
End Property

Public Property Value() As Date?
    Get
        If Me.HasDate Then
            Return Convert.ToDateTime(MaskedTextBox1.Text)
        Else
            Return Nothing
        End If
    End Get
    Set(ByVal value As Date?)
        If Information.IsDate(value) Then
            MaskedTextBox1.Text = _
              Convert.ToDateTime(value).ToString("MM/dd/yyyy")
        Else
            MaskedTextBox1.Text = ""
        End If
    End Set
End Property

Room for improvement

This control is pretty basic, but it does what I need: allows the user to easily select a date, to manually enter a date, and to represent the absence of a date. Making it data-bound should be pretty easy. Properties to change the appearance of the calendar pop-up would also be handy.

Points of interest

Note the use of Information.IsDate above. Information is a static class found in the Microsoft.VisualBasic namespace. I wrote the code this way to help C# programmers who might want to translate this control; I don't think C# has anything that is really equivalent to IsDate. To use this function (and some other useful tools), add a reference to the namespace in your project.

As noted above, Visual Basic 2008 has added a shorthand notation for Nullable(Of T) where T is a value type. This is indicated by adding a ? after either the variable name or the variable type in the declaration. Thus, my use of Date? is effectively the same as using Nullable(Of Date); in fact, I believe, either way compiles the same.

History

  • 2008-05-15 - Added a second label to the pop-up control which allows the user to close the form without changing the displayed value, and changed the text of the article to reflect this. I also expanded a bit on the new Nullable shorthand notation in VB.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
Gregory Gadow recently graduated from Central Washington University with a B.S. that combined economics and statistical analysis, and currently works for the Washington Department of Fish & Wildlife as an IT developer. He has been writing code for 30 years in more than a dozen programming languages, including Visual Basic, VB.Net, C++, C#, ASP, HTML, XML, SQL, and R.

Comments and Discussions

 
QuestionIts excellent. Helped me a lot Pin
mailRichard10-Feb-15 6:45
mailRichard10-Feb-15 6:45 
GeneralMy vote of 5 Pin
Daniel Slapman22-Apr-14 23:22
Daniel Slapman22-Apr-14 23:22 
QuestionProblem when opened in vista Pin
athiradiya2-Nov-11 2:06
athiradiya2-Nov-11 2:06 
QuestionTo add in c# window applicatiom Pin
athiradiya23-Jun-10 0:46
athiradiya23-Jun-10 0:46 
AnswerRe: To add in c# window applicatiom Pin
Gregory Gadow24-Jun-10 3:39
Gregory Gadow24-Jun-10 3:39 
If you put this code in a separate assembly, you can use the assembly from any managed code application, VB or C#.
GeneralHI Pin
rdssiva21-Jun-10 3:43
rdssiva21-Jun-10 3:43 
GeneralRe: HI Pin
Gregory Gadow24-Jun-10 3:40
Gregory Gadow24-Jun-10 3:40 
QuestionBind data? Pin
Alla255225-Aug-09 5:33
Alla255225-Aug-09 5:33 
AnswerRe: Bind data? Pin
Gregory Gadow25-Aug-09 5:43
Gregory Gadow25-Aug-09 5:43 
GeneralRe: Bind data? Pin
Alla255225-Aug-09 5:45
Alla255225-Aug-09 5:45 
GeneralNullable DTP works great Pin
Lorri Feidt24-Jul-09 6:38
Lorri Feidt24-Jul-09 6:38 
GeneralMy vote of 2 Pin
Claudio Grazioli14-Jul-09 23:07
Claudio Grazioli14-Jul-09 23:07 
GeneralValue returned is Nothing, always. Pin
Cesare Esposito2-May-09 6:34
Cesare Esposito2-May-09 6:34 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.