[翻译]-WinCE 程序设计 (3rd 版)--4.3 Windows控件

如果没有Windows控件库的话,编写Windows程序将会是一件缓慢而又艰苦的事情。并且,每个程序将会有自己的外观和反应。这会迫使用户对每种新的应用程序都要学习一套新的操作方式。幸运的是,通过操作系统提供的一系列控件,避免了上述情况的发生。简单的说,控件只不过是预先定义好的窗口类。每个类有一个Windows提供的特定的窗口过程,给这些控件提供预定义的用户和编程接口。

因为控件只是又一个窗口,所以可用CreateWindows或CreateWindowEx来创建窗口。控件是通过事件来通知父窗口的,而事件中包含WM_COMMAND消息,并且控件ID和句柄都编码在消息的参数中。

和所有的消息一样,WM_COMMAND含有wParam和lParam这两个通用参数。对一个WM_COMMAND消息来说,wParam的高字位含有通知码,用来说明发送该消息的原因。wParam的低字位含有发送该消息的控件的ID,通常在创建控件的时候定义该ID,为了更好的使用,应该保证ID在控件的所有兄弟窗口中是唯一的。lParam则包含了控件窗口的句柄。通常,通过控件ID来追踪WM_COMMAND消息来源比通过控件的窗口句柄来追踪要更容易一些,不过这两个信息都可以从该消息中获得。下面是典型的WM_COMMAND消息处理程序中的头几行代码:
case WM_COMMAND:
    WORD idItem, wNotifyCode;
    HWND hwndCtl;
  
    // Parse the parameters.
    idItem = (WORD) LOWORD (wParam);
    wNotifyCode = (WORD) HIWORD(wParam);
    hwndCtl = (HWND) lParam;

从这里可以看出,WM_COMMAND消息处理程序通常是用控件ID和通知码来判断WM_COMMAND消息发送的原因。

通过给控件发送预定义的消息,可以配置和操纵控件。除了这些事情,应用程序可以设置按钮的状态,给列表框增加或删除项,设置编辑框中选择的文本,所有这些都是通过给控件发送消息来完成的。控件通常是通过ID来标识的,但许多Window函数都要求用控件的句柄。这时可以用GetDlgItem函数来进行简单的转换。函数原型如下:
HWND GetDlgItem(HWND hDlg, int nIDDlgItem);
两个参数分别是控件的父窗口句柄和控件的ID。虽然从名字上看这个函数只能用在对话框上,但实际上它能用在任何窗口的控件上。关于对话框我将在第六章进行讨论。

另外一个很方便的函数是SendDlgItemMessage,用于给控件发送消息。该函数发送消息给指定ID的子窗口。函数原型如下:
LONG SendDlgItemMessage (HWND hParent, int nIDChild, UINT Msg, WPARAM wParam, LPARAM lParam);
这些参数和SendMessage中的很类似。实际上,下面的这段代码从功能上讲和SendDlgItemMessage是一样的。
LONG SendMessage (GetDlgItem (hParent, nIDChild), Msg, wParam, lParam);
唯一的不同在于方便性,SendMessage没有嵌入GetDlgItem而已。

下面是六个预定义的窗口控件类。它们是:
Button 各种按钮。
Edit   一种用于输入和显示文本的窗口。
List   一种包含字符串列表的窗口
Combo  编辑框和列表框的组合。
Static 显示用户不能编辑的文本或图片的窗口
Scroll bar 未和具体窗口进行绑定滚动条

每个控件都有很多函数,所以本章难以涉及全部。但我会快速浏览一下这些控件,对重要的函数也会提及的。后面还会为您展示一个示例程序--CtlView,用来演示这些控件及它们同父窗口之间的交互。

按钮控件
按钮控件允许有几种输入方式,并且有许多风格,包括下压按钮、复选框和单选框。每种风格都是为特定用途设计的,例如,下压按钮用来接收即时输入,复选框用于开/关(on/off)输入,单选框用于从多个选择中挑选一个。

下压按钮
下压按钮通常用于激发某种行为。当用户用手写笔按一个下压按钮,按钮会发送WM_COMMAND消息,其中wParam参数的高字位包含BN_CLICKED(用于按钮被单击的通知)通知码。

复选框
复选框包括一个正方形的框和一个标签,用来让用户指定选择。复选框会保持选择或未选择状态,直到用户点击它或者程序强制按钮改变状态。除了标准BS_CHECKBOX风格外,复选框还有一个三态风格BS_3STATE,允许按钮失效并显示为灰色。另两个风格BS_AUTOCHECKBOX和BS_AUTO3STATE,会自动更新控件的状态和外观来反映出选择或未选择以及三态复选框下的失效风格。

和下压按钮一样,当单击复选框时,它会发送BN_CLICKED通知。除非复选框具有前面说的自动风格,否则需要应用程序负责手工改变按钮的状态。通过给按钮发送BM_SETCHECK消息来设置按钮的状态,其中,把参数wParam设置为0来取消按钮选择,设置为1则是选择按钮。对于三态复选框,可以将BM_SETCHECK消息的wParam参数设置为2来表示按钮失效状态。通过BM_GETCHECK消息,应用程序可以判断当前按钮状态。

单选框
单选框允许用户从多个选项里进行选择。单选框将多个按钮集分组,每组集合里同时只有一个能被选中。如果使用的是标准风格BS_RADIOBUTTON,则需要由应用程序负责选中或取消选中单选框,来保证同时只有一个被选中。当然,和复选框一样,单选框有一个BS_AUTORADIONBUTTON风格,用来自动维护按钮组的状态,来保证只有一个被选择。

分组框
您可能会奇怪,分组框也是一种按钮。分组框看起来象一个带有文本标签的中空的框,包围着被分成一组的控件集。分组框只是用于分组,除了其上的标题文字外,没有其它编程的接口,该标题文字是在创建分组框的时候作为其窗口标题文字而指定的。应该在创建分组框内的控件后创建分组框。这可以保证分组框在Z坐标上低于其内的控件。

在Windows CE设备上使用分组框应该小心。问题不在于分组框自身,而在于Windows CE较小的屏幕。分组框占用了颇有价值的屏幕,而这本应该可以被功能控件更好的使用的。尤其是在具有非常小的屏幕的Pocket PC上会出现这种情况。在许多情况下,在控件集之间绘制一条线就可以明显将控件分组了,这和使用分组框的效果一样。

定制按钮外观
通过使用许多附加风格,您可以进一步定制目前提到的按钮的外观。风格BS_RIGHT,BS_LEFT,BS_BOTTOM和BS_TOP允许您指定按钮文本的位置,而不是使用默认在按钮上居中的风格。BS_MULTILINE风格允许您在按钮上指定多行文本。文字会自动调整来适合按钮的。按钮文字中的换行符(/n)可以用来具体指定从哪里换行。Windows CE并不支持BS_ICON和BS_BITMAP按钮风格。

自绘制按钮
通过指定BS_OWNERDRAW风格,您可以完全控制按钮的外观。当按钮被指定为自绘制风格,则由拥有按钮的窗口负责绘制按钮可能出现的所有状态。当窗口包含有自绘制按钮时,会收到一个WM_DRAWITME消息,用来通知窗口有一个按钮需要绘制。对于该消息,wParam参数包含有按钮ID,lParam则指向一个DRAWITEMSTRUCT结构,该结构定义如下:
typedef struct tagDRAWITEMSTRUCT
{
    UINT CtlType;
    UINT CtlID;
    UINT itemID;
    UINT itemAction;
    UINT itemState;
    HWND hwndItem;
    HDC  hDC;
    RECT rcItem;
    DWORD itemData;
} DRAWITEMSTRUCT;
CtlType设置为ODT_BUTTON(自绘制按钮),CtlID则和wParam一样,包含有按钮ID。itemAction包含标志位,指出需要绘制什么和为什么绘制。这些域中最重要的是itemState,它包含了按钮选择、失效等状态。hDC包含了按钮窗口的设备描述表句柄,rcItem则包含了按钮的尺寸。对自绘制按钮来说,itemDate应该设置为NULL。

正如您所期望的,WM_DRAWITEM处理程序中包含有许多GDI函数,用来绘制线条、矩形以及绘制按钮所需要的一切。绘制按钮的一个重要方面是要和系统中其它窗口的标准颜色匹配。因为这些颜色是可变的,所以不能硬编码到程序里。您可以通过GetSysColor函数来查询出合适的颜色。函数原型如下:DWORD GetSysColor(int nIndex);
该函数返回的是为系统中窗口和控件的不同外观而定义的颜色的RGB值。在众多作为参数的预定义索引值中,COLOR_BTNFACE返回的是按钮的表面颜色,COLOR_BTNSHADOW返回的是用于创建三维按钮的暗色。

