2 Replies - 2596 Views - Last Post: 26 October 2016 - 07:46 AM

#1 rusoaica   User is offline

  • They're watching you, Neo!
  • member icon

Reputation: 218
  • View blog
  • Posts: 689
  • Joined: 10-March 12

Extending Calendar Control

Posted 26 August 2014 - 11:58 PM

Although there are a few examples of a custom calendar control over the internet, none of them is fully customizable, and none of them have the complete functions such a control should have. The main feature that most calendar control lack is the ability to display the previous/next month and/or years. They only display the current month. The ones that try to implement date navigation have a second major problem, the correct calculation of the leap years.
So, after struggling for a few weeks, here is the solution i came up with for a fully working, customizable calendar control. It is meant as an extension and a completion part of jacobjordan's tutorial, Making Your Own Calendar Control

Create a new Windows Form project, add a form named CalendarControl. First, we need two labels that will display the current month and the current year. Second (these are optional), 7 labels that will indicate the names of the days of the week. Third, we need four buttons for month and year forward-backward navigation. Finally, we need 7 columns of 6 rows of buttons. These will be the actual days of the displayed month. Here is a scheme of how the controls are arranged and named:

Posted Image

So, the first column of days button are named from Sunday1 to Sunday6, the second column is from Monday1 to Monday6 and so on, until the last column, Saturday1 to Saturday6.

Now, we can dive into codes. We have to declare some global variables that we will need later:

Public Class UserMainForm
    Dim DisplayedMonth As Integer = Now.Month
    Dim DisplayedYear As Integer = Now.Year
    Dim i As Integer = Now.Month
    Dim Incremental_i As Integer = Now.Month
    Dim Decremental_i As Integer = Now.Month
    Dim yearNr As Integer = Now.Year
    Dim ArrowRight As Boolean = False
    Dim ArrowLeft As Boolean = False
    Dim PastYear As Integer = 0



Next, we add four functions that will be called when we navigate to the next and previous month/year

Function YearDecrement() As Integer
    PastYear = PastYear - 1
    Return PastYear
End Function

Function YearIncrement() As Integer
    PastYear = PastYear + 1
    Return PastYear
End Function

Public Function increment() As Integer
    If i = 12 Then
        i = 1
    Else
        i = i + 1
    End If
    Return i
End Function

Public Function decrement() As Integer
    If i = 1 Then
        i = 12
    Else
        i = i - 1
    End If
    Return i
End Function




as noted in the last two functions, if we are at the 12th month and increment the month, or if we are at the first month and decrement it, we must reset the month value to 1, respectively 12.
The next function will return some day buttons based on the days of the week, so we can correctly display the starting day of the first week of the month and the end day of the last week of the month:

Function getlabel(ByVal day As DayOfWeek, ByVal row As Integer) As Button
    Select Case day
        Case DayOfWeek.Sunday
            Select Case row
                Case 1
                    Return Sunday1
                Case 2
                    Return Sunday2
                Case 3
                    Return Sunday3
                Case 4
                    Return Sunday4
                Case 5
                    Return Sunday5
                Case 6
                    Return Sunday6
            End Select
        Case DayOfWeek.Monday
            Select Case row
                Case 1
                    Return Monday1
                Case 2
                    Return Monday2
                Case 3
                    Return Monday3
                Case 4
                    Return Monday4
                Case 5
                    Return Monday5
                Case 6
                    Return Monday6
            End Select
        Case DayOfWeek.Tuesday
            Select Case row
                Case 1
                    Return Tuesday1
                Case 2
                    Return Tuesday2
                Case 3
                    Return Tuesday3
                Case 4
                    Return Tuesday4
                Case 5
                    Return Tuesday5
                Case 6
                    Return Tuesday6
            End Select
        Case DayOfWeek.Wednesday
            Select Case row
                Case 1
                    Return Wednesday1
                Case 2
                    Return Wednesday2
                Case 3
                    Return Wednesday3
                Case 4
                    Return Wednesday4
                Case 5
                    Return Wednesday5
                Case 6
                    Return Wednesday6
            End Select
        Case DayOfWeek.Thursday
            Select Case row
                Case 1
                    Return Thursday1
                Case 2
                    Return Thursday2
                Case 3
                    Return Thursday3
                Case 4
                    Return Thursday4
                Case 5
                    Return Thursday5
                Case 6
                    Return Thursday6
            End Select
        Case DayOfWeek.Friday
            Select Case row
                Case 1
                    Return Friday1
                Case 2
                    Return Friday2
                Case 3
                    Return Friday3
                Case 4
                    Return Friday4
                Case 5
                    Return Friday5
                Case 6
                    Return Friday6
            End Select
        Case DayOfWeek.Saturday
            Select Case row
                Case 1
                    Return Saturday1
                Case 2
                    Return Saturday2
                Case 3
                    Return Saturday3
                Case 4
                    Return Saturday4
                Case 5
                    Return Saturday5
                Case 6
                    Return Saturday6
            End Select
    End Select
End Function




But, we need to hide the buttons that do not display a date of the month (this is optional, but it gives the calendar a more clean and professional look)

Private Sub HideEmptyButtons()
     Sunday1.Visible = Sunday1.Text <> ""
     Sunday2.Visible = Sunday2.Text <> ""
     Sunday3.Visible = Sunday3.Text <> ""
     Sunday4.Visible = Sunday4.Text <> ""
     Sunday5.Visible = Sunday5.Text <> ""
     Sunday6.Visible = Sunday6.Text <> ""
     Monday1.Visible = Monday1.Text <> ""
     Monday2.Visible = Monday2.Text <> ""
     Monday3.Visible = Monday3.Text <> ""
     Monday4.Visible = Monday4.Text <> ""
     Monday5.Visible = Monday5.Text <> ""
     Monday6.Visible = Monday6.Text <> ""
     Tuesday1.Visible = Tuesday1.Text <> ""
     Tuesday2.Visible = Tuesday2.Text <> ""
     Tuesday3.Visible = Tuesday3.Text <> ""
     Tuesday4.Visible = Tuesday4.Text <> ""
     Tuesday5.Visible = Tuesday5.Text <> ""
     Tuesday6.Visible = Tuesday6.Text <> ""
     Wednesday1.Visible = Wednesday1.Text <> ""
     Wednesday2.Visible = Wednesday2.Text <> ""
     Wednesday3.Visible = Wednesday3.Text <> ""
     Wednesday4.Visible = Wednesday4.Text <> ""
     Wednesday5.Visible = Wednesday5.Text <> ""
     Wednesday6.Visible = Wednesday6.Text <> ""
     Thursday1.Visible = Thursday1.Text <> ""
     Thursday2.Visible = Thursday2.Text <> ""
     Thursday3.Visible = Thursday3.Text <> ""
     Thursday4.Visible = Thursday4.Text <> ""
     Thursday5.Visible = Thursday5.Text <> ""
     Thursday6.Visible = Thursday6.Text <> ""
     Friday1.Visible = Friday1.Text <> ""
     Friday2.Visible = Friday2.Text <> ""
     Friday3.Visible = Friday3.Text <> ""
     Friday4.Visible = Friday4.Text <> ""
     Friday5.Visible = Friday5.Text <> ""
     Friday6.Visible = Friday6.Text <> ""
     Saturday1.Visible = Saturday1.Text <> ""
     Saturday2.Visible = Saturday2.Text <> ""
     Saturday3.Visible = Saturday3.Text <> ""
     Saturday4.Visible = Saturday4.Text <> ""
     Saturday5.Visible = Saturday5.Text <> ""
     Saturday6.Visible = Saturday6.Text <> ""
 End Sub



If you are more ergonomic, you could put all the day-buttons inside a panel and use a for-each loop to go through all the controls inside the panel and set their visibility based on their text property.

Anyway, we now add a function that will return the month name of the currently displayed month. This will be the text displayed on the MonthName button.

Function MonthNameText(ByRef MonthToDisplay As Integer) As String
    Dim GetDisplayedMonth As String
    GetDisplayedMonth = Globalization.CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(MonthToDisplay)
    Return GetDisplayedMonth
End Function




Notice that we are using Globalization.CultureInfo.CurrentCulture, which will give us the month name string formatted in the local language of your computer.

When we first start the calendar (and not only), we need to clear all the text of the buttons, as following:

Sub clearall()
    Sunday1.Text = ""
    Sunday2.Text = ""
    Sunday3.Text = ""
    Sunday4.Text = ""
    Sunday5.Text = ""
    Sunday6.Text = ""
    Monday1.Text = ""
    Monday2.Text = ""
    Monday3.Text = ""
    Monday4.Text = ""
    Monday5.Text = ""
    Monday6.Text = ""
    Tuesday1.Text = ""
    Tuesday2.Text = ""
    Tuesday3.Text = ""
    Tuesday4.Text = ""
    Tuesday5.Text = ""
    Tuesday6.Text = ""
    Wednesday1.Text = ""
    Wednesday2.Text = ""
    Wednesday3.Text = ""
    Wednesday4.Text = ""
    Wednesday5.Text = ""
    Wednesday6.Text = ""
    Thursday1.Text = ""
    Thursday2.Text = ""
    Thursday3.Text = ""
    Thursday4.Text = ""
    Thursday5.Text = ""
    Thursday6.Text = ""
    Friday1.Text = ""
    Friday2.Text = ""
    Friday3.Text = ""
    Friday4.Text = ""
    Friday5.Text = ""
    Friday6.Text = ""
    Saturday1.Text = ""
    Saturday2.Text = ""
    Saturday3.Text = ""
    Saturday4.Text = ""
    Saturday5.Text = ""
    Saturday6.Text = ""
End Sub



Again, this could be more easily done if the buttons would be inside a panel and we would loop through them.
Self explanatory comes the next function:

Private Function GetFirstOfMonthDay(ByVal ThisDay As Date) As DayOfWeek
    Dim tday As DayOfWeek = ThisDay.DayOfWeek
    Dim tint As Integer = ThisDay.Day
    If tint = 1 Then
        Return tday
        Exit Function
    End If
    Do
        tint -= 1
        tday = ydate(tday)
        If tint = 1 Then Exit Do
    Loop
    Return tday
End Function



It will give us the first day of each month we are navigating.
Next two functions will help us get the days when we are loading the month:

Private Function ydate(ByVal tday As DayOfWeek) As DayOfWeek
    Dim rday As DayOfWeek
    Select Case tday
        Case DayOfWeek.Sunday
            rday = DayOfWeek.Saturday
        Case DayOfWeek.Monday
            rday = DayOfWeek.Sunday
        Case DayOfWeek.Tuesday
            rday = DayOfWeek.Monday
        Case DayOfWeek.Wednesday
            rday = DayOfWeek.Tuesday
        Case DayOfWeek.Thursday
            rday = DayOfWeek.Wednesday
        Case DayOfWeek.Friday
            rday = DayOfWeek.Thursday
        Case DayOfWeek.Saturday
            rday = DayOfWeek.Friday
    End Select
    Return rday
End Function

Private Function tdate(ByVal tday As DayOfWeek) As DayOfWeek
    Dim rday As DayOfWeek
    Select Case tday
        Case DayOfWeek.Sunday
            rday = DayOfWeek.Monday
        Case DayOfWeek.Monday
            rday = DayOfWeek.Tuesday
        Case DayOfWeek.Tuesday
            rday = DayOfWeek.Wednesday
        Case DayOfWeek.Wednesday
            rday = DayOfWeek.Thursday
        Case DayOfWeek.Thursday
            rday = DayOfWeek.Friday
        Case DayOfWeek.Friday
            rday = DayOfWeek.Saturday
        Case DayOfWeek.Saturday
            rday = DayOfWeek.Sunday
    End Select
    Return rday
End Function



The next step will be to add the codes that will be executed when we will click the next/previous month/year buttons. Put the codes inside the click event of your month and year navigation buttons, as following:

Private Sub MonthForward_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MonthForward.Click
    DisplayedMonth = increment()
    ArrowRight = True
    Dim IncrementMonth As Date = Now.AddDays((Now.Day - 1) * -1).AddMonths(i - Now.Month)
    If (DisplayedMonth = 12) And (ArrowRight = True) Then
        YearIncrement()
        IncrementMonth = Now.AddDays((Now.Day - 1) * -1).AddMonths(i - Now.Month).AddYears(PastYear)
    End If
    If PastYear <> 0 Then
        IncrementMonth = Now.AddDays((Now.Day - 1) * -1).AddMonths(i - Now.Month).AddYears(PastYear)
    End If
    ReloadCal(IncrementMonth, 0)
    If (DisplayedMonth = 1) And (ArrowRight = True) Then
        yearNr = yearNr + 1
    End If
    ArrowRight = False
    MonthName.Text = MonthNameText(DisplayedMonth)
    HideEmptyButtons()
    YearNumber.Text = yearNr
End Sub

