Schrittweises Entwickeln einer einfachen modularen...

22
Schrittweises Entwickeln einer einfachen modularen Anwendungen mit dem “Composite Application Guidance for WPF and Silverlight 2.0 ” Framework Einführung In diesem Dokument soll das schrittweise Erstellen einer modularen Anwendung mit dem “Composite Application Guidance for WPF” Framework beschrieben werden. Dieses Framework wurde unter dem Codenamen PRISM entwickelt. Dieser Codename wird der Einfachheit halber im Dokument verwendet werden. Die Hauptseite des PRISM Communityprojekte ist http://www.codeplex.com/CompositeWPF . Das PRISM Framework ist unter Microsoft public license veröffentlicht. Aus meiner Sicht wichtige Aspekte dieser Lizenzvereinbarung: das PRISM Framework kann kostenfrei genutzt werden und der unter Verwendung von PRISM erstellte Code muss nicht offengelegt werden. Supportanfragen und Featurerequest kann man nur auf der Codeplex Communityseite stellen, und können dort von der Community beantwortet werden. Die Motivation für dieses Dokument ist, dass viele Geschäftsanwendungen Anforderungen haben, die mit dem PRISM Framework sehr gut abgedeckt werden können. Das Typische für viele Geschäftsanwendungen ist, dass diese Anwendungen zur Abwicklung und Durchführung von bestimmten geschäftlichen Abläufen entwickelt wurden. Diese Anwendungen lassen sich häufig in verschiedene fachliche Module, wie z.B. „Human Resources“, „Custom Relationship Management“, u.v.a.m. unterteilen. Die Anforderungen an Modularität kann aus verschiedenen Gründen für den Entwurf einer Anwendung gewünscht sein: Modularisierte Entwicklung für/durch fachliche Module Modularisierte Softwareverteilung (z.B. der „HR Arbeitsplatz“, der „CRM Arbeitsplatz“, ... ) Anwendungsmodule für den Zugriff auf verschiedene (Web)-Services bzw. Backend- Module Modularisierte Updates U.v.a.m. Diese Anforderungen für modularisierte Anwendungen unterstützt PRISM als integriertes Basiskonzept und es soll in diesem Dokument gezeigt werden, wie einfach die Verwendung von PRISM in Hinblick auf die Umsetzung dieser Anforderungen ist. Die nächsten Seiten beschrieben also die Erstellung einer modularen Rahmenanwendung mit PRISM, um die Grundfunktionen zu demonstrieren und die ersten Schritte zu einer generischen modularen Anwendung zu demonstrieren.

Transcript of Schrittweises Entwickeln einer einfachen modularen...

Page 1: Schrittweises Entwickeln einer einfachen modularen ...download.microsoft.com/.../Prism-HowToGuide_V2.pdfSchrittweises Entwickeln einer einfachen modularen Anwendungen mit dem “Composite

Schrittweises Entwickeln einer einfachen modularen Anwendungen mit dem “Composite Application Guidance for WPF and Silverlight 2.0” Framework

Einführung In diesem Dokument soll das schrittweise Erstellen einer modularen Anwendung mit dem “Composite Application Guidance for WPF” Framework beschrieben werden. Dieses Framework wurde unter dem Codenamen PRISM entwickelt. Dieser Codename wird der Einfachheit halber im Dokument verwendet werden. Die Hauptseite des PRISM Communityprojekte ist http://www.codeplex.com/CompositeWPF. Das PRISM Framework ist unter Microsoft public license veröffentlicht. Aus meiner Sicht wichtige Aspekte dieser Lizenzvereinbarung: das PRISM Framework kann kostenfrei genutzt werden und der unter Verwendung von PRISM erstellte Code muss nicht offengelegt werden. Supportanfragen und Featurerequest kann man nur auf der Codeplex Communityseite stellen, und können dort von der Community beantwortet werden. Die Motivation für dieses Dokument ist, dass viele Geschäftsanwendungen Anforderungen haben, die mit dem PRISM Framework sehr gut abgedeckt werden können. Das Typische für viele Geschäftsanwendungen ist, dass diese Anwendungen zur Abwicklung und Durchführung von bestimmten geschäftlichen Abläufen entwickelt wurden. Diese Anwendungen lassen sich häufig in verschiedene fachliche Module, wie z.B. „Human Resources“, „Custom Relationship Management“, u.v.a.m. unterteilen. Die Anforderungen an Modularität kann aus verschiedenen Gründen für den Entwurf einer Anwendung gewünscht sein:

Modularisierte Entwicklung für/durch fachliche Module

Modularisierte Softwareverteilung (z.B. der „HR Arbeitsplatz“, der „CRM

Arbeitsplatz“, ... )

Anwendungsmodule für den Zugriff auf verschiedene (Web)-Services bzw. Backend-

Module

Modularisierte Updates

U.v.a.m.

Diese Anforderungen für modularisierte Anwendungen unterstützt PRISM als integriertes Basiskonzept und es soll in diesem Dokument gezeigt werden, wie einfach die Verwendung von PRISM in Hinblick auf die Umsetzung dieser Anforderungen ist. Die nächsten Seiten beschrieben also die Erstellung einer modularen Rahmenanwendung mit PRISM, um die Grundfunktionen zu demonstrieren und die ersten Schritte zu einer generischen modularen Anwendung zu demonstrieren.

Page 2: Schrittweises Entwickeln einer einfachen modularen ...download.microsoft.com/.../Prism-HowToGuide_V2.pdfSchrittweises Entwickeln einer einfachen modularen Anwendungen mit dem “Composite

Diese Anleitung soll nicht die Dokumentation zum PRISM Framework ersetzen. Es empfiehlt sich einen kurzen Blick auf die PRISM Dokumenten zu werfen, die umfassend sehr viele wichtige Aspekte von PRSIM beschreibt. Dieses Dokument soll nur einfach nachzuvollziehender Wegweiser für die wichtigsten Arbeitsschritte zum Anlegen einer einfachen modularen Anwendung sein. Zu diesem Guide gibt es ein fertiges Demoprojekt, dass die komplette Lösung enthält. Diese Anleitung wendet sich an Entwickler und technisch versierte Leser.

Technische Voraussetzungen .NET Framework 3.5 (SP1)

Visual Studio (Express, Standard, …)

– (Anmerkung – bei Visual Studio Express gibt es den Projekttyp “WPF User Control

Library” nicht; aber eine „Class library“ mit den entsprechenden WPF Referenzen

kann natürlich einfach erzeugt werden.)

Umsetzungsschritte

Anwendungsshell 1. Bereitstellen des PRISM Frameworks und der Enterprise Library 4.1 (ETL optional)

Wenn man PRISM zur lokalen Entwicklung nutzen will, sollte man zuerst die Enterprise

Library 4.1 installieren.

Zwar ist das PRISM Framework auch ohne den ETL Download nutzbar, da aber PRISM auf

Funktionen der ETL 4.1 aufsetzt und wenn man Dokumentation und ggf. Sourcecode

nachschlagen möchte, ist die Installation der ETL empfehlenswert. Die Enterprise Library

bekommt man von

http://entlib.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=18859 .

Danach muss man das PRISM Framework herunterladen

http://www.microsoft.com/downloads/details.aspx?FamilyID=fa07e1ce-ca3f-4b9b-a21b-

e3fa10d013dd&DisplayLang=en#filelist .

Als Ergebnis des Downloads hat man ein Exe-File auf der lokalen Festplatte, das man

durch Ausführen in ein beliebiges Verzeichnis extrahieren kann. Hierzu sollte man ein

Verzeichnis wählen, in dem man auch bei der täglichen Arbeit Schreiben bzw. Verändern

darf, zum Beispiel um die Quickstarts auszuprobieren....

Im Verzeichnis „<Extract Directory>Source\CAL“ befindet sich der Sourcecode des PRISM

Framework mit Unittests. Dort findet man zwei Visual Studio Solutions, einmal für

Silverlight und für Desktop bzw. WPF.

Leider werden in der aktuellen Version werden die notwendigen Assemblies nicht direkt als Binaries mitinstalliert, sondern man muss diese erst über mitgelieferte Visual Studio 2008 Solutions erstellen. Dies ist dort in der “read me“ beschrieben. Nach dem Erstellen und umkopieren der Assemblies findet man die hier benötigten DLLs im Verzeichnis:

Page 3: Schrittweises Entwickeln einer einfachen modularen ...download.microsoft.com/.../Prism-HowToGuide_V2.pdfSchrittweises Entwickeln einer einfachen modularen Anwendungen mit dem “Composite

„<Extract Directory>Lib“ und dort wird wiederum unterteilt in Desktop, Silverlight und White. Im Verzeichnis Desktop findet man dann folgende Assemblies: Microsoft.Practices.Composite.dll

Microsoft.Practices.Composite.Presentation.dll

Microsoft.Practices.Composite.UnityExtensions.dll

Microsoft.Practices.Composite.UnityExtensions.Tests.dll

Microsoft.Practices.ServiceLocation.dll

Im Unterverzeichnis Unity findet man die Assemblies zum Unity Block aus der Enterprise

Library:

Microsoft.Practices.ObjectBuilder2.dll

Microsoft.Practices.Unity.Configuration.dll

Microsoft.Practices.Unity.dll

Microsoft.Practices.Unity.Interception.Configuration.dll

Microsoft.Practices.Unity.Interception.dll

Microsoft.Practices.Unity.StaticFactory.dll

Im Unterverzeichnis liegen dann die DLLs aus der Enterprise Library die auch in PRISM

Verwendung finden:

Microsoft.Practices.EnterpriseLibrary.Common.dll

Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.dll

Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.dll

Microsoft.Practices.EnterpriseLibrary.Logging.dll

Im Unterverzeichnis CommonServiceLocation liegt auch nochmal das Assembly

Microsoft.Practises.ServiceLocation.dll.

Eine Integration von PRISM als Sourcecode in eine eigene Visual Studio Lösung ist auch möglich, aber dann kompiliert man für jeden Test etc. Immer das PRISM Framework mit.

Page 4: Schrittweises Entwickeln einer einfachen modularen ...download.microsoft.com/.../Prism-HowToGuide_V2.pdfSchrittweises Entwickeln einer einfachen modularen Anwendungen mit dem “Composite

2. Anlegen der Solution

In Visual Studio eine leere („blank“) Solution erstellen. File->New->Project hier dann

unter „Other Project Types“ den Eintrag „Visual Studio Solutions“ auswählen im großen

Fenster dann die „Blank Solution“ anklicken und den Namen der Solution eintragen. In

unserem Beispiel ist der Name der Solution PrismHowTo.

Danach müssen die eigentlichen Projekte angelegt werden. Dazu braucht man

mindestens 3 Projekte:

1. Die Anwendungsshell = das WPF Projekt aus dem das Anwendungsfenster

gestartet wird

2. Das Module 1 Projekt, um die Modularität zu demonstrieren

3. Das Module 2 Projekt, um die Modularität zu demonstrieren

Im Hauptfenster unserer Anwendungsshell werden dann die fachlichen Module ihr UI

visualisieren.

Abbildung 1: Anlegen der Solution

3. Anlegen des Projektes für die Anwendungsshell

Das Hauptprojekt wird als WPF Application Projekt mit dem Namen „ApplicationShell“ angelegt. „File – Add New Project – Visual C# - Windows – WPF Application“.

Page 5: Schrittweises Entwickeln einer einfachen modularen ...download.microsoft.com/.../Prism-HowToGuide_V2.pdfSchrittweises Entwickeln einer einfachen modularen Anwendungen mit dem “Composite

Abbildung 2: 2. Anlegen des Projektes für die Anwendungsshell

4. Hinzufügen der notwendigen Referenzen zu den Prism und Enterprise Library

Assemblies. Die vier zu referenzierenden Assemblies von Prism aus dem Verzeichnis <Extract Directory>Lib \Desktop sind:

Microsoft.Practices.Composite.dll

Microsoft.Practices.Composite.UnityExtensions.dll

Microsoft.Practices.Composite.Presentation.dll

Microsoft.Practices.ServiceLocation.dll Aus dem Unterverzeichnis <Extract Directory>Source\Lib\Desktop\Unity müssen folgende Assemblies referenziert werden:

Microsoft.Practices.Unity.dll

Microsoft.Practices.ObjectBuilder2.dll Dazu auf das Project ApplicationShell rechts klicken und im Contextmenu „Add Reference“ auswählen. Und dann über den Browse Button zu den Prism Libs navigieren und hinzufügen. Dann die Unity Lib Assemblies aus dem entsprechenden Unterverzeichnis referenzieren.

Page 6: Schrittweises Entwickeln einer einfachen modularen ...download.microsoft.com/.../Prism-HowToGuide_V2.pdfSchrittweises Entwickeln einer einfachen modularen Anwendungen mit dem “Composite

Danach sollte das Projekt folgende Referenzen aufweisen.

Abbildung 3: Referenzen zu Prism und Unity Lib Assemblies

5. Sinnvolle Klassenstruktur im ApplicationShell Projekt erstellen

Um den Startvorgang der Anwendung und das Einstellen der Laufzeitumgebung nach den eigenen Bedürfnissen zu gestalten, fügt man eine Klasse Program.cs in das Projekt ein. In der Klasse Program wird es die Main Methode der Anwendung geben. Weiterhin benötigt man eine neue Klasse Bootstrapper.cs, welche ebenfalls hinzugefügt wird, für das Anpassen des PRISM Frameworks. Mit rechter Maustaste auf das Project ApplicationShell klicken und dann mit Add->Class die Program Klasse in Program.cs hinzufügen. Die Klasse Program bekommt vorerst nur eine Methode:

[STAThread()]

public static int Main(string[] args)

{

return 0;

}

Danach kam man in den Properties des Projektes das Startobjekt auf die Program Klasse einstellen. Rechtsklick auf das Project „ApplicationShell“ Properties anklicken.

Page 7: Schrittweises Entwickeln einer einfachen modularen ...download.microsoft.com/.../Prism-HowToGuide_V2.pdfSchrittweises Entwickeln einer einfachen modularen Anwendungen mit dem “Composite

Unter Application dann die Klasse ApplicatioShell.Program als Startup Klasse auswählen.

Abbildung 4: Setzen des Startup Objektes

Auf die gleiche Art und Weise wie vorher jetzt die Klasse Bootsstrapper zum Projekt hinzufügen. Die Bootstrapper Klasse leitet von der Klasse UnityBootstrapper aus dem PRISM Framework ab. In dieser Klasse kann man die PRISM Funktionalität beeinflussen, um das Framework nach seinen Wünschen zu initialisieren. In der Bootstrapper Klasse soll das ShellWindow Klasse als Hautpfenster der Anwendung gesetzt werden. Weiterhin muss das die Methode GetModuleCatalog() überladen werden. In der Methode wird dann die Klasse ConfigurationModuleCatalog erzeugt und liefert ein IModuleCatalog Interface auf die Klasse ConfigurationModuleCatalog zurück. Diese Einstellung bewirkt, dass das App.Config File für das Laden der Module genutzt wird. Der Code sieht jetzt so aus:

Page 8: Schrittweises Entwickeln einer einfachen modularen ...download.microsoft.com/.../Prism-HowToGuide_V2.pdfSchrittweises Entwickeln einer einfachen modularen Anwendungen mit dem “Composite

using System.Windows;

using Microsoft.Practices.Composite.Modularity;

using Microsoft.Practices.Composite.UnityExtensions;

namespace ApplicationShell

{

public class Bootstrapper : UnityBootstrapper

{

// Fields

protected ShellWindow mainWnd;

// Methods

protected override DependencyObject CreateShell()

{

this.mainWnd = new ShellWindow();

return this.mainWnd;

}

protected override IModuleCatalog GetModuleCatalog()

{

ModuleCatalog catalog = new ConfigurationModuleCatalog();

return catalog;

}

// Properties

public ShellWindow ApplicationShellWindow

{

get

{

return this.mainWnd;

}

}

}

}

Die Klasse ShellWindow wird jetzt noch nicht erkannt, da wir diese erst im XAML File anlegen müssen. Die Program Klasse hat als wichtigste Methode die Main Methode. Hier wird der Anwendungsstart mit:

Initialisierung der Fehlerbehandlung,

Initialisierung der DefaultCulture,

Initialisierung der Authentication Policy,

Initialisierung des PRISM Framworks

und dem Start des WPF Applicationobjektes

durchgeführt.

Page 9: Schrittweises Entwickeln einer einfachen modularen ...download.microsoft.com/.../Prism-HowToGuide_V2.pdfSchrittweises Entwickeln einer einfachen modularen Anwendungen mit dem “Composite

#define TRACE

using System;

using System.Diagnostics;

using System.Globalization;

namespace ApplicationShell

{

class Program

{

private static App applicationInstance;

private static Bootstrapper bootStrapper;

[STAThread()]

public static int Main(string[] args)

{

int iRetVal = 0;

try

{

// set the unhandled execption handler to find unhandled

exceptions

AppDomain.CurrentDomain.UnhandledException += new

UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

// set the prefered principal policy for your up domain

AppDomain.CurrentDomain.SetPrincipalPolicy(System.Security.Principal.Pri

ncipalPolicy.WindowsPrincipal);

// set the UI culture for your application

System.Threading.Thread.CurrentThread.CurrentCulture =

System.Threading.Thread.CurrentThread.CurrentUICulture = new

CultureInfo("en-US");

applicationInstance = new App();

// in RELEASE mode - capture unhandled exceptions

#if !DEBUG

applicationInstance.DispatcherUnhandledException += new

System.Windows.Threading.DispatcherUnhandledExceptionEventHandler(applic

ationInstance_DispatcherUnhandledException);

#endif

// initialize the PRISM framework

bootStrapper = new Bootstrapper();

bootStrapper.Run();

// start the WPF application with the Shell window

generated in the bootstrapper

applicationInstance.Run(bootStrapper.ApplicationShellWindow);

}

catch (Exception ex)

{

Trace.WriteLine(ex.ToString());

iRetVal = -1;

}

return iRetVal;

Page 10: Schrittweises Entwickeln einer einfachen modularen ...download.microsoft.com/.../Prism-HowToGuide_V2.pdfSchrittweises Entwickeln einer einfachen modularen Anwendungen mit dem “Composite

}

static void

applicationInstance_DispatcherUnhandledException(object sender,

System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)

{

Trace.WriteLine(e.Exception.ToString());

e.Handled = true;

}

static void CurrentDomain_UnhandledException(object sender,

UnhandledExceptionEventArgs e)

{

Trace.WriteLine(((Exception)e.ExceptionObject).ToString());

}

}

}

6. Visuelle Gestaltung des Anwendungshauptfensters

In dem Anwendungshauptfenster müssen jetzt explizit die Bereiche festgelegt werden, in denen ein Modul visuelle Veränderungen vornehmen kann. Die typische visuelle Veränderung, die ein Modul im Anwendungsfenster vornimmt, ist das Hinzufügen und Entfernen von Controls. Zum Beispiel im Menu, Toolbar, Arbeitsbereich, ... . Das PRISM Framework stellt für die Darstellungsbereiche „Regions“ eine Verwaltungsklasse bereit, die den Zugriff der Module auf die Regionen erlaubt. Damit diese Verwaltungsklasse im Hauptfenster die Bereiche erkennt muss man im WPF XAML Code solche Vermerke eintragen: <StatusBar DockPanel.Dock="Bottom"

prism:RegionManager.RegionName="MainStatusbar" Name="MainStatusbar" Height="30" VerticalAlignment="Bottom">

Anhand dieser Vermerke können bei der Initialisierung des PRISM Frameworks durch den Bootstrapper, im Hauptfenster die festgelegten Regions erkannt werden. Das Hauptfenster dieses Beispiels hat ein Menu, einen Toolbar, einen Statusbar und einen Arbeitsbereich. Im Arbeitsbereich sollen, die eigentliche „Dialoge“ zur Durchführung der fachlichen Funktionen eingeblendet werden. Zuerst wird die Datei Windows1.xaml im Solution Explorer in ShellWindow.xaml umbenannt. Nach eintragen der Regions für diese Anwendung sieht die Datei ShellWindow.xaml dann so aus:

Page 11: Schrittweises Entwickeln einer einfachen modularen ...download.microsoft.com/.../Prism-HowToGuide_V2.pdfSchrittweises Entwickeln einer einfachen modularen Anwendungen mit dem “Composite

<Window x:Class="ApplicationShell.ShellWindow"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:prism="http://www.codeplex.com/CompositeWPF"

Title="ShellWindow" Height="768" Width="1024">

<DockPanel Height="Auto" Width="Auto" LastChildFill="true">

<Menu DockPanel.Dock="Top" Height="30" Width="Auto"

Name="MainMenu" VerticalAlignment="Center"

HorizontalContentAlignment="Center">

<MenuItem Header="File" VerticalAlignment="Center"

HorizontalAlignment="Center" Margin="1">

<MenuItem Header="Exit" Click="MenuItem_Click" ></MenuItem>

</MenuItem>

<MenuItem Header="Module functions" Width="Auto"

prism:RegionManager.RegionName="ExtensionMenu"

VerticalAlignment="Center"

HorizontalAlignment="Center" Margin="1"></MenuItem>

<MenuItem Header="Help" VerticalAlignment="Center"

HorizontalAlignment="Center" Margin="1"></MenuItem>

</Menu>

<ToolBar DockPanel.Dock="Top"

prism:RegionManager.RegionName="MainToolbar" Name="MainToolbar" Height="35"

Width="Auto" VerticalAlignment="Top">

</ToolBar>

<StatusBar DockPanel.Dock="Bottom"

prism:RegionManager.RegionName="MainStatusbar" Name="MainStatusbar"

Height="30" VerticalAlignment="Bottom">

</StatusBar>

<ItemsControl Name="WorkArea"

prism:RegionManager.RegionName="WorkArea" Background="LightGray">

</ItemsControl>

</DockPanel>

</Window>

Um das Ganze jetzt vollständig zu machen müssen wir noch in der Datei ShellWindow.xaml.cs einen Handler für den Click Event im Menu auf den Eintrag „Exit“ hinzufügen. Am einfachsten geht dies, wenn man im Xaml Source (Shellwindow.xaml) mit der rechten Maustaste auf den String "MenuItem_Click" klickt und dann im Contextmenu „Navigate To Event Handler“ anwählt. Im C# File muss dann auch noch „Window1“ durch „ShellWindow“ ersetzt werden, so dass die Datei dann folgendermaßen aussieht:

Page 12: Schrittweises Entwickeln einer einfachen modularen ...download.microsoft.com/.../Prism-HowToGuide_V2.pdfSchrittweises Entwickeln einer einfachen modularen Anwendungen mit dem “Composite

using System.Windows;

namespace ApplicationShell

{

/// <summary>

/// Interaction logic for ShellWindow.xaml

/// </summary>

public partial class ShellWindow : Window

{

public ShellWindow()

{

InitializeComponent();

}

private void MenuItem_Click(object sender, RoutedEventArgs e)

{

Application.Current.Shutdown();

}

}

}

7. Konfiguration in der App.Config Jetzt müssen wir noch eine Applikations Konfigurationsdatei zum Projekt hinzufügen. Hierzu im Solution Explorer mit der rechten Maustaste auf ApplicationShell klicken und dann über Add->New Item, dann im Dialog „Application Configuration File“ auswählen und den Namen App.Config eingeben.

Abbildung 5. Hinzufügen der App.config Datei

In dieser App.Config können wir neben vielen anderen Einstellungen auch konfigurieren, welche Module zum Anwendungsstart von PRISM geladen werden sollen. Dazu benötigt die App.Config mindestens folgende Einträge:

Page 13: Schrittweises Entwickeln einer einfachen modularen ...download.microsoft.com/.../Prism-HowToGuide_V2.pdfSchrittweises Entwickeln einer einfachen modularen Anwendungen mit dem “Composite

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

<configSections>

<section name="modules"

type="Microsoft.Practices.Composite.Modularity.ModulesConfigurationSecti

on, Microsoft.Practices.Composite"/>

</configSections>

<modules>

</modules>

</configuration>

Die im Bootstrapper festgelegte ConfigurationModuleCatalog Klasse liest diese Konfigurationseinträge aus.

Anlegen eines Modules 8. Anlegen des Projektes für ein Modul

Um ein fachliches Modul anzulegen, müssen wir in der Solution ein neues Projekt hinzufügen. Wir benötigen ein neues Projekt, da wir ja die Fachlichkeit im Modul und die Funktionen des Anwendungsrahmens explizit trennen wollen. „File – Add New Project – Windows – WPF User Control Library“

Abbildung 6: Hinzufügen des Projektes für ein Modul

9. Setzen der notwendigen Referenzen

Durch den Projekt Typ „WPF User Control Library“ sind alle für WPF notwendigen Referenzen schon im neu erzeugten Projekt gesetzt.

Page 14: Schrittweises Entwickeln einer einfachen modularen ...download.microsoft.com/.../Prism-HowToGuide_V2.pdfSchrittweises Entwickeln einer einfachen modularen Anwendungen mit dem “Composite

PRISM benötigt in den Modulen nur zwei Referenzen aus dem Verzeichnis <extract directory>\Lib\Desktop und zwar:

Microsoft.Practices.Composite.dll

Microsoft.Practices.Composite.Presentation.dll Und eine DLL aus dem Unity Unterverzeichnis

Microsoft.Practices.Unity

10. Anlegen der ModulInit Klasse Während des Ladevorgangs eines Moduls ruft das PRISM Framework in einem Modul eine bestimmte Initialisierungs-Methode einmalig auf. Das gibt dem Modul die Möglichkeit, notwendige Erst-Initialisierungsschritte auszuführen, z.B. das Anlegen von Menu- und Toolbareinträgen. Zu Erzeugung dieser Klasse fügt man zum Modul-Projekt eine Klasse „ModulInit“ hinzu. Diese Klasse muss das Interface IModule aus dem PRISM Framework implementieren und public sein, damit sie von Moduleloader aus dem Hauptassembly (= ApplicationShell) aufgerufen werden kann. Die Implementierung des Interfaces IModule benötigt die Implementierung einer Methode „public void Initialize()“, die während der Module Initialisierung Sequenz ausgeführt wird.

11. Der Constructor in der ModulInit Klasse Durch den verwendeten Unity Applicationblock kann man sich bei der Initialisierung eines Modules im Constructor vorhandene notwendige globale Funktionen/Variablen übergeben lassen. Ein typisches Beispiel für dieses Szenario ist es, sich den UnityContainer und den RegionManager in Constructor des Modules zuweisen zu lassen. Dieser Code hierzu sieht folgendermaßen aus: public class ModuleInit : IModule {

private readonly IUnityContainer unityCnt;

private readonly IRegionManager regionMgr;

public ModuleInit(IUnityContainer container,

IRegionManager regionManager)

{

unityCnt = container;

regionMgr = regionManager;

}

}

Wie schon erwähnt, wird beim Laden des Modules durch das Unity Framework hier die richtige Zuweisung von Variablen ausgeführt; - via Dependency Injection. Damit stehen im Module alle notwendigen Funktionen zur Verfügung, um z.B. die Regionen im Anwendungshauptfenster mit eigenen Controls zu benutzen.

Page 15: Schrittweises Entwickeln einer einfachen modularen ...download.microsoft.com/.../Prism-HowToGuide_V2.pdfSchrittweises Entwickeln einer einfachen modularen Anwendungen mit dem “Composite

12. ModulInitialisierung

Man sollte sich die Logik, die während der Modulinitialisierung durchgeführt wird, genau überlegen. Da ein Modul nicht als alleiniges Modul in einem Hauptfenster ausgeführt wird, ist es empfehlenswert geteilte Darstellungsressourcen während der Initialisierung nicht für sich allein zu beanspruchen. Wenn ein anderes, uns nicht bekanntes Modul ebenso vorgeht, sind bestimmte UI Bereiche eventuell nicht sichtbar. Ein Beispiel wäre hier in unserem Sample die „WorkArea“, das ist ein Darstellungsbereich, in dem typischerweise nur das aktive Modul sichtbar ist und nicht mehrere Module gleichzeitig sichtbar sein sollten. Ein sinnvolles Vorgehen während der Modulinitialisierung ist es nur in zentralen Steuerungselementen, wie Toolbars und Menus, Einträge zu machen, die es dem Benutzer erlauben spezielle Modulfunktionen auf diesem Weg zu starten. Der Code eines sinnvollen ModulInit könnte also so aussehen:

using System.Diagnostics;

using System.Windows;

using System.Windows.Controls;

using Microsoft.Practices.Composite.Modularity;

using Microsoft.Practices.Composite.Regions;

using Microsoft.Practices.Unity;

using SharedFuncs.Extensions;

namespace ModuleCRM

{

public class ModuleInit : IModule

{

// central controls to trigger business functionality inside the moduel

private MenuItem mniStartSearch;

private Button btnStartSearch;

// variables for usefull PRISM/UNITY framework classes

private readonly IUnityContainer unityCnt;

private readonly IRegionManager regionMgr;

// the constructor which assigns the necessary framework classes

public ModuleInit(IUnityContainer container, IRegionManager

regionManager)

{

unityCnt = container;

regionMgr = regionManager;

}

#region IModule Members

// the module initialization sequence

public void Initialize()

{

Trace.WriteLine("ModuleCRM.ModuleInit.Initialize() ...");

#region init ui

// create a menu item to call the "Start search handler"

Page 16: Schrittweises Entwickeln einer einfachen modularen ...download.microsoft.com/.../Prism-HowToGuide_V2.pdfSchrittweises Entwickeln einer einfachen modularen Anwendungen mit dem “Composite

IRegion r = regionMgr.Regions["ExtensionMenu"];

if (r != null)

{

mniStartSearch = new MenuItem() { Header = "Start search

customer" };

mniStartSearch.Click += StartSearchEvtHdl;

r.Add(mniStartSearch);

}

// create a toolbar button to call the "Start search handler"

r = regionMgr.Regions["MainToolbar"];

if (r != null)

{

btnStartSearch = new Button() { Content = "Start search

customer" };

btnStartSearch.Click += StartSearchEvtHdl;

r.Add(btnStartSearch);

}

#endregion init ui

}

#endregion IModule Members

// the "Start search handler" creates an search UI with module

specific functionality

// and shows it in the Workarea

private void StartSearchEvtHdl(object sender, RoutedEventArgs e)

{

IRegion r = regionMgr.Regions["WorkArea"];

if (r != null)

{

r.RemoveAllOtherControlsFromRegion();

VwCrmSearch vw = new VwCrmSearch();

r.Add(vw);

}

}

}

}

Die Modulinitialisierung ist ggf. auch ein geeigneter Zeitpunkt, um Berechtigungsüberprüfungen durchzuführen. Im Module selbst weiß man, welche Fachlichkeit geladen werden sollte und könnte hiererstmalig die Berechtigung überprüfen. Z.B. darf User XYX das Module HR benutzen? Bei den einzelnen fachlichen Methoden eines Modules müssen natürlich noch granularere Berechtigungsprüfungen stattfinden.

13. Module UI

Es gibt verschiedene Weg aus einem Module in den Regionen des Hauptfensters sichtbar zu werden. Die einfachste Variante ist es, Basiscontrols aus WPF zu verwenden. Wenn man eine komplexere fachlichere Logik, zum Beispiel für eine Suchemaske mit Ergebnisanzeige, darstellen muss, empfiehlt es sich WPF User Controls zu verwenden. Ein WPF User Control kann in den Regionen des Hauptfensters dargestellt werden und bieten während der Anwendungsentwicklung WYSWIG Designer Unterstützung im Visual Studio.

Page 17: Schrittweises Entwickeln einer einfachen modularen ...download.microsoft.com/.../Prism-HowToGuide_V2.pdfSchrittweises Entwickeln einer einfachen modularen Anwendungen mit dem “Composite

Abbildung 7: Hinzufügen eines WPF User Controls zum Modulprojekt

Hier der Xaml Code des User Controls, in unserem Fall nur ein Button und ein Texteingabe feld: <UserControl x:Class="ModuleCRM.VwCrmSearch"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Height="768" Width="1024">

<Grid>

<Button Height="22" HorizontalAlignment="Left" Margin="32,30,0,0"

Name="btnStartSearch" VerticalAlignment="Top" Width="159"

Click="btnStartSearch_Click">Start Search Customer</Button>

<TextBox Height="20" Margin="228,30,238,0" Name="tbxSearchString"

VerticalAlignment="Top" />

</Grid>

</UserControl>

Über den Rechtsklick auf „btnStart Search_Click“ und Navigate toHandler wir wiederum ein EventHandler für den Click Event erzeugt. In VwCrmSearch.xaml.cs:

Page 18: Schrittweises Entwickeln einer einfachen modularen ...download.microsoft.com/.../Prism-HowToGuide_V2.pdfSchrittweises Entwickeln einer einfachen modularen Anwendungen mit dem “Composite

using System.Windows;

using System.Windows.Controls;

namespace ModuleCRM

{

/// <summary>

/// Interaction logic for VwCrmSearch.xaml

/// </summary>

public partial class VwCrmSearch : UserControl

{

public VwCrmSearch()

{

InitializeComponent();

}

private void btnStartSearch_Click(object sender, RoutedEventArgs e)

{

}

}

}

14. Modulübergreifende Funktionen In der Methode „StartSearchEvtHdl” wird eine Funktion „RemoveAllOtherControlsFromRegion” verwendet, die nicht Bestandteil des PRISM Frameworks ist. Diese Methode stellt bei der Anzeige eines WPF UserControls sicher, dass alle anderen Controls aus der „WorkArea“ Region entfernt werden. Diese Funktionalität ist so allgemein, dass sie modulübergreifend verwendet werden kann. Deswegen kann man ein extra Assembly erzeugen (Solution im Solution Explorer anklicken und dann mit: „File – Add – New Project – Windows – WPF User Control Library “ Als Namen „SharedFuncs“ eintragen), dass für alle UI Klassen gemeinsame Funktionen enthält. Mit dem Anlegen des Assemblies als „WPF User Control Library“ sind alle für WPF notwendigen Referenzen gesetzt. Jetzt muss man nur noch die für PRISM notwendigen Referenzen setzen – siehe Abscnitt: 1 In dem Module kann man jetzt eine Klasse „IRegionExtensions“ anlegen, mit der das IRegion Interface um sinnvolle Extensionsmethoden erweitert wird. Der Code dazu sieht so aus:

using System.Collections.Generic;

using System.Windows;

using System.Windows.Controls;

using Microsoft.Practices.Composite.Regions;

namespace SharedFuncs.Extensions

{

public static class IRegionExtension

{

public static void HideAllOtherControlsInRegion(this IRegion r)

{

if (r != null)

{

IEnumerator<object> o = r.ActiveViews.GetEnumerator();

Page 19: Schrittweises Entwickeln einer einfachen modularen ...download.microsoft.com/.../Prism-HowToGuide_V2.pdfSchrittweises Entwickeln einer einfachen modularen Anwendungen mit dem “Composite

while (o.MoveNext())

{

if (o.Current is System.Windows.Controls.Control)

{

((Control)o.Current).Visibility =

Visibility.Collapsed;

}

}

}

}

public static void RemoveAllOtherControlsFromRegion(this IRegion

r)

{

if (r != null)

{

List<Control> lstControls = new List<Control>();

IEnumerator<object> o = r.ActiveViews.GetEnumerator();

while (o.MoveNext())

{

if (o.Current is System.Windows.Controls.Control)

{

lstControls.Add(((Control)o.Current));

}

}

foreach (Control c in lstControls)

{

r.Remove(c);

}

}

}

}

}

Um diese gemeinsam genutzten Methoden im Module sichtbar zu machen, muss dieses noch referenziert werden und der Namespace mit using SharedFuncs.Extensions; eingebunden werden 15. Module Deployment

Das Projekt kompiliert das erzeugte Module nun schon, die Moduleinitialisierung ist vorbereitet, die Registierung in Menu und Toolbar sind vorgesehen und es könnte auf Benutzereingaben spezielle Funktionen ausgeführt werden. Was fehlt noch??? - Nun, die ApplicationShell muss das neue Modul laden, damit die Funktionalität ausführbar ist. Dazu muss man in der App.Config folgende Einträge haben:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

<configSections>

<section name="modules"

type="Microsoft.Practices.Composite.Modularity.ModulesConfigurationSecti

on, Microsoft.Practices.Composite"/>

</configSections>

<modules>

Page 20: Schrittweises Entwickeln einer einfachen modularen ...download.microsoft.com/.../Prism-HowToGuide_V2.pdfSchrittweises Entwickeln einer einfachen modularen Anwendungen mit dem “Composite

<module assemblyFile="ModuleCRM.dll"

moduleType="ModuleCRM.ModuleInit, ModuleCRM" moduleName="Module Customer

Relationship Management"/>

</modules>

</configuration>

Dieser Eintrag in der Konfigurationsdatei wird von ConfigurationModuleLoader ausgewertet und der Ladevorgang und die Initialiiserung für das Module ausgeführt. Sehr wichtig ist der nächste Schritte Das Assembly des neuen Modules muss in das Directory des ApplicationShell (oder ein Subdirectory) kopiert werden, damit der Loader das Assemblyfile findet. Diesen Kopiervorgang kann man automatisieren, indem man auf das Post-Build Ereignis des Modules diesen Eintrag setzt:

xcopy "$(TargetDir)*.*" "$(SolutionDir)ApplicationShell\$(OutDir)" /S /Y

Das Post-Build Ereignis kann man in den Properties des Projektes im Tab “Build Events” setzen.

Abbildung 8: Konfigurieren des Post Build Ereignis für automatisches Kopieren

16. Die Ausgabe der Bootstrapper Klasse im Output Windows bzw. Debug Console

In dieser Ausgabe kann man gut nachverfolgen, welche Module geladen wurden usw. ApplicationShell.vshost.exe Information: 0 : Creating Unity container

Page 21: Schrittweises Entwickeln einer einfachen modularen ...download.microsoft.com/.../Prism-HowToGuide_V2.pdfSchrittweises Entwickeln einer einfachen modularen Anwendungen mit dem “Composite

'ApplicationShell.vshost.exe' (Managed): Loaded

'C:\martinv\scratch\VS2008\PrismHowTo\ApplicationShell\bin\Debug\Microso

ft.Practices.ObjectBuilder2.dll'

ApplicationShell.vshost.exe Information: 0 : Configuring container

ApplicationShell.vshost.exe Information: 0 : Configuring region adapters

ApplicationShell.vshost.exe Information: 0 : Creating shell

'ApplicationShell.vshost.exe' (Managed): Loaded

'C:\Windows\assembly\GAC_MSIL\PresentationFramework.Aero\3.0.0.0__31bf38

56ad364e35\PresentationFramework.Aero.dll', Skipped loading symbols.

Module is optimized and the debugger option 'Just My Code' is enabled.

ApplicationShell.vshost.exe Information: 0 : Initializing modules

'ApplicationShell.vshost.exe' (Managed): Loaded

'C:\martinv\scratch\VS2008\PrismHowTo\ApplicationShell\bin\Debug\ModuleC

RM.dll', Symbols loaded.

ModuleCRM.ModuleInit.Initialize() ...

ApplicationShell.vshost.exe Information: 0 : Bootstrapper sequence

completed

17. Hinzufügen weiterer Module

Auf diese Weise kann man nun beliebige andere Module erzeugen und in der ApplicationShell benutzbar machen.

Zusammenfassung & nächste Schritte Mit diesen einfachen Umsetzungsschritten kann man eine generische modulare Anwendung schreiben. Sollte man die Anwendung später ausbauen, werden schnell weitere technische Anforderungen hinzukommen. Um die gewonnen Modularität beizubehalten sollte man sich mit:

dem Konzept der WPFCommand Infrastructure und den Delegate/Composite

Commands im Prism für globale, entkoppelte Kommandos zwischen der WPF

Bedienoberfläche/-Elementen und den Modulen

dem Eventkonzept in PRISM für die fachliche, entkoppelte Kommunikation zwischen

Modulen

vertraut machen. Eine architekturelle, konzeptionelle Frage, die PRISM explizit nicht mit dem Framework beantwortet, ist die Frage nach der logischen Organisation des UI Codes. Zum Beispiel, setzt man Model-View-Controller Pattern oder Model-View-Presenter Pattern in den Modulen ein. Um die Codestruktur in den fachlichen Modulen zu organisieren, ist es zwingend empfohlen vor dem Start eines echten Projektes mit PRISM hier architekturellen Vorgaben zu entwerfen und festzulegen.

Page 22: Schrittweises Entwickeln einer einfachen modularen ...download.microsoft.com/.../Prism-HowToGuide_V2.pdfSchrittweises Entwickeln einer einfachen modularen Anwendungen mit dem “Composite

Links Beispiel Code http://code.msdn.microsoft.com/Project/Download/FileDownload.aspx?ProjectName=PrismHowTo&DownloadId=3278 PRISM Community Seite auf Codeplex http://www.codeplex.com/CompositeWPF Enterpise Library Community Seite auf Codeplex http://www.codeplex.com/entlib WPF Windows Presentation Foundation http://msdn.microsoft.com/en-us/netframework/aa663326.aspx Windows Client Informationen http://windowsclient.net/