Win32 Controls - Month Calendar

 

The Month Calendar Control

 

Introduction

The Win32 API provides a control used to select dates on a colorful calendar. The dates used and the way they display are based on the Regional Settings of the Control Panel. It may also depend on the operating system:

This convenient control is called Month Calendar. The title bar of the control displays two buttons and two labels. The left button allows the user to select the previous month by clicking the button. The left label displays the currently selected month. The right label displays the year of the displayed date. The right button is used to get to the next month.

The calendar can be configured to display more than one month. Here is an example that displays two months:

If the control is displaying more than one month, the buttons would increment or decrement by the previous or next month in the list. For example, if the control is displaying April and May, if the user clicks the left button, the control would display March and April. If the control is displaying April and May and the user clicks the right button, the control would display May and June. Also, to select any month of the current year, the user can click the name of the month, which displays the list of months and this allows the user to click the desired month:

To select a year, the user clicks the year number. This changes the year label into a spin button:

To change the year, the user can click the up or down arrows of the spin button. As the spin button is displaying, the user can also use the arrow keys of the keyboard to increase or decrease the value.

Under the title bar, the short names of week days display, using the format set in Control Panel. In US English, the first day is usually Sunday. The first day can be changed by the programmer.

On the control, the currently selected date has a circle around. To select a date on the control, the user clicks the desired date, which changes from the previous selection.

In the main area, the numeric days of the month display on a white background (this color and any color on the control can be changed as we will see in the next section). To select a date, the user clicks it in the list. By default, the calendar opens with today's day circled with a hand-drawn-look-alike ellipse. Using the buttons of the title bar, the month label, and/or the year, the user can change the date. If at one time the calendar is displaying a date other than today, and if the user wants to return to today's date, he can click the bottom label that displays Today (you as the programmer can hide the Today label if you want).

Practical Learning Practical Learning: Creating the Application

  1. Because Borland C++BuilderX is free, we are going to use it.
    Start Borland C++BuilderX and, on the main menu, click File -> New...
     
  2. In the Object Gallery dialog box, click New GUI Application and click OK
  3. In the New GUI Application Project Wizard - Step 1 of 3, in the Directory edit box of the Project Settings section, type the path you want. Otherwise, type
    C:\Programs\Win32 Programming
  4. In the Name edit box, type MonthCalendar
     
    New GUI Application Project Wizard
  5. Click Next
  6. In the New GUI Application Project Wizard - Step 2 of 3, accept the defaults and click Next
  7. In the New GUI Application Project Wizard - Step 3 of 3, click the check box under Create
  8. Select Untitled under the Name column header. Type Exercise to replace the name and press Tab
     
  9. Click Finish
  10. To create the resource header file, on the main menu, click File -> New File...
  11. In the Create New File dialog box, change the contents of the Name edit box with resource
  12. In the Type combo box, select h
     
    Create New File
  13. Click OK
  14. In the file, type #define IDD_CONTROLSDLG 101
  15. To create the rc resource file, on the main menu of C++BuilderX, click File -> New File...
  16. In the Create New File dialog box, change the contents of the Name edit box to MonthCalendar
  17. In the Type combo box, select rc
     
    Create New File
  18. Click OK
  19. In the empty file, type the following (the referenced header file will be created next):
     
    #include "resource.h"
    
    IDD_CONTROLSDLG DIALOG 260, 200, 260, 150
    STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
    CAPTION "Windows Controls"
    FONT 8, "MS Shell Dlg"
    BEGIN
        DEFPUSHBUTTON   "Close", IDCANCEL, 202,14,50,14
    END
  20. In the left frame, double-click Exercise.cpp and change the file to the following:
    #include <windows.h>
    #ifdef __BORLANDC__
      #pragma argsused
    #endif
    
    #include "resource.h"
    //---------------------------------------------------------------------------
    HWND hWnd;
    LRESULT CALLBACK DlgProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
    //---------------------------------------------------------------------------
    int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
                          LPSTR lpCmdLine, int nCmdShow )
    {
            DialogBox(hInstance, MAKEINTRESOURCE(IDD_CONTROLSDLG),
                      hWnd, reinterpret_cast<DLGPROC>(DlgProc));
    
      return 0;
    }
    //---------------------------------------------------------------------------
    LRESULT CALLBACK DlgProc(HWND hWndDlg, UINT Msg, WPARAM wParam, LPARAM lParam)
    {
            switch(Msg)
            {
            case WM_INITDIALOG:
                    return TRUE;
    
            case WM_COMMAND:
                    switch(wParam)
                    {
                    case IDCANCEL:
                            EndDialog(hWndDlg, 0);
                            return TRUE;
                    }
                    break;
            }
    
            return FALSE;
    }
    //---------------------------------------------------------------------------
  21. Press F9 to test the application
  22. Click Close to dismiss the dialog box
 

