Quino Projekt-Template für Visual Studio 2012

  Subscribe
12/7/2012 - urs (updated on 9/18/2020)

Einleitung

Visual Studio bietet die Möglichkeit, zusätzlich zu den von Haus aus mitgelieferten Projekt-Templates eigene Templates zu erstellen und diese dann zu verwenden. Dies ist von Vorteil, wenn häufig ähnliche Projekte erstellt werden und das Projektsetup verhältnismässig aufwändig ist. Diese Voraussetzugen treffen auf unser hauseigenenes Framework Quino bestens zu: Für jede neue Quino Applikation muss ein Model erstellt werden, was jeweils einige Code- und Konfigurationsdateien erfordert. Dies nimmt, insbesondere wenn man es zum ersten Mal macht, schnell einige Stunden Zeit in Anspruch bis alles wie gewünscht läuft.

Mit einem Projekt-Template ist dies deutlich einfacher: Neues Projekt erstellen, die gewünschten Module wählen und schon wird eine lauffähige Quino Applikation mit einem einfachen Model erstellt. Darauf aufbauend kann man dann die eingene Applikation implementieren.

Die Erstellung eines eigenen Projekt-Templates ist aber, vor Allem wenn es etwas umfangreicher ist und zusätzlich einen eigenen Wizard haben soll, mit einigen Fallstricken versehen. Auch sind die Informationen dazu auf MSDN und generell im Internet eher spärlich und teilweise verwirrend. Um das gewonnene Know-How mit anderen Entwicklern zu teilen haben wir eine kleine Anleitung verfasst. Diese ist zwar auf Quino zugeschnitten, kann aber natürlich auch auf eigene Bedürfnisse angepasst werden.

image

Ziel

Es soll ein Projekt-Template erstellt werden, das eine lauffähige Quino Applikation generiert. Dabei sollen die Dateinamen sowie die verwendeten Namespaces dem Namen des Projektes angepasst werden. Ausserdem soll das Projekt-Template beim Generieren des Projekts einen Wizard anzeigen, in dem verschiedenen Quino Module an- oder abgewählt werden können. Momentan sind dies Core für das Model und Winforms für eine Winforms Oberfläche. Für jedes gewählte Modul wird ein Projekt in der Solution erstellt und jeweils alle benötigten Referenzen richtig gesetzt.

Vorbereitung

Diese Anleitung geht davon aus, dass Visual Studio 2012 mit allen aktuellen Updates installiert ist. Zusätzlich wird das Microsoft Visual Studio 2012 SDK benötigt. Dies stellt den Projekttyp Project Template zur Verfügung. Ausserdem ist es ratsam, ein Projekt zu erstellen das genau dem Projekt entspricht, das nachher generiert werden soll.

Template

Ein Projekt-Template ist im Grunde nichts anderes als eine Zip Datei, in welche alle benötigten Dateien gepackt sind. Von zentraler Bedeutung sind hier die .vstemplate-Dateien, von welchen jedes Projekt-Template mindestens eine beinhalten muss. Innerhalb einer .vstemplate-Datei kann dann wiederum auf andere .vstemplate-Dateien verlinkt werden um so Subprojekte zu generieren. Ausserdem sind in den .vstemplate-Dateien die Metadaten der einzelnen Projekte hinterlegt. Dies ist für die Haupt-.vstemplate-Datei besonders wichtig, da diese Daten im New 'Project Dialog' von Visual Studio angezeigt werden. Dazu gehören der Name des Projekts, eine kurze Beschreibung, ein Icon sowie eine grössere Grafik welche beispielsweise einen Screenshot oder ein Logo enthalten kann. Ausserdem sind alle Dateien enthalten, welche später in das neue Projekt eingefügt werden. Alle Dateien können mit Platzhalter versehen werden, welche dann bei der Generierung des Projekts durch die entsprechenden Werte ersetzt werden.

Dateisystem (nicht vollständig):

QuinoTemplate.vstemplate
__PreviewImage.png
__TemplateIcon.png
Core/Core.vstemplate
Core/Quino.Core.vstemplate
Core/App/QuinoConfiguration.cs
Core/Models/QuinoModelClasses.cs
...
Core/Models/Generators/QuinoCoreGenerator.cs
...
Winform/Winform.vstemplate
Winform/Quino.Winform.App.csproj
Winform/Program.cs
Winform/data-configuration.xml
...
Winform/Forms/MainForm.cs
...

QuinoTemplate.vstemplate:

<VSTemplate Version="3.0.0" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" Type="ProjectGroup">
  <TemplateData>
    <Name>Quino Application</Name>
    <Description>A quino application with different modules.</Description>
    <ProjectType>CSharp</ProjectType>
    <ProjectSubType>
    </ProjectSubType>
    <SortOrder>1000</SortOrder>
    <CreateNewFolder>true</CreateNewFolder>
    <DefaultName>MyQuinoApplication</DefaultName>
    <ProvideDefaultName>true</ProvideDefaultName>
    <LocationField>Enabled</LocationField>
    <EnableLocationBrowseButton>true</EnableLocationBrowseButton>
    <Icon>__TemplateIcon.png</Icon>
    <PreviewImage>__PreviewImage.png</PreviewImage>
  </TemplateData>
  <TemplateContent>
    <ProjectCollection>
      <ProjectTemplateLink ProjectName="$quinoapplicationname$.Core">
        Core\Core.vstemplate
      </ProjectTemplateLink>
      <ProjectTemplateLink ProjectName="$quinoapplicationname$.Winform">
        Winform\Winform.vstemplate
      </ProjectTemplateLink>
    </ProjectCollection>
  </TemplateContent>
</VSTemplate>

Die Namen der Projekte können durch Platzhalter beeinflusst werden.

Core.vstemplate (stellvertretend für die Subprojekte):

<VSTemplate Version="3.0.0" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" Type="Project">
  <TemplateData>
    <Name>Quino Core</Name>
    <Description>Provides a quino core project containing the model generation</Description>
    <ProjectType>CSharp</ProjectType>
    <ProjectSubType>
    </ProjectSubType>
    <SortOrder>1000</SortOrder>
    <CreateNewFolder>true</CreateNewFolder>
    <DefaultName>MyQuinoApplication.Core</DefaultName>
    <ProvideDefaultName>true</ProvideDefaultName>
    <LocationField>Enabled</LocationField>
    <EnableLocationBrowseButton>true</EnableLocationBrowseButton>
  </TemplateData>
  <TemplateContent>
    <Project
      TargetFileName="$quinoapplicationname$.Core.csproj"
      ProjectName="$quinoapplicationname$.Core"
      File="Quino.Core.csproj" 
      ReplaceParameters="true">
      <Folder Name="App" TargetFolderName="App">
        <ProjectItem ReplaceParameters="true" TargetFileName="$quinoapplicationname$Configuration.cs">
          QuinoConfiguration.cs
        </ProjectItem>
      </Folder>
      <Folder Name="Models" TargetFolderName="Models">
        <Folder Name="Generators" TargetFolderName="Generators">
          <ProjectItem ReplaceParameters="true" TargetFileName="$quinoapplicationname$CoreGenerator.cs">
            QuinoCoreGenerator.cs
          </ProjectItem>
          ...
        </Folder>
        <ProjectItem ReplaceParameters="true" TargetFileName="$quinoapplicationname$ModelClasses.cs">
          QuinoModelClasses.cs
        </ProjectItem>
        ...
      </Folder>
    </Project>
  </TemplateContent>
</VSTemplate>

Die Namen der generierten Dateinen können ebenfalls durch Platzhalter beeinflusst werden.

Auch innerhalb der einzelnen Codedateien können Platzhalter verwendet werden die dann beim Generieren des Projekts durch die entsprechenden Variablen erstetzt werden:

using Encodo.Quino.Meta;

namespace $quinoapplicationname$.Models
{
  public class $quinoapplicationname$ModelClasses
  {
    public IMetaClass Company { get; set; }

    public IMetaClass Person { get; set; }
  }
}

Wizard

Damit die einzelnen Quino Module an- oder abgewählt werden können sowie um dem Anwender andere Konfigurationsmöglichkeiten - wie etwa das Auswählen des Namespaces - zu geben kann man das Projekt-Template mit einem eigenen Wizard versehen. Dieser wird jedes Mal angezeigt, wenn aus dem Projekt-Template ein neues Projekt erzeugt wird. Der Wizard für das Projekt-Template ist grundsätzlich eine ganz normale .Net Anwendung welche in eine DLL kompiliert und dann im Template registriert wird.

image

Wizard erstellen

Ein eigener Wizard für das Projekt-Template kann implementiert werden indem man vom Interface Microsoft.VisualStudio.TemplateWizard.IWizard ableitet. Da das Quino Projekt-Template momentan zwei Module (Core und Winform) generieren kann werden insgesamt drei Wizards gebraucht:

  • QuinoWizard: Der Hauptwizard welcher den eigentlichen Wizard zur Verfügung stellt in dem die Anwender des Templates die gewünschten Optionen einstellen können.
  • CoreWizard, WinformWizard: Leiten die Einstellungen des QuinoWizards an das jeweilge Modul weiter.

Die beiden Sub-Wizards sind nötig, weil der Hauptwizard keinen Zugriff auf die Replace-Parameter der SubProjekt-Templates hat. Damit der Hauptwizard mit den Subwizards kommunizieren kann ist ein kleiner Trick nötig: Die im Hauptwizard getätigten Einstellungen werden in public static Parametern abgelegt auf diese wiederum die beiden Subwizards zugreifen können. Dies funktioniert, weil alle drei Wizards in der gleichen Runtime Umgebung laufen.

QuinoWizard.cs:

public class QuinoWizard : IWizard
{
  #region Implementation of IWizard

  public void RunStarted(
    object automationObject, 
    Dictionary<string, string> replacementsDictionary,
    WizardRunKind runKind,
    object[] customParams)
  {
    try
    {
      using (var inputForm = new UserInputForm())
      {
        // Der Winforms Dialog wird angezeigt und die Einstellungen des
        // Benutzers werden in public static Parametern abgelegt.
        inputForm.ShowDialog();
        GenerateCore = inputForm.GenerateCore;
        GenerateWinform = inputForm.GenerateWinform;
        QuinoApplicationName = inputForm.DefaultNamespace;
        EncodoSourceRoot = inputForm.EncodoSourceRoot;

        // Die Parameter werden in das replacementsDictionary übernommen.
        Tools.SetReplacementParameters(replacementsDictionary);
      }
    }
    catch (Exception ex)
    {
      MessageBox.Show(ex.ToString());
    }
  }

  public bool ShouldAddProjectItem(string filePath)
  {
    return true;
  }

  // Alle anderen implementierten Methoden haben einen leeren Methodenrumpf.

  #endregion

  public static string QuinoApplicationName { get; private set; }
  public static bool GenerateCore { get; private set; }
  public static bool GenerateWinform { get; private set; }
  public static string EncodoSourceRoot { get; private set; }
}

CoreWizard.cs (stellvertretend für die beiden Subwizards):

{
  #region Implementation of IWizard

  public void RunStarted(
    object automationObject, 
    Dictionary<string, string> replacementsDictionary, 
    WizardRunKind runKind, 
    object[] customParams)
  {
    if (!QuinoWizard.GenerateCore)
    {
      // So wird die Generierung des Subprojekts gegebenenfalls verhindert.
      throw new WizardCancelledException();
    }

    // Die Parameter werden in das replacementsDictionary übernommen.
    Tools.SetReplacementParameters(replacementsDictionary);
  }

  public bool ShouldAddProjectItem(string filePath)
  {
    return true;
  }

  // Alle anderen implementierten Methoden haben einen leeren Methodenrumpf.

  #endregion
}

Der Vollständigkeit halber die Methode Tools.SetReplacementParameters:

public static void SetReplacementParameters(Dictionary<string, string> replacementsDictionary)
{
  replacementsDictionary.Add("$quinoapplicationname$", QuinoWizard.QuinoApplicationName);
  replacementsDictionary.Add("$encodosourceroot$", QuinoWizard.EncodoSourceRoot);
}

Registrieren im GAC

