用C#寫水晶按鈕

       UI(User Interface)編程在整個項目開發過程中是個頗爲重要的環節,任何好的解決方案若沒有良好的用戶界面呈現給最終用戶,那麼就算包含了最先進的技術也不能算是好程序。UI編程體現在兩個方面,一是設計精美的用戶界面,再有就是符合大多數用戶習慣和易於使用的操作流程,而製作出精美的、絢麗多彩的用戶界面是博得最終用戶喜愛的第一步。我們就以製作一個水晶樣式的三維按鈕爲例來豐富.Net界面素材庫,爲Windows 窗體程序增加亮點。

一、技術要點 不可否認的是,Windows編程已經進入.Net時代,雖然現在的編程平臺仍然是多家並存,但是微軟的.Net框架類庫已經全面佔據了主流地位。.Net框架爲我們提供了非常豐富的類、函數和方法,從桌面到Web它可以觸及到編程的任何領域,以至於可以完全放棄Win32 API 的手工調用,因爲.Net框架已經爲我們準備好了一切。尤其是GDI+的發佈,Win32程序員都應該非常清楚,在VC6和Delphi5、6、7下要想繪製不規則圖形、設計獨特樣式的窗口控件是件多麼不輕鬆的事情,我們需要返回當前設備指針,然後座標映射,再調用標準的Win32 GDI函數來進行繪製操作,最後還得記着釋放設備指針等一系列內存清理操作,現在有了.Net類庫,我們只需在控件的OnPaint事件中通過e.Graphics返回Graphics對象,然後創建一個自定義的刷子對象Brush來填充Graphics表面,再創建一個Region對象設計好我們需要的控件輪廓,並將其賦給控件的Region屬性,在這個過程中可以使用GDI+爲我們提供的豐富的類和方法來設計美輪美奐的控件外觀,這樣一個全新的控件就創建完成了。 我們的示例控件是一個水晶樣式的三維按鈕,設計這樣一個按鈕控件其實並不複雜,我們只需要捕獲OnPaint事件,並在事件中按照不同的按鈕狀態譬如:鼠標進入、鼠標懸停、鼠標單擊以及鼠標離開等重新繪製按鈕的外觀。按鈕的三維樣式實際上就是三個矩形區域的疊加,即陰影位於最底層、按鈕本身以及最頂層的按鈕頭部白色泡泡部分,這三部分有效的疊加就製作成了一個惟妙惟肖的水晶樣式的按鈕了,如下圖所示: 單擊顯示全圖,Ctrl+滾輪縮放圖片 在這個示例中用到的比較關鍵的類是GraphicsPath、LinearGradientBrush和PathGradientBrush。我們已經知道,對於複雜的圖形繪製操作都會用到GraphicsPath對象,GraphicsPath爲我們提供了一個非常方便的解決方案,它由一系列直線和曲線組成,通過創建複雜的閉合路徑,我們可以輕鬆創建任意不規則圖形;LinearGradientBrush和PathGradientBrush對象是我們成功創建該示例程序的關鍵類,它們都繼承於基類Brush,Brush對象定義用於填充圖形形狀的內部的對象。LinearGradientBrush按照起始和結束座標以及漸變的起始和結束顏色來創建一個該類的實例,該類支持雙色漸變和自定義多色漸變,所有漸變都是沿由矩形的寬度或兩個點指定的直線定義的,默認情況下,雙色漸變是沿指定直線從起始色到結束色的均勻水平線性混合,所以,根據這個特性我們利用該類來繪製按鈕本身和按鈕頂部的白色漸變部分,部分代碼如下:

 
//創建按鈕本身的圖形
Rectangle rc = new Rectangle(
    btnOffset,
    btnOffset,
    this.ClientSize.Width - 8 - btnOffset,
    this.ClientSize.Height - 8 - btnOffset);

GraphicsPath path1 = this.GetGraphicsPath(rc, 20);

LinearGradientBrush br1 = new LinearGradientBrush(
    new Point(0, 0),
    new Point(0, rc.Height + 6),

  Color.Blue, Color.White);
 
