从头开始教你创建一个自定义可视化的Winows Form控件(Divider Panel)--For Begnners...

Divider Panel-从头开始教你创建一个自定义可视化的Winows Form控件<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

-For Beginners

 

原文(英文)http://www.codeproject.com/cs/miscctrl/DividerPanel.asp

系统:WindowsXP   P<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />42.2G   内存:512MB

环境:Visual Studio.NET 2003 语言:C#
                                                                                                                    源码下载

 

说明:参考原文的主线,部分翻译并加了很多个人见解,希望给初学者带来帮助,如在学习这篇文章的时候有什么问题,欢迎提问,我会尽力给大家解决

 

首先看看完成之后的效果:

<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" />

screenshot.gif 

介绍

在这个向导中我们将从开始创造一个可以在.Net ToolBox中使用的Divider Palel控件;控件是很简单的,从Palel继承,增加了可以选择边框外观和边界的属性

 

跟着我的步骤一步一步完成这个控件,你可以学习到:

1.如何继承一个类

2.如何创建自定义的属性

3.如何重载基类的方法

4.如何使用属性的注释

5.如何为你的可视化控件创建自己的图标

6.如何创建一个简单的设计类

7.如何把你的控件整合到.NET IDE的ToolBox中去

 

在这个控件的完成过程中,还将告诉大家一些使用Visual Studio 为我们提供的小捷径,提高我们的效率,相信大家会有所收获,废话不多说,下面正式开始

 

第一步:创建一个空白解决方案

Image1.gif
   为什么我们要创建一个空的解决方案,而不是用向导直接生成一个类库工程呢?相信很多人会有这样的疑问。好听我一一道来:在这样的一个空的解决方案中,我们可以添加多个复合的工程,也就是说除了我们的类库工程,我们还可以添加一个测试用的工程,在不同的工程中测试我们的控件,这样还有点好处就是能方便的使用共享的资源。这是个很好的编程习惯,希望大家能学会用。

 

第二步:添加一个Windows控件库工程


      Image2.gif
      #
在添加工程的对话框中选择Windows控件库工程,工程名为DividerPanel

 

可以看到向导自动为我们生成了一个UserControl1.cs文件,我们不需要它,选择该文件,删除它,接着我们添加一个我们自己需要的类

Image3.gif
      
      #
在添加类的对话框中输入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 None.gif
 2 None.gif
 3 None.gif //  This system of private properties with public accessors is a
 4 None.gif //  best practice coding style.
 5 None.gif //  Note that our private properties are in camelCasing -
 6 None.gif //  the first letter is lower case, and additional word 
 7 None.gif //  boundaries are capitalized.
 8 None.gif
 9 None.gif private  System.Windows.Forms.Border3DSide borderSide;
10 None.gif private  System.Windows.Forms.Border3DStyle border3DStyle;
11 None.gif
12 None.gif //  Next we have our public accessors, Note that for public accessors
13 None.gif //  we use PascalCasing - the first letter is capitalized and additional
14 None.gif //  word boundaries are also capitalized.
15 None.gif
16 None.gif public  System.Windows.Forms.Border3DSide BorderSide
17 ExpandedBlockStart.gifContractedBlock.gif dot.gif {
18ExpandedSubBlockStart.gifContractedSubBlock.gif    get dot.gifreturn this.borderSide; }
19InBlock.gif    set
20ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
21InBlock.gif        ifthis.borderSide != value )
22ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
23InBlock.gif            this.borderSide = value;
24InBlock.gif            this.Invalidate();
25ExpandedSubBlockEnd.gif        }

26ExpandedSubBlockEnd.gif    }

27ExpandedBlockEnd.gif}

28 None.gif
29 None.gif public  System.Windows.Forms.Border3DStyle Border3DStyle
30 ExpandedBlockStart.gifContractedBlock.gif dot.gif {
31ExpandedSubBlockStart.gifContractedSubBlock.gif    get dot.gifreturn this.border3DStyle; }
32InBlock.gif    set
33ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
34InBlock.gif        ifthis.border3DStyle != value )
35ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
36InBlock.gif            this.border3DStyle = value;
37InBlock.gif            this.Invalidate();
38ExpandedSubBlockEnd.gif        }

39ExpandedSubBlockEnd.gif    }

40ExpandedBlockEnd.gif}

41 None.gif

 

注意:添加变量类型应该尽量使用完全路径,这是一个好的编程习惯大家应该养成;

就像这样:(private System.Windows.Forms.Border3DSide borderSide;

 

接下来说说我们的公共属性访问器,我们的两个公共属性都用了GetSet访问器,表示是可读可写的属性

Set属性访问器,我们先检查当前值是否等于要设置的值,如果不同在修改,然后调用this.Invalidate()方法;

注意:Invalidate()这个方法的作用是使我们的控件重绘,也就是支持可视化的原理,也就是这么简单的一句话。

 

这里再给大家介绍一点小知识

1.              大家应该养成良好的编码规范,就拿对象命名来说,很多人习惯了使用其他语言的命名规则来命名C#的变量,例如他就把我们的borderSide命名为    m_ BorderSide,虽然这样看来也没什么不妥之处,但是当你使用this.***智能感应机制的时候,问题来了,他就不智能,大家不信可以试一试,既然这个IDE为我们提供了如此方便快捷的方法,能提高很多效率,为何不用呢,我想没有人跟自己过不去。

2.              还有一点小技巧,当你输入你的变量的时候,比如我们的borderSide这个变量,你不必全部输入,当你输入bor还没有输完的时候,按下Alt键加方向右键,你就会奇迹般的发现你的变量borderSide已经出现在屏幕上了,这对初学者来说是个神奇的功能吧……
      
3.              定义的变量要直观的表示它的含义

4.              通常在构造函数中为我们的变量赋初值,还有其他的初始化工作通常也是在构造函数中完成

 

好了继续我们的程序,现在我们的构造函数应该如下了

 1 None.gif //  This is the Constructor for our class. Any private variables 
 2 None.gif //  we have defined should have their initial values set here. It 
 3 None.gif //  is good practice to always initialize every variable to a specific 
 4 None.gif //  value, rather then leaving it as an inferred value. For example, 
 5 None.gif //  all bool's should be set to false rather than leaving them as an 
 6 None.gif //  inferred false, and any objects referenced that are initially null 
 7 None.gif //  should be explicitly set to null. (eg. myObject = null)
 8 None.gif
 9 None.gif public  DividerPanel()
10 ExpandedBlockStart.gifContractedBlock.gif dot.gif {
11InBlock.gif    // Set default values for our control's properties
12InBlock.gif    this.borderSide = System.Windows.Forms.Border3DSide.All;
13InBlock.gif    this.border3DStyle = System.Windows.Forms.Border3DStyle.Etched;
14ExpandedBlockEnd.gif}

15 None.gif

第五步:添加重载的方法

       在标签页选择类视图,效果差不多如下图所示

Image4.gif
   接着找到
OnPaint方法,再其上面右击,选择添加重写方法,效果如下图所示

Image5.gif
   我们重写后的代码

 1 None.gif protected   override   void  OnPaint(System.Windows.Forms.PaintEventArgs e)
 2 ExpandedBlockStart.gifContractedBlock.gif dot.gif {
 3InBlock.gif    // allow normal control painting to occur first
 4InBlock.gif    base.OnPaint(e);
 5InBlock.gif
 6InBlock.gif    // add our custom border
 7InBlock.gif    System.Windows.Forms.ControlPaint.DrawBorder3D (
 8InBlock.gif        e.Graphics,
 9InBlock.gif        this.ClientRectangle,
10InBlock.gif        this.border3DStyle,
11InBlock.gif        this.borderSide );
12ExpandedBlockEnd.gif}

13 None.gif

下面解说上面代码的意义:

base.OnPaint(e)调用基类的OnPaint函数,它可以帮我们完成很多繁琐的控件绘制工作,当基类的控件绘制完成之后我们接着调用System.Windows.Forms.ControlPaint.DrawBorder3D()绘图函数,在Panel的上面绘制我们自定义的边框样式,参数就是我们先前定义的样式

另外给大家个小提醒吧System.Windows.Forms.ControlPaint.值得你好好研究,它提供了很多静态的方法,用来windows样式的控件,比如ButtonCheckBoxes, RadioButtons, Grids……

到此为止我们的控件似乎已经完成了,但是不能高兴得太早,距离成功我们还有很多的事情要做呢。

 

第六步:添加属性描述和文件说明

       为了使我们的控件和ToolBox里面提供的没有多大区别,我们要做得更加完善,就像下图所示,对我们的属性要有描述和支持

Image6.gif
   这里还有一点小的技巧,当你在一个函数前面输入“
///”的时候,VS 2003自动为你产生一个描述的格式,它自动把函数参数什么的都搞好了,你需要做的只是添加必要的说明。大家试一下便知道。

先看我们的代码,然后我再一一分析,修改之后的代码如下:

 1 ExpandedBlockStart.gif ContractedBlock.gif /**/ /// <summary>
 2InBlock.gif/// Specifies the sides of the panel to apply a three-dimensional border to.
 3ExpandedBlockEnd.gif/// </summary>

 4 None.gif [Bindable( true ), Category( " Border Options " ), 
 5 None.gifDefaultValue(System.Windows.Forms.Border3DSide.All),
 6 None.gifDescription( " Specifies the sides of the panel to apply a 
 7 None.gif three - dimensional border to. " )]
 8 None.gif public  System.Windows.Forms.Border3DSide BorderSide
 9 ExpandedBlockStart.gifContractedBlock.gif dot.gif {
10ExpandedSubBlockStart.gifContractedSubBlock.gif    get dot.gifreturn this.borderSide; }
11InBlock.gif    set
12ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
13InBlock.gif        ifthis.borderSide != value )
14ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
15InBlock.gif            this.borderSide = value;
16InBlock.gif            this.Invalidate();
17ExpandedSubBlockEnd.gif        }

18ExpandedSubBlockEnd.gif    }

19ExpandedBlockEnd.gif}

20 None.gif
21 ExpandedBlockStart.gifContractedBlock.gif /**/ /// <summary>
22InBlock.gif/// Specifies the style of the three-dimensional border.
23ExpandedBlockEnd.gif/// </summary>

24 None.gif [Bindable( true ), Category( " Border Options " ), 
25 None.gifDefaultValue(System.Windows.Forms.Border3DStyle.Etched),
26 None.gifDescription( " Specifies the style of the three-dimensional border. " )]
27 None.gif public  System.Windows.Forms.Border3DStyle Border3DStyle
28 ExpandedBlockStart.gifContractedBlock.gif dot.gif {
29ExpandedSubBlockStart.gifContractedSubBlock.gif    get dot.gifreturn this.border3DStyle; }
30InBlock.gif    set
31ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
32InBlock.gif        ifthis.border3DStyle != value )
33ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
34InBlock.gif            this.border3DStyle = value;
35InBlock.gif            this.Invalidate();
36ExpandedSubBlockEnd.gif        }

37ExpandedSubBlockEnd.gif    }

38ExpandedBlockEnd.gif}

39 None.gif

 

Bindable(true)把这个属性设置为True,可以使你的设计器能捕获属性变化的消息,这就是在设计期你能预览效果的原因

Category("Border Options")属性的分组,很简单,看上面一个图,我们直观的看到Broder3DstyleBroderSizeBorder 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位色

       Image7.gif

自己想把这个图画成啥样都行的,下面只是个简单的示范

Image8.gif
   最后添加代码是
ToolBox能识别出控件的图标资源

[ToolboxItem(true)]

[ToolboxBitmap(typeof(DividerPanel))]

public class DividerPanel : System.Windows.Forms.Panel

{

}

说明:第一行是说让ToolBox支持我们的控件,虽然默认这个属性是true但还是鼓励大家把代码写出来,这样更加直观

第二行告诉编译器,DividerPanel.bmp是控件的图标文件

 

第八步:添加一个简单的设计类

       右击我们的工程,选择添加类,类名为DividerPanelDesigner.cs

       为我们的类添加引用,System.Design;,你会发现当你准备使用using System.**的时候,那个点,点不出来我们需要的Design

       这是请在解决方案资源管理器的引用上面右击,选择添加,找到我们的System.Design.dll双击之后,点击下面的确定,这时我们的System.Design.dll就添加完成了

Image9.gif

我们的
DividerPanelDesigner.cs将继承自

System.Windows.Forms.Design.ScrollableControlDesigner

 

代码如下:

public class DividerPanelDesigner : System.Windows.Forms.Design.ScrollableControlDesigner

{

}

Image10.gif
    这时我们已经可以轻车熟路的为我们的类添加重载方法,如上图,相信大家很
easy的搞定。


     

1 None.gif          protected   override   void  PreFilterProperties(
2 None.gif            System.Collections.IDictionary properties)
3 ExpandedBlockStart.gifContractedBlock.gif         dot.gif {
4InBlock.gif            properties.Remove("BorderStyle");
5ExpandedBlockEnd.gif        }

6 None.gif


   在我们的DividerPanel类中:

