C#用DesignSurface实现一个简单的窗体设计器

System.ComponentModel.Design.DesignSurface是为设计组件提供一个用户界面,通过它可以实现一个简单的窗体设计器。

在构建之前,我们需要引入System.Design.dll,否则会出现找不到DesignSurface的错误。

 1         private void Form1_Load(object sender, EventArgs e) 2         { 3            //引用System.Deisgn.dll 4            DesignSurface ds = new DesignSurface(); 5             //开始加载窗体 6             ds.BeginLoad(typeof(Form)); 7             Control designerContorl = (Control)ds.View; 8             designerContorl.Dock = DockStyle.Fill; 9             this.Controls.Add(designerContorl);10         }

运行后出现简单的一个UI设计器

但是该设计器并不能实现控件拖放和UI设计器,以及控件的属性配置。

为了支持从源代码加载初始化窗体,需要对源码中的相关方法进行解析,这里我们 CodeDomDesignerLoader来实现定制化业务,CodeDomDesignerLoader是提供用于实现基于 CodeDOM 的设计器加载程序的基类。

继承它的类需要重写CodeCompileUnit Parse()方法,来实现加载窗体:

 1         protected override CodeCompileUnit Parse() 2         { 3           4             #region 源文件读取 5             var sw = new StreamReader(@"E:\FrmUser.cs"); 6             var sw_designer = new StreamReader(@"E:\FrmUser.Designer.cs"); 7  8             string formCodeCS = sw.ReadToEnd(); 9             string formCodeDesigner = sw_designer.ReadToEnd();10 11             List<string> source = new List<string>();12             source.Add(formCodeCS);13             source.Add(formCodeDesigner);14 15             #endregion16             //Rolsyn解析C#17             var rootDesigner = Source2CodeDom.Parse(formCodeDesigner);18             codeDesingerCompileUnit = Source2CodeDom.GetDesignerCodeComplieUnit(rootDesigner);19             var rootCS = Source2CodeDom.Parse(formCodeCS);20             codeCSCompileUnit = Source2CodeDom.GetCodeComplieUnit(rootCS);21             //MergeFormSource22             string mergeS = Source2CodeDom.MergeFormSource(formCodeDesigner, formCodeCS);23             codeMergeCompileUnit = Source2CodeDom.GetMergeDesignerCodeComplieUnit(mergeS);24             return codeMergeCompileUnit;

解析的方法如下,但是此解析只是用于代码的生成,并不能用户UI界面的显示:

  1        public static CodeCompileUnit GetDesignerCodeComplieUnit2(CompilationUnitSyntax root)  2         {  3             CodeCompileUnit ccu = new CodeCompileUnit();  4             var firstMember = root.Members[0];  5             var namespaceDeclration = (NamespaceDeclarationSyntax)firstMember;  6             var designClassDeclaration = (ClassDeclarationSyntax)namespaceDeclration.Members[0];  7             var myDesignerClass = new CodeTypeDeclaration(designClassDeclaration.Identifier.ToString());  8             var initializeComponent = new CodeMemberMethod();  9             var ns = new CodeNamespace(namespaceDeclration.Name.ToString()); 10  11             foreach (var m in designClassDeclaration.Members) 12             { 13  14                 if (m is ConstructorDeclarationSyntax) 15                 { 16                     var ctor = ((ConstructorDeclarationSyntax)m); 17                     var codeBody = ctor.Body.ToString(); 18                     codeBody = codeBody.Trim().TrimStart(‘{‘).TrimEnd(‘}‘).Trim().TrimEnd(‘;‘); 19                     CodeSnippetExpression csbody = new CodeSnippetExpression(codeBody); 20                     CodeExpressionStatement stmt = new CodeExpressionStatement(csbody); 21                     //Add the expression statements to the method. 22                     // InitializeComponent 23                     var cctor = new CodeConstructor(); 24                     cctor.Name = ctor.Identifier.ToString(); 25                     //var cmm = new CodeMemberMethod(); 26                     //cmm.Name = ctor.Identifier.ToString(); 27                     //cmm.Attributes = GetCtoRAttrMapping(ctor); 28                     //cmm.ReturnType = new CodeTypeReference(typeof(void)); 29                     cctor.Statements.Add(stmt); 30  31                     myDesignerClass.Members.Add(cctor); 32                 } 33                 if (m is FieldDeclarationSyntax) 34                 { 35                     var F = ((FieldDeclarationSyntax)m); 36                     var type = F.Declaration.Type; 37                     foreach (var variable in F.Declaration.Variables) 38                     { 39                         var field = new CodeMemberField(); 40                         field.Name = variable.Identifier.ToString(); 41                         field.Type = new CodeTypeReference(type.ToString()); 42                         field.Attributes = GetFieldAttrMapping(F); 43                         //field.InitExpression = new CodePrimitiveExpression(null); 44                         myDesignerClass.Members.Add(field); 45                     } 46                 } 47                 if (m is MethodDeclarationSyntax) 48                 { 49                     var node = m as MethodDeclarationSyntax; 50                     #region xml comments 51                     var xmlTrivia = node.GetLeadingTrivia() 52                         .Select(i => i.GetStructure()) 53                         .OfType<DocumentationCommentTriviaSyntax>() 54                         .FirstOrDefault(); 55  56         57  58                     #endregion 59  60  61  62                     var method = (MethodDeclarationSyntax)m; 63  64                     var cmm = new CodeMemberMethod(); 65                     cmm.Name = method.Identifier.ToString(); 66  67  68  69                     ///XML注释 70                     string[] comments = xmlTrivia.ToString().Split("\r\n".ToCharArray()); 71                     foreach (string text in comments) 72                     { 73                         if (text.Trim() != "") 74                         { 75                             cmm.Comments.Add(new CodeCommentStatement(text.Trim().TrimStart("///".ToCharArray()).Trim(), true)); 76                         } 77                     } 78  79  80  81                     if (cmm.Name == "InitializeComponent") 82                     { 83                         //region  84                         CodeRegionDirective codeRegion = new CodeRegionDirective(CodeRegionMode.Start, "Windows 窗体设计器生成的代码"); 85                         CodeRegionDirective codeEndRegion = new CodeRegionDirective(CodeRegionMode.End, ""); 86  87                         cmm.StartDirectives.Add(codeRegion); 88                         cmm.EndDirectives.Add(codeEndRegion); 89                     } 90  91                     //MemberAttributes.Family is protected 92                     //cmm.Attributes = MemberAttributes.Override | MemberAttributes.Family; 93                     cmm.Attributes = GetMethodAttrMapping(method); 94                     cmm.ReturnType = new CodeTypeReference(method.ReturnType.ToString()); 95  96                     foreach (var p in method.ParameterList.Parameters) 97                     { 98                         CodeParameterDeclarationExpression cpd = new CodeParameterDeclarationExpression(); 99                         cpd.Name = p.Identifier.ToString();100 101                         cpd.Type = new CodeTypeReference(p.Type.ToString());102 103                         cmm.Parameters.Add(cpd);104                     }105                     //包含方法{};,会重复生成{};106                     string codeBody = method.Body.ToString();107                     codeBody = codeBody.Trim().TrimStart(‘{‘).TrimEnd(‘}‘).Trim().TrimEnd(‘;‘);108                     if (codeBody != "")109                     {110                         CodeSnippetExpression csbody = new CodeSnippetExpression(codeBody);111                         CodeExpressionStatement stmt = new CodeExpressionStatement(csbody);112                         //Add the expression statements to the method.113                         cmm.Statements.Add(stmt);114                     }115                     myDesignerClass.Members.Add(cmm);116 117                 }118                 if (m is MemberDeclarationSyntax)119                 {120 121                 }122             }123 124             ccu.Namespaces.Add(ns);125 126             //Partial Class127             myDesignerClass.IsPartial = true;128    129 130             ns.Types.Add(myDesignerClass);131 132           133 134             return ccu;135         }

窗体的显示,需要逐句进行C#解析,特别是InitializeComponent()方法。

.CS Code其实最简单的就是读取源代码,然后返回就可以了。当设计器添加控件或者绑定事件时,可以通过文本操作进行代码完善。

1 //直接返回代码,最简单2  public string GetTextCSCode()3  {4        Flush();5        return __CSTextCode;6 }

CodeDomHostLoader类中有OnComponentRename,在设计器重命名组件时候响应,这里可以修复后台.cs中的控件引用

但此设计器还有很多不完善的地方,后期有时间再完善吧。

时间: 2024-12-10 12:28:30

C#用DesignSurface实现一个简单的窗体设计器的相关文章

C#如何实现一个简单的流程图设计器

以前看过不少Window Form开发的流程图设计器,支持节点拖放,非常方便即可设计出很美观的流程图,作为一个程序员,对其内部实现原理一直很好奇,感叹有朝一日自己如果可以开发一款类似的软件那是多么让人兴奋的事情呀!自从有了这样的想法,一直都在积累和学习这方面的知识,最近一个偶然的机会竟然实现了一个简单的流程图设计器(虽然其功能还有很多不完善之处,但是心中还是非常高兴,满满的成就感). 话不多说,先看一下实现的主界面效果: 左边是一个ListView(listView1),右边的画布是一个Pane

解析大型.NET ERP系统核心组件 查询设计器 报表设计器 窗体设计器 工作流设计器 任务计划设计器

企业管理软件包含一些公共的组件,这些基础的组件在每个新项目立项阶段就必须考虑.核心的稳定不变功能,方便系统开发与维护,也为系统二次开发提供了诸多便利.比如通用权限管理系统,通用附件管理,通用查询等组件,若是在项目开发前就准备好了这些组件,为项目如期交付提供了保证. 查询设计器 Query Designer  支持选择一个或多个数据库表,通过左右连接的方式构建查询结果,支持直接手写SQL语句设计查询,支持调用存储过程查询,支持用代码设计查询. 报表设计器 Report Designer 支持配置的

C#基础系列:开发自己的窗体设计器(PropertyGrid显示中文属性名)

既然是一个窗体设计器,那就应该能够设置控件的属性,设置属性最好的当然是PropertyGrid了,我们仅仅需要使用一个PropertyGrid.SelectedObject = Control就可以搞定,让PropertyGrid显示Control的所有属性.可是这里显示的属性名是英文的.对于我们开发人员来说这无可厚非,我们也乐于接受.并且让PropertyGrid显示中文属性名,这对于我们开发人员的使用来说显得多此一举.可是,对于我这种类型的一个应用工具,英文属性名对于很多客户来说可能就很难懂

Windows 窗体设计器中的设计时错误

在用 VS.NET进行窗体设计的时候,经常会遇到这样的问题:我们需要在构造函数或者在OnLoad事件中进行自己的一些初始化操作,比如连接一个数据库.调用一个资源文件或者后期绑定一个组件.如果代码通过编译,在运行时会执行得相当如你所愿.然而,当我们用窗体设计器打开这样一个窗体或者继承的窗体,IDE环境会抛出非常令人不愉快的异常,比如(NullReferenceException ). 究其原因,主要是窗体设计器在载入窗体时会自动初始化该对象,自动执行诸如构造函数.OnLoad方法和Initiali

Winform窗体设计器上的菜单及工具栏上的图标不见恢复解决方案

前段时间有朋友问我,他在设计窗体的时候由于资源文件未找到, 运行后发现窗口中的菜单栏上没有任何菜单项,工具栏上的图标按钮也都不见了, 遇到这种情况,不要急着把该窗体的资源文.设计文件和代码文件删除,否则又要从头开始,得不偿失. 这时,首先看自己的窗体资源文件是否缺少图标文件,打个比方对于窗体BrowseQuestion, 首先打开BrowseQuestion.resx文件,如果没有缺少图标文件,则打开BrowseQuestion.designer.cs文件, 打开窗体设计器自动生成的代码并展开,

窗体设计器出不来

今天发现个问题,C# WINFORM.如果在窗体类上写一些代码.那么双击FORM就出不来设计器了.FORM变成普通类了.去掉之后正常了 在form1窗体上面写了一个类A.结果图标变成C#类的.如果双击它,打开的是类,而不是窗体设计视图.按shift+F7也无效 去掉这些代码之后,正常了.双击打开设计视图了

springboot2+activiti 7 整合(四) 用流程设计器设计一个流程

我们已常用的请假来设计一个流程. 流程设计器参见springboot2+activiti 7 整合(二) IDEA安装Activiti工作流设计器,通过在项目中右键找到External Tools -> camunda-modeler开始流程设计. 相同的业务流程,流程定义的 id 名字定义一样,比如,如果需要创建新的业务流程,请假流程则使用新的 id. 指定任务负责人:在 properties 指定每个任务结点的负责人,比如下边是填写请假单的负责人为 tom 部门经理审核的负责人是jack 行

C# 一个简单的秒表引发的窗体卡死问题

一个秒表程序也是我的一个心病,因为一直想写这样的一个东西,但是总往GUI那边想,所以就比较怵,可能是上学的时候学MFC搞出的后遗症吧,不过当我今天想好用Win Form(话说还是第一次写win form)写这么一个东西的时候,居然so easy. 所以说,做不了不可怕,怕的是你不去做,因为你不去做,你就永远不知道你能不能做它.事实证明,大部分你犹豫能不能做的事情,实际上你都能搞定. 虽然成功实现了一个秒表的简单功能,即开始计时和停止.但是却引发了一个关于win form和C#线程的问题. 下面一

一个简单的WInCE(转载百度)

VS2008中开发智能设备程序的一些总结收藏1 结合前几日开发的<全国大坝基础数据库采集端>中的PDA程序开发过程,对VS2008开发智能设备上的程序做个小总结. 1         程序结构 程序中包括四个部分: 1. 系统配置 这个部分用来配置系统中的相关参数,参数包括数据库信息和串口的配置信息.这部分的主要技术是XML文件的读取和写入. 2. 数据下载 从数据库中下载数据到PDA,PDA上的保存也是使用数据库.这部分的技术主要是PDA设备上的移动数据库开发和使及用PDA连接PC数据库 3