Как я могу настроить генерацию кода 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;
}