Seiten

Donnerstag, 14. Oktober 2010

Sichtbarkeit von einer Expression Blend Erweiterung (Add-In) abhängig vom Projekttyp

Die Funktionalität einer Expression Blend-Erweiterung zielt meistens auf einen bestimmten Projekttyp ab. Deswegen ist es nützlich zu wissen, wie man die Sichtbarkeit der Expression Blend-Erweiterung abhängig davon steuern kann, welcher Projekttyp geöfnet ist bzw. geöffnet wird. Dieser Artikel beschreibt, wie man die Sichtbarkeit von einer Expression Blend-Erweiterung in Abhängigkeit vom Projekttyp steuern kann.

Die vorgestellte Lösung funktioniert mit allen Solutiontypen von Expression Blend 4, einschließlich SketchFlow-Solutions.

Beispielprojekt

Es wird eine einfache Beispiel-Erweiterung erstellt, um zu demonstrieren, wie der Quellcode funktioniert. Die Benutzeroberfläche besteht nur aus einem Fenster mit einem Textblock-Steuerelement.



Generelle Vorgehensweise

Die Assembly Microsoft.Expression.Project stellt die Schnittstelle IProjectManager zur Verfügung. Über die Verwendung dieser Schnittstelle kann auf das Ereignis SolutionOpened() reagiert werden. Dieses Ereignis tritt auf, wenn eine Solution geöffnet wird. Eine Solution kann gegebenenfalls aus mehreren Projekten mit unterschiedlichen Projekttypen bestehen. Dem Ereignis SolutionOpened() wird ein Delegate hinzugefügt. Innerhalb der Routine, auf die der Delegat zeigt, wird der aktuelle Projekttyp ermittelt, indem der Parameter e ausgewertet wird. Der Parameter e hat den Typ SolutionEventArgs. Mithilfe der Klasse SolutionEventArgs() kann der Projekttyp jedes Projekts identifiziert werden, das Teil der Solution ist. Die Projekte einer Solution können über SolutionEventArgs.Solution.Projects abgefragt werden. Der Rückgabewert von SolutionEventArgs.Solution.Projects ist vom Typ IEnumerable(Of IProject). Aber es ist nicht erforderlich, IProject selbst zu verwenden. Für das Identifizieren eines Silverlight-basierten Projekts innerhalb einer Solution, reicht es außer bei einem bestimmten Solutiontyp vielmehr aus, wenn nur der Projekttyp des Projekts ausgewertet wird, der in der Collection von SolutionEventArgs.Solution.Projects zuoberst gelistet ist.

Der einzige Solutiontyp, bei dem das nicht ausreicht, ist eine Silverlight-Anwendung mit ASP-Website. Für diesen Fall, kann über die Verwendung der Schnittstelle IProjectManager ergänzend auf das Ereignis ProjectOpened() reagiert werden. Dieses Ereignis tritt auf, wenn ein Projekt geöffnet wird. Wenn eine Solution mehrere Projekte enthält, dann feuert das Ereignis ProjektOpened() für jedes Projekt, das in der Solution enthalten ist. Es ist nach den von mir durchgeführten Tests nur für diesen Solutiontyp erforderlich, zusätzlich dem Ereignis ProjectOpened() einen Delegaten hinzuzufügen, der auf eine Routine zeigt, die sicherstellt, dass die Expression Blend-Erweiterung auch für das in dieser Solution enthaltene Silverlight-basierte Projekt zuverlässig bereitgestellt wird.

Schritt-für-Schritt

Schritt 1 - Konfigurieren des Entwicklungsprojekts

Für diesen Schritt verweise ich auf Schritt 1 in einem Artikel, den ich früher veröffentlicht habe.

Schritt 2 - Assemblyverweise

Auf die folgenden Assemblies müssen im Beispielprojekt durch Verweise einbezogen werden (in alphabetischer Reihenfolge):

Microsoft.Expression.Extensibility
Microsoft.Expression.Framework
Microsoft.Expression.Project
System.ComponentModel.Composition

Zu beachten ist, dass der Verweis auf Microsoft.Expression.Project nur funktioniert, wenn das Ziel-Framework in den Projekt-Eigenschaften in der Registerkarte "Kompilieren" unter "Erweiterte Kompilierungsoptionen ..." auf das .Net Framework 4 gesetzt wird.

Schritt 3 - Die Klasse ProjectManagerLabExtension.vb

Diese Klasse stellt die Expression Blend-Erweiterung bereit. Hier ist der vollständige Quellcode der Klasse:


Und so funktioniert sie im Detail:

Es werden drei private Variablen benötigt:

1. Die Variable windowService. Diese Variable ist vom Typ IWindowService. Die Methode IWindowService.RegisterPalette() fügt der Entwicklungsumgebung von Expression Blend eine neue RegisterPalette hinzu. Diese RegisterPallete enthält die Benutzeroberfläche der Expression Blend-Erweiterung in Gestalt des WPF-UserControls des Beispielprojekts.

2. Die Variable DemoUI. Diese Variable ist ein Object vom Typ des WPF-UserControls und liefert wie gesagt die Benutzeroberfläche.

3. Die Variable projectManager. Diese Variable ist vom Typ IProjectManager. Mit ihrer Hilfe prüfen wir den Solutiontyp sowie erforderlichenfalls den Projekttyp.

Im Load()-Ereignisbehandler werden den Variablen windowService und projectManager die jeweiligen Schnittstellen-Typen zugewiesen. Es wird die Routine BuildExtensionUI() aufgerufen. Diese Routine erstellt die Benutzeroberfläche der Expression Blend-Erweiterung und registriert eine neue RegisterPalette. Schließlich wird dem Ereignis SolutionOpened() eine Delegat hinzugefügt, der auf die Routine ASolutionOpened() zeigt.