编辑框
编辑框是一种允许用户输入和编辑文本的窗口。正如您所猜想的,编辑框是Windows控件库中最方便的控件之一。编辑框具有完整的编辑功能,包括与系统剪切板交互来剪切、复制和粘贴等,而这些都不需要应用程序协助。编辑框可以显示单行文本,或者当指定EX_MULTILINE风格时,显示多行文本。桌面版Windows中提供的记事本就是一个包含多行编辑框的顶层窗口。

编辑框有一些其它特性需要提一下。具有ES_PASSWORD风格的编辑框默认将输入的每个字符显示为星号(*)。控件保存了真正的字符。ES_READONLY风格则保护控件里的文本只能读和复制,但不能被修改。ES_LOWERCASE和ES_UPPERCASE风格则把输入的字符强制转换为对应的大小写。

通过WM_SETTEXT消息可以给编辑框添加文本,通过WM_GETTEXT消息可以检索文本。通过EM_SETSEL消息可以控制文本选择。该消息指定选择区域的起始字符和终止字符。其它消息允许查询和设置光标(指出在编辑域里的当前入口点)的位置。多行编辑框包含许多其它消息,比如控制滚动、按行列位置访问字符等。

列表框控件
列表框控件显示一个文本项列表,用户可以从中选择一个或多个项。列表框可以存储文本,还可以对列表项排序,管理列表项的显示,包括滚动显示等。通过设置列表框,可以允许单选、多选或者禁止选择列表项。

通过给列表框发送LB_ADDSTRING或LB_INSERTSTRING消息,传递指向字符串的指针到lParam参数里,就可以增加列表项。LB_ADDSTRING消息会把新添加的字符串放到列表项的尾部,而LB_INSERTSTRING可以把字符串放到列表项的任何位置。使用LB_FIND消息可以在列表框中搜索特定的项。

使用LB_GETCURSEL可以查询单选列表框的选择状态。对于多选框,使用LB_GETSELCOUNT和LB_GETSELITEMS来检索当前选择的项。使用LB_SETCURSEL和LB_SETSEL消息,可以用编程的方式来选择列表框里的项。

除了不支持自绘制列表框以及LB_DIR消息族,Windows CE支持大部分其它版本Windows所支持的列表框功能。Windows CE支持一种新风格LBS_EX_CONSTRINGDATA。具有这种风格的列表框不保存传给它的字符串,而是保存指向字符串的指针,并且由应用程序负责维护字符串。对于可能从资源文件中装载大型字符串数组的情况,使用这种处理方式可以节省内存,因为列表框不用维护一个字符串列表的单独副本。

组合框
组合框(如名字所暗示的)是控件的组合,也就是一个单行编辑框控件和列表框的组合。在从多个选项列表中选择一项或者为预定义的建议输入值提供编辑功能时,组合框是很节省空间的。在Windows CE下,组合框具有两种风格:下拉方式和下拉列表方式。(简化方式的组合框并不支持。)下拉风格的组合框包括一个编辑区域和一个位于右端的按钮。点击按钮会显示一个列表框,包含了更多选项。在选项上选择一个,会用选择的内容填写组合框的编辑域。下拉列表风格的组合框使用静态文本控件替代了编辑框。这允许用户从列表中选择一项,并防止用户输入不在列表中的项。

因为组合框组合了编辑控件和列表框控件,所以组合框的消息列表就像是这两个基本控件的消息组合。CB_ADDSTRING,CB_INSERTSTRING和CB_FINDSTRING的效果和列表框里中的同类消息类似。同样地,对下拉风格或下拉列表风格的组合框使用CB_SETEDITSELECT和CB_GETEDITSELECT消息,可以设置和查询编辑框里被选择的字符。而要控制下拉风格或下拉列表风格的组合框的下拉状态,可以使用CB_SHOWDROPDOWN和CB_GETDROPPEDSTATE消息。

和列表框的情况一样,Windows CE不支持自绘制组合框。但是组合框支持扩展风格--CBS_EX_CONSTSTRINGDATA,具有这种风格的组合框不保存列表项字符串,而是保存指向字符串的指针。和具有LBS_EX_CONSTSTRINGDATA风格的列表框一样,当应用程序有存储在ROM里的大型字符串数组时,这种处理方式可以节省内存,因为组合框不用维护字符串列表的单独副本。

