jenswinter.com
Software Development 24/7

PDC-Blog-Verweigerer

October 29, 2008 07:52 by Jens

Hiermit gebe ich bekannt, dass ich keinerlei PDC Posts schreiben werde. Damit gehöre ich nun also offiziell zur absoluten Minderheit der bloggenden .NET-Community, die nichts über die Geschehnisse der PDC schreibt. :-D


Tags:
Actions: E-mail | Permalink | Comments (2) | Comment RSSRSS comment feed

WPF Toolkit veröffentlicht

October 28, 2008 20:36 by Jens

Das WPF Team hat heute die erste stabile Version des WPF Toolkit veröffentlicht. Das Toolkit enthält unter anderem das WPF DataGrid, ein Kalender-Control und Vorschau-Versionen des Ribbon-Controls und des VisualStateManagers.
Die Projekt-Homepage befindet sich auf CodePlex: http://www.codeplex.com/wpf.

Dort kann auch der Quelltext des Paketes heruntergeladen werden:
http://www.codeplex.com/wpf/Release/ProjectReleases.aspx?ReleaseId=15598

.NET bekommt neues Logo

October 25, 2008 20:37 by Jens

Microsoft spendiert .NET ein neues Logo:

Bzw. als horizontale Variante:

Gefällt mir eigentlich ganz gut. Smile


Tags:
Actions: E-mail | Permalink | Comments (0) | Comment RSSRSS comment feed

Einführung in das Managed Extensibility Framework (Teil 4)

October 9, 2008 20:35 by Jens
Bisherige Artikel der Serie:


Das Beispiel dieser Serie ist noch nicht flexibel genug. Im Hauptfenster ist festgelegt, dass das TabControl drei Seiten enthält:

DataContext = new List<object>
                  {
                      new {Title = "Page 1", Page = Page1},
                      new {Title = "Page 2", Page = Page2},
                      new {Title = "Page 3", Page = Page3}
                  };


Viel besser wäre es doch, wenn eine beliebige Anzahl von Seiten dargestellt werden können.

Das MEF bietet diese Möglichkeit. Es ist in der Lage, mehrere Komponenten, die denselben Contract haben, in eine Collection zu importieren. Das bedeutet zunächst, die Export-Contracts der UserControls müssen angepasst werden, sodass sie alle gleich lauten:

[Export("Page")]
public partial class Page1 : UserControl
{
    public Page1()
    {
        InitializeComponent();
    }
}

[Export("Page")]
public partial class Page2 : UserControl
{
    public Page2()
    {
        InitializeComponent();
    }
}

