Freigeben über


Dieser Artikel wurde maschinell übersetzt.

Team System

Erstellen einer Visual Studio Team Explorer-Erweiterung

Brian A. Randell und Marcel de de

Downloaden des Codebeispiels

Die Haupt-Benutzeroberfläche, die Sie, verwenden um die Interaktion mit Team Foundation Server (TFS) ist der Team Explorer-Client. Team Explorer bietet die Möglichkeit, auf Informationen zugreifen und anzeigen auf einen oder mehrere TFS-Server. Team Explorer bietet standardmäßig den Zugriff auf TFS-Funktionen, wie Elemente, SharePoint-Dokumentbibliotheken, Berichte, Builds und Quellcodeverwaltung funktionieren.

Marcel des Entwicklungsteams verwenden mehrere separate Visual Studio-Projektmappen jeweils bestehend aus fünf bis zehn Projekte. In verschiedenen Lösungen besteht ein Einzelpunkt-Bereitstellung, Wartung und Entwurf. Manchmal sind Cross-Projektmappe Abhängigkeiten. Das Team verwaltet diese Abhängigkeiten über einen speziellen Ordner in Repository der Versionskontrolle von TFS. Jede Projektmappe schreibt seine Ausgabe in einem bekannten Speicherort und das Team in der Ausgabe nach jedem Build überprüft. Eine Lösung, die die Ausgabe von einer anderen Projektmappe abhängt, kann die Assembly verweisen, beim Abrufen von TFS.

Leider konfrontiert Marcel des Teams ein Problem: Visual Studio speichert Dateiverweise mit einem relativen Pfad der aktuellen Projektmappe, aber möchten Entwickler die Flexibilität der Verwendung Ihrer eigenen Verzeichnisstrukturen und die Arbeitsbereichszuordnungen. Wenn Visual Studio-Lösungen erstellen kann, da referenzierte Dateien nicht gefunden werden kann, führt diese wünschenswert Flexibilität zu Frustration.

Eine Möglichkeit zum Beheben dieses Problems besteht darin, den Speicherort zuordnen, der die binäre Verweise als SUBST zugeordnete Laufwerke mit dem Befehl subst.exe enthält. Indem Sie auf die Assemblys auf dem Laufwerk mit SUBST zugeordnete verweisen, ist Visual Studio die Dateien in einem einheitlichen Ort finden. Entwickler können legen Sie die Dateien an den Speicherorten, die Sie bevorzugen, und dann mit subst.exe Ihre Speicherorte auf die standardmäßige Zuordnung zuordnen. Da die Dateien auf einem anderen Laufwerk befinden, speichert Visual Studio einen vollständigen Pfad, anstatt einen relativen Pfad. Ein weiterer Vorteil ist, dass ein Entwickler eine andere Version testen kann, indem Sie einfach die Zuordnung ändern.

Dieses Verfahren funktioniert, zwar noch besser wäre Erweiterung Team Explorer, die einen Entwickler zum Definieren einer Zuordnung zwischen der Version Steuerelementposition und ein zugeordnetes Laufwerk ermöglicht. Marcel des Team implementiert diese Funktionalität in eine Erweiterung von Team Explorer mit dem Namen SUBST Explorer. Sie können das Menü für die Erweiterung SUBST Explorer in Siehe Abbildung 1 angezeigt.

The Subst Explorer Loaded in the Team Explorer Window

Abbildung 1 Die SUBST-Explorer in Team Explorer-Fenster geladen

Erste Schritte

Um Ihre eigenen Team Explorer-Plug-In zu erstellen, werden Sie benötigen Visual Studio 2008 Standard Edition oder höher, zusammen Explorer und im Visual Studio 2008 SDK, das Sie von der Visual Studio-Erweiterbarkeitsentwicklercenter ( msdn.microsoft.com/vsx ) abrufen können.

Bevor Sie erstellen ein Team Explorer-Plug-In, müssen Sie einen VSPackage erstellen. Das Paket stellt eine Klasse, die in Microsoft.VisualStudio.TeamFoundation.Client.dll definierte Microsoft.TeamFoundation.Common.ITeamExplorerPlugin-Schnittstelle implementiert.

In Visual Studio-Sprachgebrauch wird Ihr plug-in einen Teil der Hierarchie. Eine Funktion, die von anderen Hierarchie Implementierungen in Visual Studio Team Explorer-Hierarchie unterscheidet besteht darin, dass Team Explorer unterstützt asynchrone Laden der Hierarchie, ist im Wesentlichen da beim Laden der Hierarchie für Dinge Arbeitsaufgaben und Dokumente häufig wie erforderlich Remoteabfragen auf TFS. Diese Aufrufe würden andernfalls Visual Studio während dieser Abfragen vor, was eine schlechte Benutzererfahrung blockieren. Die ITeamExplorerPlugin-Schnittstelle implementiert, indem ihre VSPackage stellt den Mechanismus, mit dem Team Explorer, verwendet um den Inhalt der einzelnen Knoten asynchron zu laden.

Erstellen Sie einen VSPackage, indem Sie Datei | neu | Projekt. Erweitern Sie den Knoten andere Projekttypen, und wählen Sie den Knoten "Erweiterbarkeit aus, in das Dialogfeld Neues Projekt. Wählen Sie im Bereich Vorlagen über die Visual Studio Integration Package-Vorlage.

Nachdem Sie das Dialogfeld Neues Projekt ausfüllen, und klicken Sie auf OK, startet Visual Studio Assistent für Visual Studio-Integrationspaket. Wählen Sie zunächst Ihre bevorzugte Sprache – C++, c# oder Visual Basic (C#-verwendet in diesem Artikel). Wenn Sie Visual Basic oder c# auswählen, müssen Sie die Option, um eine neue Schlüsseldatei mit starkem Namen generieren oder eine vorhandene Datei angeben. Füllen Sie auf der nächsten Seite die Informationen für Ihr Unternehmen und einige Details zum Paket, einschließlich der Paketname, Symbol, und So weiter. Viele dieser Informationen wird angezeigt, in der Hilfe | Info-Dialogfeld in Visual Studio.

