在皮肤界面中,最难的恐怕就是自画窗口标题了,网上虽然有各种各样的解决方法,但是都不是很完美,而商业皮肤界面程序包是需要花钱买的。
其中最主要的问题有:
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 freeze
51 }
52 else
53 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 else
92 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 }