系統:WindowsXP P
環境:Visual Studio.NET 2003 語言:C# 源碼下載
在開始我們的講解之前先看一下我們位圖Button控件的效果(很漂亮吧!)如果你對這個控件感興趣的話,就跟著我們的腳步學習如何做出這樣的一個控件吧!
簡介:
創建自定義位圖控件的目的是允許在每一種按鈕狀態下呈現不同的位圖,這些狀態包括:disabled, normal, mouse over,還有button pressed;除了按鈕的圖像,讓我們的按鈕飽含文本,并且根據按鈕圖片控制文本的對齊方式也很重要。按鈕采用XP樣式,還包含了我們定制的一些特性。
代碼使用:
程序的源碼可以分為3大部分:data(數據), rendering(表現), and events(事件)
Data:存儲狀態和設置屬性的私有變量,下表中有每一個屬性的描述。
Rendering:按鈕的呈現是靠幾個方法來實現的,OnPaint方法是調用其它繪制方法的一個驅動性質的方法(意思就是靠我們的OnPaint方法,調用自定義的繪制方法),用來呈現我們的Button控件
Events:事件處理操作按鈕的狀態這些事件有:OnMouseDown, OnMouseUp, OnMouseLeave, OnMouseMove, OnEnabledChanged, OnLostFocus.
Data:
首先讓我們研究一下這些屬性
|
BITMAP BUTTON PROPERTIES | ||||||||||||||||||||||||||||||||||||||||||||
|
所有的這些屬性都被加到屬性標簽頁了,下面是個截圖:

Rendering:
按鈕控件的呈現工作是OnPaint方法實現的,它輪流調用幾個方法呈現Button我們設置的狀態。
CreateRegion: 創建按鈕的圓角邊框paint_Background: 繪制呈現按鈕背景paint_Text: 繪制呈現按鈕文本和文本陰影paint_Border: 繪制 1像素的按鈕邊框paint_InnerBorder: 繪制 2像素的按鈕內邊框paint_FocusBorder: 繪制 1像素的按鈕虛線焦點邊框
/// This method paints the button in its entirety.
/// </summary>
/// <param name="e">paint arguments use to paint the button</param>
protected override void OnPaint(PaintEventArgs e){
CreateRegion(0);
paint_Background(e);
paint_Text(e);
paint_Image(e);
paint_Border(e);
paint_InnerBorder(e);
paint_FocusBorder(e);
}
繪制背景應該是很有趣的:
Painting the background can be of some interest. The approach that was taken allows for a gradient background interpolation between multiple colors (meaning more then 2 colors). First, a blend object needs to be initialized with an array of colors, and the position of interpolation. Next, the gradient brush can be created as usual. The Final step involves linking the blend object to the brush. This is accomplished by setting the InterpolationColors property of a brush.
下面是復合顏色的代碼示例:
System.Drawing.Color.White,
System.Drawing.Color.Yellow,
System.Drawing.Color.Blue,
System.Drawing.Color.Green,
System.Drawing.Color.Red,
System.Drawing.Color.Black};
float[] PositionArray = new float[]{0.0f,.15f,.40f,.65f,.80f,1.0f};
//
// create blend variable for the interpolate the colors
//
System.Drawing.Drawing2D.ColorBlend blend
= new System.Drawing.Drawing2D.ColorBlend();
blend.Colors = ColorArray;
blend.Positions = PositionArray;
//
// create vertical gradient brush
//
System.Drawing.Drawing2D.LinearGradientBrush brush
= new System.Drawing.Drawing2D.LinearGradientBrush(rect,
this.BackColor,Blend(this.BackColor,this.BackColor,10),
System.Drawing.Drawing2D.LinearGradientMode.Vertical);
brush.InterpolationColors = blend;
//
// fill the rectangle
//
g.FillRectangle(brush, rect);
//
// release resources
//
brush.Dispose();
我使用了System.Drawing.DrawString()方法繪制按鈕文本,在何處使用這個方法呢,為了區別paint_Text()方法,我們將代碼放置在Helper函數部分(看源代碼大家就一目了然是什么意思了)還有一處大家一定會感興趣,就是文本的陰影效果是如何實現的呢?我們看看接下來的代碼:
//
// paint text shadow
//
if(TextDropShadow){
System.Drawing.Brush TransparentBrush0
= new System.Drawing.SolidBrush( System.Drawing.Color.FromArgb(50,
System.Drawing.Color.Black ) ) ;
System.Drawing.Brush TransparentBrush1
= new System.Drawing.SolidBrush( System.Drawing.Color.FromArgb(20,
System.Drawing.Color.Black ) ) ;
e.Graphics.DrawString(this.Text,this.Font,
TransparentBrush0,pt.X,pt.Y+1);
e.Graphics.DrawString(this.Text,this.Font,
TransparentBrush0,pt.X+1,pt.Y);
e.Graphics.DrawString(this.Text,this.Font,
TransparentBrush1,pt.X+1,pt.Y+1);
e.Graphics.DrawString(this.Text,this.Font,
TransparentBrush1,pt.X,pt.Y+2);
e.Graphics.DrawString(this.Text,this.Font,
TransparentBrush1,pt.X+2,pt.Y);
TransparentBrush0.Dispose();
TransparentBrush1.Dispose();
}
相信不用我多解釋,大家就知道實現的原理是什么了吧,好了我們繼續。
繪制圖像更是一個直接的過程,但是在使用下面的方法的時候,我遇到了一些問題,當他繪制一個有我們資源編輯器產生的為圖的時候確實沒有什么問題,但是當我們繪制一個由第三方程序產生的24位位圖的時候,失敗了。它必須使用另一種DrawImage方法,現在我們知道接下來需要怎么修改我們的方法了。
// FAILED
g.DrawImage(image,rect.Left,rect.Top)
// WORKAROUND
g.DrawImage(image,rect, 0, 0 ,image.Width,image.Height, GraphicsUnit.Pixel); 繪制邊框的代碼當然也不難,像前面一樣創建一個梯度畫刷對象,然后繪制的時候把這個對象當作參數傳遞進去,看下面的代碼,其實很簡單。
//.
//
// create brush and pens
//
System.Drawing.Drawing2D.LinearGradientBrush brush
= new System.Drawing.Drawing2D.LinearGradientBrush(rect,
this.BackColor,Blend(this.BackColor,this.BackColor,10),
System.Drawing.Drawing2D.LinearGradientMode.Vertical);
brush.InterpolationColors = blend;
System.Drawing.Pen pen0 = new System.Drawing.Pen(brush,1);
//
// draw line 0
//
g.DrawLine(pen0 , point_0,point_1);
//.
Events:
數據成員、一些方法都簡單的陳述了,接下來看看我們的事件處理機制。按鈕的一個大的方面就是事件和捕獲與處理。我們走了個大的捷徑,那就是重載按鈕事件的方法,這些方法通過按鈕屬性直接修改按鈕的狀態。一旦狀態改變,他們就會讓控件無效,然后刷新機制重新繪制我們的Button。下面是事件方法列表和簡單說明。
|
Event Methods |
Button state |
|
|
Set BtnState to Pushed and Capturing mouse to true 設置Button狀態為按下,并且將捕獲鼠標屬性設為true |
|
|
Set BtnState to 設置Button狀態為普通,并且將捕獲鼠標屬性設為false |
|
|
Set BtnState to normal if we CapturingMouse = true 設置Button狀態為普通,如果捕獲鼠標狀態屬性為true |
|
|
If CapturingMouse = true and mouse coordinates are within button region, set BtnState to Pushed, otherwise set BtnState to 簡單的說就是根據十分捕獲鼠標設置Button狀態 |
|
|
The button either became enabled or disabled. If button became enabled, set BtnState to Button是否可用改變時的方法 |
|
|
Set btnState to 失去焦點,把Button狀態屬性變為普通的方法 |
下面的代碼是一個簡單的事件處理。這里值得注意的是Capture屬性。
/// Mouse Down Event:
/// set BtnState to Pushed and Capturing mouse to true
/// </summary>
/// <param name="e"></param>
protected override void OnMouseDown(MouseEventArgs e){
base.OnMouseDown (e);
this.Capture = true;
this.CapturingMouse = true;
btnState = BtnState.Pushed;
this.Invalidate();
}
按鈕是否獲取焦點問題,大家知道如果這個焦點一直被設置在Button上,那其它當實際焦點改變時,我們的事件處理機制就不能按照我們的要求正常工作了。給出原文供大家參考:
The below code block shows an example of the event code. Events generally are composed of little code. One tidbit of information I should cover is the Capture property of the control. By setting this to true, the button does not lose input focus when the pointer is outside the button region. This is important because if the mouse button is held down and the user moves the mouse pointer in and out of the button region, the state of the button needs to change accordingly. If the Capture property is not set, the control will stop capturing input events when the pointer leaves the button region.
由此展開的課題:
這只是程序最原始的一個版本,所有代碼都在一個源文件中,以后我們可以擴展這個程序,比如預先定義好好看的樣式,從一個XML文檔加載我們主題信息,希望大家能好好利用這個程序,也希望這個程序真的能給大家帶來幫助。
這里有一些額外的說明:
CodeProject這篇文章的源碼是很不錯的,值得大家研究研究,代碼其實很簡單,有這樣的思路才是重點,這樣的控件我們也完全能開發出來。
大家可以好好研究 HelperMethods,其實這些才是該控件比較精華的代碼。
但是我也發現了一個問題,該控件對設計期的支持不夠完善,具體說來就是你改變了某一個屬性,它并沒有立即在設計期反映出來,我研究了一下,發現它的屬性設置完成了都沒有這句話,前面我翻譯的一篇文章已經強調了,要想讓你的控件及時反映出屬性的變化,做出相應的反映this.Invalidate()這句話是重點,希望大家不能忽視,我已將修改好的源代碼打包,還有一個Demo程序,這樣就比較完美了。
怎么把這個不錯的控件添加到你的ToolBox中,相信不用我再教一次了,定位到這個dll就行了;還有如何定制自己的空間圖標等等小的方面,參看我的這篇文章吧:http://www.cnblogs.com/jht/archive/

