win32消息映射12-对话框

11 对话框

对话框是一种特殊的窗口,它起源于所见即所得(WYSIWYG)的设计思想。当使用api创建窗口的时候,界面的布局存在于程序员的脑海中,只有等代码完成了,能运行了,才能看到效果。有没有一种方式方式,能在设计的时候就能看到效果,不用等到运行的时候呢?在win32 SDK编程里,只有对话框能做到这一点。对初学者而言,对话框好像减少了入门的难度,但实际上,难度并没有减少,一个问题的解决又引入了另外一个问题。在界面的布局上,的确做到了所见即所得,但如何把界面上的控件和代码里的变量相关联,这里就八仙过海,各显神通。MFC里引入了DDX的概念,把控件和资源相关联。对于初学者而言,DDX是个很抽象的概念,只知道要这么做,但背后的原理,要等到对SDK编程有了一定的了解才能有所体会。掌握win32编程,SDK是绕不过去的坎。

对话框也是窗口,也是用CreateWindowEx api创建的,但它的特殊性在于,是操作系统帮你创建,不需要手动创建(这当然包括对话框上控件的创建)。对话框的窗口过程由操作系统接管,这样,对话框就会有一些默认的系统行为,比如,TAB键的处理、模式对话框消息循环的处理等等。我们若用CreateWindowEx创建窗口,也能模拟对话框的这些行为,但若有操作系统支持,又何必自己模拟呢?有时候,顺势而为能省很多事情。

这篇文章,只讲对话框里面消息映射的部分,其它部分,有机会再详细阐述。

先讲模式对话框。模式对话框,是指对话框创建之后,对话框窗口一直处于“活动”(active)状态,除非它里面又创建了模式对话框。

模式对话框用DialogBoxParam创建:

INT_PTR DialogBoxParam(HINSTANCE hInstance,
    LPCTSTR lpTemplateName,
    HWND hWndParent,
    DLGPROC lpDialogFunc,
    LPARAM dwInitParam
);

第一个参数,传入的是lpTemplateName所在的HINSTANCE;
第二个参数,是对话框的资源id;
第三个参数,对话框的父窗口,若指定了父窗口,在对话框退出之前,父窗口不会响应鼠标消息;
第四个参数,对话框的回调函数。对话框的消息,通过这个回调函数告之;
第五个参数,和CreateWindowEx最后一个参数意义一样。在CreateWindowEx里,这参数的值能在WM_NCCREATE和WM_CREATE消息取回,而对于对话框,是在WM_INITDIALOG消息里获得。对话框里没有WM_CREATE消息吗?有,但被操作系统接管了。在“窗口的创建和注销”里说过,初始化是很重要的,若外面能接管对话框的WM_CREATE消息,对话框就有可能丧失初始化的机会。所以,对于对话框来说,WM_INITDIALOG消息是被认为第一个被接收的消息。

一个简单的使用DialogBoxParam创建模式对话框的例子:

int CALLBACK DlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_DIAOLOG),0,&DlgProc, 0);

由于对话框的回调函数和窗口的回调函数原型不一样,所以要重新定义对话框的回调函数。

class wndproc
{
    // ...
    bool process(msg_struct &msg);
public:
    // ...
    static int CALLBACK DlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
};

再定义一个dialog类:

int CALLBACK wndproc::DlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	dialog *p;

	if(message == WM_INITDIALOG)
	{
		p= reinterpret_cast< dialog *>(lParam);
		::SetWindowLongPtr(hWnd, GWL_USERDATA, (LONG)p);
	}
	else if((p= reinterpret_cast<dialog *>(::GetWindowLongPtr(hWnd, GWL_USERDATA))==0)
		return FALSE;

	msg_struct msg={0};

	msg.hWnd = m_hWnd;
	msg.message = message;
	msg.wParam = wParam;
	msg.lParam = lParam;

	msg.wnd = p;
	msg.cur_slot = p->m_msgslot_head.next;

	const BOOL b= process(msg);
	if(b)
	{
		::SetWindowLongPtr(hWnd, DWL_MSGRESULT, msg.result);
		return TRUE;
	}
	return FALSE;
}

