The Ex09a Example: An ActiveX Control Dialog Container
Now it's time to build an application that uses a calendar control in a dialog box. Here are the steps to create the Ex09a example:
Verify that the calendar control is registered. If the control does not appear in your system directory, copy the files



Run the MFC Application Wizard to generate the Ex09a project. Accept all of the default settings but two: Select Single Document and deselect Printing And Print Preview. On the Advanced Features page, be sure the ActiveX Controls option is selected, as shown here:

Install the calendar control in the Ex09a project. Choose Add Class from Visual C++ .NET's Project menu. Choose MFC Class From ActiveX Control and then click Open. Select Calendar Control 9.0 from the list of available controls in the Add Class From ActiveX Control Wizard. Visual Studio .NET will generate a class in the Ex09a directory. The Add Class From ActiveX Control Wizard is shown here:

Edit the calendar control class to handle help messages. Add the following message map code to

BEGIN_MESSAGE_MAP(CCalendar, CWnd)
ON_WM_HELPINFO()
END_MESSAGE_MAP()
In the same file, add the OnHelpInfo function:
BOOL CCalendar::OnHelpInfo(HELPINFO* pHelpInfo)
{
// Edit the following string for your system
::WinHelp(GetSafeHwnd(), "c:\\winnt\\system32\\mscal.hlp",
HELP_FINDER, 0);
return FALSE;
}
In

protected:
afx_msg BOOL OnHelpInfo(HELPINFO* pHelpInfo);
DECLARE_MESSAGE_MAP()
The OnHelpInfo function is called if the user presses the F1 key when the calendar control has the input focus. We have to add the message map code by hand because Visual Studio .NET doesn't modify generated ActiveX classes.
Note | The ON_WM_HELPINFO macro maps the WM_HELP message. You can use ON_WM_HELPINFO in any view or dialog class and then code the handler to activate any help system. |
Use the dialog editor to create a new dialog resource. Choose Add Resource from Visual C++ .NET's Project menu, and then choose Dialog. The dialog editor will assign the ID IDD_DIALOG1 to the new dialog box. Next, change the ID to IDD_ACTIVEXDIALOG, change the dialog caption to ActiveX Dialog, and set the dialog's Context Help property. Accept the default OK and Cancel buttons with the IDs IDOK and IDCANCEL, and then add the other controls as shown earlier in Figure 9-1. Make the Select Date button the default button. Right-click on the dialog box, select Insert ActiveX Control, and then select the calendar control from the list. Then set an appropriate tab order. While the dialog template is showing in the dialog editor, choose Tab Order from the Format menu. Click on the controls in the order you want them to tab. You'll see numbers next to the controls, indicating the tab order. Assign control IDs as shown in the following table.
Control | ID |
---|---|
Calendar control | IDC_CALENDAR1 |
Select Date button | IDC_SELECTDATE |
Edit control | IDC_DAY |
Edit control | IDC_MONTH |
Edit control | IDC_YEAR |
Next Week button | IDC_NEXTWEEK |
Use Visual Studio .NET to create the CActiveXDialog class. Choose Add Class from Visual C++ .NET's Project menu. Choose MFC Class and click Open. In the MFC Class Wizard, create a CDialog derived class based on the IDD_ACTIVEXDIALOG template. Be sure to select CDialog as the base class. Name the class CActiveXDialog.Right-click on the CActiveXDialog class in Class View and then choose Properties. Click the Overrides button on the Properties window toolbar to list virtual functions for CActiveXDialog. Add overriding functions for OnInitDialog and OnOK.Click the Events button on the Properties window toolbar, and then add the message handler functions shown in the following table. To add a message handler function, click on an object ID, click on an event, click the drop-down combo box that appears next to the entry, and select <Add>_. The function will be written into the code and will appear inside the Code Editor.
Object ID | Event | Member Function |
---|---|---|
IDC_CALENDAR1 | NewMonth | NewMonthCalendar1 |
IDC_SELECTDATE | BN_CLICKED | OnBnClickedSelectdate |
IDC_NEXTWEEK | BN_CLICKED | OnBnClickedNextweek |
Use the Add Member Variable Wizard to add data members to the CActiveXDialog class. Right-click on CActiveDialog in Class View and choose Add Variable from the shortcut menu, and then add the data members m_calendar, m_sDay, m_sMonth, and m_sYear, as shown here:

Edit the CActiveXDialog class. Add m_varValue and m_BackColor data members, and then edit the code for the two overriding functions and three handler functions (OnInitDialog, NewMonthCalendar1, OnBnClickedSelectdate, OnBnClickedNextweek, and OnOK). The following code shows all the code for the dialog class, with new code in boldface.ActiveXDialog.h
#pragma once
#include "ccalendar.h"
//////////////////////////////////////////////////////////////////////
// CActiveXDialog dialog
class CActiveXDialog : public CDialog
{
DECLARE_DYNAMIC(CActiveXDialog)
public:
CActiveXDialog(CWnd* pParent = NULL); // standard constructor
virtual ~ActiveXDialog();
// Dialog Data
enum { IDD = IDD_ACTIVEXDIALOG };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV
// support
DECLARE_MESSAGE_MAP()
public:
virtual BOOL OnInitDialog();
void NewMonthCalendar1();
afx_msg void OnBnClickedSelectdate();
afx_msg void OnBnClickedNextweek();
DECLARE_EVENTSINK_MAP()
public:
CCalendar m_calendar;
short m_sDay;
short m_sMonth;
short m_sYear;
COleVariant m_varValue;
unsigned long m_BackColor;
protected:
virtual void OnOK();
};
ActiveXDialog.cpp
// ActiveXDialog.cpp : implementation file
//
#include "Stdafx.h"
#include "Ex09a.h"
#include "ActiveXDialog.h"
// CActiveXDialog dialog
IMPLEMENT_DYNAMIC(CActiveXDialog, CDialog)
CActiveXDialog::CActiveXDialog(CWnd* pParent /*=NULL*/)
: CDialog(CActiveXDialog::IDD, pParent)
{
m_sDay = 0;
m_sMonth = 0;
m_sYear = 0;
m_BackColor = 0x8000000F;
}
ActiveXDialog::~ActiveXDialog()
{
}
void CActiveXDialog::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_CALENDAR1, m_calendar);
DDX_Text(pDX, IDC_DAY, m_sDay);
DDX_Text(pDX, IDC_MONTH, m_sMonth);
DDX_Text(pDX, IDC_YEAR, m_sYear);
DDX_OCColor(pDX, IDC_CALENDAR1, DISPID_BACKCOLOR, m_BackColor);
}
BEGIN_MESSAGE_MAP(CActiveXDialog, CDialog)
ON_BN_CLICKED(IDC_SELECTDATE, OnBnClickedSelectdate)
ON_BN_CLICKED(IDC_NEXTWEEK, OnBnClickedNextweek)
END_MESSAGE_MAP()
//////////////////////////////////////////////////////////////////////
// CActiveXDialog message handlers
BEGIN_EVENTSINK_MAP(CActiveXDialog, CDialog)
ON_EVENT(CActiveXDialog, IDC_CALENDAR1, 3,
NewMonthCalendar1, VTS_NONE)
END_EVENTSINK_MAP()
BOOL CActiveXDialog::OnInitDialog()
{
CDialog::OnInitDialog();
m_calendar.put_Value(m_varValue); // no DDX for VARIANTs
return TRUE; //return TRUE unless you set
//the focus to a control
//EXCEPTION: OCX Property Pages
//should return FALSE
}
void CActiveXDialog::OnOK()
{
CDialog::OnOK();
m_varValue = m_calendar.get_Value(); // no DDX for VARIANTs
}
void CActiveXDialog::NewMonthCalendar1()
{
AfxMessageBox("EVENT: CActiveXDialog::NewMonthCalendar1");
}
void CActiveXDialog::OnBnClickedSelectdate()
{
CDataExchange dx(this, TRUE);
DDX_Text(&dx, IDC_DAY, m_sDay);
DDX_Text(&dx, IDC_MONTH, m_sMonth);
DDX_Text(&dx, IDC_YEAR, m_sYear);
m_calendar.put_Day(m_sDay);
m_calendar.put_Month(m_sMonth);
m_calendar.put_Year(m_sYear);
}
void CActiveXDialog::OnBnClickedNextweek()
{
m_calendar.NextWeek();
}
The OnBnClickedSelectdate function is called when the user clicks the Select Date button. The function gets the day, month, and year values from the three edit controls and transfers them to the control's properties. The Add Member Variable Wizard can't add DDX code for the BackColor property, so you must add it by hand. In addition, there's no DDX code for VARIANT types, so you must add code to the OnInitDialog and OnOK functions to set and retrieve the date with the control's Value property.
Connect the dialog box to the view. Use the Class View's Properties window to map the WM_LBUTTONDOWN message, and then edit the handler function as follows:
void CEx09aView::OnLButtonDown(UINT nFlags, CPoint point)
{
CActiveXDialog dlg;
dlg.m_BackColor = RGB(255, 251, 240); // light yellow
COleDateTime today = COleDateTime::GetCurrentTime();
dlg.m_varValue = COleDateTime(today.get_Year(),
today.get_Month(),
today.get_Day(), 0, 0, 0);
if (dlg.DoModal() == IDOK) {
COleDateTime date(dlg.m_varValue);
AfxMessageBox(date.Format("%B %d, %Y"));
}
}
The code sets the background color to light yellow and the date to today's date, displays the modal dialog box, and reports the date returned by the calendar control. You'll need to include


Edit the virtual OnDraw function in the file

pDC->TextOut(0, 0, "Press the left mouse button here.");
Build and test the Ex09a application. Open the dialog box, enter a date in the three edit controls, and then click the Select Date button. Click the Next Week button. Try moving the selected date directly to a new month, and observe the message box that is triggered by the NewMonth event. Watch for the final date in another message box when you click OK.
If you use a text editor to look inside the

CONTROL ",IDC_CALENDAR1,
"{8E27C92B-1264-101C-8A2F-040224009C02}",
WS_TABSTOP,7,7,217,113
There's a 32-digit number sequence where the window class name should be. What's going on? Actually, the resource template isn't the one that Windows sees. The CDialog::DoModal function "preprocesses" the resource template before passing it on to the dialog box procedure within Windows. It strips out all the ActiveX controls and creates the dialog window without them. Then it loads the controls (based on their 32-digit identification numbers, called CLSIDs) and activates them in place, causing them to create their own windows in the correct places. The initial values for the properties you set in the dialog editor are stored in binary form inside the project's custom DLGINIT resource.When the modal dialog box runs, the MFC code coordinates the messages sent to the dialog window both by the ordinary controls and by the ActiveX controls. This allows the user to tab between all the controls in the dialog box, even though the ActiveX controls are not part of the actual dialog template.When you call the member functions for the control object, you might think you're calling functions for a child window. The control window is quite far removed, but MFC steps in to make it seem as if you're communicating with a real child window. In ActiveX terminology, the container owns a site, which is not a window. You call functions for the site, and ActiveX and MFC make the connection to the underlying window in the ActiveX control.The container window is an object of a class derived from CWnd. The control site is also an object of a class derived from CWnd—the ActiveX control wrapper class. That means that the CWnd class has built-in support for both containers and sites.