C#用DesignSurface實現一個簡單的窗體設計器

轉自:http://www.cnblogs.com/isaboy/p/DesignSurface.html



  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中的控件引用

 

 但此設計器還有很多不完善的地方,後期有時間再完善吧


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章