自绘Dialog窗体(非Hook Api的方式)
效果图如下所示:
所有图片资源取自QQ.
要自绘非客户区,以下几件事是必做的:
a,设置非客户区的大小。
b,处理WM_NCPAINT消息
c, 处理WM_NCHITTEST消息。
d, 处理WM_NCACTIVE消息。
e, 处理WM_ACTIVE消息。
f,处理WM_NCLBUTTONDOWN消息。
g,处理WM_NCLBUTTONUP消息。
f, Hook鼠标。(我觉得需要,到目前为止,如果不做到这一点我还没有重绘好)
下面一点一点分别说明
1,设置非客户区的大小,其实这个相对比较简单,下面说一下Window窗体计算时的计算方式:
如上图所示,在窗体收到消息WM_NCCALCSIZE时,我们可以设置客户区的大小,由于窗体大小是已经知道了的,在创建时指定了,因此,我们在这一消息中设置了大小以后,客户区,非客户区的大小都确定了。
WM_NCCALCSIZE,根据MSDN上的说明,在这一消息中,只有wParam参数为TRUE时,我们设置才有效,为FALSE是设置无效,但无所谓,不会产生负面影响,所以具体到MFC的OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS* lpncsp)中,我们没有必要判断bCalcValidRects是否为真,在我的程序中做了判断。
此消息的第二个参数lParam其实是会因第一个参数wParam的有所不同而有所改变的,wParam为真时,它为NCCALCSIZE_PARAMS的一个指针,当为假时,它为一个RECT结构体的指针,具体参见MSDN,但对于MFC这一框架中写代码,我们只需要将OnNcCalcSize中的第二个参数中的矩形(客户区的大小)的top,bottom,left,right,设为我们所需要的值即可。
需要说明的以下两点时,在取Border时,要注意一下窗体的样式:
a:如果是Dialog,并且样式为:DialogFrame,则在调用GetSystemMetric取边框宽时应该是SM_CXDLGFRAME, SM_CYDLGFRAME,而不是SM_CXBORDER.
b:计算top边框值时,应该注意,非客户区的上面的高度应该是CAPTION的高度+y方向上的border的高度。具体见程序。
2,处理WM_NCPAINT消息
这个固名思义就是在WM_NCPAINT是,不让系统画,而我们自己画自已的非客户区.
3,处理WM_NCHITTEST消息
处理这一消息的目的在于,不让关闭(或者最大化,最小化)按钮跑出来。当鼠标移到到关闭按钮处,由于系统做NCHITTEST时,如果发现鼠标在这些系统按钮上面,而当前的对话框的风格又是需要显示这些按钮,刚系统会得重绘这几个按钮。大家都自己,BUTTON有三态。
4,处理WM_NCMOUSEMOVE消息
处理这一消息的目与上面类似。
5,处理WM_NCACTIVE消息。
在这个时候,系统会画非客户区,我们处理这一消息,不让其画,然后调用我们画非客户区的方法画上我们的图形。
6,处理WM_ACTIVE消息
当我们在Taskbar里点我们的窗体时,会激发active消息,在这个消息中,默认是会画非客户区的。我们也要用我们自己的画笔画上去。
7,处理WM_NCLBUTTONDOWN消息。
做HitTest画我们自己的三态按钮。
8,处理WM_NCLBUTTONUP消息。
同上。
9,Hook鼠标行为。
这是我想到的唯一处理好7,8两点的方法。如果不Hook鼠标的行为,我们在非客户区按下,弹起左键时,无法收到这两个消息,除非你載获WM_NCLBUTTONDOWN消息后啥都不干(准确,具体的说是不做defwindowproc的调用)。当然,当你做完这后你会发现无法点击标题移动对话框了。所以我的做法是Hook鼠标行为行为,他不发我们自己给自己发.这一做法来自codegrunu上一老外.
还有一些很小很小的细节,比如禁用WM_SYSCOMMAND,不一一指出。具体看代码