C#實(shí)現(xiàn)窗體顯示背景知識(shí)
C#實(shí)現(xiàn)窗體顯示是比較常見的東東,這里就主要介紹一下C#實(shí)現(xiàn)窗體顯示背景知識(shí),對(duì)常用QQ、MSN等聊天的人們給點(diǎn)提示。
想必大部分網(wǎng)友都使用過(guò)QQ、MSN等聊天程序,它們的界面都相當(dāng)華麗,尤其是當(dāng)網(wǎng)友上線以及消息提示時(shí)會(huì)有一個(gè)浮動(dòng)的窗體從屏幕的右下方緩慢升起,既美觀又人性化,作為程序員在享受的同時(shí)我們也不禁要問(wèn):這到底是怎么實(shí)現(xiàn)的呢?本文就利用C#實(shí)現(xiàn)窗體顯示。
簡(jiǎn)介
QQ和MSN的任務(wù)欄通知窗口很人性化,它可以在不丟失主窗體焦點(diǎn)的前提下顯示一個(gè)具備皮膚Skin的通知窗體,當(dāng)它顯示一段時(shí)間后會(huì)自動(dòng)消失,所以用戶根本不用干預(yù)它。這樣的通知窗體和一般的具備標(biāo)題欄、系統(tǒng)圖標(biāo)和按鈕的窗體沒有太大的區(qū)別,窗體表面其實(shí)就是畫上去的一張位圖而已,而窗體的浮動(dòng)則會(huì)復(fù)雜一點(diǎn),我們會(huì)用到.Net框架的雙重緩沖區(qū)繪圖技術(shù)(參見作者編譯文章"Windows 窗體的.Net框架繪圖技術(shù)")來(lái)保證移 動(dòng)窗體時(shí)所顯示的內(nèi)容平滑且不閃爍,以及使用P/Invoke平臺(tái)調(diào)用進(jìn)行對(duì)Win32API函數(shù)的調(diào)用來(lái)完成不獲得焦點(diǎn)的窗體顯示和非標(biāo)題欄窗體拖動(dòng)。
C#實(shí)現(xiàn)窗體顯示背景知識(shí)
通知窗口就是將一般的窗體附加上一層皮膚,這里所謂的皮膚就是一張位圖圖片,該位圖圖片通過(guò)窗體的OnPaintbackground事件被繪制到窗體表面,在附加位圖之前需要調(diào)整窗體的可視屬性,由于繪制操作是針對(duì)于窗體客戶區(qū)域的,所謂客戶區(qū)域就是指窗體標(biāo)題欄下方以及窗體邊框以內(nèi)的所有區(qū)域,所以需要將窗體的邊框和外觀屬性 FormBorderStyle調(diào)整為:None,這樣所繪制的圖像就會(huì)填充整個(gè)窗體。
首先,我們會(huì)用到Region對(duì)象,Region對(duì)象可以精確的描繪出任意形狀的輪廓范圍,通過(guò)一個(gè)位圖圖像創(chuàng)建Region對(duì)象后再將其傳遞給窗體的Region屬性就可以使窗體按照 Region所定義的輪廓顯示出來(lái)。作為皮膚使用的位圖文件可以通過(guò)任何圖像編輯軟件諸如:Photeshop來(lái)創(chuàng)建和編輯,只是注意一點(diǎn),需要將圖片的背景色調(diào)成特定顏色以便程序繪制時(shí)將其清除,我們?cè)谶@里使用的背景色為粉紅色。為了能夠讓Region對(duì)象按照?qǐng)D像中感興趣的內(nèi)容邊框來(lái)創(chuàng)建窗體,我們還需要使用GraphicsPath類將圖像輪廓按照一定路徑標(biāo)注下來(lái),稍后便按照該路徑創(chuàng)建Region對(duì)象。
然后通過(guò)窗體的繪圖事件將位圖的內(nèi)容顯示在窗體表面,我們沒有直接使用OnPaintbackground事件而是重載了該方法,這樣做的好處就是一些低層的繪制操作還繼續(xù)交由.Net框架運(yùn)行時(shí)來(lái)處理,我們只考慮實(shí)際需要的繪制操作即可。在OnPaintbackground方法中我們啟用了雙重緩沖區(qū)繪圖技術(shù),所謂該技術(shù)就是指先在內(nèi)存中的一塊畫布上把將要顯示的圖像顯示出來(lái)或進(jìn)行處理,等到操作完成再將該畫布上所顯示的圖像放置到窗體表面,這樣的機(jī)制可以非常有效的降低閃爍的出現(xiàn),使圖像顯示更加平滑。通知窗體從屏幕的右下方進(jìn)行升起停留一段時(shí)間后再慢慢回落,這里需要用到返回屏幕區(qū)域的大小范圍的.Net框架方法 Screen.GetWorkingArea(WorkAreaRectangle),通過(guò)一定算法計(jì)算出通知窗體顯示前的初始位置。最后,我們將要顯示的文本按照一定格式和Rectangle對(duì)象所指定的區(qū)域范圍繪制到窗體表面。通知窗體的關(guān)閉操作是通過(guò)設(shè)定一個(gè)區(qū)域,當(dāng)用戶用鼠標(biāo)單擊時(shí)檢測(cè)單擊坐標(biāo)是否在該區(qū)域內(nèi),若在區(qū)域內(nèi)就可以執(zhí)行隱藏通知窗體的代碼。
我們注意了,當(dāng)QQ和MSN的通知窗口顯示時(shí)其主窗體的焦點(diǎn)沒有丟失,也就是說(shuō)程序沒有將自身的焦點(diǎn)轉(zhuǎn)移到顯示的通知窗體上。經(jīng)過(guò)測(cè)試,我們無(wú)論怎么樣調(diào)用.Net框架提供的窗體顯示例程譬如:Form.Show都無(wú)法保證主窗體的焦點(diǎn)不丟失,在VC環(huán)境下我們可以使用Win32API的ShowWindows函數(shù)來(lái)完成復(fù)雜的窗體顯示操作,但是.Net框架根本沒有提供類似的方法,那么我們能否通過(guò).Net框架調(diào)用該API函數(shù)來(lái)顯示窗體呢?幸好.Net框架提供了P/Invoke平臺(tái)調(diào)用,利用平臺(tái)調(diào)用這種服務(wù),托管代碼就可以調(diào)用在動(dòng)態(tài)鏈接庫(kù)中實(shí)現(xiàn)的非托管函數(shù),并可以封送其參數(shù),我們可以輕松的顯示但不獲得焦點(diǎn)的窗體。程序中用到的Windows API以及常量的定義都保存在WinUser.h頭文件中,其對(duì)應(yīng)的動(dòng)態(tài)鏈接庫(kù)文件就是user32.dll,使用.Net框架提供的 DllImportAttribute類對(duì)導(dǎo)入的函數(shù)進(jìn)行定義,然后就可以非常方便的在程序中調(diào)用該函數(shù)了。
由于我們將通知窗體的標(biāo)題欄隱藏了,所以對(duì)窗體拖動(dòng)操作還需要我們自己動(dòng)手進(jìn)行處理。本文介紹了如何更加高效的進(jìn)行拖動(dòng)窗體操作,有些網(wǎng)友在對(duì)于非標(biāo)題欄拖動(dòng)窗體編程時(shí)偏向組合使用鼠標(biāo)事件來(lái)進(jìn)行,這樣做的本質(zhì)沒有任何不妥,但是頻繁的事件響應(yīng)和處理反而使程序性能有所降低。我們將繼續(xù)使用 Win32API的底層處理方法來(lái)解決該問(wèn)題,就是向窗體發(fā)送標(biāo)題欄被單擊的消息,模擬實(shí)際的拖動(dòng)操作。
我們會(huì)通過(guò)2個(gè)計(jì)時(shí)器來(lái)完成窗體的顯示、停留和隱藏,通過(guò)設(shè)置速度變量可以改變C#實(shí)現(xiàn)窗體顯示和隱藏的速度。
【編輯推薦】