(13):握手对话框
最后更新于:2022-04-01 20:07:33
一提到对话框,相信对它熟悉的人不在少数,更不用说码农们了,你可能会问,对话框和窗口有什么区别吗?本质上是没有区别的,对话框也是一种窗口(前面也说过,控件也可视为子窗口)。
最简单的对话框要数MessageBox弹出来的对话框了,是吧?这个函数我有信心,大家都会用的,毕竟很简单。
好的,废话不多扯了,马上开始本文第一件事,创建一个对话框。
对话框作为一种资源,它存放在资源文件中(.rc),如果项目中没有rc文件,第一种方法是在“解决方案资源管理器”中在“资源文件”节点右击,从菜单中选择“添加”-“新建项”来加入一个rc文件。第二种方法,可以从VS的“视图”菜单中打开“资源视图”,在资源视图中,在项目名节点上右击,从菜单中找到“添加”-“资源”。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-14_575fd2d21ca71.png)
然后,选择对话框,点新建按钮。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-14_575fd2d22dab4.png)
在属性窗口中为这个对话框名命一个ID,随便你喜欢,我把它设为IDD_MYDLG。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-14_575fd2d255efe.png)
OK,现在,我们就可以利用可视化设计器来玩了,看看,还不错的,虽然没有WinForm的设计器那么猛。
把默认两个按钮删掉,我们从工具箱中拖放一些控件。记得为控件的ID命名,就像在WinForm里面要设置Name属性一样。
大概就这样,拖一个Static Text和Button控件,随后我们尝试实现一个功能:点击按钮后,在静态文本框中显示文本。这个编辑器怎么用,就就不说了。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-14_575fd2d267341.png)
保存资源文件,下面我们开始写代码。
1、在主窗口的消息处理程序中响应WM_CREATE消息,用CreateDialog函数创建并显示非模态对话框。
~~~
LRESULT CALLBACK WinMainProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM LParam)
{
HWND hdlg;
switch (msg)
{
case WM_CREATE:
hdlg = CreateDialog(hgapp,MAKEINTRESOURCE(IDD_MYDLG),hwnd,(DLGPROC)DlgProc);
if(hdlg)
{
//显示对话框
ShowWindow(hdlg, SW_NORMAL);
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hwnd,msg,wParam,LParam);
}
return 0;
}
~~~
CreateDialog的最后一个参数是一个CALLBACK,这个和我们的WindowProc是一鸟样的,注意在定义该函数时,一定要先在头文件或源文件的前面声明一下,不然到这里会找不到,通常我们会把这些函数都放到WinMain函数后面来写,只是通常这样,并不是说一定要这样。
DlgProc如下:
~~~
// 处理对话框中的数据
INT_PTR CALLBACK DlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
return (INT_PTR)FALSE;
}
~~~
和WindowProc一样,也有一个DefDlgProc,但是这里最好不要调用,注意MSDN上关于这函数说明的最后一段话。
The DefDlgProc function must not be called by a dialog box procedure; doing so results in recursive execution.
如果在DialogProc中调用DefDlgProc会导致死循环。其实,我们为窗口写的消息循环也是死循环,GetMessage是不断执行,除非接到WM_QUIT消息让它返回假(0)就跳出循环,而对于对话框,我们并没有为它写GetMessage,也不向它PostQuitMessage,它有可能会无法跳出循环。
现在,程序是可以运行的。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-14_575fd2d2785a7.PNG)
不过,无论你怎么操作,对话框还是没返应,因为现在我们还没处理相关消息。
当用户操作系统菜单或者点击标题栏的 最大化, 最小化 或 关闭 按钮,都会收到WM_SYSCOMMAND消息,如果不响应WM_SYSCOMMAND,就会放到WM_COMMAND,但WM_COMMAND通常要处理控件的消息,故最好用WM_SYSCOMMAND消息。
2、在对话框的DlgProc中响应WM_SYSCOMMAND。
~~~
case WM_SYSCOMMAND:
if(wParam == SC_CLOSE)
{
// 如果执行了关闭
// 销毁对话框,将收到WM_DESTROY消息
DestroyWindow(hdlg);
}
return 0;
~~~
3、我们已经知道响应按钮单击是处理WM_COMMAND。要改变静态文本中的文本,一可以用 Static_SetText宏,二可以用SetWindowText,三可以发送WM_SETTEXT消息。但是,无论采用哪种方法,我们都得解决一个问题——怎么获取到静态文本控件的句柄。所以,认识一下这个函数:
~~~
HWND WINAPI GetDlgItem(
_In_opt_ HWND hDlg,
_In_ int nIDDlgItem
);
~~~
你猜都猜到了,参数一是对话框的句柄,参数二是要返回句柄的控件的ID。好,我们试试。
~~~
case WM_COMMAND:
{
if(LOWORD(wParam) == IDC_BTN)
{
nCount ++; //每点击一次,就+1
// 获取控件句柄
HWND hStatic = GetDlgItem(hdlg,IDC_DISP);
// 设置控件文本
WCHAR str[MAXCHAR];
// 格式化字符串
int n = wsprintf(str, L"你点击了%d次按钮。", nCount);
//在最后一个字符后加上结止符
str[n] = '\0';
SetWindowText(hStatic, str);
}
}
return 0;
~~~
现在来运行一下,每点击一次按钮,就会在静态文本控件中显示“你点击了X次按钮。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-14_575fd2d289a79.PNG)
好,大功告成。
下面是完整的代码清单。
~~~
#include
#include "resource.h"
LRESULT CALLBACK WinMainProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM LParam);
INT_PTR CALLBACK DlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam);
HINSTANCE hgapp; //当前应用程序句柄
int WINAPI WinMain(HINSTANCE hThisApp, HINSTANCE hPrevApp, LPSTR lpCmd, int nShow)
{
LPCWSTR cn = L"My";
WNDCLASS wc = {sizeof(WNDCLASS)};
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.hInstance = hThisApp;
wc.lpfnWndProc = WinMainProc;
wc.lpszClassName = cn;
wc.style = CS_HREDRAW | CS_VREDRAW;
//注册窗口类
RegisterClass(&wc);
//创建窗口
HWND hwnd = CreateWindow(cn,L"主窗口",WS_OVERLAPPEDWINDOW,
30,22,360,280,NULL,NULL,hThisApp,NULL);
if(!hwnd)
return 0;
hgapp = hThisApp;
//显示窗口
ShowWindow(hwnd,nShow);
//更新窗口
UpdateWindow(hwnd);
//消息循环
MSG msg;
while (GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WinMainProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM LParam)
{
HWND hdlg;
switch (msg)
{
case WM_CREATE:
hdlg = CreateDialog(hgapp,MAKEINTRESOURCE(IDD_MYDLG),hwnd,(DLGPROC)DlgProc);
if(hdlg)
{
//显示对话框
ShowWindow(hdlg, SW_NORMAL);
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hwnd,msg,wParam,LParam);
}
return 0;
}
// 处理对话框中的数据
INT_PTR CALLBACK DlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
static int nCount = 0;//点击按钮的次数
switch(msg)
{
case WM_SYSCOMMAND:
if(wParam == SC_CLOSE)
{
// 如果执行了关闭
// 销毁对话框,将收到WM_DESTROY消息
DestroyWindow(hdlg);
}
return 0;
case WM_COMMAND:
{
if(LOWORD(wParam) == IDC_BTN)
{
nCount ++; //每点击一次,就+1
// 获取控件句柄
HWND hStatic = GetDlgItem(hdlg,IDC_DISP);
// 设置控件文本
WCHAR str[MAXCHAR];
// 格式化字符串
int n = wsprintf(str, L"你点击了%d次按钮。", nCount);
//在最后一个字符后加上结止符
str[n] = '\0';
SetWindowText(hStatic, str);
}
}
return 0;
}
return (INT_PTR)FALSE;
}
~~~
';