2012-04-22 13 views
9

Estoy tratando de personalizar la generación de código del Diseñador de Windows Forms para InitializeComponent. El artículo "Customizing Code Generation in the .NET Framework Visual Designers" de MSDN contiene una sección "Controlling Code Generation" que explica los conceptos básicos de cómo se puede hacer esto.¿Cómo puedo personalizar la generación de código de InitializeComponent? Más específicamente, ¿cómo puedo postprocesar todo el código generado?

He seguido de cerca un ejemplo en el artículo anterior:

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

Ahora conectar esto a mi forma SomeForm:

[DesignerSerializer(typeof(SomeFormSerializer), typeof(CodeDomSerializer))] 
class SomeForm : Form { … } 

El Diseñador de formularios podría entonces generar el siguiente código 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`) */ 
} 

Tenga en cuenta que el comme nt // CODEDOM WAS HERE no se agregó como la primera línea en InitializeComponent, pero solo como la primera línea del bloque de código que trata con las propiedades del objeto de formulario en sí.

¿Qué tendría que hacer si quisiera poder modificar el CodeDOM generado de todo el método, y no solo de la parte que trata con un objeto específico?

Antecedentes: ¿Por qué quiero hacer esto? En Windows Forms, si se quiere una conversión de valor flexible durante el enlace de datos, generalmente se debe recurrir a la suscripción a los eventos Format y Parse de algún objeto particular Binding. Así que estoy creando una subclase especializada Binding (llamémosla ConvertingBinding) que simplifica un poco este proceso.

Ahora, el problema es que cuando los enlaces de datos se configuran en el Diseñador de Windows Forms, el código generado crea instancias de Binding; sin embargo, me gustaría que el diseñador instanciara mi subclase especializada en su lugar. Mi enfoque actual es permitir que el diseñador cree primero un árbol CodeDOM, luego recorra ese árbol y reemplace todas las instancias de Binding por instanciaciones de ConvertingBinding.

Respuesta

11

Necesita crear dos clases Form. Primero Form con un DesignerSerializerAttribute. El segundo Form es descendiente del primero. Después de eso, puede personalizar InitializeComponent() para el segundo Form y sus controles o componentes. Para esto, debe usar manager.Context para obtener todos los objetos StatementContext y CodeStatementCollection que contengan códigos serializados de los controles Form.

Aquí hay algunos pasos simples.
incluyen bibliotecas:

using System.CodeDom; 
using System.ComponentModel.Design.Serialization; 
using System.Collections; 

Crear nuevo formulario y agregue DesignerSerializerAttribute:

[DesignerSerializer(typeof(CustomFormSerializer), typeof(CodeDomSerializer))] 
class CustomForm : Form { … } 

Crear CustomForm descendiente y añadir algunos controles o componentes a la misma:

class CustomForm1 : CustomForm { … } 

Agregar método para CustomFormSerializer de procesamiento CodeStatementCollection, por ejemplo:

private void DoSomethingWith(CodeStatementCollection statements) 
{ 
    statements.Insert(0, new CodeCommentStatement("CODEDOM WAS HERE")); 
} 

En Serialize ciclo de uso del método a través 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; 
} 
+0

Esto es casi lo que quiero. Esto parece suficiente para reemplazar las instancias 'Binding'; pero todavía no puedo acceder a todo el método, es decir, las primeras líneas y las últimas líneas en 'InitializeComponent'. – stakx

+1

@stakx: las primeras líneas en 'InitializeComponent' siempre están compuestas de objetos' CodeVariableDeclarationStatement'. Si realiza cambios en cualquiera de estos objetos en 'CodeStatementCollection' o agrega un nuevo' CodeVariableDeclarationStatement' a la colección, todos estos cambios aparecerán en las primeras líneas. Cualquier otra línea 'CodeStatement' aparecerá después de todas las líneas' CodeVariableDeclarationStatement'. – nempoBu4

Cuestiones relacionadas