自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

利用C#實(shí)現(xiàn)任務(wù)欄通知窗口

開(kāi)發(fā) 后端
想必大部分網(wǎng)友都使用過(guò)QQ、MSN等聊天程序,它們的界面都相當(dāng)華麗,尤其是當(dāng)網(wǎng)友上線以及消息提示時(shí)會(huì)有一個(gè)浮動(dòng)的窗體從屏幕的右下方緩慢升起,既美觀又人性化。本文主要講解用C#來(lái)實(shí)現(xiàn)任務(wù)欄通知窗口。

作為程序員在享受的同時(shí)我們也不禁要問(wèn):這到底是怎么實(shí)現(xiàn)的呢?本文就利用Visual Studio .Net C# 2005以及.Net框架繪圖技術(shù)來(lái)實(shí)現(xiàn)這種任務(wù)欄通知窗口。

簡(jiǎn)介

QQ和MSN的任務(wù)欄通知窗口很人性化,它可以在不丟失主窗體焦點(diǎn)的前提下顯示一個(gè)具備皮膚Skin的通知窗體,當(dāng)它顯示一段時(shí)間后會(huì)自動(dòng)消失,所以用戶根本不用干預(yù)它。

這樣的通知窗體和一般的具備標(biāo)題欄、系統(tǒng)圖標(biāo)和按鈕的窗體沒(méi)有太大的區(qū)別,窗體表面其實(shí)就是畫(huà)上去的一張位圖而已,而窗體的浮動(dòng)則會(huì)復(fù)雜一點(diǎn),我們會(huì)用到.Net框架

的雙重緩沖區(qū)繪圖技術(shù)(參見(jiàn)作者編譯文章“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)。兩種位圖的皮膚運(yùn)行時(shí)的界面如下:

