Ach ja, bevor ich es vergesse - das alles natürlich nur in HTML, bitte schön! Vielleicht hatten Sie auch schon solche Gespräche, in denen euphorische Kunden die Vorzüge von modernen Benutzeroberflächen aufzuzählen wissen - inklusive ausgefeilter Baumstrukturen, Drag & Drop und nicht zuletzt der Nutzung von Web Services für die Kommunikation sämtlicher Systeme untereinander. Schließlich gelangt man jedoch zu der Erkenntnis, dass bei all den Ansprüchen doch bitte HTML zum Einsatz kommen soll.
Wird der Grund dieser Anforderung erfragt, dann wird sie zumeist mit der nicht notwendigen Installation von Anwendungen begründet. Daneben taucht teilweise auch die Plattformunabhängigkeit von HTML-Anwendungen als möglicher Grund auf. Aber bleiben wir doch realistisch: Die wenigsten komplexen ASP.NET-Anwendungen werden wohl den Mac-Test ohne Komplikationen bestehen.
Für reine Microsoft-Umgebungen bietet sich ein weiterer Weg an, um komplexe Oberflächen zumindest ohne aufwändige Installationsprozeduren in ASP.NET-Anwendungen zu integrieren. Hierbei erstellt der Entwickler Benutzerkontrollelemente unter Nutzung von Windows Forms und integriert diese mit Hilfe des <Object>-Tags in HTML-Seiten. Diese Fähigkeit besitzt allerdings nur der Microsoft Internet Explorer ab der Version 5.05.
Bis zur fertigen Anwendung haben die Entwickler des .NET Frameworks aber nicht mit Steinen gespart, die sie uns in den Weg gelegt haben. Die Grundzüge der Technik bestehen zum einen in Altbekanntem wie der Nutzung von ActiveX-Kontrollelementen (OCX) im Internet Explorer, zum anderen aber auch im so genannte No-Touch Deployment. Diese Grundelemente werden nun mit reichlich inkonsistenten Elementen von COM Interop - also der Verbindung zwischen der .NET- und COM-Welt - angereichert.
Die Basis
Beginnen wir mit einem einfachen Beispiel und legen zunächst die Zielvorgaben fest. In einer HTML-Seite soll eine Baumstruktur dargestellt werden. Nach der Auswahl der einzelnen Knoten sollen die zugehörigen Daten auf der Seite angezeigt werden. Die resultierende Darstellung der HTML-Seite finden Sie in Abpictureung 1.Zur Realisierung erzeugen Sie zunächst eine Klassenbibliothek und dort ein Benutzersteuerelement. Wichtig ist dabei, die resultierende Assembly mit einem Strong Name auszustatten. Somit ist eine Schlüsseldatei (sn.exe) zu erzeugen und diese der Assembly zuzuweisen. Daneben ist es sinnvoll, die Versionskennung auf einen festen Wert einzustellen, damit die resultierende Datei später besser identifiziert werden kann.
Im nächsten Schritt gilt es nun, eine HTML-Datei zu erzeugen und dort auf die Komponente zu verweisen. Für die Einbettung nutzen Sie den IE-spezifischen <Object>-Tag. Das Kontrollelement aus unserem Beispiel wird wie folgt in den Code eingebunden:
<OBJECT id="mytree" name="mytree" height="100%" width="40%" classid="http:userControl.dll#userControl.ucMyTree" VIEWASTEXT></object>
Wichtig ist hierbei, dass sich die Assembly und die HTML-Datei im selben Verzeichnis auf einem Webserver befinden. Bei Bedarf kann der URL der Assembly angepasst werden; eine Nutzung direkt aus dem Dateisystem ist allerdings nicht möglich.
Die Nutzung des Object-Tags für die Einbettung von ActiveX-Kontrollelementen ist schon seit Längerem im Internet Explorer möglich. Die Syntax dort unterscheidet sich allerdings wesentlich von dem Aufbau hier. Ein ActiveX-Kontrollelement ist letztlich ein COM-Objekt mit einem definierten Satz von zu implementierenden Interfaces. Wie bei COM üblich, sind solche Objekte immer zunächst in der Registry mit der entsprechenden GUID einzutragen. Die GUID kann dann im Object-Tag genutzt werden. Vielleicht ist Ihnen eine Syntax wie classid=CLSID:xxxx-xxx bekannt. Daneben nutzt die ActiveX-Welt das codebase-Attribut im Object-Tag, um eventuell fehlende Objekte mit Hilfe einer CAB-Datei nachzuladen. Prinzipiell bleibt das Grundschema erhalten. Bei einem installierten .NET Framework ist der Internet Explorer jedoch in der Lage, den Object-Tag auch mit der geänderten Syntax zu interpretieren. Unter der class-id steht nun einfach ein URL, der die Lage einer .NET-Komponenten-DLL angibt. Getrennt durch ein Hash (#) wird hiernach der vollständige Namespace-Name des Kontrollelements angegeben.
Versteckte COM-Objekte
Als Resultat übergibt der Internet Explorer die Anforderung an das .NET Framework und lädt somit die Komponenten in den so genannten Assembly Download Cache (ADC). Bei Bedarf lässt sich der Status dort mit Hilfe des GACUTILS einsehen bzw. der Cache entleeren. Nach dem Herunterladen kann die Komponente dann gestartet werden - sprich: Ein Objekt wird erzeugt. Hierzu wird dann der hinter dem Hash-Zeichen angegebene Name verwendet. Bitte beachten Sie hier, dass dies schematisch der Nutzung des Reflection-Mechanismus entspricht, allerdings bleibt der Internet Explorer auch nach der .NET-Installation eine unverwaltete (unmanaged) Anwendung und daher kann die Komponente nicht direkt ausgeführt werden. Als besonderen Trick verwendet Microsoft hier den Umweg über Interop. Die Komponente erscheint dem Internet Explorer daher intern als ActiveX-Kontrollelement. Nach der Erzeugung können die .NET-Komponenten dann auch entsprechend genutzt werden.Bei der Programmierung des Steuerelements sind Ihnen letztlich keine Grenzen gesetzt. So können Sie hier sämtliche Fähigkeiten des .NET Frameworks nutzen. Möglicherweise wird dabei allerdings das Sicherheitssystem von .NET Ihren Tatendrang zunächst beschränken. Nutzern von ActiveX-Kontrollelementen ist dieses Konzept zunächst fremd. Entscheidet sich der Benutzer, das Kontrollelement auszuführen, dann erbt der Code dort letztlich die Rechte des Benutzers - ein entscheidender Nachteil dieser Technologie. Beim Einsatz von .NET-Windows Forms-Benutzersteuerelementen tritt das .NET Framework mit seinen mächtigen Sicherheitseinstellungen in Erscheinung. Als Ergebnis des Sicherheitssystems wird die Assembly zwar geladen und im ADC abgelegt, allerdings kann das Objekt weder erzeugt noch ausgeführt werden.
Fatal ist nun, dass der Anwender keine Meldung erhält. Um den Start der Assembly zu ermöglichen, ist mittels der .NET-Konfiguration eine Code-Gruppe zu erzeugen und die Evidence der Assembly so anzugeben, dass die Komponente entsprechend erfasst wird. Für die ersten Versuche erzeugen Sie unter LAUFZEITRICHTLINIEN | COMPUTER eine neue Codegruppe und setzen die Mitgliedschaftsbedingung (Evidence) einfach über die URL Ihres Webservers (siehe Abb. 2). Als Berechtigungssatz wählen Sie Full Trust. Hiermit stellen Sie die Verhältnisse wieder in Richtung ActiveX her und für eine spätere Produktionsumgebung sollten die Berechtigungen nach Bedarf geringer ausfallen.
Feinarbeit
Eine Komponente wird dann richtig sinnvoll, wenn sie nach Bedarf parametrisiert werden kann. Durch die Verpackung in ein COM-Objekt werden auch sämtliche öffentlichen Klassen-Variablen sowie alle Properties sichtbar. In der HTML-Seite können die Werte mit Hilfe des Param-Tags gesetzt werden. Dabei sind die Parameter jeweils mit Angabe von Name und Value zu setzen. Die entsprechenden Tags werden syntaktisch innerhalb des Object-Tags des Kontrollelements genutzt. Hier ein Beispiel dazu:<OBJECT id="mydetail" name="mydetail" height="100%" width="60%" classid="http:userControl.dll#userControl.ucMyControl" style="FONT-SIZE: 10pt; COLOR: lime; FONT-FAMILY: Verdana, 'Arial Black'; BACKGROUND-COLOR: gray" ><param name="InnerText" value="default value"></object>
Beachten Sie bitte, das diese Form der Parametrisierung nur bei Werten funktioniert, deren Typ durch einen einfachen Datentyp dargestellt wird. Komplexere Datentypen können hier nicht genutzt werden.
Eine weitere interessante Fähigkeit besteht darin, dass die Kontrollelemente die Darstellungsumgebung des Internet Explorer nutzen. So können Art und Größe der Schrift sowie Farben und Hintergründe über Stylesheets oder die Einstellungen im MSIE direkt angegeben werden. Entsprechendes gilt auch für externe CSS-Dateien, sodass sich auch die .NET-Kontrollelemente sehr einfach an ein beliebiges Design anpassen lassen.
Scipting von Komponenten
Ähnlich nahtlos wie bei der Nutzung von Stylesheets integrieren sich die .NET-Komponenten auch bei ihren Scriptfähigkeiten in eine HTML-Seite. So lassen sich die Eigenschaften der Komponenten auch über ein Script setzen und abfragen. Ähnliches gilt auch für jene Methoden, die auch der .NET-Seite des Benutzersteuerelements hinzugefügt wurden. Die Methoden lassen sich ohne Probleme in einem JavaScript-Code auf der Clientseite aufrufen. Auch bei den Parametern der Methoden sollten nur die einfachen Datentypen wie String und die numerischen Typen genutzt werden. Eine beispielhafte Implementierung finden Sie im folgenden Quellcode:<script language=javascript>function LocalRefresh() {Form1.mytree.refreshData();}</script><INPUT type="button" value="Local Refresh" onClick="Javascript:LocalRefresh();">
Beachten Sie hier, dass der Aufruf indirekt über die Nutzung eines Ereignis-Handlers auf einem HTML-Button erfolgt. Wie in JavaScript üblich ist beim Festlegen der Methodennamen auf die richtige Groß/Klein-Schreibweise zu achten. Daneben ist es sinnvoll, auf das entsprechende Objekt über eine geeignete Hierarchie zuzugreifen wie etwa hier durch ein benanntes Objekt innerhalb eines bestimmten Formulars. Dabei ist bei dem Object-Tag dann auch das name-Attribut zu nutzen, damit der Zugriff innerhalb der DOM-Hierarchie möglich ist.
Behandlung von Ereignissen im IE
Neben Eigenschaften und Methoden sind Ereignisse und die Reaktion auf selbige eine typische Anwendung von Komponenten. Leider ist die Nutzung innerhalb des Internet Explorers hier recht eigentümlich. Bei der Nutzung von VBScript kann eine spezielle Benennung der Event Handler-Methoden erfolgen, wobei dann allerdings auch der Code mit dieser Sprache geschrieben werden muss. Bei der Nutzung in HTML-Seiten ist aber JavaScript die Sprache der Wahl und gerade hier scheint der einzig zuverlässige Weg über die Nutzung der attachEvent-Methode zu führen. Betrachten Sie dazu das folgende Beispiel:<SCRIPT language=javascript>function init() {Form1.mytree.attachEvent("OnAfterSelected", mytree_OnAfterSelected);}function mytree_OnAfterSelected(key) {Form1.mydetail.InnerText = "Event " + key;}</script><body onLoad="javascript:init()">
Hier wird das onLoad-Event des Body-Tags dazu genutzt, die Verbindung zwischen der Komponenten- und einer Ereignisprozedur herzustellen. Auch gilt ähnlich wie beim Scripting die Beachtung der Groß/Klein-Schreibung und die Nutzung des Name-Attributs zur Benennung des Kontrollelements.
Der Interop Event-Sumpf
Ist die Nutzung der Ereignisprozeduren auf der Clientseite noch als fast komfortabel zu bezeichnen, so ist die Implementierung der notwendigen Elemente in der Komponente auf den ersten Blick eher kryptisch. Ein Grundproblem besteht darin, dass die Nutzung der Komponente im IE letztlich auf der COM-Interop-Brücke basiert. Daher müssen auch die Ereignisse als COM-konform implementiert werden. Aber gerade hier liegt das grundsätzliche Problem: Die Implementierung von .NET-Ereignissen basiert auf der Nutzung von Delegaten, wohingegen COM nur einfache Methoden nutzt. Leider wird bei der Verpackung einer .NET-Komponente in einem COM-Objekt die Wandlung der Ereignisse nicht so vorgenommen, dass der MSIE diese richtig erkennt.Wesentlich für die Nutzung von Ereignissen ist ein COM-Interface, das die Quelle (Source) von Ereignissen darstellt. Ein Objekt, welches potenziell Ereignisse auslösen kann, wird mit einem solchen Interface markiert. Glücklicherweise können wir auch in .NET ein solches Interface definieren und unsere Klasse mit Hilfe eines Attributs mit einem solchen Source-Interface verbinden. Leider funktioniert dies allerdings nur dann, wenn auch alle weiteren Methoden und Eigenschaften nicht automatisch definiert werden. Letztlich schalten wir sämtliche Automatismen zunächst aus, um sie dann manuell erneut zu implementieren.
Bei der Durchsicht des Sourcecodes im Listing 1 sehen Sie, dass zunächst die automatische COM-Wandlung mittels des Attributs ClassInterface und der Angabe von ClassInterfaceType.None für die Control-Klasse ucMyTree ausgeschaltet wird. Das Interface IieTreeCOMEvents wird nun als ComSourceInterface definiert. Dabei sind bei der Interface-Definition eine GUID, der Interface-Typ InterfaceIsIDispatch und bei den einzelnen Methoden jeweils eine DispatchID ab 0x60020000 (dezimal = 1610743808) anzugeben. Zur einfacheren Nutzung bietet es sich an, innerhalb der Klasse auch die .NET-Ereignisform mittels eines Delegaten zu definieren. Dabei muss der Name des Events (OnAfterSelected) mit dem Namen der Methoden innerhalb des Source-Interfaces und die Signatur der Methode mit der Delegaten-Signatur übereinstimmen. Nur dann führt ein RaiseEvent zur Weitergabe an die COM Welt. Das zweite Interface IieTreeCOMIncoming stellt die äußere Schnittstelle dar. Somit sollten hier sämtliche öffentlichen Methoden und Eigenschaften definiert werden. Wie Sie sehen, ist die Implementierung nicht wirklich intuitiv, wobei die hier beschriebene Form nur bei der Bereitstellung von Ereignissen genutzt werden muss.
Listing 1
<ClassInterface(ClassInterfaceType.None), _ComSourceInterfaces("userControl.IieTreeCOMEvents")> _Public Class ucMyTreeInherits System.Windows.Forms.UserControlImplements IieTreeCOMIncomingPublic Delegate Sub AfterSelectedHandler(ByVal key As String)Public Event OnAfterSelected As AfterSelectedHandlerPublic Sub refreshData() Implements IieTreeCOMIncoming.refreshData...End SubPrivate Sub TreeView1_AfterSelect(...)RaiseEvent OnAfterSelected(e.Node.Text)End SubEnd Class<Guid("CACAC1DC-F83D-40e6-879E-FA659EFD53AF"), _InterfaceType(ComInterfaceType.InterfaceIsIDispatch)> _Public Interface IieTreeCOMEvents<DispId(1610743808)> _Sub OnAfterSelected(ByVal key As String)End InterfacePublic Interface IieTreeCOMIncomingSub refreshData()End Interface
Kommunikation mit dem Server
Natürlich können die Komponenten auch auf die Fähigkeiten von .NET für die Nutzung von Web Services zurückgreifen. Gerade im Rahmen einer ASP.NET-Entwicklung ist dies sicherlich die zu bevorzugende Form der Kommunikation mit dem Webserver. Die Standard-Sicherheitseinstellung erlaubt dabei einer Komponente nur den Zugriff auf einen Web Service, der sich auf dem selben Webserver befindet, von dem auch die Komponente geladen wurde.Grundsätzlich kann eine Komponente auch weitere Komponenten und Assemblies nachladen. Somit ist etwa auch die Nutzung von Ressourcen oder Konfigurationsdateien möglich. Grundsätzlich werden dabei die selben Regeln für das Auffinden einer nachzuladenden Assembly genutzt wie auch im lokalen Betrieb. Einzig die Einstellungen des Webserver könnten eventuell etwa das Nachladen bestimmter Datei-Typen (z.B. config-Dateien) grundsätzlich unterbinden. Bei Bedarf sollte diese Restriktion für einzelne Verzeichnisse aufgehoben werden.
Fazit
Die Nutzung von .NET-Windows Forms-Komponenten in ASP.NET-Anwendungen ist eine gelungene Kombination aus einer Fat Client-Anwendung mit allen Möglichkeiten des Frameworks und den Vorteilen einer HTML-Anwendung. Zumindest für die Entwicklung von Administrationsanwendungen im Intranet-Bereich erweist sich diese Technologie als äußerst leistungsfähig, ohne die bisherigen von ActiveX gewohnten Sicherheitsprobleme aufzuweisen.Carsten Harnisch ist ein freier Systemberater im Bereich Dokumentenmanagement, Content Management, Knowledge Management und Customer Relationship Management. Sie erreichen ihn mit Fragen und Anregungen unter c.harnisch@harnisch-consulting.de.