Wählen Sie auf der nächsten Seite wie Visual Studio, um Ihr Paket verfügbar gemacht werden sollen. Für diesem besonderen Beispiel möchten Sie nur eine MenuCommand haben. Das plug-in wird dieses verwenden, um die Kontextmenüs zu behandeln. Auf der nächsten Seite Geben Sie einen Befehl Name und Command-ID. Übernehmen Sie die Standardeinstellungen nur, da Sie Sie später ändern müssen. Auf der nächsten Seite können Sie eine Unterstützung für Testprojekte hinzufügen. Wir wird nicht werden Sie in diesem Artikel abdeckt, so können Sie diese Auswahl aufheben, und beenden Sie den Assistenten. Der Assistent generiert die grundlegenden Klassen und Ressourcen, die Sie benötigen für die VSPackage implementieren.

Als Nächstes müssen Sie die folgenden Verweise hinzufügen, die den Zugriff auf die Team Explorer-Basisklassen verwendet, um die Team Explorer-Plug-In erstellen, bereitstellen:

Microsoft.VisualStudio.TeamFoundation.Client.(9.0.0)
Microsoft.VisualStudio.TeamFoundation (9.0.0)
Microsoft.VisualStudio.Shell (2.0.0)

Sie müssen auch die Standardverweis auf Microsoft.VisualStudio.Shell.9.0, entfernt werden, da Microsoft die Team Foundation-Assemblys für die Assemblys anstelle der Version 9.0, Version 2.0 integriert. Darüber hinaus nimmt als generiert, das Projekt es regpkg.exe-Tool verwenden, um das Paket in der Registrierung nach dem Kompilieren zu registrieren. Regpkg.exe hängt jedoch von der Shell.9.0 Assembly. Damit das Projekt in Visual Studio 2008 erstellen, müssen Sie die .proj-Datei des Projekts ändern. Sie müssen die Projektdatei entladen, und fügen Sie folgende Eigenschaften auf die Datei unter der RegisterOutputPackage-Eigenschaft:

<!-- We are 2005 compatible, and don't rely on RegPkg.exe
of VS2008 which uses Microsoft.VisualStudio.Shell.9.0 --> 
<UseVS2005MPF>true</UseVS2005MPF> 
<!-- Don't try to run as a normal user (RANA), 
create experimental hive in HKEY_LOCAL_MACHINE --> 
<RegisterWithRanu>false</RegisterWithRanu>.

Die Microsoft.VisualStudio.TeamFoundation.Client Assembly bietet einen Microsoft.TeamFoundation.Common-Namespace, der eine Basisklasse namens PluginHostPackage enthält. Verwenden Sie diese als Basisklasse für Ihr Paket. Außerdem enthält er eine Basisklasse namens BasicAsyncPlugin, die die erforderliche ITeamExplorerPlugin-Schnittstelle implementiert. Sie müssen die Standardimplementierung der generierten Klasse Paket löschen und dann von PluginHostPackage anstelle der Standard-Paket-Klasse erben.

Da die Klasse von PluginHostPackage jetzt erbt, müssen Sie nur die OnCreateService-Methode außer Kraft setzen. Diese Methode gibt eine neue Instanz einer BasicAsyncPlugin abgeleitete Klasse, die die eigentliche Implementierung-Plug-In verwaltet. Sie können die Implementierung der HostPackage des Explorers SUBST im Abbildung 2 sehen. Außerdem müssen Sie Ihr Team Explorer-Plug-In manuell zu registrieren, eine Aufgabe wir wird zu weiter unten in diesem Artikel.

Abbildung 2 SubstExplorerPackage Implementierung

...

[ProvideService(typeof(SubstExplorer))]
[PluginRegistration(Catalogs.TeamProject, "Subst explorer", typeof(SubstExplorer))]
public sealed class SubstExplorerPackage: PluginHostPackage, IVsInstalledProduct {
 private static SubstExplorerPackage _instance;
 public static SubstExplorerPackage Instance {
  get { return _instance; }
 }

 public SubstExplorerPackage () : base() {
  _instance = this;
 }
   
 protected override object OnCreateService(
  IServiceContainer container, Type serviceType) {

  if (serviceType == typeof(SubstExplorer)) {
  return new SubstExplorer();
  }
  throw new ArgumentException(serviceType.ToString());
 }
}

In Abbildung 2 sind zwei Attribute, die von besonderem Interesse für die Team Explorer-Plug-in sind. ProvideService gibt an, dass dieses Paket enthält einen Dienst, und der Diensttyp ist SubstExplorer. PluginRegistration gibt an, dass das Paket ein Team Explorer-Plug-In stellt, und dieser zusätzliche Registrierung erforderlich ist. Dieses Attribut wird von RegistrationAttribute abgeleitet und regpkg.exe normalerweise wird verarbeitet.

Knoten und Hierarchien

Wie Sie in Abbildung 2 sehen können, ist die Implementierung von OnCreateService einfach. Es gibt eine neue Instanz der SubstExplorer-Klasse, die die Implementierung der BasicAsyncPlugin-Klasse bereitstellt. Die SubstExplorer-Klasse ist verantwortlich für die Verwaltung von einen Teil der Hierarchie Team Explorer. Eine Hierarchie in Visual Studio ist eine Knotenstruktur jeder Knoten über eine Reihe von zugeordneten Eigenschaften verfügt. Beispiele für andere Hierarchien in Visual Studio sind im Projektmappen-Explorer, Server-Explorer und den Leistungs-Explorer.

Der SubstExplorer verwaltet die Plug-in-Hierarchie durch Überschreiben von zwei Methoden aufgerufen, CreateNewTree und GetNewUIHierarchy . Im Abbildung 3 sehen Sie die Implementierung der SubstExplorer-Klasse, die von BasicAsyncPlugin abgeleitet ist.

Abbildung 3 SubstExplorer Implementierung

[Guid("97CE787C-DE2D-4b5c-AF6D-79E254D83111")]
public class SubstExplorer : BasicAsyncPlugin {
 public SubstExplorer() : 
  base(MSDNMagazine.TFSPlugins.SubstExplorerHostPackage.Instance) {}

 public override String Name
 { get { return "Subst drive mappings"; } }

 public override int DisplayPriority {
  get { 
  // After team explorer build, but before any installed power tools
  // power tools start at 450
  return 400; 
  }
 }

 public override IntPtr OpenFolderIconHandle
 { get { return IconHandle; }}

 public override IntPtr IconHandle
 { get { return new Bitmap(
  SubstConfigurationFile.GetCommandImages().Images[2]).GetHicon(); } }

 protected override BaseUIHierarchy GetNewUIHierarchy(
  IVsUIHierarchy parentHierarchy, uint itemId) { 

  SubstExplorerUIHierarchy uiHierarchy = 
  new SubstExplorerUIHierarchy(parentHierarchy, itemId, this);
  return uiHierarchy;
 }

 protected override BaseHierarchyNode CreateNewTree(
  BaseUIHierarchy hierarchy) {

  SubstExplorerRoot root = 
  new SubstExplorerRoot(hierarchy.ProjectName + 
  '/' + "SubstExplorerRoot");
  PopulateTree(root);
  // add the tree to the UIHierarchy so it can handle the commands
  if (hierarchy.HierarchyNode == null)
  { hierarchy.AddTreeToHierarchy(root, true); }
  return root;
 }

 public static void PopulateTree(BaseHierarchyNode teNode) {
  string projectName = 
  teNode.CanonicalName.Substring(0, 
  teNode.CanonicalName.IndexOf("/"));
  var substNodes = 
  SubstConfigurationFile.GetMappingsForProject(projectName);
  if (substNodes != null) {
  foreach (var substNode in substNodes) {
   SubstExplorerLeaf leafNode = 
    new SubstExplorerLeaf(substNode.name, substNode.drive, 
    substNode.versionControlPath);
   teNode.AddChild(leafNode);
  }
  // (bug workaround) force refresh of icon that changed 
  // during add, to force icon refresh
  if (teNode.IsExpanded) {
   teNode.Expand(false);
   teNode.Expand(true);
  }
  }
 }
}

Die SubstExplorer-Klasse verwaltet die Erstellung einer Gruppe von Knoten der Hierarchie. Für das Paket SubstExplorer darstellen dieser Knoten die virtuelle Speicherorte, die das plug-in als ein Laufwerk zuordnen können. Jeder Knoten enthält die Eigenschaften, die benötigt werden, ein Laufwerk mit dem Befehl subst.exe zuzuordnen. Das Paket verfolgt nach Name, Laufwerkbuchstaben und Pfad (in Repository der Versionskontrolle).

Das Paket wird die Struktur in zwei Schritten erstellt. Erstens erstellt es die Handler Befehlsklasse aller Knoten der Hierarchie, besser bekannt als einer UIHierarchy zuzugreifen. Die GetNewUIHierarchy-Methode initiiert dieser Schritt. Zweitens behandelt die CreateNewTree-Methode die Erstellung der Struktur von Knoten, die virtuelle Laufwerkszuordnungen darstellen.

GetNewUIHierarchy wird vom Benutzeroberflächenthread aufgerufen und gibt eine Instanz einer Klasse, die von der BaseUIHierarchy-Basisklasse abgeleitet wird. Das Paket Implementierung finden Sie in der SubstExplorerUIHierarchy-Klasse. SubstExplorerUIHierarchy muss verarbeiten alle hinzufügen, löschen und bearbeiten Befehle ausgeführt, die aus einem der Knoten Team Explorer das Paket hinzufügt. Die ExecCommand-Methode-Klasse behandelt diese Befehle. Doch zunächst müssen Sie die Menüs und Befehle in Visual Studio zu erstellen.

In der SubstExplorer-Klasse überschreiben Sie die CreateNewTree-Methode, die von einem nicht-UI-Thread aufgerufen wird und die Struktur der Knoten, die darstellen, der alle für ein Teamprojekt konfigurierten Laufwerk Ersetzungen zurückgibt. Die Struktur beginnt immer mit einem Stammknoten, von der RootNode-Klasse abgeleitet. Für jede Definition fügen Sie einen untergeordneten Knoten in das Stammverzeichnis. Der Blattknoten enthält die Eigenschaften, die Sie ein Laufwerk zuzuordnen müssen.

Eigenschaften und Befehle

Nachdem Sie die grundlegenden Anforderungen zum Einrichten einer Team Explorer-Plug-in gesehen haben, müssen Sie einige Funktionen hinzu. Die SubstExplorerRoot-Klasse wird von der in der Microsoft.TeamFoundation.Common Assembly gefundenen RootNode-Klasse abgeleitet. Hier überschreiben Sie die Symbole, PropertiesClassName und ContexMenu Eigenschaften.

Die Symbole-Eigenschaft gibt eine ImageList aus, die die Symbole enthält, die Sie zum Anzeigen von Knoten verwenden möchten. Im Konstruktor des der RootNode müssen Sie die ImageIndex festgelegt, sodass es auf das rechte Bild in der ImageList verweist.

Das PropertiesClassName gibt eine Zeichenfolge, die den Namen, den Visual Studio im Eigenschaftenfenster Raster angezeigt wird darstellt, wenn Sie einen Knoten auswählen. Eignet sich eine beliebige Zeichenfolge, die Ihrer Meinung nach hier reicht.

Die ContextMenu-Eigenschaft gibt eine CommandID, das das Kontextmenü darstellt, das Sie anzeigen möchten. Für den Stammknoten benötigen Sie ein Kontextmenü mit einer Option, mit der Bezeichnung hinzufügen. Abbildung 4 zeigt die Implementierung von der SubstExplorerRoot.

Abbildung 4 SubstExplorerRoot

public class SubstExplorerRoot : RootNode {
 static private readonly CommandID command = 
  new CommandID(GuidList.guidPackageCmdSet, 
  CommandList.mnuAdd);

 public SubstExplorerRoot(string path) : base(path) {
  this.ImageIndex = 2;
  NodePriority = (int)TeamExplorerNodePriority.Folder;
 }

 public override System.Windows.Forms.ImageList Icons
 { get { return SubstConfigurationFile.GetCommandImages(); } }

 public override string PropertiesClassName {
  //Name of the node to show in the properties window
  get { return "Subst Explorer Root"; }
 }

 public override 
  System.ComponentModel.Design.CommandID ContextMenu
 { get { return command; } }
}

Die SubstExplorerLeaf von BaseHierarchyNode und hier abgeleitet (siehe Abbildung 5-) ist Endknoten Knotenklasse müssen Sie die Eigenschaften ContextMenu, PropertiesClassName und PropertiesObject überschreiben. Youl müssen auch eine benutzerdefinierte Implementierung der DoDefaultAction bereitstellen. Visual Studio ruft diese Methode auf, wenn Sie auf einen Endknoten doppelklicken. DoDefaultAction führt den Code, der den Befehl SUBST durchführt. Wenn Sie zuvor den SUBST-Befehl ausgeführt haben, wird die Zuordnung entfernt.