[Export("Page")]
public partial class Page2 : UserControl
{
    public Page2()
    {
        InitializeComponent();
    }


Diese UserControls können nun in eine Collection importiert werden. Dazu wird die Deklaration der Imports geändert. Die Member "Page1", "Page2" und "Page3" können entfernt werden und durch die Deklaration des Members "Pages" ersetzt werden:

[Import("Page")]
public ExportCollection<UserControl> Pages; 


Dadurch wird der CompositionContainer angewiesen, alle Komponenten mit dem Contract "Page" zu importieren. Über die ExportCollection<T>-Klasse haben wir wieder Zugriff auf die Metadaten der Exports. Die Seiten können nun wie folgt für den DataContext ausgewertet werden:

DataContext = from p in Pages
              select new {Title = p.Metadata["Title"], Page = p.GetExportedObject()};

              
Nun werden beliebig viele UserControls, die mit den entsprechenden Attributen versehen sind, in das TabControl geladen.

Der Quelltext der Beispiel-Anwendung kann hier heruntergeladen werden.

Moderator bei .NET-GUI.com

October 8, 2008 21:12 by Jens


Seit heute gehöre ich dem Moderatoren-Team von .NET-GUI.com an.
Ein herzliches Dankeschön an Norbert Eder für das entgegengebrachte Vertrauen. Gerne werde ich mich den Aufgaben stellen und hoffe, eine Bereicherung für das Team und die Community zu sein.

.NET GUI ist die .NET Community, die sich mit der Entwicklung von grafischen Oberflächen beschäftigt. Gerade mit dem Aufkommen der Windows Presentation Foundation und Silverlight, haben sich viele neue Möglichkeiten ergeben. Der Gestaltung von Oberflächen wird höhere Priorität zuteil. Diese Community bietet die Chance, sich über alle Belange von grafischen Oberflächen auszutauschen. Einzige Einschränkung: Die Basis muss durch das .NET Framework gegeben sein.

Wenn Ihr also Anwendungen mit den Technologien Windows Forms, WPF oder Silverlight entwickelt, so ist .NET-GUI.com die richtige Anlaufstelle für alle Fragen über die Oberflächenprogrammierung. 

Channel8 beim .NET Open Space

October 8, 2008 19:30 by Jens

Also wird es tatsächlich Videos vom .NET Open Space 2008 geben. Woohoo! Rock on!


Kein .NET Open Space 2008 für mich

October 7, 2008 21:02 by Jens

Ich habe heute meine Anmeldung für den .NET Open Space 2008 zurückgezogen. Leider ist mir etwas dazwischen gekommen.
Ich wäre wirklich gerne dabei gewesen.

Ich wünsche allen Teilnehmern viel Spaß. Vielleicht ist ja auch jemand dabei, der ein paar Filmchen dreht. Dann kann ich mich später in eine dunkle Kammer einschließen, die Filme startenund so tun als wäre ich gerade beim Open Space dabei. Muähähä...


Einführung in das Managed Extensibility Framework (Teil 3)

October 7, 2008 20:15 by Jens

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.


SVN Integration bei Codeplex

October 6, 2008 22:31 by Jens

Ich habe heute herausgefunden, dass Codeplex eine SVN-Integration besitzt. Bisher habe ich SVNBridge für den Zugriff auf die Projektquellen verwendet. Das ist nun nicht mehr nötig.
Man kann nun ganz einfach mit solch einer URL auf die Sourcen zugreifen:

https://<Projektname>.svn.codeplex.com/svn

Nice!


Einführung in das Managed Extensibility Framework (Teil 2)

October 5, 2008 22:55 by Jens

Bisherige Artikel der Serie:


Im ersten Artikel dieser Serie ist beschrieben, was das Managed Extensibility Framework ist und welche Möglichkeiten es bietet. In diesem Artikel werde ich eine erste Einführung in die Verwendung des MEF zeigen. Anhand einer einfachen Demo-Anwendung soll der Einsatz demonstriert werden. Programme, die das MEF einsetzen, müssen die Assembly System.ComponentModel.Composition referenzieren. Sie kann hier heruntergeladen werden.

In der Demo-App, einem einfachen WPF-Programm, soll ein TabControl mit bestimmten Seiten gefüllt werden. So sieht das Ergebnis aus:



In einem ersten Schritt zeige ich die Umsetzung ohne den Einsatz von MEF. Die Anwendung besteht aus einem Hauptfenster, welches ein TabControl enthält und drei UserControls, die als Seiten in das TabControl geladen werden.

XAML-Code des Hauptfensters:

<Window x:Class="MefDemoApp.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="500" Width="600">

    <Grid>
        <TabControl ItemsSource="{Binding}" DisplayMemberPath="Title">
            <TabControl.ContentTemplate>
                <DataTemplate>
                    <ContentPresenter Content="{Binding Page}"/>
                </DataTemplate>
            </TabControl.ContentTemplate>
        </TabControl>
    </Grid>
</Window>

CodeBehind des Hauptfensters:

public partial class MainWindow : Window
{
    public UserControl Page1 = new Page1();
    public UserControl Page2 = new Page2();
    public UserControl Page3 = new Page3();

    public MainWindow()
    {
        InitializeComponent();

        DataContext = new List<object>
                          {
                              new {Title = "Page 1", Page = Page1},
                              new {Title = "Page 2", Page = Page2},
                              new {Title = "Page 3", Page = Page3}
                          };
    }
}

Der DataContext des Hauptfensters wird mit einer Liste von anonymen Objekten geladen, die jeweils einen Titel und die entsprechende Seite enthalten. Die Titel-Eigenschaft wird für die Beschriftung des Tabs verwendet. Wie im XAML-Code zu sehen ist, wird die ItemsSource des TabControls an den DataContext gebunden und der Content der Tab-Seiten an die Page-Eigenschaft gebunden.
Die Klassen Page1, Page2 und Page3 sind UserControls mit beliebigem Inhalt.

Ein erstes Problem, das im Quelltext zu sehen ist besteht in der direkten Abhängigkeit zu den UserControls. Die Erzeugung der Seiten mittels new Page1() reduziert die Wartbarkeit und die Erweiterbarkeit. Das Hauptfenster sollte nicht dafür verantwortlich sein, welche Seiten angezeigt werden. Erst recht sollte es die Seiten nicht selbst erzeugen.

Die wichtigsten Bestandteile des MEF sind der Katalog und der CompositionContainer. Der Katalog ist verantwortlich für Bereitstellung von Erweiterungen und der CompositionContainer sorgt für die Erzeugung dieser Erweiterungen und der Erfüllung der Abhängigkeiten.
Eine Erweiterung wird im MEF ComposablePart genannt. Ein ComposablePart enthält ein oder mehrere Komponenten (Exports), die anderen ComposableParts zur Verfügung gestellt werden. Es kann Abhängigkeiten zu anderen ComposableParts definieren. Diese Abhängigkeiten werden Imports genannt. Der CompositionContainer erzeugt ein ComposablePart und füllt die als Import deklarierten Komponenten mit den Komponenten, die als Export deklariert sind. Die Verbindung eines Imports mit einem Export ist möglich, da ein Contract zwischen Import und Export definiert wird. Der CompositionContainer kann über diesen Contract feststellen, welche exportierte Komponente zu welcher importierenden Komponente gemappt werden muss.

Die Deklaration der UserControls sieht bei der Verwendung des MEF nun so aus:

[Import("Page1")]
public UserControl Page1;

[Import("Page2")]
public UserControl Page2;

[Import("Page3")]
public UserControl Page3;

Wie hier zu sehen ist, werden die Instanzen nicht mehr explizit mittels new erzeugt. Stattdessen sind sie mit einem Import-Attribut versehen. Jedes Import-Attribut erthält eine Zeichenkette, die den Contract zum jeweiligen Export darstellt. Der CompositionContainer wird versuchen die Felder, die mit einem Import gekennzeichnet sind, mit einer passenden Komponente zu initialisieren.

Dafür müssen die UserControls natürlich als Export deklariert werden. Der CodeBehind eines UserControls sieht dann so aus:

[Export("Page1")]
public partial class Page1 : UserControl
{
    public Page1()
    {
        InitializeComponent();
    }
}

Die Klasse wird mit dem Export-Attribut versehen. Das Attribut erhält hier die Zeichenkette "Page1". Das ist der Contract, über den Imports mit derselben Bezeichnung verknüpft werden können. Die Contract-Bezeichnung kann im Export-Attribut weggelassen werden. Ist das der Fall, wird der Klassenname als Contract-Bezeichnung verwendet.

Jetzt fehlt eigentlich nur noch die Herstellung der Verbindung zwischen exportierten und importierenden Parts. Diese Aufgabe übernimmt der CompositionContainer. Im folgenden Quelltext ist zu sehen, wie der Konstructor des Hauptfensters erweitert wurde:

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}
                      };
}

Ein spezieller Katalog wird erzeugt, damit Exports und Imports gefunden werden können, die sich in der aktuellen Assembly befinden. Neben dem AttributedAssemblyPartCatalog gibt es noch diese Katalog-Varianten:
  • AttributedTypesPartCatalog - lädt Erweiterungen, die als Typen angegeben wurden
  • AggregatingComposablePartCatalog - fässt mehrere Cataloge zusammeny
  • DirectoryPartCatalog - lädt Erweiterungen aus einem angegebenen Verzeichnis

Der CompositionContainer wird erzeugt und erhält dabei eine ValueResolver-Instanz des Kataloges. Der Container benötigt diesen Resolver um die einzelnen Instanzen der angeforderten Erweiterungen erzeugen zu können. Nun muss nur noch mittels container.AddPart(this) die aktuelle Instanz des Hauptfensters hinzugefügt werden. Jetzt hat der Container alle Informationen um beim Aufruf der Compose()-Methode alle notwendigen Erweiterungen zusammenzusammeln und die Abhängigkeiten zu mit korrekten Instanzen zu füllen.
In dem vorliegenden Beispiel erkennt das MEF, dass die MainWindow-Instanz Parts importiert, die die Contracts "Page1", "Page2" und "Page3" haben. Dem CompositionContainer wurde ein Katalog zur Verfügung gestellt wurde, der ein exportierenden Part für jeden dieser Contracts enthält. Daher kann im Compose()-Aufruf auch das Setzen der einzelnen Seiten stattfinden.

Was haben wir eigentlich erreicht? Ich gebe zu, der Quelltext ist nicht gerade hübscher geworden. Für dieselbe Funktionalität ist er zudem auch noch um einige Zeilen gewachsen. Allerdings haben wir eine direkte Abhängigkeit zu den UserControls etwas "aufgeweicht". Dort wo vorher die Typen und deren Konstruktor-Aufrufe standen, haben wir nun eine Abstrahierung in Form von Kontrakten.

Noch besser ist, dass an dieser Stelle nicht Schluss ist. Das hier gezeigte Beispiel war nur ein kleiner Auschnitt. In folgenden Artikeln werde ich auf die fortgeschritteren Möglichkeiten des MEF eingehen.

Der Quelltext der Beispiel-Anwendung kann hier heruntergeladen werden.