写在前面:因为业务的需要,有时会使用到自定义运行时窗体设计器Runtime FormDesigner,实现的功能,就是IDE设计器的简化。设想一下,如果可以在程序运行时,再设计一个Form,然后编译代码,那是多么强大呀。下面介绍几个重要的Runtime FormDesigner,其中大部分来自了微软的官方网站或MSDN。
重要的链接资源
下面介绍有4个重要的Form设计器,它们是: 1,Designerhost INFO: 代码示例演示如何通过使用 VisualC # .NET 创建自定义窗体设计器 .Net1.1下运行的Form设计器2,DesignerHosting
.Net2.0下运行的Form设计器3,CustomFormsDesigner
.Net1.1下运行的Form设计器4,Hosting
来自divil.co.uk的Hosting .Net2.0下运行的Form设计器运行效果图
下面是1,DesignerHost就运行图,你是不是很震惊呢?反正,我第一眼看到这个运行时窗体设计器时,实在是太震惊了,一个只有20-30个源文件的项目,运行时,能够产生如此强大的界面,真是不敢相信,又万分喜悦,而其它三个FormDesigner也是产生相似的界面。这简直就是一个IDE系统,与VisualStudio2005一样啊,而且,它可以产生代码,可以产生C# Source,VB Source和XML。功能介绍
工具的左边是工具箱,右边是属性设置窗口,中间是Form设计器,工具箱,跟平时使用的VisualStudio一样,多个选项卡,WindowsForms选项卡中有多个普通工具,有Components选项卡中有部件控件,而CustomControls选项卡中有用户自定义的控件,Datas选项卡中有系统的数据链接控件,如OleDbConnection之类,当然,也不仅仅是这些内容,你可以通过代码修改来增改左侧的控件。 往上一点看,有Design,C#Source,VB Source,XML四个视图的选择,Design是设计视图,指的就是当前看到的界面,C# Source,VB Source跟我们在IDE工具中使用查看源代码所得到的东西一样。而XML则是我们之前没有见到的。它用于描述当前的Form的内容,如Form的属性和值,布局,层次,Form中的控件等等。 菜单栏中,File下拉后,就是日常的保存,另保存,打开,退出等功能,而Edit就是编辑功能,如,复制,剪切,删除,对齐等,View就是视图界面,如Design,C#Source,VB Source,XML,Layout就是控件的层次移动等,Debug中,就是运行,编译等功能。可以说上,这个控件是四个中做得比较好的。操作起来也方便。
精彩的文章还在继续,下一会,将介绍Runtime Form Designer的机制,编写,编译方法,请留意订阅首页的Rss运行时窗体设计器Runtime Form Designer一出现,就会以其出色的表现,奇怪的代码吸引,怎么说是奇怪的代码呢?设计器所使用到的ImenuCommandService,InameCreationService,ItoolboxService,IUIService,IdesignerHost,IselectionService 这些接口,使用过没用?在设计一个运行时设计器时,忽然学得,自己是在学习一个新的语言,是使用.Net来表示的底层界面接口语言。简单而言,.Net已经帮助你写好了底层的底层,那就是编写设计的接口。你只要继承或重写代码,就可以实现设计器的功能。查看重要的Form设计器用这个网址:
为了使用最简单的方式来说明问题,使用最简单的设计器来做说明,下面介绍Hosting效果图
所有代码图 这个项目,相当的小,上面显示的就是所有的源文件,可以高兴地认为,只要学会了上面的类,就成功了。事实上,只要学会几个重要的类,就可以了。重点研究的接口
在理解下面接口的时候,可以理解为,自己是微软的高级工程师,正在为VisualStudio开发工具箱,界面,界面,菜单等等界面功能。 IMenuCommandService 提供的方法用于管理设计模式下可用的全局设计器谓词和菜单命令,以及显示某些类型的快捷菜单。 简单说,就是新建的控件,在窗体Form中产生的命令,如移动,对齐,层次叠放,复制,剪切,粘贴 INameCreationService 提供可以生成对象的唯一名称的服务。 因为各个拖出来的控件都必须有唯一的名称,这样,才可以编译通过的。 IToolboxService 提供在开发环境下管理和查询工具箱的方法和属性。 这就是一个工具箱,一个同工具箱提供一样功能的服务。 IDesignerHost 提供用于管理设计器事务和组件的接口。 这是最重要的接口,所有的控件都是由这个接口发出,可以理解为,从这个接口可以找到所有控件的引用。 ISelectionService 为设计器提供用于选择组件的接口。 就是当程序运行时,在Form设计器选择控件时,产生的事件,它常常被用来更新属性窗口。 IUIService 启用与开发环境对象(正在承载设计器)的用户界面的交互。 PropertyGrid 这不是一个接口,这只是一个控件,不过,提供了属性设置服务,是用户交互最频繁的服务之一。服务的加载
可以这样理解,运时时Form设计器,就是一个服务的集合,服务管理器ServiceContainer初始化之后,绑定DesignerHost,然后不断增加服务,如 InameCreationService,名字唯一服务 ItoolboxService,工具箱服务 ImenuCommandService,菜单命令服务 IselectionService,选择服务(同时产生选择变化事件,从而改变属性显示) PropertyGrid,属性设置服务 最后,由DesignerHost做激活动作,host.Activate();关于命令服务
在运行时设计器中,要删除选定的控件,只能使用命令接口提供的功能。如 menuService.GlobalInvoke(StandardCommands.Delete); 当然,一个很高明的做法是,一开始就将DesignerHost的所有可能的命令纳入线程的管理,在designerhost.exe,中有使用。你可以在上一篇介绍Runtime Form Designer的文章中,找到这个控件的链接。下一篇,将介绍Runtime FormDesigner的运行和编译机制,不可错过。
之前已经有两篇文章对Form Designer运行了分析和展示,具体展示了几个重要的开源Form设计器,也介绍了最基本的Form设计器的编写方式。而在介绍Hosting设计器时,没有提及到代码的展示与编译运行机制,下面,本文就对运行时Form设计器的代码转换和编译机制进行分析和学习。下面介绍Designerhost工具。
界面效果图
代码的产生
将注意力投放到SampleDesignerLoader.cs类,上图中Design,C# Source,VB Source,XML视图的变化,都会产生事件,事件处理方法就是SampleDesignerLoader类的Flush方法,表示对当前控件与代码的刷新。 一个重要的类,类中的类 System.CodeDom.CodeCompileUnit 在.Net中,一个源文件,如C#写好的.cs文件,其实可以转化为一个类中类CodeCompileUnit,这个类中类有构造函数,方法体,属性,命名空间等等的定义。如引入System命名空间,你可以这样写Imports.Add(new CodeNamespaceImport("System")); CodeConstructor是用来定义构造函数的。可以说,一个类实体的定义,运行时本身就是一个CodeCompileUnit类的实例。类的序列化功能,就是由CodeCompileUnit发起的重要功能。 类的序列化可以将一个类变成一个文件如XML,而这个XML中记录了各个属性和值,在网络环境中,就可以作为一个传送的介质,这样,就可以实现类的远程调用。重要的代码
RootDesignerSerializerAttribute a = TypeDescriptor.GetAttributes(root)[(RootDesignerSerializerAttribute)] RootDesignerSerializerAttribute;
Type t = host.GetType(a.SerializerTypeName);
CodeDomSerializer cds = Activator.CreateInstance(t) CodeDomSerializer;
IDesignerSerializationManager manager = host.GetService((IDesignerSerializationManager)) IDesignerSerializationManager;
CodeTypeDeclaration td = cds.Serialize(manager, root) CodeTypeDeclaration;
编译与生成
要编译的话,就必须传入数据CodeCompileUnit,然后使用CsharpCodeProvider, VBCodeProvider就可以生成相应代码,也就是说,在设计的时候,不必要先指定使用何种语言,在设计完Form之后,再选定就可以了。因为,Form设计器最后产生的,是CodeCompileUnit,而不是代码。 在SampleDesignerLoader.cs类上,也提供了Build方法,这就是生成DLL或EXE文件,因为在Windows平台上,可以正常使用类集合的,就只要用这两个扩展名。只要使用CompilerParameters类和当前的程序类,传入CodeCompileUnit就可以了。之后,就只是生成选择了。详情请查看Build方法