Webparts - Microsoft Press
Transcription
Webparts - Microsoft Press
Kapitel 4 Webparts In diesem Kapitel: 쮿 Sie lernen den Unterschied zwischen der herkömmlichen ASP.NET-Entwicklung und der SharePointEntwicklung mit Webparts kennen. 쮿 Sie beschäftigen sich mit den Grundlagen der Webpartentwicklung, der Fehlersuche und Bereitstellung für WSS. 쮿 Sie erfahren, wie persönliche Einstellungen und Anpassungen in Webparts erfolgen. 쮿 Sie erstellen verbindungsfähige Webparts. 쮿 Sie arbeiten im SharePoint-Websiteobjektmodell mit Webparts. Ted Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7) 113 114 Kapitel 4: Webparts Webparts Die Hauptaufgabe eines SharePoint-Entwicklers ist die Entwicklung von wiederverwendbaren Komponenten für Geschäftskunden. Die Geschäftskunden wiederum verwenden diese Komponenten zur Erstellung von Anwendungen, zur Anpassung der Anwendung an spezielle Aufgaben und zur Einstellung der Anwendungen nach ihren persönlichen Vorlieben und Arbeitsweisen. Diese Vorgehensweise steht im Gegensatz zur Entwicklung der typischen Windows- oder Websoftware, die normalerweise als geschlossene Einheit bereitgestellt wird. Mit der WSS-Technologie entwickelt sich eine Anwendung weiter, wenn Geschäftskunden mit den bereitgestellten Komponenten ihre eigenen Anwendungen und Arbeitsbereiche zusammenstellen. Webparts werden in der Website zur Verbesserung der Zusammenarbeit und der Integration der Anwendungen verwendet. Webparts sind wichtige Grundbausteine der SharePoint-Benutzeroberfläche. Mit ihnen lassen sich viele verschiedene Anwendungsarten erstellen. Die bereits vorhandenen Webparts können Ihnen als Beispiele für die Entwicklung eigener Webparts dienen. Das DataViewWebpart lässt sich zum Beispiel so einstellen, dass es Daten aus einer beliebigen geeigneten Datenquelle auf verschiedene Arten anzeigt. Die Webparts aus dem Lieferumfang von WSS sind allgemein gehalten und für die Wiederverwendung vorgesehen. Auch wenn Sie mit Webparts sehr spezielle Anwendungen erstellen, empfiehlt es sich, die Webparts so allgemein anwendbar wie möglich zu halten, um Kunden, die Webparts für eine spezielle Aufgabe verwenden, die größtmögliche Flexibilität zu bieten. Integrierte Webparts WSS wird mit einer ganzen Reihe von Webparts geliefert, die Ihre Anforderungen vielleicht schon erfüllen können. Die Webpartausstattung von Microsoft Office SharePoint Server ist noch umfangreicher. Um Ihre Anwendung in SharePoint-Websites zu integrieren, ist es nicht immer erforderlich, Programmcode zu schreiben. Das gilt besonders für Geschäftsdaten. Es gibt viele Microsoft-Webparts, die sich unter Verwendung der Webpartdateien bereitstellen und anpassen oder in anderen Webparts als Websteuerelemente (WebControls) einsetzen lassen. Die folgenden Webparts stellen nur eine Auswahl dar: Inhalts-Editor-Webpart Verwenden Sie dieses Webpart zur Anzeige von statischen HTML-Inhalten mit einem WYSIWYG-Editor oder zur Darstellung einer Textdatei. DataViewWebpart Zur Anzeige von Datenbanken, XML oder SharePoint-Listendaten mit umfangreicher Designunterstützung durch den Microsoft SharePoint Designer. Listen-Viewer-Webpart Zur Anzeige von Listendaten aus allen Listen der SharePoint-Website. Bildwebpart Verwenden Sie dieses Webpart zur Anzeige eines Bilds. Websitebenutzer-Webpart Verwenden Sie dieses Webpart zur Anzeige der Mitglieder einer Website. Seiten-Viewer-Webpart Verwenden Sie dieses Webpart zur Anzeige einer vorhandenen Webseite in einem IFrame. Grundlagen der Webparts Ein Webpart ist eine Klasse, die von der im Namespace System.Web.UI.WebControls.WebParts definierten Klasse WebPart aus der Assembly System.Web abgeleitet wird. Das Webpart ist eine spezielle Art von Websteuerelement, das innerhalb eines Webpartzonensteuerelements verwendet werden kann, nachdem die betreffende Seite erstellt und bereitgestellt wurde. Ein Webpart ist lose mit der Seite verbunden, auf Ted Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7) 115 Webparts der es verwendet wird, sofern die Webpartinfrastruktur installiert und funktionsfähig ist. (Informationen über die Erstellung von benutzerdefinierten Webpartseiten finden Sie in Kapitel 3, »Seiten und Design«.) Webparts arbeiten mit einem WebPartManager-Steuerelement zusammen, das die Webpartinstanzen entweder beim Entwurf der Seite oder zur Laufzeit zu den Webpartzonen hinzufügt und verwaltet. Der Webpartmanager verwaltet die Seite und fügt bei der Initialisierung der Seite Webparts aus der Personalisierungsdatenbank zu den Webpartzonen hinzu. Sie können auch außerhalb von SharePoint in herkömmlichen ASP.NET-Anwendungen Webparts erstellen, aber das Webpartframework von WSS 3.0 bietet umfangreiche zusätzliche Funktionen, wie zum Beispiel ein dynamisches SharePoint-Websitemodell, ein Templateframework, ein Sicherheitsframework und die Webpartverwaltung in der WSSWebpartgalerie. Abbildung 4.1 stellt die Vererbung zwischen den Webpartzonen- und Webpartmanagersteuerelementen von SharePoint und den Webpartframeworkklassen von ASP.NET dar. Beachten Sie bitte, dass Sie die Webpartklassen von ASP.NET als Basisklassen für Ihre Webpartanwendungen verwenden, obwohl SharePoint ebenfalls Implementierungen der Webpartzone und des Webpartmanagers enthält. Abbildung 4.1 Die SharePoint-Implementierung des Webpartframeworks SharePoint verwendet die Webpartmanagerklasse SPWebPartManager, die man sich als Brücke zwischen den Webpartzonenobjekten der Seite und der Inhaltsdatenbank vorstellen kann. Wenn Sie ein Webpart zu einer Seite hinzufügen, dann fügen Sie eine serialisierte Instanz des Webparts zur Inhaltsdatenbank hinzu. Sie können in Modulen von Features auch Standardwebparts deklarieren und zu Seiten hinzufügen, wie im vorigen Kapitel gezeigt. Um ein einfaches Webpart zu entwickeln, leiten Sie von System.Web. UI.WebControls.WebParts.WebPart eine neue Klasse ab. Wenn Sie in einer Seite Text oder HTML anzeigen möchten, überschreiben Sie einfach die Methode RenderContents. Dabei verwenden Sie dieselbe Syntax, die Sie bei der Erstellung eines einfachen Websteuerelements für eine ASP.NET-Anwendung verwenden. Ted Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7) 116 Listing 4.1 Kapitel 4: Webparts Ein sehr einfaches Webpart namespace LitwareWebParts { // Ein sehr einfaches Webpart public class HelloWebPart : System.Web.UI.WebControls.WebParts.WebPart { protected override void RenderContents(System.Web.UI.HtmlTextWriter writer) { writer.Write(string.Format("Hallo, {0}!", this.Page.User.Identity.Name)); } } } Webparts werden in einem Anzeigerahmen angezeigt, der auch Chrome genannt wird. Damit sind die allgemeinen Benutzeroberflächenelemente gemeint, wie eine formatierte Titelleiste und die Rahmen um den Körper des Webparts, den das Framework zu Steuerelementen hinzufügt. Dieser Anzeigerahmen gibt Ihrem Webpart ein gewisses Format, das zur restlichen Benutzeroberfläche der Anwendung passt. Die Anzeige des Rahmens wird vom Anwendungsframework übernommen. Wenn dieses Webpart in WSS bereitgestellt wird, erfolgt die Anzeige mit dem Titel des Webparts und einem Dropdownmenü mit Anpassungsoptionen. Der Webpartrahmen ist für die Anzeige der allgemeinen Eigenschaften und Stilvorgaben, des Menüs und der Verwaltungsoberflächenelemente zuständig. Abbildung 4.2 zeigt, wie der Anzeigerahmen für das HelloWebPart-Beispiel aussieht. Beachten Sie bitte, dass der Anzeigerahmen nur dann vom Webpartframework angezeigt wird, wenn Webparts in Webpartzonen bereitgestellt werden. Er wird nicht angezeigt, wenn das Webpart deklarativ als Websteuerelement bereitgestellt wird. Abbildung 4.2 Auch das einfachste Webpart wird mit einem Anzeigerahmen (auch Chrome genannt) angezeigt Da der Anzeigerahmen vom Anwendungsframework angezeigt wird (in unserem Fall also von WSS), lassen sich Anwendungen, die aus mehreren Webparts zusammengestellt werden, leicht und ohne zusätzliche Trainingskosten oder Lernkurven von Benutzern des Portals warten. Sobald die Benutzer gelernt Ted Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7) 117 Webparts haben, wie man Webparts mit dem Toolbereich von SharePoint hinzufügt oder entfernt, können sie auch Anwendungen verwenden, die als Webparts für sie bereitgestellt werden. Grundlagen der Websteuerelemente Da es sich bei Webparts um WebControl-Klassen handelt, ist für die Programmierung von Webparts ein gewisses Grundlagenwissen über Websteuerelemente erforderlich. Websteuerelemente gehören ebenfalls zu den Komponenten der Benutzeroberfläche, die Sie in Ihren Webpartlösungen verwenden können, ob Sie nun eigene Websteuerelemente schreiben oder die Standardelemente von ASP.NET oder von SharePoint verwenden, wie zum Beispiel das SPGridView-Steuerelement. Abbildung 4.3 zeigt das Webpartklassenmodell. Die Klasse WebPart wird vom Panel-Websteuerelement abgeleitet und implementiert die Schnittstelle IWebEditable, die eine Bereitstellung und Anpassung im Webpartframework ermöglicht. Sie können in Ihrem eigenen benutzerdefinierten Webpart auch Webparts als Steuerelemente einsetzen, einschließlich der WSS-Webparts von Microsoft. Abbildung 4.3 von ASP.NET Das WebPartklassenmodell HINWEIS Webparts für die SharePoint-Bereitstellung werden von der Klasse System.Web.UI.WebControls.WebParts. WebPart abgeleitet. Sie können in ASP.NET 2.0 zwar ein Benutzersteuerelement (.ascx-Datei) als Webpart verwenden (mit GenericWebPart als Verpackung), aber das GenericWebPart ist in SharePoint nicht zur Bereitstellung geeignet. SharePoint ist restriktiver als ASP.NET 2.0, weil Sie keine Benutzersteuerelemente direkt als Webparts verwenden können. Stattdessen müssen Sie eine indirekte Methode verwenden, bei der Sie eine Art Verpackungswebparts (wrapper web parts) schreiben, die dynamisch Benutzerelemente laden und aufnehmen. Wenn Sie herausfinden möchten, wie weit Sie diese Idee weiterentwickeln können, lesen Sie die Informationen über das Smart Part (http://www.SmartPart.info), das von Jan Tielens berühmt gemacht wurde. Ted Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7) 118 Kapitel 4: Webparts Es gibt einige Methoden, die für die Lebensdauer eines Steuerelements im Speicher des Computers entscheidend sind. OnLoad dient zwar zur Initialisierung des Steuerelements, ist aber nicht dafür vorgesehen, Daten zu laden oder andere Funktionen zu übernehmen. OnPreRender wird zur Einleitung von Vorgängen verwendet, die etwas länger dauern, wie zum Beispiel Datenbankabfragen oder asynchrone Aufrufe von Webdiensten. Die Page-Klasse meldet zudem ein PreRenderComplete-Ereignis, wenn alle Anzeigevorarbeiten der Seite abgeschlossen sind. Wenn Sie für die Anzeige Steuerelemente verwenden, ist CreateChildControls die einzige Methode, die Sie für die Benutzeroberflächenkomponenten zu implementieren brauchen. Die Anzeige der Steuerelemente aus der Steuerelementsammlung des Websteuerelements wird vom Framework gesteuert. Normalerweise brauchen Sie also keine Ausgabemethoden zu überschreiben, sofern Sie nicht direkt mit der HtmlTextWriter arbeiten müssen. Wenn Sie Ihre eigenen Steuerelemente selbst anzeigen müssen, können Sie das in der Methode RenderContents tun. Die Anzeige erfolgt dann im Anzeigerahmen (Chrome) des Websteuerelements. Vielleicht möchten Sie Ihre Steuerelemente auch wegen spezieller Anforderungen selbst anzeigen, beispielsweise zur Anzeige in einer Tabelle oder in einem anderen HTML-Layout. Um Postbackdaten aus einem enthaltenen Steuerelement annehmen zu können, müssen Sie einen privaten Verweis auf Ihr Steuerelement erstellen und es in der Methode CreateChildControls erstellen. Vor dem Zugriff auf das Steuerelement können Sie mit dem Aufruf der Methode EnsureChildControls dafür sorgen, dass es existiert. Tabelle 4.1 beschreibt den Lebenszyklus eines Webparts in Form des Ablaufs der Methodenaufrufe, wobei auch die Standardmethoden und Ereignisse bei der Erstellung von Webparts aufgeführt sind. Allerdings gibt es noch einige Methoden, deren Aufruf sich nicht in einen genau definierten Ablauf einordnen lässt. Dazu gehören CreateChildControls, EnsureChildControls und Methoden, die mit den Attributen ConnectionConsumer und ConnectionProvider gekennzeichnet sind. Bevor Sie auf die Eigenschaften zusammengesetzter Steuerelemente zugreifen, muss die Methode EnsureChildControls aufgerufen werden, um Ausnahmen durch Nullverweise zu vermeiden. Tabelle 4.1 Der Lebenszyklus eines Webparts Methode/Ereignis Beschreibung OnInit Übernimmt die Initialisierung des Steuerelements. OnLoad Wird beim Load-Ereignis aufgerufen. CreateChildControls Erstellt alle untergeordneten Steuerelemente, die zu den Bestandteilen eines zusammengesetzten Steuerelements gehören. EnsureChildControls Sorgt dafür, dass CreateChildControls ausgeführt wurde. Verwenden Sie diese Methode, um vor dem Zugriff auf die Daten eines Steuerelements sicherzustellen, dass das Steuerelement existiert. OnPreRender Führt Arbeiten durch oder leitet sie ein, die vor der Anzeige eines Steuerelements erledigt werden müssen, wie zum Beispiel das Laden von Daten. Asynchrone Arbeiten an der Seite sollten in dieser Methode eingeleitet werden. Page.PreRenderComplete Die Seite meldet das Ereignis PreRenderComplete, sobald alle Steuerelemente ihre OnPreRender-Methoden abgeschlossen haben und die Seite alle asynchronen Arbeiten abgeschlossen hat. Render Zeigt das gesamte Steuerelement an, einschließlich äußerer Tags und Anzeigerahmen (Chrome). RenderContents Zeigt nur den Inhalt des Steuerelements an, innerhalb der äußeren Tags und Style-Eigenschaften. Ted Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7) Webparts 119 SharePoint- und ASP.NET-Entwicklung im Vergleich Da SharePoint-Anwendungen in einem virtualisierten Websitekontext ausgeführt werden, in dem auch die Inhaltsdatenbank eine wichtige Aufgabe hat, ergeben sich einige wichtige Unterschiede zur herkömmlichen ASP.NET-Entwicklung und einige Anforderungen an das ASP.NET-Framework, die vielleicht nicht offensichtlich sind. Webparts, die innerhalb von Webpartzonen zu Seiten hinzugefügt werden, existieren vollständig in der Inhaltsdatenbank. Das bedeutet, dass sie vom SharePoint-Parser für den sicheren Modus bearbeitet werden. Außerdem werden Webparts im Lebenszyklus einer Seite später als deklarative Steuerelemente zur Seite hinzugefügt. Webpartanwendungen für WSS sollten vollständig in einer kompilierten Assembly vorliegen, ohne zusätzliche dynamisch kompilierten Codedateien im app_code-Ordner, wie es herkömmlichen ASP.NET 2.0-Anwendungen zulassen. Webpartanwendungen sollten so eigenständig wie möglich sein. Die erforderlichen Ressourcen wie Bilder und Skripts sollten als Ressourcen in die Assembly kompiliert und mit dem Clientskriptmanager (Client Script Manager) eingefügt werden. Außerdem werden Webpartanwendungen für WSS unter bestimmten Sicherheitseinstellungen ausgeführt und sollten durch Lösungspakete bereitgestellt werden, damit die korrekten Vertrauenseinstellungen vorgenommen werden. Während der Entwicklung müssen Sie die Vertrauenseinstellung (trust level) in der web.configDatei der Website vermutlich auf WSS_Medium oder Full erhöhen. Vertrauenseinstellungen und Sicherheitsrichtlinien werden in Kapitel 9, »Projekte und Bereitstellung«, ausführlicher besprochen. TIPP Sicherheitsausnahmen durch die Ausführung des Codes mit einer zu restriktiven Codezugriffssicherheitseinstellung (Code Access Security) sind in Webpartanwendungen eine häufige Fehlerursache. Der Parser für den sicheren Modus Die Codebereitstellung aus der Inhaltsdatenbank erfolgt durch den Parser für den sicheren Modus (Safe Mode Parser). Der Parser für den sicheren Modus sorgt dafür, dass nur Code, der als vertrauenswürdig konfiguriert wurde, dynamisch auf dem SharePoint-Server bereitgestellt werden kann. Ohne den Parser für den sicheren Modus könnten Benutzer in dynamischen Webseiten, die als Dokumente erstellt werden, bösartigen Code einfügen und dadurch die Datenintegrität und Sicherheit der SharePoint-Website gefährden. Die Bereitstellung von Code, der nicht vom Parser für den sicheren Modus überprüft wird, erfordert physischen Zugang zum Dateisystem des Webservers, wie bei der in Kapitel 2 beschriebenen Bereitstellung von benutzerdefinierten Anwendungsseiten. Der Parser für den sicheren Modus garantiert, dass der Code, der in der SharePoint-Anwendung ausgeführt wird, vom Serveradministrator genehmigt wurde. Webparts wurden erstmals in WSS 2.0 als nächste Version der Microsofts Dashboard-Technologie eingeführt. ASP.NET 2.0 enthält eine neue Version des Webpartframeworks, die außerhalb von WSS mit ASP.NET-Standardkomponenten benutzt werden kann. Das Webpartframework von WSS 3.0 setzt auf demselben Webpartframework auf. Um ein Webpart für SharePoint zu entwickeln, leiten Sie von der ASP.NET-Webpartklasse System.Web.UI.WebControls.WebParts.WebPart eine neue Klasse ab. Aus Gründen der Abwärtskompatibilität gibt es in WSS 3.0 auch noch die Webpartklasse Microsoft.SharePoint.WebPartPages. Diese Klasse sollte nur bei der Umstellung von vorhandenem WSS 2.0-Code auf WSS 3.0 verwendet werden. Diese Klasse wird von der ASP.NET-Klasse WebPart abgeleitet und enthält mehrere Kompatibilitätsschichten, um die Codeübernahme zu erleichtern. Sie ist nicht für Neuentwicklungen vorgesehen. Tabelle 4.2 beschreibt Eigenschaften, Methoden und Komponenten der ASP.NET-Webparts sowie die entsprechende abwärtskompatible Eigenschaft, Methode oder Komponente aus dem Namespace. Ted Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7) 120 Kapitel 4: Webparts SharePoint.WebPartPages. Ein weiterer wichtiger Unterschied ist, dass ASP.NET-Webparts und abwärtskompatible SharePoint-Webparts unterschiedliche Serialisierungsformate verwenden. Eine Änderung in der Basisklasse kann also dazu führen, dass bereitgestellte Webparts nicht mehr richtig funktionieren. Tabelle 4.2 Vergleichstabelle zur WSS-Abwärtskompatibilität ASP.NET-Webparts SharePoint-Abwärtskompatibilität WebBrowsableAttribute BrowsableAttribute WebDisplayName FriendlyName WebDescription Description Personalizable WebPartStorage PersonalizationScope Storage EditorPart ToolPart EditorPartCollection ToolPart[] CreateEditorParts() GetToolParts() RenderContents() RenderWebPart() SetPersonalizationDirty() SaveProperties Entwickeln von Webparts für WSS 3.0 Bei der Entwicklung einer Webpartanwendung für WSS beginnen Sie in Visual Studio 2005 eine neue Klassenbibliothek und nehmen einen Verweis auf System.Web auf. Um das SharePoint-Websiteobjektmodell verwenden zu können, nehmen Sie Verweise auf die primäre WSS-Assembly Microsoft.SharePoint.dll und auf Microsoft.SharePoint.Security.dll ins Projekt auf. Um die Webpartassembly im Ordner bin mit einem starken Namen bereitstellen zu können, müssen Sie auch das Assemblyattribut AllowPartiallyTrustedCallers verwenden. Dieses Attribut muss einmal in die Assembly aufgenommen werden und wird gewöhnlich in der Codedatei AssemblyInfo angegeben. [assembly: System.Security.AllowPartiallyTrustedCallers] Erstellen Sie nun Ihr erstes Webpart, indem Sie von WebPart eine Klasse ableiten und die Methode CreateChildControls überschreiben. Weil das ASP.NET-Framework automatisch die Anzeige der Steuerelemente aus der Sammlung übernimmt, brauchen Sie gewöhnlich nur in dieser Methode Steuerelemente und Handler hinzuzufügen, um die gewünschte Benutzeroberfläche zusammenzustellen. In den folgenden Beispielen erstellen Sie ein einfaches RSS-Anzeigewebpart, ein Webpart zur Auswahl eines RSS-Feeds und ein Editorwebpart. Der Code aus Listing 4.2 zeigt das übliche simple »HelloWorld«-Beispiel, das Sie zum RSS-Anzeigewebpart umbauen. Das resultierende Webpart ist relativ komplex. Daher ist es wichtig und sinnvoll, das Webpart schon während der Entwicklung zu testen. Dazu beginnt man am besten mit einem ganz einfachen Grundgerüst. Zur Bereitstellung des Webparts in Ihrer WSS-Website müssen Sie das Steuerelement im Knoten SafeControls der Datei web.config als sicheres Steuerelement registrieren, wie in Kapitel 3 beschrieben. Wenn das Projekt LitwareWebParts heißt, wird die Assembly und der Namespace durch folgenden SafeControlKnoten in web.config als sicher registriert: <SafeControl Assembly="LitwareWebParts" Namespace="LitwareWebParts" TypeName="*" Safe="True" /> Ted Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7) 121 Webparts Listing 4.2 Die ersten Schritte in der Webpartentwicklung Es geht los: ein RSS-Webpart using using using using using System; System.Web; System.Web.UI; System.Web.UI.WebControls; System.Web.UI.WebControls.WebParts; namespace LitwareWebParts { public class RssViewWebPart : WebPart { protected override void CreateChildControls() { base.CreateChildControls(); this.Controls.Add(new LiteralControl("Hello, world!")); } } } Beachten Sie bitte, dass wir der Assembly in diesem Beispiel keinen starken Namen geben und daher den einfachen Assemblynamen verwenden können. Wenn Sie der Assembly einen starken Namen geben, müssen Sie im SafeControl-Eintrag den vollständigen vierteiligen Assemblynamen angeben. Dann kompilieren Sie das Projekt zur Assembly-DLL (Dynamic Link Library) und kopieren die DLL ins bin-Verzeichnis Ihrer SharePoint-Webanwendung. Viele Entwickler tun dies beim Postbuildereignis von Visual Studio oder als benutzerdefinierte MSBuild-Aufgabe, wie im folgenden Beispiel: xcopy /Y *.* C:\Inetpub\LitwareWebApp\bin\*.* Im nächsten Schritt nach dem Hinzufügen des SafeControl-Eintrags testen Sie das Webpart, indem Sie es in den Katalog aufnehmen und zu einer Webpartseite hinzufügen. Zur Bereitstellung eines Webparts bieten sich viele Wege an, aber während der Entwicklung werden Sie dies meistens manuell tun. Um das Webpart in den Katalog aufzunehmen, wechseln Sie in Ihrer Website zu folgendem URL: http://localhost/ _catalogs/wp. Dieser Katalog ist auch über die Seite Websiteeinstellungen der Website auf der obersten Ebene zugänglich. Wechseln Sie von dieser Seite aus durch einen Klick auf die Schaltfläche Neu zur Seite http://localhost/_layouts/NewDwp.aspx. Auf dieser Seite werden alle Webparts aufgelistet, die im binVerzeichnis oder im globalen Assemblycache verfügbar sind und im SafeControls-Abschnitt der Webanwendung registriert wurden. Falls Sie Ihr Webpart nicht auf dieser Seite vorfinden, wird es Zeit für die Fehlersuche. Überprüfen Sie zum Beispiel, ob Ihr Webpart öffentlich (public) ist, von der Klasse WebPart abgeleitet wird, mit dem vollständigen Namespace und dem richtigen Assemblynamen als sicher registriert ist und im richtigen Ordner bereitgestellt wird. Wenn Ihre Webpartassembly signiert ist, sorgen Sie dafür, dass im SafeControls-Eintrag der korrekte starke Name angegeben wird. Abbildung 4.4 zeigt die Seite Neue Webparts, in der Sie mit der Webschnittstelle neue Webpartkatalogeinträge vornehmen können. TIPP Der Webpartkatalog (eine andere Bezeichnung ist Webpartgalerie) ist eine spezielle Dokumentbibliothek, die XML-Dateien mit serialisierten Webparts enthält. Die Namenserweiterung der Webparts, die im neuen ASP.NET-Format serialisiert wurden, lautet .webpart, während abwärtskompatible Webparts im älteren WSS 2.0-Format serialisiert werden und die Dateinamenserweiterung .dwp tragen. Wenn Sie auf der Seite Neue Webparts ein Webpart auswählen und auf die Schaltfläche Katalog auffüllen klicken, nimmt WSS im Webpartkatalog der Websitesammlung einen neuen Eintrag vor. Anschließend ist das Webpart auf allen Seiten verfügbar und lässt sich nach dem Menübefehl Seite bearbeiten mit der Ted Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7) 122 Kapitel 4: Webparts Option Webpart hinzufügen zur Webpartseite hinzufügen. Nachdem das Webpart zur Seite hinzugefügt wurde, können Sie es als XML-Webpartdatei (.webpart) exportieren. Das ist besonders nützlich, wenn Sie vor der Bereitstellung Standardeigenschaften festlegen möchten. Dieser XML-Inhalt ist es, den Sie verwenden können, wenn Sie Ihr Webpart zu einer Website oder zu Featuredefinitionsseiten hinzufügen. Oder Sie verwenden den XML-Code, mit dem Ihr Webpart in die Inhaltsdatenbank serialisiert wird. Außerdem können Sie diese XML-Datei in Ihr Visual Studio-Projekt und in die Quellcodeverwaltung aufnehmen, damit Sie ein Feature erstellen können, das die Webpartkatalogeinträge automatisch importiert. Listing 4.3 zeigt eine (gekürzte) Webpartbeispieldatei, die aus unserem einfachen RSS-Anzeigewebpartgerüst generiert wurde. Abbildung 4.4 Die Seite Neue Webparts zeigt Webparts an, die als sicher gekennzeichnet wurden; auf ihr können Webparts zum Webpartkatalog der aktuellen Websitesammlung hinzugefügt werden Listing 4.3 Ein Beispiel für eine Webpartbeschreibungsdatei Eine Webpartbeispieldatei <webParts> <webPart xmlns="http://schemas.microsoft.com/WebPart/v3"> <metaData> <type name="LitwareWebParts.RssViewWebPart" /> <importErrorMessage>Fehler beim Import des Litware-Webparts.</importErrorMessage> </metaData> <data> <properties> <!-- Webpart-Standardeigenschaften --> <property name="ChromeType" type="chrometype">Default</property> <property name="Title" type="string">Litware-RSS-Anzeigewebpart</property> <property name="Description" type="string">Zur Erstellung von RSS-Feeds für jede Quelle</property> Ted Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7) 123 Webparts <property name="CatalogIconImageUrl" type="string"> /_layouts/images/msxmll.gif </property> <property name="AllowConnect" type="bool">True</property> <property name="ExportMode" type="exportmode">All</property> <!-- Benutzerdefinierte Webparteigenschaften --> <property name="XmlUrl" type="string"> http://blogs.msdn.com/MainFeed.aspx?Type=AllBlogs </property> </properties> </data> </webPart> </webParts> Erstellen eines Features für den Import von Webparts Wenn Sie Webparts für den Vertrieb vorbereiten, empfehlen wir, ein Feature zu erstellen, mit dem sich Ihre .webpart-Dateien in den Webpartkatalog importieren lassen. Da es in jeder Websitesammlung nur einen Webpartkatalog gibt, sollte ein Feature für den Import von .webpart-Dateien auf der Ebene der Websitesammlung erstellt werden, also nicht nur für eine bestimmte Website gelten. Das folgende Beispiel zeigt eine feature.xml-Datei für ein Feature namens LitwareWebParts, das Sie auch im Visual Studio-Beispielprojekt dieses Kapitels finden. <Feature Id="FE016E00-8639-4839-925D-B40F659458A9" Title="Kapitel 4: Ein Beispielfeature für die Bereitstellung von Litware-Webparts" Description="Demo aus Windows SharePoint Services 3.0-Programmierung (Pattison/Larson)" Hidden="FALSE" Scope="Site" ImageUrl="actionssettings.gif" xmlns="http://schemas.microsoft.com/sharepoint/"> <ElementManifests> <ElementManifest Location="elements.xml"/> </ElementManifests> </Feature> Wenn ein Feature für den Import von Webparts bereitgestellt werden soll, müssen die eigentlichen .webpart-Dateien auf das Dateisystem des Frontend-Webservers kopiert werden. Dabei sollten Sie Ihre .webpart-Dateien in den Featureordner kopieren. Im Feature LitwareWebParts haben wir uns an eine MicrosoftKonvention gehalten und unsere .webpart-Dateien in einem Unterordner namens DWP des Featureordners gespeichert. Nachdem Sie Ihre .webpart-Dateien in den Featureordner kopiert haben, aktualisieren Sie die Datei Elements.xml, indem Sie in einem Module-Element die erforderlichen File-Elemente angeben, wie im folgenden Beispiel. Dadurch werden die Dateien bei der Aktivierung des Features in den Webpartkatalog der aktuellen Websitesammlung kopiert. Beachten Sie bitte, dass ein Module-Element, das für einen Webpartkatalog vorgesehen ist, mit dem Listentyp 113, dem Url-Wert »_catalogs/wp« und der RootWebOnlyEinstellung true definiert werden muss. <Elements xmlns="http://schemas.microsoft.com/sharepoint/"> <Module Name="LitwareWebParts" Path="dwp" List="113" Url="_catalogs/wp" RootWebOnly="true"> Ted Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7) 124 Kapitel 4: Webparts <File Url="HelloWebPart.webpart" Type="GhostableInLibrary" > <Property Name="Group" Value="Litware-Webparts" /> </File> <File Url="RssViewWebPart.webpart" Type="GhostableInLibrary" > <Property Name="Group" Value="Litware-Webparts" /> </File> <File Url="FeedListWebPart.webpart" Type="GhostableInLibrary" > <Property Name="Group" Value="Litware-Webparts" /> </File> </Module> </Elements> Beachten Sie bitte in diesem Listing der Datei elements.xml, dass jedes File-Element für eine .webpartDatei über ein inneres Property-Element verfügt, das eine Group-Eigenschaft mit dem Wert »LitwareWebparts« hinzufügt. Der Wert der Eigenschaft Group muss in dieser Weise innerhalb des File-Elements festgelegt werden. Das ist anfangs ein wenig verwirrend, weil man die Group-Eigenschaft nicht wie andere Webparteigenschaften in der .webpart-Datei festlegen kann, wie zum Beispiel Title oder Description. Allerdings ist die Group-Eigenschaft für Benutzer eine Orientierungshilfe, weil Ihre Webparts dann im WSS-Standarddialog Webparts hinzufügen zu benutzerdefinierten Gruppen zusammengefasst werden (Abbildung 4.5). Abbildung 4.5 Wenn Sie ein Feature für den Import eines Webparts zum Webpartkatalog vorgesehen haben, kann ein Benutzer das Webpart leicht zu Webpartseiten hinzufügen Ted Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7) Webparts 125 Sie haben zwar gerade gesehen, wie man ein Feature für den Import von .webpart-Dateien in den Webpartkatalog erstellt, aber damit ist die Bereitstellung von Webparts in einer Produktivumgebung noch nicht abgeschlossen. Allerdings müssen Sie noch bis Kapitel 9 warten, um mehr zu erfahren, wenn wir Lösungspakete vorstellen und die noch fehlenden Teile einfügen. Wie Sie dann sehen, kann ein Lösungspaket automatisch Webpart-Assembly-DLLs und dazugehörige Features bereitstellen, um die .webpartDateien zu importieren. Ein Lösungspaket kann auch zur Bereitstellung anderer Webpartdateien verwendet werden, wie zum Beispiel Benutzersteuerelemente, und um die erforderlichen SafeControlEinträge in den web.config-Dateien der verschiedenen Frontend-Webserver einer Farm vorzunehmen. Außerdem verwenden wir das Lösungspaket in Kapitel 9, um die erforderlichen Codezugriffsicherheitsrichtlinien und Konfigurationen festzulegen, damit unsere Webparts in Umgebungen verwendet werden können, die zumindest zum Teil vertrauenswürdig sind. Weitere Änderungen an der web.config-Datei können mit Featureempfängern durchgeführt werden, die mit der Klasse SPWebConfigModification im Projekt bereitgestellt werden. Debuggen von Webparts Das Debuggen von Webparts ist eine wichtige Fertigkeit. Zum Glück unterscheidet es sich nicht sehr vom Debuggen herkömmlicher ASP.NET-Anwendungen. Der wichtigste Unterschied besteht darin, dass Sie in Visual Studio nicht F5 drücken, sondern eine Verbindung mit dem w3wp.exe-Prozess auf dem Windows-Server herstellen. Um in Visual Studio 2005 eine Verbindung mit dem Prozess herzustellen, drücken Sie STRG+ALT+P oder wählen im Menü Debuggen den Befehl An den Prozess anhängen. Bei der Entwicklung von Webparts für WSS ist es wichtig, auf dem Entwicklungscomputer, auf dem Visual Studio installiert ist, Windows Server zu verwenden und WSS zu installieren. Um die .dll- und .pdbDateien ins bin-Verzeichnis zu kopieren, können Sie ein Postbuildereignis verwenden. Anschließend können Sie den Debugger an den Zielprozess anhängen. In einer Webpartanwendung für SharePoint können viele Dinge schiefgehen. Die Codeausführung Schritt für Schritt zu überprüfen gehört daher zum Programmiereralltag. TIPP Wenn Sie als Desktopbetriebssystem keine Serverumgebung zur Verfügung haben, können Microsoft Virtual PC oder Microsoft Virtual Server eine brauchbare Alternative für die Entwicklung sein, allerdings zum Preis einer deutlich spürbaren Leistungseinbuße. Anpassung und Personalisierung Durch die Möglichkeit zur Anpassung und Personalisierung von Webparts lassen sich Webparts in vielen Anwendungen wiederverwenden. Mit Anpassung sind Änderungen gemeint, die für alle Benutzer der Webpartinstanz gelten, während mit Personalisierung die persönlichen Einstellungen gemeint sind, die ein Benutzer an einem Webpart vornimmt und die nur für diesen Benutzer gelten. Der Besitzer einer Website könnte zum Beispiel festlegen, dass auf der Stammseite der Website ein bestimmter Newsfeed angezeigt werden soll. Das wäre eine Anpassung, weil diese Änderung für alle Benutzer der Website sichtbar wird. Einzelne Benutzer der Websites stellen vielleicht andere Anzeigeformate ein, die sie bevorzugen. Das wäre ein Beispiel für eine Personalisierung. Personalisierungen gelten für einzelne Benutzer und werden auf Benutzerbasis gespeichert. TIPP Der Leistungsbedarf einer Anwendung ist für Personalisierungen höher als für Anpassungen und sollte in der Planung entsprechend berücksichtigt werden. Ted Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7) 126 Kapitel 4: Webparts Um eine neue Eigenschaft hinzuzufügen, fügen Sie eine öffentliche Eigenschaft ein und geben das Attribut Personalizable an. Auch die Attribute WebBrowsable, WebDisplayName, WebDescription und Category sind zur Verwaltung der Eigenschaft im Editorpart des Webparts von Nutzen. Diese Attribute werden in den Namespaces System.Web.UI.WebControls.WebParts und System.ComponentModel definiert. Wenn Sie eine Eigenschaft definieren und das Attribut WebBrowsable angeben, zeigt das Webpartframework diese Eigenschaft automatisch in einem allgemeinen Standardeditorpart an, wenn der Benutzer den Befehl Webpart bearbeiten wählt. Ein Editorpart ist ein spezielles Steuerelement, das zur Bearbeitung von Webparteigenschaften verwendet wird, die anpassbar und personalisierbar sind. Das folgende Beispiel fügt eine Webparteigenschaft namens XmlUrl hinzu. private string xmlUrl; [ Personalizable(PersonalizationScope.Shared), WebBrowsable(true), WebDisplayName("Feed-Url"), WebDescription("Geben Sie hier die XML-URL Ihres RSS-Feeds an!"), Category("Konfiguration")] public string XmlUrl { get { return xmlUrl; } set { xmlUrl = value; } } Das Attribut Personalizable weist den Webpartmanager an, den Wert der Eigenschaft XmlUrl im Webpartframework zu speichern. In WSS wird der Wert in der Inhaltsdatenbank gespeichert. Da der Gültigkeitsbereich der Personalisierung auf Shared eingestellt wird, unterstützt die XmlUrl-Eigenschaft Anpassungen auf Websitebasis durch den Websitebesitzer, aber keine benutzerspezifischen Personalisierungen. Die anderen Attribute der Eigenschaft bedeuten, dass die Eigenschaft im Editorpart mit einem bestimmten Anzeigenamen angezeigt wird, über eine Beschreibung für ein QuickInfo verfügt und einer Kategorie zugeordnet wird. Ohne zusätzlichen Code wird die XmlUrl-Eigenschaft im SharePoint-Arbeitsbereich von einem Standardeditorpart angezeigt. Erstellen wir nun eine zweite Eigenschaft namens HeadlineMode, die eine Personalisierung zulässt. Das erreichen wir durch die Angabe des PersonalizationScope-Werts User statt Shared. Dadurch ist jeder Benutzer unseres Webparts in der Lage, zwischen zwei unterstützten Anzeigemodi zu wechseln. Die Werte der HeadlineMode-Eigenschaft werden durch die Werte der benutzerdefinierten Enumeration RenderMode definiert: public enum RenderMode { Full, Titles } Nachdem wir die Enumeration RenderMode definiert haben, können wir mit folgendem Code eine neue personalisierbare Eigenschaft mit einer privaten Variablen definieren, in der ihr Wert gespeichert wird: private RenderMode headlineMode = RenderMode.Full; [Personalizable(PersonalizationScope.User), WebBrowsable(true), WebDisplayName("Schlagzeilenmodus"), WebDescription("Sollen nur Schlagzeilen angezeigt werden?"), Category("Konfiguration")] public RenderMode HeadlineMode { get { return headlineMode; } set { headlineMode = value; } } Ted Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7) 127 Webparts Beide Eigenschaften XmlUrl und HeadlineMode wurden mit dem Attribut WebBrowsable definiert. Daher brauchen Sie kein benutzerdefiniertes Editorpart zu erstellen, weil WSS diese Eigenschaften automatisch im allgemeinen Webparteditor anzeigt, damit Benutzer deren Werte ändern können. Gibt der Benutzer zum Beispiel für sein Webpart den Befehl Freigegebenes Webpart bearbeiten, zeigt der Aufgabenbereich diese beiden Eigenschaften im allgemeinen Editorpart an (Abbildung 4.6). Beachten Sie bitte, dass die zulässigen Werte der Eigenschaft mit dem RenderMode-Enumerationstyp als Dropdownliste angezeigt werden. Abbildung 4.6 Mit dem Attribut WebBrowsable definierte Eigenschaften werden automatisch im WSS-Standardeditorpart angezeigt Vergessen Sie bitte nicht, dass die HeadlineMode-Eigenschaft mit der Personalisierungseinstellung User definiert wird, während die Eigenschaft XmlUrl mit der Personalisierungseinstellung Shared definiert wird. Wechselt ein Benutzer nun mit dem Menüpunkt Diese Seite personalisieren des oberen WillkommenMenüs in den Personalisierungsmodus, kann er die Eigenschaft XmlUrl weder sehen noch bearbeiten. Sie wird nur im Freigabemodus angezeigt, nicht im Benutzermodus. Erstellen von benutzerdefinierten Editorparts Für manche Webparts wird es ausreichen, wenn Sie die beständigen (persistenten) Eigenschaften mit dem Attribut WebBrowsable definieren, damit WSS die Eigenschaften zur Bearbeitung automatisch im allgemeinen Editorpart anzeigt. Allerdings reicht das nicht immer aus. Gelegentlich werden Sie für den Toolbereich eigene Editorparts erstellen wollen. Dieser Abschnitt beschreibt, wie Sie dann vorgehen. Zur Erstellung eines benutzerdefinierten Editorparts leiten Sie von der Klasse EditorPart aus dem Namespace System.Web.UI.WebControls.WebParts eine neue Klasse ab. In unserem Beispiel erstellen wir eine neue Klasse namens RssViewEditorPart, die von EditorPart abgeleitet wird und als benutzerdefinierter Editorpart für die Klasse RssViewWebPart dient. Beachten Sie bitte, dass wir nicht nur die RssViewEditorPart-Klasse erstellen, sondern auch einige Änderungen an der Klasse RssViewWebPart vornehmen müssen. Zuerst ändern wir die Personalizable-Eigenschaften, damit sie nicht im allgemeinen Editorpart erscheinen. Dazu reicht es aus, dem WebBrowsableAttribut den Wert false zu geben. [Personalizable(PersonalizationScope.Shared), WebBrowsable(false)] public string XmlUrl { get { return xmlUrl; } set { xmlUrl = value; } } Ted Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7) 128 Kapitel 4: Webparts Dann müssen Sie die in der Webpartklasse RssViewWebPart die Methode CreateEditorParts überschreiben, damit WSS statt oder zusätzlich zum Standardeditorpart, das normalerweise zur Anzeige von Webparts geladen wird, Ihr benutzerdefiniertes Webpart lädt. Bei der Überschreibung von CreateEditorParts arbeiten Sie mit Sammlungs- oder Auflistungsobjekten des Typs EditorPartCollection. Einmal erstellt, lässt sich ein EditorPartCollection-Objekt nicht mehr ändern. Aber EditorPartCollection verfügt über einen Konstruktor, der ein vorhandenes EditorPartCollection-Objekt und eine ICollection-Schnittstelle für EditorPart-Objekte als Parameter hat. Daher können Sie so vorgehen, dass Sie ein neues EditorPartCollection-Objekt erstellen, das neben Ihrem benutzerdefinierten Editorpart auch die Standardeditorparts enthält. Wenn Sie ein EditorPart-Objekt erstellen und initialisieren, müssen Sie auch die ID-Eigenschaft festlegen. Falls Sie das vergessen, kommt es zu einer Ausnahme, deren Beschreibung mehrdeutig ist und kaum Informationen zum Debuggen liefert. Das folgende Beispiel zeigt die Syntax für die Überschreibung der Methode CreateEditorParts und für die Bereitstellung eines benutzerdefinierten Editorparts, das zusammen mit den normalerweise verwendeten Standardeditorparts geladen wird: public override EditorPartCollection CreateEditorParts() { List<EditorPart> editorParts = new List<EditorPart>(1); EditorPart part = new RssViewEditorPart(); part.ID = this.ID + "_rssViewEditor"; editorParts.Add(part); EditorPartCollection baseParts = base.CreateEditorParts(); return new EditorPartCollection(baseParts, editorParts); } Wenden wir uns nun der Erstellung der Klasse RssViewEditorPart zu. Dabei handelt es sich um eine neue öffentliche Klasse, die von der Klasse EditorParts abgeleitet wird. Wie bei der Erstellung einer Webpartklasse können Sie die Methode CreateChildControls überschreiben und in dieser Methode Steuerelemente zu einem Editorpart hinzufügen. Bei der Initialisierung der Steuerelemente in einem Editorpart ist es üblich, die aktuellen Werte der Webparteigenschaften zu verwenden. Das Webpart, das in Ihrem Webpart bearbeitet wird, ist über eine Eigenschaft namens WebPartToEdit zugänglich. In der Klasse EditorPart gibt es zwei wichtige abstrakte Methoden mit den Namen ApplyChanges und SyncChanges. Diese Methoden legen Eigenschaften des Webparts oder des für das Webpart zuständigen Editorparts fest. Die Methode ApplyChanges übernimmt Werte vom benutzerdefinierten Editorpart und überträgt sie auf die Eigenschaften des Webparts. Die Methode SyncChanges übernimmt Werte aus dem Webpart und überträgt sie auf die Eingabesteuerelemente des Editorparts. Wenn Sie das Editorpart laden, ist es die Methode SyncChanges (also nicht OnLoad oder CreateChildControls), die das Editorpart mit seinen Anfangswerten versorgt. Listing 4.4 zeigt ein einfaches benutzerdefiniertes RSS-Anzeigewebpart. In größeren Anwendungen werden Sie sich bei der Erstellung von Editorparts wahrscheinlich an den Benutzeroberflächen der gemeinsamen Webparteigenschaften orientieren. Beachten Sie bitte, dass Sie im Editorpart die Personalisierungseinstellung überprüfen müssen, um die Einstellung der Werte entsprechend zu sperren oder zuzulassen. Der Benutzer bearbeitet entweder freigegebene Webparts oder er nimmt eine persönliche Einstellung des Webparts vor. Im letzteren Fall wird die PersonalizationScopeEinstellung des Webpartmanagers PersonalizationScope.User lauten. TIPP Wenn Sie ein benutzerdefiniertes Webpart für eine Eigenschaft entwickeln, sollten Sie auch das Attribut WebBrowsable der Eigenschaft entfernen, damit die Eigenschaft nicht in den Standardwebparts angezeigt wird. Ted Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7) 129 Webparts Listing 4.4 Ein benutzerdefiniertes Webpart für die Aktualisierung der RssViewWebPart-Eigenschaftswerte Editorpartbeispiel: RssViewEditorPart public class RssViewEditorPart : EditorPart { TextBox txtXmlUrl; RadioButtonList lstHeadlineMode; protected override void CreateChildControls() { Controls.Add(new LiteralControl("Feed-Url:<br/>")); txtXmlUrl = new TextBox(); txtXmlUrl.Width = new Unit("100%"); txtXmlUrl.TextMode = TextBoxMode.MultiLine; txtXmlUrl.Rows = 3; // Im Personalisierumgsmodus darf XmlUrl nicht bearbeitet werden. if (WebPartManager.Personalization.Scope == PersonalizationScope.User) txtXmlUrl.Enabled = false; this.Controls.Add(txtXmlUrl); Controls.Add(new LiteralControl("Schlagzeilenanzeige:<br/>")); lstHeadlineMode = new RadioButtonList(); lstHeadlineMode.Items.Add(RenderMode.Full.ToString()); lstHeadlineMode.Items.Add(RenderMode.Titles.ToString()); this.Controls.Add(lstHeadlineMode); } public override void SyncChanges() { this.EnsureChildControls(); RssViewWebPart sourcePart = (RssViewWebPart)this.WebPartToEdit; string SelectedMode = sourcePart.HeadlineMode.ToString(); lstHeadlineMode.Items.FindByText(SelectedMode).Selected = true; txtXmlUrl.Text = sourcePart.XmlUrl; } public override bool ApplyChanges() { this.EnsureChildControls(); RssViewWebPart targetPart = (RssViewWebPart)this.WebPartToEdit; targetPart.XmlUrl = txtXmlUrl.Text; if(lstHeadlineMode.SelectedValue.Equals("Full")) targetPart.HeadlineMode = RenderMode.Full; else targetPart.HeadlineMode = RenderMode.Titles; return true; } } Abbildung 4.7 zeigt das benutzerdefinierte Editorpart, das dem Benutzer durch ein mehrzeiliges Textfeld und ein RadioButtonList-Steuerelement eine umfassendere Bearbeitung ermöglicht. Beachten Sie bitte auch, dass die Implementierung des benutzerdefinierten Editorparts zwischen der Personalisierungseinstellung Shared und User unterscheidet. Wenn das Editorpart ermittelt, dass der Benutzer das Webpart mit der Personalisierungseinstellung User bearbeitet, stellt es die Enabled-Eigenschaft des Textfelds txtXmlUrl auf false, damit der Benutzer den Wert nicht ändern kann. Der entscheidende Punkt ist, dass Sie bei der Entwicklung eines Editorparts wissen müssen, welche Eigenschaften angepasst, aber nicht personalisiert werden können. Und Sie müssen dafür sorgen, dass Benutzer diese Eigenschaften im Personalisierungsmodus User nicht ändern können. Ted Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7) 130 Kapitel 4: Webparts Abbildung 4.7 Mit einem benutzerdefinierten Editorpart können Sie die Benutzeroberfläche in dem Aufgabenbereich gestalten, in dem Benutzer die persistenten Webparteigenschaften anpassen und personalisieren können Asynchrone Webpartausführung Gewöhnlich werden Webparts nicht einzeln, sondern zusammen mit einigen anderen Webparts auf den Seiten eingesetzt. Sie wissen aber normalerweise nicht im Voraus, um wie viele und um welche Webparts es sich handelt. Gelegentlich sind auf derselben Seite auch Webparts vorhanden, die sehr viel Rechenzeit beanspruchen. Daher ist es wichtig, dass alle Webparts ihre Daten so effizient wie möglich bearbeiten. Der Aufbau einer Seite kann zum Beispiel durch eine Datenbankabfrage, einen LDAP-Aufruf und mehrere Webdienstaufrufe durch mehrere Webparts verlangsamt werden. Statt nun alle Vorgänge nacheinander ablaufen zu lassen, wodurch sich die Ladezeit der Seite entsprechend verlängert, werden die Vorgänge asynchron durchgeführt. Im Idealfall entspricht die Ladezeit der Seite dann ungefähr der Zeitdauer, die der längste Vorgang beansprucht (oder der längste Vorgang, der eine vorgegebene Zeitgrenze überschreitet). Die asynchrone Ausführung von Aufgaben ist zwar keine reine Webparttechnologie, lässt sich aber wegen des Aufbaus der Webpartseiten aus mehreren Webparts in fast allen Webpartanwendungen einsetzen. Mit ASP.NET 2.0 kann die Page-Klasse asynchrone Aufgaben mit der Methode RegisterAsyncTask registrieren, damit mehrere Vorgänge gleichzeitig ablaufen können. Dadurch wird nicht nur die Ladezeit der Seite verkürzt, sondern auch die Threads des IIS-Arbeitsprozesses, die auf Ihrem Server nur in beschränkter Anzahl zur Verfügung stehen, werden weniger stark beansprucht. Da die Zahl der Threads beschränkt ist, sollte man Threads nicht so einsetzen, dass sie auf die Ergebnisse länger andauernder Vorgänge warten, sondern sie nur zur Einleitung der Vorgänge verwenden und dann wieder in den Pool zurückgeben, damit sie andere Arbeiten durchführen können. Sind auf einer Seite asynchrone Aufgaben zu erledigen, wird der IIS-Arbeitsthread wieder in den Threadpool zurückgegeben, wo er bei Bedarf für andere Arbeiten abgerufen werden kann, während die Seite auf die Beendigung des asynchronen Vorgangs wartet. Im Beispiel mit dem RSS-Anzeigewebpart kann die Beschaffung der XML-Remotedaten als asynchrone Aufgabe durchgeführt werden. Dadurch wird der IIS-Arbeitsthread für andere Aufgaben verfügbar, während die Anwendung noch auf eine Antwort vom Remoteserver wartet. Außerdem ist es sinnvoll, vor der Einleitung des Remoteaufrufs mit einer Abfrage der DisplayMode-Eigenschaft des Webpartmanagers zu überprüfen, ob sich die Seite im Entwurfsmodus befindet. Klassen, die das IAsyncResult-Entwurfsmuster (design pattern) implementieren, lassen sich gut mit asynchronen Aufgaben kombinieren und stellen für Ihren Code aus der mittleren Schicht ein gutes Entwurfsmuster dar. Zu den Beispielen für solche Klassen gehören ADO.NET-Datenbankklassen, Webabfragen und Webdienstproxys. Listing 4.5 stellt die asynchrone Aufgabe für die Beschaffung von Remotedaten mit der Klasse WebRequest dar. Listing 4.6 ist eine einfache XSLT-Datei für eine RSS-Webpartanwendung, die die Ausgabe mit der Klasse XslCompiledTransform umformt. In diesem Beispiel wird die Datei als eingebettete Ressource kompiliert, wobei der vollTed Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7) 131 Webparts ständige Name LitwareWebParts.Resources.RSS.xslt lautet. (Als Ressourcenstream ist sie mit der Hilfsklasse WebPartResources aus Listing 4.13 verfügbar). Außerdem finden Sie im Beispielcode für dieses Kapitel noch eine andere XSLT-Datei für die Umformung des Feeds in Titel, die im Anzeigemodus RenderMode. Titles erfolgt. Listing 4.5 Ein asynchrones RSS-Webpart verwendet Page.RegisterAsyncTask RSS-Anzeiger mit Page.RegisterAsyncTask using using using using using using using using using using using using System; System.Collections.Generic; System.IO; System.Net; System.Web; System.Web.UI; System.Web.UI.WebControls; System.Web.UI.WebControls.WebParts; System.Xml; System.Xml.XPath; System.Xml.Xsl; System.Reflection; namespace LitwareWebParts { public class RssViewWebPart : WebPart, IWebEditable { // Die Eigenschaften xmlUrl und RenderMode wurden der besseren Übersicht halber weggelassen. private Stream xmlResponseStream = null; private WebRequest xmlReq; // Führe alle Arbeiten durch, die vor der Anzeige erfolgen. // Dazu gehört auch die Einleitung asynchroner Vorgänge. protected override void OnPreRender(EventArgs e) { base.OnPreRender(e); if (string.IsNullOrEmpty(this.xmlUrl)) return; // Überprüfe, ob sich die Seite im Entwurfsmodus befindet. // Überspringe in diesem Fall die Abfrage. if (this.WebPartManager.DisplayMode.AllowPageDesign) { this.Controls.Add(new LiteralControl("Keine Anzeige im Entwurfsmodus.")); return; } try { Uri xmlUri = new Uri(this.xmlUrl); xmlReq = WebRequest.CreateDefault(xmlUri); xmlReq.Credentials = CredentialCache.DefaultCredentials; xmlReq.Timeout = 10000; // 10 Sekunden maximal this.Page.RegisterAsyncTask( new PageAsyncTask(new BeginEventHandler(BeginXmlRequest), new EndEventHandler(EndXmlRequest), new EndEventHandler(XmlRequestTimeout), null, true) ); Ted Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7) 132 Kapitel 4: Webparts } catch (System.Security.SecurityException) { this.Controls.Add( new LiteralControl("Zugriff verweigert - stellen Sie die Vertrauensebene auf WSS_Medium.")); } } IAsyncResult BeginXmlRequest(object src, EventArgs args, AsyncCallback callback, object state) { return this.xmlReq.BeginGetResponse(callback, state); } void XmlRequestTimeout(IAsyncResult ar) { Label timeoutLabel = new Label(); timeoutLabel.Text = string.Format( "Frist abgelaufen beim Warten auf {0}.", this.XmlUrl); this.Controls.Add(timeoutLabel); } void EndXmlRequest(IAsyncResult ar) { WebResponse response = this.xmlReq.EndGetResponse(ar); this.xmlResponseStream = response.GetResponseStream(); } protected override void RenderContents(HtmlTextWriter writer) { base.RenderContents(writer); if (string.IsNullOrEmpty(this.xmlUrl) || this.xmlResponseStream == null) return; XslCompiledTransform transform = new XslCompiledTransform(); string xslt; if (this.HeadlineMode == RenderMode.Full) xslt = @"Resources.RSS.xslt"; else xslt = @"Resources.RssTitles.xslt"; string resourceName = @"LitwareWebParts" + xslt; using (Stream res = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)) { using (XmlTextReader stylesheet = new XmlTextReader(res)) { transform.Load(stylesheet); } } try { using (XmlReader reader = new XmlTextReader(this.xmlResponseStream)) { XmlTextWriter results = new XmlTextWriter(writer.InnerWriter); transform.Transform(reader, results); reader.Close(); } } catch (Exception ex) { writer.Write(ex.Message); if (this.xmlResponseStream != null) { this.xmlResponseStream.Close(); this.xmlResponseStream.Dispose(); } } } } } Ted Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7) 133 Webparts RSS und Agenturdaten RSS (Really Simple Syndication) ist ein XML-Standardvertriebsformat für Newsfeeds, Blogs, WSSListen und Unternehmensdatenströme. Es ist zwar sehr gut geeignet, um externe Daten in ein Portal einzubinden, aber es stellt auch ein Sicherheitsrisiko dar. Daher sollten Sie den Einsatz auf vertrauenswürdige Websites beschränken. Da externer HTML-Code und externe Skripts auf Ihrer Portalseite angezeigt werden, die für die meisten Benutzer eine vertrauenswürdige Website darstellt, sollten Sie sorgfältig prüfen, welche Feeds Sie aktivieren. RSS ist zudem ein Format, das sich gut mit Daten kombinieren lässt, die in Listenform vorliegen. Da SharePoint seine Daten in Listen speichert, bietet sich RSS an und ist in WSS 3.0 als integrierte Komponente verfügbar. Die Anwendungsseite /_layouts/listfeed.aspx bietet jede Liste als RSS-Feed mit Sicherheitseinschränkungen an, wenn die GUID der Liste als Parameter angegeben wird. RSS lässt sich auch mit gebräuchlichen Erweiterungen wie Simple List Extensions und benutzerdefinierten SharePoint-Erweiterungen erweitern, damit Remoteclients und Aggregatoren bestimmte Listentypen verwenden können, zum Beispiel stark typisierte Listen. Weitere Informationen über die RSSSpezifikation finden Sie unter http://blogs.law.harvard.edu/tech/rss. Listing 4.6 Ein einfacher XSLT-Quellcode für die Umformung von RSS-XML RSS XSLT Transform <?xml version='1.0' encoding='UTF-8'?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:dc="http://purl.org/dc/elements/1.1/" version="1.0" > <xsl:output omit-xml-declaration="yes" method="html" encoding="utf-16" /> <xsl:template match='/rss'> <h3> <xsl:value-of select='channel/title'/> </h3> <xsl:apply-templates select='channel/item' /> </xsl:template> <xsl:template match='item'> <div style='margin:6px;'> <strong> <a href='{link}'> <xsl:value-of select='title'/> </a> </strong><br/> <xsl:value-of select='description' disable-output-escaping='yes' /> <xsl:text disable-output-escaping='yes'>&nbsp;</xsl:text> <a href='{link}'>Den Beitrag lesen</a>.<br /> <xsl:if test='dc:creator'> <strong>Autor: </strong><xsl:value-of select='dc:creator' /> <br/> </xsl:if> <strong>Datum: </strong><xsl:value-of select='pubDate' /><br/> Ted Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7) 134 Kapitel 4: Webparts <font color='gray'> <xsl:for-each select='category'> <xsl:value-of select='.' /> | </xsl:for-each> </font> <br /> </div> </xsl:template> <xsl:template match='category'> <xsl:value-of select='.'/> | </xsl:template> </xsl:stylesheet> Webpartbausteine Wenn Sie Webparts entwickeln, empfiehlt es sich im Allgemeinen, die beteiligten Steuerelemente, Ressourcen und die Geschäftslogik zu geschlossenen Einheiten zusammenzufassen. Wie man Ressourcen verwenden kann, haben Sie bereits am Beispiel einer XSLT-Datei gesehen, die als Ressource in die Assembly des RSS-Webparts eingebettet wurde. Auch statischen HTML-Code, JavaScript oder Bilder können Sie als Ressourcen einbinden. Außerdem werden Sie auch Benutzersteuerelemente verwenden, wenn Sie bereits vorhandene Anwendungen auf SharePoint umstellen, und gelegentlich XML-Datenquellen benutzen, beispielsweise RSS-Feeds oder Webdienstendpunkte. Im nächsten Abschnitt werden wir uns weitere Webpartbausteine für zusammengesetzte Webpartanwendungen ansehen. Verwenden von Benutzersteuerelementen in Webparts Vielleicht stehen Sie vor der Aufgabe, eine vorhandene Webseitenanwendung auf SharePoint umzustellen. Das ist relativ leicht, wenn man die Seiten in Benutzersteuerelemente konvertiert. Benutzersteuerelemente werden zwar nicht für alle Webpartanwendungen empfohlen, können aber schnell zu Ergebnissen führen, wenn es darum geht, eine ASP.NET-Anwendung in Ihrer SharePoint-Website bereitzustellen. Benutzersteuerelemente, die in SharePoint verwendet werden, müssen von der Klasse UserControl oder einer davon abgeleiteten Klasse abgeleitet werden, die in der Webanwendung verfügbar ist (entweder im Verzeichnis bin oder im globalen Assemblycache), und sie müssen in derselben IIS-Webanwendung vorhanden sein. Dann kann der Webpartcode das Steuerelement mit der Methode Page.LoadControl laden und zu Ihrem Webpart hinzufügen. Da Ihr Benutzersteuerelement im Dateisystem vorhanden ist, also nicht aus der Inhaltsdatenbank geladen wird, kann es bei Bedarf Inlinecode enthalten, da es nicht vom Parser für sicheren Modus (Safe Mode Parser) bearbeitet wird. Beachten Sie bitte, dass Sie Benutzersteuerelemente nicht in der Inhaltsdatenbank bereitstellen können. Listing 4.7 zeigt ein BenutzersteuerelementhostWebpart. Sie brauchen nur das Steuerelement zu laden und es zur Controls-Auflistung hinzuzufügen. Das Steuerelementframework sorgt dann für die Anzeige. TIPP Benutzersteuerelemente können zwar an einem beliebigen Ort in der IIS-Webanwendung (im zugrunde liegenden Dateisystem) bereitgestellt werden, aber der für WSS 3.0 bevorzugte Pfad ist ein Unterordner des Ordners _controltemplates, der als %ProgramFiles%\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\CONTROLTEMPLATES im Dateisystem des Servers zu finden ist. Sie können auch ein benutzerdefiniertes Editorpart erstellen, mit dem Benutzer ihre Wahl unter den Benutzersteuerelementen treffen können, die in einem bestimmten Ordner vorhanden sind. Ted Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7) 135 Webparts Listing 4.7 Ein Beispielwebpart, das ein Benutzersteuerelement lädt Benutzersteuerelementhost-Webpart using using using using using System; System.Web; System.Web.UI; System.Web.UI.WebControls; System.Web.UI.WebControls.WebParts; namespace LitwareWebParts { public class UserControlHost : WebPart { protected Control userControl; protected override void CreateChildControls() { this.Controls.Clear(); string userControlPath = @"/_controltemplates/Litware/LitwareUserControl.ascx"; this.userControl = this.Page.LoadControl(userControlPath); this.Controls.Add(this.userControl); } } } Verwenden des Steuerelements SPGridView SharePoint bietet im Namensraum Microsoft.SharePoint.WebControls einige nützliche Steuerelemente, die gebräuchliche ASP.NET-Steuerelemente um SharePoint-Funktionen und das entsprechende Aussehen und Verhalten erweitern. Das wohl nützlichste Websteuerelement ist SPGridView. Das SPGridView-Steuerelement erweitert die ASP.NET-Klasse GridView um Stildeklarationen für SharePoint und kann außerdem mit der Klasse SPDataSource WSS-Datenquellen wie Listen und websiteübergreifende Abfragen unterstützen. In unserem nächsten Beispiel verwenden wir den SharePoint-Websitekontext, um eine Liste der Feed-URLs in der SharePoint-Website und ihren Unterwebsites zusammenzustellen. Wir zeigen die Feedliste mit einem SPGridView-Steuerelement an, das wir später mit unserem RSS-Anzeigewebpart verbinden. Das SPGridView-Steuerelement kann auch an ein SPDataSource-Objekt gebunden werden, das eine Liste als Datenquelle anbieten kann. In diesem Beispiel verwenden wir ein DataTable-Objekt, das aus Listen mehrerer zusammengefasster SharePoint-Websites erstellt wird. Da dieses Beispiel verfügbare Feeds auflistet, überspringen wir Listen, die nicht auf Nachrichten basieren, wie Kataloge, Galerien und Kategorielisten. Sehen Sie sich im Code von Listing 4.8 an, wie das SPGridView-Steuerelement mit Daten über bestimmte Listen aus der aktuellen Website versorgt wird. Listing 4.8 Dieses Feedlistenwebpart verwendet das SPGridView-Steuerelement Ein Feedlistenwebpart mit dem SPGridView-Steuerelement public class FeedListWebPart : WebPart { private SPGridView listsView; private DataTable dt; protected override void CreateChildControls() { List<SPList> lists = new List<SPList>(); this.AddLists(lists, SPContext.Current.Web); Ted Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7) 136 Kapitel 4: Webparts listsView = new SPGridView(); listsView.AutoGenerateColumns = false; this.Controls.Add(listsView); BoundField colTitle = new BoundField(); colTitle.DataField = "Title"; colTitle.HeaderText = "Titel"; listsView.Columns.Add(colTitle); BoundField colXml = new BoundField(); colXml.DataField = "ItemCount"; colXml.HeaderText = "Elementanzahl"; listsView.Columns.Add(colXml); CommandField colSelectButton = new CommandField(); colSelectButton.HeaderText = "Aktion"; colSelectButton.ControlStyle.Width = new Unit(75); colSelectButton.SelectText = "Zeige RSS"; colSelectButton.ShowSelectButton = true; listsView.Columns.Add(colSelectButton); listsView.SelectedIndexChanged += new EventHandler(view_SelectedIndexChanged); if (!this.Page.IsPostBack) { dt = new DataTable(); dt.Columns.Add("Title"); dt.Columns.Add("ItemCount"); dt.Columns.Add("XmlUrl"); dt.Columns.Add("ID"); foreach (SPList list in lists) { DataRow dr = dt.NewRow(); dr["Title"] = list.Title; dr["ItemCount"] = list.ItemCount.ToString(); dr["ID"] = list.ID; string url = this.Page.Request.Url.GetLeftPart(UriPartial.Authority) + SPUtility.MapWebURLToVirtualServerURL( list.ParentWeb, string.Format("{0}/_layouts/listfeed.aspx?List={1}", list.ParentWebUrl, list.ID.ToString())); dr["XmlUrl"] = url; dt.Rows.Add(dr); } listsView.DataKeyNames = new string[] { "XmlUrl" }; listsView.DataSource = dt; listsView.DataBind(); } } void view_SelectedIndexChanged(object sender, EventArgs e) { GridViewRow row = listsView.SelectedRow; this.xmlUrl = listsView.SelectedValue.ToString(); } Ted Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7) 137 Webparts private void AddLists(List<SPList> lists, SPWeb web) { foreach (SPList list in web.Lists) if (list.AllowRssFeeds && list.EnableSyndication && list.BaseTemplate != SPListTemplateType.Categories && list.BaseTemplate != SPListTemplateType.ListTemplateCatalog && list.BaseTemplate != SPListTemplateType.MasterPageCatalog && list.BaseTemplate != SPListTemplateType.WebPageLibrary && list.BaseTemplate != SPListTemplateType.WebPartCatalog && list.BaseTemplate != SPListTemplateType.WebTemplateCatalog && list.BaseTemplate != SPListTemplateType.UserInformation && list.DoesUserHavePermissions(SPBasePermissions.ViewListItems)) lists.Add(list); foreach (SPWeb subweb in web.Webs) { if (web.DoesUserHavePermissions(SPBasePermissions.ViewListItems)) AddLists(lists, subweb); } } } Webpartverben Ein Webpartverb bezeichnet eine Aktion, die vom Webpartframework als Bestandteil des Anzeigerahmens (Chrome) im Webpartmenü angeboten wird. Durch diese Aktion kann eine clientseitige Funktion oder ein serverseitiger Handler aufgerufen werden. Um Webpartverben als Menübefehle hinzuzufügen, überschreiben Sie die Verbs-Eigenschaft des Webparts. Die Eigenschaft Verbs gibt eine WebPartVerbCollection zurück. Daher müssen Sie aus den gewünschten neuen Verben und der base.Verbs-Eigenschaft eine neue WebPartVerbCollection erstellen. Listing 4.9 fügt Verben für eine serverseitige Übertragung und ein clientseitiges JavaScript zum RSS-Anzeigewebpart hinzu, mit denen der Benutzer zum RSSQuellfeed wechseln kann. Abbildung 4.8 zeigt das resultierende Menü. Abbildung 4.8 Webparts können benutzerdefinierte Verben als Menüpunkte verwenden, wobei die Handler auf der Seite des Clients oder des Servers liegen können Listing 4.9 Ein Beispiel für eine Webpartverbimplementierung Verwenden von Webpartverben für benutzerdefinierte Aktionen namespace LitwareWebParts { public class RssViewWebPart : WebPart, IWebEditable { public override WebPartVerbCollection Verbs { get { List<WebPartVerb> verbs = new List<WebPartVerb>(); Ted Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7) 138 Kapitel 4: Webparts if (!string.IsNullOrEmpty(this.XmlUrl)) { WebPartVerb verb1 = new WebPartVerb(this.ID + "_ClientSideRssOpenerVerb", string.Format("window.open('{0}','RSSXML')", this.XmlUrl)); verb1.Description = "Öffne den RSS-Feed in einem externen Fenster."; verb1.Text = "Öffne den RSS-Feed"; verbs.Add(verb1); WebPartVerb verb2 = new WebPartVerb(this.ID + "_ServerSideRssOpenerVerb", new WebPartEventHandler(ServerSideVerbHandler)); verb2.Description = "Lade den RSS-Quellfeed."; verb2.Text = "Zeige den RSS-Quellfeed"; verbs.Add(verb2); } WebPartVerbCollection allverbs = new WebPartVerbCollection(base.Verbs, verbs); return allverbs; } } public void ServerSideVerbHandler(object sender, WebPartEventArgs e) { if (!string.IsNullOrEmpty(this.XmlUrl)) Context.Response.Redirect(this.XmlUrl); } // (... der restliche Code wurde der Übersichtlichkeit halber weggelassen ...) } } Webpartverbindungen Webpartverbindungen sind eine weitere Webparttechnologie, die eine Wiederverwendung der Webparts in anderen Anwendungen fördert. Verbindungen werden oft für Hauptbegriff/Detail-Datensätze verwendet. Verbindungen werden in Webparts spät (late-bound) hergestellt. Solange ein Webpart die Daten liefert, die Ihr Webpart verwenden kann, lässt es sich im Webpartframework verbinden. TIPP Webpartverbindungen wurden mit WSS 2.0 eingeführt. Für Version 3.0 wurde die Implementierung stark verändert. Die Schnittstelle ist wesentlich einfacher und beruht auf ASP.NET-Standardschnittstellen. Wie bei der Klasse Microsoft.SharePoint.WebPartPages.WebPart wurden der Namespace Microsoft.SharePoint.WebPartPages.Communication und die entsprechenden Schnittstellen zur Erhaltung der Abwärtskompatibilität noch beibehalten und sollten nicht für Neuentwicklungen verwendet werden. Verwenden Sie stattdessen die Attribute und Schnittstellen, die in System.Web.UI.WebControls.WebParts definiert werden. Verbindungen werden mit dem Attribut ConnectionProvider aktiviert. Am einfachsten können Sie eine Verbindung zu Ihrem Webpart hinzufügen, indem Sie eine benutzerdefinierte Schnittstelle wie die Schnittstelle ICustomerProvider aus Listing 4.10 implementieren und das Datenobjekt mit dem Attribut ConnectionProvider angeben. Die Schnittstelle definiert in diesem Beispiel einfach eine CustomerID-Eigenschaft, die an das verbundene Webpart übermittelt wird. Damit eine Verbindung mit dem Anbieterwebpart hergestellt wird, kennzeichnet das Verbraucherwebpart einfach eine Verbindungsmethode mit dem Attribut ConnectionConsumer. Im folgenden Beispiel finden Sie die für die Verbindung erforderlichen Methodensignaturen. Beachten Sie bitte, dass die Attribute ConnectionProvider und ConnectionConsumer für komplexere Verbindungsarten mehrfach überladen wurden. Aber die hier gezeigte einfache Verbindung dürfte für die meisten Entwicklungszwecke ausreichen. Ted Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7) 139 Webparts Listing 4.10 Ein Beispiel für Verbindungsanbieter- und Verbindungsverbraucherwebparts Beispiel für die Verbindung von Webparts using System; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; namespace LitwareWebParts { public interface ICustomerProvider { string CustomerID { get; } } public class SimpleProviderExample : WebPart, ICustomerProvider { private string customerID = "P1284"; protected override void RenderContents(HtmlTextWriter writer) { writer.Write("Kunden-ID: " + this.CustomerID); } public string CustomerID { get { return this.customerID; } } [ConnectionProvider("Customer ID", AllowsMultipleConnections = true)] public ICustomerProvider GetCustomerProvider() { return this; } } public class SimpleConsumerExample : WebPart { private ICustomerProvider customerProvider; [ConnectionConsumer("Customer ID")] public void RegisterCustomerProvider(ICustomerProvider provider) { this.customerProvider = provider; } protected override void RenderContents(HtmlTextWriter writer) { if (this.customerProvider != null) writer.Write(this.customerProvider.CustomerID); else writer.Write("Keine Verbindung"); } } } Listing 4.10 demonstriert eine Verbindung, die eine bekannte Schnittstelle für die Geschäftslogik verwendet. Außerdem gibt es im Namespace System.Web.UI.WebControls.WebParts einige vordefinierte Schnittstellen für allgemeine Verbindungen, mit denen Sie auch ein Datenfeld zwischen Webparts übergeben können. Dazu bietet sich die Schnittstelle IWebPartField an. Mit der Schnittstelle IWebPartRow können Sie auch einen ganzen Datensatz (eine Tabellenzeile) übergeben und mit der Schnittstelle IWebPartTable sogar eine ganze Tabelle. Diese Schnittstellen eignen sich für die Verbindung von lose gekoppelten Komponenten und zur Aktivierung von Verbindungen mit Webparts von anderen Herstellern. Durch die Implementierung der Schnittstelle IWebPartField im RSS-Webpart können wir Verbindungen mit selbstgeschriebenen Komponenten oder mit Listenansichtwebparts für Hyperlinklisten herstellen. TIPP Die Schnittstelle IWebPartField wird von vielen Webparts implementiert und für Verbindungen verwendet, beispielsweise von Listenansichtwebparts und vom Bildwebpart. Wenn Ihr Webpart zum Beispiel eine Verbindung mit IWebPartField herstellen kann, können Sie eine Verbraucherbindung mit einem Hyperlinklistenwebpart herstellen. Ted Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7) 140 Kapitel 4: Webparts Listing 4.11 zeigt ein Webpart, das die Schnittstelle IWebPartField implementiert und eine Verbindung mit der RSS-Anzeige herstellt. Dabei handelt es sich um eine Variante des bereits besprochenen Feedlistenwebparts, das einen XML-URL mit dem RSS-Anzeigewebpart verbindet. Die IWebPartField-Schnittstelle definiert die Eigenschaft Schema und die Methode GetFieldValue, wobei die Eigenschaft Schema Reflektionsdaten über die Parameter liefert und die Methode GetFieldValue im Webpartverbraucher von einem Delegaten benutzt wird. Um die Implementierung der Verbindung zu vervollständigen, muss noch eine Methode, die einen Verweis auf den Anbieter an den Verbraucher zurückgibt, implementiert und mit dem Attribut ConnectionProvider versehen werden. Beachten Sie bitte, dass die Methode, die für die Verbindung sorgt, keine Schnittstelle oder Basisklassenmethode ist. Es handelt sich einfach um eine Methode, die mit dem Attribut ConnectionProvider verfügbar gemacht wird. Listing 4.11 Umwandlung des Feedlistenwebparts in ein IWebPartField Das Feedlistenwebpart als IWebPartField using using using using using using using System; System.ComponentModel; System.Web; System.Web.UI; System.Web.UI.WebControls.WebParts; System.Xml.Serialization; System.Web.UI.WebControls; namespace LitwareWebParts { public class FeedListWebPart : WebPart, IWebPartField{ /* Der SPGridView-Code wurde der Übersichtlichkeit halber weggelassen. */ private string xmlUrl; [WebBrowsable(true), Category("Konfiguration"), Personalizable(PersonalizationScope.User), DefaultValue(""), WebDisplayName("Xml Url"), WebDescription("RSS Feed XML URL")] public string XmlUrl { get { return xmlUrl; } set { if (!string.IsNullOrEmpty(xmlUrl)) { Uri xmlUri = new Uri(value); xmlUrl = xmlUri.AbsolutePath; } else xmlUrl = null; } } // Erlaube dem Verbraucherwebpart den Zugriff auf diese Daten. public void GetFieldValue(FieldCallback callback) { callback(Schema.GetValue(this)); } Ted Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7) 141 Webparts // Beschaffe einen PropertyDescriptor für diese Verbindung. public System.ComponentModel.PropertyDescriptor Schema { get { PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(this); return properties.Find("XmlUrl", false); } } [ConnectionProvider("Xml-URL-Anbieter")] public IWebPartField GetConnectionInterface() { return this; } } } Nun können Sie diese Verbindung in jedem Webpart als Verbraucher herstellen, indem Sie das Attribut ConnectionConsumer für die Methode SetConnectionInterface angeben. Beachten Sie bitte, dass die Methode weder in einer Schnittstelle ist noch zu einer Basisklasse gehört. Das Besondere an ihr ist das Attribut. Nun fügen wir einen Verbindungspunkt zum RSS-Webpart hinzu, damit es die Verbindung annehmen kann. Listing 4.12 zeigt den Code für die Aktivierung der IWebPartField-Verbindung. Beachten Sie bitte den zusätzlichen Code für die Bearbeitung der durch Kommas separierten Felder, die vom Hyperlinklistenansichtswebpart geliefert werden. Die Liste liefert den Hyperlink und eine Beschreibung, die durch ein Komma getrennt wird. Listing 4.12 Aktivierung einer Verbindung mit einem IWebPartField-Anbieter Annehmen einer IWebPartField-Verbindung namespace LitwareWebParts { public class RssViewWebPart: WebPart, IWebEditable { /* Der restliche Code wurde der Übersicht halber weggelassen */ // Verbindung anfordern. [ConnectionConsumer("Xml-URL-Verbraucher", AllowsMultipleConnections = false)] public void SetConnectionInterface(IWebPartField provider) { provider.GetFieldValue(new FieldCallback(GetXmlUrl)); } // Eine Rückrufmethode für den IWebPartField-Delegaten private void GetXmlUrl(object providedUrl) { if (providedUrl != null) { // Ein Workaround für den Url-Feldtyp string[] urls = ((string)providedUrl).Split(','); if (urls.Length > 0) this.XmlUrl = urls[0]; } } } } Ted Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7) 142 Kapitel 4: Webparts Nachdem nun alle Vorbereitungen getroffen sind, können unsere Webparts verbunden werden. Beachten Sie bitte, dass es nicht erforderlich war, beide Webparts gleichzeitig zu programmieren. Außerdem brauchen die beiden Webparts nichts voneinander zu wissen, bevor sie verbunden werden. Unsere Geschäftskunden können die Webparts nun zur Laufzeit verbinden und unsere Webparts sogar mit einem Webpart von einem anderen Hersteller verbinden. Beachten Sie bitte außerdem, dass Benutzer die Webpartseite in den Bearbeitungsmodus umschalten müssen, um eine Webpartverbindung herstellen zu können. Die Verbindung kann mit dem Menüpunkt Verbindungen eingerichtet werden, der im Standardwebpartmenü erscheint (Abbildung 4.9). Abbildung 4.9 Sie können eine Verbindung zwischen zwei verbindungsfähigen Webparts herstellen, während sich die Webpartseite im Bearbeitungsmodus befindet Verwenden von Ressourcen Ressourcen sind ein ausgezeichneter Weg, um Webparts mit statischen Inhalten zu versorgen, wenn das Steuerelement keine weiteren Arbeiten durchführen muss. Außerdem ist es eine gute Lösung, statische oder lokalisierte Ressourcen einzubinden, weil dadurch keine zusätzlichen Bereitstellungsarbeiten erforderlich werden. Ressourcen lassen sich zum Beispiel verwenden, um Zeichenketten für die Zusammenstellung der Seite bereitzustellen. Eine weitere Verwendungsmöglichkeit wäre, eingebundene Skripts und Bilder über einen URL verfügbar zu machen, die Ihrem Webpart zugänglich ist. Wenn Sie eine Ressource in Ihre Webpartassembly aufnehmen möchten, wählen Sie die gewünschte Ressource aus und wählen in den Dateieigenschaften für den Buildvorgang die Option Eingebettete Ressource. Dann können Sie in der Assembly mit dem vollständigen Namespace und Dateinamen der Ressource auf die Ressource zugreifen. Listing 4.13 zeigt eine Hilfsklasse, die sich für den Zugriff auf einen Ressourcenstream oder eine Ressourcenzeichenfolge aus einer Assembly eignet, beispielsweise zur Verwendung in einem Literal-Steuerelement. Ted Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7) 143 Webparts Listing 4.13 Eine Hilfsklasse für den Ressourcenzugriff Ressourcen aus einer Assembly auslesen using System; using System.Reflection; using System.IO; internal static class WebPartResources { internal static string GetNamedResource(object reference, string fileName) { Assembly thisApp = Assembly.GetExecutingAssembly(); string resource = null; string resourceName = string.Format(@"{0}.{1}.{2}", thisApp.GetName().Name, reference.GetType().Namespace, fileName); using (Stream resStream = thisApp.GetManifestResourceStream(resourceName)) { using (StreamReader reader = new StreamReader(resStream)) { resource = reader.ReadToEnd(); } resStream.Close(); } return resource; } internal static Stream GetNamedResourceStream(object reference, string fileName) { Assembly thisApp = Assembly.GetExecutingAssembly(); string resourceName = string.Format(@"{0}.{1}", reference.GetType().Namespace, fileName); return thisApp.GetManifestResourceStream(resourceName); } } Diese Technik lässt sich für HTML-Ressourcen in Ihren Webparts einsetzen, beispielsweise für Tabellenlayouts. Stellen Sie sich zum Beispiel vor, Sie haben einen komplexen HTML-Toolbereich für Ihr Steuerelement entwickelt. Statt ihn in ASP.NET-Code mit HtmlControls zu kodieren, können Sie die Tabelle als HTML-Datei oder als .ascx-Datei entwerfen, die als Toolbar.ascx kompiliert wird, und sie dann als LiteralControl hinzufügen. Der Entwurf von HTML-Ressourcen ist in Visual Studio einfacher, wenn Sie die Dateinamensendung .ascx wählen. Vergessen Sie aber nicht, dass Sie in einer Ressource, die als eingebettete Ressource kompiliert wird, keinen Servercode unterbringen sollten. Mit dem folgenden Code könnten Sie mit der Methode GetNamedResource eine eingebettete Ressource namens Toolbar.ascx aus dem Resources-Ordner der Assembly laden: string controlText = WebPartResources.GetNamedResource("Resources", "Toolbar.ascx"); Ressourcen lassen sich außerdem verwenden, um Bilder oder JavaScript-Dateien für den Assemblyressourcenhandler bereitzustellen. Der Assemblyressourcenhandler (Assembly Resource Handler) beantwortet Webanfragen, die auf webresource.axd abgebildet werden. Diesen Mechanismus verwendet das ASP.NET AJAX Toolkit für Skriptressourcen. Mit dem Assemblyressourcenhandler können Sie alle externen Ressourcen einbinden, beispielsweise statischen HTML-Code, JavaScript oder Bilder, die von Ihrem Webpart aus der Webpartassembly verwendet werden. Das hilft Ihnen, das Entwurfsziel zu erreichen, eine Webpartanwendung so eigenständig wie möglich zu gestalten, ohne Verknüpfungen mit externen Skriptressourcen herzustellen oder JavaScript in den C#-Code einzubetten. Ted Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7) 144 Kapitel 4: Webparts Der Webressourcenhandler (Web Resource Handler) ist ein ASP.NET-HTTP-Handler, der in einer Webpartanwendung besonders nützlich ist, wenn das Webpart eine eigenständig bereitstellbare Anwendung ist. Um Ressourcen durch den Handler verfügbar zu machen, müssen Sie die Ressourcen mit dem Attribut WebResource aus dem Namespace System.Web.UI kennzeichnen. Außerdem müssen Sie in Visual Studio den Buildvorgang Eingebettete Ressource wählen. Das WebResource-Attribut sorgt dafür, dass nur solche Ressourcen über den Handler bereitgestellt werden, die als Webressourcen vorgesehen sind. Außerdem können Sie mit dem WebResource-Attribut einen Inhaltstyp für die Ressource angeben, so wie Sie in jedem HTTP-Handler einen Inhaltstyp für den Antwortstream festlegen können. Wenn Sie die Eigenschaft PerformSubstitution verwenden, erfolgt der Zugriff auf die Webressource mit ersetzten RessourceURLs. Mit der Syntax <%=WebResource("VollständigerRessourcenname")%> können Sie also den URL einer anderen kompilierten Webressource angeben. Das folgende Attribut gibt den Inhaltstyp und den vollständigen Ressourcennamen für die Einbindung der HTML-Datei help.html als Webressource an, die man als Onlinehilfe für Litware-Webparts verwenden könnte: [assembly: WebResource(@"LitwareWebParts.Resources.help.html", @"text/html")] Bei der Registrierung von Skripts durch die Klasse System.Web.UI.ScriptManager greift der Webbrowser über WebResource.axd oder ScriptResource.axd auf die Ressource zu. Der generierte URL enthält auch den Zeitstempel der letzten Erstellung der Assembly, damit der Browser eine entsprechende Zwischenspeicherung vornehmen kann. Für kulturspezifische Lokalisierungen können Sie Ressourcen auch in Satellitenassemblys unterbringen, einschließlich lokalisierter JavaScript-Ressourcen, mit denen Sie Ihre Webpartanwendungen auf den internationalen Einsatz vorbereiten können. Im nächsten Kapitel, wenn wir uns mit der Erstellung von AJAX-fähigen Webparts beschäftigen, verwenden wir wieder JavaScriptRessourcen. Arbeiten mit Webparts im SharePoint-Websiteobjektmodell Wie alle Daten von SharePoint ist auch das Webpartframework über das Objektmodell von SharePoint zugänglich. Webpartinstanzen werden über Webpartauflistungen (Webpartsammlungen) bereitgestellt, die über das SPFile-Objekt zugänglich sind. Im Programmcode erhalten Sie Zugriff auf den Webpartmanager, wenn Sie die Methode GetLimitedWebPartManager eines SPFile-Objekts verwenden. Das ist eine andere Klasse als der Webpartmanager, der von der Webpartseite verwendet wird (dabei handelt es sich um ein Steuerelement, wie Abbildung 4.10 zeigt). Stattdessen ist der SPLimitedWebPartManager ein Ressourcenmanager, der nur für die Verwaltung der Webparts im Websiteobjektmodell verwendet wird. Während die Klasse SPWebPartManager von der Klasse WebPartManager abgeleitet wird, die wiederum von Control abgeleitet wird, wird die Klasse SPLimitedWebPartManager von System.Object abgeleitet und enthält keine Steuerelementfunktionen. Demselben Grundmuster folgt die Klasse SPLimitedWebPartCollection, die sie als ihre WebParts-Eigenschaft zugänglich macht. Dabei handelt es sich um eine leichtgewichtige (lightweight) Klasse, die der Klasse SPWebPartCollection ähnelt, aber schreibgeschützt ist und nur über einen beschränkten Funktionsumfang verfügt. Wenn Sie die Klasse SPFile mit einem bestimmten URL verwenden, können Sie die Webparts in den Webpartzonen der Seite mit SPLimitedWebPartManager bearbeiten. Bei der Featureaktivierung können Sie zum Beispiel die Eigenschaften der bekannten Webparts bearbeiten, Webparts hinzufügen und andere Webparts entfernen. Sie können auch von vorhandenen Websites Webparts in ihre XML-Definitionen exportieren und Dateimodule mit eingebetteten Webpartbeschreibungen für die Verwendung in Features und Websitedefinitionen erstellen. Der Code in Listing 4.14 zeigt, wie man eine Instanz von RssViewWebPart zur Standardwebseite hinzufügt. Die Methode ExportWebPart der Klasse SPLimitedWebPartManager kann auch für den Export von Webparts nach XML verwendet werden. Listing 4.15 zeigt, wie Ted Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7) 145 Webparts ein Webpart in eine Datei exportiert wird. Diese Methode ist nützlich, wenn Sie eine Seitendefinition in XML erstellen und in eine Featuredefinition aufnehmen möchten. Abbildung 4.10 Ein Vergleich der Klassen SPLimitedWebPartManager und SPWebPartManager; der Übersichtlichkeit halber wurden nicht alle Methoden aufgelistet Listing 4.14 Bearbeitung von Webparts mit dem Websiteobjektmodell von SharePoint Verwenden von SPLimitedWebPartManager zur Bearbeitung von Webparts SPSite siteCollection = new SPSite("http://localhost"); SPWeb web = siteCollection.RootWeb; SPFile file = web.Files["default.aspx"]; SPLimitedWebPartManager webPartManager = file.GetLimitedWebPartManager(PersonalizationScope.Shared); WebPart rssPart = new RssViewWebPart(); webPartManager.AddWebPart(rssPart, "Links", 0); Ted Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7) 146 Listing 4.15 Kapitel 4: Webparts Exportieren von Webparts mit dem Websiteobjektmodell von SharePoint Verwenden von SPLimitedWebPartManager für den Export von Webparts SPSite siteCollection = new SPSite("http://localhost"); SPWeb web = siteCollection.RootWeb; SPFile file = web.Files["default.aspx"]; SPLimitedWebPartManager webPartManager = file.GetLimitedWebPartManager(PersonalizationScope.Shared); XmlWriter xwriter = new XmlTextWriter("Example.webpart", Encoding.UTF8); if (webPartManager.WebParts.Count > 0) webPartManager.ExportWebPart(webPartManager.WebParts[0], writer); Zusammenfassung In diesem Kapitel haben Sie die Grundlagen der Webpartentwicklung und weiterführende Webpartkonzepte wie Editorparts, Verben, Verbindungen und die asynchrone Ausführung von Aufgaben kennengelernt. Wir haben uns mit den Unterschieden zwischen der WSS-Webpartentwicklung und der herkömmlichen ASP.NET-Programmierung beschäftigt. Nun sollten Sie in der Lage sein, Ihre eigenen Webparts zu entwickeln, wobei Sie anpassbare und personalisierbare Eigenschaften verwenden, aggregierte Steuerelemente und asynchrone Methoden benutzen und die Webparts nach Bedarf untereinander verbinden. Außerdem sollten Sie wissen, wie man mit dem Websiteobjektmodell von SharePoint Webparts auf Webpartseiteninstanzen bearbeitet. Im nächsten Kapitel entwickeln wird diese Grundtechniken weiter und beschäftigen uns damit, wie man mithilfe des ASP.NET AJAX-Frameworks eine umfangreiche Internetclientschnittstelle entwickelt. Ted Pattison, Daniel Larson: Windows SharePoint Services 3.0-Programmierung. Microsoft Press2007 (ISBN 978-3-86645-632-7)