Programming Microsoft Windows Ce Net 3Rd [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Programming Microsoft Windows Ce Net 3Rd [Electronic resources] - نسخه متنی

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید






Windows Controls

Were it not for the Windows Control library, programming Windows applications would be a slow and arduous process. In addition, every application would have its own look and feel. This would force the user to learn a new way of working with each new application. Fortunately, this scenario is avoided with an assortment of controls that the operating system provides. In short, controls are simply predefined window classes. Each has a custom window procedure supplied by Windows that gives each of these controls a tightly defined user and programming interface.

Since a control is just another window, it can be created with a call to CreateWindow or CreateWindowEx. Controls notify the parent window of events via WM_COMMAND messages encoding events and the ID and window handle of the control encoded in the parameters of the message.

Like all messages, WM_COMMAND contains two generic parameters, wParam and lParam. For a WM_COMMAND message, the high word of wParam contains the notification code, the reason for the WM_COMMAND message being sent. The low word of wParam contains the ID value of the control that sent the message. The ID is a word that's typically defined when the control is created and, to be useful, should be unique among all the sibling windows of the control. The lParam value contains the handle of the child window that sent the control. In general, it's easier to track the source of a WM_COMMAND message though the control ID rather than the window handle of the control, but both are available in the message. The following code is typical of the first few lines of a WM_COMMAND handler:

case WM_COMMAND:
WORD idItem, wNotifyCode;
HWND hwndCtl;
// Parse the parameters.
idItem = (WORD) LOWORD (wParam);
wNotifyCode = (WORD) HIWORD(wParam);
hwndCtl = (HWND) lParam;

From this point, the WM_COMMAND handler typically uses the ID of the control and then uses the notification code to determine why the WM_COMMAND message was sent.

Controls can also be configured and manipulated using predefined messages sent to the control. Among other things, applications can set the state of buttons, add items to or delete items from list boxes, and set the selection of text in edit boxes, all by sending messages to the controls. Controls are typically indentified by their ID, but many Windows functions require the handle of the control. The GetDlgItem function provides a simple conversion. The function is prototyped as

HWND GetDlgItem (HWND hDlg, int nIDDlgItem);

The two parameters are the handle of the parent window of the control and the ID value for the control. Although the name implies that the function can be used only in dialog boxes, something I'll talk about in Chapter 6, it works quite fine for a control in any window.

Another convenient function to send a message to a control is SendDlgItemMessage. This function sends a message to a child window with a specific ID. The prototype of the message is shown here:

LONG SendDlgItemMessage (HWND hParent, int nIDChild, UINT Msg, 
WPARAM wParam, LPARAM lParam);

The parameters are similar to those for SendMessage. In fact, the following code is functionally identical to that of SendDlgItemMessage:

LONG SendMessage (GetDlgItem (hParent, nIDChild), Msg, wParam, lParam);

The only difference is the convenience of not having to embed the GetDlgItem call within SendMessage.

There are six predefined window control classes. They are



ButtonA wide variety of buttons



EditA window that can be used to enter or display text



ListA window that contains a list of strings



ComboA combination edit box and list box



StaticA window that displays text or graphics that a user can't change



Scroll barA scroll bar not attached to a specific window



Each of these controls has a wide range of function, far too much for me to cover completely in this chapter. But I'll quickly review these controls, mentioning at least the highlights. Afterward, I'll show you an example program, CtlView, to demonstrate these controls and their interactions with their parent windows.


Button Controls


Button controls enable several forms of input to the program. Buttons come in many styles, including push buttons, check boxes, and radio buttons. Each style is designed for a specific use—for example, push buttons are designed for receiving momentary input, check boxes are designed for on/off input, and radio buttons allow a user to select one of a number of choices.

Push Buttons


In general, push buttons are used to invoke some action. When a user presses a push button using a stylus, the button sends a WM_COMMAND message with a BN_CLICKED (for button notification clicked) notify code in the high word of the wParam parameter.

Check Boxes


Check boxes display a square box and a label that asks the user to specify a choice. A check box retains its state, either checked or unchecked, until the user clicks it again or the program forces the button to change state. In addition to the standard BS_CHECKBOX style, check boxes can come in a three-state style, BS_3STATE, that allows the button to be disabled and shown grayed out. Two additional styles, BS_AUTOCHECKBOX and BS_AUTO3STATE, automatically update the state and look of the control to reflect the checked, the unchecked, and, in the case of the three-state check box, the disabled state.

As with push buttons, check boxes send a BN_CLICKED notification when the button is clicked. Unless the check box has one of the automatic styles, it's the responsibility of the application to manually change the state of the button. This can be done by sending a BM_SETCHECK message to the button with the wParam set to 0 to uncheck the button or 1 to check the button. The three-state check boxes have a third, disabled, state that can be set by means of the BM_SETCHECK message with the wParam value set to 2. An application can determine the current state using the BM_GETCHECK message.

Radio Buttons


Radio buttons allow a user to select from a number of choices. Radio buttons are grouped in a set, with only one of the set ever being checked at a time. If it's using the standard BS_RADIOBUTTON style, the application is responsible for checking and unchecking the radio buttons so that only one is checked at a time. However, like check boxes, radio buttons have an alternative style, BS_AUTORADIOBUTTON, that automatically maintains the group of buttons so that only one is checked.

Group Boxes


Strangely, the group box is also a type of button. A group box appears to the user as a hollow box with an integrated text label surrounding a set of controls that are naturally grouped together. Group boxes are merely an organizational device and have no programming interface other than the text of the box, which is specified in the window title text upon creation of the group box. Group boxes should be created after the controls within the box are created. This ensures that the group box will be "beneath" the controls it contains in the window Z-order.

You should also be careful when using group boxes on Windows CE devices. The problem isn't with the group box itself, but with the small size of the Windows CE screen. Group boxes take up valuable screen real estate that can be better used by functional controls. This is especially the case on the Pocket PC with its very small screen. In many cases, a line drawn between sets of controls can visually group the controls as well as a group box can.

Customizing the Appearance of a Button


You can further customize the appearance of the buttons described so far by using a number of additional styles. The styles, BS_RIGHT, BS_LEFT, BS_BOTTOM, and BS_TOP, allow you to position the button text in a place other than the default center of the button. The BS_MULTILINE style allows you to specify more than one line of text in the button. The text is flowed to fit within the button. The newline character (\n) in the button text can be used to specifically define where line breaks occur. Windows CE doesn't support the BS_ICON and BS_BITMAP button styles supported by other versions of Windows.

Owner-Draw Buttons


You can totally control the look of a button by specifying the BS_OWNERDRAW style. When a button is specified as owner-draw, its owner window is entirely responsible for drawing the button for all the states in which it might occur. When a window contains an owner-draw button, it's sent a WM_DRAWITEM message to inform it that a button needs to be drawn. For this message, the wParam parameter contains the ID value for the button and the lParam parameter points to a DRAWITEMSTRUCT structure defined as

typedef struct tagDRAWITEMSTRUCT {
UINT CtlType;
UINT CtlID;
UINT itemID;
UINT itemAction;
UINT itemState;
HWND hwndItem;
HDC hDC;
RECT rcItem;
DWORD itemData;
} DRAWITEMSTRUCT;

The CtlType field is set to ODT_BUTTON, while the CtlID field, like the wParam parameter, contains the button's ID value. The itemAction field contains flags that indicate what needs to be drawn and why. The most significant of these fields is itemState, which contains the state (selected, disabled, and so forth) of the button. The hDC field contains the device context handle for the button window, while the rcItem RECT contains the dimensions of the button. The itemData field is NULL for owner-draw buttons.

As you might expect, the WM_DRAWITEM handler contains a number of GDI calls to draw lines, rectangles, and whatever else is needed to render the button. An important aspect of drawing a button is matching the standard colors of the other windows in the system. Since these colors can change, they shouldn't be hard coded. You can query to find out which are the proper colors by using the function

DWORD GetSysColor (int nIndex);

This function returns an RGB color value for the colors defined for different aspects of windows and controls in the system. Among a number of predefined index values passed in the index parameter, an index of COLOR_BTNFACE returns the proper color for the face of a button, while COLOR_BTNSHADOW returns the dark color for creating the three-dimensional look of a button.


The Edit Control


The edit control is a window that allows the user to enter and edit text. As you might imagine, the edit control is one of the handiest controls in the Windows control pantheon. The edit control is equipped with full editing capability, including cut, copy, and paste interaction with the system clipboard, all without assistance from the application. Edit controls display a single line or, when the ES_MULTILINE style is specified, multiple lines of text. The Notepad accessory, provided with the desktop versions of Windows, is simply a top-level window that contains a multiline edit control.

The edit control has a few other features that should be mentioned. An edit control with the ES_PASSWORD style displays an asterisk (*) character by default in the control for each character typed; the control saves the real character. The ES_READONLY style protects the text contained in the control so that it can be read, or copied into the clipboard, but not modified. The ES_LOWERCASE and ES_UPPERCASE styles force characters entered into the control to be changed to the specified case.

You can add text to an edit control by using the WM_SETTEXT message and retrieve text by using the WM_GETTEXT message. Selection can be controlled using the EM_SETSEL message. This message specifies the starting and ending characters in the selected area. Other messages allow the position of the caret (the marker that indicates the current entry point in an edit field) to be queried and set. Multiline edit controls contain a number of additional messages to control scrolling as well as to access characters by line and column position.


The List Box Control


The list box control displays a list of text items so that the user might select one or more of the items within the list. The list box stores the text, optionally sorts the items, and manages the display of the items, including scrolling. List boxes can be configured to allow selection of a single item or multiple items or to prevent any selection at all.

You can add an item to a list box by sending an LB_ADDSTRING or LB_INSERTSTRING message to the control, passing a pointer to the string to add the lParam parameter. The LB_ADDSTRING message places the newly added string at the end of the list of items, while LB_INSERTSTRING can place the string anywhere within the list of items in the list box. The list box can be searched for a particular item using the LB_FIND message.

Selection status can be queried using LB_GETCURSEL for single selection list boxes. For multiple selection list boxes, LB_GETSELCOUNT and LB_GETSELITEMS can be used to retrieve the items currently selected. Items in the list box can be selected programmatically using the LB_SETCURSEL and LB_SETSEL messages.

Windows CE supports most of the list box functionality available in other versions of Windows with the exception of owner-draw list boxes, as well as the LB_DIR family of messages. A new style, LBS_EX_CONSTSTRINGDATA, is supported under Windows CE. A list box with this style doesn't store strings passed to it. Instead, the pointer to the string is stored, and the application is responsible for maintaining the string. For large arrays of strings that might be loaded from a resource, this procedure can save RAM because the list box won't maintain a separate copy of the list of strings.


The Combo Box Control


The combo box is (as the name implies) a combination of controls—in this case, a single-line edit control and a list box. The combo box is a space-efficient control for selecting one item from a list of many or for providing an edit field with a list of predefined suggested entries. Under Windows CE, the combo box comes in two styles: drop-down and drop-down list. (Simple combo boxes aren't supported.) The drop-down style combo box contains an edit field with a button at the right end. Clicking on the button displays a list box that might contain more selections. Clicking on one of the selections fills the edit field of the combo box with the selection. The drop-down list style replaces the edit box with a static text control. This allows the user to select from an item in the list but prevents the user from entering an item that's not in the list.

Because the combo box combines the edit and list controls, a list of the messages used to control the combo box strongly resembles a merged list of the messages for the two base controls. CB_ADDSTRING, CB_INSERTSTRING, and CB_FINDSTRING act like their list box cousins. Likewise, the CB_SETEDITSELECT and CB_GETEDITSELECT messages set and query the selected characters in the edit box of a drop-down or a drop-down list combo box. To control the dropdown state of a drop-down or drop-down list combo box, the messages CB_SHOWDROPDOWN and CB_GETDROPPEDSTATE can be used.

As in the case of the list box, Windows CE doesn't support owner-draw combo boxes. However, the combo box supports the CBS_EX_CONSTSTRINGDATA extended style, which instructs the combo box to store a pointer to the string for an item instead of the string itself. As with the list box LBS_EX_CONSTSTRINGDATA style, this procedure can save RAM if an application has a large array of strings stored in ROM because the combo box won't maintain a separate copy of the list of strings.


Static Controls


Static controls are windows that display text, icons, or bitmaps not intended for user interaction. You can use static text controls to label other controls in a window. What a static control displays is defined by the text and the style for the control. Under Windows CE, static controls support the following styles:



SS_LEFTDisplays a line of left-aligned text. The text is wrapped if necessary, to fit inside the control.



SS_CENTERDisplays a line of text centered in the control. The text is wrapped if necessary, to fit inside the control.



SS_RIGHTDisplays a line of text aligned with the right side of the control. The text is wrapped if necessary, to fit inside the control.



SS_LEFTNOWORDWRAPDisplays a line of left-aligned text. The text isn't wrapped to multiple lines. Any text extending beyond the right side of the control is clipped.



SS_BITMAPDisplays a bitmap. Window text for the control specifies the name of the resource containing the bitmap.



SS_ICONDisplays an icon. Window text for the control specifies the name of the resource containing the icon.



Static controls with the SS_NOTIFY style send a WM_COMMAND message when the control is clicked, enabled, or disabled, although the Windows CE version of the static control doesn't send a notification when it's double-clicked. The SS_CENTERIMAGE style, used in combination with the SS_BITMAP or SS_ICON style, centers the image within the control. The SS_NOPREFIX style can be used in combination with the text styles. It prevents the ampersand (&) character from being interpreted as indicating that the next character is an accelerator character.

Windows CE doesn't support static controls that display filled or hollow rectangles such as those drawn with the SS_WHITEFRAME or SS_BLACKRECT style. Also, Windows CE doesn't support owner-draw static controls.


The Scroll Bar Control


The scroll bar control is a somewhat different beast from the other controls. Scroll bars are typically seen attached to the sides of windows to control the data being viewed in the window. Indeed, other window controls, such as the edit box and the list box, use the scroll bar control internally. Because of this tight relationship to the parent window, the interface of a scroll bar is different from that of the other controls.

Instead of using WM_COMMAND messages to report actions, scroll bars use WM_VSCROLL and WM_HSCROLL messages. WM_VSCROLL messages are sent by vertically oriented scroll bars, whereas WM_HSCROLL messages are sent by horizontally oriented scroll bars. In addition, instead of something like a SB_SETPOSITION message being sent to a scroll bar to set its position, there are dedicated functions to do this. Let's look at this unique interface.

Scroll Bar Messages


A WM_VSCROLL message is sent to the owner of a vertical scroll bar any time the user taps on the scroll bar to change its position. A complementary message, WM_HSCROLL, is identical to WM_VSCROLL but is sent when the user taps on a horizontal scroll bar. For both these messages, the wParam and lParam assignments are the same. The low word of the wParam parameter contains a code indicating why the message was sent. Figure 4-1 shows a diagram of horizontal and vertical scroll bars and how tapping on different parts of the scroll bars results in different messages. The high word of wParam is the position of the thumb, but this value is valid only while you're processing the SB_THUMBPOSITION and SB_THUMBTRACK codes, which I'll explain shortly. If the scroll bar sending the message is a stand-alone control and not attached to a window, the lParam parameter contains the window handle of the scroll bar.


Figure 4-1: Scroll bars and their hot spots

The scroll bar message codes sent by the scroll bar allow the program to react to all the different user actions allowable by a scroll bar. The response required by each code is listed in the following table, Table 4-1.




































































Table 4-1: Scroll Codes

Codes


Response


For WM_VSCROLL


SB_LINEUP


Program should scroll the screen up one line.


SB_LINEDOWN


Program should scroll the screen down one line.


SB_PAGEUP


Program should scroll the screen up one screen's worth of data.


SB_PAGEDOWN


Program should scroll the screen down one screen's worth of data.


For WM_HSCROLL


SB_LINELEFT


Program should scroll the screen left one character.


SB_LINERIGHT


Program should scroll the screen right one character.


SB_PAGELEFT


Program should scroll the screen left one screen's worth of data.


SB_PAGERIGHT


Program should scroll the screen right one screen's worth of data.


For both WM_VSCROLL and WM_HSCROLL


SB_THUMBTRACK


Programs with enough speed to keep up should update the display with the new scroll position.


SB_THUMBPOSITION


Programs that can't update the display fast enough to keep up with the SB_THUMBTRACK message should update the display with the new scroll position.


SB_ENDSCROLL


This code indicates that the scroll bar has completed the scroll event. No action is required by the program.


SB_TOP


Program should set the display to the top or left end of the data.


SB_BOTTOM


Program should set the display to the bottom or right end of the data.


The SB_LINExxx and SB_PAGExxx codes are pretty straightforward. You move the scroll position either a line or a page at a time. The SB_THUMBPOSITION and SB_THUMBTRACK codes can be processed in one of two ways. When the user drags the scroll bar thumb, the scroll bar sends SB_THUMBTRACK code so that a program can interactively track the dragging of the thumb. If your application is fast enough, you can simply process the SB_THUMBTRACK code and interactively update the display. If you field the SB_THUMBTRACK code, however, your application must be quick enough to redraw the display so that the thumb can be dragged without hesitation or jumping of the scroll bar. This is especially a problem on the slower devices that run Windows CE.

If your application (or the system it's running on) is too slow to quickly update the display for every SB_THUMBTRACK code, you can ignore the SB_THUMBTRACK and wait for the SB_THUMBPOSITION code that's sent when the user drops the scroll bar thumb. Then you have to update the display only once, after the user has finished moving the scroll bar thumb.

Configuring a Scroll Bar


To use a scroll bar, an application should first set the minimum and maximum values—the range of the scroll bar, along with the initial position. Windows CE scroll bars, like their desktop cousins, support proportional thumb sizes, which provide feedback to the user about the size of the current visible page compared with the entire scroll range. To set all these parameters, Windows CE applications should use the SetScrollInfo function, prototyped as

int SetScrollInfo (HWND hwnd, int fnBar, LPSCROLLINFO lpsi, BOOL fRedraw);

The first parameter is either the handle of the window that contains the scroll bar or the window handle of the scroll bar itself. The second parameter, fnBar, is a flag that determines the use of the window handle. The scroll bar flag can be one of three values: SB_HORZ for a window's standard horizontal scroll bar, SB_VERT for a window's standard vertical scroll bar, or SB_CTL if the scroll bar being set is a stand-alone control. Unless the scroll bar is a control, the window handle is the handle of the window containing the scroll bar. With SB_CTL, however, the handle is the window handle of the scroll bar control itself. The last parameter is fRedraw, a Boolean value that indicates whether the scroll bar should be redrawn after the call has been completed.

The third parameter is a pointer to a SCROLLINFO structure, which is defined as

typedef struct tagSCROLLINFO {
UINT cbSize;
UINT fMask;
int nMin;
int nMax;
UINT nPage;
int nPos;
int nTrackPos;
} SCROLLINFO;

This structure allows you to completely specify the scroll bar parameters. The cbSize field must be set to the size of the SCROLLINFO structure. The fMask field contains flags indicating what other fields in the structure contain valid data. The nMin and nMax fields can contain the minimum and maximum scroll values the scroll bar can report. Windows looks at the values in these fields if the fMask parameter contains the SIF_RANGE flag. Likewise, the nPos field sets the position of the scroll bar within its predefined range if the fMask field contains the SIF_POS flag.

The nPage field allows a program to define the size of the currently viewable area of the screen in relation to the entire scrollable area. This allows a user to have a feel for how much of the entire scrolling range is currently visible. This field is used only if the fMask field contains the SIF_PAGE flag. The last member of the SCROLLINFO structure, nTrackPos, isn't used by the SetScrollInfo call and is ignored.

The fMask field can contain one last flag. Passing an SIF_DISABLENOSCROLL flag causes the scroll bar to be disabled but still visible. This is handy when the entire scrolling range is visible within the viewable area and no scrolling is necessary. Disabling the scroll bar in this case is often preferable to simply removing the scroll bar completely.

Those with a sharp eye for detail will notice a problem with the width of the fields in the SCROLLINFO structure. The nMin, nMax, and nPos fields are integers and therefore, in the world of Windows CE, are 32 bits wide. On the other hand, the WM_HSCROLL and WM_VSCROLL messages can return only a 16-bit position in the high word of the wParam parameter. If you're using scroll ranges greater than 65,535, use this function:

BOOL GetScrollInfo (HWND hwnd, int fnBar, LPSCROLLINFO lpsi);

As with SetScrollInfo, the flags in the fnBar field indicate the window handle that should be passed to the function. The SCROLLINFO structure is identical to the one used in SetScrollInfo; however, before it can be passed to GetScrollInfo, it must be initialized with the size of the structure in cbSize. An application must also indicate what data it wants the function to return by setting the appropriate flags in the fMask field. The flags used in fMask are the same as the ones used in SetScrollInfo, with a couple of additions. Now an SIF_TRACKPOS flag can be passed to have the scroll bar return its current thumb position. When called during a WM_xSCROLL message, the nTrackPos field contains the real time position, while the nPos field contains the scroll bar position at the start of the drag of the thumb.

The scroll bar is an unusual control in that it can be added easily to windows simply by specifying a window style flag. It's also unusual in that the control is placed outside the client area of the window. The reason for this assistance is that scroll bars are commonly needed by applications, so the Windows developers made it easy to attach scroll bars to windows. Now let's look at the other basic Windows controls.


The CtlView Example Program


The CtlView example program, shown in Listing 4-1, demonstrates all the controls I've just described. The example makes use of several application-defined child windows that contain various controls. You switch between the different child windows by clicking on one of five radio buttons displayed across the top of the main window. As each of the controls reports a notification through a WM_COMMAND message, that notification is displayed in a list box on the right side of the window. CtlView is handy for observing just what messages a control sends to its parent window and when they're sent. CtlView is designed to use different control layouts depending on the width of the screen. This means that even on the Pocket PC's narrow screen, all the controls are visible.

Listing 4-1: The CtlView program







CtlView.h
//======================================================================
// Header file
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
// Returns number of elements
#define dim(x) (sizeof(x) / sizeof(x[0]))
//----------------------------------------------------------------------
// Generic defines and data types
//
struct decodeUINT { // Structure associates
UINT Code; // messages
// with a function.
LRESULT (*Fxn)(HWND, UINT, WPARAM, LPARAM);
};
struct decodeCMD { // Structure associates
UINT Code; // menu IDs with a
LRESULT (*Fxn)(HWND, WORD, HWND, WORD); // function.
};
//----------------------------------------------------------------------
// Generic defines used by application
#define IDI_BTNICON 20 // Icon used on button
#define ID_ICON 1 // Icon ID
#define IDC_CMDBAR 2 // Command bar ID
#define IDC_RPTLIST 3 // Report window ID
// Client window IDs go from 5 through 9.
#define IDC_WNDSEL 5 // Starting client
// window IDs
// Radio button IDs go from 10 through 14.
#define IDC_RADIOBTNS 10 // Starting ID of
// radio buttons
// Button window defines
#define IDC_PUSHBTN 100
#define IDC_CHKBOX 101
#define IDC_ACHKBOX 102
#define IDC_A3STBOX 103
#define IDC_RADIO1 104
#define IDC_RADIO2 105
#define IDC_OWNRDRAW 106
// Edit window defines
#define IDC_SINGLELINE 100
#define IDC_MULTILINE 101
#define IDC_PASSBOX 102
// List box window defines
#define IDC_COMBOBOX 100
#define IDC_SNGLELIST 101
#define IDC_MULTILIST 102
// Static control window defines
#define IDC_LEFTTEXT 100
#define IDC_RIGHTTEXT 101
#define IDC_CENTERTEXT 102
#define IDC_ICONCTL 103
#define IDC_BITMAPCTL 104
// Scroll bar window defines
#define IDC_LRSCROLL 100
#define IDC_UDSCROLL 101
// User-defined message to add a line to the window
#define MYMSG_ADDLINE (WM_USER + 10)
typedef struct {
TCHAR *szClass;
int nID;
TCHAR *szTitle;
int x;
int y;
int cx;
int cy;
DWORD lStyle;
} CTLWNDSTRUCT, *PCTLWNDSTRUCT;
typedef struct {
WORD wMsg;
int nID;
WPARAM wParam;
LPARAM lParam;
} CTLMSG, * PCTLMSG;
typedef struct {
TCHAR *pszLabel;
WORD wNotification;
} NOTELABELS, *PNOTELABELS;
//----------------------------------------------------------------------
// Function prototypes
//
HWND InitInstance (HINSTANCE, LPWSTR, int);
int TermInstance (HINSTANCE, int);
// Window procedures
LRESULT CALLBACK FrameWndProc (HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK ClientWndProc (HWND, UINT, WPARAM, LPARAM);
// Message handlers
LRESULT DoCreateFrame (HWND, UINT, WPARAM, LPARAM);
LRESULT DoSizeFrame (HWND, UINT, WPARAM, LPARAM);
LRESULT DoCommandFrame (HWND, UINT, WPARAM, LPARAM);
LRESULT DoAddLineFrame (HWND, UINT, WPARAM, LPARAM);
LRESULT DoDestroyFrame (HWND, UINT, WPARAM, LPARAM);
//----------------------------------------------------------------------
// Window prototypes and defines for BtnWnd
//
#define BTNWND TEXT ("ButtonWnd")
int InitBtnWnd (HINSTANCE);
// Window procedures
LRESULT CALLBACK BtnWndProc (HWND, UINT, WPARAM, LPARAM);
LRESULT DoCreateBtnWnd (HWND, UINT, WPARAM, LPARAM);
LRESULT DoCtlColorBtnWnd (HWND, UINT, WPARAM, LPARAM);
LRESULT DoCommandBtnWnd (HWND, UINT, WPARAM, LPARAM);
LRESULT DoDrawItemBtnWnd (HWND, UINT, WPARAM, LPARAM);
LRESULT DoMeasureItemBtnWnd (HWND, UINT, WPARAM, LPARAM);
//----------------------------------------------------------------------
// Window prototypes and defines for EditWnd
//
#define EDITWND TEXT ("EditWnd")
int InitEditWnd (HINSTANCE);
// Window procedures
LRESULT CALLBACK EditWndProc (HWND, UINT, WPARAM, LPARAM);
LRESULT DoCreateEditWnd (HWND, UINT, WPARAM, LPARAM);
LRESULT DoCommandEditWnd (HWND, UINT, WPARAM, LPARAM);
LRESULT DoDrawItemEditWnd (HWND, UINT, WPARAM, LPARAM);
LRESULT DoMeasureItemEditWnd (HWND, UINT, WPARAM, LPARAM);
//----------------------------------------------------------------------
// Window prototypes and defines for ListWnd
//
#define LISTWND TEXT ("ListWnd")
int InitListWnd (HINSTANCE);
// Window procedures
LRESULT CALLBACK ListWndProc (HWND, UINT, WPARAM, LPARAM);
LRESULT DoCreateListWnd (HWND, UINT, WPARAM, LPARAM);
LRESULT DoCommandListWnd (HWND, UINT, WPARAM, LPARAM);
LRESULT DoDrawItemListWnd (HWND, UINT, WPARAM, LPARAM);
LRESULT DoMeasureItemListWnd (HWND, UINT, WPARAM, LPARAM);
//----------------------------------------------------------------------
// Window prototypes and defines for StatWnd
//
#define STATWND TEXT ("StaticWnd")
int InitStatWnd (HINSTANCE);
// Window procedures
LRESULT CALLBACK StatWndProc (HWND, UINT, WPARAM, LPARAM);
LRESULT DoCreateStatWnd (HWND, UINT, WPARAM, LPARAM);
LRESULT DoCommandStatWnd (HWND, UINT, WPARAM, LPARAM);
LRESULT DoDrawItemStatWnd (HWND, UINT, WPARAM, LPARAM);
LRESULT DoMeasureItemStatWnd (HWND, UINT, WPARAM, LPARAM);
//----------------------------------------------------------------------
// Window prototypes and defines ScrollWnd
//
#define SCROLLWND TEXT ("ScrollWnd")
int InitScrollWnd (HINSTANCE);
// Window procedures
LRESULT CALLBACK ScrollWndProc (HWND, UINT, WPARAM, LPARAM);
LRESULT DoCreateScrollWnd (HWND, UINT, WPARAM, LPARAM);
LRESULT DoVScrollScrollWnd (HWND, UINT, WPARAM, LPARAM);
LRESULT DoHScrollScrollWnd (HWND, UINT, WPARAM, LPARAM);


CtlView.cpp
//======================================================================
// CtlView - Lists the available fonts in the system.
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
#include <windows.h> // For all that Windows stuff
#include <commctrl.h> // Command bar includes
#include "CtlView.h" // Program-specific stuff
//----------------------------------------------------------------------
// Global data
//
const TCHAR szAppName[] = TEXT ("CtlView");
HINSTANCE hInst; // Program instance handle
// Message dispatch table for FrameWindowProc
const struct decodeUINT FrameMessages[] = {
WM_CREATE, DoCreateFrame,
WM_SIZE, DoSizeFrame,
WM_COMMAND, DoCommandFrame,
MYMSG_ADDLINE, DoAddLineFrame,
WM_DESTROY, DoDestroyFrame,
};
typedef struct {
TCHAR *szTitle;
int nID;
TCHAR *szCtlWnds;
HWND hWndClient;
} RBTNDATA;
// Text for main window radio buttons
TCHAR *szBtnTitle[] = {TEXT ("Buttons"), TEXT ("Edit"), TEXT ("List"),
TEXT ("Static"), TEXT ("Scroll")};
// Class names for child windows containing controls
TCHAR *szCtlWnds[] = {BTNWND, EDITWND, LISTWND, STATWND, SCROLLWND};
int nWndSel = 0;
//======================================================================
// Program entry point
//
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPWSTR lpCmdLine, int nCmdShow) {
MSG msg;
int rc = 0;
HWND hwndFrame;
// Initialize application.
hwndFrame = InitInstance (hInstance, lpCmdLine, nCmdShow);
if (hwndFrame == 0)
return 0x10;
// Application message loop
while (GetMessage (&msg, NULL, 0, 0)) {
TranslateMessage (&msg);
DispatchMessage (&msg);
}
// Instance cleanup
return TermInstance (hInstance, msg.wParam);
}
//----------------------------------------------------------------------
// InitInstance - Instance initialization
//
HWND InitInstance (HINSTANCE hInstance, LPWSTR lpCmdLine, int nCmdShow) {
WNDCLASS wc;
HWND hWnd;
// Save program instance handle in global variable.
hInst = hInstance;
#if defined(WIN32_PLATFORM_PSPC)
// If Pocket PC, allow only one instance of the application
hWnd = FindWindow (szAppName, NULL);
if (hWnd) {
SetForegroundWindow ((HWND)(((DWORD)hWnd) | 0x01));
return 0;
}
#endif
// Register application frame window class.
wc.style = 0; // Window style
wc.lpfnWndProc = FrameWndProc; // Callback function
wc.cbClsExtra = 0; // Extra class data
wc.cbWndExtra = 0; // Extra window data
wc.hInstance = hInstance; // Owner handle
wc.hIcon = NULL, // Application icon
wc.hCursor = LoadCursor (NULL, IDC_ARROW);// Default cursor
wc.hbrBackground = (HBRUSH) GetSysColorBrush (COLOR_STATIC);
wc.lpszMenuName = NULL; // Menu name
wc.lpszClassName = szAppName; // Window class name
if (RegisterClass (&wc) == 0) return 0;
// Initialize client window classes
if (InitBtnWnd (hInstance) != 0) return 0;
if (InitEditWnd (hInstance) != 0) return 0;
if (InitListWnd (hInstance) != 0) return 0;
if (InitStatWnd (hInstance) != 0) return 0;
if (InitScrollWnd (hInstance) != 0) return 0;
// Create frame window.
hWnd = CreateWindowEx (WS_EX_NODRAG, szAppName, TEXT ("Control View"),
WS_VISIBLE | WS_CAPTION | WS_SYSMENU,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
// Return fail code if window not created.
if (!IsWindow (hWnd)) return 0;
// Standard show and update calls
ShowWindow (hWnd, nCmdShow);
UpdateWindow (hWnd);
return hWnd;
}
//----------------------------------------------------------------------
// TermInstance - Program cleanup
//
int TermInstance (HINSTANCE hInstance, int nDefRC) {
return nDefRC;
}
//======================================================================
// Message handling procedures for FrameWindow
//
//----------------------------------------------------------------------
// FrameWndProc - Callback function for application window
//
LRESULT CALLBACK FrameWndProc (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
int i;
//
// Search message list to see if we need to handle this
// message. If in list, call procedure.
//
for (i = 0; i < dim(FrameMessages); i++) {
if (wMsg == FrameMessages[i].Code)
return (*FrameMessages[i].Fxn)(hWnd, wMsg, wParam, lParam);
}
return DefWindowProc (hWnd, wMsg, wParam, lParam);
}
//----------------------------------------------------------------------
// DoCreateFrame - Process WM_CREATE message for window.
//
LRESULT DoCreateFrame (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
HWND hwndChild;
INT i;
// Set currently viewed window
nWndSel = 0;
// Create the radio buttons.
for (i = 0; i < dim(szBtnTitle); i++) {
hwndChild = CreateWindow (TEXT ("BUTTON"),
szBtnTitle[i], BS_AUTORADIOBUTTON |
WS_VISIBLE | WS_CHILD, 0,
0, 80, 20, hWnd,
(HMENU)(IDC_RADIOBTNS+i), hInst, NULL);
// Destroy frame if window not created.
if (!IsWindow (hwndChild)) {
DestroyWindow (hWnd);
break;
}
}
// Create report window. Size it so that it fits either on the right
// or below the control windows, depending on the size of the screen.
hwndChild = CreateWindowEx (WS_EX_CLIENTEDGE, TEXT ("listbox"),
TEXT ("), WS_VISIBLE | WS_CHILD | WS_VSCROLL |
LBS_USETABSTOPS | LBS_NOINTEGRALHEIGHT, 0, 0,
100, 100,hWnd, (HMENU)IDC_RPTLIST, hInst, NULL);
// Destroy frame if window not created.
if (!IsWindow (hwndChild)) {
DestroyWindow (hWnd);
return 0;
}
// Initialize tab stops for display list box.
i = 24;
SendMessage (hwndChild, LB_SETTABSTOPS, 1, (LPARAM)&i);
// Create the child windows. Size them so that they fit under
// the command bar and fill the left side of the child area.
for (i = 0; i < dim(szCtlWnds); i++) {
hwndChild = CreateWindowEx (WS_EX_CLIENTEDGE, szCtlWnds[i],
TEXT ("), WS_CHILD, 0, 0, 200, 200, hWnd,
(HMENU)(IDC_WNDSEL+i), hInst, NULL);
// Destroy frame if client window not created.
if (!IsWindow (hwndChild)) {
DestroyWindow (hWnd);
return 0;
}
}
// Check one of the auto radio buttons.
SendDlgItemMessage (hWnd, IDC_RADIOBTNS+nWndSel, BM_SETCHECK, 1, 0);
hwndChild = GetDlgItem (hWnd, IDC_WNDSEL+nWndSel);
ShowWindow (hwndChild, SW_SHOW);
return 0;
}
//----------------------------------------------------------------------
// DoSizeFrame - Process WM_SIZE message for window.
//
LRESULT DoSizeFrame (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
int nWidth, nHeight;
int i, x, y, cx, cy;
BOOL bWide = TRUE;
RECT rect;
GetWindowRect (hWnd, &rect);
GetClientRect (hWnd, &rect);
// These arrays are used to adjust between wide and narrow screens.
POINT ptRBtnsN[] = {{5,0}, {90,0}, {180,0}, {5,20}, {90,20}};
POINT ptRBtnsW[] = {{5,0}, {90,0}, {180,0}, {270,0}, {360,0}};
LPPOINT pptRbtns = ptRBtnsW;
nWidth = LOWORD (lParam);
nHeight = HIWORD (lParam);
// Use different layouts for narrow (Pocket PC) screens.
if (GetSystemMetrics (SM_CXSCREEN) < 480) {
pptRbtns = ptRBtnsN;
bWide = FALSE;
}
// Move the radio buttons.
for (i = 0; i < dim(szBtnTitle); i++)
SetWindowPos (GetDlgItem (hWnd, IDC_RADIOBTNS+i), 0,
pptRbtns[i].x, pptRbtns[i].y,
0, 0, SWP_NOSIZE | SWP_NOZORDER);
// Size report window so that it fits either on the right or
// below the control windows, depending on the size of the screen.
x = bWide ? nWidth/2 : 0;
y = bWide ? 20 : (nHeight)/2 + 40;
cx = bWide ? nWidth/2 : nWidth;
cy = nHeight - y;
SetWindowPos (GetDlgItem (hWnd, IDC_RPTLIST), 0, x, y, cx, cy,
SWP_NOZORDER);
// Size the child windows so that they fit under
// the command bar and fill the left side of the child area.
x = 0;
y = bWide ? 20 : 40;
cx = bWide ? nWidth/2 : nWidth;
cy = bWide ? nHeight : (nHeight)/2+40;
for (i = 0; i < dim(szCtlWnds); i++)
SetWindowPos (GetDlgItem (hWnd, IDC_WNDSEL+i), 0, x, y, cx, cy,
SWP_NOZORDER);
return 0;
}
//----------------------------------------------------------------------
// DoCommandFrame - Process WM_COMMAND message for window.
//
LRESULT DoCommandFrame (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
HWND hwndTemp;
int nBtn;
// Don't look at list box messages.
if (LOWORD (wParam) == IDC_RPTLIST)
return 0;
nBtn = LOWORD (wParam) - IDC_RADIOBTNS;
if (nWndSel != nBtn) {
// Hide the currently visible window.
hwndTemp = GetDlgItem (hWnd, IDC_WNDSEL+nWndSel);
ShowWindow (hwndTemp, SW_HIDE);
// Save the current selection.
nWndSel = nBtn;
// Show the window selected via the radio button.
hwndTemp = GetDlgItem (hWnd, IDC_WNDSEL+nWndSel);
ShowWindow (hwndTemp, SW_SHOW);
}
return 0;
}
//----------------------------------------------------------------------
// DoAddLineFrame - Process MYMSG_ADDLINE message for window.
//
LRESULT DoAddLineFrame (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
TCHAR szOut[128];
int i;
if (LOWORD (wParam) == 0xffff)
wsprintf (szOut, TEXT (" \t %s"), (LPTSTR)lParam);
else
wsprintf (szOut, TEXT ("id:%3d \t %s"), LOWORD (wParam),
(LPTSTR)lParam);
i = SendDlgItemMessage (hWnd, IDC_RPTLIST, LB_ADDSTRING, 0,
(LPARAM)(LPCTSTR)szOut);
if (i != LB_ERR)
SendDlgItemMessage (hWnd, IDC_RPTLIST, LB_SETTOPINDEX, i,
(LPARAM)(LPCTSTR)szOut);
return 0;
}
//----------------------------------------------------------------------
// DoDestroyFrame - Process WM_DESTROY message for window.
//
LRESULT DoDestroyFrame (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
PostQuitMessage (0);
return 0;
}


BtnWnd.cpp
//======================================================================
// BtnWnd - Button window code
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
#include <windows.h> // For all that Windows stuff
#include "Ctlview.h" // Program-specific stuff
extern HINSTANCE hInst;
LRESULT DrawButton (HWND hWnd, LPDRAWITEMSTRUCT pdi);
//----------------------------------------------------------------------
// Global data
//
// Message dispatch table for BtnWndWindowProc
const struct decodeUINT BtnWndMessages[] = {
WM_CREATE, DoCreateBtnWnd,
WM_CTLCOLORSTATIC, DoCtlColorBtnWnd,
WM_COMMAND, DoCommandBtnWnd,
WM_DRAWITEM, DoDrawItemBtnWnd,
};
// Structure defining the controls in the window
CTLWNDSTRUCT Btns [] = {
{TEXT ("BUTTON"), IDC_PUSHBTN, TEXT ("Button"),
10, 10, 120, 23, BS_PUSHBUTTON | BS_NOTIFY},
{TEXT ("BUTTON"), IDC_CHKBOX, TEXT ("Check box"),
10, 35, 120, 23, BS_CHECKBOX},
{TEXT ("BUTTON"), IDC_ACHKBOX, TEXT ("Auto check box"),
10, 60, 110, 23, BS_AUTOCHECKBOX},
{TEXT ("BUTTON"), IDC_A3STBOX, TEXT ("Multiline auto 3-state box"),
140, 60, 90, 52, BS_AUTO3STATE | BS_MULTILINE},
{TEXT ("BUTTON"), IDC_RADIO1, TEXT ("Auto radio button 1"),
10, 85, 120, 23, BS_AUTORADIOBUTTON},
{TEXT ("BUTTON"), IDC_RADIO2, TEXT ("Auto radio button 2"),
10, 110, 120, 23, BS_AUTORADIOBUTTON},
{TEXT ("BUTTON"), IDC_OWNRDRAW, TEXT ("OwnerDraw"),
150, 10, 44, 44, BS_PUSHBUTTON | BS_OWNERDRAW},
};
// Structure labeling the button control WM_COMMAND notifications
NOTELABELS nlBtn[] = {{TEXT ("BN_CLICKED "), 0},
{TEXT ("BN_PAINT "), 1},
{TEXT ("BN_HILITE "), 2},
{TEXT ("BN_UNHILITE"), 3},
{TEXT ("BN_DISABLE "), 4},
{TEXT ("BN_DOUBLECLICKED"), 5},
{TEXT ("BN_SETFOCUS "), 6},
{TEXT ("BN_KILLFOCUS"), 7}
};
// Handle for icon used in owner-draw icon
HICON hIcon = 0;
//----------------------------------------------------------------------
// InitBtnWnd - BtnWnd window initialization
//
int InitBtnWnd (HINSTANCE hInstance) {
WNDCLASS wc;
// Register application BtnWnd window class.
wc.style = 0; // Window style
wc.lpfnWndProc = BtnWndProc; // Callback function
wc.cbClsExtra = 0; // Extra class data
wc.cbWndExtra = 0; // Extra window data
wc.hInstance = hInstance; // Owner handle
wc.hIcon = NULL, // Application icon
wc.hCursor = LoadCursor (NULL, IDC_ARROW);// Default cursor
wc.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
wc.lpszMenuName = NULL; // Menu name
wc.lpszClassName = BTNWND; // Window class name
if (RegisterClass (&wc) == 0) return 1;
return 0;
}
//======================================================================
// Message handling procedures for BtnWindow
//----------------------------------------------------------------------
// BtnWndWndProc - Callback function for application window
//
LRESULT CALLBACK BtnWndProc (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
int i;
//
// Search message list to see if we need to handle this
// message. If in list, call procedure.
//
for (i = 0; i < dim(BtnWndMessages); i++) {
if (wMsg == BtnWndMessages[i].Code)
return (*BtnWndMessages[i].Fxn)(hWnd, wMsg, wParam, lParam);
}
return DefWindowProc (hWnd, wMsg, wParam, lParam);
}
//----------------------------------------------------------------------
// DoCreateBtnWnd - Process WM_CREATE message for window.
//
LRESULT DoCreateBtnWnd (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
int i;
for (i = 0; i < dim(Btns); i++) {
CreateWindow (Btns[i].szClass, Btns[i].szTitle,
Btns[i].lStyle | WS_VISIBLE | WS_CHILD,
Btns[i].x, Btns[i].y, Btns[i].cx, Btns[i].cy,
hWnd, (HMENU) Btns[i].nID, hInst, NULL);
}
hIcon = LoadIcon (hInst, TEXT ("TEXTICON"));
// We need to set the initial state of the radio buttons.
CheckRadioButton (hWnd, IDC_RADIO1, IDC_RADIO2, IDC_RADIO1);
return 0;
}
//----------------------------------------------------------------------
// DoCtlColorBtnWnd - process WM_CTLCOLORxx messages for window.
//
LRESULT DoCtlColorBtnWnd (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
return (LRESULT)GetStockObject (WHITE_BRUSH);
}
//----------------------------------------------------------------------
// DoCommandBtnWnd - Process WM_COMMAND message for window.
//
LRESULT DoCommandBtnWnd (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
TCHAR szOut[128];
int i;
// Since the Check Box button is not an auto check box, it
// must be set manually.
if ((LOWORD (wParam) == IDC_CHKBOX) &&
(HIWORD (wParam) == BN_CLICKED)) {
// Get the current state, complement, and set.
i = SendDlgItemMessage (hWnd, IDC_CHKBOX, BM_GETCHECK, 0, 0);
if (i == 0)
SendDlgItemMessage (hWnd, IDC_CHKBOX, BM_SETCHECK, 1, 0);
else
SendDlgItemMessage (hWnd, IDC_CHKBOX, BM_SETCHECK, 0, 0);
}
// Report WM_COMMAND messages to main window.
for (i = 0; i < dim(nlBtn); i++) {
if (HIWORD (wParam) == nlBtn[i].wNotification) {
lstrcpy (szOut, nlBtn[i].pszLabel);
break;
}
}
if (i == dim(nlBtn))
wsprintf (szOut, TEXT ("notification: %x"), HIWORD (wParam));
SendMessage (GetParent (hWnd), MYMSG_ADDLINE, wParam,
(LPARAM)szOut);
return 0;
}
//----------------------------------------------------------------------
// DoDrawItemBtnWnd - Process WM_DRAWITEM message for window.
//
LRESULT DoDrawItemBtnWnd (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
return DrawButton (hWnd, (LPDRAWITEMSTRUCT)lParam);
}
//---------------------------------------------------------------------
// DrawButton - Draws an owner-draw button
//
LRESULT DrawButton (HWND hWnd, LPDRAWITEMSTRUCT pdi) {
HPEN hPenShadow, hPenLight, hPenDkShadow, hRedPen, hOldPen;
HBRUSH hBr, hOldBr;
LOGPEN lpen;
TCHAR szOut[128];
POINT ptOut[3], ptIn[3];
// Reflect the messages to the report window.
wsprintf (szOut, TEXT ("WM_DRAWITEM Act:%x State:%x"),
pdi->itemAction, pdi->itemState);
SendMessage (GetParent (hWnd), MYMSG_ADDLINE, pdi->CtlID,
(LPARAM)szOut);
// Create pens for drawing.
lpen.lopnStyle = PS_SOLID;
lpen.lopnWidth.x = 3;
lpen.lopnWidth.y = 3;
lpen.lopnColor = GetSysColor (COLOR_3DSHADOW);
hPenShadow = CreatePenIndirect (&lpen);
lpen.lopnColor = RGB (255, 0, 0);
hRedPen = CreatePenIndirect (&lpen);
lpen.lopnWidth.x = 1;
lpen.lopnWidth.y = 1;
lpen.lopnColor = GetSysColor (COLOR_3DLIGHT);
hPenLight = CreatePenIndirect (&lpen);
lpen.lopnColor = GetSysColor (COLOR_3DDKSHADOW);
hPenDkShadow = CreatePenIndirect (&lpen);
// Create a brush for the face of the button.
hBr = CreateSolidBrush (GetSysColor (COLOR_3DFACE));
// Draw a rectangle with a thick outside border to start the
// frame drawing.
hOldPen = (HPEN_SelectObject (pdi->hDC, hPenShadow);
hOldBr = (HBRUSH)SelectObject (pdi->hDC, hBr);
Rectangle (pdi->hDC, pdi->rcItem.left, pdi->rcItem.top,
pdi->rcItem.right, pdi->rcItem.bottom);
// Draw the upper left inside line.
ptIn[0].x = pdi->rcItem.left + 1;
ptIn[0].y = pdi->rcItem.bottom - 2;
ptIn[1].x = pdi->rcItem.left + 1;
ptIn[1].y = pdi->rcItem.top + 1;
ptIn[2].x = pdi->rcItem.right - 2;
ptIn[2].y = pdi->rcItem.top + 1;
// Select a pen to draw shadow or light side of button.
if (pdi->itemState & ODS_SELECTED) {
SelectObject (pdi->hDC, hPenDkShadow);
} else {
SelectObject (pdi->hDC, hPenLight);
}
Polyline (pdi->hDC, ptIn, 3);
// If selected, also draw a bright line inside the lower
// right corner.
if (pdi->itemState & ODS_SELECTED) {
SelectObject (pdi->hDC, hPenLight);
ptIn[1].x = pdi->rcItem.right - 2;
ptIn[1].y = pdi->rcItem.bottom - 2;
Polyline (pdi->hDC, ptIn, 3);
}
// Now draw the black outside line on either the upper left or lower
// right corner.
ptOut[0].x = pdi->rcItem.left;
ptOut[0].y = pdi->rcItem.bottom - 1;
ptOut[2].x = pdi->rcItem.right - 1;
ptOut[2].y = pdi->rcItem.top;
SelectObject (pdi->hDC, hPenDkShadow);
if (pdi->itemState & ODS_SELECTED) {
ptOut[1].x = pdi->rcItem.left;
ptOut[1].y = pdi->rcItem.top;
} else {
ptOut[1].x = pdi->rcItem.right - 1;
ptOut[1].y = pdi->rcItem.bottom - 1;
}
Polyline (pdi->hDC, ptOut, 3);
// Draw the triangle.
ptOut[0].x = (pdi->rcItem.right - pdi->rcItem.left)/2;
ptOut[0].y = pdi->rcItem.top + 4;
ptOut[1].x = pdi->rcItem.left + 3;
ptOut[1].y = pdi->rcItem.bottom - 6;
ptOut[2].x = pdi->rcItem.right - 6;
ptOut[2].y = pdi->rcItem.bottom - 6;
SelectObject (pdi->hDC, hRedPen);
Polygon (pdi->hDC, ptOut, 3);
// If button has the focus, draw the dotted rect inside the button.
if (pdi->itemState & ODS_FOCUS) {
pdi->rcItem.left += 3;
pdi->rcItem.top += 3;
pdi->rcItem.right -= 4;
pdi->rcItem.bottom -= 4;
DrawFocusRect (pdi->hDC, &pdi->rcItem);
}
// Clean up. First select the original brush and pen into the DC.
SelectObject (pdi->hDC, hOldBr);
SelectObject (pdi->hDC, hOldPen);
// Now delete the brushes and pens created.
DeleteObject (hBr);
DeleteObject (hPenShadow);
DeleteObject (hPenDkShadow);
DeleteObject (hPenLight);
return 0;
}


EditWnd.cpp
//======================================================================
// EditWnd - Edit control window code
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
#include <windows.h> // For all that Windows stuff
#include "Ctlview.h" // Program-specific stuff
extern HINSTANCE hInst;
//----------------------------------------------------------------------
// Global data
//
// Message dispatch table for EditWndWindowProc
const struct decodeUINT EditWndMessages[] = {
WM_CREATE, DoCreateEditWnd,
WM_COMMAND, DoCommandEditWnd,
};
// Structure defining the controls in the window
CTLWNDSTRUCT Edits[] = {
{TEXT ("edit"), IDC_SINGLELINE, TEXT ("Single line edit control"),
10, 10, 180, 23, ES_AUTOHSCROLL},
{TEXT ("edit"), IDC_MULTILINE, TEXT ("Multiline edit control"),
10, 35, 180, 70, ES_MULTILINE | ES_AUTOVSCROLL},
{TEXT ("edit"), IDC_PASSBOX, TEXT ("),
10, 107, 180, 23, ES_PASSWORD},
};
// Structure labeling the edit control WM_COMMAND notifications
NOTELABELS nlEdit[] = {{TEXT ("EN_SETFOCUS "), 0x0100},
{TEXT ("EN_KILLFOCUS"), 0x0200},
{TEXT ("EN_CHANGE "), 0x0300},
{TEXT ("EN_UPDATE "), 0x0400},
{TEXT ("EN_ERRSPACE "), 0x0500},
{TEXT ("EN_MAXTEXT "), 0x0501},
{TEXT ("EN_HSCROLL "), 0x0601},
{TEXT ("EN_VSCROLL "), 0x0602},
};
//----------------------------------------------------------------------
// InitEditWnd - EditWnd window initialization
//
int InitEditWnd (HINSTANCE hInstance) {
WNDCLASS wc;
// Register application EditWnd window class.
wc.style = 0; // Window style
wc.lpfnWndProc = EditWndProc; // Callback function
wc.cbClsExtra = 0; // Extra class data
wc.cbWndExtra = 0; // Extra window data
wc.hInstance = hInstance; // Owner handle
wc.hIcon = NULL, // Application icon
wc.hCursor = LoadCursor (NULL, IDC_ARROW);// Default cursor
wc.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
wc.lpszMenuName = NULL; // Menu name
wc.lpszClassName = EDITWND; // Window class name
if (RegisterClass (&wc) == 0) return 1;
return 0;
}
//======================================================================
// Message handling procedures for EditWindow
//----------------------------------------------------------------------
// EditWndWndProc - Callback function for application window
//
LRESULT CALLBACK EditWndProc (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
int i;
//
// Search message list to see if we need to handle this
// message. If in list, call procedure.
//
for (i = 0; i < dim(EditWndMessages); i++) {
if (wMsg == EditWndMessages[i].Code)
return (*EditWndMessages[i].Fxn)(hWnd, wMsg, wParam, lParam);
}
return DefWindowProc (hWnd, wMsg, wParam, lParam);
}
//----------------------------------------------------------------------
// DoCreateEditWnd - Process WM_CREATE message for window.
//
LRESULT DoCreateEditWnd (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
int i;
for (i = 0; i < dim(Edits); i++) {
CreateWindow (Edits[i].szClass, Edits[i].szTitle,
Edits[i].lStyle | WS_VISIBLE | WS_CHILD | WS_BORDER,
Edits[i].x, Edits[i].y, Edits[i].cx, Edits[i].cy,
hWnd, (HMENU) Edits[i].nID, hInst, NULL);
}
return 0;
}
//----------------------------------------------------------------------
// DoCommandEditWnd - Process WM_COMMAND message for window.
//
LRESULT DoCommandEditWnd (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
TCHAR szOut[128];
int i;
for (i = 0; i < dim(nlEdit); i++) {
if (HIWORD (wParam) == nlEdit[i].wNotification) {
lstrcpy (szOut, nlEdit[i].pszLabel);
break;
}
}
if (i == dim(nlEdit))
wsprintf (szOut, TEXT ("notification: %x"), HIWORD (wParam));
SendMessage (GetParent (hWnd), MYMSG_ADDLINE, wParam,
(LPARAM)szOut);
return 0;
}


ListWnd.cpp
//======================================================================
// ListWnd - List box control window code
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
#include <windows.h> // For all that Windows stuff
#include "Ctlview.h" // Program-specific stuff
extern HINSTANCE hInst;
//----------------------------------------------------------------------
// Global data
//
// Message dispatch table for ListWndWindowProc
const struct decodeUINT ListWndMessages[] = {
WM_CREATE, DoCreateListWnd,
WM_COMMAND, DoCommandListWnd,
};
// Structure defining the controls in the window
CTLWNDSTRUCT Lists[] = {
{TEXT ("combobox"), IDC_COMBOBOX, TEXT ("), 10, 10, 205, 100,
WS_VSCROLL},
{TEXT ("Listbox"), IDC_SNGLELIST, TEXT ("), 10, 35, 100, 90,
WS_VSCROLL | LBS_NOTIFY},
{TEXT ("Listbox"), IDC_MULTILIST, TEXT ("), 115, 35, 100, 90,
WS_VSCROLL | LBS_EXTENDEDSEL | LBS_NOTIFY}
};
// Structure labeling the list box control WM_COMMAND notifications
NOTELABELS nlList[] = {{TEXT ("LBN_ERRSPACE "), (-2)},
{TEXT ("LBN_SELCHANGE"), 1},
{TEXT ("LBN_DBLCLK "), 2},
{TEXT ("LBN_SELCANCEL"), 3},
{TEXT ("LBN_SETFOCUS "), 4},
{TEXT ("LBN_KILLFOCUS"), 5},
};
// Structure labeling the combo box control WM_COMMAND notifications
NOTELABELS nlCombo[] = {{TEXT ("CBN_ERRSPACE "), (-1)},
{TEXT ("CBN_SELCHANGE "), 1},
{TEXT ("CBN_DBLCLK "), 2},
{TEXT ("CBN_SETFOCUS "), 3},
{TEXT ("CBN_KILLFOCUS "), 4},
{TEXT ("CBN_EDITCHANGE "), 5},
{TEXT ("CBN_EDITUPDATE "), 6},
{TEXT ("CBN_DROPDOWN "), 7},
{TEXT ("CBN_CLOSEUP "), 8},
{TEXT ("CBN_SELENDOK "), 9},
{TEXT ("CBN_SELENDCANCEL"), 10},
};
//----------------------------------------------------------------------
// InitListWnd - ListWnd window initialization
//
int InitListWnd (HINSTANCE hInstance) {
WNDCLASS wc;
// Register application ListWnd window class.
wc.style = 0; // Window style
wc.lpfnWndProc = ListWndProc; // Callback function
wc.cbClsExtra = 0; // Extra class data
wc.cbWndExtra = 0; // Extra window data
wc.hInstance = hInstance; // Owner handle
wc.hIcon = NULL, // Application icon
wc.hCursor = LoadCursor (NULL, IDC_ARROW); // Default cursor
wc.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
wc.lpszMenuName = NULL; // Menu name
wc.lpszClassName = LISTWND; // Window class name
if (RegisterClass (&wc) == 0) return 1;
return 0;
}
//======================================================================
// Message handling procedures for ListWindow
//----------------------------------------------------------------------
// ListWndProc - Callback function for application window
//
LRESULT CALLBACK ListWndProc (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
int i;
//
// Search message list to see if we need to handle this
// message. If in list, call procedure.
//
for (i = 0; i < dim(ListWndMessages); i++) {
if (wMsg == ListWndMessages[i].Code)
return (*ListWndMessages[i].Fxn)(hWnd, wMsg, wParam, lParam);
}
return DefWindowProc (hWnd, wMsg, wParam, lParam);
}
//----------------------------------------------------------------------
// DoCreateListWnd - Process WM_CREATE message for window.
//
LRESULT DoCreateListWnd (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
int i;
TCHAR szOut[64];
for (i = 0; i < dim(Lists); i++) {
CreateWindow (Lists[i].szClass, Lists[i].szTitle,
Lists[i].lStyle | WS_VISIBLE | WS_CHILD | WS_BORDER,
Lists[i].x, Lists[i].y, Lists[i].cx, Lists[i].cy,
hWnd, (HMENU) Lists[i].nID, hInst, NULL);
}
for (i = 0; i < 20; i++) {
wsprintf (szOut, TEXT ("Item %d"), i);
SendDlgItemMessage (hWnd, IDC_SNGLELIST, LB_ADDSTRING, 0,
(LPARAM)szOut);
SendDlgItemMessage (hWnd, IDC_MULTILIST, LB_ADDSTRING, 0,
(LPARAM)szOut);
SendDlgItemMessage (hWnd, IDC_COMBOBOX, CB_ADDSTRING, 0,
(LPARAM)szOut);
}
// Set initial selection.
SendDlgItemMessage (hWnd, IDC_COMBOBOX, CB_SETCURSEL, 0, 0);
return 0;
}
//----------------------------------------------------------------------
// DoCommandListWnd - Process WM_COMMAND message for window.
//
LRESULT DoCommandListWnd (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
TCHAR szOut[128];
int i;
if (LOWORD (wParam) == IDC_COMBOBOX) {
for (i = 0; i < dim(nlCombo); i++) {
if (HIWORD (wParam) == nlCombo[i].wNotification) {
lstrcpy (szOut, nlCombo[i].pszLabel);
break;
}
}
if (i == dim(nlList))
wsprintf (szOut, TEXT ("notification: %x"), HIWORD (wParam));
} else {
for (i = 0; i < dim(nlList); i++) {
if (HIWORD (wParam) == nlList[i].wNotification) {
lstrcpy (szOut, nlList[i].pszLabel);
break;
}
}
if (i == dim(nlList))
wsprintf (szOut, TEXT ("notification: %x"), HIWORD (wParam));
}
SendMessage (GetParent (hWnd), MYMSG_ADDLINE, wParam,
(LPARAM)szOut);
return 0;
}


StatWnd.cpp
//======================================================================
// StatWnd - Static control window code
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
#include <windows.h> // For all that Windows stuff
#include "Ctlview.h" // Program-specific stuff
extern HINSTANCE hInst;
//----------------------------------------------------------------------
// Global data
//
// Message dispatch table for StatWndWindowProc
const struct decodeUINT StatWndMessages[] = {
WM_CREATE, DoCreateStatWnd,
WM_COMMAND, DoCommandStatWnd,
};
// Structure defining the controls in the window
CTLWNDSTRUCT Stats [] = {
{TEXT ("static"), IDC_LEFTTEXT, TEXT ("Left text"),
10, 10, 120, 23, SS_LEFT | SS_NOTIFY},
{TEXT ("static"), IDC_RIGHTTEXT, TEXT ("Right text"),
10, 35, 120, 23, SS_RIGHT},
{TEXT ("static"), IDC_CENTERTEXT, TEXT ("Center text"),
10, 60, 120, 23, SS_CENTER | WS_BORDER},
};
// Structure labeling the static control WM_COMMAND notifications
NOTELABELS nlStatic[] = {{TEXT ("STN_CLICKED"), 0},
{TEXT ("STN_ENABLE "), 2},
{TEXT ("STN_DISABLE"), 3},
};
//----------------------------------------------------------------------
// InitStatWnd - StatWnd window initialization
//
int InitStatWnd (HINSTANCE hInstance) {
WNDCLASS wc;
// Register application StatWnd window class.
wc.style = 0; // Window style
wc.lpfnWndProc = StatWndProc; // Callback function
wc.cbClsExtra = 0; // Extra class data
wc.cbWndExtra = 0; // Extra window data
wc.hInstance = hInstance; // Owner handle
wc.hIcon = NULL, // Application icon
wc.hCursor = LoadCursor (NULL, IDC_ARROW);// Default cursor
wc.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
wc.lpszMenuName = NULL; // Menu name
wc.lpszClassName = STATWND; // Window class name
if (RegisterClass (&wc) == 0) return 1;
return 0;
}
//======================================================================
// Message handling procedures for StatWindow
//----------------------------------------------------------------------
// StatWndProc - Callback function for application window
//
LRESULT CALLBACK StatWndProc (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
int i;
//
// Search message list to see if we need to handle this
// message. If in list, call procedure.
//
for (i = 0; i < dim(StatWndMessages); i++) {
if (wMsg == StatWndMessages[i].Code)
return (*StatWndMessages[i].Fxn)(hWnd, wMsg, wParam, lParam);
}
return DefWindowProc (hWnd, wMsg, wParam, lParam);
}
//----------------------------------------------------------------------
// DoCreateStatWnd - Process WM_CREATE message for window.
//
LRESULT DoCreateStatWnd (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
int i;
for (i = 0; i < dim(Stats); i++) {
CreateWindow (Stats[i].szClass, Stats[i].szTitle,
Stats[i].lStyle | WS_VISIBLE | WS_CHILD,
Stats[i].x, Stats[i].y, Stats[i].cx, Stats[i].cy,
hWnd, (HMENU) Stats[i].nID, hInst, NULL);
}
return 0;
}
//----------------------------------------------------------------------
// DoCommandStatWnd - Process WM_COMMAND message for window.
//
LRESULT DoCommandStatWnd (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
TCHAR szOut[128];
int i;
for (i = 0; i < dim(nlStatic); i++) {
if (HIWORD (wParam) == nlStatic[i].wNotification) {
lstrcpy (szOut, nlStatic[i].pszLabel);
break;
}
}
if (i == dim(nlStatic))
wsprintf (szOut, TEXT ("notification: %x"), HIWORD (wParam));
SendMessage (GetParent (hWnd), MYMSG_ADDLINE, wParam,
(LPARAM)szOut);
return 0;
}


ScrollWnd.cpp
//======================================================================
// ScrollWnd - Scroll bar control window code
//
// Written for the book Programming Windows CE
// Copyright (C) 2001 Douglas Boling
//======================================================================
#include <windows.h> // For all that Windows stuff
#include "Ctlview.h" // Program-specific stuff
extern HINSTANCE hInst;
//----------------------------------------------------------------------
// Global data
//
// Message dispatch table for ScrollWndWindowProc
const struct decodeUINT ScrollWndMessages[] = {
WM_CREATE, DoCreateScrollWnd,
WM_HSCROLL, DoVScrollScrollWnd,
WM_VSCROLL, DoVScrollScrollWnd,
};
// Structure defining the controls in the window
CTLWNDSTRUCT Scrolls [] = {
{TEXT ("Scrollbar"), IDC_LRSCROLL, TEXT ("),
10, 10, 150, 23, SBS_HORZ},
{TEXT ("Scrollbar"), IDC_UDSCROLL, TEXT ("),
180, 10, 23, 120, SBS_VERT},
};
// Structure labeling the scroll bar control scroll codes for WM_VSCROLL
NOTELABELS nlVScroll[] = {{TEXT ("SB_LINEUP "), 0},
{TEXT ("SB_LINEDOWN "), 1},
{TEXT ("SB_PAGEUP "), 2},
{TEXT ("SB_PAGEDOWN "), 3},
{TEXT ("SB_THUMBPOSITION"), 4},
{TEXT ("SB_THUMBTRACK "), 5},
{TEXT ("SB_TOP "), 6},
{TEXT ("SB_BOTTOM "), 7},
{TEXT ("SB_ENDSCROLL "), 8},
};
// Structure labeling the scroll bar control scroll codes for WM_HSCROLL
NOTELABELS nlHScroll[] = {{TEXT ("SB_LINELEFT "), 0},
{TEXT ("SB_LINERIGHT "), 1},
{TEXT ("SB_PAGELEFT "), 2},
{TEXT ("SB_PAGERIGHT "), 3},
{TEXT ("SB_THUMBPOSITION"), 4},
{TEXT ("SB_THUMBTRACK "), 5},
{TEXT ("SB_LEFT "), 6},
{TEXT ("SB_RIGHT "), 7},
{TEXT ("SB_ENDSCROLL "), 8},
};
//----------------------------------------------------------------------
// InitScrollWnd - ScrollWnd window initialization
//
int InitScrollWnd (HINSTANCE hInstance) {
WNDCLASS wc;
// Register application ScrollWnd window class.
wc.style = 0; // Window style
wc.lpfnWndProc = ScrollWndProc; // Callback function
wc.cbClsExtra = 0; // Extra class data
wc.cbWndExtra = 0; // Extra window data
wc.hInstance = hInstance; // Owner handle
wc.hIcon = NULL, // Application icon
wc.hCursor = LoadCursor (NULL, IDC_ARROW);// Default cursor
wc.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
wc.lpszMenuName = NULL; // Menu name
wc.lpszClassName = SCROLLWND; // Window class name
if (RegisterClass (&wc) == 0) return 1;
return 0;
}
//======================================================================
// Message handling procedures for ScrollWindow
//----------------------------------------------------------------------
// ScrollWndProc - Callback function for application window
//
LRESULT CALLBACK ScrollWndProc (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
int i;
//
// Search message list to see if we need to handle this
// message. If in list, call procedure.
//
for (i = 0; i < dim(ScrollWndMessages); i++) {
if (wMsg == ScrollWndMessages[i].Code)
return (*ScrollWndMessages[i].Fxn)(hWnd, wMsg, wParam, lParam);
}
return DefWindowProc (hWnd, wMsg, wParam, lParam);
}
//----------------------------------------------------------------------
// DoCreateScrollWnd - Process WM_CREATE message for window.
//
LRESULT DoCreateScrollWnd (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
int i;
for (i = 0; i < dim(Scrolls); i++) {
CreateWindow (Scrolls[i].szClass, Scrolls[i].szTitle,
Scrolls[i].lStyle | WS_VISIBLE | WS_CHILD,
Scrolls[i].x, Scrolls[i].y, Scrolls[i].cx,
Scrolls[i].cy,
hWnd, (HMENU) Scrolls[i].nID, hInst, NULL);
}
return 0;
}
//----------------------------------------------------------------------
// DoVScrollScrollWnd - Process WM_VSCROLL message for window.
//
LRESULT DoVScrollScrollWnd (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
TCHAR szOut[128];
SCROLLINFO si;
int i, sPos;
// Update the report window.
if (GetDlgItem (hWnd, 101) == (HWND)lParam) {
for (i = 0; i < dim(nlVScroll); i++) {
if (LOWORD (wParam) == nlVScroll[i].wNotification) {
lstrcpy (szOut, nlVScroll[i].pszLabel);
break;
}
}
if (i == dim(nlVScroll))
wsprintf (szOut, TEXT ("notification: %x"), HIWORD (wParam));
} else {
for (i = 0; i < dim(nlHScroll); i++) {
if (LOWORD (wParam) == nlHScroll[i].wNotification) {
lstrcpy (szOut, nlHScroll[i].pszLabel);
break;
}
}
if (i == dim(nlHScroll))
wsprintf (szOut, TEXT ("notification: %x"), HIWORD (wParam));
}
SendMessage (GetParent (hWnd), MYMSG_ADDLINE, -1, (LPARAM)szOut);
// Get scroll bar position.
si.cbSize = sizeof (si);
si.fMask = SIF_POS;
GetScrollInfo ((HWND)lParam, SB_CTL, &si);
sPos = si.nPos;
// Act on the scroll code.
switch (LOWORD (wParam)) {
case SB_LINEUP: // Also SB_LINELEFT
sPos -= 2;
break;
case SB_LINEDOWN: // Also SB_LINERIGHT
sPos += 2;
break;
case SB_PAGEUP: // Also SB_PAGELEFT
sPos -= 10;
break;
case SB_PAGEDOWN: // Also SB_PAGERIGHT
sPos += 10;
break;
case SB_THUMBPOSITION:
sPos = HIWORD (wParam);
break;
}
// Check range.
if (sPos < 0)
sPos = 0;
if (sPos > 100)
sPos = 100;
// Update scroll bar position.
si.cbSize = sizeof (si);
si.nPos = sPos;
si.fMask = SIF_POS;
SetScrollInfo ((HWND)lParam, SB_CTL, &si, TRUE);
return 0;
}











When the CtlView program starts, the WM_CREATE handler of the main window, DoCreateFrame, creates a row of radio buttons across the top of the window, a list box for message reporting, and five different child windows. (The five child windows are all created without the WS_VISIBLE style, so they're initially hidden.) Each of the child windows in turn creates a number of controls. Before returning from DoCreateFrame, CtlView checks one of the auto radio buttons and makes the BtnWnd child window (the window that contains the example button controls) visible using ShowWindow.

The WM_SIZE handler of the main window, DoSizeMain, positions each of the child windows in the frame window. This needs to be done here because the window size parameters in WM_CREATE don't take into account the size of the caption bar.

As each of the controls on the child windows is tapped, clicked, or selected, the control sends WM_COMMAND messages to its parent window. That window in turn sends the information from the WM_COMMAND message to its parent, the frame window, using the application-defined message MYMSG_ADDLINE. There the notification data is formatted and displayed in the list box on the right side, or below on the Pocket PC, of the frame window.

The other function of the frame window is to switch between the different child windows. The application accomplishes this by displaying only the child window that matches the selection of the radio buttons across the top of the frame window. The processing for this is done in the WM_COMMAND handler, DoCommandFrame in notifications is to run the example program and use each of the controls. Figure 4-2 shows the Control View window with the button controls displayed. As each of the buttons is clicked, a BN_CLICKED notification is sent to the parent window of the control. The parent window simply labels the notification and forwards it to the display list box. Because the Check Box button isn't an auto check box, CtlView must manually change the state of the check box when a user clicks it. The other check boxes and radio buttons, however, do automatically change state because they were created with the BS_AUTOCHECKBOX, BS_AUTO3STATE, and BS_AUTORADIOBUTTON styles. The square button with the exclamation mark inside a triangular icon is an owner-draw button.


Figure 4-2: The Control View window with the button child window displayed in the left pane

The source code for each child window is contained in a separate file. The source for the window containing the button controls is contained in InitBtnWnd) that registers the window and a window procedure (BtnWndProc) for the window itself. The button controls themselves are created during the WM_CREATE message using CreateWindow. The position, style, and other aspects of each control are contained in an array of structures named Btns. The DoCreateBtnWnd function cycles through each of the entries in the array, calling CreateWindow for each one. Each child window in CtlView uses a similar process to create its controls.

To support the owner-draw button, BtnWndProc must handle the WM_DRAWITEM message. The WM_DRAWITEM message is sent when the button needs to be drawn because it has changed state, gained or lost the focus, or been uncovered. Although the DrawButton function (called each time a WM_DRAWITEM message is received) expends a great deal of effort to make the button look like a standard button, there's no reason a button can't have any look you want.

The other window procedures provide only basic support for their controls. The WM_COMMAND handlers simply reflect the notifications back to the main window. The ScrollWnd child window procedure, ScrollWndProc, handles WM_VSCROLL and WM_HSCROLL messages because that's how scroll bar controls communicate with their parent windows.

Controls and Colors


Finally, a word about colors. In CtlView, the frame window class is registered in a subtly different way from the way I've registered it in previous programs. In the CtlView example, I set the background brush for the frame window using the line

wc.hbrBackground = (HBRUSH)GetSysColorBrush (COLOR_STATIC);

This sets the background color of the frame window to the same background color I used to draw the radio buttons. The functionChapter 18.

/ 169