//創建按鈕頂部的白色漸變
Rectangle rc3 = rc;
rc3.Inflate(-5, -5); //
rc3.Height = 15;
GraphicsPath path3 = GetGraphicsPath(rc3, 20);
LinearGradientBrush br3 = new LinearGradientBrush(
    rc3,
    Color.FromArgb(255, Color.White),
    Color.FromArgb(0, Color.White),
    LinearGradientMode.Vertical);
        PathGradientBrush類通過漸變填充 GraphicsPath對象的內部,它可以從路徑的中點到路徑的外邊界邊緣的進行平滑的彩色漸變,我們利用該類來創建按鈕的陰影部分,CenterColor屬性用來設定路徑漸變的中心處的顏色即黑色,SurroundColors數組用來設定路徑中點相對應的顏色的數組,部分代碼如下:
Rectangle rc2 = rc;
rc2.Offset(shadowOffset, shadowOffset);
GraphicsPath path2 = this.GetGraphicsPath(rc2, 20);
PathGradientBrush br2 = new PathGradientBrush(path2);
br2.CenterColor = Color.Black;
br2.SurroundColors = new Color[] { SystemColors.ButtonFace };
//爲了更逼真,我們將漸變結束顏色設定爲窗體前景顏色,可以根據窗口的前景顏色適當調整

創建完該對象的實例後,陰影圖形的內部被填充爲一個放射狀的漸變,也許讀者會問,爲什麼得用PathGradientBrush填充圖形內部,而不用LinearGradientBrush呢?其實原因很簡單,就是爲了保證按鈕下方和右方的陰影漸變顏色和程度是一致的,否則若還使用線性漸變LinearGradientBrush,結果就是這樣如下圖:

 單擊顯示全圖,Ctrl+滾輪縮放圖片

        三個Brush對象的實例br1、br2和br3創建完成後,我們需要按照陰影、按鈕本身,最後是按鈕頂部的白色漸變部分這樣的順序來繪製它們。調用Graphics對象的FillPath方法分別使用三種不同的Brush對象來填充 GraphicsPath 的內部,這樣具備水晶樣式的三維按鈕就基本創建完成了。最後,我們再利用.Net二級緩存繪圖技術將按鈕以及按鈕上所顯示的文字繪製到屏幕上。

一、實現 我們利用C# 來製作水晶按鈕控件

        首先啓動Visual Studio 2005,新建空白解決方案,我們取名爲:TestCrystalButton,然後在項目導航欄上右擊鼠標添加新項目,爲此解決方案添加新的C# Windows 控件庫,取名爲MyControls。IDE會創建一個繼承於UserControl名爲UserControl1的類,修改代碼使其繼承自Button,並將原文件中所有引用UserControl1名稱的地方都更改爲CrystalButton,在項目導航欄中把UserControl1.cs更名爲CrystalButton.cs,如下圖:

 
namespace MyControls
{
    public partial class CrystalButton : Button
    {
        public CrystalButton()
        {
            InitializeComponent();
        }
    }
}
然後將InitializeComponent()方法中的
 
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
語句註釋掉,因爲從Button控件不可能有自動縮放功能,它必須依賴於其父控件。在源文件頭部添加對System.Drawing.Imaging和System.Drawing.Drawing2D程序集的引用。首先需要創建一個枚舉類型MouseActionType,當按鈕需要繪製時會根據當前鼠標的位置進行不同狀態的繪製,如下圖:
private enum MouseActionType
{
    None,
    Hover,
    Click
}
控件會捕獲OnMouseDown、OnMouseUp、OnMouseHover、OnMouseEnter和OnMouseLeave事件並將mouseAction變量設置爲不同的枚舉值,以便在OnPaint事件發生時根據mouseAction變量進行不同狀態的繪製。 控件的OnPaint事件是整個項目中最重要的代碼段,它按照按鈕組成的三部分分別進行繪製,最後將按鈕的文本也同樣繪製到按鈕表面,所有操作都使用了二級緩存繪圖技術,代碼如下:
 
protected override void OnPaint(PaintEventArgs e)
{
    Graphics g = e.Graphics;
    g.Clear(SystemColors.ButtonFace);
    Color clr = this.BackColor;
    int shadowOffset = 8;
    int btnOffset = 0;
    switch (mouseAction)
    {
    case MouseActionType.Click:
        shadowOffset = 4;
        clr = Color.LightGray;
        btnOffset = 2;
        break;
    case MouseActionType.Hover:
        clr = Color.LightGray;
        break;
    }
    g.SmoothingMode = SmoothingMode.AntiAlias;
    // 創建按鈕本身的圖形
    Rectangle rc = new Rectangle(btnOffset, btnOffset, this.ClientSize.Width - 8 - btnOffset,
        this.ClientSize.Height - 8 - btnOffset);
    GraphicsPath path1 = this.GetGraphicsPath(rc, 20);
    LinearGradientBrush br1 = new LinearGradientBrush(new Point(0, 0),
        new Point(0, rc.Height + 6), clr, Color.White);
    // 創建按鈕陰影
    Rectangle rc2 = rc;
    rc2.Offset(shadowOffset, shadowOffset);
    GraphicsPath path2 = this.GetGraphicsPath(rc2, 20);
    PathGradientBrush br2 = new PathGradientBrush(path2);
    br2.CenterColor = Color.Black;
    br2.SurroundColors = new Color[] { SystemColors.ButtonFace };
    //爲了更逼真,我們將漸變結束顏色設定爲窗體前景顏色,可以根據窗口的前景顏色適當調整
    //創建按鈕頂部白色漸變
    Rectangle rc3 = rc;
    rc3.Inflate(-5, -5);
    rc3.Height = 15;
    GraphicsPath path3 = GetGraphicsPath(rc3, 20);
    LinearGradientBrush br3 = new LinearGradientBrush(rc3, Color.FromArgb(255, Color.White),
        Color.FromArgb(0, Color.White), LinearGradientMode.Vertical);
    // 繪製圖形
    g.FillPath(br2, path2);    //繪製陰影
    g.FillPath(br1, path1); //繪製按鈕
    g.FillPath(br3, path3); //繪製頂部白色泡泡
    //設定內存位圖對象,進行二級緩存繪圖操作
    buttonBitmapRectangle = new Rectangle(rc.Location, rc.Size);
    buttonBitmap = new Bitmap(buttonBitmapRectangle.Width, buttonBitmapRectangle.Height);
    Graphics g_bmp = Graphics.FromImage(buttonBitmap);
    g_bmp.SmoothingMode = SmoothingMode.AntiAlias;
    g_bmp.FillPath(br1, path1);
    g_bmp.FillPath(br3, path3);
    //將region賦值給button
    Region rgn = new Region(path1);
    rgn.Union(path2);
    this.Region = rgn;
    // 繪製按鈕的文本
    GraphicsPath path4 = new GraphicsPath();
    RectangleF path1bounds = path1.GetBounds();
    Rectangle rcText = new Rectangle((int) path1bounds.X + btnOffset,
       (int) path1bounds.Y + btnOffset, (int) path1bounds.Width, (int) path1bounds.Height);
    StringFormat strformat = new StringFormat();
    strformat.Alignment = StringAlignment.Center;
    strformat.LineAlignment = StringAlignment.Center;
    path4.AddString(this.Text, this.Font.FontFamily, (int) this.Font.Style,
       this.Font.Size, rcText, strformat);
    Pen txtPen = new Pen(this.ForeColor, 1);
    g.DrawPath(txtPen, path4);
    g_bmp.DrawPath(txtPen, path4);
}

       控件製作完畢,編譯該解決方案及控件項目生成CrystalButton.dll,您可以在當前解決方案中添加使用該控件的Windows窗體程序項目,也可以在任意解決方案中添加對該控件的引用,簡單到只需從工具箱中把CrystalButton拖拽到窗體上。

一、總結 .Net 框架的GDI+增強型的圖形圖像繪製技術爲我們提供了強大的圖形圖像繪製能力,它所包含的豐富的類和方法爲我們實現完全自定義控件外觀提供了有力保證,不但簡化了程序代碼還大大節省了編程時間。 該示例控件和程序在Visual Studio 2005 Team Suite、Windows XP SP2下編譯運行通過。

 

轉自:http://www.chenjiliang.com/Article/View.aspx?ArticleID=4439&TypeID=88

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