DateTimePicker?

Jess31
Jess31 used Ask the Experts™
on
How can I gray out or disable non weekdays (Sat, Sun) from the DateTimePicker?
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Top Expert 2016

Commented:
You can make them non clickable
Jquery
$("#datepicker").datepicker({ beforeShowDay: $.datepicker.noWeekends });

or use CSS
/* Hide all weekend options in the jQueryUI DatePicker dialog */
th.ui-datepicker-week-end,
td.ui-datepicker-week-end {
    display: none;
}

Holidays are a bit trickier as you have to define them
https://stackoverflow.com/questions/2029626/jquery-datepicker-disable-weekends-holidays-and-the-next-three-working-days
Éric MoreauSenior .Net Consultant
Top Expert 2016

Commented:
Windows Forms or WPF or ...?

If it is for WPF, have a look at http://emoreau.com/Entries/Articles/2015/09/Customizing-the-WPF-Calendar-control-.aspx

Author

Commented:
I am only familiar with vb.net. I am not sure how to convert this to vb.net.
Become a Microsoft Certified Solutions Expert

This course teaches how to install and configure Windows Server 2012 R2.  It is the first step on your path to becoming a Microsoft Certified Solutions Expert (MCSE).

Éric MoreauSenior .Net Consultant
Top Expert 2016

Commented:
the very first question before starting to convert code is which platform are you targeting? If you have a Windows Form, the code above won't work since it is only for web!

Author

Commented:
vb.net / winform
Éric MoreauSenior .Net Consultant
Top Expert 2016

Commented:
I would go with a 3rd party control (telerik, devexpress)

you could also use the WPF control and host it on your Winform: http://emoreau.com/Entries/Articles/2014/05/Hosting-WPF-controls-on-a-Windows-Form.aspx
Imports System.Runtime.InteropServices

Public Class DateTimePickerEx
    Inherits System.Windows.Forms.DateTimePicker