Private Sub MonthBackward_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MonthBackward.Click
    DisplayedMonth = decrement()
    ArrowLeft = True
    Dim DecrementMonth As Date = Now.AddDays((Now.Day - 1) * -1).AddMonths(i - Now.Month)
    If (DisplayedMonth = 12) And (ArrowLeft = True) Then
        YearDecrement()
        DecrementMonth = Now.AddDays((Now.Day - 1) * -1).AddMonths(i - Now.Month).AddYears(PastYear)
    End If
    If PastYear <> 0 Then
        DecrementMonth = Now.AddDays((Now.Day - 1) * -1).AddMonths(i - Now.Month).AddYears(PastYear)
    End If
    ReloadCal(DecrementMonth, 0)
    If (DisplayedMonth = 12) And (ArrowLeft = True) Then
        yearNr = yearNr - 1
    End If
    ArrowLeft = False
    MonthName.Text = MonthNameText(DisplayedMonth)
    HideEmptyButtons()
    YearNumber.Text = yearNr
End Sub

Private Sub YearBackward_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles YearBackward.Click
    yearNr = yearNr - 1
    YearDecrement()
    Dim DecrementMonth As Date = Now.AddDays((Now.Day - 1) * -1).AddMonths(i - Now.Month).AddYears(PastYear)
    ReloadCal(DecrementMonth, 0)
    MonthName.Text = MonthNameText(DisplayedMonth)
    YearNumber.Text = yearNr
    HideEmptyButtons()
End Sub

Private Sub YearForward_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles YearForward.Click
    yearNr = yearNr + 1
    YearIncrement()
    Dim IncrementMonth As Date = Now.AddDays((Now.Day - 1) * -1).AddMonths(i - Now.Month).AddYears(PastYear)
    ReloadCal(IncrementMonth, 0)
    MonthName.Text = MonthNameText(DisplayedMonth)
    YearNumber.Text = yearNr
    HideEmptyButtons()
End Sub



The last method we need to add is the one that will actually do the calculation of the month and days:

    Public Sub ReloadCal(ByVal ldate As Date, ByVal Selected As Integer)
        On Error Resume Next
        Me.clearall()
        Dim fdate As DayOfWeek = GetFirstOfMonthDay(ldate)
        Dim idate As Integer = 1
        Dim row As Integer = 1
        Do
            getlabel(fdate, row).Text = idate
            If idate = Selected Then
                getlabel(fdate, row).BackColorColor = Color.White
            End If
            If fdate = DayOfWeek.Saturday Then
                row += 1
            End If
            fdate = tdate(fdate)
            idate += 1
            If idate = Date.DaysInMonth((ldate).Year, (ldate).Month) + 1 Then
                Exit Do
            End If
        Loop
        MonthName.Text = MonthNameText(ldate.Month)
    End Sub
End Class



This will re-load our calendar control. Analyzing the codes, you notice

If idate = Selected Then
         getlabel(fdate, row).BackColorColor = Color.White
End If



This will set the background color of the button that indicates the selected day to white. This can be used to display the current day, when the calendar is displayed.
Inside the Load event of our form, we add these lines, to initialize our calendar:

Private Sub CalendarControl_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    ReloadCal(Date.Today, Date.Today.Day)
    YearNumber.Text = Now.Year
    HideEmptyButtons()
End Sub



This concludes this tutorial. If you put everything right, you should compile it without any errors. This tutorial is based on jacobjordan's tutorial, Making Your Own Calendar Control.

Is This A Good Question/Topic? 2
  • +

Replies To: Extending Calendar Control

#2 escfoe2   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 2
  • Joined: 26-August 09

Re: Extending Calendar Control

Posted 12 October 2016 - 11:02 AM

I understand these aren't meant to be hardcore awesome examples, but I would really save some space to loop through those controls instead of setting their text one by one. It's really simple. Any label, button, or textbox that you don't want cleared, just assign it a tag. You'll reference the tag to make sure you don't clear that object.

Dim Ctrl as Control
Dim Btn as Button

'Loop through all of the controls on the form.
For Each Ctrl in Me.Controls
    'If it's a button, single it out.
    If TypeOf Ctrl Is Button Then
        'Push the control to a specific button object
        Btn = Ctrl
        'Make sure we don't want to keep the text
        If Btn.Tag <> "YOURTAG" Then
            'Remove the text
            Btn.Text = vbNullString
        End If
    End If
Next



There are tons of reasons to loop through controls. The main one here is for efficiency and code appearance.
Was This Post Helpful? 0
  • +
  • -

#3 rusoaica   User is offline

  • They're watching you, Neo!
  • member icon

Reputation: 218
  • View blog
  • Posts: 689
  • Joined: 10-March 12

Re: Extending Calendar Control

Posted 26 October 2016 - 07:46 AM

hello! At the time i extended that control, i was still very new to VB.NET, so i made a lot of mistakes. Since i never used it afterwards, I never took time to improve it. I'm sure there are other things to be corrected...
Anyway, nice job!
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1