Abbildung 5 SubstExplorerLeaf

public class SubstExplorerLeaf : BaseHierarchyNode {
 private enum SubstIconId {
  unsubsted = 1,
  substed = 2
 }

 CommandID command = 
  new CommandID(GuidList.guidPackageCmdSet, 
  CommandList.mnuDelete);
 bool IsDriveSubsted { get; set; }

 public string VersionControlPath { get; set; }
 public string SubstDriveLetter { get; set; }

 public SubstExplorerLeaf(string path, 
  string substDriveLetter, string versionControlPath)
  : base(path, path + " (" + substDriveLetter + ":)") {

  this.ImageIndex = (int)SubstIconId.unsubsted;
  this.NodePriority = (int)TeamExplorerNodePriority.Leaf;
    
  this.VersionControlPath = versionControlPath;
  this.SubstDriveLetter = substDriveLetter;
  this.IsDriveSubsted = false;
 }

 public override void DoDefaultAction() {
  if (!IsDriveSubsted) {
  SubstDrive();
  }
  else {
  UnsubstDrive(SubstDriveLetter);
  }
 }

 public override CommandID ContextMenu
 { get { return command; } }

 public override string PropertiesClassName
 { get { return "Subst Leaf Node"; }}

 public override ICustomTypeDescriptor PropertiesObject {
  get {
  return new SubstExplorerProperties(this);
  }
 }

 private void SubstDrive() {
  if (IsDriveAlreadySubsted(SubstDriveLetter)) {
  UnsubstDrive(SubstDriveLetter);
  }
  string substresponse = 
  SubstHelper.Subst(SubstDriveLetter, GetLocalFolder());
 
  if (string.IsNullOrEmpty(substresponse)) {
  IsDriveSubsted = true;
  this.ImageIndex = (int)SubstIconId.substed;
  }
  else {
  MessageBox.Show(string.Format(
   "Unable to make subst mapping. Message:\n {0}", 
   substresponse));
  }
 }

 private bool IsDriveAlreadySubsted(string driveLetter) {
  bool IsdrivePhysicalyMaped = 
  SubstHelper.SubstedDrives().Where(
  d => d.Contains(driveLetter + ":\\")).Count() != 0;
  bool IsdriveKnownToBeMaped = 
  (from substedNode in _substedNodes
  where substedNode.SubstDriveLetter == driveLetter
  select substedNode).ToArray<SubstExplorerLeaf>().Length > 0;
  return IsdriveKnownToBeMaped || IsdrivePhysicalyMaped;
 }

 public void UnsubstDrive(string substDriveLetter) {
  string substResponse = SubstHelper.DeleteSubst(substDriveLetter);
  IsDriveSubsted = false;
  this.ImageIndex = (int)SubstIconId.unsubsted;
 }

 public string localPath {
  get { return VersionControlPath; }
 }
}

Die ContextMenu-Eigenschaft stellt des Kontextmenüs an der Endknoten angezeigt werden sollen. Das Kontextmenü stellt zwei Befehle zur Verfügung: Eigenschaften und löschen. In der Klasse hat die PropertiesClassName denselben Zweck wie in den Stammknoten. Verwenden Sie die PropertiesObject-Eigenschaft wieder auf ein Objekt abzurufen, das Sie verwenden können, um die Eigenschaften des ausgewählten Knotens im Eigenschaftenfenster anzuzeigen. Für die Endknoten werden die verfügbar gemachten Eigenschaften Name, DriveLetter und VersionControlPath.

Sie können eine neue Instanz des Typs SubstExplorerProperties (siehe Abbildung 6 ) zurückgeben. Sie können dieses Objekt verwenden, um die Eigenschaften der Endknoten angezeigt werden. SubstExplorerProperties stellt eine Implementierung der ICustomTypeDescriptor-Schnittstelle, die Informationen zurückgibt, auf welche Eigenschaften Sie anzeigen möchten, und wie Sie Sie anzeigen möchten. BaseHierarchyNode hat ein Standardobjekt Eigenschaften zeigt, dass Dinge wie der URL, ServerName und ProjectName, aber, offenbar nicht für unsere Endknoten nützlich.

Abbildung 6 SubstExplorerProperties

public class SubstExplorerProperties 
 : ICustomTypeDescriptor, IVsProvideUserContext {

 private BaseHierarchyNode m_node = null;
 public SubstExplorerProperties(BaseHierarchyNode node)
  { m_node = node; }

 public string GetClassName()
  { return m_node.PropertiesClassName;}

 public string GetComponentName()
  { return m_node.Name; }
 public PropertyDescriptorCollection 
  GetProperties(Attribute[] attributes) { 

  // create for each of our properties the 
  // appropriate PropertyDescriptor
  List<PropertyDescriptor> list = new List<PropertyDescriptor>();
  PropertyDescriptorCollection descriptors = 
  TypeDescriptor.GetProperties(this, attributes, true);

  for (int i = 0; i < descriptors.Count; i++) {
  list.Add(new DesignPropertyDescriptor(descriptors[i]));
  }
  return new PropertyDescriptorCollection(list.ToArray());
 }

 public object GetPropertyOwner(PropertyDescriptor pd) { 
  // return the object implementing the properties
  return this;
 }

 // rest of ICustomTypeDescriptor methods are not 
 // shown since they are returning defaults 
 // actual properties start here
 [Category("Drive mapping")]
 [Description("...")]
 [DisplayName("Version Control Path")]
 public string VersionControlPath
  { get { return ((SubstExplorerLeaf)m_node).VersionControlPath; } }

 [Category("Drive mapping")]
 [Description("...")]
 [DisplayName("Subst drive letter")]
 public SubstDriveEnum SubstDriveLetter {
  get { return 
  (SubstDriveEnum)Enum.Parse(typeof(SubstDriveEnum),
  ((SubstExplorerLeaf)m_node).SubstDriveLetter); }
 }

 [Category("Drive mapping")]
 [Description("...")]
 [DisplayName("Mapping name")]
 public string MappingName
  { get { return ((SubstExplorerLeaf)m_node).Name; } }
}

Befehle und Menüs

Wenn Sie den Stamm untersuchen und Knoten-Implementierungen Endknotenelemente, sehen Sie, dass beide ein Kontextmenü angezeigt werden müssen. Der Stammknoten benötigt ein Menüelement hinzufügen. Ein Endknoten benötigt Menüelemente löschen und Eigenschaften. Beide Knoten-Implementierungen zurückgeben eine CommandID-Instanz wie die Implementierung Ihrer jeweiligen ContextMenu Eigenschaften. Damit die CommandID-Klasse, um ordnungsgemäß zu funktionieren müssen Sie die Menüs und Befehle in der Projektmappe zu definieren.

Visual Studio ein Menü und ein Befehl hinzufügen möchten, müssen Sie die Befehle in einer Befehlstabelle definiert. Fügen Sie Befehl Tabellen die Assembly als eingebettete Ressource. Darüber hinaus müssen Sie den Befehlstabelle und der Registrierung während der Paket-Registrierung registrieren. Wenn Sie Devenv/Setup ausführen, wird Visual Studio sammelt alle Befehl Ressourcen vom alle registrierten Pakete und erstellt eine interne Darstellung aller Befehle in der Entwicklungsumgebung.

Beginnend mit Visual Studio 2005, könnten Sie Befehl Tabellen in einer XML-Datei mit der Erweiterung .vsct definieren. In dieser Datei definieren Sie die Menüs, die Befehlsgruppen und die Schaltflächen, die Sie im Menü angezeigt werden sollen. Ein Visual Studio-Befehl ist Teil einer Befehlsgruppe. Sie legen Befehlsgruppen auf Menüs.

Für den Stammknoten benötigen Sie einen Befehl hinzufügen, platziert in einer Gruppe, das in einem Menü enthalten. Der Blattknoten benötigt Befehle löschen und Eigenschaften. Sie müssen eine zweite Menü definieren, die eine andere Gruppe enthält, die diese beiden Befehle enthält. (Siehe den Download zu diesem Artikel für eine .vsct-Beispieldatei).

Die Datei .vsct benötigt besondere Behandlung in Visual Studio-Projekt. Müssen Sie ihn in eine Ressource kompilieren und dann die Ressource in die Assembly einbetten. Nach der Installation von Visual Studio SDK können Sie ein besonderes Build Aktion für den Befehlsdatei mit dem Namen VSCTCompile auswählen. Diese Aktion kümmert kompilieren und die Ressource in der Assembly einzubetten.

In der Tabelle "Befehl" XML-werden einige Symbole in der Definition der Menüs und Befehle verwendet. Sie hinzufügen alle Menüs, Befehle und Schaltflächen, um die gleichen CommandSet GuidPackageCmdSet aufgerufen:

<Symbols>
 <!-- This is the package guid. -->
 <GuidSymbol name="GuidPackage" value=
"{9B024C14-2F6F-4e38-AA67-3791524A807E}"/>
 <GuidSymbol name="GuidPackageCmdSet" value=
"{D0C59149-AC1D-4257-A68E-789592381830}"/>
  <IDSymbol name="mnuAdd" value="0x1001" />
  <IDSymbol name="mnuDelete" value="0x1002" />

Überall Sie Kontext Menü Informationen bereitstellen müssen, verweisen Sie wieder zu diesem Symbol als Container des Menüs. Folglich in der SubstExplorerRootNode und SubstExplorerLeafNode-Implementierungen, Sie erstellen eine Instanz vom Typ CommandID und GuidPackageCommandSet als das erste Argument und das tatsächliche Menü, das als zweites Argument angezeigt werden soll:

CommandID command = new CommandID(
 GuidList.guidPackageCmdSet, 
 CommandList.mnuDelete);

In der Datei .vsct sind drei Befehle, denen das UIHierarchy reagieren muss. Die ExecCommand-Methode wird aufgerufen, wenn Sie auf eine der Menüelemente klicken. Die Methode muss Wählen Sie die Aktion auszuführende basierend auf den nCmdId an Sie übergeben wird. Die grundlegende Implementierung der SubstExplorerUIHierarchy wird im Abbildung 7 angezeigt.

Abbildung 7 SubstExplorerUIHierarchy

public class SubstExplorerUIHierarchy : BaseUIHierarchy, 
 IVsHierarchyDeleteHandler, IVsHierarchyDeleteHandler2 {

 public SubstExplorerUIHierarchy(IVsUIHierarchy parentHierarchy, 
  uint itemId, BasicAsyncPlugin plugin)
  : base(parentHierarchy, itemId, plugin, 
  MSDNMagazine.TFSPlugins.SubstExplorerHostPackage.Instance) { 
 }
   
 public override int ExecCommand(uint itemId, 
  ref Guid guidCmdGroup, uint nCmdId, 
  uint nCmdExecOpt, IntPtr pvain, IntPtr p) {

  if (guidCmdGroup == GuidList.guidPackageCmdSet) {
  switch (nCmdId) {
   case (uint)CommandList.cmdAdd:
     AddNewDefinition(this.ProjectName);
     return VSConstants.S_OK;
   case (uint)CommandList.cmdDelete:
     RemoveDefinition(itemId);
     return VSConstants.S_OK;
   case (uint)CommandList.cmdEdit:
     EditDefinition(itemId);
     return VSConstants.S_OK;
   default: return VSConstants.E_FAIL;
  }
  }

  return base.ExecCommand(itemId, ref guidCmdGroup, nCmdId, nCmdExecOpt, pvain, p);
 }
 ...
}

Hinzufügen, bearbeiten und löschen

Jetzt benötigen Sie eine Möglichkeit für den Benutzer hinzufügen möchten, bieten löschen oder bearbeiten Sie Zuordnungen auf den Stamm oder Endknoten-Knoten. Der Code ist vorhanden, um Aufrufe für hinzufügen, auf dem Stammknoten und die Befehle bearbeiten und löschen die Endknoten zu behandeln. Eine neue Zuordnung hinzufügen Eingaben des Benutzers erfordert, und Sie müssen die Zuordnungsdaten in einem bekannten Speicherort zu speichern. Dieser Speicherort ist vorzugsweise im servergespeicherten Profil des Benutzers. So lassen Sie uns näher auf, wie Sie den Befehl Add reagieren können.

Die AddNewDefinition-Methode in der SubstExplorerUIHierarchy-Klasse behandelt den Befehl hinzufügen. AddNewDefinition zeigt ein Dialogfeld mit einer zulassen, dass Benutzer die Zuordnung angeben, die Sie erstellen möchten. Eine Zuordnung muss einen Namen und einen Laufwerkbuchstaben für den Befehl SUBST haben. Darüber hinaus muss die Zuordnung zu einem Pfad im Repository Versionskontrolle zu zeigen. Möchten Sie dem Benutzer ermöglichen, wählen Sie den Speicherort aus Versionskontrolle, anstatt einen komplexen Pfad manuell eingeben zu müssen. Sie können dies aktivieren, indem Sie mithilfe des TFS-Objektmodells, insbesondere die GetServer-Methode aus der TeamFoundationServerFactory-Klasse. GetServer akzeptiert einen URL, der den gewünschten verwenden und eine CredentialsProvider für den Fall, dass der Benutzer nicht in derselben Domäne wie der Server ist Server darstellt, und die Serververbindung erfordert eine neue Authentifizierung. Nachdem Sie Zugriff auf eine gültige TeamFoundationServer-Instanz verfügen, haben Sie Zugriff auf die verschiedenen von TFS bereitgestellten Dienste.

Sie benötigen den VersionControlServer-Dienst, um Informationen über die Ordnerstruktur in das aktuelle Teamprojekt zu erhalten. In Brians Januar 2007 Team System-Spalte (msdn.microsoft.com/magazine/cc163498), er wurde gezeigt, wie Sie diesen Dienst verwenden können, um Ihre eigene Version Steuerelement Dialogfeld zum Durchsuchen von Ordnern zu erstellen. Wir haben das in diesem Artikel hier beschriebene Dialogfeld wiederverwendet (siehe Abbildung 8 ). Das Dialogfeld gibt den vom Benutzer im Repository Versionskontrolle wie dargestellt im Abbildung 9-ausgewählten Ordner zurück. Sie speichern den Pfad in einer Konfigurationsdatei zurückgegeben.

Wenn der Benutzer auf OK klickt, können Sie einen neuen Knoten in die Konfigurationsdatei und einen neuen untergeordneten Knoten zur Hierarchie hinzufügen. Sie haben einen neuen Knoten durch einen Aufruf der AddChild-Methode für die HierarchyNode-Instanz hinzufügen.

Adding a New Mapping Definition

Abbildung 8 Hinzufügen eine neue Zuordnung Definition

Choosing a Location in Version Control

Abbildung 9 Auswählen eines Speicherorts unter Version Control

Ausführen des Befehls "Standard"

Die SubstExplorerUIHierarchy-Klasse ist verantwortlich für die Verarbeitung aller Befehle im Menüoptionen durch das plug-in wird ausgelöst. Eine der anderen Befehle, die Sie behandeln müssen ist bei einem auf einem Knoten Doppelklick. Die DoDefaultAction-Methode verarbeitet dieses Ereignis. Für den Stammknoten ist die Standard-Aktion von reduzieren oder Erweitern der Knoten in der Hierarchie akzeptabel. Für Endknoten, werden Sie jedoch eine benutzerdefinierte Implementierung bereitstellen.

Ersetzen Sie das Laufwerk, auf der Grundlage der Eigenschaften für diesen Knoten festgelegt werden soll. Subst ein Laufwerk Sie zum Ausstellen einer Befehlszeilen-Aktion und die erforderlichen Parameter angeben. Für diesen Zweck haben wir eine SubstHelperClass, die in den System.Diagnostics-Namespace erstellen einen neuen Prozess subst.exe aufgerufen und versehen es mit den notwendigen Parametern aufruft. Erforderliche Parameter gibt den Laufwerkbuchstaben und den lokalen Ordner als dem Laufwerk zugeordnet werden soll. Sie haben den Laufwerkbuchstaben verfügbar. Allerdings müssen Sie den Pfad Version Steuerelement lokalen Ordner zugeordnet. In diesem Fall Sie TFS-Objektmodell verwenden und einen Verweis auf das VersionControlServer-Objekt abrufen. Sie können dieses Objekt für alle verfügbaren Arbeitsbereiche Abfragen und versuchen Sie, eine Zuordnung zu einem lokalen Ordner basierend auf der Version Steuerelement Pfad, den Sie haben, zu erhalten. Abbildung 10 stellt eine Implementierung bereit.

Abbildung 10 Mapping einen Version Control Pfad bis zu einer Position auf dem Datenträger

private string GetLocalFolder() {
 VersionControlServer vcs = 
  (VersionControlServer)((
  SubstExplorerUIHierarchy)ParentHierarchy).
  tfs.GetService(typeof(VersionControlServer));
 Workspace[] workspaces = 
  vcs.QueryWorkspaces(null, vcs.AuthenticatedUser, 
  Environment.MachineName);
 foreach (Workspace ws in workspaces) {
  WorkingFolder wf = 
  ws.TryGetWorkingFolderForServerItem(VersionControlPath);
  if (wf != null) {
  // We found a workspace that contains this versioncontrolled item
  // get the local location to map the drive to this location....
  return wf.LocalItem;
  }
 }
 return null;
}

Abschluss des Workflows

Sie verfügen nun über die Logik implementiert, um eine Knotenstruktur anzeigen und verarbeiten Laufwerkzuordnung. Sie möchten jedoch Ihre Team Explorer-Plug-In, um Sie hervorzuheben. Möglicherweise möchten Sie hinzufügen, löschen Sie einige zusätzliche Features hinsichtlich der Behandlung von Knoten und andere professionelle Note, z. B. das Hinzufügen eines Symbols in der Begrüßungsbildschirm von Visual Studio.

Die Delete-Funktionalität hinzufügen, müssen Sie eine zusätzliche Schnittstelle in der SubstExplorerUIHierarchy-Klasse implementieren. Visual Studio verfügt über eine bestimmte Schnittstelle namens IVsHierarchyDeleteHandler, die Sie implementieren eine Standarddialogfeld angezeigt wird, wenn Sie die ENTF-TASTE drücken. Für diese-Plug-In, sollten Sie zum Bereitstellen von einem benutzerdefinierten Dialogfeld den Benutzer auffordert, den Löschvorgang des Knotens zu bestätigen, die ausgewählt ist. Damit diese funktioniert, müssen Sie auch die IVsHierarchyDeleteHandler2-Schnittstelle für die Behandlung von löschen, über die Tastatur zu arbeiten implementieren. Da Sie bereits die tatsächlichen Löschen-Funktionalität implementiert haben, müssen Sie nur diese Schnittstelle implementieren und die vorhandenen Funktionen aufrufen. Im Abbildung 11 können Sie die Implementierung der Schnittstellen sehen.

Abbildung 11 IVsHierarchyDelete Handler Implementierung

#region IVsHierarchyDeleteHandler2 Members

public int ShowMultiSelDeleteOrRemoveMessage(
 uint dwDelItemOp, uint cDelItems, 
 uint[] rgDelItems, out int pfCancelOperation) {

 pfCancelOperation = Convert.ToInt32(true);
 return VSConstants.S_OK;
}

public int ShowSpecificDeleteRemoveMessage(
 uint dwDelItemOps, uint cDelItems, uint[] rgDelItems, 
 out int pfShowStandardMessage, out uint pdwDelItemOp) {

 SubstExplorerLeaf nodeToDelete = 
  NodeFromItemId(rgDelItems[0]) as SubstExplorerLeaf;
 if (AreYouSureToDelete(nodeToDelete.Name)) { 
  pdwDelItemOp = 1; // == DELITEMOP_DeleteFromStorage; 
        // DELITEMOP_RemoveFromProject==2; 
 }
 else { 
  pdwDelItemOp = 0; // NO delete, user selected NO option }

 pfShowStandardMessage = Convert.ToInt32(false);
 return VSConstants.S_OK;
}

#endregion
#region IVsHierarchyDeleteHandler Members

public int DeleteItem(uint dwDelItemOp, uint itemid) {
 SubstExplorerLeaf nodeToDelete = 
  NodeFromItemId(itemid) as SubstExplorerLeaf;
 if (nodeToDelete != null) {
  // remove from storage
  RemoveDefinitionFromFile(nodeToDelete);
  // remove from UI
  nodeToDelete.Remove();
 }
 return VSConstants.S_OK;
}

public int QueryDeleteItem(uint dwDelItemOp, uint itemid, 
 out int pfCanDelete) {

 pfCanDelete = Convert.ToInt32(NodeFromItemId(itemid) is SubstExplorerLeaf);
 return VSConstants.S_OK;
}
#endregion

Es ist wichtig zu beachten, dass das plug-in mehrere ausgewählte Knoten auf einmal gelöscht wird nicht unterstützt, daher ist die PfCancelOperation auf festgelegt true in der ShowMultiSelDeleteOrRemoveMessage-Methode. Bei der Implementierung von ShowSpecificDeleteRemoveMessage-Methode müssen Sie die richtige Rückgabewert was Sie löschen möchten. Sie geben einen Wert von 1, um anzugeben, dass Sie es aus dem Speicher entfernt zurück. Diese Flags werden normalerweise in Visual Studio-Projektsystem verwendet, und nur einen Wert von 1 erzeugt die richtigen Ergebnisse.

Sie könnten auch Unterstützung für die Integration der Splash-Bildschirm hinzufügen möchten. Standardmäßig wird bei jedem Start von Visual Studio einen Begrüßungsbildschirm Auflisten der registrierten Produkte angezeigt. Zu diesem Zweck in der Implementierungsklasse SubstExplorerHostPackage die IVsInstalledProduct-Schnittstelle implementiert. Die Methoden benötigen Sie die Ressourcen-IDs für das Symbol in der Begrüßungsbildschirm und das Symbol verwendet werden, um in das Infofeld verwenden registrieren.

Die Implementierung ist nichts mehr als den Out-Parameter auf den richtigen Ganzzahlwert festlegen und ein 32 x 32 Pixel-Symbol als Ressource in der Assembly einzubetten. Um die Ressource ordnungsgemäß in Ihrer Assembly einzubetten, müssen Sie öffnen Sie die Datei resources.resx im XML-Editor, und fügen Sie die folgenden Zeilen zur Ressourcendatei:

<data name="500" 
 type="System.Resources.ResXFileRef, System.Windows.Forms">
 <value>..\Resources\SplashIcon.bmp;System.Drawing.Bitmap, 
  System.Drawing, Version=2.0.0.0, Culture=neutral, 
  PublicKeyToken=b03f5f7f11d50a3a</value> 
</data>

Dies fügt die Ressource-Bitmap, die sich in den Ordner Ressourcen im Projekt auf die Ressource befindet und bettet es auf den Verweis auf 500. In der IdBmpSplash-Methode können Sie jetzt pIdBmp auf 500 festgelegt und die Konstante S_OK als Rückgabewert zurückgeben. Das Symbol in der Begrüßungsbildschirm abrufen möchten, müssen Sie die Assembly erstellen, und führen Sie Devenv/Setup von der Befehlszeile aus. Dadurch erhalten die Informationen aus dem Paket, das Sie erstellt haben, und es werden die Daten zwischengespeichert. Dadurch wird sichergestellt, muss das Paket nicht geladen werden, wenn den Begrüßungsbildschirm von Visual Studio angezeigt wird. Geben Sie dafür den gleichen Gründen, die Sie benötigen dazu für die Optionen im Menü Sie hinzugefügt: Um die Ladezeit von Visual Studio zu beschleunigen.

Paket-Registrierung

Nun, da Sie die Team Explorer-Erweiterung haben, ist es Zeit, zu verpacken Sie das Produkt und ihn auf andere Entwickler ’s Systemen ausgeführt. Also, wie können Sie die Ergebnisse verteilen?

Erstens weist Visual Studio Wenn Sie das SDK installiert haben. Standardmäßig wird akzeptieren oder alle VSPackage laden. Dies wird nicht der Fall sein auf Computern, auf dem noch nicht das SDK installiert.

Für ein Paket ordnungsgemäß geladen werden soll, müssen Sie ein Paket Ladeschlüssel einbetten, die Sie von Microsoft erhalten (siehe msdn.microsoft.com/vsx/cc655795 ). Der wichtigste Teil dieses Prozesses wird sichergestellt, dass Sie genaue die gleiche Informationen, bereitstellen Wenn Sie für Ihren Schlüssel wie die Informationen registrieren Sie in den Attributen für die HostPackage-Klasse (in diesem Fall die SubstExplorerHostPackage-Klasse) bereitgestellt. Wenn der-Website aufgefordert werden Geben Sie den Paketnamen, müssen Sie außerdem den Namen des Produkts bereitstellen, den Sie in der ProvideLoadKey-Attribut verwendet.

Nachdem Sie Ihre Ladeschlüssel erhalten, können Sie in der Ressourcendatei mit dem Ressourcenbezeichner einfügen, die Sie als letztes Argument des Attributs ProvideLoadKey bereitgestellt. Stellen Sie sicher, dass Sie die Carriage Return/Line Feeds aus der Zeichenfolge entfernen, wenn Sie es von der Website, kopieren damit es eine aufeinander folgende Zeichenfolge, ist bevor Sie es in der Ressourcendatei einfügen.

Nachdem Sie testen können, wenn Ihre-Plug-in funktioniert, indem Sie einen weiteren Debug-Parameter angeben: / NoVsip. Dieser Parameter wird sichergestellt, dass Visual Studio das normale laden-Verhalten verwendet. Wenn der Schlüssel nicht akzeptiert wird, wird in Visual Studio eine Last-Fehler-Dialogfeld angezeigt. Mit dem SDK installiert finden unter dem Visual Studio im Menü Extras der Paket-Analyzer Laden Sie. Sie können dies zeigen Sie auf die Assembly für die Fehlersuche zu debuggen. Wenn es nur ein Paket laden Schlüssel handelt, vergewissern Sie sich Sie genau die gleichen Parameter wie in das Attribut auf der Website eingegeben haben.

Die letzte Schritt, der verbleibt, wird die Paket-Registrierung für einen Produktions-Computer. Da die Assemblys von Team System eine andere Version der Shell Assemblys verwenden, können nicht Sie leider regpkg.exe verwenden, um Ihr Paket zu erfassen. Stattdessen müssen Sie es mit einer Registrierungsdatei Datei manuell ausführen. In dieser Datei müssen Sie das Paket an der Stelle in der Registrierung korrekt zu veröffentlichen. Abbildung 12 zeigt die Registrierungsskripts erforderlich.

Abbildung 12 Package Registrierung Skript

REGEDIT4
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\TeamSystemPlugins\Team Explorer Project Plugins\SubstExplorer]
@="97CE787C-DE2D-4b5c-AF6D-79E254D83111"
"Enabled"=dword:00000001
 
