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 #endregion 16 //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 //MergeFormSource 22 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 Class 127 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中的控件引用
但此设计器还有很多不完善的地方,后期有时间再完善吧。
水平有限,望各位园友不吝赐教!如果觉得不错,请点击推荐和关注!
出处:http://www.cnblogs.com/isaboy/
声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
出处:http://www.cnblogs.com/isaboy/
声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。