C#建立不規則窗體和控制元件

lovehappystudy發表於2008-01-07
C#建立不規則窗體和控制元件
作者:貝塔樂隊的吉他手

在 以前版本的Visual Basic或Visual C++中,建立不規則窗體和控制元件是一件很複雜的事,不僅需要呼叫大量API函式而且工作量也不小。不過,現在在Visual C#下,情況就完全不同了。運用Windows Forms你就可以很輕易地建立出一個不規則的窗體以及窗體上的控制元件。一個具有不規則窗體和控制元件的應用程式肯定會更吸引廣大的使用者,微軟的Windows Media Player 7就顯示出這一點。作為程式設計師,您一定想在自己的程式中運用這點技術吧。

程式的窗體和控制元件都可以以非傳統的方式被建立。本文就向大家展示如何在應用程式中建立不規則窗體,以及如何在窗體上建立各式各樣的自定義形狀的控制元件。

注:建立不規則窗體和控制元件這個過程包含了大量的圖形程式設計工作,所以不同的計算機因記憶體和顯示卡的不同可能會導致最終的效果有所不同。因此,在釋出你的應用程式前,務必在各種不同型別的計算機上做好測試工作。

實現方法

首先,建立一個點陣圖檔案作為程式的窗體。點陣圖可以是任意形狀的,但是點陣圖檔案區域一定要足夠大,這樣才能包含窗體上的所有控制元件。然後,你可以通過設定一些屬性使該圖成為程式的窗體。

把程式中的標題欄去掉,否則整個介面將顯得很不協調。當然你去掉了標題欄也就去掉了它的最大化、最小化、關閉、移動窗體等功能。為了使程式仍然具有這些功能,我們需在程式中新增一些程式碼,這樣使用者就仍然可以像以前一樣和程式進行互動。

因此,你需要完成如下工作:

1.建立一個作為窗體的點陣圖檔案。

2.建立一個Windows應用程式,用上述點陣圖檔案作為程式的窗體同時去掉其標題欄。

3.新增原標題欄具有的功能所需的程式碼。

 

具體步驟

下面我就具體向大家介紹如何建立不規則窗體。

建立一個具有不規則形狀的點陣圖檔案

1.用任何畫圖程式就可以建立不規則形狀的點陣圖,你可以使用最容易也是最方便的畫圖程式。

2.用一種顏色畫出一個不規則的區域作為程式的窗體,並用另一種顏色畫出該點陣圖的背景。(你要使該不規則區域足夠大。)

3.儲存點陣圖檔案。

下面就是一個例子:

在VS.net中建立一個新的工程

首先,設定窗體的背景從而建立窗體形狀。

1.在窗體設計器中選中窗體使之獲得焦點。

2.在屬性對話方塊中進行如下設定:

● 將FormBorderStyle屬性設定為None。該屬性去掉了程式的標題欄,同時也除去了標題欄的功能,不過我在後面還會向大家介紹如何新增程式碼以恢復這些功能的。

● 將BackgroundImage屬性設定為你建立的點陣圖檔案。你不必在工程中新增該檔案,因為你一旦指定了該檔案,它就會自動被新增到工程中。

● 將TransparencyKey屬性設定為點陣圖檔案的背景顏色值(在本例中是藍色)。該屬性使得點陣圖的背景即上圖中的藍色部分不可見,從而窗體就呈現出一個不規則的橢圓形。
 

注 意:如果監視器的顏色深度設定大於 24 位,則不管 TransparencyKey 屬性是如何設定的,窗體的非透明部分都會產生顯示問題。若要避免出現這種問題,請確保“顯示”控制皮膚中的監視器顏色深度的設定小於 24 位。當開發具有這種透明功能的應用程式時,請牢記應使您的使用者意識到此問題。


3. 儲存工程。按Ctrl+F5可以執行此程式。(注:因為沒有標題欄,所以你可以通過Alt+F4來關閉程式)

將FormBorderStyle屬性設定為None後,程式的標題欄就被去掉了。這樣,為了獲得原來標題欄的功能,我們必須手動新增程式碼。下面我就向大家介紹如何新增程式碼實現關閉功能以及移動窗體的功能。

實現窗體的關閉及移動

1.往窗體上拖放一個按鈕控制元件。

2.在屬性對話方塊中,將該控制元件的Text屬性設定為“關閉”。

3.雙擊按鈕新增一個Click事件處理函式。

4.在程式碼編輯器中新增如下程式碼:

private void button1_Click(object sender, System.EventArgs e)
{
this.Close();
}

5. 接下來就是實現窗體的移動功能。新增以下程式碼來建立一個Point物件,該物件(作為一個變數)決定在什麼情況下移動窗體。

private Point mouse_offset;

6. 建立窗體的MouseDown事件的事件處理函式。為該事件新增程式碼後,使用者就可以在任何位置移動窗體了。程式碼如下:

private void Form1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
mouse_offset = new Point(-e.X, -e.Y);
}

7. 建立窗體的MouseMove事件的事件處理函式。當滑鼠左鍵被按下,同時滑鼠被移動時,窗體的Location屬性就被設定為新的位置了,這樣窗體就被使用者拖動了。

private void Form1_MouseMove(object sender, 
System.Windows.Forms.MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
Point mousePos = Control.MousePosition;
mousePos.Offset(mouse_offset.X, mouse_offset.Y);
Location = mousePos;
}
}

8. 儲存工程。按Ctrl+F5可以執行該程式。現在程式的介面還是和以前的一樣,不過你可以用滑鼠移動窗體並通過按按鈕來關閉窗體了。

建立自定義形狀的控制元件

現在,我們已經建立了一個不規則的窗體,並實現了一些基本的移動窗體、關閉窗體的功能。然而,窗體上的按鈕控制元件還是老一套,那麼方方正正,使得整個介面不美觀。接下來我就向大家介紹如何建立自定義形狀的控制元件。

前 面我們建立不規則窗體的時候用到了TransparencyKey屬性,但是控制元件是沒有該屬性的,所以我們得找其他的方法來實現控制元件的不規則形狀了。在窗 體上畫一個自定義形狀的控制元件時,你需要精確的告知窗體在什麼位置以及如何畫該控制元件。在.Net Framework中有相應的類和方法來幫你實現這些,所以你不必擔心具體實現。

.Net Framework中的類提供給控制元件一個指示說明,該指示說明能確定控制元件被畫的形狀。通過不同的指示說明,你就可以按你想要的方法來畫控制元件了。該指示說明 利用了GraphicsPath這個類,這個類代表了一系列用來畫圖的直線和曲線。首先,你得指定一個GraphicsPath類的物件並告知它你要畫什 麼圖形。然後,你將控制元件的Region屬性設定為上述GraphicsPath類的物件。這樣,你就可以建立任何自定義形狀的控制元件了。

步驟如下:

●建立一個GraphicsPath類的例項物件。

●指定好該物件的各項細節(如大小、形狀等等)。

●將控制元件的Region屬性設定為上面建立的GraphicsPath類的例項物件。

建立一個像文字的按鈕控制元件:

1.拖放一個按鈕控制元件到窗體上。

2.在屬性對話方塊中進行如下設定:

●將Name屬性設定為CustomButton。

●將BackColor屬性設定為一個和窗體背景顏色不同的顏色值。

●將其Text屬性設定為空字串。

3.新增窗體的Paint事件的事件處理函式。

4. 新增以下程式碼,用GraphicsPath類的例項物件來畫控制元件。下面的程式碼以一串字串的形式畫該按鈕控制元件,同時,程式還設定了字串的字型、大小、風 格等屬性。字串被賦給GraphicsPath類的例項物件。然後,該例項物件就被設定為按鈕控制元件的Region屬性。這樣一個自定義形狀的控制元件就完成 了。

private void CustomButton_Paint(object sender, 
System.Windows.Forms.PaintEventArgs e)
{
//初始化一個GraphicsPath類的物件
System.Drawing.Drawing2D.GraphicsPath myGraphicsPath = new
System.Drawing.Drawing2D.GraphicsPath();
//確定一個字串,該字串就是控制元件的形狀
string stringText = "Click Me!";
//確定字串的字型
FontFamily family = new FontFamily("Arial");
//確定字串的風格
int fontStyle = (int)FontStyle.Bold;
//確定字串的高度
int emSize = 35;
//確定字串的起始位置,它是從控制元件開始計算而非窗體
PointF origin = new PointF(0, 0);
//一個StringFormat物件來確定字串的字間距以及對齊方式
StringFormat format = new StringFormat(StringFormat.GenericDefault);
//用AddString方法建立字串
myGraphicsPath.AddString(stringText, family, fontStyle, emSize, origin, format);
//將控制元件的Region屬性設定為上面建立的GraphicsPath物件
CustomButton.Region = new Region(myGraphicsPath);
}

5. 建立按鈕的Click事件的事件處理函式。新增該處理函式來改變控制元件的背景顏色,從而證實控制元件原來的那些功能沒有被削減。

private void CustomButton_Click(object sender, System.EventArgs e)
{
CustomButton.BackColor = Color.BlanchedAlmond;
}

6.儲存工程並執行。

 

進一步優化效果

以 上我們運用了GraphicsPath類的例項物件來建立了自定義形狀的一個按鈕控制元件。不過我們用的是文字字串形式的一個形狀,是否可以用三角形或是圓 形等形狀呢?答案是肯定的。.Net Framework能為我們提供一些預先定義好了的形狀以供我們在程式中使用。通過運用這些,你可以創造出幾乎任意形狀的控制元件,你還可以把它們結合起來使 用以發揮更大的功能。

下面的例項就運用了四個橢圓,當它們被運用到控制元件上後,看起來就像人的眼睛,很有意思吧。

private void button1_Paint(object sender, 
System.Windows.Forms.PaintEventArgs e)
{
System.Drawing.Drawing2D.GraphicsPath myGraphicsPath = new
System.Drawing.Drawing2D.GraphicsPath();
myGraphicsPath.AddEllipse(new Rectangle(0, 0, 125, 125));
myGraphicsPath.AddEllipse(new Rectangle(75, 75, 20, 20));
myGraphicsPath.AddEllipse(new Rectangle(120, 0, 125, 125));
myGraphicsPath.AddEllipse(new Rectangle(145, 75, 20, 20));
//改變按鈕的背景顏色使之能被容易辨認
button1.BackColor = Color.Chartreuse;
button1.Size = new System.Drawing.Size(256, 256);
button1.Region = new Region(myGraphicsPath);

}

最 後,你還得搞清楚窗體類是從System.Windows.Forms.Control類繼承而來的。也就是說,由窗體設計器提供給你的窗體最終還是一個 控制元件。因此,你能用點陣圖檔案建立一個不規則的窗體,你還能用GraphicsPath類物件來像建立自定義形狀的控制元件那樣建立不規則的窗體。有興趣的讀者 不妨用此方法一試效果。



一個更好的例子

下面我給大家介紹一個更好的例子,該例項的介面就和微軟的Windows Media Player 7的介面差不多。不過在這個例子中,我只是介紹瞭如何製作出介面,並沒有實現媒體播放的功能,要實現那些功能,還需要您自行完善。該例項執行的介面如下:

1.將某種顏色設定為窗體的背景顏色,然後將窗體的TransparenceKey屬性設定為那種顏色,同時將窗體的FormBorderStyle屬性設定為None。

2.過載Form_Paint()函式:

protected override void  OnPaint(PaintEventArgs e)
或是this.Paint += new System.Windows.Form.PaintEventHandler(Form_Paint)。

3.程式的主體部分的函式如下:

private void Form_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
Rectangle mainRect = new Rectangle(0, 0, 695, 278);
Region mainRegion = new Region(mainRect);
e.Graphics.SetClip(mainRegion, CombineMode.Replace);
Point point1 = new Point(0, 32);
Point point2 = new Point(9, 20);
Point point3 = new Point(21, 13);
Point point4 = new Point(34, 9);
// 建立一個以點為元素的陣列
Point[] curvePoints = { point1, point2, point3, point4 };
// 建立一個GraphicsPath物件並新增一條曲線
GraphicsPath myPath = new GraphicsPath();
myPath.AddCurve(curvePoints, 0, 3, 0.8f);
myPath.AddLine(36, 9, 378, 9);
point1.X=378;point1.Y=9;
point2.X=387;point2.Y=5;
point3.X=394;point3.Y=0;
Point[] curvePoints2 = { point1, point2, point3 };
myPath.AddCurve(curvePoints2, 0, 2, 0.8f);
myPath.AddLine(394, 0, 0, 0);
Region ExcludeRegion3 = new Region(myPath);
e.Graphics.ExcludeClip(ExcludeRegion3);

GraphicsPath myPath3 = new GraphicsPath();
point1.X=0;point1.Y=180;
point2.X=19;point2.Y=198;
point3.X=62;point3.Y=204;
point4.X=83;point4.Y=221;
Point point5 = new Point(93, 248);
Point point6 = new Point(102, 267);
Point point7 = new Point(125, 278);
Point[] curvePoints3 = { point1, point2, point3, point4,
point5, point6, point7 };
myPath3.AddCurve(curvePoints3, 0, 6, 0.8f);
myPath3.AddLine(125, 278, 90, 300);
myPath3.AddLine(90, 300, 0, 300);
Region ExcludeRegion2 = new Region(myPath3);
e.Graphics.ExcludeClip(ExcludeRegion2);
point1.X=454;point1.Y=0;
point2.X=470;point2.Y=12;
point3.X=481;point3.Y=34;
Point[] curvePoints4 = { point1, point2, point3 };
GraphicsPath myPath2 = new GraphicsPath();
myPath2.AddCurve(curvePoints4, 0, 2, 0.8f);
myPath2.AddLine(481, 30, 481, 76);
myPath2.AddLine(481, 76, 495, 76);
myPath2.AddLine(495, 76, 495, 0);
Region ExcludeRegion4 = new Region(myPath2);
e.Graphics.ExcludeClip(ExcludeRegion4);
GraphicsPath myPath5 = new GraphicsPath();
point1.X=481;point1.Y=76;
point2.X=494;point2.Y=115;
point3.X=481;point3.Y=158;
Point[] curvePoints5 = { point1, point2, point3 };
myPath5.AddCurve(curvePoints5, 0, 2, 0.8f);
myPath5.AddLine(481, 158, 481, 279);
myPath5.AddLine(481, 255, 495, 279);
myPath5.AddLine(495, 279, 495, 0);
Region ExcludeRegion6 = new Region(myPath5);
e.Graphics.ExcludeClip(ExcludeRegion6);

point1.X=480;point1.Y=250;
point2.X=469;point2.Y=264;
point3.X=446;point3.Y=278;
Point[] curvePoints6 = { point1, point2, point3 };
GraphicsPath myPath4 = new GraphicsPath();
myPath4.AddCurve(curvePoints6, 0, 2, 0.8f);
myPath4.AddLine(450, 277, 495, 279);
Region ExcludeRegion5 = new Region(myPath4);
e.Graphics.ExcludeClip(ExcludeRegion5);

e.Graphics.DrawImage(img, 0, 0, 695,278);

// 重設剪下好的區域
e.Graphics.ResetClip();
}

該函式運用Region類和GraphicsPath類實現了程式的主介面,函式中具體的資料可能會因圖片大小等原因而有所不同,至於其他的訊息響應函式讀者可以參看附帶的原始碼檔案(Source.rar)。

 

總結

通 過本文,我們不難發現在Visual C#下建立不規則窗體以及自定義形狀的控制元件是件相當容易的事。我們僅僅需要一幅不規則的影像就可以完成不規則窗體的建立;我們也只要用到 GraphicsPath類物件就可輕易地建立出自定義形狀的控制元件。相信讀者在讀完本文後,對在.Net下建立豐富的使用者介面有了基本的瞭解。希望各位讀 者因此能建立出更豐富的、更賦時代特色的使用者介面。  

 

相關文章