循序漸進實現仿QQ界面(五):半透明窗體與不透明控件

本篇演示實現仿QQ界面的中間客戶區。QQ是可以調整界面透明度的,但是調整了透明度卻連中間客戶區也變得半透明瞭。客戶區畢竟是軟件最重要的部分,是要與用戶交互的,透明瞭就影響操作了,因此這裏的客戶區不學QQ,始終不透明。要實現不透明控件,只能創建一個彈出窗口,遮住主界面的客戶區域,然後設定與主界面連動,即始終跟着主窗口移動及調整尺寸。這個方法並不是很好,但卻幾乎是唯一的方法。爲什麼說幾乎是唯一的方法呢?的確存在着另外的解決方案,但是這個方法太麻煩了,在此討論一下這個方法。

 

實現半透明窗體,不透明控件,應該有不少朋友碰到過這個問題:爲什麼設定了窗口透明,窗口上的子窗口及控件也變得透明瞭?這個是受系統限制的,創建一個窗口,這個窗口區域就相當於一塊畫布,最終系統要在這塊畫布上繪製出窗口標題,客戶區,控件等等,而窗口和控件繪製時的GetDC,BeginPaint等不過是獲取到了跟自己相關的這塊畫布的一部分,在限定的區域內繪製,最終是畫到這塊畫布上,系統最後顯示窗口就是在桌面上顯示這塊畫布,透明度也是認這塊畫布,因此上面的子窗口及控件也一起變得透明瞭。另建一個彈出式窗口,相當於創建了另一塊畫布,就不受主窗口的透明度限制。

 

知道了原因,現在來討論如何不創建彈出式窗口,實現不透明控件。透明窗口有兩種實現方法,一種是調用SetLayeredWindowAttributes,設定統一的窗口透明度,既整個窗口採用同樣的透明度,QQ2009和這裏的演示程序採用的就是這個方法,這個方法不創建彈出窗口是無法實現不透明控件的,因爲想要控件不透明,就必須在控件區域採用不同的透明度,SetLayeredWindowAttributes無法做到,只能通過調用另一個函數UpdateLayeredWindow來實現可指定不同區域不同透明度的窗口,API代碼大致如下:

 

最關鍵的部分就是SelectObject(hMemDC,hbmp);實現不同區域不同透明度,全在這個選入設備的hbmp的圖象數據,Windows的32位色圖像的像素數據是COLORREF類型,0x00bbggrr格式,關鍵就在最高位的字節0x00,UpdateLayeredWindow是認這個字節來設定透明度,0x00爲全透明,0xFF爲不透明,這個字節的集合有個專門名稱叫ALPHA通道。設定hbmp圖象每一個像素的這個最高位字節數據,就可以實現像素級別的透明度。網上應該能搜到大把利用PNG圖片實現半透明窗口的例子,因爲PNG圖片是可以帶ALPHA通道的,解碼PNG圖片,自然就有了ALPHA通道,即設定了這個最高位字節,就不需要用代碼來一個個像素指定透明度了。實現這樣的半透明窗口是這樣一個過程:首先要用雙緩衝,創建與窗口相同大小的內存圖象,然後在這個內存圖象上繪製窗口的各個部分,標題欄,背景等等,一般是用PNG圖片實現,這樣就不用逐個像素指定透明度了,最後把這個內存圖象繪製到窗口。想實現不透明控件,就要把控件區域的ALPHA通道值設爲0xFF,然而不幸的是,幾乎所有的GDI操作,除了TransparentBlt,那些最常用的BitBlt,TextOut,FillRect等等都是忽略ALPHA通道的,繪製過後這些區域的ALPHA值都變成了0,即全透明。因此想要實現不透明控件,就要實現所有控件的自繪,使控件繪製到內存圖象上,常規GDI操作過後再設定這些區域的ALPHA值爲0xFF。是不是頭大了?這個方法太麻煩了,而且僅僅是爲了在半透明窗口上實現不透明控件這樣一個效果,代價太大,因此並不實用。QQ2009所用的DirectUI應該能很容易實現這個效果,但是並沒實現,估計跟執行效率有關,因爲這種像素級透明的程序在繪製時很耗時,調整窗口大小時可能會有延遲現象,在速度慢一點的機器上更是明顯。

 

現在來講模仿QQ的客戶區,有很多種方法,這裏選用相對比較簡單的方法,有更好的解決方案歡迎留言討論。首先是上部的搜索欄,當然是子類化EDIT控件進行自繪,處理WM_NCCALCSIZE消息加大其非客戶區,畫個外方內圓的邊框。在編輯框輸入內容後會有個自繪的下拉列表出來,這個其實跟點了“更改外觀”按鈕後出現的界面調色對話框是一樣的,不過是把那對話框改一下表現形式,然後搬到編輯框下面,就不演示了,還有右邊會出現清除和執行按鈕,又是貼圖,屬於非典型編輯框功能,也不演示了,有興趣的可以自己完成。

 

然後是側邊欄,看起來是TAB控件的功能,其實用工具欄更簡單一些,添加TBSTYLE_BUTTON|TBSTYLE_CHECKGROUP類型的按鈕就跟TAB控件的效果差不多。怎麼繪製前面一篇已經講過了,收起和展開只是隱藏和顯示而已。最重要的好友列表部分,這個要用到TAB控件了,調整窗口尺寸時3個標籤的寬度是跟着變的,這個需要創建TAB控件時指定TCS_FIXEDWIDTH窗口類型,子類化後在WM_SIZE消息裏發送TCM_SETITEMSIZE消息調整標籤的寬度。標籤需要自繪,鼠標點擊上面的下拉箭頭會彈出菜單,這個需要在WM_LBUTTONDOWN消息裏判斷一下,子類化後其實想幹什麼都行,只是麻煩一點罷了:)點擊標籤後下面列表子窗口的滑動效果切換也很簡單,把兩個窗口並排然後連續移動就可以,不過因爲刷新的關係,可能會有重影,如果想要更好的效果,應該是把子窗口截圖,然後用雙緩衝繪製出滑動效果,這裏就簡單一點,不用這個方法了。

 

好友列表,羣列表和最近聯繫人列表是用ListBox控件實現,本來不需要子類化,不過ListBox不支持鼠標移到選項上的高亮功能,因此還是實現了子類化,處理WM_MOUSEMOVE消息進行判斷。好友列表裏面是有“我的好友”,“陌生人”,“黑名單”等分類的,這些分類選項的高度與用戶項的高度不同,因此創建控件時需要指定LBS_OWNERDRAWVARIABLE類型,還有ListBox控件默認是會計算控件高度並調整尺寸適應列表項的高度,不會在客戶區顯示不完整的列表選項,這個功能我們不需要,因此還需指定LBS_NOINTEGRALHEIGHT類型,不自動調整高度,再指定一下LBS_HASSTRINGS|LBS_NOTIFY|WS_VSCROLL常規類型,然後就是在父窗口的WM_DRAWITEM消息裏進行列表選項的自繪了。加入選項時需要指定選項的高度,爲了區分分類選項和用戶信息選項,通過發送LB_SETITEMDATA消息綁定了不同的數據,這樣就能通過綁定的DATA來確定如何繪製。點擊分類選項是可以收起/展開該類下的用戶列表的,本來想收起時通過設定其下的用戶列表高度爲0來實現,結果發現LB_SETITEMHEIGHT消息只能設定選項的高度爲1~255之間的值,殘念,只能是收起時刪掉用戶列表,展開時再添加進來。用戶列表項高亮狀態時會有“發送短信”,“發送郵件”等按鈕,這裏只演示了“發送郵件”按鈕,同樣是通過靜態文本控件實現,前面一篇文章已經講過了。信息提示和右鍵菜單這裏就不演示了,那個菜單項實在太多,看着就害怕:)

 

現在看看程序的截圖:

 

 

差不多快完成了,還差滾動條和異型菜單,下一篇再說了。

 

演示程序下載地址:http://download.csdn.net/source/2059841

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章