上篇說了如何創建自定義控件,接下來說說如何自定義屬性,如何繪制控件,以進度控制條為例,先上效果圖
這里只實現了簡單的進度控制功能,該控件由三部分組成,總長度(底部白色矩形),已加載長度(灰色矩形),控制塊(黑色矩形),百分比
1、首先創建一個類庫,命名為MySlider, 繼承自 Control 類
public class MySlider : Control
{
public MySlider()
{
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.UserPaint, true);
}
}
在構造函數中設置控件Style,ControlStyles枚舉可以參考
http://technet.microsoft.com/zh-cn/subscriptions/system.windows.forms.controlstyles.aspx
2、接下來,我們需要定義以下變量,並對一些變量進行一些默認設置
Rectangle foreRect;
Rectangle backRect;
Rectangle setRect;
Color backgroundColor = Color.White;
Color foregroundColor = Color.Gray;
Color setRectColor = Color.Black;
Color fontColor = Color.Black;
int maximum = 100;
int minimum = 0;
double myValue = 0;
bool showPercent;
float fontSize = 9;
FontFamily myFontFamily = new FontFamily("宋體");
3、再來,設置屬性值

[Category("General"), Description("Show Percent Tag"), Browsable(true)]
public bool ShowPercentTag
{
get { return showPercent; }
set
{
showPercent = value;
Invalidate();
}
}
[Category("General"), Description("Control's Maximum"), Browsable(true)]
public int Maximum
{
get { return maximum; }
set
{
maximum = value;
Invalidate();
}
}
[Category("General"), Description("Control's Minimum"), Browsable(true)]
public int Minimum
{
get { return minimum; }
set
{
minimum = value;
Invalidate();
}
}
[Category("General"), Description("Control's FontSize"), Browsable(true)]
public float FontSize
{
get { return fontSize; }
set
{
this.fontSize = value;
Invalidate();
}
}
[Category("General"), Description("Control's FontFamily"), Browsable(true)]
public FontFamily MyFontFamily
{
get { return myFontFamily; }
set
{
this.myFontFamily = value;
Invalidate();
}
}
[Category("Color"), Browsable(true)]
public Color BackgroundColor
{
get { return backgroundColor; }
set
{
this.backgroundColor = value;
Invalidate();
}
}
[Category("Color"), Browsable(true)]
public Color ForegroundColor
{
get { return foregroundColor; }
set
{
this.foregroundColor = value;
Invalidate();
}
}
[Category("Color"), Browsable(true)]
public Color SetRectColor
{
get { return setRectColor; }
set
{
this.setRectColor = value;
Invalidate();
}
}
[Category("Color"), Browsable(true)]
public Color FontColor
{
get { return fontColor; }
set
{
this.fontColor = value;
Invalidate();
}
}
還有一個myValue沒有設置,后面會講到
4、接下來要確定控件的位置,我們根據 Width 和 Height 屬性來確定矩形的位置,由於Control 類也有這兩個屬性,我們在前面加上new覆蓋掉原有的屬性
[Category("General"), Description("Control's Width"), Browsable(true)]
public new int Width
{
get { return base.Width; }
set
{
base.Width = value;
foreRect.X = backRect.X = base.Width / 20;
backRect.Width = base.Width * 9 / 10;
foreRect.Width = (int)(myValue / maximum * backRect.Width);
setRect.X = (int)(myValue / maximum * (backRect.Width - backRect.Height) + foreRect.X);
Invalidate();
}
}
[Category("General"), Description("Control's Height"), Browsable(true)]
public new int Height
{
get { return base.Height; }
set
{
base.Height = value;
foreRect.Height = backRect.Height = setRect.Height = setRect.Width = base.Height / 3;
foreRect.Y = backRect.Y = setRect.Y = base.Height / 3;
Invalidate();
}
}
通過Width 和 Height 屬性,計算出三個矩形的位置和大小,接下來是Value屬性
5、在進度控制條中,這個Value屬性比較重要,我們經常需要相應這個Value值變化的事件,例如,播放音樂的時候,用戶拖動進度條改變Value時,需要使音樂播放到相應的位置
所以在這里我們先定義一個事件,當外部為該事件添加了響應函數時,事件就會生效,否則為OnValueChanged的值為null
protected EventHandler OnValueChanged;
public event EventHandler ValueChanged
{
add
{
if (OnValueChanged != null)
{
foreach (Delegate d in OnValueChanged.GetInvocationList())
{
if (object.ReferenceEquals(d, value)) { return; }
}
}
OnValueChanged = (EventHandler)Delegate.Combine(OnValueChanged, value);
}
remove
{
OnValueChanged = (EventHandler)Delegate.Remove(OnValueChanged, value);
}
}
6、接下來定義Value屬性
當Value值改變的時候,重新設置矩形的進度,控制塊的位置,並且重繪控件
[Category("General"), Description("Control's Value"), Browsable(true)]
public double Value
{
get { return myValue; }
set
{
if (myValue < Minimum)
throw new ArgumentException("小於最小值");
if (myValue > Maximum)
throw new ArgumentException("超過最大值");
myValue = value;
foreRect.Width = (int)(myValue / maximum * backRect.Width);
setRect.X = (int)(myValue / maximum * (backRect.Width - backRect.Height) + backRect.X);
if ((myValue - maximum) > 0)
{
foreRect.Width = backRect.Width;
setRect.X = backRect.Width - backRect.Height + backRect.X;
}
//如果添加了響應函數,則執行該函數
if (OnValueChanged != null)
{
OnValueChanged(this, EventArgs.Empty);
}
Invalidate();
}
}
這樣,屬性就算添加完了,有一個地方需要注意的,Value屬性內如果對進度條的值進行修改,使用myValue變量,而在其他地方,則用Value屬性
7、接下來是繪制控件,從過重載OnPaint方法對控件進行繪制
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
DrawRect(e.Graphics);
DrawText(e.Graphics);
}
private void DrawRect(Graphics e)
{
Pen pen = new Pen(this.foregroundColor);
e.FillRectangle(new SolidBrush(this.backgroundColor), backRect);
e.DrawRectangle(new Pen(Color.Black), backRect);
e.FillRectangle(new SolidBrush(this.foregroundColor), foreRect);
e.DrawRectangle(new Pen(Color.Black), foreRect);
e.FillRectangle(new SolidBrush(this.setRectColor), setRect);
e.DrawRectangle(new Pen(Color.Black), setRect);
}
private void DrawText(Graphics e)
{
Point point = new Point();
point.X = this.backRect.X + this.backRect.Width * 3 / 7;
point.Y = this.backRect.Y + this.backRect.Height / 3;
SolidBrush brush = new SolidBrush(fontColor);
Font font = new Font(myFontFamily, this.fontSize);
string percent = ((int)this.myValue).ToString() + "%";
StringFormat format = new StringFormat();
format.Alignment = StringAlignment.Center;
format.LineAlignment = StringAlignment.Center;
e.DrawString(percent, font, brush, backRect, format);
}
使用Graphics進行繪制,這里有個要點,通過設置StringFormat可以讓文字居中顯示
8、最后還有一個方法OnResize,在設計時,修改控件的大小會調用這個方法,比如:拖動邊緣的箭頭改變控件的大小時,控件也要做相應的改變時,就可以重載該方法,如果沒有重載,就只有在修改完成后才更新控件,不懂的可以自己試一下 : )
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
this.Width = Width;
this.Height = Height;
Invalidate();
}
好了,控件算是繪制完成了
對了,還有,控件繪制完了,卻不能操作,還要對控件進行操作
9、這里通過三個鼠標事件函數,讓鼠標可以拖動控制條
在構造函數中添加下面語句
this.MouseDown += MySlider_MouseDown; this.MouseMove += MySlider_MouseMove; this.MouseUp += MySlider_MouseUp;
添加三個輔助變量,添加響應函數
Point originPoint; Point originsetRectPoint; bool setRectDown = false; void MySlider_MouseUp(object sender, MouseEventArgs e) { setRectDown = false; } void MySlider_MouseMove(object sender, MouseEventArgs e) { if (setRectDown) { int dd = e.Location.X - originPoint.X; double percent = (double)(originsetRectPoint.X + dd - this.backRect.X) / (this.backRect.Width - this.backRect.Height); if (percent < 0) { this.Value = minimum; this.foreRect.Width = 0; this.setRect.X = backRect.X; } else if (percent > 1) { this.Value = maximum; this.foreRect.Width = this.backRect.Width; this.setRect.X = backRect.X + backRect.Width - backRect.Height; } else { this.Value = percent * maximum; this.foreRect.Width = (int)(percent * this.backRect.Width); this.setRect.X = originsetRectPoint.X + dd; } Invalidate(); } } void MySlider_MouseDown(object sender, MouseEventArgs e) { if (setRect.Contains(e.Location)) { this.originPoint = e.Location; originsetRectPoint = this.setRect.Location; this.setRectDown = true; } }
到這里,一個自定義的進度控制條算是完成了,功能比較簡單,大家可以自己完善