在皮肤界面中,最难的恐怕就是自画窗口标题了,网上虽然有各种各样的解决方法,但是都不是很完美,而商业皮肤界面程序包是需要花钱买的。
其中最主要的问题有:
1、在标题栏或边框移动鼠标时系统会画窗口标题
2、没有任务栏图标。当取消了 SYS_CAPTION Style 后系统不会画窗口标题了,但是同时该窗口也没有任务栏图标。
3、最大化后在标题栏按下鼠标时系统会画窗口标题
4、按下系统按钮再移动鼠标到按钮外后没有正确的重画按钮
没办法,只有自己摸索了,研究了网络上的一些代码片段和几个比较成功的皮肤界面程序比如 RealPlay, Microsoft Money ,发现它们采用的手法都不尽相同,而网上关于这个问题也没有很完美的解决方案,总是有点小瑕疵。最后,去研究了一下 Windows 2000 的核心代码中关于窗口管理部分,终于算是比较满意的解决了上面的这些问题,但是,我采用的方法和RealPlay, Microsoft Money 都不太一样,RealPlay 好象没有了 NC_* 的消息,而Microsoft Money 则是采用遮盖的方法,而我是采用直接重画的方法,关键是找到重画的关键点。
关键代码:
1
BEGIN_MSG_MAP(TCaptionBaseT)2
MESSAGE_HANDLER(WM_NCHITTEST, OnNCHitTest)3
MESSAGE_HANDLER(WM_NCPAINT, OnNCPaint)4
5
MESSAGE_HANDLER(WM_NCLBUTTONDOWN, OnNCLButtonDown)6
MESSAGE_HANDLER(WM_NCLBUTTONUP, OnNCLButtonUp)7
MESSAGE_HANDLER(WM_NCMOUSEMOVE, OnNCMouseMove)8
MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp)9
10
MESSAGE_HANDLER(WM_NCACTIVATE, OnNCActivate)11
12
MESSAGE_HANDLER(WM_SYSCOMMAND, OnSysCommand)13
MESSAGE_HANDLER(WM_INITMENU, OnInitMenu)14
15
MESSAGE_HANDLER(WM_SIZE, OnSizeChanged)16
MESSAGE_HANDLER(WM_STYLECHANGED , OnStyleChanged)17
MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingsChange) 18 MESSAGE_HANDLER(WM_SETTEXT, OnSetText)19
MESSAGE_HANDLER(WM_SETICON , OnSetIcon)20
21
MESSAGE_HANDLER(WM_CREATE, OnCreate)22
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)23
END_MSG_MAP()24
25
LRESULT OnNCLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)26
...{27
// wParam 由 OnNcHitTest 返回28
T* pT = static_cast<T*>(this);29
30
POINT pt = ...{ GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };31
mn_ButtonPress = HTNOWHERE;32
33
switch( wParam )34
...{35
case HTMINBUTTON:36
case HTMAXBUTTON:37
case HTCLOSE:38
case HTHELP:39
mn_ButtonPress = wParam;40
mb_OnPressed = TRUE;41
bHandled = TRUE; // 表示已经处理过了,否则系统会重画按钮且 WM_NC_LBUTTONUP 不会被触发。42
pT->doReDrawCaption();43
break;
44
case HTCAPTION:45
bHandled = FALSE; // 继续让系统进行缺省处理46
...{47
DWORD nStyle = pT->GetStyle();48
if(nStyle & WS_MINIMIZE)49
...{50
return 0; // BUG: Prevents move of iconic window, but fixes Windows freeze51
}52
else53
if(nStyle & WS_MAXIMIZE)54
...{55
// 在系统最大化的时候需要在鼠标按下时重画标题栏56
bHandled = TRUE; // 表示已经处理过了,否则系统会重画按钮。57
// 让系统对 HTCAPTION 进行处理58
pT->DefWindowProc(uMsg, wParam, lParam);59
pT->doReDrawCaption();60
}61
}62
break;
63
default:64
bHandled = FALSE; // 继续让系统进行缺省处理65
break;
66
}67
return 0;68
}69
70
LRESULT OnNCLButtonUp(UINT /**//*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)71
...{72
// wParam 由 OnNcHitTest 返回73
T* pT = static_cast<T*>(this);74
75
mb_OnPressed = FALSE;76
77
if(wParam == mn_ButtonPress)78
...{79
// 鼠标按下和释放在相同的按钮上80
POINT pt = ...{ GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };81
switch( wParam )82
...{83
case HTMINBUTTON:84
pT->PostMessage(WM_SYSCOMMAND, SC_MINIMIZE, MAKELPARAM(pt.x, pt.y));85
bHandled = TRUE; // 禁止系统做缺省处理86
break;
87
case HTMAXBUTTON:88
mn_ButtonMove = HTNOWHERE; // 最大画后鼠标位置将改变89
if(pT->GetStyle() & WS_MAXIMIZE)90
pT->PostMessage(WM_SYSCOMMAND, SC_RESTORE, MAKELPARAM(pt.x, pt.y));91
else92
pT->PostMessage(WM_SYSCOMMAND, SC_MAXIMIZE, MAKELPARAM(pt.x, pt.y));93
bHandled = TRUE; // 禁止系统做缺省处理94
break;
95
case HTCLOSE:96
pT->PostMessage(WM_CLOSE);97
bHandled = TRUE; // 禁止系统做缺省处理98
break;
99
case HTHELP:100
pT->PostMessage(WM_HELP);101
bHandled = TRUE; // 禁止系统做缺省处理102
break;
103
case HTSYSMENU:104
default:105
bHandled = FALSE; // 让系统进行缺省处理106
}107
}108
mn_ButtonPress = HTNOWHERE;109
return 0;110
}111
112
LRESULT OnLButtonUp(UINT /**//*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)113
...{114
bHandled = FALSE;115
116
mb_OnPressed = FALSE;117
mn_ButtonPress = HTNOWHERE;118
119
return 0;120
}