Damit der Wizard vom Projekt-Template aufgerufen werden kann muss er im Global Assembly Cache (GAC) registriert werden. Dazu das Wizward Projekt kompilieren, den Visual Studio Command Prompt im Administratormodus öffnen und das Assembly registrieren:

gacutil -i Wizard.dll

Verlinken mit dem Projekt-Template

In den vorherigen beiden Schritten wurde für jedes Subprojekt sowie für das Hauptprojekt jeweils ein Wizard erstellt. Diese Wizwards müssen jetzt in den einzelnen .vstemplate-Dateien verlinkt werden. Dazu jeweils nach dem Knoten folgenden XML Code einfügen:

QuinoTemplate.vstemplate:

<WizardExtension>
  <Assembly>Wizard, Version=1.0.0.0, Culture=Neutral, PublicKeyToken=[PublicKey]</Assembly>
  <FullClassName>Wizard.QuinoWizard</FullClassName>
</WizardExtension>

Core.vstemplate:

<WizardExtension>
  <Assembly>Wizard, Version=1.0.0.0, Culture=Neutral, PublicKeyToken=[PublicKey]</Assembly>
  <FullClassName>Wizard.CoreWizard</FullClassName>
</WizardExtension>

Winform.vstemplate:

<WizardExtension>
  <Assembly>Wizard, Version=1.0.0.0, Culture=Neutral, PublicKeyToken=[PublicKey]</Assembly>
  <FullClassName>Wizard.WinformWizard</FullClassName>
</WizardExtension>

Der Public Key ist in allen drei Fällen gleich und kann beispielsweise mit Tools wie dotPeek von JetBrains ermittelt werden. Der FullClassName hingegen muss auf den jeweiligen Wizard des Templates verweisen.

Installation

Um das fertige Template zu testen bietet Visual Studio einige praktische Hilfsmittel. So kann kann man einfach F5 drücken und eine neue Instanz von Visual Studio wird hochgezogen in der das neue Template bereits registriert ist. Dies bietet den Vorteil, dass man die Generierung der Projekte debuggen kann und so etwaige Fehler einfach findet.

Wenn das Template fertig ist und funktioniert kann man das Template im Release-Modus builden woraufhin ein Zip-File mit dem Projekt-Template generiert wird. Dieses kann man dann entweder in den Ordner %UserProfile%\Documents\Visual Studio 2012\Templates\ProjectTemplates\Visual C# kopieren wo das Projekt für den aktuellen Benutzer zur Verfügung steht oder in den Ordner %ProgramFiles%\Microsoft Visual Studio 11.0\Common7\IDE\ProjectTemplates\CSharp woraufhin das neue Projekt-Template dann bei allen Benutzern im New Project Dialog erscheint.

Fazit

Das Erstellen eines eigenen Projekt-Templates ist insbesondere für Frameworkentwickler eine gute Möglichkeit, den Umgang mit dem Framework zu erleichtern. Der Anwender kann so mit wenigen Mausklicks eine funktionsfähige Applikation erstellen und sieht auch gleich die grundlegenden Designpatterns. Dies ermöglicht es ihm dann, die Anwendung nach seinen eigenen Bedürfnissen zu erweitern.

Natürlich bedeutet das Erstellen und Unterhalten eines eigenen Projekt-Templates auch einigen Aufwand, da das Template für jede neue Quino Version wieder geprüft und gegebenenfalls angepasst werden muss. Hier bietet es sich an, diesen Prozess zu automatisieren. Das Projekt-Template kann auf einem Buildserver vollautomatisch gebuildet und daraus dann ein Projekt generiert werden. Dieses kann der Buildserver dann wiederum builden und so prüfen, ob das Projekt-Template noch funktionsfähig ist.

Um die Installation des Templates auf verschiedenen Rechnern zu vereinfachen haben wir zusätzlich ein Installationsprogramm erstellt der den Wizard im GAC registriert und das Projekt-Template ins richtige Verzeichnis kopiert.

Insgesamt war das Erstellen des Projekt-Templates für Quino zwar aufwändig, ich würde aber auf jeden Fall sagen, dass der dadurch gewonnene Nutzen den Aufwand mehr als wett macht.

Sign up for our Newsletter