2012-04-22 12 views
9

Sto cercando di personalizzare la generazione del codice di Windows Forms Designer per InitializeComponent. L'articolo MSDN "Customizing Code Generation in the .NET Framework Visual Designers" contiene una sezione "Controlling Code Generation" che spiega le basi di come ciò può essere fatto.Come posso personalizzare la generazione del codice di InitializeComponent? Più specificamente, come posso post-elaborare tutto il codice generato?

ho seguito da vicino un esempio nell'articolo di cui sopra:

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

Ora collego questo fino alla mia forma SomeForm:

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

La Progettazione form potrebbe quindi generare il codice seguente 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`) */ 
} 

Si noti che il comme nt // CODEDOM WAS HERE non è stato aggiunto come prima riga in InitializeComponent, ma solo come prima riga del blocco di codice che gestisce le proprietà dell'oggetto modulo stesso.

Cosa dovrei fare se volessi essere in grado di modificare il CodeDOM generato dell'intero metodo, e non solo della parte che riguarda un oggetto specifico?

Contesto: Perché voglio farlo? In Windows Form, se si desidera la conversione del valore flessibile durante l'associazione dei dati, in genere si deve ricorrere agli abbonamenti agli eventi Format e Parse di un particolare oggetto Binding. Quindi sto creando una sottoclasse specializzata Binding (chiamiamola ConvertingBinding) che semplifica questo processo un po '.

Ora, il problema è che quando i collegamenti dati sono impostati in Progettazione Windows Form, il codice generato crea istanze di Binding; tuttavia, vorrei che il progettista istanziasse invece la mia sottoclasse specializzata. Il mio attuale approccio è quello di consentire al progettista di creare prima un albero CodeDOM, quindi di camminare su quell'albero e sostituire tutte le istanze di Binding con le istanze di ConvertingBinding.

risposta

11

È necessario creare due classi Form. Primo Form con un DesignerSerializerAttribute. Il secondo Form è discendente dal primo. Dopodiché puoi personalizzare Form per il secondo Form e controlli o componenti. Per questo è necessario utilizzare manager.Context per ottenere tutti gli oggetti StatementContext e CodeStatementCollection che contiene il codice serializzato dei comandi di Form.

Ecco alcuni semplici passaggi.
includono librerie:

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

Creare nuovo modulo e aggiungere DesignerSerializerAttribute:

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

Creare CustomForm discendente e aggiungere alcuni controlli o componenti ad esso:

class CustomForm1 : CustomForm { … } 

Aggiungi metodo per CustomFormSerializer per elaborazione CodeStatementCollection, ad esempio:

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

In Serialize metodo ciclo di utilizzo attraverso 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

Questo è quasi quello che voglio. Questo sembra sufficiente per sostituire le istanze di 'Binding'; ma non riesco ancora ad accedere all'intero metodo, ad esempio le prime poche righe e le ultime poche righe in "InitializeComponent". – stakx

+1

@stakx: le prime righe di 'InitializeComponent' sono sempre costituite da oggetti' CodeVariableDeclarationStatement'. Se apporti modifiche a uno di questi oggetti in 'CodeStatementCollection' o aggiungi un nuovo' CodeVariableDeclarationStatement' alla raccolta, tutte queste modifiche appariranno nelle prime righe. Qualsiasi altra riga 'CodeStatement' apparirà dopo tutte le righe' CodeVariableDeclarationStatement'. – nempoBu4

Problemi correlati