静态控件
静态控件是显示文本、图标或者位图的窗口,不具有用户交互性。可以使用静态文本控件来标记窗口中的其它控件。静态文本控件显示的效果是由文本和控件风格来决定的。在Windows CE下,静态控件支持以下几种风格:
SS_LEFT 文本左对齐显示。如果需要,文本会折行来适应控件大小。
SS_CENTER 将文本在控件中间显示。如果需要,文本会折行来适应控件大小。
SS_RIGHT 文本右对齐显示。如果需要,文本会折行来适应控件大小。
SS_LEFTNOWORDWRAP 文本左对齐显示。文本不会折成多行显示,任何超出控件右边界的文本都会被裁剪掉。
SS_BITMAP 显示一个位图。控件的窗口文字指定了包含位图的资源的名字。
SS_ICON   显示一个位图。控件的窗口文字指定了包含图标的资源的名字。

具有SS_NOTIFY风格的静态控件在被点击、有效或失效的时候,会发送WM_COMMAND消息,但是Windows CE版的静态控件在双击的时候不会发送通知消息。SS_CENTERIMAGE风格在和SS_BITMAP或SS_ICON一起使用的时候,会使图片在控件上居中显示。SS_NOPREFIX风格可以和文本风格一起组合使用。能够避免将&符号解释为转义符号,防止把下个字符解释成加速字符。

Windows CE不支持具有SS_WHITEFRAME或SS_BLACKRECT风格的显示成填充矩形或中空矩形的静态控件;也不支持自绘制静态控件。

滚动条控件
滚动条控件是一个与其它控件有些不同的家伙。通常滚动条是绑定在窗口的侧边,用来控制窗口里数据的显示的。实际上,诸如编辑框和列表框等窗口控件都内部使用了滚动条控件。正是因为这种和父窗口的紧密关系,使得滚动条的接口与其它控件的有所不同。

滚动条使用WM_VSCROLL和WM_HSCROLL消息而不是WM_COMMAND消息来报告行为。垂直滚动条会发送WM_VSCROLL消息,水平滚动条会发送WM_HSCROLL消息。另外,不使用SB_SETPOSITION消息来设置滚动条的位置,而是有专用的函数来完成。下面来看一下这个独特的接口。

滚动条消息
一旦用户点击垂直滚动条来改变其位置的时候,WM_VSCROLL消息会发送到垂直滚动条的拥有者上。WM_HSCROLL则是当用户点击水平滚动条的时候发送到其拥有者上的。对这两个消息来说,wParam和lParam参数是一样的。wParam的低字位包含的代码指出为什么会发送该消息。图4-1显示了水平和垂直滚动条以及如何在滚动条不同位置点击来产生不同的消息。wParam的高字位包含滑块的位置,但仅仅在您处理SB_THUMBPOSITION和SB_THUMBTRACK代码(后面将简单介绍它们)时,这个值才是有效的。如果发送消息的滚动条是独立的没有绑定到窗口的控件,那么lParam参数则包含有滚动条的窗口句柄。

图4-1(略):滚动条和其热点。
滚动条发送的消息代码允许程序对滚动条支持的所有用户行为作出响应。表4-1列出了每个代码对应的行为。
表4-1:滚动代码

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.


SB_LINExxx和SB_PAGExxx代码是相当易懂的。每次您可以将滚动位置移动一行或者一页。SB_THUMBPOSITION和SB_THUMBTRACK可以用两种方式之一来处理。当用户拖动滚动条滑块时,滚动条会发送SB_THUMBTRACK代码,这样程序可以交互地跟踪滑块的拖动。如果您的应用程序足够快,您就可以只处理SB_THUMBTRACK代码并交互的更新显示。如果您填写了SB_THUMBTRACK代码,但是您的应用程序必须快到足以重绘显示,这样滑块在拖动过程中不会出现停顿。在运行Windows CE的较慢设备上,这会是一个问题。

如果您的应用程序(或者操作系统)太慢,不能为每个SB_THUMBTRACK代码进行快速更新显示,您可以忽略掉SB_THUMBTRACK,并等待在用户拖动滚动条滑块时发出的SB_THUMBPOSITION代码。这样在用户移动完滑块后,您只需要更新显示一次即可。

配置滚动条
要使用滚动条,应用程序首先应该设置滚动范围的最小值和最大值以及初始位置。和桌面系统里的滚动条一样,Windows CE滚动条支持按比例调整滑块大小,这可以给用户提供反馈,了解当前看到的页占整个滚动范围的比例。要设置这些参数,Windows CE应用程序可以使用SetScrollInfo函数,其原型如下:
int SetScrollInfo(HWND hwnd, int fnBar, LPSCROLLINFO lpsi, BOOL fRedraw);

