當年Vista系統剛出來的時候,最吸引人的莫過於半透明磨砂的窗體界面了,迷倒了多少人。這個界面技術隨即引起了編程界的一陣騷動,不少人都在問:如何實現這一界面效果?固然,在Vista下卻是很簡單,系統自己支持,因此幾乎不須要寫一句代碼,可是當時仍是XP的天下,因而你們就能夠研究在XP下如何實現這一效果。html
最早實現的應該是桌面天氣秀,還有笨笨鍾,後來魚魚軟件的魚魚桌面秀也成功在XP下模仿了Vista的側邊欄,的確,讓人很激動,可是他們保密,問也問不到到底是用了什麼技術,記得當年大富翁論壇(http://www.delphibbs.com 比較著名的Delphi論壇)上還爲此進行過討論,最後有個ID叫小雨哥的人提供了一個方法(固然是否是他原創我不得而知,小雨哥目前在盛大網絡,見過一面,無限膜拜中),下面是Delphi的實現代碼: ios
01.
var
02.
pt1, pt2 : TPoint;
03.
sz : TSize;
04.
bf : TBlendFunction;
05.
begin
06.
07.
bitmap:=tgpbitmap
.
Create(PNGFile);
//這個PNGFile是具體的PNG圖片路徑
08.
pt1 := Point(left,top);
//窗口作上角的座標
09.
pt2 := Point(
0
,
0
);
//這個就不用說了,一看見(0,0)就應該明白了
10.
sz
.
cx := bitmap
.
GetWidth;
//尺寸不要超過圖像大小,否則窗口就什麼都沒有了,連個影子都沒有
11.
sz
.
cy := bitmap
.
GetHeight;
//同上
12.
bf
.
BlendOp := AC_SRC_OVER;
//這些死記就好了
13.
bf
.
BlendFlags :=
0
;
//同上
14.
if
(nTran<
0
)
or
(nTran>
255
)
then
nTran:=
255
;
15.
bf
.
SourceConstantAlpha := nTran;
//同上
16.
bf
.
AlphaFormat := AC_SRC_ALPHA;
//同上
17.
DeleteObject(bmp);
//前面就是在這裏犯的錯誤,否則佔用的內存會無限增大
18.
bitmap
.
GetHBITMAP(
0
,bmp);
// HBITMAP是windows標準位圖格式,支持透明,這裏是從tgpbitmap 轉化成 HBITMAP
19.
DeleteDC(DC);
20.
DC := CreateCompatibleDC(Canvas
.
Handle);
21.
old_bmp := SelectObject(DC, bmp);
22.
UpdateLayeredWindow(Handle, Canvas
.
Handle, @pt1, @sz, DC, @pt2,
0
, @bf,ULW_ALPHA);
//調用UpdateLayeredWindow實現
23.
end
;
這個方法實際上是生成一個PNG的窗體,咱們知道,PNG圖片是具備Alpha屬性的,因此,若是PNG是半透明磨砂裝的,那麼生成的窗體也就是半透明磨砂裝的,注意,上面的代碼須要使用GDIPlus類uses gdipapi, gdipobj;編程
咱們這裏不討論這段代碼,這段代碼是別人寫的,看似很是完美,好比日期查詢器的系統初始化界面就能夠由上面的代碼生成:windows
可是這段代碼有個致命的問題,你能夠試試在Form上面放一些控件,好比button,edit等,再次編譯你會神奇的發現,全部的控件都不顯示,這是怎麼回事呢?經過查閱MSDN,咱們發現問題出在UpdateLayeredWindow函數上。api
MSDN中,關於該函數的Remarks中有這樣一段說明:網絡
The UpdateLayeredWindow function maintains the window's appearance on the screen. The windows underneath a layered window do not need to be repainted when they are uncovered due to a call to UpdateLayeredWindow, because the system will automatically repaint them. This permits seamless animation of the layered window.app
大意是說,使用這個函數之後,下一層窗體不會再從新繪製,也就是說,窗體不會響應Onpaint事件來重繪全部控件,致使控件沒法看見,可是實際上控件是存在的,你能夠在響應位置上點擊一下button,你會發現button依然會響應點擊事件,但就是看不見。less
這就頭疼了,若是不能使用控件,或者說控件看不見,光一個窗體再好看有什麼用呢?其實微軟貌似是用這個函數作無縫鏈接動畫用的,MSDN說的很清楚嘛:This permits seamless animation of the layered window.函數
嗯,好吧,既然這個方法不行,那就換一個吧,因而有人想到了使用2個窗體來解決。post
2個窗體怎麼解決呢?其實也很簡單,一個窗體做爲半透明的PNG放在後面,一個窗體做爲放置控件的窗體放在前面,而後只要2個窗體同步移動就能夠了,就拿日期查詢器來講吧,登錄窗體:
這個窗體上下都有半透明的邊框,上面也有控件顯示,也許你很差理解,若是我分解一下:
怎麼樣?這樣你就發現了,實際上是2個窗體,後面一個背景窗體,前面一個Border:=none的控件窗體,而後兩個窗體同步移動便可,這樣咱們就僞造了一個半透明的窗體。事實上,不少軟件也是這麼作的,包括有些帶陰影的窗體也是一樣原理。
至於同步移動,也很簡單,處理下OnMove消息就能夠了:
01.
function
WndNewProc(Wnd: HWND; uMsg: UINT; wPar: WPARAM; lPar: LPARAM): LRESULT; stdcall;
02.
var
Rect: TRect;
03.
begin
04.
Result :=
0
;
05.
case
uMsg
of
06.
WM_LBUTTONDOWN: SendMessage(Wnd, WM_SYSCOMMAND, SC_MOVE+
2
,
0
);
07.
else
08.
begin
09.
if
((uMsg = WM_MOVING)
or
(uMsg = WM_MOVE))
and
GetWindowRect(Wnd, Rect)
then
10.
SetWindowPos(ComponentForm
.
Handle,
0
, Rect
.
Left, Rect
.
Top,
0
,
0
, SWP_NOSIZE);
11.
Result := DefWindowProc(Wnd, uMsg, wPar, lPar);
12.
end
;
13.
end
;
14.
end
;
咱們假設放置控件的窗體名字叫ComponentForm,當咱們鼠標左鍵按下並移動背景窗體的時候,控件窗體跟着同步移動便可。
固然,有時候咱們能夠直接用代碼生成背景窗體,這樣的話能夠減小一些程序體積,生成背景窗體咱們能夠用一個函數叫CreateWindowEx,注意這裏要用帶Ex的,表示會有附加參數,咱們只要把這個函數的第一個參數設置爲WS_EX_LAYERED就能夠了,他表示一個額外的層屬性。
兩個方法均可以,咱們既能夠直接用2個窗體,也能夠用CreateWindowEx函數來生成背景窗體,效果是同樣的,看我的喜愛。