Imports System.ComponentModel

'-----------------------------------------------------------
'Class name : DateTimePickerEx
'Description: Extends the datetimepicker class to allow binding a DBNull database value
' to it.
'
' This class uses the display format to simulate an "empty" selection since the
' base datetimepicker class doesn't support displaying an empty text string.
'
' Since DBNull is not compatible with Date datatype, I created a new method called
' NullableValue to support it.
'History :
'-----------------------------------------------------------

Public Class DateTimePickerEx
    Inherits System.Windows.Forms.DateTimePicker

    'Public Property _value As String = Nothing

    Public Property _value As String
        Get
            If Not Me.Checked Then
                Return ""
            Else
                Return (Me.Value)
            End If
        End Get
        Set(ByVal newValue As String)
            If newValue Is DBNull.Value Or newValue = "" Then
                ' MsgBox("null")
                Me.NullableValue = DBNull.Value
                Me.Checked = False
            Else
                'MsgBox(newValue)
                Me.NullableValue = newValue

            End If

        End Set
    End Property


    Private m_enmOriginalFormat As Windows.Forms.DateTimePickerFormat
    Private m_strOriginalCustomFormat As String
    Private m_blnRefreshing As Boolean = False

#Region " Windows Form Designer generated code "

    Public Sub New()
        MyBase.New()

        'This call is required by the Windows Form Designer.
        InitializeComponent()

        'Add any initialization after the InitializeComponent() call
        InitializeMembers()
    End Sub

    'UserControl overrides dispose to clean up the component list.
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing Then
            If Not (components Is Nothing) Then
                components.Dispose()
            End If
        End If
        MyBase.Dispose(disposing)
    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()
        components = New System.ComponentModel.Container
    End Sub

#End Region

#Region " Event Declarations "

    Public Event NullableValueChanged As EventHandler

    '-----------------------------------------------------------
    'Description: Raise the NullableValueChanged event.
    ' Required for "Bindable" properties.
    'Return :
    'Parameters :
    'History :
    '-----------------------------------------------------------
    Protected Overridable Sub OnNullableValueChanged(ByVal e As EventArgs)
        RaiseEvent NullableValueChanged(Me, e)
    End Sub

#End Region

#Region " Public Methods and Properties "

    '-----------------------------------------------------------
    'Description: Gets or Sets the date time picker value.
    ' Accepts DBNull values.
    'Return :
    'Parameters :
    'History :
    '-----------------------------------------------------------
    <Bindable(True), _
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
    Public Property NullableValue() As Object
        Get
            If Not Me.Checked Then
                Return (DBNull.Value)
            Else
                Return (Me.Value)
            End If
        End Get
        Set(ByVal newValue As Object)

            Dim blnRaiseEvent As Boolean = False

            If newValue Is DBNull.Value Then
                'If the value changed
                If Me.Checked Then

                    Me.Checked = False
                    Me.RefreshText()
                    blnRaiseEvent = True
                End If

            ElseIf IsDate(newValue) Then

                'If only the "Checked" state changes (newValue parameter is equal to the Value property)
                'We must raise the NullableValueChanged event manually since the parents ValueChanged
                'event won't be fired.
                blnRaiseEvent = (Not Me.Checked) And (CType(newValue, Date).Equals(Me.Value))

                Me.Checked = True
                Me.Value = CType(newValue, Date)
                Me.RefreshText()

            Else
                Throw New ArgumentException
            End If

            If blnRaiseEvent Then
                Me.OnNullableValueChanged(New EventArgs)
            End If

        End Set
    End Property

#End Region

#Region " Base Class Overrides "

    '-----------------------------------------------------------
    'Description: When the user changes the value, we need to refresh
    ' the display text formats and to throw our NullableValueChanged event.
    'Return :
    'Parameters :
    'History :
    '-----------------------------------------------------------
    Protected Overrides Sub OnValueChanged(ByVal e As System.EventArgs)

        Me.RefreshText()

        'When the value changes, the nullable value also changes.
        Me.OnNullableValueChanged(e)

        MyBase.OnValueChanged(e)
    End Sub

    '-----------------------------------------------------------
    'Description: If the format is changed we need to get the
    ' new format and store it as the new original format.
    'Return :
    'Parameters :
    'History :
    '-----------------------------------------------------------
    Protected Overrides Sub OnFormatChanged(ByVal e As System.EventArgs)

        'If the format changes because of our RefreshText method, we don't want to save
        'the "temporary" format used to hide text as our original format...

        If Not m_blnRefreshing Then
            Me.SaveOriginalFormats()
        End If

        MyBase.OnFormatChanged(e)
    End Sub

    '-----------------------------------------------------------
    'Description: When the control is ask to refresh itself, we also
    ' refresh the display format.
    'Return :
    'Parameters :
    'History :
    '-----------------------------------------------------------
    Public Overrides Sub Refresh()
        Me.RefreshText()

        MyBase.Refresh()
    End Sub

#End Region

    '-----------------------------------------------------------
    'Description: Initialize the member variables.
    'Return :
    'Parameters :
    'History :
    '-----------------------------------------------------------
    Private Sub InitializeMembers()
        Me.SaveOriginalFormats()

        'The default for the DateTimePickerEx is to display the checkbox.
        MyBase.ShowCheckBox = False
    End Sub

    '-----------------------------------------------------------
    'Description: Save the current display formats.
    'Return :
    'Parameters :
    'History :
    '-----------------------------------------------------------
    Private Sub SaveOriginalFormats()
        m_enmOriginalFormat = Me.Format
        m_strOriginalCustomFormat = Me.CustomFormat
    End Sub

    '-----------------------------------------------------------
    'Description: Restore the original display formats
    'Return :
    'Parameters :
    'History :
    '-----------------------------------------------------------
    Private Sub RestoreOriginalFormats()
        Me.CustomFormat = m_strOriginalCustomFormat
        Me.Format = m_enmOriginalFormat
    End Sub

    '-----------------------------------------------------------
    'Description: Refresh the display format based on the current state
    ' (Checked or not)
    'Return :
    'Parameters :
    'History :
    '-----------------------------------------------------------
    Private Sub RefreshText()

        m_blnRefreshing = True

        If Me.Checked Then
            Me.RestoreOriginalFormats()
        Else
            Me.Format = Windows.Forms.DateTimePickerFormat.Custom
            Me.CustomFormat = " "
        End If

        m_blnRefreshing = False

    End Sub


End Class
