Home Index  | Back

New Page 1
Lesson 1 -Building Your First Windows Application

1.1 : Creating a Windows Application

1.2 : Creating a Dialog-Based Application

1.3 : Creating DLLs, Console Applications, and More

1.4 : Changing Your AppWizard Decisions
 1.5 : Understanding AppWizard’s Code
 1.6 : Understanding a MDI Application
 1.7 : Understanding the Components of a Dialog-Based Application

Lesson 2 - Dialogs and Controls

2.1 : Understanding Dialog Boxes

2.2 : Creating a Dialog Box Resource

 2.3 : Writing a Dialog Box Class
 2.4 : Using the Dialog Box Class
Lesson 3 - Messages and Commands

3.1 : Understanding Message Routing

3.2 : Understanding Message Loops

 3.3 : Reading Message Maps
 3.4 : Learning How ClassWizard Helps You Catch Messages
 3.5 : Recognizing Messages
 3.6 : Understanding Commands
 3.7 : Understanding Command Updates
 3.8 : Learning How ClassWizard Helps You Catch Commands and Command Updates

Lesson 4 - Documents and Views

4.1 : Understanding the Document Class

4.2 : Understanding the View Class

4.3 : Creating the Rectangles Application

 4.4 : Other View Classes

4.5 : Document Templates, Views, and Frame Windows

Lesson 5 - Drawing on the Screen

5.1 :Understanding Device Contexts

 5.2 : Introducing the Paint1 Application
 5.3 : Building the Paint1 Application
 5.4 : Scrolling Windows
 5.5 : Building the Scroll Application
Lesson 6 - Building a Complete Application: ShowString

6.1 : Building an Application That Displays a String

 6.2 : Building the ShowString Menus
 6.3 : Building the ShowString Dialog Boxes
 6.4 : Making the Menu Work
 6.5 : Making the Dialog Box Work
 6.6 : Adding Appearance Options to the Options Dialog Box
Lesson 7 -  Status Bars and Toolbars

7.1 : Working with Toolbars

 7.2 : Working with Status Bars
Lesson 8 - Common Controls

8.1 : The Progress Bar Control

 8.2 : The Up-Down Control
 8.3 : The Image List Control
 8.4 : The List View Control
 8.5 : The Tree View Control
 8.6 : The Rich Edit Control
 8.7 : The Date Picker Control
 8.8 : Month Calendar Control
 8.9 : Scrolling the View
Lesson 9 - Property Pages and Sheets

9.1 : Introducing Property Sheets

 9.2 : Creating the Property Sheet Demo Application
 9.3 : Running the Property Sheet Demo Application
Lesson 10 - ActiveX Concepts

10.1 : The Purpose of ActiveX

10.2 : Object Linking

10.3 : Object Embedding

 10.4 : Containers and Servers
 10.5 : Toward a More Intuitive User Interface
 10.6 : The Component Object Model  

10.7 : Automation

 10.8 : ActiveX Controls

Lesson 11 -  Building an ActiveX Control

11.1 : Creating a Rolling-Die Control

11.2 : Displaying the Current Value

11.3 : Reacting to a Mouse Click and Rolling the Die 

 11.4 : Creating a Better User Interface
 11.5 : Generating Property Sheets
Lesson 12 - Database Access

12.1 : Understanding Database Concepts

12.2 : Creating an ODBC Database Program

 12.3 : Choosing Between ODBC and DAO
 12.4 : OLE DB

Lesson 11: Building an ActiveX Control

11.5 - Generating Property Sheets

ActiveX controls have property sheets that enable the user to set properties without any change to the container application. You set these up as dialog boxes, taking advantage of prewritten pages for font, color, and other common properties. For this control, the obvious properties to add are the following:

· A flag to indicate whether the value should be displayed as a digit or a dot pattern

· Foreground color

· Background color

11.5.1 Digits Versus Dots

It’s a simple enough matter to allow the user to choose whether to display the current value as a digit or a dot pattern. Simply add a property that indicates this preference and then use the property in OnDraw(). The user can set the property, using the property page.

First, add the property using ClassWizard. Here’s how: Bring up ClassWizard and select the Automation tab. Make sure that the CDierollCtrl class is selected and then click Add Property. On the Add Property dialog box, provide the external name Dots and the internal name m_dots. The type should be BOOL because Dots can be either TRUE or FALSE. Implement this new property as a member variable (direct-access) property. Click OK to complete the Add Property dialog box and click OK to close ClassWizard. The member variable is added to the class, the dispatch map is updated, and a stub is added for the notification function, OnDotsChanged().

To initialize Dots and arrange for it to be saved with a document, add the following line to DoPropExchange() after the call to PX_Short():

PX_Bool( pPX, "Dots", m_dots, TRUE);

Initializing the Dots property to TRUE ensures that the control’s default behavior is to display the dot pattern.

In OnDraw(), uncomment those lines that displayed the digit. Wrap an if around them so that the digit is displayed if m_dots is FALSE and dots are displayed if it is TRUE. The code looks like Listing 11.12.

Listing 11.12 DierollCtl.cpp—CDierollCtrl::OnDraw()

void CDierollCtrl::OnDraw(

CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)

{

pdc->FillRect(rcBounds,

CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)));

if (!m_dots)

{

CString val; //character representation of the short value

val.Format("%i",m_number);

pdc->ExtTextOut( 0, 0, ETO_OPAQUE, rcBounds, val, NULL );

}

else

{

//dots are 4 units wide and high, one unit from the edge

int Xunit = rcBounds.Width()/16;

int Yunit = rcBounds.Height()/16;

int Xleft = rcBounds.Width()%16;

int Yleft = rcBounds.Height()%16;

// adjust top left by amount left over

int Top = rcBounds.top + Yleft/2;

int Left = rcBounds.left + Xleft/2;

CBrush Black;

Black.CreateSolidBrush(RGB(0x00,0x00,0x00)); //solid black brush

CBrush* savebrush = pdc->SelectObject(&Black);

switch(m_number)

{

case 1:

...

}

pdc->SelectObject(savebrush);

}

}

To give the user a way to set Dots, you build a property page by following these steps:

1. Click the ResourceView tab in the Project Workspace window and then click the + next to Dialog.

2. The OCX has two dialog boxes: one for the About box and one for the property page. Double-click IDD_PROPPAGE_DIEROLL to open it. Figure 11.10 shows the boilerplate property page generated by AppWizard.

3. Remove the static control with the TODO reminder by highlighting it and pressing Delete.

4. Drag a check box from the Control Palette onto the dialog box. Choose View, Properties and then pin the Property dialog box in place.


FIG. 11.10 AppWizard generates an empty property page.

5. Change the caption to Display Dot Pattern and change the resource ID to IDC_DOTS, as shown in Figure 11.11.

FIG. 11.11 You build the property page for the die-roll control like any other dialog box.

When the user brings up the property page and clicks to set or unset the check box, that doesn’t directly affect the value of m_dots or the Dots property. To connect the dialog box to member variables, use ClassWizard and follow these steps:

1. Bring up ClassWizard while the dialog box is still open and on top, and then select the Member Variables tab.

2. Make sure that CDierollPropPage is the selected class and that the IDC_DOTS resource ID is highlighted, and then click the Add Variable button.

3. Fill in m_dots as the name and BOOL as the type, and fill in the Optional Property Name combo box with Dots, as shown in Figure 11.12.

4. Click OK, and ClassWizard generates code to connect the property page with the member variables in CDierollPropPage::DoDataExchange().

FIG. 11.12 You connect the property page to the properties of the control with ClassWizard.

The path that data follows can be a little twisty. When the user brings up the property sheet, the value of TRUE or FALSE is in a temporary variable. Clicking the check box toggles the value of that temporary variable. When the user clicks OK, that value goes into CDierollPropPage::m_dots and also to the Automation property Dots. That property has already been connected to CDierollCtrl::m_dots, so the dispatch map in CDierollCtrl will make sure that the other m_dots is changed. Because the OnDraw() function uses CDierollCtrl::m_dots, the control’s appearance changes in response to the change made by the user on the property page. Having the same name for the two member variables makes things more confusing to first-time control builders but less confusing in the long run.

This works now. Build the control and insert it into the test container. To change the properties, choose Edit, Dieroll Control Object, and Properties; your own property page will appear, as shown in Figure 11.13. (The Extended tab is provided for you, but as you can see, it doesn’t really do anything. Your General tab is the important one at the moment.) Prove to yourself that the control displays dots or a digit, depending on the page’s setting, by changing the setting, clicking OK, and then watching the control redraw.

When the control is displaying the value as a number, you might want to display that number in a font that’s more in proportion with the control’s current width and height and centered within the control. That’s a relatively simple modification to OnDraw(), which you can investigate on your own.


FIG. 11.13 The control test container displays your own property page.

11.5.2 User-Selected Colors

The die you’ve created will always have black dots on a white background, but giving the user control to change this is remarkably simple. You need a property for the foreground color and another for the background color. These have already been implemented as stock properties: BackColor and ForeColor.

Stock Properties Here is the complete list of stock properties available to a control that you write:

· Appearance. Specifies the control’s general look

· BackColor. Specifies the control’s background color

· BorderStyle. Specifies either the standard border or no border

· Caption. Specifies the control’s caption or text

· Enabled. Specifies whether the control can be used

· Font. Specifies the control’s default font

· ForeColor. Specifies the control’s foreground color

· Text. Also specifies the control’s caption or text

· hWnd. Specifies the control’s window handle

Ambient Properties Controls can also access ambient properties, which are properties of the environment that surrounds the control—that is, properties of the container into which you place the control. You can’t change ambient properties, but the control can use them to adjust its own properties. For example, the control can set its background color to match that of the container.

The container provides all support for ambient properties. Any of your code that uses an ambient property should be prepared to use a default value if the container doesn’t support that property. Here’s how to use an ambient property called UserMode:

BOOL bUserMode;

if( !GetAmbientProperty( DISPID_AMBIENT_USERMODE,

VT_BOOL, &bUserMode ) )

{

bUserMode = TRUE;

}

This code calls GetAmbientProperty() with the display ID (DISPID) and variable type (vartype) required. It also provides a pointer to a variable into which the value is placed. This variable’s type must match the vartype. If GetAmbientProperty() returns FALSE, bUserMode is set to a default value.

A number of useful DISPIDs are defined in olectl.h, including these:

  • DISPID_AMBIENT_BACKCOLOR

  • DISPID_AMBIENT_DISPLAYNAME

  • DISPID_AMBIENT_FONT

  • DISPID_AMBIENT_FORECOLOR

  • DISPID_AMBIENT_LOCALEID

  • DISPID_AMBIENT_MESSAGEREFLECT

  • DISPID_AMBIENT_SCALEUNITS

  • DISPID_AMBIENT_TEXTALIGN

  • DISPID_AMBIENT_USERMODE

  • DISPID_AMBIENT_UIDEAD

  • DISPID_AMBIENT_SHOWGRABHANDLES

  • DISPID_AMBIENT_SHOWHATCHING

  • DISPID_AMBIENT_DISPLAYASDEFAULT

  • DISPID_AMBIENT_SUPPORTSMNEMONICS

  • DISPID_AMBIENT_AUTOCLIP

  • DISPID_AMBIENT_APPEARANCE

Remember that not all containers support all these properties. Some might not support any, and still others might support properties not included in the preceding list.

The vartypes include those shown in Table 11.1.

Table 11.1 Variable Types for Ambient Properties

vartype Description
VT_BOOL BOOL
VT_BSTR CString
VT_I2 short
VT_I4 long
VT_R4 float
VT_R8 double
VT_CY CY
VT_COLOR OLE_COLOR
VT_DISPATCH

LPDISPATCH

VT_FONT LPFONTDISP

Remembering which vartype goes with which DISPID and checking the return from GetAmbientProperty() are a bothersome process, so the framework provides member functions of COleControl to get the most popular ambient properties:

· OLE_COLOR AmbientBackColor()

· CString AmbientDisplayName()

· LPFONTDISP AmbientFont() (Don’t forget to release the font by using Release().)

· OLE_COLOR AmbientForeColor()

· LCID AmbientLocaleID()

· CString AmbientScaleUnits()

· short AmbientTextAlign() (0 means general—numbers right, text left; 1 means left-justify; 2 means center; and 3 means right-justify.)

· BOOL AmbientUserMode() (TRUE means user mode; FALSE means design mode.)

· BOOL AmbientUIDead()

· BOOL AmbientShowHatching()

· BOOL AmbientShowGrabHandles()

All these functions assign reasonable defaults if the container doesn’t support the requested property.

Implementing BackColor and ForeColor To add BackColor and ForeColor to the control, follow these steps:

1. Bring up ClassWizard, and select the Automation tab.

2. Make sure that CDierollCtrl is the selected class, and click Add Property.

3. Choose BackColor from the top combo box, and the rest of the dialog box is filled out for you; it is grayed out to remind you that you can’t set any of these fields for a stock property. Figure 11.14 shows the values provided for you.


FIG. 11.14 ClassWizard describes stock properties for you.

4. Click OK and then add ForeColor in the same way. After you click OK, ClassWizard’s Automation tab will resemble Figure 11.15. The S next to these new properties reminds you that they are stock properties.

5. Click OK to close ClassWizard.

FIG. 11.15 An S precedes the stock properties in the OLE Automation list of properties and methods.

Setting up the property pages for these colors is almost as simple because there is a prewritten page that you can use. Look through DierollCtl.cpp for a block of code like Listing 11.13.

Listing 11.13 DierollCtl.cpp—Property Pages

///////////////////////////////////////////////

// Property pages

// TODO: Add more property pages as needed. Remember to increase the count!

BEGIN_PROPPAGEIDS(CDierollCtrl, 1)

PROPPAGEID(CDierollPropPage::guid)

END_PROPPAGEIDS(CDierollCtrl)

Remove the TODO reminder, change the count to 2, and add another PROPPAGEID so that the block looks like Listing 11.14.

Listing 11.14 DierollCtl.cpp—Property Pages

////////////////////////////////////////

// Property pages

BEGIN_PROPPAGEIDS(CDierollCtrl, 2)

PROPPAGEID(CDierollPropPage::guid)

PROPPAGEID(CLSID_CColorPropPage)

END_PROPPAGEIDS(CDierollCtrl)

CLSID_CColorPropPage is a class ID for a property page that is used to set colors. Now when the user brings up the property sheet, there will be two property pages: one to set colors and the general page that you already created. Both ForeColor and BackColor will be available on this page, so all that remains to be done is using the values set by the user. You will have a chance to see that very soon, but first, your code needs to use these colors.

Changes to OnDraw() In OnDraw(), your code can access the background color with GetBackColor(). Though you can’t see it, this function was added by ClassWizard when you added the stock property. The dispatch map for CDierollCtrl now looks like Listing 11.15.

Listing 11.15 DierollCtl.cpp—Dispatch Map

BEGIN_DISPATCH_MAP(CDierollCtrl, COleControl)

//{{AFX_DISPATCH_MAP(CDierollCtrl)

DISP_PROPERTY_NOTIFY(CDierollCtrl, "Number", m_number,

[ccc] OnNumberChanged, VT_I2)

DISP_PROPERTY_NOTIFY(CDierollCtrl, "Dots", m_dots,

[ccc] OnDotsChanged, VT_BOOL)

DISP_STOCKPROP_BACKCOLOR()

DISP_STOCKPROP_FORECOLOR()

//}}AFX_DISPATCH_MAP

DISP_FUNCTION_ID(CDierollCtrl, "AboutBox",

[ccc]DISPID_ABOUTBOX, AboutBox, VT_EMPTY, VTS_NONE)

END_DISPATCH_MAP()

The macro DISP_STOCKPROP_BACKCOLOR() expands to these lines:

#define DISP_STOCKPROP_BACKCOLOR() \

DISP_PROPERTY_STOCK(COleControl, "BackColor", \

DISPID_BACKCOLOR, COleControl::GetBackColor, \

COleControl::SetBackColor, VT_COLOR)

This code is calling another macro, DISP_PROPERTY_STOCK, which ends up declaring the GetBackColor() function as a member of CDierollCtrl, which inherits from COleControl. Although you can’t see it, this function is available to you. It returns an OLE_COLOR, which you translate to a COLORREF with TranslateColor(). You can pass this COLORREF to CreateSolidBrush() and use that brush to paint the background. Access the foreground color with GetForeColor() and give it the same treatment. (Use SetTextColor() in the digit part of the code.) Listing 11.16 shows the completed OnDraw() (with most of the switch statement cropped out).

Listing 11.16 DierollCtl.cpp—CDierollCtrl::OnDraw()

void CDierollCtrl::OnDraw(CDC* pdc, const CRect& rcBounds,

const CRect& rcInvalid)

{

COLORREF back = TranslateColor(GetBackColor());

CBrush backbrush;

backbrush.CreateSolidBrush(back);

pdc->FillRect(rcBounds, &backbrush);

if (!m_dots)

{

CString val; //character representation of the short value

val.Format("%i",m_number);

pdc->SetTextColor(TranslateColor(GetForeColor()));

pdc->ExtTextOut( 0, 0, ETO_OPAQUE, rcBounds, val, NULL );

}

else

{

//dots are 4 units wide and high, one unit from the edge

int Xunit = rcBounds.Width()/16;

int Yunit = rcBounds.Height()/16;

int Top = rcBounds.top;

int Left = rcBounds.left;

COLORREF fore = TranslateColor(GetForeColor());

CBrush forebrush;

forebrush.CreateSolidBrush(fore);

CBrush* savebrush = pdc->SelectObject(&forebrush);

switch(m_number)

{

...

}

pdc->SelectObject(savebrush);

}

}

Build the control again, insert it into the test container, and again bring up the property sheet by choosing Edit, Dieroll Control Object, Properties. As Figure 11.16 shows, the new property page is just fine for setting colors. Change the foreground and background colors a few times and experiment with both dots and digit display to exercise all your new code.


FIG. 11.16 Stock property pages make short work of letting the user set colors.

 

Next>>
 
© Dewsoft Overseas