MFC对话框非客户区完美自绘
声明:这篇帖子我在csdn上发表了,好久没来编程中国了,希望可以把更多的东西分享给大家,同时与大家交流学习!!!下载地址:
http://download.
贴张效果图:
如果大家觉得效果一般的话,可是参看代码,自己进行改进
我们先从ON_WM_NCHITTEST这个消息入手,其消息映射函数为OnNcHitTest(CPoint point),该消息的作用是:命中测试,即判断鼠标当前位于窗口的什么地方,比如:HTCAPTION(标题栏上),HTMINBUTTON(最小化按钮上)等等,并将测试的结果作为返回值,,在标题栏上的最小化、最大化和关闭“按钮”其实并非真正的按钮,而是我们绘制的位图,使其看起来好像是一个个按钮,于是乎,当我们产生一个WM_LBUTTONDOWN消息,如何知道是否按在了“按钮”呢,这些工作全靠OnNcHitTest函数的返回来进行判断,因此这个消息的处理对我们后续的工作起到至关重要的角色。
我们按照自己的想法,设定了最小化、最大化和关闭“按钮”的位置,程序中我设定它们的位置相对于窗口左上角(0, 0),所以在OnNcHitTest函数中,还需要将这些位置转换为相对于屏幕的坐标,具体的转换,参看BOOL PtInRect(CRect &rtWin, CRect &rtClientBtn, CPoint &pt)函数,
程序代码:
HRESULT CDrawDlg::OnNcHitTest(CPoint point) { CRect rtWin; GetWindowRect(&rtWin); if(PtInRect(rtWin, m_rtCloseBox, point)) { return HTCLOSE; } if(PtInRect(rtWin, m_rtMaxBox, point)) { return HTMAXBUTTON; } if(PtInRect(rtWin, m_rtMinBox, point)) { return HTMINBUTTON; } return CDialog::OnNcHitTest(point); }
然后再说一下ON_WM_NCRBUTTONDOWN这个消息,其映射函数为OnNcRButtonDown(UINT nHitTest, CPoint point),这个消息将决定即将弹出的系统菜单,如果当前鼠标的位置位于最小化、最大化或是关闭“按钮”上,阻止即将弹出的系统菜单,这样就需要屏蔽掉HTCLOSE、HTMINBUTTON和HTMAXBUTTON情况,很多人看到人想到的是应该是处理ON_WM_NCRBUTTONUP消息,开始的时候我也是这样想的,可是实际上这是徒劳的,仍然会弹出系统菜单,没办法只有处理ON_WM_NCRBUTTONDOWN消息了,
程序代码:
void CDrawDlg::OnNcRButtonDown(UINT nHitTest, CPoint point) { if(HTCLOSE == nHitTest || HTMAXBUTTON == nHitTest || HTMINBUTTON == nHitTest) { return; } CDialog::OnNcRButtonDown(nHitTest, point); }
一个对话框窗口有可能存在几种风格,可能只存在/不存在标题栏,也可能只存在关闭按钮,或是还包含一个最大化按钮,也可能还包含一个最小化按钮,程序中针对这一情况添加了处理函数,GetWindowStyle()用于获取当前窗口的风格,程序启动时在PreSubclassWindow()中调用一次,在OnNcPaint()处理中参考获取到的窗口风格进行绘制,同时,用户还可能在外部调用SetWindowLong()给窗口设置新的风格,针对这一情况,也添加了额外的消息处理:ON_WM_STYLECHANGED,WM_STYLECHANGED是当窗口风格发生改变时发送的消息,在该消息的处理函数中再次调用GetWindowStyle()函数获取到窗口新的风格。
最后说一下一个比较重要的问题,当我们做完上述的工作后,运行程序,会发现会出现默认的关闭/最小化/最大化按钮,这是我们不希望看到的,究其原因,是WM_NCUAHDRAWCAPTION和WM_NCUAHDRAWFRAME这两个该死的消息在作怪,我想把这两个消息详细的讲解一下,可是居然在msdn上没有找到这两个消息,还好,最后在一篇帖子上找到了这样一段话:
原来是windows对绘制处理的一个操作(XP才增加的消息),猜测是一个只有微软内部才知道的BUG处理,其处理方式是通过向标题栏发送以下消息以解决余留问题,但我们重写后未曾予以适当的处理,故出现如题所述的现象
而我们大家所作的就是屏蔽掉这两个消息,不做任何的处理。
欢迎和大家一起交流学习,如果有什么问题,回复在该帖子里就行!