自己看对话框也有些时间了,下面来总结下对话框的一些知识点。
对话框有模态对话框和非模态对话框两种基本形式。
非模态对话框允许我们把输入焦点切换到同一个应用程序的另一个窗口,该对话框也无需关闭,这个比较普遍。
模态对话框主要是两类:应用程序模式对话框和系统对话框。应用程序对话框不允许我们在本应用程序中切换输入焦点,但是可以切换到其他应用程序中去,比如点击记事本的文件打开按钮,会弹出选择打开文件的对话框,这个对话框不能切换到记事本中,但是可以切换到其他应用程序中去。这个就是属于应用程序模式对话框。系统对话框则比较恶毒,不允许你切换到任何一个应用程序中,我们一看名字就知道,系统对话框,看似比较紧急,要求我们必须首要解决····
我的第一种方法是用模态对话框来建立一个最简单的对话框。首先是.c文件的代码
#include <windows.h> #include "resource.h" LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; BOOL CALLBACK AboutDlgProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("ModeDia") ; 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 = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = szAppName ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("A Dialog Program"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc ; PAINTSTRUCT ps ; RECT rect ; static HINSTANCE hInstance; switch (message) { case WM_CREATE: hInstance = ((LPCREATESTRUCT) lParam) -> hInstance; return 0 ; case WM_COMMAND: switch (LOWORD (wParam)) { case IDM_APP_ABOUT : DialogBox (hInstance, TEXT("AboutBox"), hwnd, AboutDlgProc); break; } return 0; case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; GetClientRect (hwnd, &rect) ; DrawText (hdc, TEXT ("Hello, Windows 98!"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER) ; EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } BOOL CALLBACK AboutDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_INITDIALOG: return TRUE; case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: case IDCANCEL: EndDialog(hDlg, 0); return TRUE; } break; } return FALSE; }
这里我们还是要用到菜单和资源。所以首先添加菜单资源。我们把菜单的ID改成"MODEDIA",这里的双引号也要加上。
将对话框的ID改为 “AboutBox” (注意加上双引号和大小写)
然后在菜单里添加名称为 help 的 一栏,在这栏的下拉列表里面添加 About... 这个选项,我们将这个选项的ID改成IDM_APP_ABOUT
然后我们添加对话框资源,确认新生成的对话框有两个按钮,分别为确定和取消,确定按钮的ID为IDOK 取消按钮的ID为IDCANCEL系统默认的名字就是这样的。我们为了以防万一检查一下。
最后编译运行一下,一个最简单的模态对话框就出来了。
接下来我们来实现一下非模态对话框。
其实只是创建对话框的函数不一样,整个创建对话框的工作是交给windows去完成的,不用我们操心的。
非模态对话框相对来说是比较常用的。下面是几个注意点,我们其实只要在模态对话框里面改进一下就可以了~~~~~
还记得模态对话框的创建函数吗?
DialogBox (hInstance, TEXT("AboutBox"), hwnd, AboutDlgProc);
下面是非模态对话框的创建函数:
hDlgModeless = CreateDialog (hInstance, szTemplate, hwndParent, DialogProc);
CreateDialog函数返回的是对话框的窗口句柄,我们把这个窗口句柄存在全局变量hDlgModeless当中。
而在模态对话框当中,DIalogBox是直到对话框销毁后才返回值的。
接下来我们来看一下非模态对话框的样式语句:
STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS VISIBLE
你可以用记事本打开你刚才编译好的模态对话框的.rc资源文件来进行对比。为了创建一个非模态的对话框,我们把样式改成上面的语句。
在这里要注意最后一个样式WS_VISIBLE (可视的)
如果你忘记把这个样式加入进去的话,对话框将不会显示,除非你再调用下面的语句:
ShowWindow (hDlgModeless,SW_SHOW);
来达到同样的效果。
第三个差异:非模态对话框进入你的消息队列。所以我们应该更改消息循环:
while (GetMessage (&msg, NULL, 0, 0))
{
if (hDlgModeless == 0 || !IsDialogMessage (hDlgModeless, &msg))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
}
什么时候进入消息循环呢?我们在对话框还没生成或已经销毁 hDlgModeless == 0 (这个时候的消息不是由非模态对话框产生的)
或者 消息是针对非模态对话框的时候 ! IsDialogMessage (hDlgModeless, &msg) (这和时候的消息是由非模态对话框产生的)
最后的一点差异,模态对话框使用EndDialog结束, 非模态对话框使用DestroyWindow来结束。同时把hDlgModeless的全局变量设置为NULL
DestroyWindow(hDlg);
hDlgModeless = NULL;
好了,下面就是非模态对话框的代码了:
#include <windows.h> #include "resource.h" LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; BOOL CALLBACK AboutDlgProc (HWND, UINT, WPARAM, LPARAM) ; HWND hDlgModeless; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("MODEDIA") ; 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 = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = szAppName ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("A Modeless Dialog Program"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; 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) { HDC hdc ; PAINTSTRUCT ps ; RECT rect ; static HINSTANCE hInstance; switch (message) { case WM_CREATE: hInstance = ((LPCREATESTRUCT) lParam) -> hInstance; return 0 ; case WM_COMMAND: switch (LOWORD (wParam)) { case IDM_APP_ABOUT : hDlgModeless = CreateDialog (hInstance, TEXT("AboutBox"), hwnd, AboutDlgProc); break; } return 0; case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; GetClientRect (hwnd, &rect) ; DrawText (hdc, TEXT ("Hello, Windows 98!"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER) ; EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } BOOL CALLBACK AboutDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_INITDIALOG: return TRUE; case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: case IDCANCEL: DestroyWindow(hDlg); hDlgModeless = NULL; break; } break; } return 0; }
资源的创建过程与上面的相同,这里就不再叙述了。
现在再试一下,是不是可以移动父窗口了,呵呵~~~~~~
第三种方法是把对话框作为窗口,注册一个窗口类来实现。我下面的例子创建的是一个非模态对话框。
下面先上代码.cpp文件
#include <windows.h> #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 ("Dialog3"); HWND hwnd; MSG msg; WNDCLASS wndclass; wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; 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 ; } hwnd = CreateDialog (hInstance, szAppName, 0, NULL);//创建非模态对话框 ShowWindow (hwnd, iCmdShow); while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_COMMAND: SetFocus(hwnd); switch (LOWORD (wParam)) { case OK: MessageBox(hwnd, TEXT("This is a demoless Dialog program"), TEXT("OK"), MB_OK); return 0; case CANCEL: return 0; } return 0 ; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; }
resource.h文件
#define OK 1 #define CANCEL 2
在源文件里面添加名为Dialog.dlg的文件(创建一个.txt的文件然后重命名)
Dialog3 DIALOG -1, -1, 102, 122 //STYLE WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX STYLE 0x00000000L | 0x00C00000L | 0x00080000L | 0x00020000L CLASS "Dialog3" CAPTION "Dialog3" { PUSHBUTTON "OK", 68, 8, 24, 30, 10 PUSHBUTTON "CANCEL", 65, 8, 40, 30, 10 }
最后在编译生成的资源文件里面添加
#include "Dialog3.dlg"
第三种方法看起来比较奇怪。这到底是对话框还是窗口呢?其实对话框就是一个窗口!!
下面来看一下注意点:
wndclass.cbWndExtra = DLGWINDOWEXTRA ; // 注意这里!
这句告诉windows我要使用自己的窗口过程
另外一定要注意资源文件里面的
CLASS "Dialog3"
这句告诉windows我要使用自己的窗口过程来处理对话框消息
资源文件引用了Dialog.dlg,这个文件用来创建自定义的对话框
这里我还有2个不明白的地方:
//STYLE WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX STYLE 0x00000000L | 0x00C00000L | 0x00080000L | 0x00020000L我在引用STYLE的时候,直接引用编译时提示找不到定义,于是我找到样式对应的值就可以了。我不知道为什么会这样,请高手指教。
另外这个对话框创建出来后按钮不起作用。我也不知道是什么原因,照理来说LOWORD(wParam)存的应该是按钮的ID啊。这里也暂时留下疑问,请高手解答。
第二个问题已经解决了。
可以在编译后的资源文件里面看到生成的对话框和按钮,可以在那里设置ID,用上面的方法生成的ID是随机的。
PUSHBUTTON "OK", 68, 8, 24, 30, 10这里的OK只不过是按钮上的文本字符串而已,并不是按钮的ID。我把这里搞混了,所以出错了。
我可以设置按钮的ID为“OK”,然后就可以了(注:一般设置按钮ID的时候我们使用ID_BTNOK,这样标准一点)
以上是我自己总结的对话框的知识。有错误的地方,还请指教。谢谢^_^