引言:誰都希望自己的應用程式能讓人留下一個深刻的印象,讓自己的程式表單有一件與眾不同的"外衣"是一個好辦法。試想:在一大堆的普通視窗中突然跳出一個很酷的介面,一定能讓人眼睛一亮進而產生興趣的。在VB,VC中如何定製可伸縮個人化視窗早就不是什麼秘密了,已經有了大量相關的文章進行介紹,無非都是如何調用系統API之類的方法,但是在.Net中調用API卻相對比較麻煩,所以使用.Net製作個人化表單的文章也有一些,一般都是使用透明背景加圖片的方式,所以不能移動或者不能任意放大縮小表單。那有沒有不需要調用系統API的方法來實現可伸縮的個人化表單的辦法呢?當然有,.Net Framework提供了一套非常強大的系統類別庫,我們下面就要做一個使用"純".Net打造的可伸縮個人化表單。我們需要將表單所有的"皮膚"全部換成我們自己定義的,包括標題列,邊框和系統按紐等,所以我們首先需要定做一套自己的皮膚圖形檔案。因為表單是可伸縮的,所以我們不能簡單的取一整幅圖片來作為表單皮膚,而是根據需要先將圖片切割為不同的部分,一般來說,有以示幾大部分(紅線為切割線):根據方位,將圖片各部分命名為:Bottom_Left,Bottom_Middle,Bottom_Right,Middle_Left,Middle_Right,Top_Left,Top_Middle,Top_Right,SysButton_Min,SysButton_Max,SysButton_Close,SysButton_Restore等。注意,有些圖片是可以伸縮的地方,比如Middle_Left,Bottom_Middle等處的圖片可以只是一小塊,以後需要進行重複貼圖。而有些固定大小的圖片,比如Bottom_Left,Top_Left等以後只用貼一次,實際應用的時候要注意區分。採用以上原則,你便可以製作皮膚圖片,圖示如下:然後可以將這些圖片放到ImageList控制項或資源檔中供程式調用。(關於如何製作資源檔請參考:Visual C#資源檔編程--建立資源檔)接下來,我們使用Visual Studio .Net建立一個Windows應用程式的項目,在表單的屬性設定中,將表單的FormBorderStyle屬性設定為None(無邊框樣式),如所示:定義一個資源管理員:private ResourceManager rm ;然後使用以下的方法在Form的建構函式中將圖片取出來(資源檔名為Skin.resources):rm = new ResourceManager("SkinWindow.Skin", Assembly.GetExecutingAssembly());Bottom_Left = (Bitmap)rm.GetObject("Bottom_Left");…(其它的圖片也按照此方法取)重載Form的OnPaint事件:Graphics g = e.Graphics;//手工畫表單的各個部分DrawMiddle_Left(e.Graphics);//畫左邊框DrawBottom_Middle(e.Graphics);//畫下邊框DrawMiddle_Right(e.Graphics);//畫右邊框DrawBottom_Left(e.Graphics);//畫左下角DrawBottom_Right(e.Graphics);//畫右下角DrawTop_Left(e.Graphics);//畫標題列左邊DrawTop_Right(e.Graphics);//畫標題列右邊DrawTop_Middle(e.Graphics);//畫標題列中間DrawSys_Button(e.Graphics);//畫系統按紐以下是上述畫皮膚方法的具體實現部分,我只舉一個畫左邊框的程式碼範例,其它的部分請讀者舉一返三:
private void DrawMiddle_Left(Graphics g){Brush brush = new TextureBrush(Middle_Left, new Rectangle(0, 0, Middle_Left.Width, Middle_Left.Height));g.FillRectangle(brush, 0, TITLE_WIDTH, Middle_Left.Width, Height - Bottom_Middle.Height - TITLE_WIDTH);}
衣服穿上了,現在我們的程式就有了不同的外觀:看上去已經很酷了,不過只是花架子,因為邊框,標題列,系統按紐都是我們自己畫上去的假的邊框,標題列和系統按紐,所以這個表單既不能移動也不能自由的放大縮小,點關閉都沒用。以前我們寫程式從來都不需要關心這個的,這些都是表單的準系統呀?沒有從來都沒有想到這個竟然還會是個問題吧?怎麼辦呢?答案就是我們自己來做,不過會比較麻煩,因為取消掉了邊框,所以Windows不會幫你發出系統事件,你捕捉不到系統發生了什麼事情的話,就沒有辦法寫下響應代碼,所以我們要自己檢測滑鼠的座標,並根據滑鼠的動作,自己來發出事件訊息,然後進行響應。首先我們先定義出一些響應事件的代碼,我定義了一個抽象的基類MouseAction,用來表示所有的滑鼠事件,它有一個抽象方法Action:public abstract class MouseAction{public abstract void Action(int ScreenX, int ScreenY, System.Windows.Forms.Form form);}然後再來定義出它的各個衍生類別來表示出具體每個滑鼠事件響應的代碼。下面是一個向右展開視窗事件的代碼響應:
public class MouseSizeRight : MouseAction{private int lx;public MouseSizeRight(int LocationX){lx = LocationX;}public override void Action(int ScreenX, int ScreenY, System.Windows.Forms.Form form){form.Width = ScreenX - lx;form.Invalidate();}}
非常簡單和容易理解,我就不再贅述,其它的各個事件也都一樣簡單,這裡也不給出所有事件的實現代碼,只是列舉一下還需要實現的代碼響應類:MouseSizeLeft:展開左邊框MouseSizeBottom:展開下邊框MouseSizeTop:展開上邊框MouseSizeTopLeft:展開左上方MouseSizeTopRight:展開右上方MouseSizeBottomLeft:展開左下角MouseSizeBottomRight:展開右下角MouseDrag:滑鼠拖動滑鼠拖動同樣也很簡單,不過卻稍不同於視窗的縮放展開,這裡舉出它的實現代碼:
public class MouseDrag : MouseAction{private int x, y;public MouseDrag(int hitX, int hitY){x = hitX;y = hitY;}public override void Action(int ScreenX, int ScreenY, System.Windows.Forms.Form form){form.Location = new Point(ScreenX - x, ScreenY - y);}}
接下來我們開始編寫發出事件的代碼,先定義幾個變數:private int LEFT = 5, RIGHT = 5, BOTTOM = 5, TOP = 5, TITLE_WIDTH = 45;//邊框和標題列的大小private int x = 0, y = 0;//儲存滑鼠的臨時座標private MouseAction mouse;//滑鼠的事件響應對象然後在Form的MouseDown事件中記錄下滑鼠的當前座標:x = e.X;y = e.Y;附:e為System.Windows.Forms.MouseEventArgs然後再根據滑鼠的座標定義出事件響應對象:
//滑鼠點擊左上邊框if((e.X <= LEFT + 10 && e.Y <= TOP) || (e.Y <= TOP + 10 && e.X <= LEFT)){mouse = new MouseSizeTopLeft(Location.X, Location.Y, Width, Height);return;}
當然有的事件也可以直接響應:
//滑鼠點擊系統關閉按紐if(e.X > Width - 20 && e.Y > 6 && e.X < Width - 20 + SysButton_Min.Width && e.Y < 6 + SysButton_Min.Height){Close();return;}
大部分的事件響應實際上是在MouseMove事件中完成的:
private void Form_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e){this.Parent.Cursor = CheckCursorType(e.X, e.Y);//改變滑鼠的指標形狀if(mouse != null){mouse.Action(Control.MousePosition.X, Control.MousePosition.Y, this);//執行時間響應//注意座標是Control.MousePosition這個靜態變數給出的,它的值為滑鼠在案頭上的全域座標}}
給出每個不同部位的滑鼠的指標形狀:
private Cursor CheckCursorType(int X, int Y){if(((X <= LEFT + 10 && Y <= TOP) || (Y <= TOP + 10 && X <= LEFT)) || ((X >= Width - RIGHT - 10 && Y >= Height - BOTTOM) || (Y >= Height - BOTTOM - 10 && X >= Width - RIGHT))){return Cursors.SizeNWSE;}else if(((Y <= TOP + 10 && X >= Width - RIGHT) || (Y <= TOP && X >= Width - RIGHT - 10)) || ((X <= LEFT && Y >= Height - BOTTOM - 10) || (Y >= Height - BOTTOM && X <= LEFT + 10))){return Cursors.SizeNESW;}else if(X >= Width - RIGHT || X <= LEFT){return Cursors.SizeWE;}else if(Y >= Height - BOTTOM || Y <= TOP){return Cursors.SizeNS;}else{return Cursors.Arrow;}}
最後在MouseUp事件中將mouse變數釋放掉:
private void Form_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e){mouse = null;}
為了更加逼真,還可以加上標題列的雙擊最大化或者還原的事件:
private void Form_DoubleClick(object sender, System.EventArgs e){if(y > TOP && y < TITLE_WIDTH){if(WindowState == FormWindowState.Normal){WindowState = FormWindowState.Maximized;SysButton = SysButton_Restore;Invalidate();}else if(WindowState == FormWindowState.Maximized){WindowState = FormWindowState.Normal;SysButton = SysButton_Max;Invalidate();}}}