.NET Server Controls verwenden

12.08.2003 von THOMAS WOELFER 
Die Server-seitigen Controls von .NET erlauben die Gestaltung von Webseiten, ohne Rücksicht auf jeden einzelnen Browser nehmen zu müssen. Wie Sie die Controls richtig einsetzen, zeigt dieser Artikel.

Webseiten zu bauen, ist gar nicht so einfach: Will man irgendetwas außerhalb der Reihe realisieren, bekommt man schnell Probleme mit den unterschiedlichen Browsern. Nun hat der Internet Explorer sich zwar im Großen und Ganzen durchgesetzt, trotzdem will man aber Netscape, Mozilla, Konqueror oder Opera nicht aussperren. Eine einfache Lösung zur Vermeidung von Mehrarbeit bieten dabei die .NET Server Controls.

Ein typisches Problem auf Webseiten ist die Organisation und Darstellung von hierarchischen Daten. Optimal wäre dazu eine Baumstruktur wie im Explorer von Windows, doch das ist mit reinem HTML eine Qual. Verwendet man hingegen Javascript, DHTML und das DOM, läuft man schnell gegen eine Wand:

Trotz der vielfach gepriesenen Konformität zum Standard vieler Browser funktionieren die Dinge nicht immer so, wie man sich das wünschen würde. Schlimmer noch: Mit jedem neuen Release oder Patch verändert sich das Verhalten der Browser ein bisschen - und mit etwas Pech funktioniert dann der eigene Code nicht mehr. Als Resultat hat man dann hinterher eine Browser-Weiche im Javascript-Code und jede Menge unterschiedlicher Implementierungen für ein und dieselbe Funktionalität. Das gilt nicht nur für Bäume: Auch Navigationselemente wie Pulldown-Menüs und Ähnliches sind von diesem Problem betroffen.

Dem kann mit .NET Abhilfe geschaffen werden. Dazu benötigt man einen Internet Information Server 5 oder 6 - die 5er Variante gibt es zum Beispiel in Windows 2000 und XP Professional, die 6er Variante in Windows 2003 Server. Zusätzlich benötigt man das .NET Framework auf dem Server. Bei Windows XP und 2000 muss man das Framework nachträglich installieren, wenn das nicht bereits im Rahmen eines Servicepack geschehen ist. Beim Windows 2003 Server ist das Framework von vornherein dabei.

Ob man dabei das Framework in Version 1.0 oder in Version 1.1 benutzt, ist gleichgültig, denn die Server Controls lassen sich mit beiden Varianten verwenden.

Bäume darstellen mit dem TreeControl

TreeControls - egal ob für Client- oder Server-Seite - sind in kommerzieller Form von Drittanbietern schon länger zu haben. Ein besonderer Fall ist da zum Beispiel der Javascript-Baum von Ansgar Federhen, der hier kostenlos zu haben ist. Das Control läuft auf dem Client in Javascript und funktioniert mit praktisch allen gängigen Browsern. Allerdings muss dazu immer eine Menge Javascript-Code ausgeliefert werden: Wen das nicht stört, der ist mit "js-menue" gut bedient.

Praktischer sind allerdings die DHTML-Behaviours von Microsoft. Diese - nicht offiziell unterstützten - Controls funktionieren ebenfalls rein Client-seitig, haben aber den Vorteil, dass die Bestückung mit Daten über XML-Dateien erfolgt. Diese sind relativ einfach zu erstellen und zu warten. Der Nachteil: Diese Elemente funktionieren ausschließlich mit dem Internet Explorer und können daher bestenfalls in Intranets oder anderen kontrollierten Umgebungen verwendet werden.

Beim "TreeView Server Control" von .NET sieht die Sache besser aus. Es lässt sich einfach auf einer ASP-Seite verwenden, ohne dass man sich Gedanken über den Browser des Besuchers machen muss: Im Wesentlichen läuft die Sache dabei so ab, dass bereits beim Request des Client ermittelt wird, welchen Code er benötigt. Je nach Client wird dann unterschiedlicher Code ausgeliefert. Im besten Fall gibt es reines DHTML mit Script-Elementen. Bei älteren Browsern wird bei Bedarf auch reines HTML 3.2 ausgegeben, wodurch allerdings deutlich mehr Traffic entsteht: Öffnet der Benutzer einen Ast im Baum, so erzeugt das eine erneute Anfrage an den Server, der mit einer neuen HTML-Seite antwortet, in der der gewünschte Ast dann eben in geöffneter Form dargestellt ist.

Ähnlich wie beim DHTML-Behaviour erfolgt die Bestückung mit Daten auch beim "TreeView Server Control" per XML-Datei. Dabei können auf Wunsch auch mehrere Dateien zum Zuge kommen. Jede dieser Dateien wird XML-Island genannt und kann einen Teil des anzuzeigenden Baums enthalten. Das ist dann sehr praktisch, wenn zum Beispiel unterschiedliche Teile des Baumes von unterschiedlichen Personen verwaltet werden oder wenn ein Teil des Baumes in statischer Form vorliegt, während ein anderer dynamisch erzeugt werden muss.

Knoten definieren: Inline oder per XML

Im Prinzip funktioniert das Ganze so, dass man für jeden Knoten im Baum eine Quelle angeben kann. Diese Quelle ist entweder inline definiert oder sie ist ein anderes XML-Island. Zusätzlich zu seinen Daten kann jeder Knoten eine ganze Reihe an Optionen haben. Dazu gehört beispielsweise der anzuzeigende Text, die dazugehörige URL und auch die beiden Icons für geöffneten oder geschlossenen Zustand der Zweige. Bei inline definierten Knoten werden diese Attribute direkt in der Knotendefinition angegeben, bei Daten aus einem anderen XML-Island befinden sich die Attribute innerhalb dieses Island.

Um das Server-basierte TreeControl benutzen zu können, muss man auf dem Webserver zwei Verzeichnisse anlegen: bin und webctrl_client\\1.0. In das Verzeichnis bin kopieren Sie einfach die DLL mit den Controls aus dem .NET-Framework (Microsoft.Web.UI.WebControls.dll). Nach webctrl_client\\1.0 kommen die Bilder und Behaviours. Beide Elemente sind im Download zu den WebControls auf dem Server von Microsoft zu finden. Für die WebControls gibt es übrigens auch den Quellcode: Wenn Sie also Änderungen daran vornehmen wollen, benötigen Sie dazu das .NET Framework SDK.

Den Download der Server Controls finden Sie hier.

Erster Test des TreeControl

Haben Sie die Support-Dateien für die Server-Controls installiert, können Sie den ersten Test mit dem TreeControl starten. Dazu brauchen Sie eine ASPX-Seite auf Ihrem Webserver. Die Seite hat im Wesentlichen den folgenden Inhalt:

<%@ Register TagPrefix="ie" Namespace="Microsoft.Web.UI.WebControls" Assembly="Microsoft.Web.UI.WebControls" %>

<HTML><body>

<form runat="server">
<ie:TreeView runat="server" SystemImagesPath="/webctrl_client/1_0/treeimages" id="TreeView1">

<ie:TreeNode TreeNodeSrc="island.xml" Expanded="true" ImageUrl="/webctrl_client/1_0/images/root.gif" Text="Ein Island"></ie:TreeNode>

<ie:TreeNode IMAGEURL="../common/images/text.gif" TEXT="text1" NAVIGATEURL="seite1.html" />
<ie:TreeNode IMAGEURL="../common/images/text.gif" TEXT="text1" NAVIGATEURL="seite2.html" />

</ie:TreeView>
</form>

</body></HTML>

Ganz oben auf der Seite befindet sich eine Angabe darüber, welcher Namespace beziehungsweise welche Assembly verwendet werden soll. Für den Namespace wird bei der Gelegenheit direkt eine Abkürzung registriert, so dass die Objekte aus diesem Namespace später leichter verwendbar sind. Das erledigt die ZeileRegister.

Dann kommt der ganz normale HTML-Teil, bestehend aus dem HTML- und dem body-Tag.

Server Controls müssen in eine Form verpackt werden

Im Folgenden benötigen Sie ein Form. Immer wenn Sie eines der WebControls verwenden möchten, müssen Sie das in einem Form tun - ansonsten versagen die Controls teilweise ihren Dienst. Das Form erhält einen zusätzlichen Parameter runat, mit dem klargestellt wird, dass die Ausführung auf dem Server stattfinden soll. Das Form wird weiter unten ebenso normal abgeschlossen, wie auch die anderen HTML-Statements.

Dem TreeView werden noch zwei Parameter mitgegeben: Der eine dient einfach zur Identifizierung des Controls, der andere gibt an, wo sich die zu verwendenden Bilder befinden. Bei diesen Bildern handelt es sich um die Bitmaps für die "+"- und "-"-Zeichen im Baum sowie um weitere Bitmaps, die für die Baumdarstellung benötigt werden.

