📄 ch17.htm
字号:
<HTML>
<HEAD>
<TITLE>Special Edition Using Visual C++ 5 - Chapter 17</TITLE>
<LINK REL="Next" HREF="ch18.htm" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/ch18.htm">
<LINK REL="Previous" HREF="ch16.htm" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/ch16.htm"></HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<H2><B>Chapter 17</B></H2>
<H2><B>Building an ActiveX Control</B></H2>
<hr>
<P>ActiveX controls replace OLE controls, though the change affects more the name than anything else. (Much of the Microsoft documentation still refers to OLE controls.) The exciting behavior of these controls is powered by ActiveX, formerly known as
OLE. This chapter draws, in part, on the ActiveX work of the previous chapters. An ActiveX control is similar to an ActiveX Automation server, but an ActiveX control also exposes <I>events</I>, and those enable the control to direct the container’s
behavior.</P>
<P>ActiveX controls take the place that VBX controls held in 16-bit Windows programming, allowing programmers to extend the control set provided by the compiler. The original purpose of VBX controls was to allow programmers to provide their users with
unusual interface controls. Controls that looked like gas gauges or volume knobs became easy to develop. Almost immediately, however, VBX programmers moved beyond simple controls to modules that involved significant amounts of calculation and processing.
In the same way, many ActiveX controls are far more than just controls—they are <I>components</I> that can be used to build powerful applications quickly and easily.</P>
<ul>
<li> <B>Designing a Die-Roll Control</B></P>
<P> The sample application for this chapter rolls a die. This section describes the control and starts the building process.</P>
<li> <B>Displaying the Die</B></P>
<P> Drawing a control and adding a property to a control are explained in this section.</P>
<li> <B>Reacting to Clicks: Rolling the Die</B></P>
<P> ActiveX controls notify their containers of user activities with events. This section explains events and works you through the process of getting your control to roll a new number whenever a user clicks the control.</P>
<li> <B>Improving the User Interface</B></P>
<P> Adding more properties gives the user more flexibility. This section shows you how.</P>
<li> <B>Implementing Property Sheets</B></P>
<P> Adding property sheets to a control is quite simple, with much of the work already done for you. This section demonstrates the process.</P>
</ul>
<H3><B>A Rolling-Die Control</B></H3>
<P>The sample application for this chapter is a <I>die,</I> one of a pair of dice. Imagine a picture of a cubic die with the familiar pattern of dots indicating the current value, between 1 and 6. When the user clicks the picture, a new, randomly-chosen
number is shown. You might implement one or more dice into any game program.</P>
<P><B> Building the Control Shell</B></P>
<P>The process of building this die control starts, as always, with AppWizard. Start Developer Studio and then choose <U>F</U>ile, <U>N</U>ew. Click the Project tab, then click MFC ActiveX ControlWizard, which is in the list at the left of the dialog box;
fill in a project name at the top, choose an appropriate folder for the project files, and then click <U>OK</U>. Figure 17.1 shows the completed dialog box, with the project name <font color="#008000">Dieroll</font>.</P>
<blockquote><p><img src="note.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/note.gif">
<P>Even though the technology is now called ActiveX, many of the class names that are used throughout this chapter have <font color="#008000">Ole</font> in their names, and comments refer to OLE. Though Microsoft has changed the name of the technology, it
has not yet propagated that change throughout Visual C++. You will have to live with these contradictions until the next release of Visual C++.</P>
<p><img src="bottom.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/bottom.gif"></blockquote>
<A HREF="Sfigs01.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/figs/ch17/Sfigs01.gif"><b>Fig. 17.1</b></A>
<P><I>AppWizard makes creating an ActiveX control simple.</I></P>
<P>There are two steps in the ActiveX control wizard. Fill out the first dialog box as shown in Figure 17.2: you want one control, no runtime licensing, source-file comments, and no help files. After you have completed the dialog box, click
<U>N</U>ext.</P>
<A HREF="Sfigs02.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/figs/ch17/Sfigs02.gif"><b>Fig. 17.2</b></A>
<P><I>AppWizard's first step sets your control’s basic parameters.</I></P>
<blockquote><p><img src="sidebar.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/sidebar.gif">
<P ALIGN="CENTER">
<CENTER><I>Runtime Licensing</I></CENTER></P>
<P>Many developers produce controls as a product that they sell. Other programmers buy the rights to use such a control in their programs. Imagine that a developer, Alice, produces a fantastic die control and sells it to Bob, who incorporates it into the
best backgammon game ever. Carol buys the backgammon game and loves the die control, and she decides that it would be perfect for a children's board game she is planning. Since the DIEROLL.OCX file is in the backgammon package, there is nothing (other than
ethics) to stop her from doing this.</P>
<P>Runtime licensing is simple: There is a second file— DIEROLL.LIC—that contains the licensing information. Without that file, a control cannot be embedded into a form or program, though a program into which the control is already embedded will
work perfectly. Alice ships both DIEROLL.OCX and DIEROLL.LIC to Bob, but their licensing agreement states that only DIEROLL.OCX goes out with the backgammon game. Now Carol can admire DIEROLL.OCX, and it will work perfectly in the backgammon game, but if
she wants to include it in the game she builds, she'll have to buy a license from Alice.</P>
<P>You arrange for runtime licensing with AppWizard when you first build the control. If you decide, after the control is already built, that you should have asked for runtime licensing after all, build a new control with licensing and copy your changes
into that control.</P>
<p><img src="bottom.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/bottom.gif"></blockquote>
<P>The second and final AppWizard step allows you to set the new control’s features. Make sure that <font color="#008000"><U>A</U></font><font color="#008000">ctivates When Visible</font>, <font color="#008000">Available in "Insert </font><font
color="#008000"><U>O</U></font><font color="#008000">bject" </font><font color="#008000">Dialog</font>, and <font color="#008000">Has an "Abou</font><font color="#008000"><U>t</U></font><font color="#008000"> Box"</font> are selected, as
shown in Figure 17.3, and then click <U>F</U>inish. AppWizard summarizes your settings in a final dialog box. Click OK, and AppWizard creates 19 files for you and adds them to a project to make them easy to work with. These files are ready to compile, but
they don't do anything at the moment. You have an empty shell; it is up to you to fill it.</P>
<A HREF="Sfigs03.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/figs/ch17/Sfigs03.gif"><b>Fig. 17.3</b></A>
<P><I>AppWizard's second step governs the appearance and behavior of your control.</I></P>
<P><B>AppWizard's Code</B></P>
<P>Nineteen files sounds like a lot, but it isn't. There are only three classes: <font color="#008000">CDierollApp</font>, <font color="#008000">CDierollCtrl</font>, and <font color="#008000">CDierollPropPage</font>. They take up six files; the other 13
are the project file, make file, resource file, ClassWizard database, ODL file, and so on.</P>
<P><B><I>CDierollApp</I></B></P>
<p><font color="#008000">CDierollApp</font> is a very small class. It inherits from <font color="#008000">COleControlModule</font> and provides overrides of <font color="#008000">InitInstance()</font> and <font color="#008000">ExitInstance()</font> that
do nothing but call the base-class versions of these functions. This is where you find <font color="#008000">_tlid</font>, the external globally unique ID for your control, and some version numbers that make delivering upgrades of your control simpler. The
lines in Dieroll.cpp that set up these identifiers are the following:</P>
<pre><font color="#008000">const GUID CDECL BASED_CODE _tlid =</font></pre>
<pre><font color="#008000"> </font><font color="#008000"> </font><font color="#008000">{ </font><font color="#008000">0x914b21a5, 0x7946, 0x11d0, { 0x9b, 0x1, 0, 0x80, 0xc8, 0x1a, 0x39, 0x7c } };const WORD _wVerMajor </font><font color="#008000">=
1;</font></pre>
<pre><font color="#008000">const WORD _wVerMinor = 0;</font></pre>
<P><B><I>CDierollCtrl</I></B></P>
<P>The <font color="#008000">CDierollCtrl</font> class inherits from <font color="#008000">COleControl</font>, and it overrides the constructor and destructor, plus these four functions:</P>
<ul>
<li> <font color="#008000">OnDraw()</font> draws the control.</P>
<li> <font color="#008000">DoPropExchange()</font> implements persistence and initialization.</P>
<li> <font color="#008000">OnResetState()</font> causes the control to be reinitialized.</P>
<li> <font color="#008000">AboutBox()</font> displays the About box for the control.</P>
</ul>
<P>None of the code for these functions is particularly interesting. However, some of the maps that have been added to this class are interesting. There is an empty message map, which is ready to accept new entries, and an empty dispatch map, which is
ready for the properties and methods that you choose to expose.</P>
<blockquote><p><img src="tip.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/tip.gif">
<P>Message maps were explained in the "Message Maps" section of <A HREF="index04.htm" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/index04.htm" target="text">Chapter 4</A>, "Messages and Commands." Dispatch maps are discussed the "AppWizard's Automation Boilerplate" section in <A
HREF="index16.htm" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/index16.htm" target="text">Chapter 16</A>, "Building an Automation Server."</P>
<p><img src="bottom.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/bottom.gif"></blockquote>
<P>Below the empty message and dispatch maps comes a new map: the <I>event map</I>. The event map in the header file is shown in Listing 17.1, and the source file event map is shown in Listing 17.2.</P>
<P><I>Listing 17.1—excerpt from DierollCtl.h—</I><I><font color="#008000">Event map</font></I></P>
<pre><font color="#008000">// Event maps</font></pre>
<pre><font color="#008000"> //{{AFX_EVENT(CDierollCtrl)</font></pre>
<pre><font color="#008000"> // NOTE - ClassWizard will add and remove member functions here.</font></pre>
<pre><font color="#008000"> // DO NOT EDIT what you see in these blocks of generated code !</font></pre>
<pre><font color="#008000"> //}}AFX_EVENT</font></pre>
<pre><font color="#008000"> DECLARE_EVENT_MAP()</font></pre>
<P><I>Listing 17.2—excerpt from DierollCtl.cpp—</I><I><font color="#008000">Event map</font></I></P>
<pre><font color="#008000">BEGIN_EVENT_MAP(CDierollCtrl, COleControl)</font></pre>
<pre><font color="#008000"> //{{AFX_EVENT_MAP(CDierollCtrl)</font></pre>
<pre><font color="#008000"> // NOTE - ClassWizard will add and remove event map entries</font></pre>
<pre><font color="#008000"> // DO NOT EDIT what you see in these blocks of generated code !</font></pre>
<pre><font color="#008000"> //}}AFX_EVENT_MAP</font></pre>
<pre><font color="#008000">END_EVENT_MAP()</font></pre>
<P><I>Event maps,</I> like message maps and dispatch maps<I>,</I> link real-world happenings to your code. Message maps catch things the user does, such as choosing a menu item or clicking a button. They also catch messages sent from one part of an
application to another. Dispatch maps direct requests to access properties or invoke methods of an Automation server or ActiveX control. Event maps direct notifications from an ActiveX control to the application that contains the control (and are discussed
in more detail later in this chapter).</P>
<P>There's one more piece of code worth noting in DierollCtl.cpp. It is shown in Listing 17.3.</P>
<P><I>Listing 17.3—excerpt from DierollCtl.cpp—</I><I><font color="#008000">Property pages</font></I></P>
<pre><font color="#008000">/////////////////////////////////////////////////////////////////////////////</font></pre>
<pre><font color="#008000">// Property pages</font></pre>
<pre><font color="#008000">// TODO: Add more property pages as needed. Remember to increase the count!</font></pre>
<pre><font color="#008000">BEGIN_PROPPAGEIDS(CDierollCtrl, 1)</font></pre>
<pre><font color="#008000"> PROPPAGEID(CDierollPropPage::guid)</font></pre>
<pre><font color="#008000">END_PROPPAGEIDS(CDierollCtrl)</font></pre>
<P>The code in Listing 17.3 is part of the mechanism that implements powerful and intuitive property pages in your controls. That mechanism is discussed later in this chapter.</P>
<P><B><I>CDierollPropPage</I></B></P>
<P>The entire <font color="#008000">CDierollPropPage</font> class is the property of ClassWizard. Like any class with a dialog box in it, it has significant data exchange components. The constructor will initialize the dialog box fields using code added
by ClassWizard. This code is shown in Listing 17.4.</P>
<P><I>Listing 17.4—DierollPpg.cpp—CDierollPropPage::CDierollPropPage()</I></P>
<pre><font color="#008000">CDierollPropPage::CDierollPropPage() :</font></pre>
<pre><font color="#008000"> COlePropertyPage(IDD, IDS_DIEROLL_PPG_CAPTION)</font></pre>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -