📄 ch17.htm
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
<HEAD>
<SCRIPT LANGUAGE="JavaScript">
<!--
function popUp(pPage) {
var fullURL = document.location;
var textURL = fullURL.toString();
var URLlen = textURL.length;
var lenMinusPage = textURL.lastIndexOf("/");
lenMinusPage += 1;
var fullPath = textURL.substring(0,lenMinusPage);
popUpWin = window.open('','popWin','resizable=yes,scrollbars=no,width=525,height=394');
figDoc= popUpWin.document;
zhtm= '<HTML><HEAD><TITLE>' + pPage + '</TITLE>';
zhtm += '<link rel="stylesheet" href="/includes/stylesheets/ebooks.css"></head>';
zhtm += '<BODY bgcolor="#FFFFFF">';
zhtm += '<IMG SRC="' + fullPath + pPage + '">';
zhtm += '<P><B>' + pPage + '</B>';
zhtm += '</BODY></HTML>';
window.popUpWin.document.write(zhtm);
window.popUpWin.document.close();
// Johnny Jackson 4/28/98
}
//-->
</SCRIPT>
<link rel="stylesheet" href="/includes/stylesheets/ebooks.css">
<TITLE>Special Edition Using Visual C++ 6 -- Ch 17 -- Building an ActiveX Control</TITLE>
</HEAD>
<BODY TEXT="#000000" BGCOLOR="#FFFFFF">
<CENTER>
<H1><IMG SRC="../button/que.gif" WIDTH="171" HEIGHT="66" ALIGN="BOTTOM" BORDER="0"><BR>
Special Edition Using Visual C++ 6</H1>
</CENTER>
<CENTER>
<P><A HREF="../ch16/ch16.htm"><IMG SRC="../button/previous.gif" WIDTH="128" HEIGHT="28"
ALIGN="BOTTOM" ALT="Previous chapter" BORDER="0"></A><A HREF="../ch18/ch18.htm"><IMG
SRC="../button/next.gif" WIDTH="128" HEIGHT="28" ALIGN="BOTTOM" ALT="Next chapter"
BORDER="0"></A><A HREF="../index.htm"><IMG SRC="../button/contents.gif" WIDTH="128"
HEIGHT="28" ALIGN="BOTTOM" ALT="Contents" BORDER="0"></A>
<HR>
</CENTER>
<CENTER>
<H1>- 17 -</H1>
</CENTER>
<CENTER>
<H1>Building an ActiveX Control</H1>
</CENTER>
<UL>
<LI><A HREF="#Heading1">Creating a Rolling-Die Control</A>
<UL>
<LI><A HREF="#Heading2">Building the Control Shell</A>
<LI><A HREF="#Heading3">AppWizard's Code</A>
<LI><A HREF="#Heading4">Designing the Control</A>
</UL>
<LI><A HREF="#Heading5">Displaying the Current Value</A>
<UL>
<LI><A HREF="#Heading6">Adding a Property</A>
<LI><A HREF="#Heading7">Writing the Drawing Code</A>
</UL>
<LI><A HREF="#Heading8">Reacting to a Mouse Click and Rolling the Die</A>
<UL>
<LI><A HREF="#Heading9">Notifying the Container</A>
<LI><A HREF="#Heading10">Rolling the Die</A>
</UL>
<LI><A HREF="#Heading11">Creating a Better User Interface</A>
<UL>
<LI><A HREF="#Heading12">A Bitmap Icon</A>
<LI><A HREF="#Heading13">Displaying Dots</A>
</UL>
<LI><A HREF="#Heading14">Generating Property Sheets</A>
<UL>
<LI><A HREF="#Heading15">Digits Versus Dots</A>
<LI><A HREF="#Heading16">User-Selected Colors</A>
</UL>
<LI><A HREF="#Heading17">Rolling on Demand</A>
<LI><A HREF="#Heading18">Future Improvements</A>
<UL>
<LI><A HREF="#Heading19">Enable and Disable Rolling</A>
<LI><A HREF="#Heading20">Dice with Unusual Numbers of Sides</A>
<LI><A HREF="#Heading21">Arrays of Dice</A>
</UL>
</UL>
<P>
<HR SIZE="4">
<CENTER>
<H1></H1>
</CENTER>
<H2><A NAME="Heading1"></A>Creating a Rolling-Die Control</H2>
<P>ActiveX controls replace OLE controls, though the change affects the name more
than anything else. (Much of the Microsoft documentation still refers to OLE controls.)
The exciting behavior of these controls is powered by COM (the Component Object Model),
which also powers OLE. This chapter draws, in part, on the work of the previous chapters.
An ActiveX control is similar to an Automation server, but an ActiveX control also
exposes events, 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,
enabling programmers to extend the control set provided by the compiler. The original
purpose of VBX controls was to enable programmers to provide their users with unusual
interface controls. Controls that look 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 components
that can be used to build powerful applications quickly and easily.</P>
<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 use one or more dice in any game program.</P>
<P>
<H3><A NAME="Heading2"></A>Building the Control Shell</H3>
<P>The process of building this die control starts, as always, with AppWizard. Begin
Developer Studio and then choose File, New. Click the Projects tab and 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 click OK. Figure 17.1 shows the completed dialog box, with the project name <I>Dieroll</I>.</P>
<P><A HREF="javascript:popUp('17uvc01.gif')"><B>FIG. 17.1</B></A><B> </B><I>AppWizard
makes creating an ActiveX control simple.</I></P>
<BLOCKQUOTE>
<P>
<HR>
<strong>NOTE:</strong> Even though the technology is now called ActiveX, many classnames
used throughout this chapter have Ole in their names, and comments refer to OLE.
Though Microsoft has changed the technology's name, 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++.
<HR>
</BLOCKQUOTE>
<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 Next.</P>
<P><A HREF="javascript:popUp('17uvc02.gif')"><B>FIG. 17.2</B></A><B> </B><I>AppWizard's
first step sets your control's basic parameters.</I></P>
<BLOCKQUOTE>
<P>
<HR>
<B>Runtime Licensing</B> </P>
<P>Many developers produce controls as a salable product. Other programmers buy the
rights to use such controls 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. Because the
DIEROLL.OCX file is in the backgammon package, there is nothing (other than ethics)
to stop her from doing this.<BR>
</P>
<P>Runtime licensing is simple: There is a second file, DIEROLL.LIC, that contains
the licensing information. Without that file, a control can't 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.
<HR>
</BLOCKQUOTE>
<P>The second and final AppWizard step enables you to set the new control's features.
Make sure that Activates When Visible, Available in "Insert Object" Dialog,
and Has an "About Box" are selected, as shown in Figure 17.3, and then
click Finish. 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's up to you to fill it.</P>
<P><A HREF="javascript:popUp('17uvc03.gif')"><B>FIG. 17.3</B></A><B> </B><I>AppWizard's
second step governs your control's appearance and behavior.</I></P>
<H3><B></B></H3>
<H3><A NAME="Heading3"></A>AppWizard's Code</H3>
<P>Nineteen files sound like a lot, but they aren't. There are only three classes:
CDierollApp, CDierollCtrl, and CDierollPropPage. 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>CDierollApp is a very small class. It inherits
from COleControlModule and provides overrides of InitInstance() and ExitInstance()
that do nothing but call the base-class versions of these functions. This is where
you find _tlid, 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>
<P>
<PRE>const GUID CDECL BASED_CODE _tlid =
{ 0x914b21a5, 0x7946, 0x11d0, { 0x9b, 0x1, 0, 0x80,
0xc8, 0x1a, 0x39, 0x7c } };
const WORD _wVerMajor = 1;
const WORD _wVerMinor = 0;
</PRE>
<P><B><I>CDierollCtrl  </I></B>The CDierollCtrl class inherits from COleControl,
and it has a constructor and destructor, plus overrides for these four functions:</P>
<UL>
<LI>OnDraw() draws the control.
<P>
<LI>DoPropExchange() implements persistence and initialization.
<P>
<LI>OnResetState() causes the control to be reinitialized.
<P>
<LI>AboutBox() displays the About box for the control.
</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 of interest. There is an empty
message map, ready to accept new entries, and an empty dispatch map, ready for the
properties and methods that you choose to expose.</P>
<BLOCKQUOTE>
<P>
<HR>
<strong>TIP:</strong> Message maps are explained in the "Message Maps" section
of Chapter 3, "Messages and Commands." Dispatch maps are discussed in the
"AppWizard's Automation Boilerplate" section in Chapter 16, "Building
an Automation Server."
<HR>
</BLOCKQUOTE>
<P>Below the empty message and dispatch maps comes a new map: the event map. Listing
17.1 shows the event map in the header file, and the source file event map is shown
in Listing 17.2.</P>
<P>
<H4>Listing 17.1  Excerpt from DierollCtl.h--Event Map</H4>
<PRE>// Event maps
//{{AFX_EVENT(CDierollCtrl)
// NOTE - ClassWizard will add and remove member functions here.
// DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_EVENT
</PRE>
<PRE> DECLARE_EVENT_MAP()
</PRE>
<H4>Listing 17.2  Excerpt from DierollCtl.cpp--Event Map</H4>
<PRE>BEGIN_EVENT_MAP(CDierollCtrl, COleControl)
//{{AFX_EVENT_MAP(CDierollCtrl)
// NOTE - ClassWizard will add and remove event map entries
// DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_EVENT_MAP
</PRE>
<PRE>END_EVENT_MAP()
</PRE>
<P><I>Event maps,</I> like message maps and dispatch maps<I>,</I> link real-world
happenings to your code. <I>Message</I> <I>maps</I> 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. <I>Dispatch</I> <I>maps</I> 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 appears in Listing
17.3.</P>
<P>
<H4>Listing 17.3  Excerpt from DierollCtl.cpp--Property Pages</H4>
<PRE>/////////////////////////////////////////////////////////////////////////////
// Property pages
// TODO: Add more property pages as needed. Remember to increase the count!
BEGIN_PROPPAGEIDS(CDierollCtrl, 1)
PROPPAGEID(CDierollPropPage::guid)
</PRE>
<PRE>END_PROPPAGEIDS(CDierollCtrl)
</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>The entire CDierollPropPage class is
the domain 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. Listing 17.4 shows this code.</P>
<P>
<H4>Listing 17.4  DierollPpg.cpp--CDierollPropPage::CDierollPropPage()</H4>
<PRE>CDierollPropPage::CDierollPropPage() :
COlePropertyPage(IDD, IDS_DIEROLL_PPG_CAPTION)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -