Как я могу настроить генерацию кода InitializeComponent? Более конкретно, как я могу постобработать весь сгенерированный код?

Я пытаюсь настроить генерацию кода в Windows Forms Designer для 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 Forms, если требуется гибкое преобразование значений во время привязки данных, обычно приходится прибегать к подписке на Format а также Parse события некоторых конкретных Binding объект. Поэтому я создаю специализированный Binding подкласс (давайте назовем это ConvertingBinding), что немного упрощает этот процесс.

Теперь проблема заключается в том, что когда привязки данных устанавливаются в конструкторе Windows Forms, сгенерированный код создает экземпляры Binding; тем не менее, я бы хотел, чтобы разработчик создал экземпляр моего специализированного подкласса. Мой текущий подход состоит в том, чтобы позволить разработчику сначала создать дерево CodeDOM, затем пройтись по этому дереву и заменить все экземпляры Binding по экземплярам ConvertingBinding,

1 ответ

Решение

Вам нужно создать два Form учебный класс. Первый Form с DesignerSerializerAttribute, второй Form является потомком от первого. После этого вы можете настроить InitializeComponent() на секунду Form и это элементы управления или компоненты. Для этого вы должны использовать manager.Context чтобы получить все StatementContext а также CodeStatementCollection объекты, которые содержат сериализованный код 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;
}
Другие вопросы по тегам