对话框:图形界面设计者的好朋友
范例: dlg_one
很难找到一个沒有使用对话框的程序.在任何文本编辑器或其它的什么编辑器之类的东西,点 文件->打开 菜单,你就会看到一个对话框,很可能就要你选择一要打开的文件.
对话框不限于标準的打开文件框,可以是任意外观和任意功能.对话框的吸引人的地方在于它提供了一种快捷的方式来创建一个图形界面和一些默认的处理工作,大大地減少了你所需要写的代码.
要记住的一点就是对话框其实就是窗口.对话框与普通的窗口的区別在于系统为对话框作了一些额外的处理工作,比如创建并初始化控件,并处理tab顺序.几乎所有用于普通窗口的API函数都可以用于对话框.
第一步是创建对话框资源.对于任何的资源而言如何处理它们都与你用的编译器/IDE相关.
这里我向你展示.rc文件中的对话框的文本并让你将其整合到你的项目中去.
IDD_ABOUT DIALOG DISCARDABLE 0, 0, 239, 66
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "My About Box"
FONT 8, "MS Sans Serif"
BEGIN
DEFPUSHBUTTON "&OK",IDOK,174,18,50,14
PUSHBUTTON "&Cancel",IDCANCEL,174,35,50,14
GROUPBOX "About this program...",IDC_STATIC,7,7,225,52
CTEXT "An example program showing how to use Dialog Boxes\r\n\r\nby theForger",
IDC_STATIC,16,18,144,33
END
第一行中,IDD_AGOUTDLG为资源的标识.DIALOG是资源的类型,四个数字为左,顶,寛,高坐标.它们不是像素,而是基于系统的字体(由用戶选择)的大小对话框单位.如果你选择了一个大的字体,对话框就大,如果选择了小点的字体,对话框就相应的小些.这一点很重要,因为要确保所有的控件用当前的字体以合适的大小显示.你可以用MapDialogRect()在运行的时候把对话框单位转換为像素.DISCARDABLE告诉系统在不用此资源的时候将其交換至磁盘以节省系统资源(这是不用说的).
第二行以STYLE开头然后在后面跟上用来创建此对话框的窗口风格.这些在你的帮助文档中的关于CreateWindow()说明中会有解释.为了使用已定义好的那些常量你需要在你的.rc文件中加上#include
"windows.h",或者如果你使用VC++的话winres.h,afxres.h也可以.如果你使用资源编辑器的话这些文件则在需要的时候自动被加上去.
CAPTION那行应该一看就知道是什么意思.
FONT那行指明了你想用来创建此对话框的字体的大小和名字.因为不同的人有不同的字体并可能指定不同的字体所以这行在不同的计算机上可能看起来不一样.当然你一般不用为此操心.
现在我们用控件的列表来创建此对话框
DEFPUSHBUTTON "&OK",IDOK,174,18,50,14
这行指定OK按扭.这里的&有点像菜单项的那个有下划線的那个字母”O”,所以用戶可以按下Alt+O来激活这个控件(这就我上面所提到的对话框所做的默认处理的一部分).IDOK是控件的标识.IDOK是己经定义好的,所以我们不需要自己用#define来定义它了.最后的四个数字为左,顶,寛,高坐标,都是对话框单位.
这些信息太过于学术化了,所以你一般都用一个资源编辑器来创建对话框,但是知道如何用文本的方式来做这些事情还是很有必要的,尤其在你沒有一个图形化的编辑器的情況下.
有两个控件的标识为IDC_STATIC(就是-1),这表明我们永远并不需要去读写它们,所以它们并不需要一个标识.但是给它们一个标识也沒有什么害处,而且资源编辑器会自动为你做一点.
靜态控件中的文本中的"\r\n"是一个CR-LF对,表示換一个新行.
至此!把这些加到.rc文件中去后我们要写一个对话框的过程来处理这个对话框的消息.不要担心,这不是什么很新的知识,实际上它跟我们的主窗口的窗口的过程差不多(不是完全一样).
BOOL CALLBACK AboutDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
switch(Message)
{
case WM_INITDIALOG:
return TRUE;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDOK:
EndDialog(hwnd, IDOK);
break;
case IDCANCEL:
EndDialog(hwnd, IDCANCEL);
break;
}
break;
default:
return FALSE;
}
return TRUE;
}
对话框过程跟窗口过程有一些重要的差別.其中一个就是对于你不处理的消息不调用DefWndowProc().在对话框中这是自动完成的(你要是真的要自己调用的话会有问题).
第二,通常上你不处理的消息就返回一个FALSE,处理的话就是TRUE,除非那个消息指明了你要返回一个別的值.注意这就是我们在上面做的,默认的就是什么都不做并返回一个FALSE,而我们处理的消息就跳出switch()并返回TRUE.
第三,不调用DestroyWindow()来关闭一个对话框,而调用EndDialog().第二个参数是返回给调用DialogBox()的那个地方的代码的.
最后,不处理WM_CREATE消息,取而代之,处理WM_INITDIALOG消息来做对话框出现之前的任何操作,并返回TRUE以使键盘将焦点置于点的控件之上.(你确实可以处理WM_CREATE消息,但那是在所有控件被创建之前就发送了,这样你就不能访问它们了,而在WM_INITDIALOG消息来的时候,控件己经创建好了).
叽叽歪歪的半天了,现在创建它...
case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_HELP_ABOUT:
{
int ret = DialogBox(GetModuleHandle(NULL),
MAKEINTRESOURCE(IDD_ABOUT), hwnd, AboutDlgProc);
if(ret == IDOK){
MessageBox(hwnd, "Dialog exited with IDOK.", "Notice",
MB_OK | MB_ICONINFORMATION);
}
else if(ret == IDCANCEL){
MessageBox(hwnd, "Dialog exited with IDCANCEL.", "Notice",
MB_OK | MB_ICONINFORMATION);
}
else if(ret == -1){
MessageBox(hwnd, "Dialog failed!", "Error",
MB_OK | MB_ICONINFORMATION);
}
}
break;
// Other menu commands...
}
break;
这就是我用来创建我的关于对话框的代码,你应该看出来这应该移到你的WM_COMMAND消息处理的代码中去,如果你对这点还不清楚,你可能需要复习关于菜单的那些章节.ID_HELP_ABOUT是我的Help->About菜单项的标识.
因为我们要我们的主窗口的菜单来创建这个对话话,我们显然需要把这些代码放到我们主窗口的WndProc()中去,而不是对话框的过程中.
现在我存储了调用DialogBox()的返回值,这样你就可以观察你点击两个按钮的效果,在对话框中按Esc,Enter等等...这样做也展示了如何通过对话框的返回值来判断是否调用成功,和用戶用选择,或是你想从你的对话框过程中想返回给调用者的任何消息.
DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_ABOUT), hwnd, AboutDlgProc);
这是唯一重要的地方,你可以把这段代码放到你想要对话框出现的任何地方.IDD_ABOUT是对话框资源的标识.hwnd是对话框的父窗口的句柄.AboutDlgProc()当然是用来控制对话框的对话过程.
搞定了!Sit
IDD_UBU,sit.
某些特別敏感的读者可能会问,如果DialogBox()直到对话关闭才返回的话,我们在它显示的时候不能处理消息,它怎么工作的?这是因为DialogBox()有个特点就是有自己的消息循环,所以当对话框显示的时候,我们的消息循环正在进行并且窗口处理了默认的消息循环.这个消息循环也处理类似当你按下Tab键时在控件之间移动键盘的焦点的事情.
另外一个使用对话框的效果就是在对话框关闭之前你的主窗口将被禁止.有时候你就是要这种效果,有时你不是想这样,比如有时我们想要我们的对话框作为一个浮动的工具栏,这种情況下我们想要能夠同时操作我们的对话框与主窗口,这也是下面的章节要讲的內容.
|