第一个参数可以是包含滚动条的窗口的句柄,也可以是滚动条自身的窗口句柄。第二个参数fnBar是一个标志位,用于判断窗口句柄的用法。该标志位可以是下面三个值之一:SB_HORZ用于窗口中标准水平滚动条;SB_VERT用于窗口中标准垂直滚动条;SB_CTL用于独立的滚动条控件。除非滚动条是控件,否则窗口句柄是包含滚动条的窗口的句柄。在句柄是滚动条控件自身的句柄时,使用SB_CTL。最后一个参数时fRedDraw,一个布尔值,指出是否在调用完成后重新绘制滚动条。第三个参数是指向SCROLLINFO结构的指针,该结构定义如下:
typedef struct tagSCROLLINFO
{
       UINT cbSize;
       UINT fMask;
       int nMin;
       int nMax;
       UINT nPage;
       int nPos;
       int nTrackPos;
} SCROLLINFO

该结构允许您完整的指定滚动条参数。cbSize必须设置成SCROLLINFO结构的大小。fMask是标志位,指出结构中其它域包含什么样的有效数据。nMin和nMax包含滚动条的最小和最大滚动值。如果fMask参数包含SIF_RANGE标志,Windows就会在这两个域中查找这些值。同样地,如果fMask包含SIF_POS标志,nPos在预定义的范围内设置滚动条位置。

nPage域允许程序定义屏幕当前可视区域相对于整个滚动区域的大小。这可以给用户一个大致印象,整个滚动区域中当前有多少是可视的。只有当fMask中包含SIF_PAGE标志的时候这个域才有用。SCROLLINFO结构的最后一个成员是nTrackPos,但SetScrollInfo不使用并忽略掉它了。

fMask最后一个标志是SIF_DISABLENOSCROLL,可以让滚动条失效但可视。当整个滚动范围在可视区域内可视且不再需要滚动的时候,用这个方法是很方便的。在这种情况下,使滚动条失效比简单的移去整个滚动条更好一些。

细心的读者一定会注意到SCROLLINFO结构中域的宽度问题。nMin,nMax和nPos是整型,在Windows CE中是32位宽。而另一方面,WM_HSCROLL和WM_VSCROLL消息只能在WParam参数的高字位中返回一个16位的位置数据。如果您使用的滚动范围超过65,535,那么可以使用GetScrollInfo函数,其原型如下:BOOL GetScrollInfo (HWND hwnd, int fnBar, LPSCROLLINFO lpsi);

和SetScrollInfo一样,fnBar中的标志位用来指出传递给函数的窗口句柄种类。SCROLLINFO结构同SetScrollInfo中的一样。在传给GetScrollInfo之前,必须先用结构的大小来始化cbSize。应用程序必须通过设置fMask中的适当标志,来指明希望函数返回什么数据。fMask中使用的标志同SetScrollInfo中使用的一样,同时增加了两个。现在可以传递SIF_TRACKPOS标志来让滚动条返回当前滑块位置。在WM_xSCROLL消息期间调用时,nTrackPos包含实时位置,而nPos包含的是开始拖动滑块时的滚动条位置。

滚动条是与众不同的控件,因为只要简单指定窗口风格,就可以很容易地把滚动条添加到窗口中;另外它是放置在窗口客户区域外边地。原因是应用程序普遍需要滚动条,所以Windows的开发者就尽量使绑定滚动条到窗口的工作更容易。现在,来看一看其它基本Windows控件。

CtlView示例程序
清单4-1给出了CtlView示例程序,它演示了我刚才描述的所有控件。
该示例程序使用了几个应用程序定义的子窗口,其中包含了各种控件。您可以通过点主窗口顶部的5个单选框,在不同子窗口之间进行切换。

当每个控件通过WM_COMMAND消息来发送通知时,通知消息会显示在窗口右边的列表框里。CtlView可以很方便的观察到控件给父窗口发送什么了消息和什么时候发送的消息。CtlView可根据屏幕宽度使用不同控件布局。这意味着即使在具有狭窄屏幕的Pocket PC上,依然可以看到所有控件。

清单4-1:CtlView 程序
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;
}

当CtlView启动时,主窗口WM_CREATE消息的处理程序DocreateFrame会在窗口顶部创建一行单选按钮,一个用于消息报告的列表框,以及5个子窗口。(这5个子窗口创建的时候都没有使用WS_VISIBLE风格,所以最初他们是隐藏的。)每个子窗口依次创建了许多控件。在DoCreateFrame返回之前,CtlView选中一个自动风格的单选框,并使用ShowWindow使BtnWnd子窗口(包含示例按钮控件的窗口)可视。

