by Rob McGregor
ActiveX controls are the new standard for OLE Control Extensions (OCXs) that use the OLE Control 96 specification. Because ActiveX controls are changing the face of the World Wide Web, they are having an enormous impact on the world of Windows communications programming. This chapter shows you how to create a new ActiveX control and add it to a Web page to help activate the Internet.
An ActiveX control is an OLE control with some special features. The control must be a COM object and must export the functions DLLRegisterServer() and DLLUnRegisterServer(). The control must also implement the IUnknown interface. MFC makes this all very easy by providing convenient wrapper classes; the Visual C++ OLE Control Wizard makes it easy to create a skeleton control. The Visual C++ 4.2 Control Wizard has been updated to support advanced features of ActiveX controls. The most important new feature is support for asynchronous code and property downloading. This feature allows control code and properties to load in the background, preventing application blocking.
OLE controls are programmable software components that can be
used in a variety of OLE-enabled containers, including COM-aware
(ActiveX-enabled) Web browsers on the Internet. The most exciting
thing about an ActiveX control is that it is inherently an Internet
control. This means that an ActiveX control can be used within
an ActiveX document or it can be a direct part of a Web page!
NOTE |
ActiveX controls aren't limited to use on the Web. They can also be used in any ActiveX-enabled container (if the control supports the COM interfaces required by that container). |
ActiveX controls are special because they use the OLE Controls 96 specification. Like standard OLE controls, ActiveX controls are self-contained, plug-in software components. The OLE Controls 96 specification provides some advanced features for OLE controls not available previously, including these:
NOTE |
Because the primary purpose of ActiveX technology at this time is to provide useful components over the Internet, it's desirable to keep the code size small for an ActiveX control. To help ensure this, Microsoft Visual C++ 4.x doesn't allow static linkage for ActiveX controls. The controls must use the shared MFC DLLs at runtime, which means that these DLLs must be present on the user's system. |
Let's now implement a basic-but useful and fun-ActiveX control.
This control was inspired by the Java applet "Nervous Text,"
which was written by Daniel Wyszynski from the Center for Applied
Large-Scale Computing (CALC).
NOTE |
The source code for the JIGGLER ActiveX control has no relation whatsoever to the original "Nervous Text" Java applet source code; I started from scratch. (Thanks for the idea though, Dan!) |
The basic idea for the control is that a text string, supplied
by the user of the control, becomes visually hyperactive in the
container, whether it be an ActiveX-enabled Web page or an OLE
container window. Each character in the text string must be offset
from the next by a random amount on both the x and y axes. This
random offset is controlled by a window timer internal to the
control; the randomly offset characters are redrawn with each
WM_TIMER message received by the control. This causes
the text string to appear to dance, or jiggle, on the display
(thus the name JIGGLER).
NOTE |
JIGGLER.OCX, along with all its source files, can be found in the SOURCE\CHAP30\JIGGLER folder on this book's companion CD-ROM. |
To test the completed JIGGLER.OCX control, you use the Test Container application that ships with Visual C++ and create a simple Web page for Microsoft's ActiveX-enabled Web browser, Internet Explorer 3 (IE3), to test the control in a typical active Web setting.
The first step in creating the JIGGLER ActiveX control is to create an ActiveX-compliant control skeleton. The easiest and most effective way to do this is to use the Visual C++ 4.2 OLE Control Wizard, which provides new ActiveX features in step 2 of the Wizard (see Figure 30.1).
Figure 30.1 : Creating a new ActiveX control project with the Visual C++ 4.2 OLE Control Wizard.
The OLE Control Wizard walks you through the two simple steps
needed to create a robust ActiveX control skeleton. Then it's
just a matter of adding the methods needed to implement the control's
desired functionality (in this case, the jiggling of some text).
NOTE |
The Visual C++ OLE Control Wizard enables you to select advanced ActiveX options for your control. The most important of these is asynchronous download. For this skeleton control, it's the only option you need to check. |
After you select the Finish button in the Control Wizard dialog box, the Control Wizard generates a set of header, source, and support files for the new control. The resulting classes for the JIGGLER control from a run of the Control Wizard are shown in Figure 30.2, as displayed in the Visual C++ ClassView pane.
Figure 30.2 : The classes and methods generated automatically by the OLE Control Wizard.
The skeleton control can be compiled at this point. Visual C++ automatically creates a type library and registers the new control with the system registry at this time. If you insert the control into the Test Container application (included with Visual C++), the default visual representation of the control simply draws an ellipse. You can modify the visual aspects of the control by adding the desired code.
Now that you have an ActiveX skeleton to build on, let's add the functionality needed to create an OCX that has some useful purpose-in this case, jiggling text. Your JIGGLER.OCX control will have the following features:
The default resources for the skeleton control are a good starting point, but they need to be customized for the professional look users have come to expect. For this control, you change the default bitmap used to identify the OCX in client programming system tool palettes (such as the Visual Basic tool palette) to that shown in Figure 30.3.
After recompiling, the OCX uses the new bitmap to help users differentiate a JIGGLER control from other controls in a tool palette.
To enable users to control the JIGGLER.OCX properties at design time, you add controls to the control's property page dialog box resource (IDD_PROPAGE_JIGGLER) to represent the state or value of each property exposed by the control. After adding the appropriate controls for every property (as discussed next), the dialog resource looks like the one shown in Figure 30.4.
Figure 30.4 : The customized property page dialog resource in the Developer Studio resource editor.
The dialog box resource has only four controls: two static text boxes and two edit controls. The edit controls allow the user to specify a text string (for the jiggling text), and the interval (in milliseconds) at which the jiggling occurs. The entire dialog resource script should look something like that shown in Listing 30.1.
Listing 30.1. The IDD_PROPPAGE_JIGGLER dialog resource.
IDD_PROPPAGE_JIGGLER DIALOG DISCARDABLE 0, 0, 250, 62 STYLE WS_CHILD FONT 8, "MS Sans Serif" BEGIN LTEXT "Jiggle Text:",IDC_STATIC,5,7,45,10 EDITTEXT IDC_EDIT_CAPTION,55,5,180,12,ES_AUTOHSCROLL LTEXT "Interval:",IDC_STATIC,5,22,45,10 EDITTEXT IDC_EDIT_INTERVAL,55,20,40,12,ES_AUTOHSCROLL END
To fill out the new JIGGLER.OCX CJigglerCtrl class and make it fully functional, perform the following steps:
Here are the properties you'll add:
The methods you'll add are listed in Table 30.1.
Method | Description |
GetDefaultFont() | Gets the custom font stored in the m_fontDefault data member. |
JiggleText() | The method that implements the text-jiggling functionality. |
OnCreate() | Starts the internal timer when the control window is created. |
OnDestroy() | Kills the internal timer when the control window is destroyed. |
OnIntervalChanged() | Called by MFC when the timer interval changes and resets the internal timer to the new value. |
OnTimer() | Called by MFC in response to the WM_TIMER message. This method calls JiggleText() for each WM_TIMER message in the control's message queue. |
SetDefaultFont() | Sets the custom font stored in the m_fontDefault data member. |
The CJigglerCtrl class declaration for the JIGGLER.OCX control is given in Listing 30.2.
Listing 30.2. The interface for the CJigglerCtrl class (JIGGLERC.H).
/////////////////////////////////////////////////////////////////// // Module : JIGGLERC.H // // Purpose : Interface for the CJigglerCtrl OLE control class. #define IDC_TIMER1 100 /////////////////////////////////////////////////////////////////// // The CJigglerCtrl class class CJigglerCtrl : public COleControl { DECLARE_DYNCREATE(CJigglerCtrl) // Constructor public: CJigglerCtrl(); virtual void DoPropExchange(CPropExchange* pPX); protected: CRect m_rcBounds; // Control bounding rect CFontHolder m_fontDefault; // Custom OLE font void JiggleText(); DECLARE_OLECREATE_EX(CJigglerCtrl) // Class factory and guid DECLARE_OLETYPELIB(CJigglerCtrl) // GetTypeInfo DECLARE_PROPPAGEIDS(CJigglerCtrl) // Property page IDs DECLARE_OLECTLTYPE(CJigglerCtrl) // Type name and misc status // Message map entries afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); afx_msg void OnDestroy(); afx_msg void OnTimer(UINT nIDEvent); afx_msg void AboutBox(); DECLARE_MESSAGE_MAP() // Event maps DECLARE_EVENT_MAP() // Dispatch maps short m_nInterval; afx_msg void OnIntervalChanged(); afx_msg LPFONTDISP GetDefaultFont(); afx_msg void SetDefaultFont(LPFONTDISP newValue); DECLARE_DISPATCH_MAP() public: // Dispatch and event IDs enum { dispidInterval = 1L, dispidDefaultFont = 2L, }; }; ///////////////////////////////////////////////////////////////////
The CJigglerCtrl class is the heart of your ActiveX control, providing all the functionality the control needs. The helper methods and dispatch map methods listed in the class declaration in Listing 30.2 must, of course, be implemented. The default font is described by the FONTDESC type, which is used by OLE to create the font (in this case, 24-point Arial). This is shown in the following code:
// Default OLE font for the control static const FONTDESC _fontdescDefault = { sizeof(FONTDESC), OLESTR("Arial"), FONTSIZE(24), FW_NORMAL, ANSI_CHARSET, FALSE, FALSE, FALSE };
The class data member m_fontDefault is used to hold the resulting CFontHolder object. The CFontHolder class encapsulates the functionality of a Windows font object and the OLE IFont interface. You use this in the Jiggler control to implement the custom DefaultFont property.
The CFontHolder class implements an OLE font object that
uses the IFontDispatch OLE interface. This interface
exposes a font object's properties through OLE automation. Table
30.2 describes the dispatch IDs for the various font properties.
Symbol | |
DISPID_FONT_NAME | |
DISPID_FONT_SIZE | |
DISPID_FONT_BOLD | |
DISPID_FONT_ITALIC | |
DISPID_FONT_UNDER | |
DISPID_FONT_STRIKE | |
DISPID_FONT_WEIGHT | |
DISPID_FONT_CHARSET |
The properties in the IFontDisp interface support both
read and write access, and they are listed in Table 30.3. These
properties will become very useful later in this chapter when
you want to manipulate font characteristics for your JIGGLER
control with a scripting language in your sample Web page.
Property | Type | Description |
Name | BSTR | The face name of the font (for example, Arial) |
Size | CY | The point size of the font |
Bold | BOOL | Indicates if the font is bold |
Italic | BOOL | Indicates if the font is italic |
Underline | BOOL | Indicates if the font is underlined |
Strikethrough | BOOL | Indicates if the font is strikethrough |
Weight | short | The boldness of the font |
Charset | short | The character set used for the font |
The CJigglerCtrl constructor simply calls the COleControl method InitializeIIDs() with the appropriate parameters, as you can see here:
CJigglerCtrl::CJigglerCtrl() : m_fontDefault(&m_xFontNotification) { InitializeIIDs(&IID_DJiggler, &IID_DJigglerEvents); m_lReadyState = READYSTATE_LOADING; }
The COleControl member m_lReadyState is set to the value READYSTATE_LOADING to indicate that the control is currently loading its properties.
The dispatch map for the CJigglerCtrl class is used to set up the connection between class data members and property values. Listing 30.3 shows the dispatch map for the CJigglerCtrl class.
Listing 30.3. The dispatch map for the CJigglerCtrl class.
/////////////////////////////////////////////////////////////////// // Dispatch map BEGIN_DISPATCH_MAP(CJigglerCtrl, COleControl) DISP_PROPERTY_NOTIFY(CJigglerCtrl, "Interval", m_nInterval, OnIntervalChanged, VT_I2) DISP_PROPERTY_EX(CJigglerCtrl, "DefaultFont", GetDefaultFont, SetDefaultFont, VT_FONT) DISP_DEFVALUE(CJigglerCtrl, "Caption") DISP_STOCKPROP_CAPTION() DISP_STOCKFUNC_REFRESH() DISP_STOCKPROP_READYSTATE() DISP_STOCKPROP_BACKCOLOR() DISP_STOCKPROP_FORECOLOR() DISP_FUNCTION_ID(CJigglerCtrl, "AboutBox", DISPID_ABOUTBOX, AboutBox, VT_EMPTY, VTS_NONE) END_DISPATCH_MAP()
NOTE |
The JIGGLER control uses the stock property Caption, which is the default property for the control. This is evident from the use of the DISP_STOCKPROP_CAPTION and DISP_DEFVALUE macros. |
There are three property pages for this control: one custom page (the General page) and two predefined pages (Colors and Font), as specified with this code:
/////////////////////////////////////////////////////////////////// // Property pages BEGIN_PROPPAGEIDS(CJigglerCtrl, 3) PROPPAGEID(CJigglerPropPage::guid) PROPPAGEID(CLSID_CColorPropPage) PROPPAGEID(CLSID_CFontPropPage) END_PROPPAGEIDS(CJigglerCtrl)
At runtime, The General property page (defined by your customized dialog template resource) looks like the one in Figure 30.5.
Figure 30.5 : The customized General property page (CJigglerPropPage::guid).
The identifiers CLSID_CColorPropPage and CLSID_CFontPropPage are predefined values that MFC uses to add the Colors and Font property pages to the control's property sheet. Figure 30.6 shows the predefined Colors property page with the two stock properties BackColor and ForeColor present.
Figure 30.6 : The predefined Colors property page (CLSID_CColorPropPage).
Figure 30.7 shows the predefined Font property page with the default Arial font selected.
Figure 30.7 : The predefined Font property page (CLSID_CFontPropPage).
To make sure that your OLE control "remembers" settings applied to properties at design time when it executes at runtime, you must implement object persistence for the control. By doing this, you can ensure that property settings made with a property editor will remain valid at runtime without writing additional code on the client side. This is achieved by using the PX_* group of persistence support functions provided by MFC.
The CJigglerCtrl::DoPropExchange() method makes calls to MFC's predefined persistence methods to provide property persistence for the JIGGLER control. The method is given in Listing 30.4.
Listing 30.4. The DoPropExchange() method.
/////////////////////////////////////////////////////////////////// // CJigglerCtrl::DoPropExchange - Persistence support void CJigglerCtrl::DoPropExchange(CPropExchange* pPX) { ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor)); // Set a default caption if (InternalGetText() == "") SetText(_T("ActiveX Jiggler!!")); COleControl::DoPropExchange(pPX); // // Call PX_* functions for each persistent custom property // // Property exchange for default font PX_Font(pPX, _T("DefaultFont"), m_fontDefault, &_fontdescDefault); PX_Short(pPX, _T("Interval"), m_nInterval, 100); }
When the CJigglerCtrl class was generated by the OLE Control Wizard, it generated some very important OLE housekeeping functions in the JIGGLERC.CPP source file. These functions perform the following tasks:
Listing 30.5 shows the code for these tasks.
Listing 30.5. OLE housekeeping functions for the CJigglerCtrl class.
/////////////////////////////////////////////////////////////////// // Initialize class factory and guid IMPLEMENT_OLECREATE_EX(CJigglerCtrl, "JIGGLER.JigglerCtrl.1", 0x5cd2fc83, 0xe7bd, 0x11cf, 0xa3, 0xbc, 0x44, 0x45, 0x53, 0x54, 0, 0) /////////////////////////////////////////////////////////////////// // Type library ID and version IMPLEMENT_OLETYPELIB(CJigglerCtrl, _tlid, _wVerMajor, _wVerMinor) /////////////////////////////////////////////////////////////////// // Interface IDs const IID BASED_CODE IID_DJiggler = { 0x5cd2fc81, 0xe7bd, 0x11cf, { 0xa3, 0xbc, 0x44, 0x45, 0x53, 0x54, 0, 0 } }; const IID BASED_CODE IID_DJigglerEvents = { 0x5cd2fc82, 0xe7bd, 0x11cf, { 0xa3, 0xbc, 0x44, 0x45, 0x53, 0x54, 0, 0 } }; /////////////////////////////////////////////////////////////////// // Control type information static const DWORD BASED_CODE _dwJigglerOleMisc = OLEMISC_ACTIVATEWHENVISIBLE | OLEMISC_SETCLIENTSITEFIRST | OLEMISC_INSIDEOUT | OLEMISC_CANTLINKINSIDE | OLEMISC_RECOMPOSEONRESIZE; IMPLEMENT_OLECTLTYPE(CJigglerCtrl, IDS_JIGGLER, _dwJigglerOleMisc) /////////////////////////////////////////////////////////////////// // CJigglerCtrl::CJigglerCtrlFactory::UpdateRegistry - // Adds or removes system registry entries for CJigglerCtrl BOOL CJigglerCtrl::CJigglerCtrlFactory::UpdateRegistry( BOOL bRegister) { if (bRegister) return AfxOleRegisterControlClass( AfxGetInstanceHandle(), m_clsid, m_lpszProgID, IDS_JIGGLER, IDB_JIGGLER, afxRegInsertable | afxRegApartmentThreading, _dwJigglerOleMisc, _tlid, _wVerMajor, _wVerMinor); else return AfxOleUnregisterClass(m_clsid, m_lpszProgID); }
Typically, MFC calls the OnDraw() method whenever a control needs to be redrawn. In this case, OnDraw() simply isn't needed because the control is continually redrawn in response to the internal timer firing WM_TIMER messages. Instead, the drawing code all takes place within the JiggleText() method, shown in Listing 30.6.
Listing 30.6. The JiggleText() method.
/////////////////////////////////////////////////////////////////// // CJigglerCtrl::JiggleText() - The meat of the control! void CJigglerCtrl::JiggleText() { // Get the client DC CClientDC dc(this); // Prepare a memory DC for holding a memory bitmap CDC dcMem; dcMem.CreateCompatibleDC(&dc); // Get the current caption text and text length const CString& strText = InternalGetText(); int nMsgLen = strText.GetLength(); // Select font and set transparent text mode, forecolor CFont* pFontOld = SelectFontObject(&dcMem, m_fontDefault); int nModeOld = dcMem.SetBkMode(TRANSPARENT); int crTextColorOldMem = dcMem.SetTextColor( TranslateColor(GetForeColor())); // Get the text char size for current font TEXTMETRIC tm; dcMem.GetTextMetrics(&tm); int nTextHeight = tm.tmHeight + tm.tmExternalLeading * 2; int nAveCharWidth = (int)(tm.tmAveCharWidth * 1.5); // Get the bounding rect for the entire string (current font) int nCtrlWidth = nAveCharWidth * nMsgLen + (nAveCharWidth / 2); int nCtrlHeight = nTextHeight + (nTextHeight / 4); // Autosize the control to the text (if needed) int nWidth, nHeight; GetControlSize(&nWidth, &nHeight); CRect rcOld(0, 0, nCtrlWidth, nCtrlHeight); CRect rcCurrent(0, 0, nWidth, nHeight); if (rcCurrent != rcOld) SetControlSize(nCtrlWidth, nCtrlHeight); // Prepare a memory bitmap CBitmap bmp; bmp.CreateCompatibleBitmap(&dc, nCtrlWidth, nCtrlHeight); // Select the bitmap CBitmap* pbmpOld = dcMem.SelectObject(&bmp); // Fill with current background color CRect rc(0, 0, nCtrlWidth, nCtrlHeight); CBrush brBack(TranslateColor(GetBackColor())); dcMem.FillRect(&rc, &brBack); // Create a random number generating object CRand rand; // Jiggle the chars for (int i = 0; i < nMsgLen; i++) { CRect rc; UINT cx = rand.MapRand(nAveCharWidth) / 2; UINT cy = rand.MapRand(nTextHeight) / 4; rc.left = (nAveCharWidth * i) + cx; rc.right = rc.left + nAveCharWidth + cx; rc.top = cy; rc.bottom = rc.top + nTextHeight + cy; // Draw next character on the bitmap dcMem.DrawText((CString)strText[i], rc, DT_CENTER); } // Blast bitmapped text to the window dc.BitBlt(0, 0, nCtrlWidth, nCtrlHeight, &dcMem, 0, 0, SRCCOPY); // Clean up dcMem.SelectObject(pbmpOld); dcMem.SelectObject(pFontOld); dcMem.SetTextColor(crTextColorOldMem); dcMem.SetBkMode(TranslateColor(nModeOld)); bmp.DeleteObject(); }
This code might look somewhat tricky at first glance, but the idea is really quite simple. To make the text jiggle, you get the bounding rectangle of the text using the text metrics of the current font. Then you subdivide this rectangle into smaller rectangles, one for each character in the string. You then draw the text, one character at a time (within a loop) on a memory bitmap, with each character offset by a random amount in the x and y axes.
The memory bitmap is then blasted to the control's client area
using the CDC::BitBlt() method. Using BitBlt()
instead of drawing directly in the control prevents flashing and
makes the jiggling look much smoother. The control's client area
is resized automatically to the maximum size of the jiggled text
string. This provides the smallest area needed for the BitBlt()
and optimizes the speed of the bit block transfer operation.
CAUTION |
It's very important to clean up by calling the DeleteObject() method for the GDI bitmap object. If this isn't done, the control will allocate a new bitmap every time the timer fires a WM_TIMER message, and the GDI heap will be quickly exhausted. This will cause unpredictable results, always resulting in the text no longer jiggling, but it could also easily cause a system crash under Windows 95! |
The CJigglerPropPage class provides the user interface needed to control JIGGLER.OCX properties at design time from development environments such as the Visual C++ Developer Studio. The interface for the CJigglerPropPage class is given in Listing 30.7.
Listing 30.7. The interface for the CJigglerPropPage class.
/////////////////////////////////////////////////////////////////// // Module : JIGGLERC.CPP // // Purpose : Interface for the CJigglerPropPage property page // class. /////////////////////////////////////////////////////////////////// // The CJigglerPropPage class class CJigglerPropPage : public COlePropertyPage { DECLARE_DYNCREATE(CJigglerPropPage) DECLARE_OLECREATE_EX(CJigglerPropPage) public: CJigglerPropPage(); // Constructor // Dialog Data enum { IDD = IDD_PROPPAGE_JIGGLER }; CString m_Caption; int m_nInterval; protected: // DDX/DDV support virtual void DoDataExchange(CDataExchange* pDX); // Message map DECLARE_MESSAGE_MAP() }; ///////////////////////////////////////////////////////////////////
This class uses two data members, m_Caption and m_nInterval, to handle the data exchange to the Caption and Interval properties. The complete implementation source code for this class is given in Listing 30.8.
Listing 30.8. The implementation of the CJigglerPropPage class.
/////////////////////////////////////////////////////////////////// // Module : JIGGLERP.CPP // // Purpose : Implementation of the CJigglerPropPage property // page class. #include "stdafx.h" #include "Jiggler.h" #include "JigglerP.h" IMPLEMENT_DYNCREATE(CJigglerPropPage, COlePropertyPage) /////////////////////////////////////////////////////////////////// // Initialize class factory and guid IMPLEMENT_OLECREATE_EX(CJigglerPropPage, "JIGGLER.JigglerPropPage.1", 0x5cd2fc84, 0xe7bd, 0x11cf, 0xa3, 0xbc, 0x44, 0x45, 0x53, 0x54, 0, 0) /////////////////////////////////////////////////////////////////// // CJigglerPropPage::CJigglerPropPageFactory::UpdateRegistry - // Adds or removes system registry entries for CJigglerPropPage BOOL CJigglerPropPage::CJigglerPropPageFactory::UpdateRegistry( BOOL bRegister) { if (bRegister) return AfxOleRegisterPropertyPageClass( AfxGetInstanceHandle(), m_clsid, IDS_JIGGLER_PPG); else return AfxOleUnregisterClass(m_clsid, NULL); } /////////////////////////////////////////////////////////////////// // CJigglerPropPage::CJigglerPropPage - Constructor CJigglerPropPage::CJigglerPropPage() : COlePropertyPage(IDD, IDS_JIGGLER_PPG_CAPTION) { m_Caption = _T(""); m_nInterval = 100; } /////////////////////////////////////////////////////////////////// // CJigglerPropPage::DoDataExchange - Moves data between page // and properties void CJigglerPropPage::DoDataExchange(CDataExchange* pDX) { DDP_Text(pDX, IDC_EDIT_CAPTION, m_Caption, _T("Caption")); DDX_Text(pDX, IDC_EDIT_CAPTION, m_Caption); DDV_MaxChars(pDX, m_Caption, 50); DDP_Text(pDX, IDC_EDIT_INTERVAL, m_nInterval, _T("Interval")); DDX_Text(pDX, IDC_EDIT_INTERVAL, m_nInterval); DDV_MinMaxInt(pDX, m_nInterval, 10, 1000); DDP_PostProcessing(pDX); } ///////////////////////////////////////////////////////////////////
To test the new functionality of the JIGGLER control, you first drop it into the Test Container application to verify that all the properties are functioning properly and that the behavior of the control is what you expect. The result of this test is shown in Figure 30.8.
Figure 30.8 : The fully implemented JIGGLER OLE control in the Test Container application.
Just to verify that everything works properly at design time in an actual Windows development environment, you dropped JIGGLER.OCX into a new Visual Basic 4 form. The result of this small test can be seen in Figure 30.9.
Figure 30.9 : The JIGGLER ActiveX control in a generic Visual Basic 4 form.
ActiveX controls are the hot new way to activate the World Wide
Web, bringing previously static Web pages to life. Now that the
control has been successfully tested in both the Test Container
application and a generic VB form, it's time to write a Web page
of your own to fully test the control in a Web browser. To "activate"
this sample Web page, you'll use three instances of the JIGGLER
control and programmatically control them with OLE Automation
by using VBScript, Microsoft's trimmed-down version of Visual
Basic for the Internet.
NOTE |
The source for the sample Web page can be found in the SOURCE\CHAP30\JIGGLER folder on this book's companion CD-ROM as the file JIGGLER3.htm. |
Before you can add a control to a Web page, you must first create an HTML document that defines the page. There are several HTML generating utilities and applications available, but you'll start your sample page by using one that comes free with every version of Windows: Notepad. Yes, with the venerable Notepad and a little knowledge of HTML syntax, you can create a simple Web page in no time. The source code for a basic Web page contains just a few simple HTML tags, as shown in Listing 30.9.
Listing 30.9. The HTML source for a minimal Web page.
<HTML> <HEAD> <TITLE>Some Title</TITLE> </HEAD> <BODY> </BODY> </HTML>
Of course, this code is pretty useless, because nothing appears in the browser! You must customize the page by adding some text that describes the new JIGGLER ActiveX control, as shown in Listing 30.10.
Listing 30.10. The modified HTML source for describing the JIGGLER ActiveX control.
<HTML> <HEAD> <TITLE>Rob's Jiggler ActiveX Control</TITLE> </HEAD> <BODY BGCOLOR="#FFFFFF"> <H2><I>Rob's ActiveX Jiggler Control</I></H2> <HR> This is my new JIGGLER ActiveX control!! Pretty cool, eh? It was inspired by the "Nervous Text" Java applet, but this was totally redesigned from scratch and written in C++ for ActiveX! It supports many background and text colors, and it's easily extensible, so go for it!<p> </BODY> </HTML>
Note that you've set the BODY tag to include a background color set to white:
<BODY BGCOLOR="#FFFFFF">
When opened in Microsoft's ActiveX-enabled Web browser Internet Explorer 3 (IE3), this code results in a Web page that resembles the one in Figure 30.10.
Figure 30.10 : The basic Web page in Internet Explorer 3.0.
Now that the basic HTML file is ready, how do you add the ActiveX
control to the page to activate it with some exciting jiggling
text? The easiest way to add the JIGGLER.OCX to the Web
page is with a very useful tool: Microsoft's ActiveX Control Pad.
The Control Pad is a free utility designed to insert ActiveX controls
into Web pages. This utility also generates basic VBScript and
JavaScript source code for the controls on your pages.
NOTE |
You can download the ActiveX Control Pad utility from the Web at this URL: http://www.microsoft.com/workshop/author/cpad/ If Microsoft has moved the utility by the time you read this, just go to www.microsoft.com and search for Control Pad. |
To add the JIGGLER control to the Web page, just open the JIGGLER3.htm file in Control Pad and then select the Edit | Insert ActiveX Control menu command. This action activates the Insert ActiveX Controls dialog box (see Figure 30.11).
Figure 30.11 : Inserting the JIGGLER.OCX into
Select JIGGLER ActiveX Control from the list; the Control Pad opens an editing window similar to the one found in Visual Basic (see Figure 30.12). By setting the Caption, ForeColor, and BackColor properties in the Properties dialog box, you can visually set these stock OLE control properties.
Figure 30.12 : The ActiveX Control Pad Properties and Edit ActiveX Control windows.
NOTE |
If the JIGGLER control isn't on the Insert Control list, you'll need to manually register the control. You can do so in the Test Container application by choosing the File | Register Controls menu command |
Set the Caption property to ActiveX using the property editor. Next, visually set the BackColor property to the color white by double-clicking the BackColor property in the Properties window and choosing the white square from the resulting color-selection dialog box (see Figure 30.13). Set the ForeColor property to the color red using the same technique. The changes are immediately reflected in the Edit ActiveX Control window.
Figure 30.13 : Changing the BackColor property with the ActiveX Control Pad property editor.
When you're finished, close the Properties and Edit ActiveX Control windows. At this point, the Control Pad generates and inserts the code for the JIGGLER control into the HTML document. The code added by Control Pad is shown in Listing 30.11.
Listing 30.11. The basic JIGGLER control source code generated by the Control Pad.
<OBJECT ID="Jiggler1" WIDTH=157 HEIGHT=47 CLASSID="CLSID:5CD2FC83-E7BD-11CF-A3BC-444553540000"> <PARAM NAME="_Version" VALUE="65536"> <PARAM NAME="_ExtentX" VALUE="4154"> <PARAM NAME="_ExtentY" VALUE="1244"> <PARAM NAME="_StockProps" VALUE="11"> <PARAM NAME="Caption" VALUE="ActiveX"> <PARAM NAME="ForeColor" VALUE="255"> <PARAM NAME="BackColor" VALUE="50331647"> </OBJECT>
This code uses the World Wide Web Consortium (W3C) <OBJECT> HTML tag to describe the ActiveX control. The CLASSID on line 2 gives the JIGGLER control's CLSID, which Control Pad automatically looked up in the registry for you. This is a very convenient feature! Notice that the second line ends the <OBJECT> tag with a closing bracket (>) after the CLSID number. You'll add a new line inside the <OBJECT> tag that specifies the actual URL of the JIGGLER ActiveX control on your Web server.
The CODEBASE keyword lets IE3 find and download the control to a user's machine if the control doesn't exist on that machine already. For your purposes, assume that the control exists in a subdirectory of the directory in which your JIGGLER3.htm file lives. Let's call this directory ocx. This gives you the following relative CODEBASE:
CODEBASE = "./ocx/Jiggler.ocx">
The preceding CODEBASE line now ends the <OBJECT> tag, which gives IE3 everything it needs to know about where to find the control. The <OBJECT> tag is followed by several PARAM NAME lines that define the values for control properties. This change yields the following code, which fully describes the JIGGLER ActiveX object, named Jiggler1, in HTML:
<OBJECT ID="Jiggler1" WIDTH=157 HEIGHT=47 CLASSID="CLSID:5CD2FC83-E7BD-11CF-A3BC-444553540000" CODEBASE="./ocx/Jiggler.ocx"> <PARAM NAME="_Version" VALUE="65536"> <PARAM NAME="_ExtentX" VALUE="4154"> <PARAM NAME="_ExtentY" VALUE="1244"> <PARAM NAME="_StockProps" VALUE="11"> <PARAM NAME="Caption" VALUE="ActiveX"> <PARAM NAME="ForeColor" VALUE="255"> <PARAM NAME="BackColor" VALUE="50331647"> </OBJECT>
By simply copying this object definition and pasting two more copies into the document, you create a total of three JIGGLER controls on the page. Each control must have a unique name, so you must change the names of the pasted controls. Change the name of the second control from Jiggler1 to Jiggler2, and change the name of the third control from Jiggler1 to Jiggler3.
The Control Pad editor window displays buttons in the left margin that you can click to bring up the edit and property windows for each control (see Figure 30.14). Use these buttons to change the Caption and ForeColor properties of the second and third instances of the JIGGLER control. Set the Caption property for Jiggler2 to Text and the ForeColor to the color green (value 65280). Set the Caption property for Jiggler3 to Jiggling and the ForeColor to the color blue (value 16711680). There! A nice red, green, and blue trio of JIGGLER objects.
The Control Pad makes creating these definitions of your JIGGLER objects easy and convenient, but the objects aren't very flexible on their own. After all, an ActiveX control is, at its core, an OLE Automation server. To take programmatic control of the object in a Web page, you must use a scripting language. The next section uses VBScript for this example.
Now that the HTML code is in place for the control, you can write
some simple VBScript code to set JIGGLER property values
when the document is loaded into the IE3 browser window. Much
more can be done with VBScript, but let's keep it simple.
NOTE |
For the full documentation on VBScript, visit the Microsoft Web site. While you're there, check out the cool VBScript code samples. You can find this stuff at the following URL: http://www.microsoft.com/vbscrip |
To begin, you want to change the Interval property for each control, and you also want to change the DefaultFont properties for each control. This should be done when the page is first loaded into a browser. By selecting the Tools | ScriptWizard menu command in the Control Pad, you can generate a default script with the proper syntax already in place. To get an idea of how this tool works, follow these steps:
These steps result in the Script Wizard associating the value of 50 with the Interval property of the Jiggler1 object when the page is loaded, as you can see in Figure 30.15.
Figure 30.15 : Defining a basic script in the Control Pad Script Wizard.
Click OK at this point to generate the following VBScript code:
<SCRIPT LANGUAGE="VBScript"> <!-- Sub window_onLoad() Jiggler1.Interval = 50 end sub --> </SCRIPT>
Because the DefaultFont property uses the IFontDispatch interface, you can use the OLE Automation properties presented earlier in Table 30.3 to programmatically set font properties. The font properties you'll set are all IFontDispatch interface subproperties of the DefaultFont property, including Name, Size, Italic, and Bold. The following is an example using these properties in VBScript code:
Jiggler1.DefaultFont.Name = "Times New Roman" Jiggler1.DefaultFont.Size = 20 Jiggler1.DefaultFont.Italic = True Jiggler1.DefaultFont.Bold = True
As the final step, duplicate the VBScript code for the other two JIGGLER object instances, changing the names to Jiggler2 and Jiggler3 (of course). This gives you the final HTML source code found in Listing 30.12. You're finished! The JIGGLER3.htm Web page is ready to be loaded into IE3, and the three JIGGLER objects will jiggle away until the cows come home.
Listing 30.12. The complete source code for the final version of JIGGLER3.htm.
<HTML> <HEAD> <TITLE>Rob's Jiggler ActiveX Control</TITLE> </HEAD> <BODY BGCOLOR="#FFFFFF"> <H2><I>Rob's Jiggler ActiveX Control</I></H2> <HR> This is my new JIGGLER ActiveX control!! Pretty cool, eh? It was inspired by the "Nervous Text" Java applet, but this was totally redesigned from scratch and written in C++ for ActiveX! It supports many background and text colors, and it's easily extensible, so go for it! <P> <!-- Create 3 Jiggler ActiveX controls --> <OBJECT ID = "Jiggler1" WIDTH = 0 HEIGHT = 0 CLASSID = "CLSID:5CD2FC83-E7BD-11CF-A3BC-444553540000" CODEBASE = "./ocx/Jiggler.ocx"> <PARAM NAME = "_Version" VALUE = "65536"> <PARAM NAME = "_ExtentX" VALUE = "11933"> <PARAM NAME = "_ExtentY" VALUE = "1244"> <PARAM NAME = "_StockProps" VALUE = "11"> <PARAM NAME = "Caption" VALUE = "ActiveX"> <PARAM NAME = "ForeColor" VALUE = "255"> <PARAM NAME = "BackColor" VALUE = "16777215"> </OBJECT> <OBJECT ID = "Jiggler2" WIDTH = 0 HEIGHT = 0 CLASSID = "CLSID:5CD2FC83-E7BD-11CF-A3BC-444553540000" CODEBASE = "./ocx/Jiggler.ocx"> <PARAM NAME = "_Version" VALUE = "65536"> <PARAM NAME = "_ExtentX" VALUE = "11933"> <PARAM NAME = "_ExtentY" VALUE = "1244"> <PARAM NAME = "_StockProps" VALUE = "11"> <PARAM NAME = "Caption" VALUE = "Jiggling"> <PARAM NAME = "ForeColor" VALUE = "65280"> <PARAM NAME = "BackColor" VALUE = "16777215"> </OBJECT> <OBJECT ID = "Jiggler3" WIDTH = 0 HEIGHT = 0 CLASSID = "CLSID:5CD2FC83-E7BD-11CF-A3BC-444553540000" CODEBASE = "./ocx/Jiggler.ocx"> <PARAM NAME = "_Version" VALUE = "65536"> <PARAM NAME = "_ExtentX" VALUE = "11933"> <PARAM NAME = "_ExtentY" VALUE = "1244"> <PARAM NAME = "_StockProps" VALUE = "11"> <PARAM NAME = "Caption" VALUE = "Text"> <PARAM NAME = "ForeColor" VALUE = "16711680"> <PARAM NAME = "BackColor" VALUE = "16777215"> </OBJECT> <P> <!-- Program the controls with OLE Automation via VBScript --> <SCRIPT LANGUAGE="VBScript"> <!-- Sub window_onLoad() rem Set the properties for Jiggler1 Jiggler1.Interval = 100 Jiggler1.DefaultFont.Name = "Times New Roman" Jiggler1.DefaultFont.Size = 20 Jiggler1.DefaultFont.Italic = True Jiggler1.DefaultFont.Bold = True rem Set the properties for Jiggler2 Jiggler2.Interval = 75 Jiggler2.DefaultFont.Name = "Arial" Jiggler2.DefaultFont.Size = 24 Jiggler2.DefaultFont.Italic = True Jiggler2.DefaultFont.Bold = True rem Set the properties for Jiggler3 Jiggler3.Interval = 50 Jiggler3.DefaultFont.Name = "Courier New" Jiggler3.DefaultFont.Size = 30 Jiggler3.DefaultFont.Italic = True Jiggler3.DefaultFont.Bold = True end sub --> </SCRIPT> </BODY> </HTML>
Figure 30.16 shows the end result of all your hard work, with the JIGGLER3.htm file loaded in the IE3 browser window. (Take my word for it; the control text is red, green, and blue.)
ActiveX controls are at the forefront of new technology that's activating the Internet. These new controls are fun and exciting and, they bring the Web to life. Visual C++ 4.2 fully supports ActiveX controls and makes creating them fairly easy, thanks to the updated OLE Control Wizard.
Here are some points to remember: