对话框(Dialog)分为模态对话框和非模态对话框(Modeless Dialog)。非模态对话框不能处理TAB键、快捷键等按键事件,也就是说用户不能在非模态对话框中通过按TAB键切换各控件之间的焦点(Focus)。但这也不是没有解决办法的。
方法一
在MSDN中,让非模态对话框处理TAB按键事件的经典代码如下:
HINSTANCE hinst; HWND hwndMain; HWND hwndDlgModeless = NULL; int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { MSG msg; BOOL bRet; WNDCLASS wc; HACCEL haccel; UNREFERENCED_PARAMETER(lpszCmdLine); // Register the window class for the main window. if (!hPrevInstance) { wc.style = 0; wc.lpfnWndProc = (WNDPROC) WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon((HINSTANCE) NULL, IDI_APPLICATION); wc.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW); wc.hbrBackground = GetStockObject(WHITE_BRUSH); wc.lpszMenuName = "MainMenu"; wc.lpszClassName = "MainWndClass"; if (!RegisterClass(&wc)) return FALSE; } hinst = hInstance; // save instance handle // Create the main window. hwndMain = CreateWindow("MainWndClass", "Sample", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, (HWND) NULL, (HMENU) NULL, hinst, (LPVOID) NULL); // If the main window cannot be created, terminate // the application. if (!hwndMain) return FALSE; // Show the window and paint its contents. ShowWindow(hwndMain, nCmdShow); UpdateWindow(hwndMain); // Start the message loop. while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0) { if (bRet == -1) { // handle the error and possibly exit } else { if (hwndDlgModeless == (HWND) NULL || !IsDialogMessage(hwndDlgModeless, &msg) && !TranslateAccelerator(hwndMain, haccel, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } } // Return the exit code to the system. return msg.wParam; }
其主要解决方案是让非模态对话框hwndDlgModeless的父窗口hwndMain,在其主消息循环中通过调用IsDialogMessage函数来通知hwndDlgModeless TAB按键事件。可是,如果hwndMain没有明确写出的主消息循环,由该怎么办呢?
方法二
譬如,当hwndMain指向的是一个模态对话框时,其WinMain函数通常实现如下:
#include #include #include #include /* --- The following code comes from e:/lccwin32/lib/wizard/dlgbased.tpl. */ /*<---------------------------------------------------------------------->*/ /* Template for a dialog based application. The main procedure for this template is the DialogFunc below. Modify it to suit your needs. */ /* prototype for the dialog box function. */ static BOOL CALLBACK DialogFunc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); /* Win main just registers a class of the same type that the dialog class, and then calls DialogBox. Then it exits. The return value is the return value of the dialog procedure. */ int APIENTRY WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpCmdLine, int nCmdShow) { WNDCLASS wc; INITCOMMONCONTROLSEX cc; memset(&wc,0,sizeof(wc)); wc.lpfnWndProc = DefDlgProc; wc.cbWndExtra = DLGWINDOWEXTRA; wc.hInstance = hinst; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); wc.lpszClassName = "MainWndClass"; RegisterClass(&wc); memset(&cc,0,sizeof(cc)); cc.dwSize = sizeof(cc); cc.dwICC = 0xffffffff; InitCommonControlsEx(&cc); return DialogBox(hinst, MAKEINTRESOURCE(IDD_MAINDIALOG), NULL, (DLGPROC) DialogFunc); } /* You should add your initialization code here. This function will be called when the dialog box receives the WM_INITDIALOG message. */ static int InitializeApp(HWND hDlg,WPARAM wParam, LPARAM lParam) { return 1; } /* This is the main function for the dialog. It handles all messages. Do what your application needs to do here. */ static BOOL CALLBACK DialogFunc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { /* This message means the dialog is started but not yet visible. Do All initializations here */ case WM_INITDIALOG: InitializeApp(hwndDlg,wParam,lParam); return TRUE; /* By default, IDOK means close this dialog returning 1, IDCANCEL means close this dialog returning zero */ case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: EndDialog(hwndDlg,1); return 1; case IDCANCEL: EndDialog(hwndDlg,0); return 1; } break; /* By default, WM_CLOSE is equivalent to CANCEL */ case WM_CLOSE: EndDialog(hwndDlg,0); return TRUE; } return FALSE; }
资源文件中,IDD_MAINDIALOG定义的对话框只有标题栏、默认的OK(IDOK)键和Cancel(IDCANCEL)键。对话框的主消息循环在系统API DialogBox中,我们无法动其分毫。于是,只有修改WinMain函数,还得添加三个全局变量:
HINSTANCE hinst; HWND hwndMain; HWND hwndDlgModeless = NULL; int APIENTRY WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpCmdLine, int nCmdShow) { WNDCLASS wc; INITCOMMONCONTROLSEX cc; MSG msg; int res; memset(&wc,0,sizeof(wc)); wc.lpfnWndProc = DefDlgProc; wc.cbWndExtra = DLGWINDOWEXTRA; wc.hInstance = hinst; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); wc.lpszClassName = "MainWndClass"; RegisterClass(&wc); memset(&cc,0,sizeof(cc)); cc.dwSize = sizeof(cc); cc.dwICC = 0xffffffff; InitCommonControlsEx(&cc); hInst = hinst; hMainDlg = CreateDialog(hinst, MAKEINTRESOURCE(DLG_MAIN), NULL, (DLGPROC) DialogFunc); while( (res = (int)GetMessage( &msg, NULL, 0, 0 )) != 0) { if (res == -1) { // handle the error and possibly exit } else { if(hMainDlg == (HWND) NULL || !IsDialogMessage(hMainDlg, &msg) || hModelessDlg == (HWND) NULL || !IsDialogMessage(hModelessDlg, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } } if(hModelessDlg != NULL) { DestroyWindow(hModelessDlg); hModelessDlg = NULL; } if(hMainDlg != NULL) { DestroyWindow(hMainDlg); hMainDlg = NULL; } return msg.wParam; }
因为将父窗口hwndMain创建为非模态对话框,所以在其主消息循环处除了调用IsDialogMessage函数处理非模态对话框hwndDlgModeless的消息外,还要调用该函数处理hwndMain的消息。另外要特别注意的是,调用DestroyWindow函数销毁对话框hModelessDlg和hwndMain,只能在退出主消息循环之后进行,千万不能放到对话框过程函数DialogFunc中对消息WM_CLOSE和WM_QUIT的处理模块中,否则会引起整个进程陷在主消息循环里,无法退出。在处理消息WM_CLOSE时,只需调用函数PostQuitMessage即可。消息WM_QUIT就不用处理了。以下是让非模态对话框处理TAB按键事件全部代码(除去注释):
#include #include #include #include static HWND hModelessDlg = NULL; static HWND hMainDlg = NULL; static HINSTANCE hInst = NULL; #define ErrMsg(s) MessageBox(NULL, (s), "Caution", MB_OK) static BOOL CALLBACK DialogFunc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); int APIENTRY WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpCmdLine, int nCmdShow) { WNDCLASS wc; INITCOMMONCONTROLSEX cc; MSG msg; int res; memset(&wc,0,sizeof(wc)); wc.lpfnWndProc = DefDlgProc; wc.cbWndExtra = DLGWINDOWEXTRA; wc.hInstance = hinst; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); wc.lpszClassName = "MainWndClass"; RegisterClass(&wc); memset(&cc,0,sizeof(cc)); cc.dwSize = sizeof(cc); cc.dwICC = 0xffffffff; InitCommonControlsEx(&cc); hInst = hinst; hMainDlg = CreateDialog(hinst, MAKEINTRESOURCE(DLG_MAIN), NULL, (DLGPROC) DialogFunc); while( (res = (int)GetMessage( &msg, NULL, 0, 0 )) != 0) { if (res == -1) { // handle the error and possibly exit } else { if(hMainDlg == (HWND) NULL || !IsDialogMessage(hMainDlg, &msg) || hModelessDlg == (HWND) NULL || !IsDialogMessage(hModelessDlg, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } } if(hModelessDlg != NULL) { DestroyWindow(hModelessDlg); hModelessDlg = NULL; } if(hMainDlg != NULL) { DestroyWindow(hMainDlg); hMainDlg = NULL; } return msg.wParam; } static int InitializeApp(HWND hDlg,WPARAM wParam, LPARAM lParam) { hMainDlg = hDlg; hModelessDlg = CreateDialog(hInst, MAKEINTRESOURCE(DLG_STEP1), hDlg, NULL); return 1; } static BOOL CALLBACK DialogFunc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { int i; switch (msg) { case WM_INITDIALOG: InitializeApp(hwndDlg,wParam,lParam); return TRUE; case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: case IDCANCEL: PostQuitMessage(0); break; } break; case WM_CLOSE: PostQuitMessage(0); break; } return FALSE; }
方法三
还可以用函数FindResource、LoadResource、LockResource、UnlockResource、FreeResource来加载和释放对话框资源,用函数CreateDialogIndirect创建非模态父对话框hwndMain。要使用这种方法的话,大部分代码都相同或类似,但查找、加载、锁定、释放对话框资源这些代码实现起来颇为麻烦,还不如直接调用函数CreateDialog来得方便、简洁。这里就不详细说明了。
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/BlackMonkey/archive/2005/09/17/483543.aspx