Innerhalb des Controls werden dann die Knoten (TreeNodes) eingefügt. Die Knoten können dabei verschachtelt werden: Diese Verschachtelung der Knoten führt dann in der Browser-Anzeige zum Bild des Baumes.

Im einfachsten Fall ist ein Knoten vollständig inline definiert. Unter Weglassen von Attributen wie dem zu verwendenden Font oder der zu verwendenden Farben hat eine solche Knotendefinition folgendes Aussehen:

<ie:TreeNode IMAGEURL=" /webctrl_client/1_0/images/html.gif" TEXT="text1" NAVIGATEURL="seite1.html" />

Innerhalb des Knotens gibt es die Eigenschaft IMAGEURL, die das Bild für den Knoten festlegt. Für aufklappbare Knoten existiert die zusätzliche Eigenschaft EXPANDEDIMAGEURL, die für den Knoten im aufgeklappten Zustand zuständig ist.

Der Parameter TEXT ist für den neben dem Knoten anzuzeigenden Text zuständig. Schließlich gibt es noch die EigenschaftNAVIGATEURL. Diese legt die URL fest, zu der der Anwender bei einem Klick auf den Text navigieren soll. Man kann sich relativ leicht vorstellen, dass die Nutzung des Controls in diesem Zusammenhang hauptsächlich innerhalb eines Frame Sinn macht. Ansonsten müsste der Baum in jeder einzelnen anzunavigierenden Seite enthalten sein.

Knoten auslagern mit XML Islands

Stammen die Daten eines Knoten aus einem XML-Island, dann muss dieses Island wie folgt aufgebaut sein:

<TREENODES>
<treenode Text="Topnode 1">
<treenode Text="Knoten 1.1" />
<treenode Text="KNoten 1.2" />
<treenode Text="Knoten 1.3" />
</treenode>
<treenode Text="Topnode 2" >
<treenode Text="Knoten 2.1" />
<treenode Text="Knoten 2.2" />
<treenode Text="Knoten 2.3" />
</treenode>
</TREENODES>

Eingeschlossen wird das Island von einem <TREENODES>-Tag. Darin enthalten sind alle Knoten des Island. Diese setzen sich aus ganz normalen TreeNode-Statements zusammen. Im Beispielcode sehen Sie dabei auch ein Beispiel für die Verschachtelung von Knoten.

Den kompletten Beispielcode mit einer funktionierenden ASPX-Seite und einem zugehörigen XML-Island gibt es bei tecChannel.de zum Download (webcode: windowscompact).

DataGrid-Control

Ein anderes interessantes Server-seitiges Control ist das ASP DataGrid. Ähnlich wie das Repeater-Control oder die DataList dient das DataGrid-Control der Anzeige von Daten. Dabei ist es aber nicht so, dass diese Daten unbedingt immer aus einer Datenbank kommen müssen: Datenquellen praktisch beliebiger Art lassen sich an das DataGrid binden. Immer wenn Sie Daten in einer Form vorliegen haben, in der das .NET Framework darüber iterieren kann, sind diese auch als Eingabewerte für das DataGrid verwendbar. Das macht die Sache interessant, denn iterieren lässt sich über praktisch jede Sammlung in .NET.

Angenommen, Sie haben ein Verzeichnis auf Ihrem Server, in dem die Download-Dateien gesammelt werden. Auch in einem solchen Fall können Sie das ASP Datagrid verwenden, um diese Liste anzuzeigen - und das mit ganz wenigen Codezeilen.

Welche der Dateiinformationen dabei angezeigt werden, bleibt Ihnen überlassen, denn wenn sich eine Sammlung aus Objekten zusammensetzt, dann kann man beim Binden der Daten an das GridControl angeben, welche der Eigenschaften aus den Ausgangsobjekten angezeigt werden sollen.

ASP bietet deutlich weniger Funktionalität als ASP.NET

Um die Dateiliste anzuzeigen, brauchen Sie erst einmal eine programmatisch verwendbare Liste der Dateien aus dem Download-Verzeichnis. Vor ASP.NET mussten Sie das FileSystem-Objekt bemühen. Das ist jetzt nicht länger notwendig - es gibt deutlich bessere Möglichkeiten. Für den Zugriff auf das FileSystem definiert .NET den Namespace System.IO mit einer Reihe von Objekten, die diese Dateisystemzugriffe kapseln.

Eines der in diesem Namespace angebotenen Objekte ist DirectoryInfo. Dieses Objekt hat eine Methode namens GetFiles(), die anhand einer Wildcard-Angabe ein Array aus FileInfo-Objekten liefert. Sie bieten alle für eine Datei relevanten Informationen in Form von Properties an. Dieses Array eignet sich auch als Datensammlung für ein Datagrid: Man kann also ganz einfach eine Dateiliste in einer DataGrid-Tabelle anzeigen lassen.

Dazu legen Sie zunächst eine neue Webanwendung an. Im Beispiel wird dafür C# verwendet. Der benötigte Code ist aber minimal, eine Umstellung auf eine VB-Variante daher kein großes Problem.

Auf das Form der WebApplication ziehen Sie dann ein DataGrid-Control. In dessen Eigenschaften stellen Sie AutoGenerateCollumns auf false und legen statt dessen die gewünschten Spalten von Hand in der Collumns-Collection in den Eigenschaften an. Im Beispielcode wurden Spalten für den Dateinamen, das Datum des letzten Schreibzugriffs und die Dateigröße angelegt. Diese drei Spalten müssen dabei nicht nur den Titeltext erhalten, es sind auch die Namen der Properties aus dem FileInfo-Objekt anzugeben: Dies sind Name, LastWriteTime und Length.

Natürlich kann man das Grid nun noch ein wenig hübscher formatieren, indem man alternierende Farben und unterschiedliche Fonts für die Überschriften angibt: Notwendig ist das aber nicht. Wichtig ist hingegen, der ID des Grid einen Namen zu geben, den man sich merken kann. Dieser Name wird dann gleich im C#-Code benötigt. Im Beispiel hat das Grid den Namen fileList.

3 Zeilen Code: Daten ans Grid binden

Mit dem so definierten Grid sind Sie schon fast fertig. Alles was nun noch fehlt, ist ein wenig Code, der eine Dateiliste ermittelt und die Objekte darin an das Grid bindet. Das tun Sie am besten im Event Load des Form. Um einen Handler für dieses Event zu erzeugen, machen Sie einen Doppelklick auf das Form. Das Visual Studio legt dann eine passende C#-Datei an, in der auch schon der leere Event-Handler eingetragen ist. Dort findet sich zudem eine Instanz vom Typ DataGrid mit dem Namen fileList. Das ist genau das DataGrid-Objekt, das Sie zuvor im HTML-Editor auf das Form gezogen haben.

Der Handler selbst ist nicht sonderlich aufwendig:

private void Page_Load(object sender, System.EventArgs e)
{
DirectoryInfo dirInfo = new DirectoryInfo( Server.MapPath(""));
fileList.DataSource = dirInfo.GetFiles("*.*");
fileList.DataBind();
}

Zunächst wird ein neues DirectoryInfo-Objekt erzeugt. Damit das wie im Beispielcode gelingt, müssen Sie oben im Code ein Statement der Form using System.IO einfügen. Andernfalls müssten Sie das DirectoryInfo-Objekt mit seinem kompletten Namen erzeugen.

Im Beispiel wird einfach ein DirectoryInfo-Objekt für das Root-Verzeichnis des aktuellen virtuellen Verzeichnisses ermittelt. Dieses Objekt wird dann nach allen Dateien gefragt. Statt dem Wildcard *.* können Sie natürlich auch beliebige andere Angaben verwenden.

Die Ermittlung dieser Dateiliste erfolgt mit GetFiles() - und das Resultat wird dann einfach als DataSource an das fileList-Objekt übergeben. Danach müssen Sie das Grid nur noch dazu bewegen, die Daten auszuwerten. Das erfolgt mit dem abschließenden Aufruf vonfileList.DataBind().

In diesem Beitrag haben Sie erfahren, wie einfach es ist, die Server Controls von .NET einzusetzen, um auch Webseiten mit komplexen Controls zu gestalten, ohne auf die Client-Browser Rücksicht nehmen zu müssen. Das TreeControl und das DataGrid-Control sind dabei sicherlich die aufwendigeren der Server-seitigen Controls - doch auch die anderen angebotenen Elemente haben eine Menge zu bieten. Es lohnt sich auf jeden Fall, ASP.NET mit einem etwas ausführlicheren Blick zu untersuchen. (mha)