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


  System.ComponentModel.Design.DesignSurface是為設計組件提供一個用戶界面,通過它可以實現一個簡單的窗體設計器。

    在構建之前,我們需要引入System.Design.dll,否則會出現找不到DesignSurface的錯誤。

 

 

private void Form1_Load(object sender, EventArgs e)
        {
           //引用System.Deisgn.dll
           DesignSurface ds = new DesignSurface();
            //開始加載窗體
            ds.BeginLoad(typeof(Form));
            Control designerContorl = (Control)ds.View;
            designerContorl.Dock = DockStyle.Fill;
            this.Controls.Add(designerContorl);
        }


運行后出現簡單的一個UI設計器

 

 

但是該設計器並不能實現控件拖放和UI設計器,以及控件的屬性配置。

為了支持從源代碼加載初始化窗體,需要對源碼中的相關方法進行解析,這里我們 CodeDomDesignerLoader來實現定制化業務,CodeDomDesignerLoader是提供用於實現基於 CodeDOM 的設計器加載程序的基類。

繼承它的類需要重寫CodeCompileUnit Parse()方法,來實現加載窗體:

protected override CodeCompileUnit Parse()
        {

            #region 源文件讀取
            var sw = new StreamReader(@"E:\FrmUser.cs");
            var sw_designer = new StreamReader(@"E:\FrmUser.Designer.cs");

            string formCodeCS = sw.ReadToEnd();
            string formCodeDesigner = sw_designer.ReadToEnd();

            List<string> source = new List<string>();
            source.Add(formCodeCS);
            source.Add(formCodeDesigner);

            #endregion
            //Rolsyn解析C#
            var rootDesigner = Source2CodeDom.Parse(formCodeDesigner);
            codeDesingerCompileUnit = Source2CodeDom.GetDesignerCodeComplieUnit(rootDesigner);
            var rootCS = Source2CodeDom.Parse(formCodeCS);
            codeCSCompileUnit = Source2CodeDom.GetCodeComplieUnit(rootCS);
            //MergeFormSource
            string mergeS = Source2CodeDom.MergeFormSource(formCodeDesigner, formCodeCS);
            codeMergeCompileUnit = Source2CodeDom.GetMergeDesignerCodeComplieUnit(mergeS);
            return codeMergeCompileUnit;

解析的方法如下,但是此解析只是用於代碼的生成,並不能用戶UI界面的顯示:

public static CodeCompileUnit GetDesignerCodeComplieUnit2(CompilationUnitSyntax root)
        {
            CodeCompileUnit ccu = new CodeCompileUnit();
            var firstMember = root.Members[0];
            var namespaceDeclration = (NamespaceDeclarationSyntax)firstMember;
            var designClassDeclaration = (ClassDeclarationSyntax)namespaceDeclration.Members[0];
            var myDesignerClass = new CodeTypeDeclaration(designClassDeclaration.Identifier.ToString());
            var initializeComponent = new CodeMemberMethod();
            var ns = new CodeNamespace(namespaceDeclration.Name.ToString());

            foreach (var m in designClassDeclaration.Members)
            {

                if (m is ConstructorDeclarationSyntax)
                {
                    var ctor = ((ConstructorDeclarationSyntax)m);
                    var codeBody = ctor.Body.ToString();
                    codeBody = codeBody.Trim().TrimStart('{').TrimEnd('}').Trim().TrimEnd(';');
                    CodeSnippetExpression csbody = new CodeSnippetExpression(codeBody);
                    CodeExpressionStatement stmt = new CodeExpressionStatement(csbody);
                    //Add the expression statements to the method.
                    // InitializeComponent
                    var cctor = new CodeConstructor();
                    cctor.Name = ctor.Identifier.ToString();
                    //var cmm = new CodeMemberMethod();
                    //cmm.Name = ctor.Identifier.ToString();
                    //cmm.Attributes = GetCtoRAttrMapping(ctor);
                    //cmm.ReturnType = new CodeTypeReference(typeof(void));
                    cctor.Statements.Add(stmt);

                    myDesignerClass.Members.Add(cctor);
                }
                if (m is FieldDeclarationSyntax)
                {
                    var F = ((FieldDeclarationSyntax)m);
                    var type = F.Declaration.Type;
                    foreach (var variable in F.Declaration.Variables)
                    {
                        var field = new CodeMemberField();
                        field.Name = variable.Identifier.ToString();
                        field.Type = new CodeTypeReference(type.ToString());
                        field.Attributes = GetFieldAttrMapping(F);
                        //field.InitExpression = new CodePrimitiveExpression(null);
                        myDesignerClass.Members.Add(field);
                    }
                }
                if (m is MethodDeclarationSyntax)
                {
                    var node = m as MethodDeclarationSyntax;
                    #region xml comments
                    var xmlTrivia = node.GetLeadingTrivia()
                        .Select(i => i.GetStructure())
                        .OfType<DocumentationCommentTriviaSyntax>()
                        .FirstOrDefault();



                    #endregion



                    var method = (MethodDeclarationSyntax)m;

                    var cmm = new CodeMemberMethod();
                    cmm.Name = method.Identifier.ToString();



                    ///XML注釋
                    string[] comments = xmlTrivia.ToString().Split("\r\n".ToCharArray());
                    foreach (string text in comments)
                    {
                        if (text.Trim() != "")
                        {
                            cmm.Comments.Add(new CodeCommentStatement(text.Trim().TrimStart("///".ToCharArray()).Trim(), true));
                        }
                    }



                    if (cmm.Name == "InitializeComponent")
                    {
                        //region
                        CodeRegionDirective codeRegion = new CodeRegionDirective(CodeRegionMode.Start, "Windows 窗體設計器生成的代碼");
                        CodeRegionDirective codeEndRegion = new CodeRegionDirective(CodeRegionMode.End, "");

                        cmm.StartDirectives.Add(codeRegion);
                        cmm.EndDirectives.Add(codeEndRegion);
                    }

                    //MemberAttributes.Family is protected
                    //cmm.Attributes = MemberAttributes.Override | MemberAttributes.Family;
                    cmm.Attributes = GetMethodAttrMapping(method);
                    cmm.ReturnType = new CodeTypeReference(method.ReturnType.ToString());

                    foreach (var p in method.ParameterList.Parameters)
                    {
                        CodeParameterDeclarationExpression cpd = new CodeParameterDeclarationExpression();
                        cpd.Name = p.Identifier.ToString();

                        cpd.Type = new CodeTypeReference(p.Type.ToString());

                        cmm.Parameters.Add(cpd);
                    }
                    //包含方法{};,會重復生成{};
                    string codeBody = method.Body.ToString();
                    codeBody = codeBody.Trim().TrimStart('{').TrimEnd('}').Trim().TrimEnd(';');
                    if (codeBody != "")
                    {
                        CodeSnippetExpression csbody = new CodeSnippetExpression(codeBody);
                        CodeExpressionStatement stmt = new CodeExpressionStatement(csbody);
                        //Add the expression statements to the method.
                        cmm.Statements.Add(stmt);
                    }
                    myDesignerClass.Members.Add(cmm);

                }
                if (m is MemberDeclarationSyntax)
                {

                }
            }

            ccu.Namespaces.Add(ns);

            //Partial Class
            myDesignerClass.IsPartial = true;


            ns.Types.Add(myDesignerClass);



            return ccu;
        }

 窗體的顯示,需要逐句進行C#解析,特別是InitializeComponent()方法。

 

 

 

 CS Code其實最簡單的就是讀取源代碼,然后返回就可以了。當設計器添加控件或者綁定事件時,可以通過文本操作進行代碼完善。

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

CodeDomHostLoader類中有OnComponentRename,在設計器重命名組件時候響應,這里可以修復后台.cs中的控件引用

 

 

 

 

 

 

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM