二、Windows非模态对话框—新的COLORS程序
第九章中所描述的COLORS1程序建立了九个子窗口,以便显示三个滚动条和六个文字项。那时候,这个程序还是我们所写过的程序中相当复杂的一个。如果将COLORS1转换为使用非模态对话框则会使程序-特别是WndProc函数-变得令人难以置信的简单,修正后的COLORS2程序如程序11-4所示。
COLORS2.C /*---------------------------------------------------------------------------- COLORS2.C -- Version using Modeless Dialog Box (c) Charles Petzold, 1998 ----------------------------------------------------------------------------*/ #include <windows.h> LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; BOOL CALLBACK ColorScrDlg (HWND, UINT, WPARAM, LPARAM) ; HWND hDlgModeless ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("Colors2") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = CreateSolidBrush (0L) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox ( NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("Color Scroll"), WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; hDlgModeless = CreateDialog (hInstance, TEXT ("ColorScrDlg"), hwnd, ColorScrDlg) ; while (GetMessage (&msg, NULL, 0, 0)) { if (hDlgModeless == 0 || !IsDialogMessage (hDlgModeless, &msg)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam) { switch (message) { case WM_DESTROY : DeleteObject ((HGDIOBJ) SetClassLong (hwnd, GCL_HBRBACKGROUND, (LONG) GetStockObject (WHITE_BRUSH))) ; PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } BOOL CALLBACK ColorScrDlg (HWND hDlg, UINT message,WPARAM wParam, LPARAM lParam) { static int iColor[3] ; HWND hwndParent, hCtrl ; int iCtrlID, iIndex ; switch (message) { case WM_INITDIALOG : for (iCtrlID = 10 ; iCtrlID < 13 ; iCtrlID++) { hCtrl = GetDlgItem (hDlg, iCtrlID) ; SetScrollRange (hCtrl, SB_CTL, 0, 255, FALSE) ; SetScrollPos (hCtrl, SB_CTL, 0, FALSE) ; } return TRUE ; case WM_VSCROLL : hCtrl = (HWND) lParam ; iCtrlID = GetWindowLong (hCtrl, GWL_ID) ; iIndex = iCtrlID - 10 ; hwndParent = GetParent (hDlg) ; switch (LOWORD (wParam)) { case SB_PAGEDOWN : iColor[iIndex] += 15 ; // fall through case SB_LINEDOWN : iColor[iIndex] = min (255, iColor[iIndex] + 1) ; break ; case SB_PAGEUP : iColor[iIndex] -= 15 ; // fall through case SB_LINEUP : iColor[iIndex] = max (0, iColor[iIndex] - 1) ; break ; case SB_TOP : iColor[iIndex] = 0 ; break ; case SB_BOTTOM : iColor[iIndex] = 255 ; break ; case SB_THUMBPOSITION : case SB_THUMBTRACK : iColor[iIndex] = HIWORD (wParam) ; break ; default : return FALSE ; } SetScrollPos (hCtrl, SB_CTL, iColor[iIndex], TRUE) ; SetDlgItemInt (hDlg, iCtrlID + 3, iColor[iIndex], FALSE) ; DeleteObject ((HGDIOBJ) SetClassLong (hwndParent, GCL_HBRBACKGROUND, (LONG) CreateSolidBrush ( RGB (iColor[0], iColor[1], iColor[2])))) ; InvalidateRect (hwndParent, NULL, TRUE) ; return TRUE ; } return FALSE ; }
//Microsoft Developer Studio generated resource script. #include "resource.h" #include "afxres.h" ///////////////////////////////////////////////////////////////////////////// // Dialog COLORSCRDLG DIALOG DISCARDABLE 16, 16, 120, 141 STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION CAPTION "Color Scroll Scrollbars" FONT 8, "MS Sans Serif" BEGIN CTEXT "&Red",IDC_STATIC,8,8,24,8,NOT WS_GROUP SCROLLBAR 10,8,20,24,100,SBS_VERT | WS_TABSTOP CTEXT "0",13,8,124,24,8,NOT WS_GROUP CTEXT "&Green",IDC_STATIC,48,8,24,8,NOT WS_GROUP SCROLLBAR 11,48,20,24,100,SBS_VERT | WS_TABSTOP CTEXT "0",14,48,124,24,8,NOT WS_GROUP CTEXT "&Blue",IDC_STATIC,89,8,24,8,NOT WS_GROUP SCROLLBAR 12,89,20,24,100,SBS_VERT | WS_TABSTOP CTEXT "0",15,89,124,24,8,NOT WS_GROUP END
// Microsoft Developer Studio generated include file. // Used by Colors2.rc #define IDC_STATIC -1
原来的COLORS1程序所显示的滚动条大小是依据窗口大小决定的,而新程序在非模态对话框内以固定的尺寸来显示它们,如图11-4所示。
当您建立对话框模板时,直接将三个滚动条的ID分别设为10、11和12,将显示滚动条目前值的三个静态文字字段的ID分别设为13、14和15。将每个滚动条都设定为Tab Stop样式,而从所有的六个静态文字字段中删除Group样式。
在COLORS2中,非模态对话框是在WinMain函数里建立的,紧跟在程序主窗口的ShowWindow呼叫之后。注意,主窗口的窗口样式包含WS_CLIPCHILDREN,这允许程序无须擦除对话框就能够重画主窗口。
如上所述,从CreateDialog传回的对话框窗口句柄存放在整体变量hDlgModeless中,并在消息循环中被测试。不过,在这个程序中,不需要将句柄存放在整体变量中,也不需要在呼叫IsDialogMessage之前测试这个值。消息循环可以编写如下:
while (GetMessage (&msg, NULL, 0, 0)) { if (!IsDialogMessage (hDlgModeless, &msg)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } }
由于对话框是在程序进入消息循环前建立,并且直到程序结束时才会被清除,所以hDlgModeless的值将总是有效的。我加入了如下的处理方式,以便您可能会往对话框的窗口消息处理程序中加入一段清除对话框的程序代码:
case WM_CLOSE : DestroyWindow (hDlg) ; hDlgModeless = NULL ; break ;
在原来的COLORS1程序中,SetWindowText在使用wsprintf将三个数值卷标转换为文字之后才设定它们的值。叙述为:
wsprintf (szBuffer, TEXT ("%i"), color[i]) ; SetWindowText (hwndValue[i], szBuffer) ;
i的值为目前处理的滚动条的ID,hwndValue是一个数组,它包含颜色数值的三个静态文字子窗口的窗口句柄。
新版本使用SetDlgItemInt为每个子窗口的每个文字字段设定一个号码:
SetDlgItemInt (hDlg, iCtrlID + 3, color [iCtrlID], FALSE) ;
尽管SetDlgItemInt和与其对应的GetDlgItemInt在编辑控件中用得最多,它们也可以用来设定其它控件的文字字段,如静态文字控件等。iCtrlID变量是滚动条的ID,给ID加上3使之变成对应数字卷标的ID。第三个参数是颜色值。通常,第四个参数表示第三个参数的值是解释为有正负号的(第四个参数为TRUE)还是无正负号的(第四个参数为FALSE)。但是,对于这个程序,值的范围是从0到256,所以这个参数没有意义。
在将COLORS1转换为COLORS2的程序中,我们把越来越多的工作交给了Windows。旧版本呼叫了CreateWindow 10次;而新版本只呼叫了CreateWindow和CreateDialog各一次。但是,如果您认为我们已经把呼叫CreateWindow的次数降到最少,那么您就错了,请看下一个程序。