My point is I want to keep the calendar always on the months view, like this:
Expected TMonthCalendar view:
So that when I click in a month, instead of showing the days of the month, it stays in this screen and call the event.
Prior to Vista, the underlying Win32 MonthCal control that TMonthCalendar
wraps has no concept of views at all, so you can't do what you are asking for in XP and earlier, unless you find a 3rd party calendar that supports what you want on those Windows versions.
However, in Vista and later, the underlying MonthCal control is view-aware (but TMonthCalendar
itself is not). You can manually send a MCM_SETCURRENTVIEW
message to the TMonthCalendar
's HWND
to set its initial view to MCMV_YEAR
, and subclass its WindowProc
property to intercept CN_NOTIFY
messages (the VCL's wrapper for WM_NOTIFY
) looking for the MCN_VIEWCHANGE
notification when the user changes the active view. You can't lock the control to a specific view, but you can react to when the user changes the active view from the Year view to the Month view, and then you can reset the calendar back to the Year view if needed.
For example:
class TMyForm : public TForm
{
__published:
TMonthCalendar *MonthCalendar1;
...
private:
TWndMethod PrevMonthCalWndProc;
void __fastcall MonthCalWndProc(TMessage &Message);
...
public:
__fastcall TMyForm(TComponent *Owner)
...
};
#include "MyForm.h"
#include <Commctrl.h>
#ifndef MCM_SETCURRENTVIEW
#define MCMV_MONTH 0
#define MCMV_YEAR 1
#define MCM_SETCURRENTVIEW (MCM_FIRST + 32)
#define MCN_VIEWCHANGE (MCN_FIRST - 4) // -750
typedef struct tagNMVIEWCHANGE
{
NMHDR nmhdr;
DWORD dwOldView;
DWORD dwNewView;
} NMVIEWCHANGE, *LPNMVIEWCHANGE;
#endif
__fastcall TMyForm(TComponent *Owner)
: TForm(Owner)
{
if (Win32MajorVersion >= 6)
{
SendMessage(MonthCalendar1->Handle, MCM_SETCURRENTVIEW, 0, MCMV_YEAR);
PrevMonthCalWndProc = MonthCalendar1->WindowProc;
MonthCalendar1->WindowProc = MonthCalWndProc;
}
}
void __fastcall TMyForm::MonthCalWndProc(TMessage &Message)
{
PrevMonthCalWndProc(Message);
if (Message.Msg == CN_NOTIFY)
{
if (reinterpret_cast<NMHDR*>(Message.LParam)->code == MCN_VIEWCHANGE)
{
LPNMVIEWCHANGE lpNMViewChange = static_cast<LPNMVIEWCHANGE>(Message.LParam);
if ((lpNMViewChange->dwOldView == MCMV_YEAR) && (lpNMViewChange->dwNewView == MCMV_MONTH))
{
// do something ...
SendMessage(MonthCalendar1->Handle, MCM_SETCURRENTVIEW, 0, MCMV_YEAR);
}
}
}
}
If you are using C++Builder 10.1 Berlin or later, look at the newer TCalendarView
and TCalendarPicker
components. They both have a DisplayMode
property that you can set to TDisplayMode::dmYear
for the current view, and an On(Calendar)ChangeView
event to react to view changes by the user.