主窗口WM_SIZE处理程序DoSizeMain用来决定框架窗口中每个子窗口的位置。之所以要在这里处理,是因为在WM_CREATE里的窗口尺寸参数没有计算标题栏的尺寸。

当子窗口中的各个控件被压触、点击或者选中,控件会发送WM_COMMAND消息给其父窗口。该父窗口又将WM_COMMAND消息里的信息通过MYMSG_ADDLINE消息发送给自己的父窗口--框架窗口,MYMSG_ADDLINE消息是应用程序自定义的消息。在那里,通知消息中的数据被格式化并显示在主框架右边的列表框里,如果是Pocket PC的话则显示在主框架的下面。

框架窗口的另一个功能是在不同子窗口之间切换。通过只显示框架窗口顶部单选按钮选中的子窗口,应用程序达到切换的目的。这一处理过程位于CtlVoiew.cpp中处理WM_COMMAND的DoCommandFrame例程中。

观察控件如何及何时发送通知的最好方法就是运行示例程序并使用每个控件。图4-2给出了显示按钮控件时的CtlView示例窗口。当每个按钮被点击,会发送一个BN_CLICKED通知给控件的父窗口。父窗口简单标记该通知并显示在列表框里。因为复选框不是自动风格的,所以当用户选择的时候,CtlView必须手工改变复选框的状态。其它复选框何单选框是可以自动改变状态,因为它们是使用BS_AUTOCHECKBOX, BS_AUTO3STATE, 和BS_AUTORADIOBUTTON风格创建的。在三角形图标里有个感叹号的那个矩形按钮是一个自绘制按钮。
图4-2(略):在左边格子中显示按钮子窗口的CtlView窗口。

每个子窗口的源代码都包含在一个独立的文件中。包含按钮控件的源代码包含在BtnWnd.cpp中。该文件包含用于窗口自身的注册窗口及窗口过程的初始化例程。按钮控件自身在WM_CREATE消息中使用CreateWindow来创建。每个控件的位置、风格和其它外观都包含在Btns结构数组中。

DoCreateBtnWnd函数循环遍历数组里的每个入口,并为每个入口调用CreateWindow。CtlView中的每个子窗口都使用了类似的过程来创建自己的控件。

为了支持自绘制按钮,BtnWndProc必须处理WM_DRAWITEM消息。当按钮因为状态改变、获得/失去焦点或者从覆盖中暴露而需要重新绘制的时候,会发送WM_DRAWITEM消息。尽管DrawButton函数(每当收到一个WM_DRAWITEM小时就被调用)花了很大力气来使按钮看上去像一个标准按钮,但一个按钮不能有我们需要的外观是没有理由的。

另外的窗口过程只为它们的控件提供了基本支持。WM_COMMAND处理程序只是简单的将通知消息反射回主窗口。ScrollWnd子窗口过程ScrollWndProc处理WM_VSCROLL和WM_HSCROLL消息,因为滚动条控件就是通过这些消息和父窗口通信的。

控件和颜色
最后,简单说一下颜色。在CtlView中,框架窗口类的注册方式同先前程序里的注册方式略微有所不同。在本例中,我使用下面代码来设置框架窗口的背景画刷。
wc.hbrBackground = (HBRUSH) GetSysColorBrush (COLOR_STATIC);
这样设置的框架窗口背景色和用于绘制单选框的背景色是一样的。GetSysColorBrush返回一个画刷,它和系统用于绘制各种对象的颜色相匹配。在本例中,给GetSysColorBrush传入的是常量COLOR_STATIC,会返回Windows用来绘制静态文本以及复选框、单选框文本的背景色。这使框架窗口背景和静态文本的背景一致。

在包含按钮控件的窗口里,通过产生WM_CTLCOLORSTATIC消息,改变复选框和单选框的背景色来和窗口的白色背景匹配。当绘制复选框或单选框的时候,该控件会向父窗口询问使用何种颜色,此时该消息被发送给静态控件或按钮控件的父窗口。在CtlView中,按钮窗口返回一个白色画刷的句柄,这使得控件的背景色何窗口的白色背景相匹配。通过产生WM_CTLCOLORBUTTON消息,您可以修改下压按钮的颜色。其它控件会发送不同的WM_CTLCOLORxxx消息,这样父窗口就可以修改用于绘制控件的颜色了。另一个使用WM_CTLCOLORSTATIC消息的例子请参见第18章的PowerBar示例程序。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值