[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\Services\{97ce787c-de2d-4b5c-af6d-79e254d83111}]
@="{9b024c14-2f6f-4e38-aa67-3791524a807e}"
"Name"="SubstExplorer"
 

[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\Packages\{9b024c14-2f6f-4e38-aa67-3791524a807e}]
@="MSDNMagazine.TFSPlugins.SubstExplorerHostPackage, TFSSubstExplorer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=324c86b3b5813447"
"InprocServer32"="C:\\Windows\\system32\\mscoree.dll"
"Class"="MSDNMagazine.TFSPlugins.SubstExplorerHostPackage"
"CodeBase"="c:\\program files\\msdnsamples\\TFSSubstExplorer.dll"
"ID"=dword:00000065
"MinEdition"="Professional"
"ProductVersion"="1.0"
"ProductName"="SubstExplorer"
"CompanyName"="vriesmarcel@hotmail.com"

[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\InstalledProducts\SubstExplorerHostPackage]
"Package"="{9b024c14-2f6f-4e38-aa67-3791524a807e}"
"UseInterface"=dword:00000001
 
[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\9.0\Menus]
"{9b024c14-2f6f-4e38-aa67-3791524a807e}"=", 1000, 1"

Im Registrierungsskript sehen Sie eine Anzahl an Einträgen.Der erste Eintrag registriert eine neue Team Explorer-Erweiterung, die Team Explorer geladen werden soll, sobald es geladen.Hier geben Sie einen Registrierungswert, der auf die Dienst-ID verweist, die eine Implementierung der ITeamExplorerPlugin bereitstellt.Der nächste Eintrag bietet die Dienst-Registrierung, in dem Sie, sehen die zuvor bezeichnet-um-Dienst-ID sowie einen Registrierungswert, der auf das Paket verweist, die das plug-in enthält.

Der nächste Eintrag ist die Registrierung Paket selbst.Sie verwenden es die Paket-ID als einen neuen Schlüssel, und geben Sie die Informationen, in dem die Assembly gefunden werden können wie es sein kann, die COM-Infrastruktur mit geladen und welche Version von Visual Studio das Paket unterstützt.Die letzten beiden Einträge sind die Registrierung der installierten Produkte, für den Begrüßungsbildschirm verwendet.Hier zeigt den UseInterface Schlüssel Visual Studio aufrufen muss, die IVsInstalledProduct-Schnittstelle der Standardvorrang das InstalledProductRegistration-Attribut, um ein Symbol und Produkt beschreiben, die beim Start angezeigt werden muss.

Der letzte Eintrag ist die Registrierung des Kontextmenüs.Hier verweisen wieder auf Ihr Paket, aber Sie enthalten auch Informationen über, in dem Sie die Ressourcen in der Assembly eingebettet haben.Dies sind die eingebetteten Ressourcen, die Sie erstellt, haben bevor Sie die .vsct-Dateien verwenden und die benutzerdefinierten Aktion für diese Datei erstellen.Mit diesem Skript und die Assembly, die Sie erstellt haben, können Sie es auf anderen Computern bereitstellen.Nur platzieren Sie die Assembly im Dateisystem, optimieren Sie das Registrierungsskript auf den richtige Assemblyspeicherort verweist, und der Registrierung zusammenführen.Anschließend wird die endgültige Schritt Devenv/Setup auf diesem Computer ausgeführt.Wenn Sie Visual Studio starten, wird das Symbol in der Begrüßungsbildschirm angezeigt und wenn Sie Team Explorer laden, sehen Sie den Stammknoten des Plug-Ins, die Sie erstellt haben.

Brian A.Randellist leitender Berater bei MCW Technologies LLC.Er hält Vorträge, unterrichtet und schreibt über Microsoft-Technologien.Außerdem ist er Autor des Pluralsight-Kurses „Applied Team System“ und Microsoft MVP.

Marcel de Vriesist als IT-Architekt bei Info Support in den Niederlanden, wobei er Lösungen für große Bank erstellt wurde und Versicherung Unternehmen über das Land und leitet die Team System und Windows Workflow-Kurse.Marcel ist ein gefragter Referent bei Entwicklerkonferenzen in Europa, ein Team System MVP seit 2006 und eine regionale Leiter Microsoft seit Januar 2009.

Dank an die folgenden technischen Experten für die Überprüfung dieses Artikels: Dennis Habib und Buck Hodges