Seiten

Freitag, 22. Oktober 2010

Untersuchung über das Hinzufügen einer ResourceDictionary.xaml Datei zu einem Silverlight 4 Projekt in einem Expression Blend 4 Add-In

Die in diesem Artikel vorgestellte Lösung zeigt, dass es grundsätzlich möglich ist, eine ResourceDictionary.xaml-Datei durch ein Add-In für Expression Blend 4 einem Silverlight 4-Entwicklungsprojekt hinzuzufügen.

Timmy Kokke hat kürzlich ein neues interessantes Add-In für Expression Blend 4 veröffentlicht. Das ist der Gradient Importer For Expresson Blend. In dem begleitenden Blog-Artikel stellt er die Frage in den Raum, ob es überhaupt möglich ist, in einer Expression Blend 4-Erweiterung für ein Projekt, das in Expression Blend bearbeitet wird, einen neuen Resourceneintrag durch den Quellcode des Add-Ins hinzuzufügen. Timmy Kokke löst das Problem in seinem Add-In durch eine Übergangslösung. Sein Add-In erzeugt das Xaml einer Resource und stellt es dem Nutzer in einer RichTextBox zur Verfügung. Der Nutzer kann den xaml-Quellcode dann durch copy and paste an die gewünschte Stelle in eine Projekt-Datei einfügen.

Die Frage, die Timmy Kokke aufgeworfen hat, ist sehr interessant. Ich habe mir überlegt, dass es schon ein gewisser Fortschritt wäre, wenn man Resourcen, die durch ein Add-In erzeugt werden, einem Entwicklungsprojekt über eine neue ResourceDictionary.xaml-Datei, die dem Entwicklungsprojekt hinzugefügt wird, bereitstellen könnte. Denn Aufgabe eines Resourcenverzeichnisses ist die zentrale Verwaltung von anwendungsweiten Resourcen. Der vielleicht größte Vorteil dieses zentralen Lösungsansatzes besteht darin, dass der Entwickler einer Blend-Erweiterung nicht alle denkbaren Varianten des Einbettens von Resourcen abdecken muss.

Ich habe ein paar Untersuchungen darüber angestellt, ob sowas möglich ist und wie das gehen könnte. Das positive Zwischenergebnis meiner Untersuchungen möchte ich hier kurz vorstellen. Die Lösung ist noch nicht endgültig. Sie löst auch nicht das von Timmy Kokke angesprochene Problem. Aber sie zeigt, dass es grundsätzlich möglich ist, eine ResourceDictionary.xaml-Datei durch ein Add-In einem geöffneten Entwicklungsprojekt hinzuzufügen.

Beispiel-Add-In
Das hier vorgestellte Beispiel-Add-In fügt einem neuen Silverlight 4-Projekt direkt beim Erzeugen in Expression Blend 4 eine neue ResourceDictionary.xaml-Datei hinzu. Das ist die Projekt-Datei TestResourceDictionary.xaml.

Bitte beachtet, dass es sich nur um ein Demonstration des Möglichen handelt. Der Quellcode ist deswegen nicht optimal.

Grundsätzliche Vorgehensweise
Über die Schnittstelle IProjectManager hängen wir uns an das ProjectOpened()-Ereignis heran. Dieses Ereignis feuert, wenn ein neues Projekt geöffnet wird. In einem anderen Artikel habe ich beschrieben, wie man in einer Expression Blend-Erweiterung den Projekttyp feststellen kann. Um den Quellcode auf das Wesentliche zu beschränken, geht das Beispiel-Add-In einfach davon aus, dass ein Silverlight-basiertes Projekt geöffnet wird. Dem ProjectOpened()-Ereignis wird also ein Delegat hinzugefügt, der auf die Routine ASilverlightProjectOpened() zeigt. Das Ereignis ProjectOpened() kennt zwei Parameter. Einer dieser Parameter ist vom Typ Microsoft.Expression.Project.ProjectEventArgs. Über diesen Parameter wird auf das neu erzeugte Silverlight 4-Projekt zugegriffen und das Projekt wird der Variablen "cp" zugewiesen. Die Variable cp ist vom Typ IProject. Die Schnittstelle IProject kennt die öffentliche Methode AddItem(). Diese Methode wird genutzt, um dem Projekt die Datei TestResourceDictionary.xaml als ProjetItem hinzuzufügen.

Die Methode IProject.AddItem() erwartet einen Parameter vom Typ Microsoft.Expression.Project.DocumentCreationInfo. Eine Variable diesen Typs wird erzeugt und schließlich der Methode IProject.AddItem() übergeben.