1 None.gif [ToolboxItem( true )]
2 None.gif[ToolboxBitmap( typeof (DividerPanel))]
3 None.gif[DesignerAttribute( typeof (DividerPanelDesigner))]
4 None.gif public   class  DividerPanel : System.Windows.Forms.Panel
5 ExpandedBlockStart.gifContractedBlock.gif dot.gif {
6ExpandedBlockEnd.gif}



   下面我们再给出代码的说明,以及为什么要这么做,为什么要设计我们的DividerPanelDesigner.cs

       因为我们绘制之前需要把BorderStyle的属性移除,这样就避免了使用何种属性的矛盾,此处唯恐翻译不周,给出原文供大家参考

原文:(The Panel control we derived from has a BorderStyle property which can be set to None, FixedSingle or Fixed3D - as we are going to be painting our custom border over the top of the standard Panel control, we should remove the BorderStyle property so that there's no confusion about which property developers should use, and also to avoid ugly visual artifacts from someone setting styles for both sets of properties.

      

第九步:设置程序集信息和注册你的组件

 

修改如下,不再赘述,双击Assembly打开的时候,里面有详细的说明,看那个足以使各位明白是什么意思

 

 1 None.gif [assembly: AssemblyTitle( " Divider Panel " )]
 2 None.gif[assembly: AssemblyDescription(  " A Panel control with selectable border appearance " )]
 3 None.gif[assembly: AssemblyConfiguration( "" )]
 4 None.gif[assembly: AssemblyCompany( " jhtjxj " )]
 5 None.gif[assembly: AssemblyProduct( " Divider Panel Tutorial " )]
 6 None.gif[assembly: AssemblyCopyright( " Copyright (c) 2005-2006 Jacky " )]
 7 None.gif[assembly: AssemblyTrademark( "" )]
 8 None.gif[assembly: AssemblyCulture( "" )]
 9 None.gif //  Flagging your assembly with the CLS Compliant attribute
10 None.gif //  lets the Framework know that it will work with all
11 None.gif //  CLS Compliant languages, and theoretically with all 
12 None.gif //  future framework platforms (such as Mono on Linux).
13 None.gif //  If possible you should avoid using any non-CLS compliant code 
14 None.gif //  in your controls such as unsigned integers (uint) and
15 None.gif //  calls to non-framework assemblies.
16 None.gif [assembly: System.CLSCompliant( true )] 
17 None.gif
18 None.gif //  The ComVisible attribute should always be explicitly set for controls.
19 None.gif //  Note that in order for your control to be consumed by
20 None.gif //  COM callers, you must use parameter-less constructors
21 None.gif //  and you cannot use static methods.
22 None.gif [assembly: System.Runtime.InteropServices.ComVisible( true )]
23 None.gif

终于到最后了,有点大功告成的感觉,我写这个写了半天时间了啊,但是不能停,赶快把最后一步完成。

在我们使用这个程序集、这个控件之前,最好注册我们的程序集,这是一个好的习惯,能防止修改程序集的修改……

打开.NET命令行工具,输入如下命令,注意,不一定是H盘,D盘,E盘这个地址随便你设置的

Image11b.gif
       OK
,我们获得了这个密钥对。

把这个密钥手工拷贝到你工程文件所在的目录

为了防止大家不够明确,现把我的目录截图,够直观吧……

{42A8F773-9A11-47F1-A097-F75AFBD79D62}0.jpg
 记得在程序集里指定密钥的相对路径,这样应该就没有错了,否则它会显示找不到的错误信息。

[assembly: AssemblyKeyFile("..\\..\\..\\DividerPanel.snk")]

 

第十步:在ToolBox中添加我们的控件

       Image13.gif

       在工具箱上右击,选择添加/移除项,在.Net FrameWork组件下选择浏览,定位到我们刚刚编写好的DividerPanel.dll的位置,确定。

       你就能在ToolBox上看到我们自己编写的控件了。

 

最后总结:

In this tutorial we have touched on the following topics:

1.        Creating a blank solution as a better starting point for a control project

2.        Inheriting functionality from existing controls

3.        Adding new Properties and Accessors to a control class

4.        Property naming conventions

5.        Overriding inherited methods

6.        Adding property descriptions and documentation support

7.        Adding Visual Studio Toolbox support

8.        Simple designer class creation and usage

9.        Specifying assembly attributes and signing a library using the strong name tool

10.    Adding a custom control to the Visual Studio Toolbox

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值