65.9K
CodeProject 正在变化。 阅读更多。
Home

易于编写和使用的可为空的 DateTimePicker

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.55/5 (15投票s)

2008年5月15日

CPOL

3分钟阅读

viewsIcon

79755

downloadIcon

2123

使用 Visual Studio 2008,可以轻松创建 Microsoft 的 DateTimePicker 的可空版本。

引言

还是老一套:我需要一个可以显示空白值的日期时间选择器,并且我不想培训我的用户,让他们知道一个灰色的日期,并且没有选中复选框实际上意味着没有日期。 在浏览了 CodeProject 上的许多控件后,我感到有些沮丧。 我尝试使用 VB 和 Visual Studio 2008 制作自己的版本,并且在没有做很多工作的情况下,我得到了一些我喜欢的东西。

Calendar2.JPG

使用代码

基本控件本身非常简单:一个包含 MaskedEditBoxPanelUserControlMaskedEditBox 被屏蔽为短日期格式,用户可以输入日期或清除控件。停靠在 MaskedEditBox 内部右边缘的是一个充当按钮的 Panel 控件。 如果用户单击它,则会弹出一个 VS 2008 MonthCalendar 控件的扩展版本。 如果 MaskedEditBox 有一个日期,则该日期将在弹出窗口中预先选择。 选择一个日期会将该日期复制到 MaskedEditBox 中,或者用户可以单击“清除日期”按钮来清除 MaskedEditBox。 任何一种选择都会关闭弹出窗口。 用户还可以选择通过单击“关闭”按钮来关闭弹出窗口,而不更改日期。

定义弹出窗口的代码作为私有类嵌入在控件中。 编写它非常简单:我创建了一个常规的 Form,添加了一个 MonthCalendar 和两个 Label 控件,根据我的喜好设置了各种属性,然后将 InitializeComponent 代码复制到类的 New 方法中。 这是清理后得到的结果

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

NullableDateTimePicker 有一个私有变量 Cal,它用作控件的弹出窗口形式的实例。 当单击控件的按钮时,如果需要,将实例化 Cal,设置为屏蔽编辑框中的值(如果它是一个有效的日期)或当前日期(如果不是),并显示为模态表单。

Calendar1.JPG

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

从控件中获取日期非常简单。 我实现了 HasDate,它指示控件是否显示有效日期,Text,它返回屏蔽编辑框的 Text 属性,以及 Value,它返回一个 Nullable(Of Date) 值(在 VB 2008 中缩写为 Date?)。

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

改进空间

这个控件非常基本,但它满足了我的需求:允许用户轻松选择日期,手动输入日期,并表示没有日期。 使其数据绑定应该很容易。 更改日历弹出窗口外观的属性也会很方便。

关注点

请注意上面使用了 Information.IsDateInformation 是在 Microsoft.VisualBasic 命名空间中找到的静态类。 我这样编写代码是为了帮助可能想要翻译此控件的 C# 程序员; 我认为 C# 没有任何真正等同于 IsDate 的东西。 要使用此函数(以及一些其他有用的工具),请在您的项目中添加对该命名空间的引用。

如上所述,Visual Basic 2008 为 Nullable(Of T) 添加了简写符号,其中 T 是一个值类型。 这是通过在声明中的变量名或变量类型之后添加一个 ? 来指示的。 因此,我对 Date? 的使用实际上与使用 Nullable(Of Date) 相同; 事实上,我相信,无论哪种方式编译都是一样的。

历史

  • 2008-05-15 - 向弹出控件添加了第二个标签,允许用户在不更改显示值的情况下关闭表单,并更改了文章的文本以反映这一点。 我还稍微扩展了 VB 中新的 Nullable 简写符号。
© . All rights reserved.