11.2非模态对话框
非模态对话框允许使用者在对话框与其它程序之间进行切换,又可以在对话框与建立对话框的窗口之间进行切换。当使用者觉得让对话框保留片刻会更加方便时,使用非模态对话框是合适的。
非模态对话框是使用CreateDialog来建立的,该函数所使用的参数与DialogBox相同。hDlgModeless = CreateDialog ( hInstance, szTemplate, hwndParent, DialogProc); 区别是CreateDialog函数立即传回对话框的窗口句柄,并通常将这个窗口句柄存放到整体变量中。
模态对话框与非模态对话框的区别
①非模态对话框通常包含一个标题列和一个系统菜单按钮。对于模态对话框,您通常无须提供标题列和系统菜单。
②用于非模态对话框的对话框模板中的STYLE叙述形如: STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_VISIBLE 如果省略了WS_VISIBLE,那么您必须在CreateDialog呼叫之后呼叫ShowWindow
hDlgModeless = CreateDialog ( . . . ) ; ShowWindow (hDlgModeless, SW_SHOW) ; 果您既没有包含WS_VISIBLE样式,又没有呼叫ShowWindow,那么非模态对话框将不会被显示。
③与模态对话框和消息框的消息不同,非模态对话框的消息要经过程序式的消息队列。要将这些消息传送给对话框窗口消息处理程序,则必须改变消息队列。具体的代码如下:
while(GetMessage(&msg, NULL, 0, 0)) { if(hDlgModeless == 0 || !IsDialogMessage (hDlgModeless, &msg)){ TranslateMessage(&msg); DispatchMessage(&msg); } }
如果消息是发送给非模态对话框的,那么IsDialogMessage将它发送给对话框中窗口消息处理程序,并传回TRUE(非0);否则,它将传回FALSE(0)。简单来说就是如果消息是发给非模态对话框的话,该消息就不进入windows的消息循环。
④使用DestroyWindow而不是EndDialog来结束非模态对话框。case WM_CLOSE: DestroyWindow (hDlg); //hDlg参数是传递给对话框程序的参数 hDlgModeless = NULL; break;
也可以用CreateDialogParam来建立非模态对话框,并让它储存一个结构指针。
模态对话框和非模态对话框的消息循环
1、非模态对话框和父窗口共享当前线程的消息循环
2、模态对话框新建一个新的消息循环,并由当前消息循环派发消息,而父窗口。模态对话框屏蔽了用户对它父窗口的操作,但是不是在消息循环里面屏蔽,所以给父窗口发送消息,父窗口还是可以接收得到。
3、调用模态对话框的窗口处理函数会被阻塞,但是新的消息循环仍然可以调用父窗口的消息处理函数,所以,发送给父窗口的新消息仍然可以被及时处理。
新的COLORS程序
在COLORS2中,非模态对话框是在WinMain函数里建立的,紧跟在程序主窗口的ShowWindow呼叫之后。注意,主窗口的窗口样式包含WS_CLIPCHILDREN,这允许程序无须擦除对话框就能够重画主窗口。
#include #include "resource.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,(LPCTSTR)IDD_DIALOG1, hwnd, ColorScrDlg); /* Windows对模态对话框和非模态对话框的处理有些不同。 在创建并显示模态对话框后,Windows会为它在内部建立一个消息循环, 在这个消息循环中把消息发送给对话框管理器, 对话框管理器在处理消息的过程中会调用用户定义的对话框过程, 当对话框关闭的时候,Windows退出内建的消息循环,并从DialogBoxParam函数返回。 而对于非模态对话框,CreateDialogParam函数在创建对话框后直接返回, 对话框窗口的消息是通过用户程序中的消息循环派送的。 */ 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: //GCL_HBRBACKGROUND:替换与类有关的背景刷子的句柄 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; //iCtrlID变量是滚动条的ID 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); //为每个子窗口的每个文字字段设定一个号码 //给iCtrlID加上3使之变成对应数字卷标的ID //第三个参数是颜色值 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; }
COLORS2.rc
/ // // Dialog // IDD_DIALOG1 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
当建立对话框模板时,直接将三个滚动条的ID分别设为10、11和12,将显示滚动条目前值的三个静态文字字段的ID分别设为13、14和15。将每个滚动条都设定为Tab Stop样式,而从所有的六个静态文字字段中删除Group样式。
HEXCALC:窗口还是对话框?
#include #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT("HexCalc"); HWND hwnd; MSG msg; WNDCLASS wndclass; wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; //资源描述文件中用CLASS指令创建的对话框时, //它必须设置这个字段为 DLGWINDOWEXTRA wndclass.cbWndExtra = DLGWINDOWEXTRA; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(hInstance, szAppName); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = szAppName; if (!RegisterClass(&wndclass)) { MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR); return 0; } //第三个参数通常是父窗口的窗口 //句柄,这里设定为0,因为窗口没有父窗口。 //最后一个参数,通常是对话框程序的地址,这里不需要。 //因为Windows不会处理这些消息,因而也不会将消息发送给对话框程序 //这个CreateDialog呼叫与对话框模板一起, //被Windows有效地转换为一个CreateWindow呼叫 hwnd = CreateDialog(hInstance, /*szAppName*/MAKEINTRESOURCE(IDD_DIALOG1), 0, NULL); //CreateWindow呼叫的功能与上面的呼叫相同: /* hwnd = CreateWindow(TEXT ("HexCalc"), TEXT ("Hex Calculator"), WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT, 102 * 4 / cxChar, 122 * 8 / cyChar, NULL, NULL, hInstance, NULL) ; */ ShowWindow(hwnd, iCmdShow); while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } void ShowNumber(HWND hwnd, UINT iNumber) { TCHAR szBuffer[20]; wsprintf(szBuffer, TEXT ("%X"), iNumber); SetDlgItemText(hwnd, VK_ESCAPE, szBuffer); } DWORD CalcIt(UINT iFirstNum, int iOperation, UINT iNum) { switch (iOperation) { case '=': return iNum; case '+': return iFirstNum + iNum; case '-': return iFirstNum - iNum; case '*': return iFirstNum * iNum; case '&': return iFirstNum & iNum; case '|': return iFirstNum | iNum; case '^': return iFirstNum ^ iNum; case '<': return iFirstNum << iNum; case '>': return iFirstNum >> iNum; case '/': return iNum ? iFirstNum / iNum: MAXDWORD; case '%': return iNum ? iFirstNum % iNum: MAXDWORD; default: return 0; } } LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam) { static BOOL bNewNumber = TRUE; static int iOperation = '='; static UINT iNumber, iFirstNum; HWND hButton; //WndProc可以完全相同地对待WM_COMMAND消息和WM_CHAR //消息。在每种情况下,wParam的低字组都是按钮的ASCII码。 switch (message) { //WndProc拦截WM_KEYDOWN消息,将左箭 //头键转换为Backspace键。 case WM_KEYDOWN: if (wParam != VK_LEFT) break; wParam = VK_BACK; // fall through case WM_CHAR: //在处理WM_CHAR消息时,WndProc将字符代码转换为大写, //Enter键(VK_RETURN)转换为等号键的ASCII码。 if((wParam =(WPARAM)CharUpper((TCHAR *)wParam)) == VK_RETURN) wParam = '='; //WM_CHAR消息的有效性检验 //如果GetDlgItem函数传回0, //那么键盘字符不是对话框模板中定义的ID之一 if(hButton = GetDlgItem(hwnd, wParam)) { //如果字符是ID之一,则通过给相应的按钮 //发送一对BM_SETSTATE消息,来使之闪烁,也就是按下效果 SendMessage(hButton, BM_SETSTATE, 1, 0); //按下 Sleep(1000); //防止按钮被按得太快而让人注意不到 SendMessage(hButton, BM_SETSTATE, 0, 0); //弹上 } else { MessageBeep(0); break; } // fall through case WM_COMMAND: //一旦使用鼠标单击某按钮,输入焦点就会切换到该按钮上。 SetFocus(hwnd); if (LOWORD(wParam) == VK_BACK) { //为了更正输入,可以使用「Back」 //按钮、Backspace或者左箭头键。 ShowNumber(hwnd, iNumber /= 16); } else if(LOWORD(wParam) == VK_ESCAPE) { //单击「display」方块或者 //Esc键即可清除目前的输入 //其中PUSHBUTTON:「display」的ID值等于27 ShowNumber(hwnd, iNumber = 0); } else if(isxdigit(LOWORD(wParam))) // hex digit { if (bNewNumber) { iFirstNum = iNumber; iNumber = 0; } bNewNumber = FALSE; if(iNumber <= MAXDWORD >> 4) ShowNumber(hwnd, iNumber = 16 * iNumber + wParam - (isdigit(wParam) ? '0': 'A' - 10)); else MessageBeep(0); } else // operation { if (!bNewNumber) ShowNumber(hwnd, iNumber = CalcIt(iFirstNum, iOperation, iNumber)); bNewNumber = TRUE; iOperation = LOWORD(wParam); } return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); }
HEXCALC.rc
/ // // Dialog // //HEXCALC.DLG IDD_DIALOG1 DIALOG -1, -1, 102, 122 STYLE WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX CLASS "HexCalc" CAPTION "Hex Calculator" { PUSHBUTTON "D", 68, 8, 24, 14, 14 PUSHBUTTON "A", 65, 8, 40, 14, 14 PUSHBUTTON "7", 55, 8, 56, 14, 14 PUSHBUTTON "4", 52, 8, 72, 14, 14 PUSHBUTTON "1", 49, 8, 88, 14, 14 PUSHBUTTON "0", 48, 8, 104,14, 14 PUSHBUTTON "0", 27, 26, 4, 50, 14 PUSHBUTTON "E", 69, 26, 24, 14, 14 PUSHBUTTON "B", 66, 26, 40, 14, 14 PUSHBUTTON "8", 56, 26, 56, 14, 14 PUSHBUTTON "5", 53, 26, 72, 14, 14 PUSHBUTTON "2", 50, 26, 88, 14, 14 PUSHBUTTON "Back", 8, 26, 104,32, 14 PUSHBUTTON "C", 67, 44, 40, 14, 14 PUSHBUTTON "F", 70, 44, 24, 14, 14 PUSHBUTTON "9", 57, 44, 56, 14, 14 PUSHBUTTON "6", 54, 44, 72, 14, 14 PUSHBUTTON "3", 51, 44, 88, 14, 14 PUSHBUTTON "+", 43, 62, 24, 14, 14 PUSHBUTTON "-", 45, 62, 40, 14, 14 PUSHBUTTON "*", 42, 62, 56, 14, 14 PUSHBUTTON "/", 47, 62, 72, 14, 14 PUSHBUTTON "%", 37, 62, 88, 14, 14 PUSHBUTTON "Equals", 61, 62, 104,32, 14 PUSHBUTTON "&&", 38, 80, 24, 14, 14 PUSHBUTTON "|", 124, 80, 40, 14, 14 PUSHBUTTON "^", 94, 80, 56, 14, 14 PUSHBUTTON "<", 60, 80, 72, 14, 14 PUSHBUTTON ">", 62, 80, 88, 14, 14 }
这个程序完全不呼叫CreateWindow,也不处理WM_PAINT消息,不取得设备内容,也不处理鼠标消息。
CLASS叙述是这个对话框与曾经建立过的对话框之间最重要的区别。
包含CLASS叙述就告诉Windows将消息发送到其它的地方-具体的说,就是发送到在HexCalc窗口类别中指定的窗口消息处理程序。
WNDCLASS结构的cbWndExtra字段设定为DLGWINDOWEXTRA。对于您自己注册的对话框程序,这是必需的。