如何自定义InitializeComponent的代码生成? 更具体地说,如何对所有生成的代码进行后处理?
我正在尝试为InitializeComponent
自定义Windows窗体设计器的代码生成。 MSDN文章“在.NET Framework视觉设计器中自定义代码生成”包含“控制代码生成”部分 ,该部分解释了如何执行此操作的基础知识。
我在上面的文章中密切关注了一个例子:
//using System.ComponentModel.Design.Serialization; class SomeFormSerializer : CodeDomSerializer { public override object Serialize(IDesignerSerializationManager manager, object value) { // first, let the default serializer do its work: var baseSerializer = (CodeDomSerializer)manager.GetSerializer( typeof(Form).BaseType, typeof(CodeDomSerializer)); object codeObject = baseSerializer.Serialize(manager, value); // then, modify the generated CodeDOM -- add a comment as the 1st line: if (codeObject is CodeStatementCollection) { var statements = (CodeStatementCollection)codeObject; statements.Insert(0, new CodeCommentStatement("CODEDOM WAS HERE")); } // finally, return the modified CodeDOM: return codeObject; } }
现在我把它连接到我的表单SomeForm
:
[DesignerSerializer(typeof(SomeFormSerializer), typeof(CodeDomSerializer))] class SomeForm : Form { … }
然后,Forms Designer可能会生成以下InitializeComponent
代码:
private void InitializeComponent() { … /* (general setup code, such as a call to `this.SuspendLayout`) */ // // someButton // … /* (someButton's properties are set) */ // CODEDOM WAS HERE! // // SomeForm // … /* (form's properties are set) */ … /* (general setup code, such as a call to `this.ResumeLayout`) */ }
请注意,注释// CODEDOM WAS HERE
未添加为InitializeComponent
中的第一行,而只是作为处理表单对象本身属性的代码块的第一行。
如果我想能够修改整个方法的生成CodeDOM,而不仅仅是处理特定对象的部分,我该怎么办?
背景:为什么我要这样做? 在Windows窗体中,如果在数据绑定期间需要灵活的值转换,通常必须求助于订阅某些特定
Binding
对象的Format
和Parse
事件。 所以我正在创建一个专门的Binding
子类(让我们称之为ConvertingBinding
),这简化了这个过程。现在,问题是当在Windows窗体设计器中设置数据绑定时,生成的代码会创建
Binding
实例; 但是,我希望设计者能够实例化我的专用子类。 我当前的方法是让设计者首先创建一个CodeDOM树,然后遍历该树并通过ConvertingBinding
的实例化替换Binding
的所有实例化。
您需要创建两个Form
类。 带有DesignerSerializerAttribute
First Form
。 第二种Form
是第一种Form
的后代。 之后,您可以为第二个Form
及其控件或组件自定义InitializeComponent()
。 为此,您应该使用manager.Context
来获取包含Form
控件的序列化代码的所有StatementContext
和CodeStatementCollection
对象。
这是一些简单的步骤。
包括库:
using System.CodeDom; using System.ComponentModel.Design.Serialization; using System.Collections;
创建新表单并添加DesignerSerializerAttribute
:
[DesignerSerializer(typeof(CustomFormSerializer), typeof(CodeDomSerializer))] class CustomForm : Form { … }
创建CustomForm
后代并向其添加一些控件或组件:
class CustomForm1 : CustomForm { … }
将方法添加到CustomFormSerializer
以处理CodeStatementCollection
,例如:
private void DoSomethingWith(CodeStatementCollection statements) { statements.Insert(0, new CodeCommentStatement("CODEDOM WAS HERE")); }
在Serialize
方法中使用循环通过manager.Context
:
public override object Serialize(IDesignerSerializationManager manager, object value) { //Cycle through manager.Context for (int iIndex = 0; manager.Context[iIndex] != null; iIndex++) { object context = manager.Context[iIndex]; if (context is StatementContext) // Get CodeStatementCollection objects from StatementContext { ObjectStatementCollection objectStatementCollection = ((StatementContext)context).StatementCollection; // Get each entry in collection. foreach (DictionaryEntry dictionaryEntry in objectStatementCollection) // dictionaryEntry.Key is control or component contained in CustomForm descendant class // dictionartEntry.Value is CodeDOM for this control or component if (dictionaryEntry.Value is CodeStatementCollection) DoSomethingWith((CodeStatementCollection)dictionaryEntry.Value); } //Do something with each collection in manager.Context: if (context is CodeStatementCollection) DoSomethingWith((CodeStatementCollection)context); } // Let the default serializer do its work: CodeDomSerializer baseClassSerializer = (CodeDomSerializer)manager. GetSerializer(value.GetType().BaseType, typeof(CodeDomSerializer)); object codeObject = baseClassSerializer.Serialize(manager, value); // Then, modify the generated CodeDOM: if (codeObject is CodeStatementCollection) DoSomethingWith((CodeStatementCollection)codeObject); // Finally, return the modified CodeDOM: return codeObject; }