背景知識(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類(lèi)將圖像輪廓按照一定路徑標(biāo)注下來(lái),稍后便按照該路徑創(chuàng)建Region對(duì)象。

然后通過(guò)窗體的繪圖事件將位圖的內(nèi)容顯示在窗體表面,我們沒(méi)有直接使用OnPaintbackground事件而是重載了該方法,這樣做的好處就是一些低層的繪制操作還繼續(xù)交由.Net框架運(yùn)行時(shí)來(lái)處理,我們只考慮實(shí)際需要的繪制操作即可。在OnPaintbackground方法中我們啟用了雙重緩沖區(qū)繪圖技術(shù),所謂該技術(shù)就是指先在內(nèi)存中的一塊畫(huà)布上把將要顯示的圖像顯示出來(lái)或進(jìn)行處理,等到操作完成再將該畫(huà)布上所顯示的圖像放置到窗體表面,這樣的機(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)沒(méi)有丟失,也就是說(shuō)程序沒(méi)有將自身的焦點(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框架根本沒(méi)有提供類(lèi)似的方法,那么我們能否通過(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類(lèi)對(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ì)沒(méi)有任何不妥,但是頻繁的事件響應(yīng)和處理反而使程序性能有所降低。我們將繼續(xù)使用 Win32API的底層處理方法來(lái)解決該問(wèn)題,就是向窗體發(fā)送標(biāo)題欄被單擊的消息,模擬實(shí)際的拖動(dòng)操作。

我們會(huì)通過(guò)2個(gè)計(jì)時(shí)器來(lái)完成窗體的顯示、停留和隱藏,通過(guò)設(shè)置速度變量可以改變窗口顯示和隱藏的速度。

  1. [DllImportAttribute("user32.dll")]   
  2. public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);   
  3. //發(fā)送消息//winuser.h 中有函數(shù)原型定義   
  4. [DllImportAttribute("user32.dll")]   
  5. public static extern bool ReleaseCapture(); //釋放鼠標(biāo)捕捉winuser.h   
  6. [DllImportAttribute("user32.dll")] //winuser.h   
  7. private static extern Boolean ShowWindow(IntPtr hWnd, Int32 nCmdShow);  

SendMessage向消息循環(huán)發(fā)送標(biāo)題欄被按下的消息來(lái)模擬窗體的拖動(dòng),ShowWindow用來(lái)將特定句柄的窗體顯示出來(lái),注意第二個(gè)參數(shù) nCmdShow,它表示窗體應(yīng)該怎樣顯示出來(lái),而我們需要窗體不獲得焦點(diǎn)顯示出來(lái),SW_SHOWNOACTIVATE可以滿足我們要求,繼續(xù)在 WinUser.h文件中搜索找到該常量對(duì)應(yīng)的值為4,于是我們就可以這樣調(diào)用來(lái)顯示窗體了:

  1. ShowWindow(this.Handle, 4);  

我們創(chuàng)建了一個(gè)自定義函數(shù)ShowForm用來(lái)封裝上面的ShowWindow用來(lái)是顯示窗體,同時(shí)傳遞了所用到的幾個(gè)Rectangle矩形區(qū)域?qū)ο?,最后調(diào)用ShowWindows函數(shù)將窗體顯示出來(lái),代碼片段如下:

  1. public void ShowForm(string ftitletext, string fcontenttext, Rectangle fRegionofFormTitle,   
  2. Rectangle fRegionofFormTitlebar, Rectangle fRegionofFormContent, Rectangle fRegionofCloseBtn)   
  3. {   
  4. titleText = ftitletext;   
  5. contentText = fcontenttext;   
  6. WorkAreaRectangle = Screen.GetWorkingArea(WorkAreaRectangle);   
  7. this.Top = WorkAreaRectangle.Height + this.Height;   
  8. FormBorderStyle. = FormBorderStyle.None;   
  9. WindowState = FormWindowState.Normal;   
  10. this.SetBounds(WorkAreaRectangle.Width - this.Width,   
  11. WorkAreaRectangle.Height - currentTop, this.Width, this.Height);   
  12. CurrentState = 1;   
  13. timer1.Enabled = true;   
  14. TitleRectangle = fRegionofFormTitle;   
  15. TitlebarRectangle = fRegionofFormTitlebar;   
  16. ContentRectangle = fRegionofFormContent;   
  17. CloseBtnRectangle = fRegionofCloseBtn;   
  18. ShowWindow(this.Handle, 4); //#define SW_SHOWNOACTIVATE   
  19. }  

CurrentState變量表示窗體的狀態(tài)是顯示中、停留中還是隱藏中,兩個(gè)計(jì)時(shí)器根據(jù)窗體不同狀態(tài)對(duì)窗體的位置進(jìn)行更改,我們會(huì)使用SetBounds來(lái)執(zhí)行該操作: 

  1. this.SetBounds(WorkAreaRectangle.Width - this.Width, WorkAreaRectangle.Height - currentTop, this.Width, this.Height);   

當(dāng)窗體需要升起時(shí)將窗體的Top屬性值不斷減少,而窗體回落時(shí)將Top屬性值增加并超過(guò)屏幕的高度窗體就消失了,雖然原理很簡(jiǎn)單但仍需精確控制。
SetBackgroundBitmap函數(shù)首先將窗體背景圖像保存到BackgroundBitmap變量中,然后根據(jù)該位圖圖像輪廓和透明色創(chuàng)建Region,BitmapToRegion就用于完成Bitmap到Region的轉(zhuǎn)換,程序再將這個(gè)Region付值給窗體的Region屬性以完成不規(guī)則窗體的創(chuàng)建。

  1. public void SetBackgroundBitmap(Image image, Color transparencyColor)     
  2. {     
  3. BackgroundBitmap = new Bitmap(image);     
  4. Width = BackgroundBitmap.Width;     
  5. Height = BackgroundBitmap.Height;     
  6. Region = BitmapToRegion(BackgroundBitmap, transparencyColor);     
  7. }     
  8. public Region BitmapToRegion(Bitmap bitmap, Color transparencyColor)     
  9. {     
  10. if (bitmap == null)     
  11. throw new ArgumentNullException("Bitmap""Bitmap cannot be null!");     
  12. int height = bitmap.Height;     
  13. int width = bitmap.Width;     
  14. GraphicsPath path = new GraphicsPath();     
  15. for (int j = 0; j < height; j++)     
  16. for (int i = 0; i < width; i++)     
  17. {     
  18. if (bitmap.GetPixel(i, j) == transparencyColor)     
  19. continue;     
  20. int x0 = i;     
  21. while ((i < width) && (bitmap.GetPixel(i, j) != transparencyColor))     
  22. i++;     
  23. path.AddRectangle(new Rectangle(x0, j, i - x0, 1));     
  24. }     
  25. Region region = new Region(path);     
  26. path.Dispose();     
  27. return region;     
  28. }   

通知窗體背景以及文字的繪制在重載的OnPaintBackground方法中完成,而且利用了雙重緩沖區(qū)技術(shù)來(lái)進(jìn)行繪制操作,代碼如下:

  1. protected override void OnPaintBackground(PaintEventArgs e)   
  2. {   
  3. Graphics grfx = e.Graphics;   
  4. grfx.PageUnit = GraphicsUnit.Pixel;   
  5. Graphics offScreenGraphics;   
  6. Bitmap offscreenBitmap;   
  7. ffscreenBitmap = new Bitmap(BackgroundBitmap.Width, BackgroundBitmap.Height);   
  8. ffScreenGraphics = Graphics.FromImage(offscreenBitmap);   
  9. if (BackgroundBitmap != null)   
  10. {   
  11. offScreenGraphics.DrawImage(BackgroundBitmap, 0, 0,   
  12. BackgroundBitmap.Width, BackgroundBitmap.Height);   
  13. }   
  14. DrawText(offScreenGraphics);   
  15. grfx.DrawImage(offscreenBitmap, 0, 0);   
  16. }  

上述代碼首先返回窗體繪制表面的Graphics并保存在變量grfx中,然后創(chuàng)建一個(gè)內(nèi)存Graphics對(duì)象 offScreenGraphics和內(nèi)存位圖對(duì)象offscreenBitmap,將內(nèi)存位圖對(duì)象的引用付值給offScreenGraphics,這樣所有對(duì)offScreenGraphics的繪制操作也都同時(shí)作用于offscreenBitmap,這時(shí)就將需要繪制到通知窗體表面的背景圖像 BackgroundBitmap繪制到內(nèi)存的Graphics對(duì)象上,DrawText函數(shù)根據(jù)需要顯示文字的大小和范圍調(diào)用 Graphics.DrawString將文字顯示在窗體的特定區(qū)域。最后,調(diào)用Graphics.DrawImage將內(nèi)存中已經(jīng)繪制完成的圖像顯示到通知窗體表面。

我們還需要捕獲窗體的鼠標(biāo)操作,有三個(gè)操作在這里進(jìn)行,1、處理拖動(dòng)窗體操作,2、處理通知窗體的關(guān)閉操作,3、內(nèi)容區(qū)域的單擊操作。三個(gè)操作都需要檢測(cè)鼠標(biāo)的當(dāng)前位置與每個(gè)Rectangle區(qū)域的包含關(guān)系,只要單擊落在特定區(qū)域我們就進(jìn)行相應(yīng)的處理,代碼如下:

  1. private void TaskbarForm_MouseDown(object sender, MouseEventArgs e)   
  2. {   
  3. if (e.Button == MouseButtons.Left)   
  4. {   
  5. if (TitlebarRectangle.Contains(e.Location)) //單擊標(biāo)題欄時(shí)拖動(dòng)   
  6. {   
  7. ReleaseCapture(); //釋放鼠標(biāo)捕捉   
  8. SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0); //發(fā)送左鍵點(diǎn)擊的   
  9. //消息至該窗體(標(biāo)題欄)   
  10. }   
  11. if (CloseBtnRectangle.Contains(e.Location)) //單擊Close按鈕關(guān)閉   
  12. {   
  13. this.Hide();   
  14. currentTop = 1;   
  15. }   
  16. if (ContentRectangle.Contains(e.Location )) //單擊內(nèi)容區(qū)域   
  17. {   
  18. System.Diagnostics.Process.Start("http://www.Rithia.com"); }   
  19. }   
  20. }  

結(jié)論

該程序可以很好的進(jìn)行通知窗體的顯示、停留和隱藏操作,并且具備簡(jiǎn)單的換膚機(jī)制,在利用了雙重緩沖區(qū)繪圖技術(shù)后,可以保證窗體的繪制平滑且沒(méi)有閃爍。

【編輯推薦】

  1. 淺談C++調(diào)用C#的DLL程序方法
  2. 教你寫(xiě)不可思議的C#代碼
  3. C#選擇正確的集合進(jìn)行編碼
  4. 走進(jìn)C#奇妙函數(shù)之ToLookup
責(zé)任編輯:于鐵 來(lái)源: 博客園
相關(guān)推薦

2010-01-08 18:10:44

VB.NET實(shí)現(xiàn)任務(wù)欄

2010-01-12 18:21:27

VB.NET任務(wù)欄程序

2009-11-06 18:56:22

Windows 7任務(wù)欄

2021-07-25 22:18:37

Windows 10Windows微軟

2021-06-17 13:10:08

WindowsWindows 11Windows 10

2021-08-24 23:11:22

Windows 10Windows微軟

2021-08-25 22:49:41

Windows 10Windows微軟

2023-03-29 08:41:52

Windows 11微軟

2010-01-14 09:04:59

Windows 7任務(wù)欄還原

2010-03-11 09:08:04

Windows 7定制任務(wù)欄

2021-10-20 22:27:17

Windows 10Windows微軟

2021-12-30 07:51:12

Windows 11操作系統(tǒng)微軟

2021-12-28 08:16:21

Windows 11操作系統(tǒng)微軟

2013-12-16 15:35:37

Win9Windows 9概念圖

2009-12-22 09:05:30

Windows 7Jumplist清理

2009-03-12 18:35:24

Windows 7任務(wù)欄

2019-12-20 14:19:50

Windows 10操作系統(tǒng)

2021-08-13 22:24:57

Windows 10Windows微軟

2009-08-13 09:07:36

Java多線程

2009-04-27 16:09:32

Windows 7微軟操作系統(tǒng)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)