Month Calendar Creation

The Month Calendar control is part of the family of Common Controls defined in the comctl32.dll library. Therefore, before including it in your application, you must register it by calling the InitCommonControlsEx() function and assign the ICC_DATE_CLASSES value to the INITCOMMONCONTROLSEX::dwICC member variable.

As always, there are two main ways you can create a control. You can use a resource script. If you use a script, the formula to follow is:

The easiest way to create a combo box is through a resource script. The syntax used to create the control in a script is:

CONTROL string, id,"SysMonthCal32", style, x, y, width, height

You must start with the CONTROL class to indicate that you are going to create a control from the Comctl32.dll family of controls.

The string factor can be anything. It is for controls that would need it. The Month Calendar control doesn't need it. Therefore, you can pass it as an empty string "" or provide any string you want.

The id is the number used to identify the control in a resource header file.

To indicate that you are creating a Month Calendar control, pass the class name as SysMonthCal32.

The style and the extended-style factors are used to configure and prepare the behavior of the combo box.

The x measure is its horizontal location with regards to the control's origin, which is located in the top left corner of the window that is hosting the combo box

The y factor is the distance from control's origin, which is located in the top left corner of the window that is hosting the combo box, to the top-left side of the combo box

The width and the height specify the dimensions of the control

Instead of using a script, to create a Month Calendar control, you can use the CreateWindowEx() function and pass the name of the class as MONTHCAL_CLASS

Practical LearningPractical Learning: Creating the Control in a Script

  1. To include the comctl32.dll library in your project, on the main menu of C++BuilderX, click Project -> Add Files
  2. Navigate to Drive:\C++BuilderX\lib\psdk folder and display its content
  3. In the right frame, click comctl32.lib
     
  4. Click OK
  5. Change the resource.h header file as follows:
     
    #define IDD_CONTROLSDLG   101
    #define IDC_MONTHCALENDAR 102
  6. Change the MonthCalendar.rc resource script as follows:
     
    #include "resource.h"
    
    IDD_CONTROLSDLG DIALOG 260, 200, 200, 120
    STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
    CAPTION "Windows Controls"
    FONT 8, "MS Shell Dlg"
    BEGIN
        CONTROL         "",IDC_MONTHCALENDAR,"SysMonthCal32",
                        WS_CHILD | WS_TABSTOP,10,10,118,96
        DEFPUSHBUTTON   "Close", IDCANCEL, 140,10,50,14
    END
  7. Change the Exercise.cpp source file as follows:
     
    #include <windows.h>
    #include <commctrl.h>
    
    #ifdef __BORLANDC__
      #pragma argsused
    #endif
    
    #include "resource.h"
    //---------------------------------------------------------------------------
    HWND hWnd;
    LRESULT CALLBACK DlgProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
    //---------------------------------------------------------------------------
    int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
                          LPSTR lpCmdLine, int nCmdShow )
    {
        DialogBox(hInstance, MAKEINTRESOURCE(IDD_CONTROLSDLG),
                      hWnd, reinterpret_cast<DLGPROC>(DlgProc));
    
        INITCOMMONCONTROLSEX InitCtrlEx;
    
        InitCtrlEx.dwSize = sizeof(INITCOMMONCONTROLSEX);
        InitCtrlEx.dwICC  = ICC_PROGRESS_CLASS;
        InitCommonControlsEx(&InitCtrlEx);
    
      return 0;
    }
    //---------------------------------------------------------------------------
    LRESULT CALLBACK DlgProc(HWND hWndDlg, UINT Msg, WPARAM wParam, LPARAM lParam)
    {
            switch(Msg)
            {
            case WM_INITDIALOG:
                    return TRUE;
    
            case WM_COMMAND:
                    switch(wParam)
                    {
                    case IDCANCEL:
                            EndDialog(hWndDlg, 0);
                            return TRUE;
                    }
                    break;
            }
    
            return FALSE;
    }
    //---------------------------------------------------------------------------
  8. Test the application

The Properties of a Month Calendar Control

 

The Effects of the Control Dimensions

After creating a Month Calendar control, it displays the current month and maybe only one month. To display more than one month, set the width of the control to provide enough space. Here is an example:

#include "resource.h"

IDD_CONTROLSDLG DIALOG 260, 200, 320, 120
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Windows Controls"
FONT 8, "MS Shell Dlg"
BEGIN
    CONTROL         "",IDC_MONTHCALENDAR,"SysMonthCal32",
                    WS_CHILD | WS_TABSTOP,10,10,240,96
    DEFPUSHBUTTON   "Close", IDCANCEL, 260,10,50,14
END

In the same way, you can increase the height to display many months.

Colors: The Background of the Title Bar

To make it a highly visual object, a calendar uses different colors to represent the background, week days, the background of the title bar, the text of the title bar, the text of the days of the previous month, and the text of the days of the subsequent month. Of course, you can programmatically change these colors. Although any color is allowed in any category, you should make sure that the calendar is still reasonably appealing and usable.

To change the colors of the Month Calendar control, you can call the MonthCal_SetColor() function. Its syntax is:

COLORREF MonthCal_SetColor(HWND hwndMC, INT iColor, COLORREF clr);

The first argument is a handle to the Month Calendar control whose color you want to set or change.

By default, the title of the Month Calendar control appears on top of a blue background. If you want a different color, pass the iColor argument as MCSC_TITLEBK, and pass the color of your choice as the clr argument. 

 Here is an example:

//---------------------------------------------------------------------------
LRESULT CALLBACK DlgProc(HWND hWndDlg, UINT Msg, WPARAM wParam, LPARAM lParam)
{
    HWND hWndMonthCal;

    hWndMonthCal = GetDlgItem(hWndDlg, IDC_MONTHCALENDAR);

        switch(Msg)
        {
        case WM_INITDIALOG:
          MonthCal_SetColor(hWndMonthCal, MCSC_TITLEBK, RGB(205, 50, 5));

                return TRUE;

        case WM_COMMAND:
                switch(wParam)
                {
                case IDCANCEL:
                        EndDialog(hWndDlg, 0);
                        return TRUE;
                }
                break;
        }

        return FALSE;
}
//---------------------------------------------------------------------------

Alternatively, you can send the MCM_SETCOLOR message. The syntax to use would be:

lResult = SendMessage(HWND hWnd, MCM_SETCOLOR, WPARAM wParam, LPARAM lParam);

In this case, pass the wParam argument as MCSC_TITLEBK, and pass the color of your choice as the lParam argument. 

 

Practical LearningPractical Learning: Changing the Background of the Title Bar

  1. To set the background color of the title bar, change the procedure as follows:
     
    //---------------------------------------------------------------------------
    LRESULT CALLBACK DlgProc(HWND hWndDlg, UINT Msg, WPARAM wParam, LPARAM lParam)
    {
        HWND hWndMonthCal;
    
        hWndMonthCal = GetDlgItem(hWndDlg, IDC_MONTHCALENDAR);
    
            switch(Msg)
            {
            case WM_INITDIALOG:
                SendMessage(hWndMonthCal, MCM_SETCOLOR, MCSC_TITLEBK, RGB(205, 50, 5));
    
                    return TRUE;
    
            case WM_COMMAND:
                    switch(wParam)
                    {
                    case IDCANCEL:
                            EndDialog(hWndDlg, 0);
                            return TRUE;
                    }
                    break;
            }
    
            return FALSE;
    }
    //---------------------------------------------------------------------------
  2. Test the application

Colors: The Caption of the Title Bar

By default, the labels on the title bar display in a white color. To change the color used to paint the text of the labels, you can call the MonthCal_SetColor() macro. Alternatively, you can send the MCM_SETCOLOR message, pass the wParam argument as MCSC_TITLETEXT, and pass the color of your choice as the lParam argument.

The names of weekdays use the same color as the color set when passing the MCSC_TITLETEXT value as the wParam or the iColor argument.

Under the names of the week, there is a horizontal line used as the separator. By default, this line separator is painted in black but it uses the same color as the numeric values of the days of the selected month.

 

Practical Learning Practical Learning: Changing the Color of the Title Bar Caption

  1. To set the background color of the title bar, change the procedure as follows:
    //---------------------------------------------------------------------------
    LRESULT CALLBACK DlgProc(HWND hWndDlg, UINT Msg, WPARAM wParam, LPARAM lParam)
    {
        HWND hWndMonthCal;
    
        hWndMonthCal = GetDlgItem(hWndDlg, IDC_MONTHCALENDAR);
    
            switch(Msg)
            {
            case WM_INITDIALOG:
                SendMessage(hWndMonthCal, MCM_SETCOLOR, MCSC_TITLEBK, RGB(205, 50, 5));
                MonthCal_SetColor(hWndMonthCal, MCSC_TITLETEXT, RGB(255, 250, 5));
    
                    return TRUE;
    
            case WM_COMMAND:
                    switch(wParam)
                    {
                    case IDCANCEL:
                            EndDialog(hWndDlg, 0);
                            return TRUE;
                    }
                    break;
            }
    
            return FALSE;
    }
    //---------------------------------------------------------------------------
  2. Test the application
 

Colors: The Caption of the Title Bar

Under the names of the week and their line separator, the numeric days of the month are listed. These days display in a different color. To specify the color of the days of the current month, you can call the MonthCal_SetColor() macro or send a MCSC_TEXT message. Pass the iColor or the wParam as MCSC_TEXT and pass the color of your choice as the lParam or the Clr argument.

Practical Learning Practical Learning: Changing the Color of the Current Month Days

  1. To set the background color of the title bar, change the procedure as follows:
     
    //---------------------------------------------------------------------------
    LRESULT CALLBACK DlgProc(HWND hWndDlg, UINT Msg, WPARAM wParam, LPARAM lParam)
    {
        HWND hWndMonthCal;
    
        hWndMonthCal = GetDlgItem(hWndDlg, IDC_MONTHCALENDAR);
    
            switch(Msg)
            {
            case WM_INITDIALOG:
                SendMessage(hWndMonthCal, MCM_SETCOLOR, MCSC_TITLEBK, RGB(205, 50, 5));
                MonthCal_SetColor(hWndMonthCal, MCSC_TITLETEXT, RGB(255, 250, 5));
                MonthCal_SetColor(hWndMonthCal, MCSC_TEXT, RGB(128, 0, 5));
    
                    return TRUE;
    
            case WM_COMMAND:
                    switch(wParam)
                    {
                    case IDCANCEL:
                            EndDialog(hWndDlg, 0);
                            return TRUE;
                    }
                    break;
            }
    
            return FALSE;
    }
    //---------------------------------------------------------------------------
  2. Test the application

 

Colors: Days of the Trailing Months

The numbers of the days of the month display in two colors. The real days of the selected month display, by default, in a black color as the WindowText system color. To change the color of the days of the other months, you can call the MonthCal_SetColor() macro, pass the iColor argument as MCSC_TRAILINGTEXT, and pass the color of your choice as the clr argument. Alternatively, you can send the MCM_SETCOLOR message

Colors: The Body Background

Under the line separator, the numeric days of the month are listed. By default, the numeric days of the control display above a white background which is the Window system color. To change the background color of the area where the numeric days appear, you can call the MonthCal_SetColor() macro or send a MCM_SETCOLOR message, pass the iColor or the wParam argument as MCSC_MONTHBK, and pass the color of your choice as the Clr or the lParam argument.

Today's Effect

The Month Calendar control is used to let the user know today's date in two ways. On the calendar, today's date is circled by a hand-drawn ellipse. In the bottom section of the calendar, today's date is also displayed as a sentence:

To programmatically hide the today label, you can apply the MCS_NOTODAY style. Here is an example:

#include <commctrl.h>
#include "resource.h"

IDD_CONTROLSDLG DIALOG 260, 200, 200, 120
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Windows Controls"
FONT 8, "MS Shell Dlg"
BEGIN
    CONTROL         "",IDC_MONTHCALENDAR,"SysMonthCal32",
                    WS_CHILD | WS_TABSTOP | MCS_NOTODAY | MCS_NOTODAY,10,10,118,96
    DEFPUSHBUTTON   "Close", IDCANCEL, 140,10,50,14
END

We also mentioned that today date appears with a circle. If you want to hide just the circle, apply the MCS_NOTODAYCIRCLE style:

#include <commctrl.h>
#include "resource.h"

IDD_CONTROLSDLG DIALOG 260, 200, 200, 120
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Windows Controls"
FONT 8, "MS Shell Dlg"
BEGIN
    CONTROL         "",IDC_MONTHCALENDAR,"SysMonthCal32",
                    WS_CHILD | WS_TABSTOP | MCS_NOTODAY |
                    MCS_NOTODAY | MCS_NOTODAYCIRCLE,10,10,118,96
    DEFPUSHBUTTON   "Close", IDCANCEL, 140,10,50,14
END

As you may know already, to programmatically change the style of a control, for example to show or hide the today label, you can call the SetWindowLong() function.

The First Day of the Week

Under the title bar, the short names of week days display, using the format set in Control Panel. In US English, the first day is Sunday. If you want to start with a different day, you can call the MonthCal_SetFirstDayOfWeek() macro. Its syntax is:

DWORD MonthCal_SetFirstDayOfWeek(HWND hwndMC, INT iDay);

Alternatively, you can send the MCM_SETFIRSTDAYOFWEEK message using the following syntax:

lResult = SendMessage(HWND hWndControl, MCM_SETFIRSTDAYOFWEEK, WPARAM wParam, LPARAM lParam);

The first argument is the handle to the Month Calendar control whose first day you want to set.

The wParam argument is not used and can be passed as 0.

The iDay or the lParam argument is a constant integer of the following values:

Value Weekday
0 Monday
1 Tuesday
2 Wednesday
3 Thursday
4 Friday
5 Saturday
6 Sunday


If the calendar is already functioning, to find what its first day of the week is, you can call the MonthCal_GetFirstDayOfWeek() macro. Its syntax is:

DWORD MonthCal_GetFirstDayOfWeek(HWND hWndMC);

To get the same information, you can send an MCM_GETFIRSTDAYOFWEEK message. Neither the wParam nor the lParam argument is used. Therefore, they must be passed as 0.

Both the MonthCal_GetFirstDayOfWeek() macro and the SendMessage() function that carries the MCM_GETFIRSTDAYOFWEEK message returns a value of the above table.

 

The Selected Day

At any time, a particular date is selected and has an ellipse with the same color as the background of the title bar. By default, the selected date is today's date. When the user clicks the calendar, a date is selected. You also can programmatically select a day. To do this, you can call the MonthCal_SetCurSel() macro whose syntax is:

BOOL MonthCal_SetCurSel(HWND hwndMC, LPSYSTEMTIME lpSysTime);

Alternatively, you can send a MCM_SETCURSEL message. The syntax used is:

lResult = SendMessage(HWND hWndControl, MCM_SETCURSEL, WPARAM wParam, LPARAM lParam);  

The wParam argument of the SendMessage() function is not used and can be passed as 0.

The lpSysTime and the lParam arguments are passed as pointers to the Win32's SYSTEMTIME structure and therefore carry the returning value of the function.

After a day has been selected, whether by you or the user, to find out what day was selected, you can call the MonthCal_GetCurSel() macro whose syntax is:

BOOL MonthCal_GetCurSel(HWND hwndMC, LPSYSTEMTIME lpSysTime);

To get the same information, you can send a MCM_GETCURSEL message using the following syntax:

lResult = SendMessage(HWND hWndControl,  MCM_GETCURSEL,  WPARAM wParam, LPARAM lParam);  

 

 

The Multiple Day Selection Option

When the user clicks the Month Calendar control, one date is selected. To control whether the user can select one or more dates, you can apply the MCS_MULTISELECT style:

#include <commctrl.h>
#include "resource.h"

IDD_CONTROLSDLG DIALOG 260, 200, 200, 120
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Windows Controls"
FONT 8, "MS Shell Dlg"
BEGIN
    CONTROL         "",IDC_MONTHCALENDAR,"SysMonthCal32",
                    WS_CHILD | WS_TABSTOP | MCS_NOTODAY |
                    MCS_NOTODAY | MCS_NOTODAYCIRCLE | MCS_MULTISELECT,
                    10,10,118,96
    DEFPUSHBUTTON   "Close", IDCANCEL, 140,10,50,14
END

With this property set, the user can select many days in the calendar. You also can programmatically select a range of days. To to this, you can call the MonthCal_SetSelRange() macro whose syntax is:

BOOL MonthCal_SetSelRange(HWND hwndMC, LPSYSTEMTIME lprgSysTimeArray);

To perform the same operation, you can send a MCM_SETSELRANGE message using the following syntax:

lResult = SendMessage(HWND hWndControl, MCM_SETSELRANGE, WPARAM wParam, LPARAM lParam);

After the user has selected a range date on the calendar, to find out what that range is, you can call the MonthCal_GetSelRange() macro.

As mentioned already, to change the month and subsequently the year of the calendar, the user can click the buttons continuously. To control the allowable dates the user can navigate from and to, you can call the MonthCal_SetRange() macro.