The BullsEye
Control Requirements
This chapter describes the ATL implementation of
a feature-rich control called BullsEye. The
BullsEye control implements all the previously described
features. The BullsEye control draws a bull's eye. You can
configure the number of rings in the bull's eye (from one to nine)
and the color of the center ring, as well as the color of the ring
adjacent to the center (called the alternate ring color).
BullsEye draws additional rings by alternately using the
center and alternate colors.
The area around the bull's eye can be
transparent or opaque. When transparent, the background around the
bull's eye shows through. When opaque, the bull's eye fills the
area around the circle using the background color. By default,
BullsEye uses the container's ambient background color as
the background color. BullsEye also uses the foreground
color to draw a line separating each ring
You can assign score values to each ring. By
default, the center ring is worth 512 points and each other ring is
worth half the points of its adjacent inner ring. When a user
clicks on a ring, the control fires an OnRingHit event and
an OnScoreChanged event. The argument to the
OnRingHit event method specifies the ring upon which the
user clicked. Rings are numbered from 1 to N, where 1 is the centermost ring. The
OnScoreChanged event specifies the point value of the
clicked ring. For example, clicking on ring 2 with default scores
fires an OnScoreChanged event with an argument of 256
points.
In addition, when you click on one of the
bull's-eye rings, the control can provide feedback by playing a
sound. By default, you hear the sound of an arrow striking the
bull's eye. The Boolean Beep property, when set to
trUE, indicates that the control should play its sound on
a ring hit.
BullsEye supports all standard control
functionality. In addition to windowed activation,
BullsEye can be activated as a windowless control when its
container supports such functionality.
Many containers ask their controls to save their
state using the IPersistStreamInit interface and an
IStream medium. When embedding a control in an
OLE document, a container asks a control to save its state using
the IPersistStorage interface and the IStorage
medium. A container, such as Internet Explorer and Visual Basic,
that prefers to save the state of a control as textual name/value
pairs uses the control's IPersistPropertyBag interface and
the IPropertyBag medium. BullsEye supports all
three persistence protocols and mediastreams, storages, and
property bags.
BullsEye also provides two property
pages. One property page is custom to the BullsEye control
and enables you to set the Enabled, Beep (sound
on ring hit), and BackStyle (TRansparent) properties (see
Figure 11.1).
The other property page is the standard
color-selection property page (see Figure 11.2). The BullsEye control has
four color properties: the center ring color, the alternate ring
color, the background color (used to fill the area around the
bull's eye) and the foreground color (used to draw the separator
line between rings).
The BullsEye control also categorizes
its properties for Visual Basic 6. VB6 has a property-view window
in which you can select a view that sorts the properties by
standard and control-defined categories (see Figure 11.3).
The BullsEye
control lists its color properties and the RingCount
property in the standard Appearance category. The control lists its
Beep property in the standard Behavior category.
BullsEye also supports per-property browsing, which allows a control
to specify a list of strings that a container should display as the
available choices for a property's value. Notice in the example
Visual Basic property view window that, in the Behavior category,
Visual Basic displays the strings "Yes, make noise" and "No, be
mute" as the selections available for the Beep
property.
Also notice that the Misc category contains an
entry called (About) that represents the AboutBox
stock method. BullsEye displays the dialog box shown in
Figure 11.4 when the user
selects the About entry.
Requirements: The
Properties and Methods
BullsEye supports the four stock
properties shown in Table
11.1.
Table 11.1. BullsEye Stock
Properties
Property Name
|
Type
|
Stock DISPID
|
Description
|
BackColor
|
OLE_COLOR
|
DISPID_BACKCOLOR
|
Background color
|
BackStyle
|
Long
|
DISPID_BACKSTYLE
|
Background style, transparent or opaque
|
Enabled
|
VARIANT_BOOL
|
DISPID_ENABLED
|
Enabled status, trUE or
FALSE
|
ForeColor
|
OLE_COLOR
|
DISPID_FORECOLOR
|
Foreground color
|
In addition, BullsEye supports all
three stock methods, as shown in Table 11.2.
Table 11.2. BullsEye Stock
Methods
Method Name
|
Stock DISPID
|
Description
|
AboutBox
|
DISPID_ABOUTBOX
|
Displays the control's Help About dialog box
|
DoClick
|
DISPID_DOCLICK
|
Simulates a mouse click on the control
|
Refresh
|
DISPID_REFRESH
|
Redraws the control
|
Finally, BullsEye supports the custom
properties listed in Table
11.3.
Table 11.3. BullsEye Custom
Properties
Property Name
|
Type
|
Custom DISPID
|
Description
|
Application
|
IDispatch*
|
DISPID_APPLICATION
|
Returns the IDispatch* for the hosting
application
|
AlternateColor
|
OLE_COLOR
|
DISPID_ALTERNATECOLOR
|
Gets/sets the color of the alternate (even)
rings
|
Beep
|
VARIANT_BOOL
|
DISPID_BEEP
|
Enables/disables sound effects for the
control
|
CenterColor
|
OLE_COLOR
|
DISPID_CENTERCOLOR
|
Gets/sets the color of the center (odd)
rings
|
Parent
|
IDispatch*
|
DISPID_PARENT
|
Returns the IDispatch* for the
control's parent
|
RingCount
|
Short
|
DISPID_RINGCOUNT
|
Gets/sets the number of rings
|
RingValue
|
Long
|
DISPID_RINGVALUE
|
Gets/sets the value of each ring
|
Declaring the
Properties and Methods in IDL
A control container accesses the properties and
methods of a control using the control's IDispatch
interface. A control must therefore provide an implementation of
IDispatch when it has properties and methods.
ATL-based controls in generaland the
BullsEye control, specificallyimplement their properties
and methods using a dual interface, not a dispatch interface, even
though a dual interface is unnecessary because the vtable
portion of the dual interface typically goes unused. A custom C++
control container can access the control's properties and methods
using the vtable, but no other container currently does.
Visual Basic 6 accesses properties and methods of a control using
the control's IDispatch interface. Visual Basic uses the
vtable portion of a dual interface only for noncontrol
objects.
The BullsEye control provides access to
its properties and methods on the default IBullsEye dual
interface. When you generate a new ATL-based control class, the wizard generates
the definition of the default dual interface, but you must populate
the definition with the accessor methods for your control's
properties and the control's methods. Listing 11.1 gives the definition of the
IBullsEye interface.
Listing 11.1. The
IBullsEye Interface
[ object,
uuid(B4FBD008-B03D-4F48-9C5B-4A981EB6A515),
dual, nonextensible, helpstring("IBullsEye Interface"),
pointer_default(unique)
]
interface IBullsEye : IDispatch {
const int DISPID_ALTERNATECOLOR = 1;
const int DISPID_BEEP = 2;
const int DISPID_CENTERCOLOR = 3;
const int DISPID_RINGCOUNT = 4;
const int DISPID_RINGVALUE = 5;
const int DISPID_APPLICATION = 6;
const int DISPID_PARENT = 7;
// Stock Properties
[propput, bindable, requestedit, id(DISPID_BACKCOLOR)]
HRESULT BackColor([in]OLE_COLOR clr);
[propget, bindable, requestedit, id(DISPID_BACKCOLOR)]
HRESULT BackColor([out,retval]OLE_COLOR* pclr);
[propput, bindable, requestedit, id(DISPID_BACKSTYLE)]
HRESULT BackStyle([in]long style);
[propget, bindable, requestedit, id(DISPID_BACKSTYLE)]
HRESULT BackStyle([out,retval]long* pstyle);
[propput, bindable, requestedit, id(DISPID_FORECOLOR)]
HRESULT ForeColor([in]OLE_COLOR clr);
[propget, bindable, requestedit, id(DISPID_FORECOLOR)]
HRESULT ForeColor([out,retval]OLE_COLOR* pclr);
[propput, bindable, requestedit, id(DISPID_ENABLED)]
HRESULT Enabled([in]VARIANT_BOOL vbool);
[propget, bindable, requestedit, id(DISPID_ENABLED)]
HRESULT Enabled([out,retval]VARIANT_BOOL* pbool);
// Stock methods
[id(DISPID_ABOUTBOX)] HRESULT AboutBox( );
[id(DISPID_DOCLICK)] HRESULT DoClick( );
[id(DISPID_REFRESH)] HRESULT Refresh( );
// Custom properties
[propget, bindable, requestedit, id(DISPID_APPLICATION)]
HRESULT Application([out, retval] IDispatch** pVal);
[propget, bindable, requestedit, id(DISPID_ALTERNATECOLOR)]
HRESULT AlternateColor([out, retval] OLE_COLOR* pVal);
[propput, bindable, requestedit, id(DISPID_ALTERNATECOLOR)]
HRESULT AlternateColor([in] OLE_COLOR newVal);
[propget, bindable, requestedit, id(DISPID_BEEP)]
HRESULT Beep([out, retval] VARIANT_BOOL* pVal);
[propput, bindable, requestedit, id(DISPID_BEEP)]
HRESULT Beep([in] VARIANT_BOOL newVal);
[propget, bindable, requestedit, id(DISPID_CENTERCOLOR)]
HRESULT CenterColor([out, retval] OLE_COLOR* pVal);
[propput, bindable, requestedit, id(DISPID_CENTERCOLOR)]
HRESULT CenterColor([in] OLE_COLOR newVal);
[propget, bindable, requestedit, id(DISPID_PARENT)]
HRESULT Parent([out, retval] IDispatch** pVal);
[propget, bindable, requestedit, id(DISPID_RINGCOUNT)]
HRESULT RingCount([out, retval] SHORT* pVal);
[propput, bindable, requestedit, id(DISPID_RINGCOUNT)]
HRESULT RingCount([in] SHORT newVal);
[propget, bindable, requestedit, id(DISPID_RINGVALUE)]
HRESULT RingValue([in] SHORT sRingNumber,
[out, retval] LONG* pVal);
[propput, bindable, requestedit, id(DISPID_RINGVALUE)]
HRESULT RingValue([in] SHORT sRingNumber, [in] LONG newVal);
};
|
Requirements: The
Events
BullsEye Custom
Events
The BullsEye
control doesn't support any of the stock events. However, it has
two custom events, as detailed in Table 11.4.
Table 11.4. BullsEye Custom
Events
Event
|
Event DISPID
|
Description
|
void OnRingHit (short sRingNumber)
|
DISPID_ONRINGHIT
|
The user clicked on one of the bull's-eye rings.
The argument specifies the ring that the user clicked. Rings are
numbers from 1 to N from the
center outward.
|
void OnScoreChanged (long RingValue)
|
DISPID_ONSCORECHANGED
|
This event follows the OnRingHit event
when the user clicks on a bull's-eye ring. The argument specifies
the score value of the ring that the user clicked.
|
An event interface contains only methods and
should be a dispatch interface for all containers to receive the
event callbacks. Some containers, such as Visual Basic, can receive
event callbacks on custom IUnknown-derived interfaces. An
event interface should never be a dual interface.
Declaring the
Event Dispatch Interface in IDL
Listing
11.2 gives the definition of the _IBullsEyeEvents
dispatch interface. For the constants for the DISPIDs to
appear in the MIDL-generated C/C++ header file, the definitions of
the constants must appear in the IDL file outside of the library
block. You must define the dispinterface itself inside the
library block.
Listing 11.2. The
_IBullsEyeEvents Dispatch Interface
const int DISPID_ONRINGHIT = 1;
const int DISPID_ONSCORECHANGED = 2;
[ uuid(58D6D8CB-765D-4C59-A41F-BBA8C40F7A14),
helpstring("Event interface for BullsEye Control")
]
dispinterface _IBullsEyeEvents {
properties:
methods:
[id(DISPID_ONRINGHIT)]
void OnRingHit(short ringNumber);
[id(DISPID_ONSCORECHANGED)]
void OnScoreChanged(long ringValue);
};
|
Requirements: The
BullsEye and Property Page Coclasses
You must also define the
BullsEye coclass in the library block of the IDL file (see
Listing 11.3). At a
minimum, you must specify the default IDispatch interface
(IBullsEye) via which a container can access the control's
properties and methods, and the default source interface
(_IBullsEyeEvents) tHRough which the BullsEye
control fires events to its container.
Listing 11.3. The
BullsEye Coclass
[ uuid(E9312AF5-1C11-4BA4-A0C6-CB660E949B78),
control, helpstring("BullsEye Class")
]
coclass BullsEye {
[default] interface IBullsEye;
[default, source] dispinterface _IBullsEyeEvents;
};
|
Additionally, you should define all the custom
property page classes implemented by your control in the library
block of the IDL file. BullsEye has only one custom
property page, called BullsEyePropPage (see Listing 11.4).
Listing 11.4. The
BullsEyePropPage Coclass
[ uuid(47446235-8500-43a2-92A7-F0686FDAA69C),
helpstring("BullsEye Property Page class")
]
coclass BullsEyePropPage {
interface IUnknown;
};
|
|