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).
Figure 11.1. BullsEye custom property page
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).
Figure 11.2. BullsEye color property page
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).
Figure 11.3. Visual Basic 6 property view window for BullsEye
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.
Figure 11.4. BullsEye About box
Requirements: The Properties and Methods
BullsEye supports the four stock properties shown in Table 11.1.
In addition, BullsEye supports all three stock methods, as shown in Table 11.2.
Finally, BullsEye supports the custom properties listed in Table 11.3.
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
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.
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
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
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