阅读本篇博客之前需要了解VS窗体设计器的工作原理,详细可参见本系列博客(十)、(十一)、(十二)。必须需要知道的一条结论就是:处于窗体设计器(Form Designer)中的任何组件(包含控件,下同),都是实际存在的一个实例。也就是说,拖进去的button1,其实就是实例化一个Button控件。

通常编码中,我们在使用一个类型对象时,通过以下方式:

 View Code

如上所示,我们在使用Car类时,是通过new的方式来创建一个实例,然后给它初始化一些信息,这些所有操作都是通过我们手动来编写代码实现的。

我们注意到,在设计UI界面的时候,窗体中的所有控件、组件都是可以通过“属性窗体”来编辑的,也就是说,界面上这些元素的初始化不需要我们手动编写代码,完全可以通过点点鼠标,按按键盘就可以做到。我们可以总结出来设计器可以帮我们做以下工作:

  • 实例化对象

没错,不用你手动new对象了,设计器帮你来完成。

  • 编辑属性

选定一个组件,在属性窗体中编辑它的属性,跟你通过编写“实例.属性=属性值”是一样的效果。

  • 注册事件

选定一个组件,在属性窗体的事件选项卡,双击事件空白处,自动注册事件。

窗体设计器不需要你手动编写一行代码,对象的实例化、属性编辑、事件注册全部搞定,也就是说,窗体设计器能够可视化设计一些对象。至于哪些类型的对象可以通过窗体设计器来进行可视化设计,请参见本系列(十、十一、十二),我在这里直接给出结果:窗体设计器能够可视化设计实现了IComponent接口类型的对象。也就是说,如果你定义了一个类型A,恰好它实现了IComponent接口(直接或者间接),那么你就完全可以通过窗体设计器来可视化设计A类型的对象。

由此可以看出,创建一个可以可视化设计的对象并不难,只要我们的类型实现了IComponent接口就行(官方称这种类型为组件)。我们再来看一下,窗体设计器初始化出来的对象,跟我们自己手动编写代码初始化的对象有哪些相同点和不同点:

不同点:

  • 前者更直观简单,隐藏的东西太多,后者复杂,但是清楚内部过程。
  • 前者对象的初始化,在程序一启动就开始,不能人工控制其时机,具体是在Form1的构造方法中的InitializeComponent()中进行,后者就更灵活,需要的时候编写代码就可以。
  • 前者初始化出来的对象几乎都跟UI界面有关(这个很容易就能想到,窗体设计器肯定设计跟窗体界面有关的东西),而后者没有这个原则,不管是什么对象,都是可以的。

相同点:

  • 都是初始化一个对象。
  • 都有代码产生,前者产生的代码在InitializeComponent()中,后者为人工编写。

我们应该清楚,程序最终都是要经过将源代码编译成可执行文件之后才能运行的,所以源代码是一切根本,没有源代码,其他的都是白扯。

综上所有之述,我们可以手动编写代码来初始化任何对象,我们可以通过窗体设计器来初始化实现了IComponent接口的类型对象。

好了知道怎样才能创建一个可以可视化设计的对象之后,我们来创建一个试一下,定义一个类型MyComponent,使其继承自Component:

 View Code

如上代码所示,该类型只包含了几个公共属性,没有其他内容。此类型对象就可以通过窗体设计器来设计了,也就是说,从工具箱中向设计器中拖放MyComponent类型之后,窗体设计器自动会实例化一个MyComponent对象,并且你可以通过属性窗体来编辑该对象的属性:

1)StringProperty

String类型属性,直接可以在属性窗体中输入。

2)ColorProperty

Color类型属性,属于.NET自带类型,所以有默认的属性编辑器,如下图:

图1

3)MyTypeProperty1

自定义类型属性,需要我们自己定义一个属性编辑器Editor(typeof(MyTypeEditor1),typeof(UITypeEditor))。

4)MyTypeProperty2

自定义类型属性,需要我们自己定义一个属性编辑器Editor(typeof(MyTypeEditor2),typeof(UITypeEditor))。

5)ControlProperty

Control类型属性,我们可以将设计器中已经存在的Control赋值给该属性,指定了属性编辑器Editor(typeof(ControlEditor),typeof(UITypeEditor))。

6)ImageListProperty

ImageList属性,这个就是我们常见的一些控件(比如TabControl)含有ImageList属性,点击右方的小三角形,就可以列出窗体设计器中已经存在的ImageList,供你选择。指定了属性编辑器Editor(typeof(ImageListEditor),typeof(UITypeEditor))。

也就是说,当我们在窗体设计器中设计一个对象的时候,如果该对象包含一些特殊(非.NET默认自带类型)类型属性时,我们需要为该属性提供一个“属性编辑器”。

以下就分别为每个属性对应的属性编辑器了(假设诸位看官都知道了UITypeEditor的作用,不知道可以查一下):

1)MyTypeProperty1属性

 View Code

鼠标点击MyTypeProperty1属性右侧的小三角形,出现一个下拉列表框。如下图:

图2

2)MyTypeProperty2属性

 View Code

鼠标点击MyTypeProperty2右侧的小三角形,弹出一个对话框。

图3

3)ControlProperty属性

 View Code

鼠标点击ControlProperty右侧的小三角形,出现下拉列表框,列表中显示的都是窗体设计器中已经存在的Control。

图4

4)ImageListProperty属性

 View Code

鼠标点击ImageListProperty右侧的小三角形,出现下拉列表,列表中显示窗体设计器中已经存在的ImageList。

图5

本篇博客介绍了怎么创建一个可以可视化设计的对象(准确来讲,应该是怎样创建一个可以可视化设计其对象的类型),首先让类型(间接或者直接)实现IComponent接口(不要问我为什么,请参见前面的博客),然后为类型的某些特殊属性创建对应的属性编辑器,就这么简单。类型创建者工作量大一点,但是类型使用者更方便一些,Demo中MyComponent类型就是最后的核心成果,供使用者使用,使用者不需要知道其他的类似属性编辑器之类的东西,在他们看来,这些相当于没有。

注意使用范围,并不是任何时候都可以使用,最好参见前面提到的“相同点”和“不同点”那里。另外,本篇博客中设计到的知识点很多,像provider.GetService(typeof(IWindowsFormsEditorService))、foreach(Component c in context.Container.Components)这些需要和设计器打交道的地方我几乎没有详细说到过,原因是这些太复杂了,要是细说的话,得说一大篓子,而且不是本篇文章的重点。

源码下载地址:http://files.cnblogs.com/xiaozhi_5638/PropertyEditorInDesigner.rar

注意不要试图运行源代码,没有任何效果,只能在设计器中看到效果。希望有帮助。

Update (2013-12-09)

上面结束提到了“支持可视化设计对象”的使用场合,建议如果该类型跟UI界面有关联(需要与界面其他元素交互),并且对象的实例化不需要人工控制其时机,那么可以让该类型支持可视化设计(也就是实现IComponent接口)。注意这里的建议,也就是说不是强制性的,你完全可以定义一个People类,让其实现IComponent接口,这合法!结果就是,从工具栏中拖放一个People到窗体设计器中,它会自动帮你实例化了一个People对象实例,并且你也能通过属性窗体编辑它的属性值(这些后台都能自动生成对应代码)。另外,经发现,具备某一些功能的类型,虽然跟界面无交互,但是它还是支持可视化设计,比如BackgroundWorker组件,拖一个BackgroundWorker到窗体设计器中去,设计器会自动帮你实例化一个Backgroundworker对象(生成对应代码),然后你可以通过属性窗体编辑它的属性值(生成对应代码),这跟你自己手动new BackgroundWorker没有区别,你还是可以在其他地方一样使用该backgroundworke对象,至少你目前看起来没区别。

为了更好的说明通过窗体设计器设计出来的对象,跟我们手动编写代码搞出来的对象有什么相同和不同点,我们先来分析一下Form1.Designer.cs中的代码:

我们注意到,我们在设计器中的每一步操作,对应生成的代码都在InitializeComponent()方法中,它像是word录制宏的功能,你在word中的每一个操作,都可以生成对应的VBA代码,也就是说,窗体设计器从实例化对象,到编辑属性,再到注册事件等等等,都有代码帮我们记录下这些操作。我们再看一下实例化对象的代码:

 View Code

没错,任何一个实例化出来的对象,都给它传递了this.components容器,我们再看MyComponent的构造方法是这样的:

 View Code

也就是说,对象实例化的时候,都将该对象放进了一个components的容器,这个容器专门用来存放由窗体设计器实例化出来的对象(控件除外)。这就像一个大的容器,专门来存放这些小个体。接下来我们再来看一下Form1.Designer.cs中的dispose方法:

 View Code

我们可以发现,在Form1对象Dispose的时候,它将components中的所有个体都Dispose掉了。到此,我们可以总结出来一条:由窗体设计器设计出来的对象由父窗体(父控件)的InitializeComponents方法统一初始化,由父窗体(父控件)的Dispose()方法统一释放资源。我们来看一张Form1运行结构图:

图(更新)1

由此可以看出,由窗体设计器设计出来的对象,它们的生命周期以及存放结构都比较有规律,这个就是窗体设计器设计出来对象的好处。

我们在往窗体设计器中拖放组件时,就是往Form1类型中添加新的成员对象,跟我们定义一个类型,向里面添加成员变量一个意思,只是前者更直观,添加的每一个成员对象,在UI设计器中都能看见与它对应的一个对象实例,只要我们改变了这个对象实例的属性,就能马上看见设计器中的对象实例效果,紧接着后台生成代码;而后者就没有这么直观了,只能手写代码,而且还看不见效果。

图(更新)2

如上图,窗体设计器中某一个对象实例属性更新之后,马上就能在设计器中看见效果(因为它是实实在在存在于堆中的对象),接着InitializeComponents中的代码就会更新,注意,窗体设计器中的myComponent1对象实例跟.cs代码中的myComponent1变量不是一个东西,他们只有一种映射关系。我们最终要的是.cs中的代码文件,而不是我们看见的窗体设计器中的图像,后者只是起到一个可视化的效果,最终一文不值。这个就像photoshop作图一样,最终保存到硬盘的图片文件才是最重要的,作图过程中作图区域显示的东西没有价值。窗体设计器隐藏得越多,我们知道得越少。

作者:周见智 
出处:http://www.cnblogs.com/xiaozhi_5638/ 
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

分类: Windows Forms,非主流玩一玩
标签: c#, Winform
本文转自周见智博客博客园博客,原文链接:http://www.cnblogs.com/xiaozhi_5638/p/3463774.html,如需转载请自行联系原作者

.Net开发笔记(十九) 创建一个可以可视化设计的对象相关推荐

  1. 【Visual C++】游戏开发笔记十六 讲解一个完整的回合制游戏demo

    本系列文章由zhmxy555编写,转载请注明出处. 文章链接  http://blog.csdn.net/zhmxy555/article/details/7447864 作者:毛星云    邮箱:  ...

  2. 【Visual C 】游戏开发笔记十六 讲解一个完整的回合制游戏demo

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 本系列文 ...

  3. [原]【Visual C++】游戏开发笔记十六 讲解一个完整的回合制游戏demo

    本系列文章由zhmxy555编写,转载请注明出处. 文章链接  http://blog.csdn.net/zhmxy555/article/details/7447864 作者:毛星云    邮箱:  ...

  4. 【Visual C++】游戏开发笔记十六 讲解一个完整的回合制游戏demo

    本系列文章由zhmxy555编写,转载请注明出处. 文章链接  http://blog.csdn.net/zhmxy555/article/details/7447864 作者:毛星云    邮箱:  ...

  5. 【Visual C++】游戏开发笔记十九 DirectX与OpenGL的博弈

    From: http://blog.csdn.net/zhmxy555/article/details/7522960 本系列文章由zhmxy555(毛星云)编写,转载请注明出处. http://bl ...

  6. 【Visual C 】游戏开发笔记十九 DirectX与OpenGL的博弈

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 本系列文 ...

  7. 游戏开发笔记十六 讲解一个完整的回合制游戏demo

    首先,我们来了解这种行为型AI的设计方法. 游戏程序中计算机角色的思考与行为,实际上是对各种不同事件进行分析思考,然后根据不同的情况作出相应的反应.但如何对发生的条件进行判断,并作出相应的反应呢? 对 ...

  8. Polyworks脚本开发学习笔记(十九)-将数据对象与参考对象对齐的方法

    Polyworks脚本开发学习笔记(十九)-将数据对象与参考对象对齐的方法 把开发手册理了一遍,发现还有几个点没有记录下来,其中一个就是使用点对的粗对齐和使用参考目标的精确对齐.为了把这个学习笔记凑够 ...

  9. ATECC508A芯片开发笔记(九):加密读写508芯片数据的流程及相应设置

    目录 ATECC508A芯片开发笔记(九):加密读写508芯片数据的流程及相应设置 1.Encrypted Read 1.1 Standard Encrypted Read Flow 1.2 Simp ...

最新文章

  1. Servlet、MySQL中文乱码
  2. Java12和Jdk12安装以及OpenJdk12源码
  3. php怎么查询mysql_php如何查询数据库
  4. python杂记-读取png图像文件头数据
  5. airflow sql_alchemy_conn mysql_airflow使用mysql数据库,LocalExecutor并发调度
  6. PERMUTATION
  7. 用cglib生成的代理类取不到注解的问题
  8. oracle错误代码及解决办法整合
  9. 窗体点击,空白处隐藏(stopPropagation)
  10. Nginx重启时丢失nginx.pid文件
  11. c语言:简单排序:冒泡排序法、选择排序法、插入排序法(待写)
  12. 基于一维卷积神经网络模型的AI量化智能选股策略
  13. 测试开发自我介绍模板
  14. 快35了,还在“点点点”?那些入行几年的测试点工后来都怎么样了?
  15. 光纤猫连接路由器_CodingPark编程公园
  16. 清华大学计算机科学学院刘钊,姚 骏-清华大学生命学院
  17. SSL: error:0B080074:x509 certificate routines:X509_check_private_key:key values mismatch
  18. 使用html制作一个旅游网站
  19. PerfDog使用说明书
  20. PPT训练营-封面页

热门文章

  1. 学习笔记Flink(四)—— Flink基础API及核心数据结构
  2. python学习笔记(十)——迭代器和生成器(外加import功能)
  3. 今天是1024也是霜降
  4. 目录创建 android,创建目录浏览器  |  Android 开发者  |  Android Developers
  5. mysql游标嵌入式_Oracle与MySQL内嵌游标的使用示例
  6. sql盲注特点_SQL注入介绍及分类解读
  7. axios 同步_githubactions进行github仓库和gitee仓库同步
  8. window系统 telnet报错:‘telnet‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件。
  9. atomic 内存序_并行编程的内存顺序 2020-11-23
  10. java包和访问权限_Java包和访问权限—1