Divider Panel-
从头开始教你创建一个自定义可视化的Winows Form
控件
-
For Beginners
系统:WindowsXP
P42.2G 内存:512MB
环境:Visual Studio.NET 2003 语言:C#
源码下载
源码下载
说明:
参考原文的主线,部分翻译并加了很多个人见解,希望给初学者带来帮助,如在学习这篇文章的时候有什么问题,欢迎提问,我会尽力给大家解决
首先看看完成之后的效果:
介绍
在这个向导中我们将从开始创造一个可以在.Net ToolBox中使用的Divider Palel控件;控件是很简单的,从Palel继承,增加了可以选择边框外观和边界的属性
跟着我的步骤一步一步完成这个控件,你可以学习到:
1.如何继承一个类
2.如何创建自定义的属性
3.如何重载基类的方法
4.如何使用属性的注释
5.如何为你的可视化控件创建自己的图标
6.如何创建一个简单的设计类
7.如何把你的控件整合到.NET IDE的ToolBox中去
在这个控件的完成过程中,还将告诉大家一些使用Visual Studio 为我们提供的小捷径,提高我们的效率,相信大家会有所收获,废话不多说,下面正式开始
第一步:创建一个空白解决方案
为什么我们要创建一个空的解决方案,而不是用向导直接生成一个类库工程呢?相信很多人会有这样的疑问。好听我一一道来:在这样的一个空的解决方案中,我们可以添加多个复合的工程,也就是说除了我们的类库工程,我们还可以添加一个测试用的工程,在不同的工程中测试我们的控件,这样还有点好处就是能方便的使用共享的资源。这是个很好的编程习惯,希望大家能学会用。
第二步:添加一个
Windows
控件库工程
#在添加工程的对话框中选择Windows控件库工程,工程名为 DividerPanel
可以看到向导自动为我们生成了一个
UserControl1.cs文件,我们不需要它,选择该文件,删除它,接着我们添加一个我们自己需要的类
#在添加类的对话框中输入 DividerPanel.cs作为类名
第三步:从现有类继承
继承机制使得面向对象编程如此强大,面向对象已经是现代软件开发的主力了,当然现在还有更新的一些其他编程思想必如:面向方面……
当我们从一个现有类继承的时候,我们自动的获取了基类的一些功能和特性,我们还能让我们的子类具有更特别的属性,具体讲就是我们可以添加新的属性,新的方法,或者是重载父类的方法。
WindowsForm
的很多控件都需要从
System.Windows.Forms.Control
继承,
Control
这个基类抽象了很多
.NET FrameWork
支持的公共的属性和方法
说了这么多继承机制,其实要把你的类继承自某一个类的代码是非常简单的:
public class DividerPanel : System.Windows.Forms.Panel
{
}
现在我们的类就具有了
Panel
类的公共属性和方法,我们接下来要做到,就是让我们的类具备我们需要的特性(重载父类的方法
or
添加新的特性)
第四步:添加属性和访问器
我们准备添加两个属性:
BorderSide
和
Border3Dstyle
BorderSide
:获取从
System.Windows.Forms.Border3DSide
enumeration(枚举)的一个引用
Border3Dstyle
:
获取从
System.Windows.Forms.Border3DStyle
enumeration(枚举)的一个引用
这里给出我们创建属性的最好的方法:
创建两个
Private
的变量,供类里面的方法访问使用
创建两个
Public
的访问器,供外部类访问我们的控件的属性
1
2
3 // This system of private properties with public accessors is a
4 // best practice coding style.
5 // Note that our private properties are in camelCasing -
6 // the first letter is lower case, and additional word
7 // boundaries are capitalized.
8
9 private System.Windows.Forms.Border3DSide borderSide;
10 private System.Windows.Forms.Border3DStyle border3DStyle;
11
12 // Next we have our public accessors, Note that for public accessors
13 // we use PascalCasing - the first letter is capitalized and additional
14 // word boundaries are also capitalized.
15
16 public System.Windows.Forms.Border3DSide BorderSide
17 {
18 get { return this.borderSide; }
19 set
20 {
21 if( this.borderSide != value )
22 {
23 this.borderSide = value;
24 this.Invalidate();
25 }
26 }
27}
28
29 public System.Windows.Forms.Border3DStyle Border3DStyle
30 {
31 get { return this.border3DStyle; }
32 set
33 {
34 if( this.border3DStyle != value )
35 {
36 this.border3DStyle = value;
37 this.Invalidate();
38 }
39 }
40}
41
2
3 // This system of private properties with public accessors is a
4 // best practice coding style.
5 // Note that our private properties are in camelCasing -
6 // the first letter is lower case, and additional word
7 // boundaries are capitalized.
8
9 private System.Windows.Forms.Border3DSide borderSide;
10 private System.Windows.Forms.Border3DStyle border3DStyle;
11
12 // Next we have our public accessors, Note that for public accessors
13 // we use PascalCasing - the first letter is capitalized and additional
14 // word boundaries are also capitalized.
15
16 public System.Windows.Forms.Border3DSide BorderSide
17 {
18 get { return this.borderSide; }
19 set
20 {
21 if( this.borderSide != value )
22 {
23 this.borderSide = value;
24 this.Invalidate();
25 }
26 }
27}
28
29 public System.Windows.Forms.Border3DStyle Border3DStyle
30 {
31 get { return this.border3DStyle; }
32 set
33 {
34 if( this.border3DStyle != value )
35 {
36 this.border3DStyle = value;
37 this.Invalidate();
38 }
39 }
40}
41
注意:添加变量类型应该尽量使用完全路径,这是一个好的编程习惯大家应该养成;
就像这样:(private System.Windows.Forms.Border3DSide borderSide;)
接下来说说我们的公共属性访问器,我们的两个公共属性都用了Get和Set访问器,表示是可读可写的属性
Set属性访问器,我们先检查当前值是否等于要设置的值,如果不同在修改,然后调用this.Invalidate()方法;
注意:Invalidate()这个方法的作用是使我们的控件重绘,也就是支持可视化的原理,也就是这么简单的一句话。
这里再给大家介绍一点小知识
1. 大家应该养成良好的编码规范,就拿对象命名来说,很多人习惯了使用其他语言的命名规则来命名C#的变量,例如他就把我们的
borderSide
命名为
m_
BorderSide
,
虽然这样看来也没什么不妥之处,但是当你使用
this.***
智能感应机制的时候,问题来了,他就不智能,大家不信可以试一试,既然这个
IDE
为我们提供了如此方便快捷的方法,能提高很多效率,为何不用呢,我想没有人跟自己过不去。
2.
还有一点小技巧,当你输入你的变量的时候,比如我们的
borderSide
这个变量,你不必全部输入,当你输入
bor
还没有输完的时候,按下
Alt
键加方向右键,你就会奇迹般的发现你的变量
borderSide
已经出现在屏幕上了,这对初学者来说是个神奇的功能吧……
3. 定义的变量要直观的表示它的含义
4. 通常在构造函数中为我们的变量赋初值,还有其他的初始化工作通常也是在构造函数中完成
好了继续我们的程序,现在我们的构造函数应该如下了
1
//
This is the Constructor for our class. Any private variables
2 // we have defined should have their initial values set here. It
3 // is good practice to always initialize every variable to a specific
4 // value, rather then leaving it as an inferred value. For example,
5 // all bool's should be set to false rather than leaving them as an
6 // inferred false, and any objects referenced that are initially null
7 // should be explicitly set to null. (eg. myObject = null)
8
9 public DividerPanel()
10 {
11 // Set default values for our control's properties
12 this.borderSide = System.Windows.Forms.Border3DSide.All;
13 this.border3DStyle = System.Windows.Forms.Border3DStyle.Etched;
14}
15
2 // we have defined should have their initial values set here. It
3 // is good practice to always initialize every variable to a specific
4 // value, rather then leaving it as an inferred value. For example,
5 // all bool's should be set to false rather than leaving them as an
6 // inferred false, and any objects referenced that are initially null
7 // should be explicitly set to null. (eg. myObject = null)
8
9 public DividerPanel()
10 {
11 // Set default values for our control's properties
12 this.borderSide = System.Windows.Forms.Border3DSide.All;
13 this.border3DStyle = System.Windows.Forms.Border3DStyle.Etched;
14}
15
第五步:添加重载的方法
在标签页选择类视图,效果差不多如下图所示
接着找到OnPaint方法,再其上面右击,选择添加重写方法,效果如下图所示
我们重写后的代码
1
protected
override
void
OnPaint(System.Windows.Forms.PaintEventArgs e)
2 {
3 // allow normal control painting to occur first
4 base.OnPaint(e);
5
6 // add our custom border
7 System.Windows.Forms.ControlPaint.DrawBorder3D (
8 e.Graphics,
9 this.ClientRectangle,
10 this.border3DStyle,
11 this.borderSide );
12}
13
2 {
3 // allow normal control painting to occur first
4 base.OnPaint(e);
5
6 // add our custom border
7 System.Windows.Forms.ControlPaint.DrawBorder3D (
8 e.Graphics,
9 this.ClientRectangle,
10 this.border3DStyle,
11 this.borderSide );
12}
13
下面解说上面代码的意义:
base.OnPaint(e)
调用基类的
OnPaint
函数,它可以帮我们完成很多繁琐的控件绘制工作,当基类的控件绘制完成之后我们接着调用
System.Windows.Forms.ControlPaint.DrawBorder3D
()绘图函数,在
Panel
的上面绘制我们自定义的边框样式,参数就是我们先前定义的样式
另外给大家个小提醒吧
System.Windows.Forms.ControlPaint.
值得你好好研究,它提供了很多静态的方法,用来
windows
样式的控件,比如
Button
、CheckBoxes, RadioButtons, Grids……
到此为止我们的控件似乎已经完成了,但是不能高兴得太早,距离成功我们还有很多的事情要做呢。
第六步:添加属性描述和文件说明
为了使我们的控件和ToolBox里面提供的没有多大区别,我们要做得更加完善,就像下图所示,对我们的属性要有描述和支持
这里还有一点小的技巧,当你在一个函数前面输入“///”的时候,VS 2003自动为你产生一个描述的格式,它自动把函数参数什么的都搞好了,你需要做的只是添加必要的说明。大家试一下便知道。
先看我们的代码,然后我再一一分析,修改之后的代码如下:
1
/**/
/// <summary>
2/// Specifies the sides of the panel to apply a three-dimensional border to.
3/// </summary>
4 [Bindable( true ), Category( " Border Options " ),
5 DefaultValue(System.Windows.Forms.Border3DSide.All),
6 Description( " Specifies the sides of the panel to apply a
7 three - dimensional border to. " )]
8 public System.Windows.Forms.Border3DSide BorderSide
9 {
10 get { return this.borderSide; }
11 set
12 {
13 if( this.borderSide != value )
14 {
15 this.borderSide = value;
16 this.Invalidate();
17 }
18 }
19}
20
21 /**/ /// <summary>
22/// Specifies the style of the three-dimensional border.
23/// </summary>
24 [Bindable( true ), Category( " Border Options " ),
25 DefaultValue(System.Windows.Forms.Border3DStyle.Etched),
26 Description( " Specifies the style of the three-dimensional border. " )]
27 public System.Windows.Forms.Border3DStyle Border3DStyle
28 {
29 get { return this.border3DStyle; }
30 set
31 {
32 if( this.border3DStyle != value )
33 {
34 this.border3DStyle = value;
35 this.Invalidate();
36 }
37 }
38}
39
2/// Specifies the sides of the panel to apply a three-dimensional border to.
3/// </summary>
4 [Bindable( true ), Category( " Border Options " ),
5 DefaultValue(System.Windows.Forms.Border3DSide.All),
6 Description( " Specifies the sides of the panel to apply a
7 three - dimensional border to. " )]
8 public System.Windows.Forms.Border3DSide BorderSide
9 {
10 get { return this.borderSide; }
11 set
12 {
13 if( this.borderSide != value )
14 {
15 this.borderSide = value;
16 this.Invalidate();
17 }
18 }
19}
20
21 /**/ /// <summary>
22/// Specifies the style of the three-dimensional border.
23/// </summary>
24 [Bindable( true ), Category( " Border Options " ),
25 DefaultValue(System.Windows.Forms.Border3DStyle.Etched),
26 Description( " Specifies the style of the three-dimensional border. " )]
27 public System.Windows.Forms.Border3DStyle Border3DStyle
28 {
29 get { return this.border3DStyle; }
30 set
31 {
32 if( this.border3DStyle != value )
33 {
34 this.border3DStyle = value;
35 this.Invalidate();
36 }
37 }
38}
39
Bindable(true)
:把这个属性设置为True,可以使你的设计器能捕获属性变化的消息,这就是在设计期你能预览效果的原因
Category("Border Options")
:属性的分组,很简单,看上面一个图,我们直观的看到Broder3Dstyle和BroderSize在Border Options这个组里面
DefaultValue(System.Windows.Forms.Border3dSide.All)
:设置该属性的默认值,从上面的图我们也能直观看出来,Broder3Dstyle使用默认属性,BroderSize使用修改的属性,它被高亮显示了(就是看到Top是加粗的)
Description("Specifies the sides of the panel to apply a three-dimensional border to.")这就更简单了,图中最下面显示的描述,对这个属性你想怎么描述,写这就行了
第七步:添加ToolBox
支持
这一步相当简单,添加一个位图文件,文件名为DividerPanel.bmp,右击属性选择编译类型为 嵌入式资源(这点很重要),位图大小16*16,采用16位色
自己想把这个图画成啥样都行的,下面只是个简单的示范
最后添加代码是ToolBox能识别出控件的图标资源
[ToolboxItem(true)]
[ToolboxBitmap(typeof(DividerPanel))]
public class DividerPanel : System.Windows.Forms.Panel
{
}
说明:第一行是说让ToolBox支持我们的控件,虽然默认这个属性是true但还是鼓励大家把代码写出来,这样更加直观
第二行告诉编译器,DividerPanel.bmp是控件的图标文件
第八步:添加一个简单的设计类
右击我们的工程,选择添加类,类名为
DividerPanelDesigner.cs
为我们的类添加引用,System.Design;,你会发现当你准备使用using System.**的时候,那个点,点不出来我们
from:http://www.cnblogs.com/jht/archive/2005/08/10/211650.html