Die Routine ASolutionOpened() ruft die Routine CheckProjectType() auf. Nachfolgend stelle ich zwei alternative Vorgehensweisen vor, um ein Silverlight-basiertes Projekt zu identifizieren.

Variante 1

Diese Variante ist nicht zu bevorzugen. Wer gleich die bessere Variante lesen will überspringt diesen Abschnitt und liest weiter bei "Variante 2".

Hier der Quellcode-Ausschnitt für Variante 1:


In dieser Variante hat die Routine CheckProjectType() zwei Parameter. Der erste Parameter (ProjectIdentifier) ist vom Typ String. Der zweite Parameter (eESA) ist vom Typ SolutionEventArgs. Dem ersten Parameter wird der Wert des ausgewerteten Projekttyps zugewiesen. Es wird der Projekttyp des Projekts ausgewertet, das in der Collection von IEnumerable(Of IProject) zuoberst gelistet ist. Mit dem zweiten Parameter wird der Wert von e weitergereicht.

In der Routine CheckProjectType wird dann der Projektidentifzierer (ProjectIdentifier) ausgewertet. Zu diesem Zweck werden vier mögliche Werte geprüft. Der Wert "Application Executable" weist auf eine WPF-Solution hin. Erfaßt werden alle Arten von WPF-Solutiontypen. Die Werte "Silverlight-based Project" und "Web Application Project" weisen auf eine Solution hin, die ein Silverlight-basiertes Project enthält. Der Wert "Web Site" weist auf ein ASP-Website-Project hin, das Teil einer Silverlight-Solution mit einer neuen ASP-Website ist. Dieser Wert wird beim Erstellen einer neuen Silverlight-Solution mit einer neuen ASP-Website übergeben. Nur in diesem Fall muss ergänzend der Projekttyp des Silverlight-Projekts abgefragt werden. Das Silverlight-Projekt in einer Silverlight-Solution mit neuer ASP-Website ist in der nullbasierten Collection IEnumerable(Of IProject) an zweiter Stelle gelistet.

Jeder der vier möglichen Werte wird als Fall ausgewertet. Es wird jeweils die Routine ControlDemoUIVisibility() aufgerufen. Je nach dem, welcher ProjectIdentifier übergeben wurde, wird der Parameter ExtensionVisibility auf True oder auf False gesetzt.

Variante 2 (empfohlen):

In dieser Variante wird die Collection IEnumerable(Of IProject) unmittelbar ausgewertet. Die Routine CheckProjectType() hat in dieser Variante nur einen Parameter. Dieser Parameter ist vom Typ SolutionEventArgs. Die Routine ASolutionOpened() ruft die Routine CheckProjectType() auf und reicht einfach den Parameter e weiter.

Hier ist der Quellcode für die Variante 2:


Die Auswertung erfolgt über eine LINQ-Abfrage. Die LINQ-Abfrage wertet für jedes Projekt in der Collection den Projekt-Identifizierer aus. Die Anzahl der in der Variablen silverlightMatches enthaltenen Elemente ist nur dann größer 0 wenn mindestens ein Silverlight-basiertes Project gefunden wurde. Wenn das der Fall ist, wird die Routine ControlDemoUIVisibility() mit dem Parameterwert True aufgerufen. Andernfalls mit dem Parameterwert False.

Hinweis: Wenn die Abfrage darauf gerichtet sein soll, ob ein WPF-Projekt geöffnet wird, braucht man einfach nur den Projekt-Identifizierer nach dem Wert "Application Extecutable" suchen zu lassen.

Die Routine ControlDemoUIVisibility() macht dann folgendes, vorausgesetzt ExtensionVisibility ist auf True gesetzt. Die Sichtbarkeit des WPF-UserControls wird auf sichtbar gesetzt. Falls die RegisterPalette noch nicht registriert ist, wird sie registriert. Die Sichtbarkeit von der RegisterPalette wird auf sichtbar gesetzt. Schließlich wird dem Ereignis IProjectManager.SolutionClosed() ein Delegat hinzugefügt, der auf die Routine ASolutionClosed() zeigt. Die Routine ASolutionClosed() entfernt die Expression Blend-Erweiterung, wenn die Silverlight-Solution geschlossen wird.

Das war's im Prinzip.

Es gibt eine Einschränkung, auf die ich hinweisen möchte. Wenn Expression Blend neu gestartet wird und der Benutzer im Willkommensbildschirm auf "Schließen" klickt, ohne ein Projekt zu öffnen oder ohne ein neues Projekt zu erstellen, wird gleichwohl die Expression Blend-Erweiterung im Menü unter "Fenster" angezeigt. Ich habe noch nicht herausgefunden wie man das verhindern kann. Weil das so ist, verarbeitet die Routine ControlDemoUIVisibility() ergänzend den Fall, dass der Parameter ExtensionVisibility den Wert False hat. Damit wird die Situation abgedeckt, dass Expression Blend geöffnet wird, der Willkommensbildschirm geschlossen und danach eine (neue) WPF-Solution geöffnet wird. Für diesen Fall wird ausnahmsweise die Routine ASolutionClosed() aufgerufen, damit die Expression Blend-Erweiterung entfernt wird, die in der Benutzeroberfläche bereits vorhanden ist.

Download: Der vollständige Quellcode des Beispielprojekts.

Keine Kommentare:

Kommentar veröffentlichen