The COLORS1 program described in Chapter 6 created nine child windows to display three scroll bars and six text items. At that time, the program was one of the more complex we had developed. Converting COLORS1 to use a modeless dialog box makes the program_ and particularly its WndProc function—almost ridiculously simple. The revised COLORS2 program is shown in Figure 10-9.
COLORS2.MAK
#-----------------------
# COLORS2.MAK make file
#-----------------------
colors2.exe : colors2.obj colors2.def colors2.res
link colors2, /align:16, NUL, /nod slibcew libw, colors2
rc colors2.res
colors2.obj : colors2.c
cl -c -Gsw -Ow -W2 -Zp colors2.c
colors2.res : colors2.rc
rc -r colors2.rc
COLORS2.C
/*--------------------------------------------------------
COLORS2.C -- Version using Modeless Dialog Box Version
(c) Charles Petzold, 1990
--------------------------------------------------------*/
#include <windows.h>
long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;
BOOL FAR PASCAL ColorScrDlg (HWND, WORD, WORD, LONG) ;
HWND hDlgModeless ;
int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow)
{
static char szAppName[] = "Colors2" ;
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;
if (hPrevInstance)
return FALSE ;
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = NULL ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = CreateSolidBrush (0L) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;
RegisterClass (&wndclass) ;
hwnd = CreateWindow (szAppName, "Color Scroll",
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;
ShowWindow (hwnd, nCmdShow) ;
UpdateWindow (hwnd) ;
hDlgModeless = CreateDialog (hInstance, "ColorScrDlg", hwnd,
MakeProcInstance (ColorScrDlg, hInstance)) ;
while (GetMessage (&msg, NULL, 0, 0))
{
if (hDlgModeless == 0 || !IsDialogMessage (hDlgModeless, &msg))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
}
return msg.wParam ;
}
BOOL FAR PASCAL ColorScrDlg (HWND hDlg, WORD message, WORD wParam, LONG lParam)
{
static short color [3] ;
HWND hwndParent, hCtrl ;
short nCtrlID, nIndex ;
switch (message)
{
case WM_INITDIALOG :
for (nCtrlID = 10 ; nCtrlID < 13 ; nCtrlID++)
{
hCtrl = GetDlgItem (hDlg, nCtrlID) ;
SetScrollRange (hCtrl, SB_CTL, 0, 255, FALSE) ;
SetScrollPos (hCtrl, SB_CTL, 0, FALSE) ;
}
return TRUE ;
case WM_VSCROLL :
hCtrl = HIWORD (lParam) ;
nCtrlID = GetWindowWord (hCtrl, GWW_ID) ;
nIndex = nCtrlID - 10 ;
hwndParent = GetParent (hDlg) ;
switch (wParam)
{
case SB_PAGEDOWN :
color [nIndex] += 15 ; // fall through
case SB_LINEDOWN :
color [nIndex] = min (255, color [nIndex] + 1) ;
break ;
case SB_PAGEUP :
color [nIndex] -= 15 ; // fall through
case SB_LINEUP :
color [nIndex] = max (0, color [nIndex] - 1) ;
break ;
case SB_TOP :
color [nIndex] = 0 ;
break ;
case SB_BOTTOM :
color [nIndex] = 255 ;
break ;
case SB_THUMBPOSITION :
case SB_THUMBTRACK :
color [nIndex] = LOWORD (lParam) ;
break ;
default :
return FALSE ;
}
SetScrollPos (hCtrl, SB_CTL, color [nIndex], TRUE) ;
SetDlgItemInt (hDlg, nCtrlID + 3, color [nIndex], FALSE) ;
DeleteObject (GetClassWord (hwndParent, GCW_HBRBACKGROUND)) ;
SetClassWord (hwndParent, GCW_HBRBACKGROUND,
CreateSolidBrush (RGB (color [0], color [1], color [2]))) ;
InvalidateRect (hwndParent, NULL, TRUE) ;
return TRUE ;
}
return FALSE ;
}
long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
{
switch (message)
{
case WM_DESTROY :
DeleteObject (GetClassWord (hwnd, GCW_HBRBACKGROUND)) ;
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
COLORS2.RC
/*----------------------------
COLORS2.RC resource script
----------------------------*/
#include <windows.h>
#define SBS_VERT_TAB (SBS_VERT | WS_TABSTOP)
ColorScrDlg DIALOG 8, 16, 124, 132
STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_VISIBLE
CAPTION "Color Scroll Scrollbars"
{
CONTROL "&Red", -1, "static", SS_CENTER, 10, 4, 24, 8
CONTROL "", 10, "scrollbar", SBS_VERT_TAB, 10, 16, 24, 100
CONTROL "0", 13, "static", SS_CENTER, 10, 120, 24, 8
CONTROL "&Green", -1, "static", SS_CENTER, 50, 4, 24, 8
CONTROL "", 11, "scrollbar", SBS_VERT_TAB, 50, 16, 24, 100
CONTROL "0", 14, "static", SS_CENTER, 50, 120, 24, 8
CONTROL "&Blue", -1, "static", SS_CENTER, 90, 4, 24, 8
CONTROL "", 12, "scrollbar", SBS_VERT_TAB, 90, 16, 24, 100
CONTROL "0", 15, "static", SS_CENTER, 90, 120, 24, 8
}
COLORS2.DEF
;------------------------------------
; COLORS2.DEF module definition file
;------------------------------------
NAME COLORS2
DESCRIPTION 'Color Scroll with Dialog Box (c) Charles Petzold, 1990'
EXETYPE WINDOWS
STUB 'WINSTUB.EXE'
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE MULTIPLE
HEAPSIZE 1024
STACKSIZE 8192
EXPORTS WndProc
ColorScrDlg
Although the original COLORS1 program displayed scroll bars that were based on the size of the window, the new version keeps them at a constant size within the modeless dialog box, as shown in Figure 10-10.
The dialog box template in COLORS2.RC uses CONTROL statements for all nine child windows in the dialog box. The modeless dialog box is created in COLORS2's WinMain function following the ShowWindow call for the program's main window. Note that the window style for the main window includes WS_CLIPCHILDREN, which allows the program to repaint the main window without erasing the dialog box.
The dialog box window handle returned from CreateDialog is stored in the global variable hDlgModeless and tested during the message loop, as described above. In this program, however, it isn't necessary to store the handle in a global variable or to test the value before calling IsDialogMessage. The message loop could have been written like this:
while (GetMessage (&msg, NULL, 0, 0))
{
if (!IsDialogMessage (hDlgModeless, &msg))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
}
Because the dialog box is created before the program enters the message loop and the dialog box is not destroyed until the program terminates, the value of hDlgModeless will always be valid. I included the logic in case you want to add some code to the dialog box window procedure to destroy the dialog box:
case WM_CLOSE :
DestroyWindow (hDlg) ;
hDlgModeless = 0 ;
break ;
In the original COLORS1 program, SetWindowText set the values of the three numeric labels after converting the integers to text with itoa. The code looked like this:
SetWindowText (hwndValue[n], itoa (color[n], szBuffer, 10)) ;
The value of n was the ID number of the current scroll bar being processed, and hChValue was an array containing the window handles of the three static text child windows for the numeric values of the colors.
The new version uses SetDlgItemInt to set each text field of each child window to a number:
SetDlgItemInt (hDlg, nCtrlID + 3, color [nCtrlID], FALSE) ;
(Although SetDlgItemInt and its companion, GetDlgItemInt, are most often used with edit controls, they can also be used to set the text field of other controls, such as static text controls.) The nCtrlID variable is the ID number of the scroll bar; adding 3 to the number converts it to the ID for the corresponding numeric label. The third parameter is the color value. Normally, the fourth parameter would be set to TRUE to indicate that numbers greater than 32,767 should be displayed as negatives. For this program, however, the values range from 0 to 255, so the fourth parameter has no effect.
In the process of converting COLORS1 to COLORS2, we passed more and more of the work to Windows. The earlier version called CreateWindow 10 times; the new version calls CreateWindow once and CreateDialog once. But if you think that we've reduced our CreateWindow calls to a minimum, get a load of this next program.