Quellcode im Detail
Nachfolgend findet Ihr den Quellcode der Routine ASilverlightProjectOpened() und der Routine CreateResourceDictionaryFile(). Der vollständige Quellcode kann über den Link am Ende dieses Artikels heruntergeladen werden.

    Private Sub ASilverlightProjectOpened(ByVal sender As Object, ByVal e As ProjectEventArgs)

      Dim cp As IProject = e.Project

      Dim resourceDictionaryCreationInfo As Microsoft.Expression.Project.DocumentCreationInfo

      resourceDictionaryCreationInfo.CreationOptions = CreationOptions.AlwaysUseDefaultBuildTask

      resourceDictionaryCreationInfo.DocumentType =
        CType(servicesPresenter.GetService(GetType(IDocumentTypeManager)), IDocumentTypeManager).DocumentTypes.ElementAt(20)

      Dim tmpPath As String = cp.DocumentReference.Path

      tmpPath = tmpPath.Replace("SilverlightApplication1.vbproj", "")

      resourceDictionaryCreationInfo.TargetFolder = tmpPath

      resourceDictionaryCreationInfo.TargetPath = tmpPath + "TestResourceDictionary.xaml"

      CreateResourceDictionaryFile(tmpPath, "TestResourceDictionary.xaml")

      resourceDictionaryCreationInfo.SourcePath = tmpPath + "TestResourceDictionary.xaml"

      cp.AddItem(resourceDictionaryCreationInfo)

    End Sub


    Private Sub CreateResourceDictionaryFile(ByVal TargetFolder As String, ByVal ResourceFile As String)

      Dim xamlDoc As XElement = <ResourceDictionary
                                  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                                  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"></ResourceDictionary>

      xamlDoc.Save(TargetFolder + "TestResourceDictionary.xaml")

    End Sub


Zunächst wird eine private Variable vom Typ DocumentCreationInfo erzeugt. Das ist die Variable resourceDictionaryCreationInfo.

Über die Eigenschaft CreationOptions wird der BuildTask, ohne den das Resourcenverzeichnis nicht als Resource in den Build einbezogen wird, auf den DefaultBuildTask gesetzt.

Über die Eigenschaft DocumentType wird der Typ der Projektdatei festgelegt. Dazu wird auf die in der Schnittstelle IDocumentTypeManager vorgehaltene DocumentTypeCollection zugegriffen. Diese null-basierte Collection enthält insgesamt 36 Items. Eines davon ist der ResourceDictionaryDocumentType, der als Item an der Position 20 abgegriffen und der DocumentType-Eigenschaft der Variablen resourceDictionaryCreationInfo zugewiesen wird.

Der weitere Quellcode weist den Eigenschaften TargetFolder, TargetPath und SourcePath Werte zu. Dazu wird der Pfad des aktuellen Projekts ausgelesen und so modifiziert, dass man den Projekt Ordner erhält. Denn darin soll die Datei TestResourceDictionary.xaml gespeichert werden. Bevor der Eigenschaft SourcePath der Wert der Datei TestResourceDictionary.xaml zugewiesen wird, muss die Datei erzeugt werden. Das geschieht in der Routine CreateResourceDictionaryFile(). Diese Routine erzeugt den Xaml-Quellcode, der in einem neu erzeugten Resourcenverzeichnis standardmäßig enthalten ist. Das Xaml wird als xml erzeugt und als TestResourceDictionary.xaml-Datei gespeichert. Das Speichern der Datei über die Methode XElement.Save() hat den Nachteil, dass eine xml ProcessingInstruction am Anfang der Datei steht, die manuell entfernt werden muss. Dafür gibt es elegantere Lösungen. Eine davon findet sich garantiert in den Assemblies von Microsoft.Expression. Mir geht es mit diesem Beispiel aber nur darum, die Frage zu klären, ob es eine grundsätzliche Lösung für das eigentliche Problem gibt. Und die gibt es.

Nachdem den o.g. Eigenschaften der Variablen resourceDictionaryCreationInfo die Werte zugewiesen wurden, erfolgt schließlich die Übergabe dieser Variablen an die Methode AddItem().

Wenn das neue Silverlight 4-Projekt in Expression Blend 4 vollständig geladen ist, erscheint nun die Datei TestResourceDictionary.xaml im Projektbaum des Projektexplorers.



Einschränkungen
Diese Lösung hat gegenwärtig zwei Einschränkungen.

Die erste Einschränkung habe ich bereits erwähnt. Sie besteht darin, dass die xml-ProcessingInstruction aus dem Quellcode von TestResourceDictionary.xaml manuell entfernt werden muss.



Die zweite Einschränkung besteht darin, dass die Datei TestResourceDictionary.xaml manuell in der Datei App.xaml als Resourcenverzeichnis aufgenommen werden muss. Andernfalls wird sie nicht als Resourcenverzeichnis erkannt. Dazu muss der folgende Quellcode in App.xaml manuell eingefügt werden.



Damit landen wir im Prinzip mit der vorgestellten Lösung erneut bei der von Timmy Kokke aufgeworfenen Frage. Ich werde meine Untersuchungen fortsetzen und vielleicht auch eine Lösung dafür finden, das Resourcenverzeichnis im Quellcode von App.xaml aufzunehmen. Vielleicht gibt es aber irgendwo in der Community jemanden, der gerade genau an diesem Problem arbeitet und für den dieser Artikel eine Hilfe darstellt.

Sobald die besagten Einschränkungen manuell beseitigt worden sind steht das Resourcenverzeichnis der Datei TestResourceDictionary.xaml anwendungsweit in Expression Blend 4 zur Verfügung.

Download: Vollständiger Quellcode des Beispiel-Projects.

1 Kommentar:

  1. Hallo,
    sehr guter Artikel, leider zeigt der Download auf ein anders Projekt.
    Grüße Friedhelm

    AntwortenLöschen