-->

我怎么可以自定义代码生成的InitializeComponent的? 更具体地讲,我怎么能后处理

2019-07-29 02:58发布

我想自定义Windows窗体设计器的代码生成InitializeComponent 。 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 { … }

然后,窗体设计器可能会生成以下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窗体,如果在数据绑定要灵活转换价值,人们通常不得不诉诸订阅的FormatParse一些特定的事件Binding对象。 所以我创建一个专门的Binding子类(姑且称之为ConvertingBinding ,简化了这一过程略)。

现在,问题是,当数据绑定在Windows窗体设计器都设置,生成的代码创建的实例Binding ; 不过,我想设计实例我的专业,而不是子类。 我目前的做法是,让设计人员首先创建一个CodeDOM的树,然后走那棵树和替换的所有实例Binding通过实例ConvertingBinding

Answer 1:

您需要创建两个Form类。 一是FormDesignerSerializerAttribute 。 第二个Form是从子孙第一。 之后,你可以自定义InitializeComponent()的第二个Form和它的控件或组件。 为此,您应该使用manager.Context让所有StatementContextCodeStatementCollection包含的序列化对象代码Form的控制。

下面是一些简单的步骤。
包括库:

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;
}


文章来源: How can I customize the code generation of InitializeComponent? More specifically, how can I post-process all of the generated code?