上面的代码都很简单,就不用再一一细说了。不过仍然有2点需要注意:

1. WM_INITDIALOG消息的处理。WM_INITDIALOG和WM_CREATE一样的,由于WM_CREATE消息没办法获得,所以,对于对话框来说,WM_INITDIALOG是第一条接收到的消息,它的作用就相当于WM_CREATE的作用,所以,在wndproc::process里,必须映射WM_INITDIALOG到process_WM_CREATE中去。

2.before_create对于对话框来说是没意义,因为对话框的窗口过程被操作系统隐藏了。

在上面的代码能看到DWL_MSGRESULT存在的意义。对话框的回调函数的原型,除返回值外,和窗口的回调函数一模一样。DlgProc是在对话框的窗口过程里调用的,对话框的窗口过程,必须知道DlgProc有没有处理了这个消息,若处理了,还要知道处理的结果(msg.result)。而窗口过程,直接返回的是结果。当初设计的时候返回值的格式没有统一好,造成了DWL_MSGRESULT的存在。

如何结束模式对话框?调用api EndDialog:

class dialog : public wndbase
{
    // ...
public:

    int show_modal(HINSTANCE hInstance, size_t nTemplateName, HWND hParent = 0);

    void end_dialog(int nModalResult)
    {
        BOOL b= ::EndDialog(m_hWnd, nModalResult);
    }
};

EndDialog需要传入两个参数,第一个是对话框的句柄,第二个模式对话框的返回值。假设某个模式对话框会有个“确定”和“取消”的按钮(当然,也可以有其它按钮),在对话框结束的时候,如何知道它是按“确定”结束的,还是按“取消”结束的呢?就通过这个返回值来判断。这个返回值,实际上也是DialogBoxParam api的返回值,从DialogBoxParam到EndDialog感觉好像很遥远,但是,只有调用EndDialog后,才会退出DialogBoxParam函数,由此可以推断出,在DialogBoxParam内部,也有一个类似于GetMessage之类的消息循环,EndDialog起到的作用是类似于PostQuitMessage,使消息循环结束。

非模式对话框由另外一个api创建:

HWND CreateDialogParam(HINSTANCE hInstance,
    LPCTSTR lpTemplateName,
    HWND hWndParent,
    DLGPROC lpDialogFunc,
    LPARAM dwInitParam
);

这api的参数,和DialogBoxParam一模一样,这里就不再复述。CreateDialogParam并没有自身的消息循环,创建完对话框,就直接返回了。由此,可以知道模式和非模式的区别。

class dialog : public wndbase
{
public:
    // ...
    BOOL create(HINSTANCE hInstance, size_t nTemplateName, HWND hParent = 0)
    {
        return CreateDialogBoxParam(hInstance, MAKEINTRESOURCE(nTemplateName),hParent,
            &wndproc::DlgProc, LPARAM(this));
    }
};

把CreateDialogBoxParam封装在create函数里,在wndbase也有create函数,dialog的create函数,会隐藏掉wndbase的create函数。一个dialog对象,调用create函数是调用dialog::create而不是wndbase::create,即使它的参数能和wndbase::create里的参数匹配。这符合设计的期望。但由于dialog是wndbase的派生类,若一个wndbase指针指向dialog对象,通过这个指针调用create就不是我们期望的了。前面说过,dialog的确是窗口,所以从wndbase派生没有问题,但它是一种特殊的窗口,创建的过程被操作系统接管了。很难平衡这里的设计,当前暂且这样设计。

总结:

对话框在某些场合是很有用的,它方便了布局的设计,简化了标准化控件的创建过程。

请点击这里下载'wabc'库的最终源码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值