C#利用DesignSurface如何实现简单的窗体设计器
程序员文章站
2022-06-20 16:09:44
system.componentmodel.design.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中的控件引用
但此设计器还有很多不完善的地方,后期有时间再完善吧。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。