2012-04-22 11 views
9

Próbuję dostosować generowanie kodu programu Windows Forms Designer do wersji InitializeComponent. Artykuł MSDN "Customizing Code Generation in the .NET Framework Visual Designers" zawiera sekcję "Controlling Code Generation", która wyjaśnia podstawy, w jaki sposób można to zrobić.Jak mogę dostosować generowanie kodu InitializeComponent? Dokładniej, jak mogę postprocesować cały wygenerowany kod?

mam ściśle przestrzegać przykład w powyższym artykule:

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

Teraz hak to do mojego formularza SomeForm:

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

Projektant formularzy może następnie wygenerować następujący kod 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`) */ 
} 

Pamiętaj, że comme nt // CODEDOM WAS HERE nie został dodany jako pierwsza linia w InitializeComponent, ale tylko jako pierwsza linia bloku kodu, który zajmuje się właściwościami samego obiektu formularza.

Co powinienem zrobić, gdybym chciał móc zmodyfikować wygenerowany CodeDOM całej metody, a nie tylko części, która zajmuje się konkretnym obiektem?

Tło: Dlaczego chcę to zrobić? W Windows Forms, jeśli chce się elastycznej konwersji wartości podczas wiązania danych, zwykle trzeba uciec się do subskrypcji zdarzeń Format i Parse jakiegoś szczególnego obiektu Binding. Dlatego tworzę wyspecjalizowaną podklasę Binding (nazwijmy ją: ConvertingBinding), która nieco upraszcza ten proces.

Problem polega na tym, że po skonfigurowaniu powiązań danych w Projektancie formularzy systemu Windows wygenerowany kod tworzy instancje Binding; jednak chciałbym, aby projektant zamiast tego utworzył moją wyspecjalizowaną podklasę. Moje obecne podejście polega na umożliwieniu projektantowi najpierw utworzenia drzewa CodeDOM, a następnie chodzenia po tym drzewie i zastępowania wszystkich wystąpień Binding przez instancje z ConvertingBinding.

Odpowiedz

11

Musisz utworzyć dwie klasy Form. Najpierw Form z DesignerSerializerAttribute. Drugi Form jest potomkiem od pierwszego. Następnie możesz dostosować InitializeComponent() dla drugiego Form i jego elementów sterujących lub komponentów. W tym celu należy użyć manager.Context, aby uzyskać wszystkie obiekty StatementContext i CodeStatementCollection zawierające kod serializowany z elementów sterujących Form.

Oto kilka prostych kroków.
Dołącz biblioteki:

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

tworzyć nowe formy i dodać DesignerSerializerAttribute:

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

Tworzenie CustomForm potomka i dodać kilka elementów sterujących lub komponenty do niego:

class CustomForm1 : CustomForm { … } 

Add metoda CustomFormSerializer dla przetwarzanie CodeStatementCollection, na przykład:

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

W Serialize metoda cyklu użytkowania przez 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

To niemal co chcę. Wydaje się, że wystarczy zastąpić instancje 'Binding'; ale nadal nie mogę uzyskać dostępu do całej metody, tj. pierwszych kilku wierszy i ostatnich kilku wierszy w 'InitializeComponent'. – stakx

+1

@stakx: Pierwsze kilka wierszy w 'InitializeComponent' składa się zawsze z obiektów' CodeVariableDeclarationStatement'. Jeśli wprowadzisz zmiany do któregokolwiek z tych obiektów w 'CodeStatementCollection' lub dodasz nowe" CodeVariableDeclarationStatement "do kolekcji, wszystkie te zmiany pojawią się w pierwszych kilku wierszach. Każda inna linia 'CodeStatement' pojawi się po wszystkich liniach' CodeVariableDeclarationStatement'. – nempoBu4

Powiązane problemy