自绘窗体(Dialog)的实现,关于标题栏的绘制,基本上都是主动在标题栏重新绘制,
为了绘制效果的好看,避免系统默认自动绘制标题栏按钮(最大化,最小化,关闭按钮),一般都是对该窗体的属性用SetWindowLong来重新设置其样式,屏蔽WS_SYSMENU,WS_MINIMIZEBOX,WS_MAXIMIZEBOX等属性并做记录,然后在绘制的时候进行绘制,此方法可以得到很好的绘制效果。本人亦采用以上方法实现,但是由于屏蔽了以上属性,当程序在任务栏被鼠标左键和右键点击的时候,作出的反应就没有了弹出系统菜单的效果。 尝试解决过程描述(附注):
1.依旧采用屏蔽属性方式,希望截获鼠标相关消息,主动弹出系统菜单,
但是用Spy++跟踪窗口消息,未屏蔽时从任务栏点击窗口图标到激活显示消息如下:
<00001> 00510644 S WM_WINDOWPOSCHANGING lpwp:0012FA84
<00002> 00510644 R WM_WINDOWPOSCHANGING
<00003> 00510644 S WM_WINDOWPOSCHANGED lpwp:0012FA84
<00004> 00510644 R WM_WINDOWPOSCHANGED
<00005> 00510644 S WM_ACTIVATEAPP fActive:True dwThreadID:00000E88
<00006> 00510644 R WM_ACTIVATEAPP
<00007> 00510644 S WM_NCACTIVATE fActive:True
<00008> 00510644 S .WM_GETICON fType:False
<00009> 00510644 R .WM_GETICON hicon:0B6E0687
<00010> 00510644 S .WM_GETTEXT cchTextMax:256 lpszText:0012E3E8
<00011> 00510644 R .WM_GETTEXT cchCopied:8 lpszText:0012ED50 ("SkinTestDlg")
<00012> 00510644 R WM_NCACTIVATE
<00013> 00510644 S WM_ACTIVATE fActive:WA_ACTIVE fMinimized:False hwndPrevious:(null)
<00014> 00510644 S .WM_ACTIVATETOPLEVEL fActive:True dwThreadID:0012F6A8
<00015> 00510644 R .WM_ACTIVATETOPLEVEL
<00016> 00510644 S ..WM_CTLCOLORBTN hdcButton:1B011103 hwndButton:003406A4
<00017> 00510644 R ..WM_CTLCOLORBTN hBrush:01100060
<00018> 00510644 R WM_ACTIVATE
<00019> 00510644 S WM_KICKIDLE
<00020> 00510644 R WM_KICKIDLE
未曾截获与鼠标相关的消息,猜测应该是系统与窗口的交互以WM_ACTIVEAPP等激活消息
为主,然后执行一系列关联的操作,因此要处理似乎比较繁琐,此法可行性不高;2.尝试不修改窗口属性方式,希望截获系统绘制消息,屏蔽其实现或者立刻刷新绘制的办法而达到良好的绘制效果,据调试,鼠标在窗口标题栏左键按下后(系统按钮自动绘制覆盖我们自己绘制的按钮),于是想当然的在鼠标消息WM_NCLBUTTONDOWN处理后立刻调用刷新绘制函数,未能达到效果,原因是该函数不是一个立刻返回的函数,其返回需要等待鼠标释放后才返回,即便在按下过程中移动鼠标也不会返回,证实方法:
TRACE("WM_NCLBUTTONDOWN start\n");
CDialog::OnNcLButtonDown(wParam, lParam);
TRACE("WM_NCLBUTTONDOWN end\n");
这两行输出消息的输出以鼠标按下,释放为基准,因此需要在函数返回后立刻绘制也没用,因为这过程中一直显示系统按钮。本人尝试跟踪进入默认CDialog::OnNcLButtonDown()函数处理,其调用的是CWnd::Deafult()来处理的,亦是相当繁复的流程了,此法亦未果.本人为这个问题调了2天了,反复尝试均不得效,无法可施,唯有看是不是有高手指点,不吝赐教。或给代码历程,不尽感激,E-Mail:[email protected]最好能在这里讨论,以便日后其他同行遇到同类问题好得方案。^_^
当鼠标移到关闭等按钮上时SetCapture,
之后把所有的处理放到OnLButtonDown等函数中,
我尝试过,效果非常理想
第44期
OnNcHitTest(CPoint point)
{
UINT uHitResult; uHitResult = CFrameWnd::OnNcHitTest(point); if(uHitResult == HTSYSMENU || uHitResult == HTMINBUTTON || uHitResult == HTMAXBUTTON || uHitResult == HTCLOSE)
{
uHitResult = HTCAPTION;
} return uHitResult;
}即可根本解决
重写WM_NCHITTEST消息的确能使得点击窗口按钮位置时候,系统不绘制默认按钮,但是情况并非如此简单,我推测您是在SDI下测试的,但在Dlg和MDI下,运行程序后第一次点击标题栏还会出现绘制系统默认按钮的现象,因此,需要处理这一问题.并且我搜索了一下这个问题,依旧CSDN有2个帖子跟我的问题基本一致:
http://topic.csdn.net/t/20051222/21/4476070.html
http://topic.csdn.net/t/20020304/15/554014.html据以上情况所述,我们可以大胆推断,一开始运行程序的时候首次点击标题栏应该发送不一样的消息,系统做特别处理,因此如果能找到这一消息,估计就好处理了,不过方案与实情也不应该拘礼与此,软件开发的奥秘在于发掘内部机制,可我凿子还是不够锋利,
至今似乎未有所得,因此还是望有经验的高手不吝赐教,以解我等之困惑!
/* 0x00A0 */
"WM_NCMOUSEMOVE", /* 0x00A0 */
"WM_NCLBUTTONDOWN", /* 0x00A1 */
"WM_NCLBUTTONUP", /* 0x00A2 */
"WM_NCLBUTTONDBLCLK", /* 0x00A3 */
"WM_NCRBUTTONDOWN", /* 0x00A4 */
"WM_NCRBUTTONUP", /* 0x00A5 */
"WM_NCRBUTTONDBLCLK", /* 0x00A6 */
"WM_NCMBUTTONDOWN", /* 0x00A7 */
"WM_NCMBUTTONUP", /* 0x00A8 */
"WM_NCMBUTTONDBLCLK", /* 0x00A9 */
NULL,
"WM_NCXBUTTONDOWN",
"WM_NCXBUTTONUP",
"WM_NCXBUTTONDBLCLK",
"WM_NCUAHDRAWCAPTION", /* 0x00AE */
"WM_NCUAHDRAWFRAME", /* 0x00AF */。。
附录XP下的消息跟踪截断:
<00353> 001D060E S WM_KICKIDLE
<00354> 001D060E R WM_KICKIDLE
<00355> 001D060E S WM_NCHITTEST xPos:476 yPos:178
<00356> 001D060E R WM_NCHITTEST nHittest:HTCAPTION
<00357> 001D060E S WM_MOUSEACTIVATE hwndTopLevel:001D060E nHittest:HTCAPTION uMsg:WM_LBUTTONDOWN
<00358> 001D060E R WM_MOUSEACTIVATE fuActivate:MA_NOACTIVATE
<00359> 001D060E S WM_SETCURSOR hwnd:001D060E nHittest:HTCAPTION wMouseMsg:WM_LBUTTONDOWN
<00360> 001D060E R WM_SETCURSOR fHaltProcessing:False
<00361> 001D060E P WM_NCLBUTTONDOWN nHittest:HTCAPTION xPos:476 yPos:178
<00362> 001D060E S WM_WINDOWPOSCHANGING lpwp:0012F3FC
<00363> 001D060E R WM_WINDOWPOSCHANGING
<00364> 001D060E S WM_WINDOWPOSCHANGED lpwp:0012F3FC
<00365> 001D060E R WM_WINDOWPOSCHANGED
<00366> 001D060E S WM_ACTIVATEAPP fActive:True dwThreadID:00000000
<00367> 001D060E R WM_ACTIVATEAPP
<00368> 001D060E S WM_NCACTIVATE fActive:True
<00369> 001D060E S .WM_GETICON fType:False
<00370> 001D060E R .WM_GETICON hicon:7406065D
<00371> 001D060E S .WM_GETTEXT cchTextMax:256 lpszText:0012DD5C
<00372> 001D060E R .WM_GETTEXT cchCopied:8 lpszText:0012E6C4 ("SkinTest")
<00373> 001D060E R WM_NCACTIVATE
<00374> 001D060E S WM_ACTIVATE fActive:WA_CLICKACTIVE fMinimized:False hwndPrevious:(null)
<00375> 001D060E S .WM_ACTIVATETOPLEVEL fActive:True dwThreadID:0012F01C
<00376> 001D060E R .WM_ACTIVATETOPLEVEL
<00377> 001D060E S ..WM_CTLCOLORBTN hdcButton:E3011CFE hwndButton:00340624
<00378> 001D060E R ..WM_CTLCOLORBTN hBrush:01100060
<00379> 001D060E R WM_ACTIVATE
<00380> 001D060E S message:0x00AE [Unknown] wParam:00001000 lParam:00000000
<00381> 001D060E S .WM_GETICON fType:False
<00382> 001D060E R .WM_GETICON hicon:7406065D
<00383> 001D060E S .WM_GETTEXT cchTextMax:256 lpszText:0012DDB8
<00384> 001D060E R .WM_GETTEXT cchCopied:8 lpszText:0012E720 ("SkinTest")
<00385> 001D060E R message:0x00AE [Unknown] lResult:00000001
<00386> 001D060E S message:0x00AE [Unknown] wParam:00001000 lParam:00000000
<00387> 001D060E S .WM_GETICON fType:False
<00388> 001D060E R .WM_GETICON hicon:7406065D解决方案:
截获以下2个消息,作抛弃或OnNcPaint()处理则可。
0x00AE://WM_NCUAHDRAWCAPTION
0x00AF://WM_NCUAHDRAWFRAME希望对以后处理窗体自绘的同行们有微薄作用, Have FUN! ^_^
{
// TODO: 在此添加专用代码和/或调用基类
if(message==0x00AE ||message==0x00AF)
{
// AfxMessageBox(_T("aaaa"));
return TRUE;
}
return CDialog::DefWindowProc(message, wParam, lParam);
}
我拦截了WM_ACTIVATE和WM_NCACTIVATE消息,对WM_NCPAINT消息不做处理进行测试,刚打开的时候标题栏是空白一片,但是当窗口失去焦点又重新得到的时候,标题栏又重新画了出来,不知道这是在哪儿画的,请高手指点
if( dwStyle & WS_VISIBLE )
pT->SetWindowLong(GWL_STYLE, (dwStyle & ~WS_VISIBLE));
pT->DefWindowProc(uMsg, wParam, lParam);
if( dwStyle & WS_VISIBLE )
pT->SetWindowLong(GWL_STYLE, dwStyle);这两个消息的确是令人讨厌的重绘了caption等,方法最好不是屏蔽,而是如上代码,暂时的设置window不可见,再调用DefWindowProc,而后再改回来。