Bisherige Artikel der Serie:
Im
vorigen Artikel habe ich gezeigt, wie mit Hilfe des MEF einem TabControls Seiten hinzugefügt werden können, ohne dass die Seiten explizit erzeugt werden müssen. Die Aufgabe der Erzeugung übernimmt dabei das MEF.
In diesem Artikel geht es um die Verwendung und Auswertung von Metadaten.
Wenn man sich nochmal den Quelltext des Hauptfensters aus dem letzten Beispiel anschaut, so fällt eine sehr unschöne Sache auf:
public partial class MainWindow : Window
{
[Import("Page1")]
public UserControl Page1;
[Import("Page2")]
public UserControl Page2;
[Import("Page3")]
public UserControl Page3;
public MainWindow()
{
InitializeComponent();
var catalog = new AttributedAssemblyPartCatalog(Assembly.GetExecutingAssembly());
var container = new CompositionContainer(catalog.CreateResolver());
container.AddPart(this);
container.Compose();
DataContext = new List<object>
{
new {Title = "Page 1", Page = Page1},
new {Title = "Page 2", Page = Page2},
new {Title = "Page 3", Page = Page3}
};
}
}
Das Fenster braucht sich zwar nicht mehr um die Herkunft und Erzeugung der Seiten kümmern, muss aber trotzdem wissen, wie die Seiten betitelt werden. Das passt irgendwie nicht zusammen.
Zu sehen ist das ganze beim Zuweisen des DataContext. Die Seiten werden importiert, ohne dass wirklich deren Herkunft bekannt ist, aber die Titel sind fest im Quelltext hinterlegt.
Besser wäre es, wenn die Seiten-Bezeichnungen genauso dynamisch bestimmt werden können, wie die Seiten selbst. Sinnvollerweise sollte jede Seite selbst wissen, wie sie bezeichnet werden soll.
Das Hinterlegen von Meta-Informationen ist mit Hilfe des ExportMetadata-Attributes möglich. Damit können Exports mit Informationen versehen werden, die beim Importieren ausgewertet werden können.
Der Quelltext der Seite Page1 sieht nun so aus:
[Export("Page1")]
[ExportMetadata("Title", "Page 1")]
public partial class Page1 : UserControl
{
public Page1()
{
InitializeComponent();
}
}
Die Klasse Page1 wird hier mit dem ExportMetadata-Attribut versehen, dem ein Schlüssel-Wert-Paar übergeben wird. Der erste Parameter ist der Schlüssel, und lautet in diesem Fall natürlich "Title". Der zweite Parameter ist die tatsächliche Bezeichnung, die im Hauptfenster verwendet werden soll.
Nachdem die anderen Seiten auf dieselbe Weise erweitert wurden, können die Metadaten beim Importieren verwendet werden. Dafür müssen aber zunächst die Imports etwas abgewandelt werden:
[Import("Page1")]
public Export<UserControl> Page1;
[Import("Page2")]
public Export<UserControl> Page2;
[Import("Page3")]
public Export<UserControl> Page3;
Die Seiten sind nun nicht mehr als UserControl deklariert, sondern als Export<UserControl>. Die Export-Klasse verfügt über eine Metadata-Eigenschaft, die mit den entsprechenden Angaben beim Importieren gefüllt werden. Beim Füllen des DataContext können diese Daten dann verwendet werden. An das eigentliche UserControl-Objekt gelangt man über die Methode GetExportedObject():
DataContext = new List<object>
{
new {Title = Page1.Metadata["Title"], Page = Page1.GetExportedObject()},
new {Title = Page2.Metadata["Title"], Page = Page2.GetExportedObject()},
new {Title = Page3.Metadata["Title"], Page = Page3.GetExportedObject()}
};
Die Anwendung zeigt nach den Änderungen erfreulicherweise noch das gleiche Ergebnis, wie vorher. Allerdings muss das Fenster nun nicht mehr wissen, wie die einzelnen Seiten betitelt werden sollen. Das ist ein großer Schritt in Richtung Erweiterbarkeit.
Der Quelltext der Beispielanwendung kann hier heruntergeladen werden.