As discussed in Hour 20, "Using ActiveX Controls," ActiveX controls are components that can be used to easily add new functionality to your application. In this hour, you will learn about
To help demonstrate these topics, you also will create and test an ActiveX control that you can use in your own projects.
New Term: The Component Object Model, or COM, is a specification that defines how software components should cooperate with each other. ActiveX technologies are all built on top of the COM specification.
New Term: In COM, an interface is a group of related functions that are implemented together. All interfaces are named beginning with I, such as IDataObject.
At a minimum, an ActiveX control must be a COM object. This means that an ActiveX control must support IUnknown, the interface supported by all COM objects. This allows for a great deal of latitude when deciding how a control is to be implemented; previously, the OLE custom control architecture required support of at least 14 interfaces.
Just a Minute: Support for the IUnknown interface is provided automatically when you use MFC and ControlWizard to build your control.
Reducing the number of required interfaces allows ActiveX controls to be much smaller than the older OLE controls. It also makes it feasible to use ActiveX controls to implement functionality where the size of the control is an important factor. Web pages can be more intelligent when a control is downloaded and activated to your browser. For example, Microsoft's Internet Explorer has support for downloading ActiveX controls from a Web page. Although this opens a lot of exciting functionality, the size of the control to be downloaded must be kept as small as possible.
Interaction with an ActiveX component takes place via properties, events, and methods.
You'll learn about each of these interaction methods in the next few sections.
Properties are exposed by ActiveX controls, as well as by the client where the control is located. There are four basic types of properties:
An event is used to send a notification message to the control's container. Typically, events are used to notify the container when mouse clicks or other events take place. There are two basic types of events:
New Term: OLE Automation, now often referred to as just Automation, is a way of enabling a COM object to be controlled by another party. Automation is provided through the IDispatch interface.
Automation was originally developed so that interpreted languages such as Visual Basic could control COM objects. Now most ActiveX controls use Automation to allow all sorts of programs--even those built using scripting languages such as JScript and VBScript--access to the methods of ActiveX controls.
New Term: Subclassing is a method of borrowing functionality from an existing window or control. By subclassing an existing window or control, you can concentrate on adding only the new features offered by your control. The control from which the functionality is borrowed is known as the superclass.
As an example of creating an ActiveX control, you will now create an ActiveX control named OleEdit that subclasses the existing Windows edit control. The OleEdit control is similar to the basic Windows edit control, except that it exposes properties that allow it to accept only numbers, letters, or a combination of both.
The control works by performing extra processing when the WM_CHAR message is received by the control. Windows sends WM_CHAR to notify the edit control that the user has pressed a key on the keyboard. Ordinarily, the edit control would simply add the new character to the edit control. When the WM_CHAR message is received by the OleEdit control, it is processed as shown in Figure 24.1.
Figure 24.1.
Handling WM_CHAR in OleEdit.
The property flags m_fTextAllowed and m_fNumbersAllowed are exposed as properties that can be changed by the OleEdit control's container.
To begin creating the OleEdit control, use the ControlWizard. Using ControlWizard to build a control is very much like using AppWizard to build applications. To start ControlWizard and create the OleEdit control, follow these steps:
Figure 24.2.
The first page of the ActiveX ControlWizard.
Figure 24.3.
The second page of the ActiveX ControlWizard.
A set of MFC classes is used as a framework for all ActiveX controls built using ControlWizard. ClassWizard creates three classes that are specific to your project, using these three classes as base classes:
All visible OLE controls must be capable of drawing themselves. Even controls that aren't visible when active should draw something as an aid during program development. The OleEdit control is visible at runtime, and it should appear to be a standard edit control.
CAUTION: You might think that because OleEdit is subclassed from the standard edit control, it can draw itself. Unfortunately, very few controls actually draw themselves properly; the edit control is not one that handles drawing properly. For most controls, you must be prepared to handle the drawing yourself.
When an ActiveX control project is initially created, the control's OnDraw function draws an ellipse inside the bounding rectangle. This is extremely useful if you happen to be creating an ellipse control. However, because OleEdit must look like an edit control, you must change the OnDraw function. The changes to OnDraw required for the OleEdit control are provided in Listing 24.1.
void COleEditCtrl::OnDraw( CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid) { COLORREF clrBackground = TranslateColor(GetBackColor()); CBrush* pOldBrush; CBrush brBackground( clrBackground ); pdc->FillRect( rcBounds, &brBackground ); pOldBrush = pdc->SelectObject( &brBackground ); pdc->SelectObject( pOldBrush ); DoSuperclassPaint(pdc, rcBounds); CRect rc(rcBounds); pdc->DrawEdge( rc, EDGE_SUNKEN, BF_RECT ); }
The code provided in Listing 24.1 does three things. First, it fills the control's bounding rectangle with the ambient background color. Next, it calls DoSuperclassPaint to give the edit control a chance to attempt to draw itself properly. Finally, it draws a three-dimensional edge along the control's bounding rectangle.
OleEdit uses four properties: the Font and Text stock properties and the fTextAllowed and fNumbersAllowed custom properties. Using ClassWizard, add the stock properties for the OleEdit control. Select the Automation tab, and click the Add Property button. Fill in the dialog box using the values provided in Table 24.1.
External Name | Implementation |
Font | Stock |
Text | Stock |
Use ClassWizard to add a custom property name fNumbersAllowed to the OleEdit project. Click the Add Property button and use the values provided in Table 24.2.
Control | Value |
External name | fNumbersAllowed |
Type | BOOL |
Member variable name | m_fNumbersAllowed |
Notification function | OnFNumbersAllowedChanged |
Implementation | Member variable |
Use ClassWizard to add the fTextAllowed property, following the steps used to add the previous properties. Use the values provided in Table 24.3.
Control | Value |
External name | fTextAllowed |
Type | BOOL |
Variable name | m_fTextAllowed |
Notification function | OnFTextAllowedChanged |
Implementation | Member variable |
Modify the COleEditCtrl class constructor to contain code that initializes the custom properties added in the previous steps. The modified constructor is shown in Listing 24.2.
ColeEditCtrl::COleEditCtrl() { InitializeIIDs(&IID_DOleEdit, &IID_DOleEditEvents); m_fTextAllowed = TRUE; m_fNumbersAllowed = TRUE; }
Every control created using ControlWizard includes a default property page. The OleEdit property page is modified by adding two check boxes that control the states of the m_fTextAllowed and m_fNumbersAllowed flags. Open the IDD_PROPPAGE_OLEEDIT dialog box resource and add two check box controls, as shown in Figure 24.4.
Figure 24.4.
The property page used in OleEdit.
Table 24.4 lists the properties for the check box controls. All properties that aren't listed should be set to the default values.
Control | Resource ID | Caption |
Numbers check box | IDC_CHECK_NUMBERS | &Numbers Allowed |
Text check box | IDC_CHECK_TEXT | &Text Allowed |
Use ClassWizard to associate COleEditPropPage member variables with the controls, using the values shown in Table 24.5.
Control ID | Variable Name | Category | Type | Property Name |
IDC_CHECK_NUMBERS | m_fNumbersAllowed | Value | BOOL | fNumbersAllowed |
IDC_CHECK_TEXT | m_fTextAllowed | Value | BOOL | fTextAllowed |
ClassWizard uses the optional Property Name field to generate source code that exchanges the values from the property sheet to the control class. The DDP and DDX macros are used to transfer and validate property page data. The code used to transfer the value of the IDC_CHECK_TEXT control looks like this:
//{{AFX_DATA_MAP(COleEditPropPage) DDP_Check(pDX, IDC_CHECK_TEXT, m_fTextAllowed, _T("fTextAllowed")); DDX_Check(pDX, IDC_CHECK_TEXT, m_fTextAllowed; //}}AFX_DATA_MAP DDP_PostProcessing(pDX);
Inside the control class, you must collect the values from the property page during DoPropExchange, as shown in Listing 24.3.
void COleEditCtrl::DoPropExchange(CPropExchange* pPX) { ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor)); COleControl::DoPropExchange(pPX); PX_Bool(pPX, _T("fNumbersAllowed"), m_fNumbersAllowed ); PX_Bool(pPX, _T("fTextAllowed"), m_fTextAllowed ); }
The OleEdit control supports the stock font property. An easy way to give the control access to all the available fonts is to add the standard font property page to the control. The property pages associated with an ActiveX control are grouped together between the BEGIN_PROPPAGEIDS and END_PROPPAGEIDS macros in the control class implementation file.
Listing 24.4 shows how the standard font property page is added to the control using the PROPPAGEID macro. Remember to change the second parameter passed to the BEGIN_PROPPAGEIDS macro, the number of property pages used by the control object. Locate the existing BEGIN_PROPPAGEIDS macro in the OleEditCtl.cpp file, and change that section of the file so that it looks like the code in Listing 24.4.
BEGIN_PROPPAGEIDS(COleEditCtrl, 2) // changed PROPPAGEID(COleEditPropPage::guid) PROPPAGEID(CLSID_CFontPropPage) // changed END_PROPPAGEIDS(COleEditCtrl)
As you will see when you test the control later in the hour, adding the font property page, along with exposing the stock font property, enables a user to easily change the control font. The only code that is written to allow the user to change the control's font is in Listing 24.4.
As discussed earlier, OleEdit uses exposed properties to determine whether characters entered on the keyboard are stored in the edit control. If an invalid character is input, an Error event is fired to the control's container. The message sent to the control as characters are input to the control is WM_CHAR. Using ClassWizard, add a message-handling function to the COleEditCtrl class, using the values from Table 24.6.
Class Name | Object ID | Message | Function |
COleEditCtrl | COleEditCtrl | WM_CHAR | OnChar |
The source code for the COleEditCtrl::OnChar function is provided in Listing 24.5.
void COleEditCtrl::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) { if(_istdigit(nChar) ) { if( m_fNumbersAllowed == FALSE ) { FireError( CTL_E_INVALIDPROPERTYVALUE, _T("Numbers not allowed") ); } else { COleControl::OnChar(nChar, nRepCnt, nFlags); } } else if( _istalpha(nChar) ) { if( m_fTextAllowed == FALSE ) { FireError( CTL_E_INVALIDPROPERTYVALUE, _T("Characters not allowed") ); } else { COleControl::OnChar(nChar, nRepCnt, nFlags); } } else COleControl::OnChar (nChar, nRepCnt, nFlags); }
The OnChar handler tests for valid characters based on the property flags m_fTextAllowed and m_fNumbersAllowed. Valid characters are passed to COleControl::OnChar, the base class handler for WM_CHAR. If an invalid character is detected, an Error event is fired to the control's container.
When an ActiveX control is used in a tool such as Developer Studio, Visual Basic, or the ActiveX control test container, a bitmap associated with the control is displayed to the user. In Developer Studio, the bitmap is added to the control palette used to design dialog box resources. In the test container, a toolbar button displaying the bitmap is added to the container's toolbar.
Open the IDB_OLEEDIT bitmap resource and edit the bitmap image as shown in Figure 24.5. Save the bitmap and compile the OleEdit project.
Figure 24.5.
The IDB_OLEEDIT bitmap resource.
Time Saver: To ensure that the text fits properly in the bitmap, use a regular (non-bold) 8-point Arial font.
Build the OleEdit project. As part of the build process, the control will be registered with the operating system. In the next section, you will learn how to test your control.
After following the steps in the previous sections, you are in possession of an OleEdit ActiveX control. However, because the control is a component rather than an executable program, it can't be run as an EXE. Testing an ActiveX control requires a few extra steps, which are discussed in this section.
Every ActiveX control requires a control container. The simplest control container is the ActiveX control test container included with Developer Studio and the Win32 SDK. Other ActiveX control containers include Microsoft Access and Visual Basic 5.0. In this section, you will test the OleEdit control with TSTCON32.EXE, the test container included with Developer Studio.
In order to launch the OleEdit control in the Developer Studio debugger, you must specify the application to be used to load the control. You can do this by following these steps:
After you have made these changes, you can use the Developer Studio debugger to launch the test container. Click the Go icon in the toolbar or otherwise start a debug session to display the test container, as shown in Figure 24.6.
Just a Minute: You can also launch the ActiveX control test container by selecting its menu item from the Tools menu. This doesn't start your control inside the Visual C++ debugger.
Figure 24.6.
The ActiveX control test container.
When an ActiveX control created by ControlWizard is compiled, the control is automatically registered. To display a list of all registered controls, select Insert OLE Control... from the Edit menu. A dialog box containing all available ActiveX controls is displayed. Select the OleEdit edit control, and click OK. The OleEdit control is inserted into the test container, as shown in Figure 24.7. Note that an OleEdit icon is also added to the test container toolbar.
Figure 24.7.
The ActiveX control test container and OleEdit control.
You can use the test container to test your control's properties in two ways:
To access all the properties implemented by an ActiveX control, select Properties from the View menu. A Properties dialog box is displayed, as shown in Figure 24.8.
Figure 24.8.
Accessing the properties exposed by OleEdit.
To display the list of properties exposed by the control, click the drop-down list. Every property can be accessed and changed through the control's property sheet. To change a particular property's value, select the property name from the drop-down list, set the property value, and click Apply.
A slightly easier way to use the interface is provided through the control's property sheet. You can use the test container to invoke the control's property sheet by selecting Properties from the Edit menu. The property sheet for OleEdit is shown in Figure 24.9.
Figure 24.9.
The property sheet used by OleEdit.
In this hour you learned how to create and test ActiveX controls. ActiveX controls are smaller and simpler versions of OLE custom controls. Developer Studio helps to simplify the task of creating an ActiveX control. ControlWizard is very similar to AppWizard and guides you through the steps required to create a skeleton version of your control.
The Workshop is designed to help you anticipate possible questions, review what you've learned, and begin thinking ahead to putting your knowledge into practice. The answers to the quiz are in Appendix B, "Quiz Answers."
© Copyright, Macmillan Computer Publishing. All rights reserved.