Lemur zaprasza
Teach Yourself Visual C++® 5 in 24 Hours - Hour 24 - Creating ActiveX Controls 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 Some of the internal plumbing that is required for an ActiveX control The support provided by MFC for ActiveX control development How to test ActiveX controls using tools supplied with Visual C++ To help demonstrate these topics, you also will create and test an ActiveX control that you can use in your own projects. What Is an ActiveX Control? 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. ActiveX Control Properties, Events, and Methods Interaction with an ActiveX component takes place via properties, events, and methods. A property is an attribute associated with the control. An event is a notification message passed to the container by the control. A method is an exposed function that can be applied to the control via IDispatch. You'll learn about each of these interaction methods in the next few sections. Properties Properties are exposed by ActiveX controls, as well as by the client where the control is located. There are four basic types of properties: Ambient properties are provided to the control by the container. The control uses these properties in order to "fit in" properly. Commonly used ambient properties include the container's background color, default font, and foreground color. Extended properties are implemented by the container but appear to be generated by the control. For example, the tab order of various controls in a container is an extended property. Stock properties are control properties implemented by the ActiveX control development kit. Examples of stock properties are the control font, the caption text, and the foreground and background colors. Custom properties are control properties that you implement. Events 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: Stock events are implemented by the ActiveX control development kit and are invoked just like a function call, such as FireError. Custom events are implemented by you, although the MFC class library and ClassWizard handle much of the work for you. Methods 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. An ActiveX Control Example 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. Creating the Project 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: 1. Select New from the File menu. The New dialog box is displayed. 2. Select the Projects tab. A list of project types is displayed. 3. To create an ActiveX control, select MFC ActiveX ControlWizard as the project type. 4. Specify OleEdit as the project name; a default location for your project will automatically be provided for the location. 5. Make sure the Create New Workspace radio button is selected, and click OK to create the project. 6. The initial page from the ControlWizard is shown in Figure 24.2. This page enables you to specify the basic characteristics of the project, such as the number of controls handled by this server, whether help files should be generated, and so on. Accept all the default options presented on this page by clicking the Next button. Figure 24.2. The first page of the ActiveX ControlWizard. 7. The second ControlWizard page is shown in Figure 24.3. This page lets you change the names associated with the control and its OLE interfaces, as well as define properties for the control. There is also a drop-down list that allows a base class to be specified for the control. Select Edit from the drop-down list to make the OleEdit control a subclass of the standard edit control. Figure 24.3. The second page of the ActiveX ControlWizard. 8. Click the Finish button. As with other ControlWizard projects, a list of the files to be created is displayed. Click OK, and the skeleton project is created. MFC Support for ActiveX Controls 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: COleControlModule is the class that manages the ActiveX control module. This class plays a role similar to the CWinApp class used in applications built using AppWizard. For the OleEdit project, the derived class is named COleEditApp. COleControl is the base class that represents the actual control window. This class is derived from CWnd and includes extra ActiveX functionality for communicating with containers. For the OleEdit project, the derived class is named CTestCtrl. COlePropertyPage is the base class used to manage the property page for the control. For the OleEdit project, the derived class is named CTestPropPage. Drawing the Control 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. TYPE: Listing 24.1. The OnDraw function used by COleEditCtrl. 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. Defining Properties for OleEdit 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. Table 24.1. Stock properties for the OleEdit control. 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. Table 24.2. The fNumbersAllowed custom property for the OleEdit control. 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. Table 24.3. The fTextAllowed custom property for the OleEdit control. 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. TYPE: Listing 24.2. Modifications to the COleEditCtrl constructor. 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. Table 24.4. Property values for check box controls in the OleEdit property page. 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. Table 24.5. Values for new member variables in COleEditPropPage. 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. TYPE: Listing 24.3. Collecting property page data during DoPropExchange. 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. TYPE: Listing 24.4. Adding the standard font property page to OleEdit. 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. Handling Character Input 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. Table 24.6. Handling the WM_CHAR message in COleEditCtrl. 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. TYPE: Listing 24.5. Handling the WM_CHAR message in COleEditCtrl::OnChar. 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. Modifying the Control's Bitmap 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. Testing an ActiveX 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. Choosing a Test Container for Your Control 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. Using the TSTCON32 Test Container 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: 1. Select Settings from the Project menu in Developer Studio. The Project Setting dialog box is displayed. 2. Click the Debug tab. 3. A small button with a right-arrow icon is located next to the Executable for Debug Session edit control. Click this button and choose ActiveX Control Test Container from the menu that is displayed. 4. Click OK to dismiss the dialog box and save your changes. 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. Testing Properties You can use the test container to test your control's properties in two ways: Through an Automation interface that lists all exposed properties Through your control's property sheet 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. Summary 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. Q&A Q If I test my ActiveX control in the test container, is it reasonable to assume that it will work in all control containers? A You might think so, but in reality you should test your control in as many containers as you can find. The test container is very useful for performing basic tests on your control. You should also test your control in whatever environment it will be used. For example, if your control will be used in Visual Basic programs, you should test your control using VB as a container. Q Why does ControlWizard offer the Invisible at Runtime option? What use is an invisible control? A There actually are a large number of controls that don't have a need for a user interface. For example, an ActiveX control that performs protocol conversion for data communications might never be presented to the user; it can just perform work for its container. By offering this option, the control is easier to develop because the control doesn't need to handle user-interface issues. The control is also easier to use because it will hide itself automatically. Workshop 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." Quiz 1. What is an ActiveX interface? 2. What interface must be supported by an ActiveX control? 3. What are some examples of ActiveX control containers? 4. What four types of properties are supported by an ActiveX control? 5. What are the two types of events generated by ActiveX controls? 6. What macros are used to transfer data to and from property pages in an ActiveX control? 7. What MFC base classes provide support for ActiveX controls? 8. What type of properties are supplied to the ActiveX control by its container? 9. What is subclassing? 10. What function is called by an ActiveX control to request that the subclassed control repaint itself? Exercises 1. Change the OleEdit project so that hexadecimal numbers can be entered when the Numbers Only flag is set. 2. Change the OleEdit project so that only letters, numbers, and backspaces can be entered into the control. © Copyright, Macmillan Computer Publishing. All rights reserved. |