#Region " API "
    <StructLayout(LayoutKind.Sequential)> _
    Private Structure SYSTEMTIME
        <MarshalAs(UnmanagedType.U2)> Public Year As Short
        <MarshalAs(UnmanagedType.U2)> Public Month As Short
        <MarshalAs(UnmanagedType.U2)> Public DayOfWeek As Short
        <MarshalAs(UnmanagedType.U2)> Public Day As Short
        <MarshalAs(UnmanagedType.U2)> Public Hour As Short
        <MarshalAs(UnmanagedType.U2)> Public Minute As Short
        <MarshalAs(UnmanagedType.U2)> Public Second As Short
        <MarshalAs(UnmanagedType.U2)> Public Milliseconds As Short
    End Structure
    <StructLayout(LayoutKind.Sequential)> _
    Private Structure RECT
        Public left As Integer
        Public top As Integer
        Public right As Integer
        Public bottom As Integer
        Public Shared Widening Operator CType(rc As RECT) As Rectangle
            Return New Rectangle(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top)
        End Operator
    End Structure
    <StructLayout(LayoutKind.Sequential)> _
    Private Structure MCGRIDINFO
        Public cbSize As UInt32
        Public dwPart As Integer
        Public dwFlags As Integer
        Public iCalendar As Integer
        Public iRow As Integer
        Public iCol As Integer
        Public bSelected As Boolean
        Public stStart As SYSTEMTIME
        Public stEnd As SYSTEMTIME
        Public rc As RECT
        <MarshalAs(UnmanagedType.LPWStr)> Public pszName As String
        Public cchName As UInteger
    End Structure

    Private Delegate Function WinProcDelegate(ByVal Handle As IntPtr, ByVal wMsg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Int32
    Private Declare Function SetWindowLong Lib "user32.dll" Alias "SetWindowLongA" (ByVal hWnd As IntPtr, ByVal nFlag As Integer, ByVal dwNewLong As WinProcDelegate) As IntPtr
    Private Declare Function SetWindowLong Lib "user32.dll" Alias "SetWindowLongA" (ByVal hWnd As IntPtr, ByVal nFlag As Integer, ByVal dwNewLong As IntPtr) As IntPtr
    Private Declare Function CallWindowProc Lib "user32.dll" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As IntPtr, ByVal hWnd As IntPtr, ByVal msg As Int32, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Int32
    Private Declare Auto Function SendMessage Lib "user32.dll" (ByVal hwnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
    Private Declare Auto Function SendMessage Lib "user32.dll" (ByVal hwnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As IntPtr, ByRef info As MCGRIDINFO) As IntPtr
    Private Declare Auto Function GetWindowDC Lib "user32.dll" (ByVal hWnd As IntPtr) As IntPtr

    Private Const DTM_GETMONTHCAL As Integer = &H1008
    Private Const GWL_WNDPROC = -4
    Private Const WM_DESTROY = 2
    Private Const WM_PAINT = &HF
    Private Const WM_ERASEBKGND = &H14
    Private Const MCMV_MONTH = 0
    Private Const MCM_FIRST = &H1000
    Private Const MCM_GETFIRSTDAYOFWEEK = (MCM_FIRST + 16)
    Private Const MCM_GETCURRENTVIEW = (MCM_FIRST + 22)
    Private Const MCM_GETCALENDARGRIDINFO = (MCM_FIRST + 24)
    Private Const MCGIP_CALENDARCONTROL = 0
    Private Const MCGIP_CALENDARCELL = 8
    Private Const MCGIF_RECT = &H2
#End Region

#Region " Subclass "

    Private prevProc As IntPtr
    Private Sub Subclass(hwnd As IntPtr)
        prevProc = SetWindowLong(hwnd, GWL_WNDPROC, AddressOf WinProc)
    End Sub
    Private Sub UnSubclass(hwnd As IntPtr)
        SetWindowLong(hwnd, GWL_WNDPROC, prevProc)
    End Sub
    Private Function WinProc(ByVal Handle As IntPtr, ByVal wMsg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Int32
        Select Case wMsg
            Case WM_DESTROY
                Dim ret = CallWindowProc(prevProc, Handle, wMsg, wParam, lParam)
                UnSubclass(Handle)
                Return ret
            Case WM_PAINT
                Dim ret = CallWindowProc(prevProc, Handle, wMsg, wParam, lParam)
                If SendMessage(Handle, MCM_GETCURRENTVIEW, IntPtr.Zero, IntPtr.Zero) <> MCMV_MONTH Then Return ret
                Dim rc As New RECT

                Dim info = New MCGRIDINFO
                info.cbSize = Marshal.SizeOf(info)
                info.iCol = 6
                info.dwPart = MCGIP_CALENDARCELL
                info.dwFlags = MCGIF_RECT
                Dim cols = {5, 6}
                If Globalization.DateTimeFormatInfo.CurrentInfo.FirstDayOfWeek = DayOfWeek.Sunday Then cols(0) = 0
                Dim dc = GetWindowDC(Handle)
                Using g = Graphics.FromHdc(dc)
                    For row = 0 To 5
                        For Each col In cols
                            info.iRow = row
                            info.iCol = col
                            Call SendMessage(Handle, MCM_GETCALENDARGRIDINFO, IntPtr.Zero, info)
                            Using p As New Pen(Color.Gray, 2)
                                g.DrawLine(p, info.rc.left, info.rc.top, info.rc.right, info.rc.bottom)
                                g.DrawLine(p, info.rc.right, info.rc.top, info.rc.left, info.rc.bottom)
                            End Using
                        Next
                    Next
                End Using
                Return ret
            Case Else
                Return CallWindowProc(prevProc, Handle, wMsg, wParam, lParam)
        End Select
    End Function
#End Region

    Public Sub New()
        MyBase.New()
        cashedValue = Me.Value
    End Sub
    Private cashedValue As DateTime, bFromCode As Boolean

    Private Sub DateTimePickerEx_DropDown(sender As Object, e As System.EventArgs) Handles Me.DropDown
        cashedValue = Me.Value
        Dim hMonth As IntPtr = SendMessage(Me.Handle, DTM_GETMONTHCAL, IntPtr.Zero, IntPtr.Zero)
        If hMonth <> IntPtr.Zero Then Subclass(hMonth)
    End Sub
    Private Sub DateTimePickerEx_ValueChanged(sender As Object, e As System.EventArgs) Handles Me.ValueChanged
        If Not bFromCode Then
            If Me.Value.DayOfWeek = DayOfWeek.Saturday OrElse Me.Value.DayOfWeek = DayOfWeek.Sunday Then
                bFromCode = True
                Me.Value = cashedValue
                bFromCode = False
            Else
                cashedValue = Me.Value
            End If
        End If
    End Sub

End Class

Open in new window

Author

Commented:
Ark,
How would I use this DateTimePickerEx on a form ?
1. Add DateTimePickerEx class to project
2. Compile project (Menu->Build->Rebuild YourProjectName)
3. Open any form in designer - you'll see DateTimePickerEx control in toolbox

Author

Commented:
Thanks.
Very cool!
A bit improved class:  
Futures:
1. Custom Back/Fore color for restricted  dates (either design or run time)
2. 3 collections and 2 Subs for restricted dates
'Restrict days of week (design time supported)
DateTimePickerEx1.RestrictedDaysOfWeek.AddRange({DayOfWeek.Saturday, DayOfWeek.Sunday})
'Restrict specific dates (design time supported)
DateTimePickerEx1.RestrictedDates.AddRange({New Date(2017, 3, 1), New Date(2017, 4, 1)})
'Restrict days for each year
DateTimePickerEx1.RestrictedDaysOfYear.Add(New DayOfYear With {.Month = 12, .Day = 25})
'Restrict before specific date
DateTimePickerEx1.RestrictBefore(New Date(2017, 1, 1))
'Restrict after specific date
DateTimePickerEx1.RestrictAfter(New Date(2017, 12, 31))

Open in new window

3. Showing "No" cursor over restricted dates
4. Clicking on restricted dates have no effect.
DateTimePickerEx.zip

Author

Commented:
DateTimePIckerEx1.MinDate is not suppose work ?
DateTimePickerEx is a Type (Class Name). DateTimePickerEx1 is an instance (control name on your form). When you drop control from toolbox on your form IDE name it as TypeName1 (TypeName2 for second etc). For example, if you drop Button its default name would be Button1. You can change control name in Properties window. In that case replace DateTimePickerEx1 with new name. Some properties you can set at design time in properties window. There are all properties from standard DateTimePicker plus new from DateTimePickerEx (RestrictedDaysOfWeek, RestrictedDates, RestrictedColors etc).
A bit more fun with Calendar styles (via properties):
- NoToday (hide "Today" link at the bottom of calendar)
- NoTodayCircle (no highlight for today date)
- NoTrailingDays (hide dates form previous and next month)
- ShowWeekNumbers (show week numbers on the left side of calendar)
DateTimePickerEx.zip
PS. You can set properties at design time or any time during runtime. For example, you can add checkbox with text "Show week numbers" and set appropriate property on it CheckedChange event:
Private Sub CheckBox1_CheckedChanged(sender As System.Object, e As System.EventArgs) Handles CheckBox1.CheckedChanged
    DateTimePickerEx1.ShowWeekNumbers = CheckBox1.Checked
End Sub

Open in new window

Author

Commented:
But when I set
DateTimePickerExxx.MinDate = DateAdd(DateInterval.Day, 1, Now())
in runtime, and then when I click on the control I get an error
{"Year, Month, and Day parameters describe an un-representable DateTime."}
and it points to this
 Public Function ToDate() As DateTime
            Return New DateTime(Year, Month, Day)
        End Function

Author

Commented:
In the last ver you posted it doesn't happen when clicking on the control but when you press on the right arrow (=next month) I get this error:

{"Value of '3/22/2017 11:23:18 PM' is not valid for 'Value'. 'Value' should be between 'MinDate' and 'MaxDate'." & vbCrLf & "Parameter name: Value"}

Author

Commented:
:) This one is charm. Thanks
BTW, you can restrict dates before tomorrow instead of setting MinDate :
Dim tomorrow = Date.Today.AddDays(1)
With DateTimePickerEx1
    .Value = tomorrow
    .RestrictBefore(tomorrow)
    .NoToday = True
    .RestrictedDaysOfWeek.AddRange({DayOfWeek.Saturday, DayOfWeek.Sunday})
End With

Open in new window

Author

Commented:
yeah, this is much nicer. Thanks.
A few more futures:
1. Transparent "No" cursor (standard "No" cursor have non-transparent white area inside circle)
2. Customizable RestrictedFont property (you can set it different from standard, ie bold/italic/strikethru etc)
3. Customizable Prompt property (display text over DateTimePicker text area if invalid date selected by typing in text area)
DateTimePickerEx.zip

Author

Commented:
with this new update I get errors on
.BorderStyle = Windows.Forms.BorderStyle.None,
says Forms is not a member of Windows.

Does something have to be added for this to work?
change to fully qualified assembly
.BorderStyle =System.Windows.Forms.BorderStyle.None
Éric MoreauSenior .Net Consultant
Top Expert 2016

Commented:
solution provided
Éric MoreauSenior .Net Consultant
Top Expert 2016

Commented:
Ark, do you happen to have the C# version of that class?

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial