C#利用DesignSurface如何實現簡單的窗體設計器。本站提示廣大學習愛好者:(C#利用DesignSurface如何實現簡單的窗體設計器)文章只能為提供參考,不一定能成為您想要的結果。以下是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其實最簡單的就是讀取源代碼,然後返回就可以了。當設計器添加控件或者綁定事件時,可以通過文本操作進行代碼完善。
//直接返回代碼,最簡單 public string GetTextCSCode() { Flush(); return __CSTextCode; }
CodeDomHostLoader類中有OnComponentRename,在設計器重命名組件時候響應,這裡可以修復後台.cs中的控件引用
但此設計器還有很多不完善的地方,後期有時間再完善吧。
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作能帶來一定的幫助,如果有疑問大家可以留言交流。
[db:作者簡介][db:原文翻譯及解析]