Internetprogrammierung
Die Reihe Go To Die zuverlässigen Führer auf dem Weg zum Profi Folgende Titel sind bereits erschienen: Stefan Wille Go To Java Server Pages 600 Seiten, ISBN 3-8273-1892-0 Mechtild Käufer Go To JavaScript 624 Seiten, ISBN 3-8273-1916-1 Armin Hanisch Go To C# 534 Seiten, ISBN 3-8273-1932-3 Drews, Kaddik, Schwab Go To Visual Basic 6.0 768 Seiten, ISBN 3-8273-1376-7 Andreas Bohne, Guido Lang Go To Delphi 6 960 Seiten, ISBN 3-8273-1774-6 André Willms Go To C++-Programmierung 768 Seiten, ISBN 3-8273-1495-X Herold, Klar, Klar Go To Objektorientierung 744 Seiten, ISBN 3-8273-1651-0 Daryl Harms, Kenneth McDonald Go To Python 640 Seiten, ISBN 3-8273-1800-9 Peter Loos Go to COM 672 Seiten, ISBN 3-8273-1678-2 Guido Krüger Go To Java 2, 2. Auflage 1.224 Seiten, ISBN 3-8273-1710-X Dirk Abels Go To C++ Builder 5.0 432 Seiten, ISBN 3-8273-1713-4 Michael Hernandez, John Viescas Go To SQL 512 Seiten, ISBN 3-8273-1772-X
Jürgen Bayer
Internetprogrammierung eBook Die nicht autorisierte Weitergabe dieses eBooks an Dritte ist eine Verletzung des Urheberrechts!
An imprint of Pearson Education München • Boston • San Francisco • Harlow, England Don Mills, Ontario • Sydney • Mexico City Madrid • Amsterdam
Die Deutsche Bibliothek – CIP-Einheitsaufnahme Ein Titeldatensatz für diese Publikation ist bei Der Deutschen Bibliothek erhältlich.
Die Informationen in diesem Buch werden ohne Rücksicht auf einen eventuellen Patentschutz veröffentlicht. Warennamen werden ohne Gewährleistung der freien Verwendbarkeit benutzt. Bei der Zusammenstellung von Texten und Abbildungen wurde mit größter Sorgfalt vorgegangen. Trotzdem können Fehler nicht vollständig ausgeschlossen werden. Verlag, Herausgeber und Autoren können für fehlerhafte Angaben und deren Folgen weder eine juristische Verantwortung noch irgendeine Haftung übernehmen. Für Verbesserungsvorschläge und Hinweise auf Fehler sind Verlag und Herausgeber dankbar. Alle Rechte vorbehalten, auch die der fotomechanischen Wiedergabe und der Speicherung in elektronischen Medien. Die gewerbliche Nutzung der in diesem Produkt gezeigten Modelle und Arbeiten ist nicht zulässig. Fast alle Hardware- und Softwarebezeichnungen, die in diesem Buch erwähnt werden, sind gleichzeitig auch eingetragene Warenzeichen oder sollten als solche betrachtet werden. Umwelthinweis: Dieses Buch wurde auf chlorfrei gebleichtem Papier gedruckt. Die Einschrumpffolie – zum Schutz vor Verschmutzung – ist aus umweltverträglichem und recyclingfähigem PE-Material.
10 9 8 7 6 5 4 3 2 1 05 04 03 02 ISBN 3-8273-1825-4 © 2002 by Addison Wesley Verlag, ein Imprint der Pearson Education Deutschland GmbH Martin-Kollar-Straße 10–12, D-81829 München/Germany Alle Rechte vorbehalten Einbandgestaltung: Barbara Thoben, Köln Lektorat: Frank Eller,
[email protected] Korrektorat: Simone Meißner, Großberghofen Herstellung: Monika Weiher,
[email protected] CD-Mastering: Gregor Kopietz,
[email protected] Satz: reemers publishing services gmbh, Krefeld, www.reemers.de Druck und Verarbeitung: Kösel, Kempten, www.KoeselBuch.de Printed in Germany
Inhaltsverzeichnis Vorwort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 1
Einführung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 1.1
1.2
1.3 1.4 1.5 2
Zum Buch 1.1.1 Die in diesem Buch behandelte Internetprogrammierung 1.1.2 Die Zielgruppe dieses Buchs / Voraussetzungen 1.1.3 Die Referenz 1.1.4 Die Buch-CD 1.1.5 Ein Wort an die weiblichen Leser 1.1.6 Typografische Konventionen 1.1.7 Die Icons in diesem Buch Internetprogrammierung in der Übersicht 1.2.1 Was unterscheidet Internetprogramme von klassischen Anwendungen? 1.2.2 Clientseitige versus serverseitige Internetprogrammierung HTML, DHTML, CSS, XHTML und XML Was ist ASP, was ist ASP.NET? Was sind Webdienste?
21 21 23 25 25 27 27 29 29 29 33 36 43 48
Basiswissen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 2.1 2.2
2.3
2.4 2.5 2.6 2.7
2.8
RFCs Die IP-Adresse 2.2.1 Die Klasseneinteilung der IP-Adressen 2.2.2 Subnetze und Subnetzmasken 2.2.3 Vergabe von IP-Adressen IP-Namensauflösung 2.3.1 Der Name-Service in lokalen Netzen 2.3.2 Der Domain Name Service (DNS) IP-Ports URIs, URLs und URNs Internet-Medientypen und MIME Einfache Internetprotokolle 2.7.1 Das IP-Protokoll 2.7.2 Das UDP-Protokoll 2.7.3 Das TCP-Protokoll Socket-Dienste
49 50 50 52 53 53 54 55 57 58 59 62 62 64 65 67
5
2.9
3
3.4
»IIS« oder »Internet-Informationsdienste«? Die Komponenten des IIS IIS-Administration für Programmierer 3.3.1 Das Basisverzeichnis 3.3.2 Eigenschaften einer Website 3.3.3 Eigenschaften der Webordner 3.3.4 Webanwendungen IIS-Sicherheit 3.4.1 Verzeichnissicherheit 3.4.2 Schutz vor externen Angreifern
83 83 87 91 92 96 97 101 101 110
JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113 4.1
4.2
4.3
6
67 68 70 78 79
Der Internet Information Server . . . . . . . . . . . . . . . . . . . . . 83 3.1 3.2 3.3
4
Die wichtigsten höheren Protokolle 2.9.1 Tools zum Testen der Kommunikation 2.9.2 Das HTTP-Protokoll 2.9.3 Das FTP-Protokoll 2.9.4 Das SMTP-Protokoll
Einführung 4.1.1 Ein kleiner Exkurs in die OOP 4.1.2 Die JavaScript-Versionen 4.1.3 Inoffizielle Referenzen zu JavaScript und ein Tipp 4.1.4 JavaScript-Programme in HTMLDokumenten 4.1.5 JavaScript-Version abfragen 4.1.6 Das Prinzip der Ereignisse Debugging 4.2.1 Debugging im Browser 4.2.2 Debuggen mit den speziellen Debuggern Grundlagen der Sprache 4.3.1 Groß- und Kleinschreibung 4.3.2 Anweisungen 4.3.3 Variablen und Datentypen 4.3.4 Alles ist ein Objekt 4.3.5 Der Wert null 4.3.6 Der this-Zeiger 4.3.7 Die with-Anweisung 4.3.8 Zuweisen von Objekten 4.3.9 Der Basistyp object 4.3.10 Die Eigenschaften und Methoden der Klasse Number
113 113 119 122 123 128 130 133 133 135 148 148 149 154 158 160 161 162 164 166 167
4.4 4.5 4.6
4.7
4.8 4.9
4.10 5
4.3.11 Die Eigenschaften und Methoden der Klasse String 167 4.3.12 Arbeiten mit Strings 170 4.3.13 Datumswerte 174 4.3.14 Arrays 179 4.3.15 Ausdrücke und Operatoren 185 4.3.16 Verzweigungen und Schleifen 193 4.3.17 Funktionen schreiben 200 Die allgemeinen JavaScript-Funktionen 202 Die Math-Klasse 204 Reguläre Ausdrücke 206 4.6.1 Das Prinzip 206 4.6.2 Die Musterzeichen 207 4.6.3 Reguläre Ausdrücke erzeugen und Strings testen 212 4.6.4 Fundstellen extrahieren 214 4.6.5 In mehrzeiligen Strings suchen 216 4.6.6 Gruppierungen auswerten 217 4.6.7 Rückreferenzen 218 4.6.8 Ersetzen von Teilstrings 220 4.6.9 Strings auftrennen 220 4.6.10 Einige Beispiele 221 Ereignisse 222 4.7.1 Die HTML-Ereignisse 222 4.7.2 Ereignisse mit Scriptcode verknüpfen 225 4.7.3 Ereignisargumente auswerten 226 4.7.4 Ereignisse abbrechen 232 4.7.5 Ereignisse registrieren 234 Die Behandlung von Ausnahmen 236 OOP mit JavaScript 241 4.9.1 Zugriff auf die Eigenschaften eines Objekts über einen String-Index und Iteration über alle Eigenschaften eines Objekts 243 4.9.2 Dynamisches Ändern von Objekten und Klassen 245 Übungen 246
DHTML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247 5.1 5.2
Einführung Arbeiten mit dem DOM 5.2.1 Grundlegendes 5.2.2 Sechs wichtige Hinweise 5.2.3 DHTML im Internet Explorer 4 bis 5
247 252 252 268 272
7
5.2.4 5.2.5
5.3
5.4 6
6.2 6.3 6.4 6.5
6.6
6.7
6.8
282 285 290 290 291 301 303 305 316 324
Wichtige Hinweise zuvor 6.1.1 Wo Sie weitere Informationen finden 6.1.2 Die Einrichtung des Webordners 6.1.3 Caching-Probleme vermeiden Die alte Welt: COM Die neue Welt: .NET Das .NET Framework Der prinzipielle Aufbau einer ASP.NET-Seite 6.5.1 Einfache ASP.NET-Programme mit C# 6.5.2 Einfache ASP.NET-Programme mit Visual Basic .NET 6.5.3 Die Ausgabe eines ASP.NET-Programms 6.5.4 Einbinden von einfachen ASP.NETProgrammen Die Ausführung von ASP.NET-Seiten im IIS 6.6.1 Die automatische Kompilation 6.6.2 Der ASP.NET-Worker-Prozess und HttpHandler Debugging und Tracing 6.7.1 Debuggen von ASP.NET-Seiten 6.7.2 Tracing unter ASP.NET Übungen
325 325 328 329 330 334 336 346 346 348 348 349 352 352 354 356 357 363 367
Visual Studio .NET . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 369 7.1 7.2 7.3
8
274
ASP.NET-Grundlagen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325 6.1
7
Netscape 4 Internet Explorer ab Version 5.5 und Netscape 6 5.2.6 Cross-Browser-Coding DHTML-Programmiertechniken 5.3.1 Bilder dynamisch laden 5.3.2 Hover-Effekte und einfache Menüs 5.3.3 Hover-Effekte mit Bildern 5.3.4 Bilder vorladen 5.3.5 HTML-Elemente dynamisch erzeugen 5.3.6 Eingabevalidierung Übungen
Grundlegendes Der Start von Visual Studio Umgang mit den Fenstern der Entwicklungsumgebung 7.3.1 Der Projektmappen-Explorer 7.3.2 Die Klassenansicht
369 372 376 377 379
7.4
7.5
7.6 7.7 8
7.3.3 Der Quellcode-/Designer-Bereich 7.3.4 IntelliSense 7.3.5 Die Toolbox 7.3.6 Das Eigenschaftenfenster Die wichtigen Eigenschaften der Entwicklungsumgebung und des Projekts 7.4.1 Optionen der Entwicklungsumgebung 7.4.2 Projekteigenschaften ASP.NET-Anwendungen mit Visual Studio .NET entwickeln 7.5.1 Umbenennen des Startformulars 7.5.2 Die Gestaltung der Oberfläche 7.5.3 Ereignisse auswerten Weitere Features von Visual Studio Übungen
379 381 382 383 387 387 388 390 390 390 392 394 396
ASP.NET-Entwicklung mit C# . . . . . . . . . . . . . . . . . . . . . . . 397 8.1 8.2 8.3
8.4
8.5
8.6
Warum C#? Umgang mit Assemblierungen und Namensräumen Anweisungen 8.3.1 Kommentare und XML-Dokumentation 8.3.2 Der Aufruf von Methoden (Funktionen) Datentypen 8.4.1 Typsicherheit, der Datentyp object, Wert- und Referenztypen 8.4.2 Übersicht über die Standarddatentypen 8.4.3 Integerdatentypen 8.4.4 Fließkommatypen 8.4.5 Über- und Unterläufe und spezielle Werte 8.4.6 Datumswerte 8.4.7 Zeichen und Zeichenketten 8.4.8 Der Typ object 8.4.9 Konvertierungen 8.4.10 Aufzählungen (Enums) Variablen, Konstanten und Arrays 8.5.1 Variablen 8.5.2 Konstanten 8.5.3 Seitenglobale Variablen 8.5.4 Programmglobale Variablen 8.5.5 Arrays 8.5.6 Namensrichtlinien Ausdrücke und Operatoren 8.6.1 Arithmetische Ausdrücke und Operatoren
397 398 404 406 408 410 410 418 420 422 425 428 429 432 433 437 439 439 441 442 443 445 451 453 453
9
8.7
8.8
8.9
8.10
8.11
8.12
10
8.6.2 Bitoperationen 8.6.3 Zuweisungen 8.6.4 Vergleiche 8.6.5 Logische Operatoren 8.6.6 ?:, typeof und is Verzweigungen und Schleifen 8.7.1 Die if-Verzweigung 8.7.2 Die switch-Verzweigung 8.7.3 Die while-Schleife 8.7.4 Die do-Schleife 8.7.5 Die for-Schleife Methoden 8.8.1 Deklaration 8.8.2 Überladene Methoden 8.8.3 ref- und out-Argumente 8.8.4 Übergeben von Referenztypen 8.8.5 Variable Argumente mit params Klassen 8.9.1 Klasse oder Struktur 8.9.2 Die Deklaration und das Einbinden einer Klasse 8.9.3 Das Schlüsselwort this 8.9.4 Konstruktoren 8.9.5 Destruktoren 8.9.6 Statische Klassenelemente 8.9.7 Vererbung Programmiertechniken 8.10.1 Strings bearbeiten 8.10.2 Die StringBuilder-Klasse 8.10.3 Berücksichtigung der Kultur 8.10.4 Formatierungen 8.10.5 Datumswerte bearbeiten 8.10.6 Mathematische Berechnungen 8.10.7 Massendaten in Auflistungen speichern Reguläre Ausdrücke 8.11.1 Auswerten von regulären Ausdrücken mit C# 8.11.2 Mehrere Teilstrings extrahieren 8.11.3 Die Optionen: Groß-/Kleinschreibung ignorieren 8.11.4 Suchen in mehreren Zeilen Ausnahmebehandlung 8.12.1 Die globale ASP.NETAusnahmebehandlung
454 456 457 459 460 463 464 465 467 468 468 469 470 470 472 473 473 474 475 475 478 479 481 481 483 485 485 495 500 502 505 508 509 515 516 517 517 518 521 522
8.13
8.14 9
523 528 528 530 531
Klassisches ASP in ASP.NET . . . . . . . . . . . . . . . . . . . . . . . . 533 9.1 9.2 9.3 9.4 9.5
9.6
9.7 9.8 10
8.12.2 Ausnahmebehandlung im Programm Klassenbibliotheken (Assemblierungen) 8.13.1 Klassenbibliotheken erzeugen und kompilieren 8.13.2 Klassenbibliothek mit Visual Studio erzeugen Übungen
Basiswissen Dynamische ASP.NET-Seiten URL-Argumente URL-Argumente auswerten Formulareingaben auswerten 9.5.1 Einlesen und Überprüfen der Eingaben 9.5.2 Auswerten der verschiedenen Steuerelemente 9.5.3 Seitenstatus in hiddenElementen speichern Serverseitiges Überprüfen von Eingaben 9.6.1 Fehlermeldung auf der Auswertungsseite anzeigen 9.6.2 Fehlermeldung auf einer separaten Seite anzeigen 9.6.3 Fehlermeldung auf der Eingabeseite anzeigen Ein Formular an verschiedene ASP-Seiten senden Übungen
533 537 542 543 546 546 549 557 561 562 564 566 568 569
Arbeiten mit ASP.NET-Seiten . . . . . . . . . . . . . . . . . . . . . . . 571 10.1
10.2
10.3 10.4
Grundlagen 10.1.1 Client- und serverseitige Steuerelemente 10.1.2 Das Rendern 10.1.3 Die Ereignisbehandlung und der Viewstate Umgang mit Steuerelementen 10.2.1 HTML- und ASP-Steuerelemente 10.2.2 Einstellung der Eigenschaften der Steuerelemente 588 Cross-Browser-Seiten und das Ziel-Schema Ereignisse auswerten 10.4.1 Die Struktur einer Ereignisbehandlungsmethode
571 571 574 576 582 582
591 592 592
11
10.5
10.6
10.7
10.8 10.9 10.10 10.11 10.12 10.13 10.14 11
594 601 604 606 608 611 611 614 621 621 622 623 623 626 629 633 641 645 648 656 662 668 671 672 673 677 682
Steuerelemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 683 11.1
11.2
12
10.4.2 Ereignisbehandlungsmethoden in einfachen aspx-Seiten 10.4.3 Codebehind-Klassen 10.4.4 Die Ereignisse der Steuerelemente und der Seite 10.4.5 Die IsPostBack-Eigenschaft 10.4.6 Direkte Auswertung von Ereignissen über AutoPostBack Die Page-Klasse 10.5.1 Eigenschaften und Methoden 10.5.2 Wichtige Seiten-Direktiven Anwendungen und Sitzungen 10.6.1 Anwendungen 10.6.2 Sitzungen Die ASP-Objekte 10.7.1 Grundlagen zu den Auflistungen 10.7.2 Das Application-Objekt 10.7.3 Das Session-Objekt 10.7.4 Arbeiten mit Sitzungen 10.7.5 Application- und Session-Ereignisse auswerten: Die Datei Global.asax 10.7.6 Das Server-Objekt 10.7.7 Das Request-Objekt 10.7.8 Das Response-Objekt Konfiguration einer ASP.NET-Anwendung Andere ASP.NET-Seiten öffnen Smarte Navigation ASP-Seiten umleiten (Redirect) Cookies Caching Übungen
Die HTML-Steuerelemente 11.1.1 Die Basisklassen HtmlControl und HtmlContainerControl 684 11.1.2 Übersicht über die Steuerelemente Die ASP-Steuerelemente 11.2.1 Eigenschaftswerte in HTML und im Programm 689 11.2.2 Das alte Browserproblem im neuen .NET 11.2.3 CSS verwenden 11.2.4 Die Basisklasse WebControl 11.2.5 Das Label-Steuerelement 11.2.6 Die TextBox 11.2.7 Der Button
683
686 688
694 696 699 701 702 703
11.3 11.4
11.5 12
11.2.8 Der LinkButton 11.2.9 Der ImageButton 11.2.10 Das HyperLink-Steuerelement 11.2.11 CheckBox und RadioButton 11.2.12 Das Image-Steuerelement 11.2.13 Panel 11.2.14 Das Calendar-Steuerelement 11.2.15 Das AdRotator-Steuerelement 11.2.16 Das Table-Steuerelement 11.2.17 Listen-Steuerelemente 11.2.18 Spezielle Steuerelemente 11.2.19 Die Validierungs-Steuerelemente Dynamisches Erzeugen von Steuerelementen Benutzerdefinierte Steuerelemente 11.4.1 Die wichtigen Basismethoden 11.4.2 Ein einfaches Steuerelement in einer cs-Datei 11.4.3 Entwicklung mit Visual Studio 11.4.4 Ein einfaches Steuerelement in einer ascx-Datei 11.4.5 Pagelets: Zusammengesetzte Steuerelemente in ascx-Dateien 11.4.6 Zusammengesetzte Steuerelemente in Assemblierungen 11.4.7 Standardeigenschaften implementieren 11.4.8 Erweiterte Techniken Übungen
706 709 710 711 714 714 716 723 725 731 737 738 749 752 755 756 759 764 769 773 777 784 799
ASP.NET-Datenzugriff . . . . . . . . . . . . . . . . . . . . . . . . . . . . 801 12.1
12.2 12.3
Einige Infos zuvor 12.1.1 ADO, ADO.NET, OLE DB, ODBC, MDAC und SQL 12.1.2 Die Datenquellen 12.1.3 Der Provider 12.1.4 Der Wert DBNull Übersicht über die Klassen Verbindung zur Datenquelle aufnehmen 12.3.1 Wo verbinden? 12.3.2 Verbindung mit Hilfe von Dialogen konfigurieren 12.3.3 Verbindung zum SQL Server und zur MSDE 12.3.4 Verbindung zu Datenquellen, für die nur ein ADO-Provider verfügbar ist
801 803 805 806 806 807 809 810 811 814 817
13
12.3.5 Verbindung zu Access-Datenbanken 12.3.6 Verbindung zu Oracle-Datenbanken 12.4 Die Befehlsklassen 12.5 Daten über DataReader-Objekte lesen 12.5.1 Das Prinzip 12.5.2 Die wichtigsten Eigenschaften und Methoden 12.5.3 Daten lesen 12.6 Daten über ein Command-Objekt editieren, anfügen und löschen 12.6.1 Daten editieren, anfügen und löschen 12.7 Ausführen von gespeicherten Prozeduren mit einem Command-Objekt 12.8 Das Prinzip des DataSet 12.8.1 XML-Daten im Arbeitsspeicher 12.8.2 Eigenschaften, Methoden und Ereignisse der DataSet-Klasse 12.8.3 Die DataTable-Klasse 12.8.4 DataRow-Objekte 12.8.5 DataColumn-Objekte 12.8.6 Das Grundprinzip am Beispiel eines dynamisch erzeugen DataSet 12.8.7 Daten in XML-Dateien verwalten 12.8.8 Das Beispiel im SQL Server 12.9 DataAdapter 12.9.1 Das Prinzip 12.9.2 Eigenschaften und Methoden der DataAdapter-Klassen 12.9.3 DataAdapter und dessen Befehle erzeugen 12.9.4 Füllen des DataSet 12.10 Mehrere Tabellen im DataSet 12.11 Daten im Programm mit dem DataSet durchgehen 12.12 Daten im Programm mit dem DataSet editieren 12.12.1 Das Prinzip 12.12.2 Datensätze hinzufügen 12.12.3 Der Trick zum Lesen von automatisch erzeugten Id-Werten 12.12.4 Datensätze ändern 12.12.5 Datensätze löschen 12.12.6 Aktualisieren der Datenquelle und Fehlerauswertung
14
819 820 821 824 824 825 826 829 829 830 834 834 835 839 844 847 849 853 857 861 861 862 866 870 871 873 874 874 877 878 881 881 881
12.13 Transaktionen 12.14 Ansätze zum Editieren mit dem DataSet in der Praxis 12.15 Sortieren und filtern mit einem DataReader-Objekt 12.16 Das DataView-Objekt 12.17 Übungen 13
894 895 896 902
ASP.NET-Datenbindung . . . . . . . . . . . . . . . . . . . . . . . . . . . 903 13.1
13.2
13.3
13.4 13.5 14
887
Einfachwert- und Listen-Datenbindung 903 13.1.1 Einfachwert-Datenbindung 904 13.1.2 Listen-Datenbindung 907 13.1.3 Stileigenschaften 918 Das DataGrid-Steuerelement 923 13.2.1 Die Eigenschaften und Ereignisse des DataGrid-Steuerelements 925 13.2.2 Seitenweise Anzeige der Daten 929 13.2.3 Sortieren des Inhalts 939 13.2.4 Benutzerdefinierte Spalten: Angepasste Darstellung und Editieren der Daten 942 13.2.5 Benutzerdefinierte normale gebundene Spalten 944 13.2.6 Benutzerdefinierte Spalten mit Schalter 946 13.2.7 Benutzerdefinierte Spalten mit Editier-Befehlen 951 13.2.8 Benutzerdefinierte Spalten mit Hyperlinks 958 13.2.9 Templates: Spalten mit beliebigen Steuerelementen 960 13.2.10 Auswahl von Zeilen zulassen 967 13.2.11 Anpassung des DataGrid in den Datenbindungs-Ereignissen 970 13.2.12 Daten mit dem DataGridSteuerelement manipulieren 978 Das DataList-Steuerelement 994 13.3.1 Eigenschaften und Ereignisse des DataList-Steuerelements 995 13.3.2 Ein Beispiel ohne Editieren 999 13.3.3 Ein Ansatz zum Editieren 1004 Das Repeater-Steuerelement 1004 Übungen 1006
ASP.NET-Programmiertechniken . . . . . . . . . . . . . . . . . . . 1007 14.1
Suchen mit dem Indexdienst
1007
15
14.2
14.3
14.4
14.5 14.6 15
1008 1013 1017 1024 1030 1030 1034 1035 1036 1038 1039 1041 1041 1043 1043 1048 1051 1051 1055 1059 1065 1068
Webdienste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1069 15.1 15.2 15.3 15.4
15.5
15.6
16
14.1.1 Katalog erstellen 14.1.2 Die allgemeine Syntax der Suchabfrage 14.1.3 Suchen mit dem ADO-Provider für den Indexdienst 14.1.4 Eine Suchseite Dateien zum Server hochladen 14.2.1 Dateien hochladen und speichern 14.2.2 Maximale Größe festlegen E-Mails versenden 14.3.1 Einfache Mails versenden 14.3.2 Dateien anhängen 14.3.3 Eigenschaften und Methoden der SmtpMail- und der MailMessage-Klasse Benutzer-Authentifizierung und Autorisation 14.4.1 Die Authentifizierungsmodi 14.4.2 Einstellen des Authentifizierungsmodus 14.4.3 Formular-Authentifizierung 14.4.4 Einrichten der Loginseite 14.4.5 Ansätze zur Verwaltung der Benutzer in einer externen Datenquelle 14.4.6 Windows-Authentifizierung 14.4.7 Personalisieren von Webseiten 14.4.8 Rollenbasierte Verwaltung der Rechte und eigene Benutzerdaten Berücksichtigung der Kultur des Anwenders Übungen
Was sind Webdienste? Ein einfacher Webdienst als Demo Implementierung des Webdienstes Direkter Aufruf eines Webdienstes 15.4.1 Aufruf über HTTP-GET 15.4.2 Aufruf über HTTP-POST 15.4.3 Aufruf über SOAP 15.4.4 Testen im Browser SOAP, WSDL, UDDI und DISCO 15.5.1 SOAP 15.5.2 WSDL 15.5.3 UDDI 15.5.4 DISCO 15.5.5 Dynamic Discovery Webdienste verwenden 15.6.1 Einfacher Aufruf eines Webdienstes
1069 1070 1070 1072 1072 1073 1074 1075 1076 1076 1081 1082 1085 1086 1088 1088
15.6.2 Eine Proxy-Klasse erzeugen 15.6.3 Webdienste unter Visual Studio .NET verwenden 15.6.4 Externe Webdienste über einen UDDIServer integrieren 15.6.5 Clientanwendungen initialisieren 15.7 Webdienste mit Assemblierung 15.7.1 Grundlagen 15.7.2 Implementierung mit Assemblierung 15.8 Webdienste mit Visual Studio .NET entwickeln 15.9 Abgesicherte Webdienste 15.9.1 Eine eigene Authentifizierung 15.10 Übungen 16
1088 1093 1094 1095 1096 1096 1097 1100 1104 1106 1109
Peer-To-Peer- und Mehrschichten-Programme . . . . . . . 1111 16.1
16.2
16.3
16.4
Was sind P2P- und MehrschichtenAnwendungen? 16.1.1 Peer-To-Peer 16.1.2 Mehrschichten-Anwendungen 16.1.3 Und wie passt das Ganze zum Buch? Kommunikation über das IP-Protokoll 16.2.1 Das Prinzip 16.2.2 Übersicht über die Klassen 16.2.3 Programmierung eines TCP-Client 16.2.4 Programmierung eines TCP-Servers Remoting 16.3.1 Was ist Remoting? 16.3.2 Wozu Remoting? 16.3.3 Das Prinzip 16.3.4 Die Möglichkeiten 16.3.5 Leasing 16.3.6 Kanäle 16.3.7 Grundlagen der Remoting Programmierung am Beispiel mit serverseitiger Aktivierung 16.3.8 Clientseitige Aktivierung 16.3.9 Remote-Konfiguration über eine Konfigurationsdatei 16.3.10 Einstellen der Lease-Zeit 16.3.11 Remoting mit Ereignissen 16.3.12 Asynchroner Aufruf von Methoden Übungen
1111 1111 1112 1113 1114 1114 1115 1128 1135 1146 1146 1146 1147 1148 1151 1152
1153 1162 1168 1174 1179 1189 1200
17
17
Lösungen zu den Übungen . . . . . . . . . . . . . . . . . . . . . . . 1201 17.1 17.2 17.3 17.4 17.5 17.6 17.7 17.8 17.9 17.10 17.11 17.12 17.13
Kapitel 4 Kapitel 5 Kapitel 6 Kapitel 7 Kapitel 8 Kapitel 9 Kapitel 10 Kapitel 11 Kapitel 12 Kapitel 13 Kapitel 14 Kapitel 15 Kapitel 16
1201 1203 1206 1207 1208 1210 1214 1218 1221 1224 1227 1233 1234
Stichwortverzeichnis . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1241
18
Vorwort
Vorwort Internetprogrammierung macht Spaß. Wenn man es kann. Wer in die Internetprogrammierung einsteigt, muss nur ein paar »kleine« Hürden überwinden: JavaScript, DHTML, ASP, Datenbankzugriffe und noch einiges anderes mehr. Aber auch der Profi kann nicht immer alle Features der gerade verwendeten Technologie im Kopf haben. Dieses Buch stellt eine Hilfe für jeden Programmierer dar: für den, der bereits Programmier-Grundkenntnisse besitzt und nun Internetprogramme entwickeln will. Und auch für den, der schon unter anderen Umgebungen Internetprogramme entwickelt und jetzt zur Microsoft-Welt umsteigen will. Schließlich wird das Buch auch dem Profi helfen, der immer mal wieder nach einem Feature suchen oder die Anwendung dieses Feature nachlesen muss. Und zu guter Letzt wird das Buch auch mir selbst immer wieder bei der Programmierung Hilfestellung leisten (womit sich das Schreiben schon gelohnt hat). Mein Hauptziel beim Schreiben dieses Buchs war, dass es bei Ihnen immer in Reichweite auf dem Schreibtisch liegt, während Sie Internetprogramme entwickeln. Ich habe allerdings selbst dafür gesorgt, dass dies vielleicht gar nicht so häufig vorkommen wird. Auf der Buch-CD finden Sie nämlich eine Referenz in PDF- und indizierter HTMLForm, die alle Referenz-Tabellen des Buchs enthält und die bei der alltäglichen Recherche wahrscheinlich ein guter Helfer sein wird .
Bei der Themenauswahl dieses Buchs hatte ich ursprünglich geplant, die Installation der benötigten Komponenten, wichtige Internetgrundlagen, HTML und CSS im ersten Teil zu beschreiben. Bei der Umsetzung merkte ich dann aber viel weiter hinten, bei der Beschreibung der wirklich wichtigen und vor allen Dingen auch interessanten und Spaß machenden Themen, dass mir die Seiten ausgehen. Also habe ich das Konzept so geändert, dass die genannten Themen nicht im Buch behandelt werden (schließlich handelt es sich ja um ein ProgrammierBuch). Sie finden die herausgenommenen Kapitel und Abschnitte stattdessen auf der Buch-CD. Ich habe das Buch bewusst so geschrieben, wie ich mir ein Buch vorstelle, das die wichtigen Aspekte der Internetprogrammierung behandelt. Einige Kapitel, wie beispielsweise das über JavaScript, werden Ihnen mög19
Vorwort
licherweise etwas zu umfangreich erscheinen. Ich habe in den einzelnen Kapiteln aber immer versucht, möglichst genau die Informationen unterzubringen, die beim Programmieren im Internet immer wieder benötigt werden. Auch wenn Sie im Moment z.B. noch nicht wissen wollen, wie Sie beispielsweise reguläre Ausdrücke in JavaScript einsetzen um Eingaben zu überprüfen: Irgendwann einmal müssen Sie damit vielleicht umgehen können. Schön, wenn Sie dann in diesem Buch nachlesen können. Ich danke allen, die bei der Entstehung dieses Buchs mitgewirkt haben. Dazu zählen im Besonderen meine Lektoren Christiane Auf und Frank Eller, die mit ihrer verständnisvollen und netten Betreuung so einige Schwierigkeiten aus dem Weg geräumt haben. Ein großes Danke gilt auch Simone Meißner für die enorm flexible, nette und vor allen Dingen auch Spaß machende Zusammenarbeit bei der Korrektur des Manuskripts. Herrn Ullrich Voss von der Firma future apps danke ich für die Begutachtung des Internetgrundlagen-Abschnitts. Sorry, Ullrich, dass dieser Abschnitt nun nur noch auf der Buch-CD erscheint. Mein besonderer Dank gilt auch Georg (der schon weiß, wer damit gemeint ist) für die wertvollen Hinweise und Anregungen zu den regulären Ausdrücken. Meinem guten Geschäftsfreund Alistair Firth danke ich für die konstruktive Mitwirkung bei der Auswahl der für das Buch wichtigen Themen. Er ist dafür mitverantwortlich, dass Sie einige Themen nun nicht mehr im Buch, sondern auf der Buch-CD finden . Schließlich danke ich meiner guten Freundin Gabi Linsel für die hervorragende, sozialwissenschaftlich fundierte Beratung in den »Das schaff ich doch nie«-Phasen der Erstellung dieses Buchs.
Ich wünsche allen Lesern, dass sie mit Hilfe dieses Buchs und über ihre eigenen Experimente sichere und gute Internetprogrammierer werden und dass dieses Buch auch in Zukunft ein unentbehrlicher Helfer bei der Programmierung sein wird. Falls Sie Anregungen oder Kritik zum Buch haben oder mir einen der kleinen, vom bösen Fehlerteufel eingebauten Fehler mitteilen wollen, schreiben Sie mir einfach eine E-Mail. Viel Spaß bei der Internetprogrammierung (und auch sonst) Jürgen Bayer
[email protected] 20
Einführung
1
Einführung
1.1 Zum Buch 1.1.1 Die in diesem Buch behandelte Internetprogrammierung Die Programmierung von Internetanwendungen ist ein weites Feld. Neben fundierten HTML-Kenntnissen benötigt ein Programmierer Kenntnisse der verschiedensten Technologien. Das Buch behandelt nicht alle Aspekte der Internetprogrammierung, weil das prinzipiell im Rahmen eines einzigen Buchs gar nicht möglich ist. Es beschreibt aber die Technologien und Sprachen, die Sie unter der vorwiegenden Verwendung von Microsoft-Servern und -Programmiersprachen einsetzen können, um ansprechende und funktionelle Internetanwendungen zu erstellen. Der Vorteil von Microsoft-Systemen ist aus meiner Sicht, dass diese recht einfach einsetzbar sind und einem relativ guten Standard entsprechen. Der sehr einfach zu administrierende (und vor allen Dingen kostenfreie) Microsoft-Webserver und ASP/ASP.NET zur Erstellung dynamischer Webseiten sind gute Beispiele dafür. Besonders das neue .NET-Konzept vereinfacht die Internetprogrammierung gegenüber anderen Konzepten oder Sprachen erheblich. Dieses Konzept steht auch sehr stark neben Java, dem wichtigsten Konkurrenten bei der Internetprogrammierung. Als wichtige Basis behandelt der erste Teil des Buchs die für Programmierer wichtisten Grundlagen. Dazu gehört eine allgemeine Einführung in die Internetprogrammierung und die Administration des Microsoft-Webservers. Ich beschreibe diese Themen dabei aus der Programmierersicht. (Teil-)Themen, die für Programmierer unwichtig sind, werden nicht angesprochen. Da Sie zwar über ASP oder ASP.NET recht einfach dynamische Webseiten erstellen können, ein ansprechendes HTML-Dokument mit dynamischer Reaktion auf Benutzereingaben meist aber nicht ohne eine clientseitige JavaScript- und DHTML-Programmierung auskommt, behandle ich im ersten Programmierteil des Buchs JavaScript und DHMTL recht ausführlich. Das Buch beschreibt in diesem Teil auch, wie Sie gängige Probleme wie beispielsweise die Überprüfung von Benutzereingaben 21
Einführung
oder die Erstellung eines Menüs lösen. Verschiedene Tipps und Tricks helfen Ihnen dabei, mit den teilweise komplexen Themen umzugehen. Dabei gehe ich immer auch auf die gängigen Browser ein (die leider oft eine unterschiedliche Programmierung erfordern). Die Grundlagenthemen HTML, CSS und XML werden im Buch nicht beschrieben, obwohl sie für Internetprogrammierer sehr wichtig sind. Ich musste die einzelnen möglichen Themen aber gewichten. Da ich davon ausgehen konnte, dass Sie HTML und CSS vielleicht schon kennen (weil Sie beispielsweise bereits Webseiten gestalten), und weil diese Themen keine wirklichen Programmierthemen sind, habe ich zu Gunsten von echten und interessanten Programmierthemen entschieden, HTML, CSS und XML nicht in das Buch zu nehmen. Sie finden auf der Buch-CD aber ausführliche, im Stil des Buchs geschriebene Artikel dazu. Einen großen Teil des Buchs nimmt dann die Programmierung dynamisch erzeugter Webseiten mit ASP.NET ein. Als Programmiersprache wird dazu zunächst C#, die »Muttersprache« des .NET Frameworks, beschrieben. ASP wird im Buch zwar nicht mehr ausführlich behandelt. Da aber ein gesundes ASP-Grundwissen für das Verständnis von ASP.NET recht wichtig ist, beschreibe ich in weiteren Verlauf dieses Buchabschnitts die ASP-Grundtechniken (unter ASP.NET). In diesem Kapitel werden auch die wichtigen ASP.NET-Objekte behandelt. Danach erläutere ich den Umgang mit den ASP.NET-Steuerelementen, die die Erstellung der Oberfläche einer Webanwendung (im Vergleich zum alten ASP) erheblich vereinfachen. Die in der Praxis wichtige Datenbankprogrammierung wird natürlich auch behandelt, genau wie das unter ASP.NET sehr einfache aber flexible Binden von Steuerelementen an Datenquellen. Neben diesen »Grundlagen« werden aber auch spezielle, praxisorientierte Themen, wie das Senden von E-Mails, der Upload von Dateien oder die Verwendung des Microsoft-Indexdienstes (zur Implementierung von Suchseiten) behandelt. Einen separaten Teil des Buchs nimmt dann die Entwicklung von Webdiensten unter .NET ein. Diese Dienste, die im Prinzip nichts anders liefern als Daten, laufen auf einem Webserver und können über das Internet von jedem Programm aus verwendet werden, das das zugrunde liegende Protokoll (SOAP) beherrscht. 22
1.1 Zum Buch
Einführung
Zur Internetprogrammierung gehört nicht nur die Entwicklung von (dynamischen) Webseiten. Der andere (relativ) wichtige Teil ist die Programmierung von klassischen Windowsanwendungen, die über eines der Internetprotokolle mit Servern oder anderen Programmen im Internet kommunizieren. Deshalb zeige ich im letzten Teil des Buchs, wie Sie unter C# Programme entwickeln, die über TCP/IP oder UDP/IP miteinander oder mit beliebigen Servern kommunizieren. Damit besitzen Sie eine gute Grundlage, um die Probleme, die sich Ihnen bei der Internetprogrammierung stellen, zu lösen. 1.1.2 Die Zielgruppe dieses Buchs / Voraussetzungen Dieses Buch ist an alle gerichtet, die bereits etwas Erfahrung in der Programmierung besitzen und nun zur Internetprogrammierung wechseln wollen. Es setzt voraus, dass der Leser die Grundlagen der Programmierung beherrscht. Dazu gehört im Wesentlichen der sichere Umgang mit Variablen, Schleifen, Verzweigungen und Funktionen, aber auch zumindest grundlegende Kenntnis der objektorientierten Programmierung (OOP). Für den Fall, dass Sie diese noch nicht besitzen, finden Sie auf der Buch-CD einen Artikel, der die Aspekte der OOP für verschiedene Programmiersprachen ausführlich beschreibt. Sie müssen aber nicht bereits ein Programmier-Crack sein, um dieses Buch verstehen zu können. Ein gesundes Grundlagenwissen reicht vollkommen aus. Im Bereich der OOP setze ich lediglich voraus, dass Sie wissen, was eine Klasse ist, und dass Sie die wesentlichen Grundlagen der Vererbung kennen. Als wichtige Basis setze ich weiterhin voraus, dass Sie die wichtigsten HTML-Gundlagen kennen. Sie sollten also wissen, was ein Tag ist, welches die wichtigsten Tags sind und wie Sie diese grundsätzlich einsetzen. Für den Fall, dass Sie dieses Wissen noch nicht besitzen, können Sie einfach den entsprechenden Artikel lesen, den Sie auf der Buch-CD finden. Weiteres wichtiges Grundwissen ist das Wissen um die Bedeutung und den Einsatz von CSS (Cascading Style Sheets). Gerade bei der Formatierung der Objekte eines HTML-Dokuments spielt CSS eine tragende Rolle. Und das gilt auch unter ASP.NET. Ich gehe immer wieder auf CSS ein, verzichte im Buch aber auf die Beschreibung der Grundlagen. Sie finden auf der Buch-CD aber (natürlich) einen Artikel, der CSS recht ausführlich behandelt. 23
Einführung
Im Datenbankteil setze ich voraus, dass Sie wissen, was eine Datenbank ist. Außerdem sollten Sie ein wenig mit SQL umgehen können. Auf der Buch-CD finden Sie (einmal wieder ...) einen ausführlichen Artikel zu dieser Datenbankabfrage und -Manipulationssprache. Das Buch kann die einzelnen verwendeten Programmiersprachen natürlich nicht ausführlich behandeln. Dazu gibt es schließlich ganze separate Bücher. Es beschreibt also nur die Grundlagen und die wesentlichen Aspekte. Wenn Sie weitere Informationen zu einer Sprache wünschen, sollten Sie ein passendes Buch parallel lesen. In der GoToReihe finden Sie zu allen hier eingesetzten Sprachen sehr gute Bücher. Als Systemumgebung benötigen Sie einen Rechner, der idealerweise Windows 2000 oder XP ausführt. Für den .NET-Teil des Buchs benötigen Sie möglichst ab 128 MB Arbeitsspeicher. Die CPU sollte natürlich nicht zu langsam sein, so etwa 700 MHz reichen aber aus. Die Festplatte sollte für die .NET-Installation zwischen 500 MB und 2 GB freien Platz besitzen (je nachdem, ob Sie nur das .NET Framework oder auch Visual Studio .NET installieren). Entwicklungsumgebungen benötigen Sie für dieses Buch nicht unbedingt. HTML-, DHTML- und JavaScript-Programme und CSSDateien können Sie in einem einfachen Editor entwickeln. Fehler in Programmen, die in HTML-Dateien eingebunden sind, können Sie mit dem frei erhältlichen Microsoft Script Debugger oder mit dem Netscape-Debugger suchen. ASP.NET-Programme können Sie, genau wie alle anderen .NET-Programme, ebenfalls in einem normalen Editor entwickeln. Wenn Sie allerdings Visual Studio .NET besitzen, sollten Sie diese Entwicklungsumgebung für die Programmierung von .NET-Anwendungen nutzen. Visual Studio bietet eine Vielzahl an hilfreichen Tools und Features. ASP.NET-Programme werden zwar im Gegensatz zu JavaScript-Programmen kompiliert, dieses geschieht allerdings automatisch. Visual Studio .NET benötigen Sie dazu also nicht. Normale .NET-Programme wie Windowsanwendungen oder Klassenbibliotheken müssen explizit kompiliert werden. Wenn Sie Visual Studio .NET nicht besitzen, können Sie
24
1.1 Zum Buch
Einführung
dazu den Kommandozeilencompiler des .NET Frameworks-SDK verwenden. Zum Debuggen von .NET-Programmen steht Ihnen im .NET Framework-SDK ein sehr guter Debugger zur Verfügung. 1.1.3 Die Referenz Das Buch ist gleichzeitig Arbeitsbuch und Referenz. In den meisten Kapiteln finden Sie neben einer grundlegenden Erläuterung eines Features teilweise umfangreiche Tabellen, die oft alle, sonst aber die wichtigsten Elemente beschreiben. Ursprünglich war geplant, zudem im letzten Teil des Buchs eine separate Referenz unterzubringen. Weil ich aber Befürchtungen hatte, dass Sie das Buch dann nicht mehr heben können ( ), habe ich mich entschieden, die Referenz in PDF-Form auf der Buch-CD unterzubringen. Ich denke, das Inhaltsverzeichnis und der Index dieser Referenz hilft Ihnen (wie mir schon jetzt) bei der täglichen Arbeit sehr dabei, herauszufinden, wie das eine oder andere Feature angewendet wird.
1.1.4 Die Buch-CD Auf der dem Buch beiliegenden CD finden Sie alle Beispielprogramme, viele zusätzliche Artikel und spezielle Tools. Im Buch sind einige Beispiele aus Platzgründen nicht vollständig abgedruckt. Die Dateien auf der CD sind identisch mit den Beispielcodes, enthalten aber komplett ausprogrammierte Programme. Die ASP.NET-Beispiele sind so angelegt, dass Sie diese direkt ausprobieren können, ohne Visual Studio .NET dazu verwenden zu müssen. Sie sollten die Beispiele in einen Ordner Ihrer Wahl kopieren und dann im IIS ein virtuelles Verzeichnis anlegen, das mit diesem Ordner verknüpft ist. Im Stammordner der Beispiele finden Sie eine Datei default.htm, über die Sie die einzelnen Beispielprogramme aufrufen können. Da ASP.NET-Programme nur dann ausgeführt werden, wenn die entsprechenden Dateien über den Webserver geöffnet werden, reicht es für diese Beispiele nicht aus, die default.htm-Datei direkt im Browser zu öffnen. Deshalb ist die Installation der Beispiele in einem virtuellen IIS-Ordner notwendig.
25
Einführung
Falls der IIS auf Ihrem System noch nicht installiert ist, lesen Sie im Artikel Installation und Einrichtung des Systems nach, den Sie auf der Buch-CD finden. Dort wird die Installation beschrieben. Öffnen Sie zum Anlegen des virtuellen Webordners dann den InternetdiensteManager über den Eintrag VERWALTUNG in der Systemsteuerung. Über den Befehl NEU im Kontextmenü des Eintrag STANDARDWEBSITE im Internetdienste-Manager können Sie ein neues virtuelles Verzeichnis anlegen. Ab Seite 87 finden Sie weitere Hinweise dazu. In den Artikeln der Buch-CD finden Sie außerdem zusätzliche Informationen zu wichtigen Themen, die vom Konzept her nicht in das Buch passten. Eine wichtige Auswahl dieser Artikel beschreibt die folgende Auflistung:
Installation und Einrichtung des Systems.pdf/.htm: beschreibt die Installation der für das Buch benötigen Komponenten und die grundsätzliche Einrichtung des Entwicklungsrechners. HTML.pdf/.htm: beschreibt die wesentlichen Aspekte von HTML, der Basissprache zur Beschreibung und Formatierung von Internet-Dokumenten. CSS.pdf/.htm: beschreibt die Grundlagen und Anwendung der Cascading Style Sheets zur flexiblen und einfachen Formatierung von HTML-Dokumenten. OOP-Grundlagen.pdf/.htm: behandelt die Grundlagen der objektorientiert Programmierung (auch für C#). SQL.pdf/htm: beschreibt die Grundlagen, die wichtigen Elemente und die Anwendung von SQL/92 (dem aktuellen Standard) und T/SQL (dem SQL des SQL Servers) zur Abfrage und Manipulation von relationalen Datenbanken.
Schauen Sie auf jeden Fall einmal in diese Artikel hinein, wenn Sie Informationen zu diesen Themen suchen. Die Artikel sind teilweise sehr umfangreich. Die Datei Links.htm im Stammordner der Buch-CD enthält Links zu allen im Buch genannten und zu anderen zu den Buch-Themen passenden Webseiten. Über diese Datei finden Sie sehr viele Informationen für den Bereich der Internetprogrammierung. Einzelne Links kön-
26
1.1 Zum Buch
Einführung
nen natürlich mit der Zeit ungültig werden, was besonders für die Microsoft-Dokumente gilt, die unverständlicherweise mit teilweise kryptischen oder eine Version beinhaltenden Namen versehen sind. Sofern ich Zeit finde, werde ich in unregelmäßigen Abständen ( ) eine aktuelle Version dieser Datei auf meiner Website unter der Adresse www.juergen-bayer.net/ip/links.htm pflegen.
1.1.5 Ein Wort an die weiblichen Leser Im Buch verwende ich immer die (leider) übliche männliche Form. Ich bin zwar ein ausgesprochener Feminist (sofern man das als Mann überhaupt von sich behaupten kann), eine zweigleisige Vorgehensweise (»der/die Programmierer/in«) oder der in den USA manchmal praktizierte Wechsel zwischen der weiblichen und der männlichen Form ist aber für den Lesefluss eher hinderlich. 1.1.6 Typografische Konventionen Dieses Buch verwendet einige typografische Konventionen, die dem allgemeinen Standard entsprechen. Syntaxbeschreibungen
Wenn ich die Syntax einer Deklaration beschreibe, verwende ich eine kompakte und übersichtliche Form wie im folgenden Beispiel (das die IndexOf-Methode der string-Klasse von C# beschreibt): int IndexOf({string | char} value [, int startIndex] [,int count])
Normale Wörter sind sprachspezifische Schlüsselwörter. Kursive Wörter sind Platzhalter für Eingaben, die Sie spezifizieren müssen. Die in eckigen Klammern stehenden Elemente sind optional. Diese Elemente können Sie, müssen Sie jedoch nicht angeben. Die eckigen Klammern sind nur Teil der Syntaxbeschreibung und werden nicht im Programmcode angegeben. Wenn an einer Stelle mehrere Varianten möglich sind, werden diese durch ein | (das Oder-Zeichen in vielen Sprachen) voneinander getrennt. Handelt es sich dabei um eine nichtoptionale Angabe, schließe ich diese in geschweifte Klammern ein. Diese Klammern werden dabei natürlich im Quellcode auch nicht mit angegeben.
27
Einführung
Sie müssen bei den Syntaxbeschreibungen ein wenig aufpassen: In einigen Sprachen wie JavaScript gehören eckige und geschweifte Klammern zur Syntax. Geschweifte Klammern werden in JavaScript und in C# (eine andere .NET-Sprache) verwendet, um Anweisungsblöcke zu kennzeichnen. In Visual Basic .NET werden diese Klammern verwendet, um Arrays direkt bei der Deklaration zu initialisieren. Eckige Klammern werden in JavaScript und C# bei Arrays verwendet, um die Indizes zu spezifizieren. Sie müssen die Syntaxbeschreibungsklammern also von den sprachspezifischen Klammern unterscheiden. Beispiel-Listings
Beispiel-Listings werden in der Schriftart Letter Gothic dargestellt: string now; now = DateTime.Now.ToShortDateString(); Response.Write("Heute ist der " + now);
Aufgrund der beschränkten Breite müssen einige Anweisungen umbrochen werden. In VBScript und Visual Basic .NET muss dazu der Unterstrich an eine umbrochene Zeile angehängt werden, was natürlich auch in den Beispiel-Listings umgesetzt wird: Response.Write("Heute ist der " & _ DateTime.Now.ToShortDateString());
Exkurse
Für den Fall, dass ein Begriff, der eigentlich nicht direkt zum jeweiligen Thema gehört, doch kurz beschrieben werden soll, erfolgt diese Beschreibung in Form eines Exkurses. Ein Exkurs wird wie dieser Absatz dargestellt. Typografische Konventionen im Fließtext
Im normalen Text werden sprachspezifische Schlüsselwörter in der Schriftart Courier dargestellt. Wörter in Kapitälchen im normalen Text bezeichnen Teile der Benutzerschnittstelle, wie z.B. Menübefehle und Schalter. Menübefehle, die in einem Menü untergeordnet sind, werden mit den übergeordneten Menüs angegeben, wobei die einzelnen
28
1.1 Zum Buch
Einführung
Menüebenen durch einen Schrägstrich getrennt werden (z.B. DATEI / BEENDEN). Datei- und Ordnernamen werden kursiv formatiert. Internetadressen werden folgendermaßen gekennzeichnet: www.addison-wesley.de. Tastenkappen wie (F1) stehen für Tasten und Tastenkombinationen, die Sie betätigen können, um bestimmte Aktionen zu starten. 1.1.7 Die Icons in diesem Buch Zur Erleichterung der Orientierung verwendet dieses Buch verschiedene Icons für Textabschnitte mit besonderer Bedeutung. Über dieses Symbol werden Hinweise gekennzeichnet, deren Beachtung wichtig ist. Programmiersprachen und die beim Programmieren verwendeten Komponenten und Server enthalten häufig so einige Fallen, in die Sie arglos tappen können. Spezielle »Achtung«-Hinweise, die mit diesem Icon gekennzeichnet sind, machen auf diese Fallen aufmerksam und bieten natürlich – sofern möglich – gleich auch eine Lösung. Dieses Symbol kennzeichnet allgemeine Tipps, Hinweise zur Lösung bestimmter Probleme, die im Buch nicht weiter behandelt werden, und Hinweise auf Webseiten oder andere Ressourcen, die ein Thema näher beleuchten.
1.2 Internetprogrammierung in der Übersicht 1.2.1 Was unterscheidet Internetprogramme von klassischen Anwendungen? Klassische Anwendungen werden lokal auf dem Rechner des Anwenders ausgeführt. Internetprogramme dagegen hauptsächlich auf entfernten Webservern. Das ist eigentlich schon der große Unterschied. Und daraus ergeben sich einige Vorteile der Internetprogrammierung gegenüber der klassischen Programmierung. Die Probleme bei der Installation klassischer Anwendungen
Klassische Anwendungen werden komplett oder teilweise (in Form verteilter Anwendungen) auf dem Rechner des Anwenders ausgeführt.
29
Einführung
Dazu ist es notwendig, dass die Anwendung auf dem Rechner des Anwenders installiert wird. Für einzelne Personen mag dies keinen großen Aufwand bedeuten. Für Firmen, die sehr viele Anwender mit einer Software ausstatten müssen, bedeutet dies aber schon recht viel Arbeit. Zudem ist die Installation häufig nicht allzu einfach, weil in der Regel einige Systemkomponenten installiert oder aktualisiert werden müssen. Eine umfangreiche Visual-Basic-6-Anwendung mit Datenbankzugriff erfordert beispielsweise die Vor-Installation von MDAC (den Microsoft Data Access Components, die für den Datenzugriff verwendet werden) in der Version, die auf dem Entwicklungsrechner verwendet wurde. Oft muss für diese Anwendungen auch die aktuelle JET-Engine (zum Zugriff auf Access-Datenbanken) installiert werden. Verwendet die Anwendung diverse andere COM-Komponenten (MDAC liegt ebenfalls in Form von COM-Komponenten vor), werden diese ebenfalls installiert. Dabei treten häufig Probleme auf, weil die eine oder andere Komponente aus irgendwelchen Gründen nicht installiert werden kann oder nach der Installation zu einem fehlerhaften Verhalten anderer Programme oder Komponenten führt. Meine Erfahrung aus der Praxis ist, dass es recht schwierig ist, eine komplexe Software an viele Anwender zu verteilen, die natürlich ganz unterschiedliche Betriebssystemumgebungen besitzen. Der Support, der für die Installation und den Betrieb der Anwendung notwendig ist, sprengt oft die Grenzen der Machbarkeit. Ein weiteres Problem ist die notwendige Nachinstallation der Anwendung, nachdem der Programmierer eine neue, fehlerbereinigte oder erweiterte Version entwickelt hat (was erfahrungsgemäß in der ersten Phase einer Softwareeinführung recht häufig vorkommt). Diese neue Version muss auf allen Rechnern erneut installiert werden. Die ITAbteilung einer Firma, die gerade erst 50 Rechner mit der ersten Version einer Anwendung ausgerüstet hat (und jetzt noch stöhnt), wird sich bedanken, wenn sie eine Woche später alles noch einmal wiederholen muss. Ist der Installationsaufwand für private Anwendungen oder innerhalb einer Firma vielleicht noch zu rechtfertigen, so ist dies aber nicht mehr der Fall für Firmen, die eine Software zum Zugriff auf Firmendaten an sehr viele externe Anwender verteilen muss. Ich habe selbst einmal eine solche Anwendung entwickelt. Das Centrum für Reisemedizin in
30
1.2 Internetprogrammierung in der Übersicht
Einführung
Düsseldorf ermöglichte damit seinen Kunden, so genannte Reisegesundheitsbriefe abzurufen, die Informationen zu gesundheitlichen Risiken und zu Vorsorgemaßnahmen für einzelne Länder beinhalten. Die Kommunikation dieses Programms mit dem Server, der die Daten bereithielt, lief (wie bei den meisten dieser Programme) über eine Telefonleitung und spezifische Protokolle. Wenn in einem solchen Fall eine neue Version der Anwendung an mehrere Tausend Kunden verteilt werden muss, bedeutet dies für die Firma einen erheblichen Aufwand und meist auch hohe Kosten. Die klassische Programmierwelt hat eine Lösung dieses Problems darin gefunden, dass Programme einfach auf mehrere Rechner verteilt werden. Auf einem oder mehreren Servern werden Komponenten installiert, die Programmteile (in Form von Klassen) enthalten. Über eine Kommunikationstechnologie wie das Microsoft COM- oder das allgemeinere CORBA-Konzept erzeugen die eigentlichen Anwenderprogramme aus diesen Klassen Objekte und rufen deren Methoden auf. Die Anwendung selbst enthält meist nur noch wenig Programmcode, die maßgebliche Logik (die »Geschäftslogik1«) befindet sich in den Komponenten. Eine Änderung der Geschäftslogik erfordert dann im Idealfall lediglich eine einzige Neuinstallation auf dem Server. Leider funktioniert dies in der Praxis oft nicht, da die Änderungen häufig so umfangreich sind, dass die Schnittstelle zu den Anwenderprogrammen geändert werden muss. Alte Anwenderprogramme werden inkompatibel und müssen neu programmiert und installiert werden. Zudem benötigt ein Anwenderprogramm eine direkte Netzwerkverbindung zu den Servern, die die Komponenten ausführen. In einem lokalen Firmennetz mag das kein Problem sein. Wenn der Anwender allerdings weit entfernt ist, wird die Kommunikation (über DFÜ oder TCP/IP) schwierig und meist auch sehr langsam. Hinzu kommt, dass das verwendete Kommunikationskonzept bestimmte Betriebssysteme voraussetzt. Das Microsoft COM-Konzept funktioniert z.B. nur unter Windows. Die Anbindung von Rechnern mit anderen Betriebssystemen ist kompliziert oder gar unmöglich.
1 Der Begriff »Geschäftslogik« kommt daher, dass diese Programmlogik die Logik widerspiegelt, mit der im »Geschäft« (in der Firma) Daten verarbeitet werden. Die Geschäftslogik meiner Firma schreibt z.B. vor, dass vogonische Kunden immer im Voraus bezahlen müssen.
31
Einführung
Die Lösung der geschilderten Probleme sind HTML-basierte Internetprogramme2. Diese werden in einem Webbrowser angezeigt und teilweise auf dem Anwenderrechner (in Form von JavaScript-Programmen), aber zum größten Teil auf dem Webserver ausgeführt (in Form von ASP-, ASP.NET-, JSP- und anderen Programmen). Da die Programmdateien immer und vollständig auf dem Webserver gespeichert sind (egal, ob client- oder serverseitig) und bei jeder Benutzung erneut zum Client heruntergeladen werden, erfordern diese Programme keine Installation und sind damit automatisch auch immer aktuell. Die Geschäftslogik ist normalerweise komplett in den auf dem Server gespeicherten Programmen untergebracht. Eine Änderung dieser Logik erfordert keine Anpassung auf dem Anwendercomputern. Ein Update der Anwendung kann also einfach auf den Webserver gespeichert werden. Fragt ein Benutzer die Anwendung erneut ab, erhält er sofort und ohne Probleme die neue Version. Hinzu kommt, dass die Anbindung neuer Anwender absolut keine Probleme bereitet. Firmen schützen den Internetzugang zu ihren kostenpflichtigen oder firmeninternen Programmen natürlich, so dass ein Anwender sich einloggen muss. Der einzige Aufwand zur Anbindung neuer Anwender ist dann, ein Anwenderkonto zu erzeugen und dem Anwender die Login-Informationen zukommen zu lassen. Das Centrum für Reisemedizin hat beispielsweise mittlerweile (natürlich) auf diese Art mein altes Programm durch ein Internetprogramm ersetzt (www.crm.de). Einfache Kommunikation
Klassische Programme mussten häufig mit anderen Anwendungen oder Servern kommunizieren, die weit entfernt laufen. Die Kommunikation erfolgte häufig über DFÜ-Verbindungen und Telefonleitungen. Die speziellen Protokolle, die dabei meist verwendet wurden, erforderten vom Programmierer viel Arbeit. Die Programmierung neuer oder die Erweiterung vorhandener klassischer Lösungen war aufgrund der komplexen, unterschiedlichen Server und Protokolle meist recht kompliziert, was besonders dann galt, wenn der Programmierer die vorhandene Lösung nicht selbst entwickelt hatte.
2 Wie Sie ab Seite 34 noch erfahren, können Internetanwendungen auch klassische Anwendungen sein, die über ein Internetprotokoll mit anderen Anwendungen kommunizieren.
32
1.2 Internetprogrammierung in der Übersicht
Einführung
Meine Reisegesundheitsbrief-Anwendung ist ein recht gutes Beispiel für diese Problematik. Die Verbindung mit dem Server des Centrums für Reisemedizin wurde dabei über ein Modem oder ISDN aufgebaut. Der Server erwartete Eingaben nach einem bestimmten textbasierten Protokoll und lieferte recht komplexe Textdaten, die das Programm zunächst interpretieren musste. Das war keine allzu leichte Aufgabe. Die Internetprogrammierung löst diese Probleme (natürlich). Reine, HTML-basierte Internetprogramme haben das Problem der Kommunikation mit entfernten Servern meist gar nicht, weil diese Server normalerweise in der Nähe des Webservers laufen. Aber auch für die Entwicklung von Windowsanwendungen, die entfernte Dienste nutzen, bietet die Internetprogrammierung Lösungen. So können Sie recht einfach Programme entwickeln, die über TCP/IP oder UDP/IP über das Internet mit anderen Programmen oder Servern kommunizieren. Die dabei standardisierten höheren Protokolle (wie HTTP und SMTP) oder Speichertechniken (wie XML) erleichtern die Kommunikation erheblich. Für die Verwendung dieser Protokolle und Techniken bieten moderne Programmiersysteme natürlich einfach anzuwendende Komponenten. Neben der reinen IP-Kommunikation können Sie aber auch mit Webdiensten arbeiten. Diese Dienste werden auf einem Webserver installiert und auch dort ausgeführt. Beliebige, SOAP3-fähige Anwendungen können diese Dienste sehr einfach verwenden, weil im Prinzip lediglich Methoden aufgerufen werden. Die Internetprogrammierung bietet also recht viele Möglichkeiten zur Lösung der Probleme der klassischen Programmierung. 1.2.2 Clientseitige versus serverseitige Internetprogrammierung Programme für das Internet können so entwickelt werden, dass diese auf dem Client (dem Anwenderrechner) oder dem Server laufen. Clientseitige Internetprogramme sind entweder echte (Windows-)Anwendungen, die über eines der Internetprotokolle (z.B. über TCP/IP, HTTP oder SOAP) mit anderen Anwendungen auf entfernten Rechnern kommunizieren, oder in HTML eingebundene Skripte. Serverseitige Programme 3 Das Simple Object Access Protokoll ist ein einfaches, textbasiertes Protokoll, das definiert, wie Anwendungen über das Internet Methoden von Webdiensten aufrufen können und wie die Rückgabewerte dieser Methoden zurückgeliefert werden.
33
Einführung
werden normalerweise (wenigstens im Fall von ASP) in HTML-Seiten eingebunden, auf dem Webserver ausgeführt und im Webbrowser des Client angezeigt. Clientseitige Internetprogramme
Internetprogramme, die komplett auf dem Computer des Anwenders ausgeführt werden, besitzen (zurzeit) nicht allzu viel Bedeutung bei der Internetprogrammierung. Die Verwendung echter (Windows-)Anwendungen für solche Programme ist problematisch, da diese für jedes Betriebssystem separat entwickelt und vor der Benutzung auf dem Client installiert werden müssen. Wird die Anwendung umgearbeitet und verbessert, muss sie von allen Anwendern neu installiert werden. Dieses Problem wurde ja bereits besprochen. Die Bedeutung von normalen Anwendungen, die über das Internet kommunizieren, könnte in Zukunft aber auch wieder größer werden. Zurzeit werden so genannte Peer-To-Peer-Programme diskutiert, die über das Internet eine direkte Verbindung zueinander aufbauen. Beispiele dafür sind die Dateitauschbörse Morpheus (www.morpheus.com) und das Projekt SETI@Home (www.seti-inst.edu/science/setiathome.html). Bei Morpheus kommuniziert ein Windows-Client direkt mit einem speziellen Server und den Clients anderer Anwender und ermöglicht so den direkten Austausch frei verfügbarer Dateien. Bei SETI@Home wertet ein spezielles Programm kleine Datenpakete aus, die ein SETI4-Server versendet, und sendet das Ergebnis zum Server zurück. SETI sucht nach außerirdischem Leben und nutzt über SETI@Home die Ressourcen von vielleicht Millionen verschiedenen Rechnern zur Auswertung der Beobachtungsdaten. Die Internetseite www.peer-to-peerwg.org befasst sich recht ausführlich mit dem Thema Peer-To-Peer. Das Buch behandelt die Programmierung solcher Anwendungen in Kapitel 16. Neben diesen Peer-To-Peer-Programmen werden clientseitige Anwendungen sehr häufig in Form von in HTML eingebundenen Programmen eingesetzt. Theoretisch wäre es damit möglich, komplette Pro4 Search for Extraterrestrial Intelligence
34
1.2 Internetprogrammierung in der Übersicht
Einführung
gramme zu entwickeln und so den Installationsaufwand zu umgehen. Script-Sprachen, Java-Applets und die anderen verwendeten Techniken sind in den Möglichkeiten aber arg eingeschränkt und können (aus Sicherheitsgründen) meist auch nicht auf die Ressourcen des lokalen Rechners zugreifen. Genutzt werden solche clientseitigen Programme aber, um HTML-Seiten, die ohne Programmierung sehr statisch sind, dynamisch zu gestalten. Eine HTML-Seite mit einem bei einer Mausberührung aufklappenden Menü wird beispielsweise fast ausschließlich über eingebettete Script-Programme erzeugt (alternativ ist die Verwendung von Java Applets oder ActiveX-Steuerelementen möglich). Sehr häufig werden Script-Programme in Verbindung mit ASP(.NET)Dokumenten verwendet, um einige Teile der Programmierung auf den Client auszulagern und damit die Ausführung der Anwendung zu beschleunigen. Serverseitige Internetprogramme
Serverseitige Internetprogramme werden auf dem Webserver ausgeführt. Ein Teil dieser Programme verwendet Techniken wie CGI, ASP, ASP.NET, JSP oder PHP zur Erzeugung von HTML-Code. Diese Programme werden normalerweise über einen Browser aufgerufen, der das HTML-Ergebnis natürlich auch darstellt. Die Artikelliste von OnlineShops wird z.B. häufig auf dieser Art aufgebaut und dargestellt. Bei vielen serverseitigen Internetprogrammen mit HTML-Oberfläche ermöglichen spezielle HTML-Steuerelemente dem Anwender die Eingabe von Daten, die beim Aufruf des Programms (meist über einen speziellen Submit-Schalter) zum Server übertragen und dort ausgewertet werden. Eine Bestellung in einem Online-Shop wird beispielsweise so auf den Webserver übertragen und dort über das serverseitige Programm in eine Datenbank gespeichert. Das Buch geht ab Seite 43 grundlegend und ab Kapitel 6 ausführlich auf ASP.NET ein. Manche Programme oder Server geben aber auch Daten zurück, oft in Form von XML. Diese Programme können von beliebigen Anwendungen aufgerufen werden, die die zugrunde liegende Technologie beherrschen. Die über den Microsoft SQL Server verwalteten Datenbanken können beispielsweise über das Internet recht einfach abgefragt werden. Das Programm auf dem Client, das dann eine Internet- oder Windowsanwendung sein kann, kann diese Daten beliebig weiterverarbeiten.
35
Einführung
Eine neue Version serverseitiger Programme sind Webdienste. Webdienste sind im Prinzip Objekte, die auf einem Webserver gespeichert sind und deren Methoden über das Internet aufgerufen werden. Der Aufruf der Methoden erfolgt über standardisierte Internetprotokolle wie HTTP oder SOAP. Geben Webdienst-Methoden Daten zurück, erfolgt dies in Form von XML. Zum Aufruf von Webdienst-Methoden und zur Verarbeitung der zurückgelieferten Daten müssen Sie aber nicht die Internet-Protokolle oder XML beherschen. Verschiedene Toolkits oder Klassen des Programmiersystems (z.B. des .NET Frameworkss) ermöglichen den einfachen Aufruf der Methoden von Webdiensten und die ebenso einfache Auswertung zurückgelieferter Daten. Webdienste werden ab Seite 48 grundlegend und in Kapitel 15 ausführlich behandelt.
1.3 HTML, DHTML, CSS, XHTML und XML HTML
HTML (HyperText Markup Language) ist eine textbasierte Sprache zur Beschreibung des Inhalts und der Formatierung von Dokumenten. Im Prinzip können Sie ein HTML-Dokument mit einem Word-Dokument vergleichen: Ein solches Dokument enthält Text, Steuerelemente, Grafiken, multimediale Inhalte und Formatierungen. Der maßgebliche Unterschied ist, dass der Quellcode eines HTML-Dokuments aus lesbarem Text besteht. Ein einfaches HTML-Dokument sieht z.B. so aus: Einfaches HTML-Dokument Einfaches HTML-Dokument Hallo, das ist ein einfaches HTML-Dokument.
Die Elemente in spitzen Klammern sind so genannte Tags, die unter anderem die Formatierung des Dokuments steuern. Das h1-Tag sorgt zum Beispiel dafür, dass der eingeschlossene Text als Überschrift erster Ebene dargestellt wird.
36
1.3 HTML, DHTML, CSS, XHTML und XML
Einführung
HTML-Dokumente werden hauptsächlich im Internet eingesetzt und von Webbrowsern angezeigt. Das kennen Sie ja wahrscheinlich bereits. Abbildung 1.1 zeigt das Beispieldokument im Internet-Explorer. Abbildung 1.1: Ein einfaches HTML-Dokument im InternetExplorer
Auf der Buch-CD finden Sie einen Artikel, der recht ausführlich auf den aktuellen HTML-Standard 4.0 eingeht. DHTML
DHTML (Dynamic HTML) ist keine Sprache wie HTML, sondern eine Erweiterung des HTML-Standards. Browser, die DHTML unterstützen, ermöglichen Script-Programmen (die in HTML eingebunden sind) den Zugriff auf die Elemente des HTML-Dokuments. HTML-Elemente werden dazu als Objekte betrachtet, deren Eigenschaften gelesen und geändert werden können. Mit JavaScript ist es über das DHTML-Modell z.B. kein Problem, die Hintergrundfarbe eines Verweises (Links) beim Herüberfahren mit der Maus zu ändern oder ein Menü beim Klicken mit der Maus aufzuklappen (wie Sie dies von einigen Websites her kennen). Nur, um zu demonstrieren, wie DHTML prinzipiell funktioniert, zeigt das folgende Beispiel ein HTML-Dokument, bei dem die Hintergrundfarbe eines Überschrift-Elements wechselt, wenn der Anwender die Maus darauf bewegt:
37
Einführung
Einfaches DHTML-Dokument function changeColor(color) { header.style.backgroundColor = color; } Einfaches DHTML-Dokument Fahren Sie mit der Maus über die Überschrift, um deren Hintergrundfarbe zu ändern.
Das Ergebnis dieses Quellcodes, dessen Verständnis schon einiges an Wissen fordert, sehen Sie in Abbildung 1.2. In Kapitel 4 geht das Buch ausführlich auf JavaScript, in Kapitel 5 auf DHTML ein. Zum grundsätzlichen Verständnis erläutere ich den Quelltext ein wenig: Das script-Tag leitet ein JavaScript-Programm ein. Innerhalb dieses Tag ist eine Funktion (changeColor) deklariert, die die Hintergrundfarbe des Header-Tag je nach dem übergebenen Argument ändert. Die Funktion greift über das DHTML-Objektmodell auf das Header-Tag (h1) zu und ändert den Stil dieses Tags (wo auch schon ein wenig CSS verwendet wird). Dieser ist dazu über das id-Attribut mit einem Namen versehen (header). Über die Attribute onmouseover und onmouseout werden die Bewegungen der Maus auf dieser Überschrift mit der Funktion change-
38
1.3 HTML, DHTML, CSS, XHTML und XML
Einführung
Color verknüpft. Immer dann, wenn die Maus über der Überschrift bewegt wird, wird die Funktion mit dem Argument 'red' aufgerufen. Wird die Maus aus dem Bereich der Überschrift herausbewegt, wird diese Funktion mit dem Argument 'white' aufgerufen. Abbildung 1.2: Das BeispielDHTMLDokument im normalen Zustand und mit der Maus auf der Überschrift
CSS
CSS (Cascading Style Sheets) ist eine Erweiterung von HTML, die es ermöglicht, den Stil (die Formatierung) von HTML-Elementen flexibler zu gestalten, als es mit HTML allein möglich ist. Über CSS-Stile können Sie einen Absatz oder eine Überschrift z.B. ohne Probleme mit einer bestimmten Hintergrundfarbe und einem Rahmen versehen, was mit HTML alleine nicht möglich ist. Das folgende Beispiel ändert den Stil des h1-Tag so ab, dass dieser einen Rahmen besitzt, mit einer grauen Hintergrundfarbe dargestellt wird und seinen Text mittig ausrichtet:
39
Einführung
Einfaches HTML-Dokument mit CSS h1 { border:thin solid black; background-color:silver; padding: 5 5; text-align:center; } Einfaches HTML-Dokument mit CSS
Abbildung 1.3: Das BeispielHTML-Dokument mit CSS
40
1.3 HTML, DHTML, CSS, XHTML und XML
Einführung
In diesem Beispiel wird der Stil des h1-Tag über ein in das Dokument integriertes style-Tag umdefiniert. CSS-Stile können aber auch in externen Dateien verwaltet und in HTML-Dokumente gelinkt werden. Damit können Sie ein bestimmtes Layout für alle HTML-Dokumente einer Website festlegen und sehr einfach auch ändern. CSS wird im gleichnamigen Artikel auf der Buch-CD beschrieben. Die übliche Kombination
Üblicherweise verwenden Websites eine Kombination aus HTML, CSS und DHTML:
HTML erzeugt den Grundaufbau der Seite, Cascading-Style-Sheet-Dateien verwalten den Stil aller zur Website gehörenden HTML-Dateien (womit eine Änderung des Stils der Website recht einfach möglich ist), JavaScript-Programme reagieren auf bestimmte Ereignisse wie der Mausbewegung auf einem HTML-Element, über DHTML erhalten JavaScript-Programme Zugriff auf alle Objekte der HTML-Seite und können diese dynamisch verändern (z.B. ein Menü dynamisch erzeugen und sichtbar machen).
XHTML
XHTML (Extensible HyperText Markup Language) ist eine Weiterentwicklung von HTML 4.0. XHTML basiert auf HTML 4.0 und XML und verbindet die Vorzüge dieser beiden Sprachen. Da die aktuellen Webbrowser zurzeit und in der näheren Zukunft lediglich HTML 4.0 unterstützen, besitzt XHTML noch keine allzu große Bedeutung. XHTML wird deshalb in diesem Buch nicht beschrieben, ich zeige aber im HTML-Artikel, wie Sie Ihre HTML-Dokumente so gestalten, dass diese möglichst XHTML-konform sind. XML
XML (Extensible Markup Language) ist eine Familie von Standards zur Speicherung und Verarbeitung von strukturierten Daten in Textform. XML-Dateien sehen zwar ähnlich aus wie HTML-Dateien, besitzen aber eine grundlegend andere Bedeutung: HTML-Dateien enthalten unstrukturierte Dokumente (deren Bedeutung es ist, irgendwie dargestellt zu werden), XML-Dateien speichern strukturierte Daten (die eine Anwendung beliebig auswerten kann, nicht nur zur Darstellung). 41
Einführung
XML-Dateien können Sie mit Datenbanken vergleichen (die ja auch strukturierte Daten speichern). XML besitzt aber einige Vorteile gegenüber Datenbanken. Einer der wichtigsten ist die Speicherung der Daten in Textform. Deshalb kann eine Anwendung oder eine Komponente, die auf einem beliebigen System läuft, Daten zu einem anderen beliebigen System senden, das diese dann problemlos auswerten kann. So kann eine Windowsanwendung beispielsweise Daten von einem Webdienst anfordern, der auf einem Unix-Rechner läuft. Ein anderer wichtiger Vorteil ist die Flexibilität der Struktur eines XML-Dokuments. Im Vergleich zu einer Datanbank ist die Änderung der Struktur von XML-Daten sehr einfach. Ein einfaches XML-Dokument, das die Daten von zwei Adressen speichert, sieht z.B. so aus:
Wenn Sie dieses Beispiel ausprobieren wollen, müssen Sie eine Datei erzeugen, deren Endung .asp ist. Diese Datei müssen Sie in einem Ordner des Webservers speichern und im Browser über die URL des Webservers aufrufen (nicht über eine Dateiangabe). Die Zeichenfolgen sind so genannte ASP-Token. In diese Token sind Programmanweisungen eingebunden. Das Beispiel verwendet dazu die Sprache VBScript. Alternativ wäre für ASP auch JScript, der Microsoft-JavaScript-Dialekt, möglich. Fordert ein Browser ein ASP-Dokument an, erkennt der Webserver an der Dateiendung, dass es sich um eine ASP-Datei handelt. Der Webserver liest die Datei ein und übergibt sie an eine Funktion der Komponente asp.dll, die für die Abarbeitung von ASP-Programmen verantwortlich ist. Diese führt die Programmanweisungen des ASP-Dokuments über eine zur verwendeten Sprache passende Script-Engine aus. Einige Anweisungen (z.B. Response.Write) erzeugen HTML-Code, der gemeinsam mit dem direkt im ASP-Dokument enthaltenen HTML-Code in das Ergebnis geschrieben wird. Nach der Abarbeitung der Datei gibt die ASP.dll das Ergebnis an den Webserver zurück, der das damit erzeugte HTMLDokument schließlich zum Client zurücksendet. 45
Einführung
Das Ergebnis des Beispiels oben zeigt der folgende HTML-Code: Hello World Hello World Hello World Hello World
ASP.NET
ASP.NET ist die neue ASP-Variante, die auf dem .NET Framework basiert. Das .NET Framework wird in Kapitel 6 noch umfassender erläutert. Für den Anfang reicht es aus, zu wissen, dass das .NET Framework eine komplette Infrastruktur zur Erzeugung, Ausführung und Verteilung von Anwendungen zur Verfügung stellt. Ein wichtiger Teil des .NET Frameworks ist die umfangreiche Klassenbibliothek, die unter anderem sehr viele Klassen für die Internetprogrammierung enthält. ASP.NET erweitert das klassische ASP um einige wichtige Features. Um diese zu verdeutlichen, liste ich zunächst die Nachteile der Programmierung unter ASP auf:
46
Die Programmierung einer benutzerfreundlichen Programmoberfläche ist unter ASP recht schwierig. Um besondere Features, wie eine Überprüfung von Benutzereingaben, zu erreichen, sind einige JavaScript-, DHTML- und andere »Tricks« notwendig. ASP-Dokumente sind meist komplex aufgebaut, da der HTMLCode des Dokuments mit dem Programm vermischt ist. Die Wartung und Weiterentwicklung eines ASP-Dokuments ist deshalb meist nicht einfach, was besonders dann gilt, wenn ein anderer Programmierer die Anwendung entwickelt hat. Die in ASP verwendeten Skriptsprachen sind, verglichen mit echten Programmiersprachen, in den Möglichkeiten arg eingeschränkt. Beispielsweise fehlen echte Datentypen (Skriptsprachen kennen nur einen allgemeinen Datentyp), allgemeine Funktionen müssen explizit in ASP-Dokumente eingebunden werden. Zudem ist die objektorientierte Programmierung nur sehr eingeschränkt möglich.
1.4 Was ist ASP, was ist ASP.NET?
Einführung
ASP-Programme werden bei jeder Ausführung vom Webserver neu interpretiert. Die Ausführungsgeschwindigkeit ist deshalb nicht besonders hoch. Das Debuggen von ASP-Seiten ist zwar möglich. Verglichen mit echten Entwicklungsumgebungen ist das ASP-Debuggen aber sehr eingeschränkt.
ASP.NET hebt diese Nachteile auf. ASP.NET-Programme werden in einer echten Programmiersprache, wie C# oder Visual Basic .NET, entwickelt. Deshalb können Sie bei der Entwicklung alle Features nutzen, die eine moderne Programmiersprache bietet. Die erzeugten Programme werden (automatisch) bei der ersten Ausführung einer ASP.NET-Seite kompiliert. Für alle weiteren Ausführungen dieser Seite (bei denen die kompilierte Version verwendet wird) ist die Performance wesentlich höher als bei ASP-Programmen. Eine große Anzahl spezieller Internet-Steuerelemente, deren Verwendung sehr einfach ist, erleichtert die Erstellung der Oberfläche. Diese Steuerelemente sorgen bei der Ausführung des Programms selbst dafür, dass für den Webbrowser auf dem Client passender HTML-Code erzeugt wird. Dynamische Webseiten, die auf Ereignisse wie z.B. einen Mausklick auf einem Schalter reagieren, sind mit ASP.NET nur noch ein sehr kleines Problem. Um solche Seiten zu erstellen, erzeugen Sie einfache Ereignisbehandlungsmethoden für die gewünschten Ereignisse, so wie es Visual Basic-, Delphi- oder .NET-Programmierer sowieso schon gewöhnt sind. Bei der Ausführung des Programms wird bei einigen Steuerelementen je nach Browser sogar automatisch JavaScript-Code in das HTML-Dokument integriert. Damit wird das beim alten ASP oft notwendige »Hin und Her« (bezeichnet als »Roundtrip«) zwischen Client und Server auf ein Minimum reduziert. Wenn Sie beispielsweise in ASP eine Eingabe auf Gültigkeit überprüfen wollen, müssen Sie entweder selbst JavaScript-Code in das Dokument integrieren oder das Dokument an eine ASP-Seite senden, die die Eingaben auf dem Server überprüft. ASP.NET nimmt Ihnen diese Arbeit über spezielle Validierungs-Steuerelemente ab. ASP.NET besitzt also fast nur Vorteile. Der einzige »Nachteil« ist der, dass ASP.NET die Installation des .NET Frameworks auf dem Webserver erfordert. Wenn das .NET Framework nicht (wie andere Micro-
47
Einführung
soft-Technologien) eine Vielzahl an Sicherheitslöchern mit sich bringt, die erst nach und nach entdeckt werden (von den Hackern natürlich wesentlich früher als von Microsoft), wird das aber kein Problem darstellen. Andererseits werden viele Firmenkunden der Installation eines relativ neuen Framework auf einem Produktionsrechner wohl nicht so ohne weiteres zustimmen. Wenigstens so lange nicht, wie das .NET Framework noch nicht Bestandteil des Betriebssystems ist (was aber in der Windows-Version nach XP der Fall sein wird).
1.5 Was sind Webdienste? Webdienste sind Komponenten, die auf einem Webserver laufen und von beliebigen Anwendungen über das Internet verwendet werden können. Im Prinzip ist ein Webdienst eine Klasse mit Methoden, die allerdings – anders als normale Methoden – über das Internet von einem beliebigen (internetfähigen) Computer aufgerufen werden können. Das Ergebnis einer solchen Methode wird ebenfalls über das Internet zurückgeliefert. Eine Bank könnte z.B. einen Webdienst anbieten, der u.a. den aktuellen Kurswert von Aktien liefert. Beim Aufruf der entsprechenden Methode würde ein Kürzel für die Aktie übergeben werden, die Methode würde den Wert der Aktie in Form von XML zurückliefern. Microsoft verfolgt damit eine Idee, die als »Software as a Service« bezeichnet wird: Eine Anwendung bedient sich verschiedener Webdienste um den Anwender mit Informationen und Features zu versorgen und muss selbst kaum noch Programmcode enthalten. Der Aufruf des Webdienstes und das Zurücksenden der Daten erfolgen über SOAP, ein spezielles, normalerweise auf HTTP basierendes Protokoll, oder über das HTTP-Protokoll selbst. Deshalb können alle SOAPbzw. HTTP-kompatiblen Anwendungen Webdienste verwenden. Microsoft bietet ein kostenloses »SOAP-Toolkit« an, mit dem die Programmierung von SOAP-fähigen Anwendungen recht einfach ist. Wenn Sie Ihre Anwendungen in einer .NET-Sprache entwickeln, können Sie stattdessen auf die im .NET Framework enthaltenen Klassen zur Verwendung von Webdiensten zurückgreifen. Natürlich können Sie mit dem .NET Framework auch eigene Webdienste entwickeln. Das Buch behandelt die Programmierung von Webdiensten in Kapitel 15.
48
1.5 Was sind Webdienste?
Basiswissen
2
Basiswissen
In diesem Kapitel behandle ich nicht die Internet-Grundlagen (also keine Angst ...), sondern stelle lediglich die für Programmierer wichtigen Bereiche vor, allerdings ohne diese ausführlich zu erläutern. Ein Internetprogrammierer muss wissen, was eine IP-Adresse ist, muss aber nicht die Feinheiten der IP-Kommunikation oder der Einteilung von IP-Adressen kennen. Genauso sollte ein Internetprogrammierer zwar wissen, was das HTTP- oder das FTP-Protokoll ist. Er wird aber eigentlich nur in absoluten Ausnahmefällen direkt mit diesem Protokoll arbeiten. Ich beschreibe die einzelnen Themen deswegen in diesem Kapitel lediglich so, dass Sie wissen, worum es sich handelt und die einzelnen Themen vielleicht einmal auch selbst ausprobieren können. Wenn Sie mehr dazu wissen wollen, finden Sie diese Themen ausführlicher im Artikel Internet-Grundlagen für Programmierer auf der Buch-CD beschrieben.
2.1 RFCs Alles, was irgendwie mit dem Internet zusammenhängt, wird in technischen Dokumenten beschrieben, die als »RFC« (Request For Comment1) bezeichnet werden. Neben Diskussionen über neue Forschungsprojekte, Berichten über den Zustand des Internet und anderem werden dort auch die Internet-Protokolle sehr ausführlich dokumentiert. RFC-Dokumente werden einfach nummeriert. RFC 791 beschreibt z.B. das IP-Protokoll. Beim Verleger der RFCs, dem RFC-Editor, finden Sie eine Möglichkeit, die einzelnen RFCs einzusehen. Gehen Sie dazu zur Seite www.rfc-editor.org/overview.html. Klicken Sie auf den SEARCH-Link und geben Sie auf der Suchseite einen Suchbegriff wie z.B. die RFC-Nummer ein. Da die Webseite des RFC-Editors oft recht langsam ist und die Seiten einiger Dokumente manchmal erst gar nicht angezeigt werden, finden Sie eine gute und schnelle Alternative bei der Internet Engineering Task Force (IETF): www.ietf.org/rfc.html.
1 Aufforderung, Kommentare zur beschriebenen Technologie einzusenden
49
Basiswissen
Im weiteren Verlauf nehme ich immer wieder Bezug auf RFC-Dokumente. Beachten Sie, dass RFCs kontinuierlich weiterentwickelt werden. Finden Sie bei der Suche auf der RFC-Editor-Seite im rechten Bereich des Suchergebnisses einen Eintrag »Obsoleted by«, ist das betreffende RFC-Dokument mittlerweile durch ein neues ersetzt worden. Im RFC 2800 (zum Zeitpunkt der Drucklegung dieses Buchs aktuell) finden Sie eine Übersicht über die RFCs, die die Internetprotokolle betreffen.
2.2 Die IP-Adresse Jeder Rechner, der an das Internet oder an ein Intranet angeschlossen ist, besitzt eine eindeutige IP-Adresse. Eigentlich handelt es sich dabei um eine einfache 32-Bit-Zahl. IP-Adressen werden aber an sich immer so dargestellt, dass die einzelnen Bytes dieser Zahl durch Punkte getrennt angegeben werden. Abweichend von der normalen Zahl-Darstellung wird das erste Byte aber links angegeben (das erste Byte ist normalerweise das kleinere und müsste folglich rechts angegeben werden). Eine typische IP-Adresse wird z.B. so dargestellt: 128.66.12.1. Dieses Beispiel steht für die Zahl 17580672. Wenn Sie einmal eine als Zahl dargestellte IP-Adresse umrechnen wollen (oder müssen), konvertieren Sie die Zahl in einen Hexadezimalwert (für das Beispiel: 10C428016), drehen die einzelnen Bytes um (8016.4216.0C16.0116) und rechnen diese in Dezimalwerte zurück. Ein Teil der Adresse adressiert das lokale Netzwerk, an das der Rechner angeschlossen ist. Der andere Teil adressiert den Rechner. Im Beispiel steht 128.66 für das Netzwerk und 12.1 für den Rechner. 2.2.1 Die Klasseneinteilung der IP-Adressen IP-Adressen werden in verschiedene Klassen eingeteilt. Abhängig von der Klasse der Adresse werden unterschiedliche Teile als Netzwerkbzw. Rechneradresse gewertet. Welcher Klasse einer IP-Adresse angehört, wird an den ersten Bits erkannt:
50
2.2 Die IP-Adresse
Basiswissen
Ist das linke Bit einer IP-Adresse 0, handelt es sich um eine KlasseA-Adresse. Da es sich bei diesem Bit eigentlich um Bit 8 des linken Byte handelt, kann man auch sagen, dass ein Wert kleiner als 128 im linken Byte eine solche Adresse bezeichnet. Bei einer Klasse-AAdresse steht das linke Bit für die Klasse, die nächsten sieben Bit identifizieren das Netzwerk. Die restlichen drei Bytes bezeichnen einen Rechner im lokalen Netz. Damit existieren maximal 127 Klasse-A-Adressen, die jedoch jede für sich Millionen von angeschlossenen Rechnern adressieren können. Wenn das linke Bit einer IP-Adresse gesetzt ist und das folgende nicht, handelt es sich um eine Klasse-B-Adresse. Alternativ kann man sagen, dass ein Wert von 128 bis 191 im linken Byte eine solche Adresse kennzeichnet. Bei einer Klasse-B-Adresse stehen die linken zwei Bit für die Klasse, die nächsten 14 Bit bezeichnen das Netzwerk, die restlichen 16 Bit den Rechner. Damit sind 16.383 Netze der Klasse B möglich, die jedes für sich 65.535 Rechner enthalten können. Wenn die linken drei Bit einer Adresse 110 sind (bzw. wenn im linken Byte ein Wert von 192 bis 223 gespeichert ist), handelt es sich um eine Klasse-C-Adresse. Bei einer solchen Adresse bestimmen die linken drei Bit die Klasse, die folgenden 21 Bit das Netzwerk und die restlichen acht Bit den Rechner. Damit sind 2.097.152 Klasse-C-Netze möglich, die jedes für sich maximal 255 Rechner enthalten können. Adressen, die mit 111 beginnen (bzw. im linken Byte einen Wert größer als 223 speichern), sind für besondere Zwecke reserviert und gehören zu keinem speziellen Netzwerk. Momentan werden in diesem Bereich so genannte Multicast-Adressen verwaltet. Über diese Adressen können Gruppen von Computern adressiert werden, die ein gemeinsames Protokoll benutzen. Ein besonderer Klasse-C-Adressbereich ist 192.168.0.x. Diese Adressen sind für private Zwecke reserviert und werden im Internet nicht verwendet. Wenn Sie ein einfaches privates Intranet aufbauen, können Sie den Bereich dieser Adresse sehr gut nutzen, um Ihren Rechnern festen Adressen zuzuteilen und dabei nicht mit Adressen im Internet in Konflikt zu geraten, wenn die Rechner mit dem Internet verbunden sind. Die Adresse 127.0.0.1 bezeichnet immer das lokale System. Für diese Adresse existiert immer auch der Alias localhost. 51
Basiswissen
2.2.2 Subnetze und Subnetzmasken Ein Subnetz ist ein Teil eines Netzes, der nur über einen Router oder ähnliche Rechner Verbindung mit den Rechnern anderer Subnetze besitzt und der über einen eigenen Bereich von IP-Adressen adressiert wird. Subnetze werden häufig eingesetzt, um die Administration eines großen Netzes zu verteilen oder um Teile eines Gesamtnetzes (aus Sicherheitsgründen) von anderen abzuschotten. Um diese Subnetze zu adressieren, kann der Administrator Teile der Bits verwenden, die in einer IP-Adresse zur Rechneridentifikation gedacht sind. Dazu werden so genannte Subnetzmasken verwendet. Klasse-B-Netzwerke arbeiten beispielsweise normalerweise mit der Subnetzmaske 255.255.0.0. Eine Subnetzmaske wird so interpretiert, dass jedes gesetzte Bit in der Maske die Netzwerkadresse identifiziert. Im Beispiel stehen also die Bits der zwei linken Bytes für die Netzwerkadresse (was bei einer Klasse-BAdresse ja normal ist). Der Router, der das Netzwerk mit der Außenwelt verbindet, arbeitet mit genau dieser Subnetzmaske. Enthält das Netzwerk Subnetze, arbeiten die internen Router häufig mit der Subnetzmaske 255.255.255.0. Das bedeutet nun, dass das dritte Byte zur Adressierung des Subnetzes verwendet wird (die ersten beiden stehen ja für das Hauptnetz). Das rechte Byte adressiert dann innerhalb des Subnetzes die angeschlossenen Rechner. In einem solchen Netz können also maximal 255 Subnetze existieren, die jedes für sich maximal 255 Rechner enthalten können. Subnetzmasken müssen nicht wie im Beispiel ein komplettes Byte einbeziehen. Wenn weniger Subnetze vorhanden sind, die mehr Rechner adressieren sollen, kann der Administrator auch weniger Bits des Rechnerteils der IP-Adresse für die Adressierung der Subnetze verwenden. Bei einer Klasse-C-Adresse erlaubt die Subnetzmaske 255.255.255.192 beispielsweise vier Subnetze mit jeweils 64 angeschlossenen Rechnern. Nur damit Sie diese Rechnung verstehen: 192 entspricht binär 110000002. Die linken zwei Bit werden also für die Adressierung des Subnetzes verwendet, und daraus ergeben sich eben genau vier Möglichkeiten. Die restlichen sechs Bit gehören zur Rechneradresse, und das ergibt 64 Möglichkeiten.
52
2.2 Die IP-Adresse
Basiswissen
2.2.3 Vergabe von IP-Adressen In einem Intranet ohne direkten Zugang zum Internet können Sie IP-Adressen ohne weiteres frei vergeben. Sie müssen lediglich darauf achten, dass der Typ der Adresse (Klasse A, B oder C) und die Subnetzmaske gleich ist. Ist einer der Rechner indirekt (z.B. über eine ISDNKarte oder ein DSL-Modem) an das Internet angeschlossen, sollten Sie den Adressbereich für private Adressen (192.168.0.0 bis 192.168.0.255) verwenden, um keine Konflikte mit vorhandenen IP-Adressen zu verursachen. Für Rechner, die direkt am Internet angeschlossen sind, müssen Sie eine IP-Adresse beziehen. Privatpersonen oder kleinere Firmen erhalten diese über den Internet-Service-Provider. Größere Firmen und Internet-Service-Provider beziehen ganze Adressbereiche von einem der drei Internet-Registrations-Unternehmen. Für Europa ist das die Firma RIPE NCC (www.ripe.net). Für den amerikanischen Raum ist das Unternehmen ARIN zuständig, für den asiatischen Raum die Firma APNIC. IP-Adressen müssen eben weltweit eindeutig sein. Rechner, die über einen Router oder Provider an das Internet angeschlossen sind, werden auch häufig dynamisch über einen DHCP-Server mit einer IP-Adresse versorgt. Das ist beispielsweise der Fall, wenn Sie Ihren Rechner mit dem PoP des Providers über das DFÜ-Netzwerk von Windows verbinden. Ein DHCP-Server beim Provider ermittelt eine freie IP-Adresse in dem (Sub-)Netzbereich des Providers und übermittelt diese an Ihren Rechner. Dieser ist dann unter zwei IPAdressen erreichbar: Im lokalen Netz unter seiner privaten und über das Internet über die zugeteilte Adresse. Die IP-Adressen Ihres Rechners können Sie übrigens über das Programm ipconfig abfragen.
2.3 IP-Namensauflösung Zur Kommunikation in einem IP-Netz werden ausschließlich die IPAdressen der angeschlossenen Rechner verwendet. Wie Sie ja sicher schon wissen, können Sie zur Adressierung eines Rechners aber auch den Rechnernamen eingeben. In der einfachsten Form ist dies in einem Intranet der bloße Name des Rechners. In komplexen Intranets und im Inter-
53
Basiswissen
net werden zudem Domänen2 verwendet, die die Identifikation der Rechner vereinfachen. Bei dem Namen »www.addison-wesley.de« steht »de« beispielsweise für die Top-Level-Domäne de (Deutschland), »addison-wesley« bezeichnet die untergeordnete Domäne von Addison-Wesley und »www« ist der Name des Rechners, der in dieser Domäne verwaltet wird. Domänennamen werden von NICs (Network Information Centers) vergeben und verwaltet. Für Deutschland ist das DE-NIC zuständig (www.nic.de). Wenn Sie nun einen solchen Rechnernamen in einem Programm verwenden, das eine IP-Kommunikation aufbaut, muss der Name in die passende IP-Adresse umgewandelt werden. Dazu stehen zwei Verfahren zur Verfügung. Bei der einfachen und älteren Variante verwaltet eine spezielle Tabelle im Rechner die Namen der benachbarten Rechner mit deren IP-Adresse. Dies ist häufig auf Unix-Systemen üblich. Windows-Rechner fragen in einem einfachen Internet die IP-Adressen allerdings ganz einfach über das normale Windows-Netzwerk ab. Das neuere Verfahren nutzt eine verteilte Datenbank, den so genannten Domain Name Service (DNS). 2.3.1 Der Name-Service in lokalen Netzen Für die Adressierung einzelner Rechner in lokalen Netzen können Sie meist problemlos deren Namen an Stelle der IP-Adresse verwenden. Eine Webserver im Intranet sprechen Sie im Webbrowser z.B. einfach mit »http://Zaphod« an (wobei »Zaphod« der Rechnername ist). Für den lokalen Webserver können Sie auch den Alias localhost verwenden: »http://localhost«. Das System setzt den angegebenen Namen automatisch in die IP-Adresse des angesprochenen Rechners um. Wird der lokale Rechner angesprochen, setzt das System den Rechnernamen (bzw. localhost) immer in die Adresse 127.0.0.1 um. Werden entfernte Rechner angesprochen, fordert das System in einfachen Windows-Netzen die IP-Adresse über das Windows-Netzwerk direkt vom anderen Rechner an. Auf kleineren Unix-Systemen wird 2 Der Begriff »Domäne« (englisch »Domain« = »Gebiet«) bezeichnet im Allgemeinen einen Wissens- oder einen Steuerungsbereich. Im Internet ist mit einer Domäne ein Bereich von IPAdressen gemeint, die geographisch oder thematisch zusammengehören. Auf Windows-Systemen ist eine Domäne zudem eine Gruppierung von Computern und Betriebsmitteln, die zu einer Gruppe Benutzer gehören.
54
2.3 IP-Namensauflösung
Basiswissen
zur Ermittlung der IP-Adresse allerdings häufig eine Rechnertabelle verwendet, die in Form einer einfachen Textdatei meist im Ordner etc/ hosts gespeichert ist. Diese Tabelle enthält die einzelnen Rechnernamen und die dazugehörigen IP-Adressen. Größere Netze nutzen dagegen meist einen Nameserver, der die Rechnernamen der angeschlossenen Netze mit den IP-Adressen verbindet. Auf Windows-Systemen ist dies der WINS (Microsoft Windows Name Service), der auf den Server-Versionen von NT, 2000 und XP läuft. Nameserver sind in der Lage, sich mit benachbarten Nameservern auszutauschen, so dass ein Nameserver auch die IP-Adressen der Rechner benachbarter (Sub-)Netzwerke kennt. Außerdem ist die Administration gegenüber einer Rechnertabelle (die normalerweise manuell gepflegt wird) erheblich vereinfacht. 2.3.2 Der Domain Name Service (DNS) Genau wie im Intranet sind IP-Adressen auch im Internet sehr unhandlich. Deshalb werden im Internet (und in größeren Intranets) Namen verwendet, die in Domänen eingeteilt sind. Der Name www.addison-wesley.de besteht beispielsweise aus der Toplevel-Domäne de, der die Subdomäne addison-wesley untergeordnet ist, und dem in dieser Domäne verwalteten Rechner www. Toplevel-Domänen bezeichnen einzelne Länder (wie de für Deutschland oder at für Österreich) oder Geschäftsbereiche (wie com für kommerzielle Unternehmen, edu für Ausbildungsunternehmen und net für Netzwerkdienste). Mittlerweile sind die Toplevel-Domänen aber etwas durcheinander geraten, da prinzipiell jeder eine Internetadresse in einer nahezu beliebigen Toplevel-Domäne beantragen kann. Subdomänen bezeichnen das Zielnetzwerk, an das der adressierte Rechner angeschlossen ist. Für größere Netzwerke können innerhalb einer Subdomäne noch weitere Subdomänen verwaltet werden. Der Webserver im Netzwerk des »Marshal Space Flight Center« der Nasa wird z.B. mit www.msfc.nasa.gov adressiert. msfc steht hier für eine untergeordnete Subdomäne, die ein Teil der NASA-Subdomäne ist. Innerhalb der untergeordneten Domäne kann eine Firma oder eine Privatperson nahezu beliebig viele Rechner verwalten. Die meisten Rechner für das World Wide Web heißen wohl www, die für den FTP55
Basiswissen
Dienst werden meist ftp genannt. Prinzipiell sind aber auch alle anderen denkbaren (und gültigen Namen) möglich. Der Suchserver von Microsoft (search.micosoft.com) oder der Übersetzungsdienst von Altavista (babelfish.altavista.com) sind Beispiele dafür. Diese Domänennamen müssen, wie auch im Intranet, vor dem Senden von Daten in die IP-Adresse des Zielrechners umgewandelt werden. Dazu werden DNS-Server verwendet. Ein DNS-Server verwaltet in einer Datenbank Rechnernamen und die zugehörigen IP-Adressen. Erhält ein DNS-Server eine Anfrage nach einem unbekannten Namen, leitet er diese an einen so genannten autorativen DNS-Server weiter. Ein autorativer DNS-Server ist ein beliebiger DNS-Server, der Informationen für die Subdomäne bereithält. Kennt der autorative DNSServer den angeforderten Namen nicht, leitet er die Anfrage an einen Primary-Nameserver weiter, der Informationen für die ToplevelDomäne bereithält. Der Primary-Nameserver für die de-Domäne läuft beispielsweise beim DE-NIC. Der Primary-Nameserver kennt zwar nicht die Namen der einzelnen Rechner der Toplevel-Domäne, aber er kennt die dieser Domäne untergeordneten autorativen Server. Er leitet die Anfrage deshalb einfach an den nächsten passenden autorativen DNS-Server weiter. Kann der autorative DNS-Server den Namen auflösen, sendet er das Ergebnis zum ursprünglich angesprochenen DNS-Server zurück. Dieser speichert den Namen und die IP-Adresse dann in seinem Cache, damit zukünftige Anfragen direkt beantwortet werden können, und sendet das Ergebnis zu der IP-Adresse, die die Anfrage gestartet hat. Kann ein Name nicht aufgelöst werden, antwortet ein DNS-Server einfach nicht. Die Internetanwendung meldet dann nach einer gewissen Wartezeit, dass der Name nicht aufgelöst werden kann (was auch eine Falschmeldung sein kann, nämlich dann, wenn die Anfrage sehr viel Zeit in Anspruch nimmt). In der IP-Konfiguration eines Rechners ist entweder die Adresse eines DNS-Servers fest eingetragen oder festgelegt, dass diese Adresse bei der Einwahl in den PoP des Internet-Providers automatisch übertragen wird. Wenn beispielsweise ein Browser ein HTML-Dokument anfordert, kennt das System die IP-Adresse eines DNS-Servers, kann diesen nach der IP-Adresse des angegebenen Domänennamens fragen und die Anforderung dann an die so ermittelte IP-Adresse senden. 56
2.3 IP-Namensauflösung
Basiswissen
2.4 IP-Ports Im Internet oder in einem Intranet laufen die verschiedensten Dienste. Die bekanntesten sind wohl der WWW-, der FTP- und der SMTPDienst. Diese Dienste können über separate Serveranwendungen auf einem einzelnen Rechner ausgeführt werden, was beispielsweise unter Windows der Fall ist, wenn Sie den IIS installieren. Aber auch spezielle Server oder Anwendungen, die über das Internet mit anderen Anwendungen kommunizieren, müssen auf einem Rechner identifiziert werden. Genau dazu werden die so genannten IP-Ports verwendet. Ein Port ist einfach eine 16-Bit-Dezimalzahl, die einen Dienst oder ein Programm identifiziert. Es gibt reservierte, so genannte »Well Known Ports«, mit einer Nummer unterhalb von 256. Diese Ports adressieren bekannte Dienste wie WWW, SMTP und FTP. Der WWW-Dienst eines Webservers wird z.B. immer über den Port 80 adressiert, der FTP-Dienst verwendet den Port 21. Wenn Sie in einem Webbrowser eine Webadresse wie z.B. http:// www.addison-wesley.de eingeben, erweitert Ihr Webbrowser diese Adresse implizit um den Port des angesprochenen Dienstes: http://www.addisonwesley.de:80. Der Port wird über einen Doppelpunkt von der eigentlichen Adresse getrennt. Der Browser erkennt den anzusprechenden Port übrigens mehr oder weniger automatisch am eingegebenen Namen. Beginnt der Name mit »ftp://« oder »ftp.«, würde er den Port 21 anhängen. Beim Senden der Daten wird der Zielport als Teilinformation eines Datenpakets mitgesendet. Daraus erkennt das Betriebssystem auf dem Server, welcher Dienst anzusprechen ist. Beim Port 80 übergibt das Betriebssystem beispielsweise die eingegangenen Daten an den Webserver, der diesen Port für sich beim Betriebssystem reserviert hat. Ports mit einer Nummer größer als 255 können beliebig verwendet werden3. Programme und Dienste, die nicht zum Internet-Standard gehören, wie z.B. der Microsoft SQL Server, nutzen eine solche freie Portnummer.
3 Früher waren noch die Ports 256 bis 1024 für UNIX-typische Dienste reserviert, was aber heute nicht mehr gilt.
57
Basiswissen
Aber nicht nur Server-, sondern auch Clientanwendungen werden über einen Port identifiziert. Der Server muss angeforderte Daten ja schließlich zum Client zurücksenden. Dazu reserviert sich jede Clientanwendung beim Start normalerweise eine dynamisch vergebene, freie Portnummer. Beim Senden einer Anforderung wird – wie Sie beim UDP- und beim TCP-Protokoll ab Seite 64 noch sehen – auch diese Portnummer mit im Datenpaket übertragen, sodass der Server weiß, wohin er die angeforderten Daten senden soll. Deshalb ist es auch problemlos möglich, mit mehreren Instanzen eines Webbrowsers gleichzeitig zu arbeiten, ohne dass diese durcheinander geraten.
2.5 URIs, URLs und URNs URIs (Uniform Resource Identifier) werden im Internet zur Identifikation von Ressourcen verwendet. Eine Ressource ist im Allgemeinen etwas, das unter einem eindeutigen Namen angesprochen werden kann, beispielsweise eine Mailbox, eine Datei, ein Dienst oder ein Programm. URIs beschreiben typischerweise den Mechanismus, der verwendet wird, um die Ressource anzusprechen (z.B. http:// für das HTTP-Protokoll), den Namen des Computers, der die Ressource verwaltet, und den Namen der Ressource selbst. Das Schema einer URI ist für die einzelnen Protokolle festgelegt und weicht auch manchmal vom allgemeinen Schema ab. Die Adresse eines HTML-Dokuments sieht beispielsweise so aus: http://www.boarder-magazin.de/index.htm
Eine über FTP erreichbare Datei wird prinzipiell identisch adressiert: ftp://ftp.is.co.za/rfc/rfc1808.txt
Die URI zur Adressierung einer Mailbox oder einer Newsgroup sieht aber etwas anders aus: mailto:
[email protected] news:microsoft.public.dotnet.languages.csharp
58
2.5 URIs, URLs und URNs
Basiswissen
URLs (Uniform Resource Locator) sind eine Unterordnung von URIs, die eine Datei bezeichnen, die über das Internet angesprochen werden kann. URLs sind Strings, die das zu verwendende Protokoll, die Adresse oder den Domänennamen und optional den Port, den Pfad und Argumente angeben: Protokoll://Host[:Port][Absoluter Pfad[?Argumente]] Für das HTTP-Protokoll sieht eine URL beispielsweise so aus: http://www.boarder-magazin.de/index.htm http://www.boarder-magazin.de:80/index.htm http://www.microsoft.com/Data http://search.microsoft.com?siteid=us/dev
Falls beim HTTP-Protokoll Argumente angegeben sind, werden diese meist von ASP(.NET)- oder CGI-Programmen ausgewertet. Falls bei einer URL keine Angabe der anzusprechenden Ressource (Datei) erfolgt, verwendet der Server die Ressource, die als Standard für den angesprochenen Ort eingestellt ist. URNs sind eine andere Art URIs, die Ressourcen bezeichnen, deren Ort unbestimmt ist, die aber über spezielle Dienste identifiziert werden können. Eine E-Mail-Adresse ist ein bekanntes Beispiel für eine URN. Der Speicherort der damit angesprochenen Mailbox kann nahezu beliebig (innerhalb der Domäne der Adresse) wechseln, die Adresse bleibt aber immer dieselbe. URNs garantieren Eindeutigkeit und eine endlose Lebenszeit.
2.6 Internet-Medientypen und MIME Wenn Programmierer über das Internet reden, fällt häufig der Begriff Medientyp bzw. MIME. Auch in diesem Buch kommt der Begriff MIME häufiger vor. Deshalb sollten Sie wissen, worum es sich dabei handelt. Mime ist die Kurzform für »Multipurpose Internet Mail Extensions« (Mehrzweck-Internet-Mail-Erweiterungen). MIME ist von der ursprünglichen Bedeutung her ein Standard, der es ermöglicht, dass
59
Basiswissen
E-Mails und angehängte (oft binäre) Daten in einer Datei gemeinsam versendet werden können. Um dem Empfänger den Typ der angehängten Daten mitteilen zu können (damit dieser die Daten korrekt interpretieren kann), werden spezielle Medientyp-Bezeichnungen verwendet. Der Typ text/html spezifiziert beispielsweise ein HTML-Dokument. Da sich herausgestellt hat, dass diese Medientypen auch zu anderen Zwecken im Internet nützlich sind, werden MIME-Typen mittlerweile sehr häufig zur Spezifizierung des Typs gesendeter Daten verwendet. Wenn ein Medientyp gemeint ist, müsste eigentlich auch der (allgemeinere) Begriff »Medientyp« verwendet werden, was auch in den offiziellen Schriften der Fall ist. Im normalen Sprachgebrauch wird aber meist der Begriff »MIME-Typ« verwendet. Eine Medientyp-Angabe besteht aus einem Haupttyp, einem optionalen Untertyp und einer ebenfalls optionalen Angabe des Zeichensatzes, falls es sich um einen Texttyp handelt. Haupttyp und Untertyp werden durch einen Schrägstrich getrennt angegeben. Tabelle 2.1 listet die zurzeit existierenden Medien-Haupttypen auf. Tabelle 2.1: Die MedienHaupttypen
Medien-Haupttyp
spezifiziert
text
Textdaten
image
Grafikdateien
video
Videodateien
audio
Sounddateien
application
Dateien, die an ein bestimmtes Programm gebunden sind
multipart
mehrteilige Daten
message
Nachrichten
model
Dateien, die mehrdimensionale Strukturen repräsentieren
Für jeden Haupttyp existieren meist recht viele Untertypen. Der textTyp wird beispielsweise in comma-separated-values, css, html, javascript, plain und andere Untertypen unterteilt. Tabelle 2.2 gibt eine Übersicht über die wichtigsten Medientypen.
60
2.6 Internet-Medientypen und MIME
Basiswissen
Medientyp
Bedeutung
application/zip
zip-Archivdateien
application/rtf
RTF-Dateien
application/msword
Word-Dateien
application/msexcel
Excel-Dateien
application/pdf
PDF-Dateien
audio/basic
Basis-Audiodateien (.au, .snd)
audio/x-midi
Midi-Dateien
audio/x-mpeg
MPEG 2-Dateien (.mp2)
audio/x-pn-realaudio
Real-Audio-Dateien (.ram, .ra)
audio/x-wav
Wav-Dateien (.wav)
image/gif
Bilddateien im GIF-Format (.gif)
image/jpeg
Bilddateien im JPEG-Format (.jpg, .jpeg, .jpe)
image/tiff
Bilddateien im TIFF-Format (.tiff, .tif)
message/http
Mail im HTTP-Format
message/news
Mail im Format für Newsgroups
multipart/byteranges
Mehrteilige (Mail-)Nachricht im Byte-Format
multipart/encrypted
Mehrteilige, verschlüsselte (Mail-)Nachricht
multipart/form-data
Mehrteilige HTTP-Nachricht mit den Daten eines HTML-Formulars
text/comma-separated-values
Textdatei mit kommabegrenzten Feldwerten (.csv)
text/css
Cascading Style Sheets-Datei (.css)
text/html
HTML-Datei (.htm, .html)
text/javascript
JavaScript-Datei (.js)
text/plain
Normale Textdatei (.txt)
text/richtext
RTF-Datei (.rtf)
text/tab-separated-values
Textdatei mit durch Tabulatoren begrenzten Feldwerten (.tsv)
video/mpeg
Videodateien im MPEG-Format (.mpeg, .mpg, .mpe)
Tabelle 2.2: Die wichtigsten Medientypen
61
Basiswissen
Tabelle 2.2: Die wichtigsten Medientypen (Forts.)
Medientyp
Bedeutung
video/x-msvideo
Videodateien im AVI-Format (.avi)
x-world/x-vrml
VRML-Dateien (.wrl)
Eine vollständigere Liste finden Sie unter selfhtml.teamone.de/diverses/ mimetypen.htm. Auf www.isi.edu/in-notes/iana/assignments/media-types finden Sie ein offizielles Verzeichnis der Medientypen. Das MIMEFormat wird in den RFCs 2045, 2046 und 2077 beschrieben. Für Texttypen kann zusätzlich der Zeichensatz angegeben werden, wenn die Textdaten in einem anderen als dem ISO-8859-1-Zeichensatz codiert sind. Dazu wird das charset-Attribut durch ein Semikolon getrennt an den Medientyp angehängt: text/plain; charset=ISO-8859-2
2.7 Einfache Internetprotokolle Das Internet basiert auf der untersten Ebene auf dem IP-Protokoll. Alle Daten werden über dieses Protokoll versendet. Die Protokolle UDP und TCP setzen auf IP auf und erweitern diese Protokolle um wichtige Features, wie beispielsweise Angaben zum Port des Clients und zum Zielport. Höhere Protokolle, wie z.B. HTTP und SMTP, basieren auf UDP oder TCP. Die folgenden Abschnitte beschreiben die niedrigen Protokolle, die höheren werden ab Seite 67 beschrieben. 2.7.1 Das IP-Protokoll Damit die Kommunikation zwischen den verschiedensten Rechnern im Internet überhaupt funktioniert, werden Daten im Internet über das standardisierte IP4-Protokoll versendet. Eine ausführliche Beschreibung dieses Protokolls finden Sie im RFC 791.
4 Internet Protocol
62
2.7 Einfache Internetprotokolle
Basiswissen
Beim IP-Protokoll werden die zu sendenden Daten in Pakete, die so genannten Datagramme, verpackt und mit zusätzlichen Informationen versehen. Ein Datagramm besteht aus einem Header, der Kontrollinformationen und die Ursprungs- und Zieladresse speichert, und aus den zu versendenden Daten. Die Größe des Headers kann fünf oder sechs 32-Bit-Wörter betragen, die aus einzelnen Feldern bestehen. Das sechste Wort ist optional. Deswegen verwaltet das Feld IHL (Internet Header Length) die aktuelle Größe des Headers. Abbildung 2.1 zeigt die schematische Darstellung eines IP-Datagramms.
Version
IHL
Dienst-Typ
Gesamtlänge Flags
Identifikation Time to Live
Protokoll
Fragmentierungs-Offset
Header-Prüfsumme
Ursprungsadresse Zieladresse Padding
Optionen Beginn des Datenbereichs
... ...
}
Abbildung 2.1: Format von IPDatagrammen Header
Anhand der Zieladresse im Header können Router die einzelnen Datagramme durch das Internet leiten. Die Ursprungsadresse wird vom empfangenden Dienst dazu verwendet, eventuelle Ergebnisdaten zurückzusenden. Beim Senden der Daten kommt es vor, dass einzelne Datagramme für ein Netzwerk, das dieses Datagramm übertragen soll, zu groß sind. Die recht großen Pakete eines Ethernet-Netzes können in einem X.25-Netz beispielsweise nicht direkt versendet werden. Der Gateway dieses Netzwerks teilt die Datagramme dann in kleinere Fragmente auf. Diese Fragmente besitzen dasselbe Format wie das gesamte Datagramm. Das Feld Identifikation speichert dann eine Information, zu welchem Datagramm das Fragment gehört, im Feld Fragmentierungs-Offset wird die Position des Fragments im Datagramm verwaltet. Im Feld Flags verwaltet IP eine Information darüber, ob ein Fragment das letzte eines Datagramms ist. Damit Datenpakete nicht endlos durch das Internet geroutet werden, wenn der Empfänger nicht erreichbar ist, verwaltet das Feld Time to Live einen Wert, der von jedem Router um den Wert 1 reduziert wird. 63
Basiswissen
Erkennt ein Router, dass dieser Wert 0 ist, verwirft er das Paket einfach. Die Anwendung, die das Datenpaket gesendet hat, erkennt nach einer gewissen Zeit an der fehlenden Antwort, dass der Empfänger nicht verfügbar ist. Das IP-Protokoll besitzt keine Möglichkeit, die Zustellung der Daten zu garantieren, empfangene Daten auf Fehler zu überprüfen (lediglich die Header-Informationen können an Hand der Header-Prüfsumme überprüft werden) und Prozesse auf dem Zielrechner zu adressieren. Die maximale Länge der Daten ist zudem beschränkt, weil ein Datagramm nur eine für das Quellnetzwerk maximale Größe annehmen kann. Eine Einschränkung entsteht daraus aber nicht, weil das IP-Protokoll lediglich auf der untersten Ebene arbeitet. Programme, die über das Internet kommunizieren wollen, können IP nicht direkt nutzen. Dazu stehen die höheren Protokolle UDP und TCP zur Verfügung, die die Einschränkungen von IP aufheben. 2.7.2 Das UDP-Protokoll Das UDP-Protokoll (User Datagram Protocol) setzt auf dem IP-Protokoll auf und erweitert dieses um Informationen über den Ursprungs- und den Zielport, um eine weitere Prüfsumme und um eine Längenangabe für die eigentlichen Daten. Diese Informationen werden in den ersten beiden 32-Bit-Wörtern des IP-Datenbereichs gespeichert. Abbildung 2.2 stellt diesen Bereich dar (ohne die Header-Felder des IP-Datagramms). Abbildung 2.2: Format von UDP (ohne IPHeaderfelder)
Ursprungsport
Zielport
Länge
Prüfsumme Beginn des Datenbereichs
...
}
IPDatenbereich
Mit UDP kann ein Programm Daten an einen definierten Zielport senden und den eigenen Port für die Antwort zum Server übermitteln. Eine Überprüfung der Daten auf das korrekte Versenden ist nicht möglich. Die Prüfsumme im UDP-Header wird lediglich zur Kontrolle der Headerinformationen verwendet. Zudem ist die Größe der Daten auf die Maximalgröße der Datagramme beschränkt. UDP sendet die Daten ohne zu
64
2.7 Einfache Internetprotokolle
Basiswissen
überprüfen, ob der Empfänger überhaupt zum Empfang bereit ist. Ist der Empfänger nicht bereit, gehen die gesendeten Daten einfach verloren. UDP wird für eine performante Übertragung geringer Datenmengen verwendet. Um eine relative Übertragungssicherheit zu ermöglichen, geben Serverdienste, die UDP verwenden, möglichst immer eine Antwort auf eine Anforderung. Geht eine Antwort beim Client ein, wird das als erfolgreiche Übertragung gewertet. Geht keine Antwort ein, wird die Anforderung einfach erneut gesendet. Das bei TCP wesentlich aufwändigere Verpacken der Daten und der Verbindungsaufbau vor dem Senden erfordern oft mehr Verwaltungsaufwand als das eventuell erneute Senden bei UDP. Eine Beschreibung des UDP-Protokolls finden Sie im RFC 768. 2.7.3 Das TCP-Protokoll Das wesentlich häufiger verwendete TCP-Protokoll (Transmission Control Protocol) setzt wie UDP ebenfalls auf IP auf. TCP erweitert IP um:
eine Überprüfung der Bereitschaft des Empfängers, eine Überprüfung der empfangenen Daten über eine Prüfsumme, eine relativ sichere Zustellung der Daten und die Fähigkeit, große Datenmengen in mehrere Pakete aufgeteilt zu versenden.
Wie bei UDP verwaltet das TCP-Protokoll zusätzliche Headerinformationen im IP-Datenbereich (Abbildung 2.3). Die zu sendenden Daten werden in kleine Pakete aufgeteilt. Jedes Paket erhält eine Sequenznummer. Der Empfänger kann die einkommenden Pakete an Hand dieser Nummer in der richtigen Reihenfolge zusammensetzen. Da jedes Paket zusätzlich mit einer Prüfsumme versehen ist, kann der Empfänger überprüfen, ob die Daten auf ihrem Weg eventuell beschädigt wurden. Erhält der Empfänger beschädigte Pakete oder sind Pakete verloren gegangen, fordert er die fehlenden Pakete einfach nach einer gewissen Zeit vom Sender nach. Damit ist die Zustellung von Daten bei TCP sehr sicher.
65
Basiswissen
Abbildung 2.3: Format des TCP-Protokolls
Ursprungsport
Zielport Sequenznummer Quittierungs-Nummer
Offset
Reserviert
Flags
Dringlichkeitszeiger
Prüfsumme
Padding
Optionen
}} TCPHeader
Fenster
Beginn des Datenbereichs
... ...
IPDatenbereich
Um das Versenden der Daten von Rechner A zu Rechner B zu garantieren, baut TCP zunächst eine Verbindung zum Zielrechner auf. Dazu sendet TCP zunächst ein Segment an Rechner B mit der Aufforderung, die Sequenznummern der folgenden Pakete zu synchronisieren. Der Zielrechner erkennt dies als Verbindungswunsch, liest die Startsequenznummer5 des Senders aus und antwortet mit einem Segment, in dem er Rechner A die Startsequenznummer der Pakete seiner potenziellen Antwort mitteilt. Daran erkennt Rechner A die Bereitschaft und beginnt mit dem Senden der Daten. Die einzelnen Pakete können dabei in loser Reihenfolge versendet werden und gehen häufig unterschiedliche Wege durchs Internet. Die Reihenfolge, in der die Pakete ankommen, ist also nicht festgelegt. An Hand der Sequenznummern kann der Zielrechner die Daten korrekt zusammensetzen. Nach dem Senden der Daten sendet TCP ein Segment mit der Information, dass keine Daten mehr folgen. Der Zielrechner sendet daraufhin das Ergebnis der Anforderung (vielleicht eine HTML-Seite), natürlich wieder in Pakete unterteilt. Danach wird die Verbindung mit dem Senden eines Segments abgeschlossen, das wiederum Rechner A mitteilt, dass keine Daten mehr folgen. Dieser Handshake zwischen den beteiligten Rechnern sichert ab, dass die Daten beim jeweiligen Zielrechner ankommen. Wie bei UDP wird der Dienst des Zielrechners und die Anwendung auf dem Client über Ports adressiert, die den TCP-Paketen beigefügt werden. Das TCP-Protokoll wird im RFC 793 beschrieben. 5 Die Startsequenznummer wird von TCP nicht vorgeschrieben, ist aber meistens die 1.
66
2.7 Einfache Internetprotokolle
Basiswissen
2.8 Socket-Dienste Auf Rechnern, die an das Internet oder an ein Intranet angeschlossen sind, sorgen so genannte Socket-Dienste dafür, dass die zu sendenden und empfangenen Daten entsprechend dem TCP- bzw. dem UDPProtokoll verarbeitet werden. Socket heißt übersetzt »Steckdose«. Ein Socket verbindet wie eine Steckdose zwei entfernte Geräte miteinander, ohne dass man wissen muss, wie die Verbindung physikalisch realisiert wird. Unter Windows übernimmt diese Aufgabe die Winsock-Schnittstelle, die ein Teil des Windows-API6 ist. Diese Schnittstelle können Sie direkt über das Windows-API nutzen, was allerdings recht komplex ist. Einfacher ist die Verwendung von speziellen Komponenten, die meist Bestandteil einer Programmiersprache sind. Unter Visual Basic 6 nutzen Sie dazu z.B. das Winsock-Steuerelement, unter C#, Visual Basic .NET und anderen .NET-Sprachen nutzen Sie die Socket-Klasse (die Sie im Namespace System.Net finden). Damit können Sie eigene Server und Clients entwickeln, die über TCP oder UDP miteinander kommunizieren, ohne sich mit den komplexe Protokollen auseinandersetzen zu müssen. Kapitel 16 beschreibt, wie Sie eine eigene TCP- oder UDPKommunikation aufbauen.
2.9 Die wichtigsten höheren Protokolle Neben den bereits beschriebenen Protokollen IP, TCP und UDP (ab Seite 62) werden im Internet noch einige weitere Protokolle verwendet. E-Mails werden z.B. über das SMTP-Protokoll versendet, HTML-Seiten über das HTTP-Protokoll. Die für Programmierer wichtigsten Protokolle beschreibe ich in den folgenden Abschnitten. Wenn Sie diese Protokolle grundlegend kennen, verstehen Sie die Kommunikation zwischen einem Internetserver und einem Client wesentlich besser und können sich viele Fragen selbst beantworten. Die Frage, wie ein Programm, das auf einem Webserver ausgeführt wird, den Browsertyp erkennt, ist beispielsweise damit beantwortet, dass der Browser Informationen über sich selbst im Header der HTTP-Nachricht mitsendet. 6 API = Application Programming Interface. Eine Schnittstelle zu den Funktionen einer Applikation, meist in Form von klassischen DLL-Datseien oder COM-Komponenten. Das Windows-API beinhaltet ca. 1000 Funktionen, die Programme nutzen können, um auf die Funktionalität von Windows zurückzugreifen.
67
Basiswissen
2.9.1 Tools zum Testen der Kommunikation Wenn Sie die IP-Protokolle einmal selbst erforschen oder ausprobieren wollen, helfen Ihnen wahrscheinlich die drei Tools, die ich hier kurz vorstelle: Ein Paket-Sniffer überwacht die Netzwerkkarte und zeigt die empfangenen und gesendeten IP-Pakete an, ein von mir entwickelter IP-Client (dessen Quellcode und Installationsversion Sie auf der BuchCD finden) hilft dabei, die höheren Protokolle einmal selbst auszuprobieren. Ein Paket-Sniffer zum Auslesen der IP-Kommunikation
Wenn Sie die IP-Protokolle selbst einmal erforschen wollen, empfiehlt sich ein Paket-Sniffer. Ein solches Tool fängt alle Pakete ab, die über das Netzwerk gesendet werden, und zeigt diese an. Ein hervorragender (weil sehr einfacher) Sniffer ist der Shareware-Sniffer von Ufasoft (www.ufasoft.com/sniffer). Die Shareware-Version läuft ohne Einschränkungen. Die Vollversion kostet 20 Dollar. Eine kostenfreie Alternative ist das Freeware-Tool Ethereal (www.ethereal.com/distribution/win32/). Dieser Sniffer besitzt mehr Möglichkeiten, ist jedoch auch komplizierter zu konfigurieren und besitzt keine Möglichkeit, die Pakete im Klartext anzuzeigen. Der Ufasoft-Sniffer ist einfach zu bedienen. Im Menü Tools / Select Adapter können Sie die Netzwerkkarte auswählen, die überwacht werden soll (falls mehrere installiert sind). Im linken oberen Fensterbereich stellen Sie ein, welche Protokolle überwacht werden sollen. Für die Überwachung der höheren Protokolle stellen Sie TCP ein. Wenn Sie den PACKETS-Eintrag im Protokoll-Ordner aktivieren, können Sie noch einstellen, ob alle oder nur bestimmte Ports überwacht werden sollen. Wenn Sie nur HTTP-Anforderungen und -Antworten überwachen wollen, sollten Sie hier nur den Port 80 einstellen. Ansonsten erhalten Sie viele Pakete, die uninteressant sind. Mit (F5) starten Sie die Überwachung. Die gesendeten und empfangenen Pakete können Sie anschauen, indem Sie im linken oberen Bereich auf das Protokoll klicken. Über das VIEW-Menü können Sie die Ansicht der Pakete zwischen Hex-Werten und Text umschalten. Beachten Sie, dass die mir vorliegende Version 3.0 Build 72 die eingefangenen Pakete nur dann direkt anzeigt, wenn Sie den Eintrag PAKETS aktivieren. Ist dieser Eintrag nicht aktiviert, werden die Pakete erst angezeigt, wenn Sie kurz ein anderes Protokoll und danach wieder das TCP-Protokoll auswählen. 68
2.9 Die wichtigsten höheren Protokolle
Basiswissen
Abbildung 2.4: Der Sniffer von Ufasoft
Ein Sniffer überwacht den Netzwerkverkehr auf der Netzwerkkarte. Deshalb funktioniert ein Sniffer nur dann korrekt, wenn er auf dem Server installiert ist und die Anfragen von einem anderen Rechner aus über das Netzwerk erfolgen. Wenn Sie z.B. einen Webbrowser auf demselben Rechner verwenden, dessen Netzwerkkarte der Sniffer überwacht (Sniffer können auch entfernte Netzwerkkarten überwachen), werden Sie kein Ergebnis erhalten. Die lokale Kommunikation läuft eben nicht über die Netzwerkkarte. Sie benötigen also zumindest zwei Rechner, um die Netzwerkpakete abfangen zu können (oder einen Trick, den ich allerdings nicht kenne ...). Ein simpler IP-Client für TCP und UDP
Um die Kommunikation mit Servern über die einfachen Protokolle TCP und UDP ausprobieren zu können, habe ich mit Visual Basic 6 und C# je einen einfachen IP-Client entwickelt. Sie finden diesen Client im Quellcode und als Setup auf der Buch-CD. Abbildung 2.5 zeigt eine HTTP-Sitzung mit meinem kleinen IP-Client.
69
Basiswissen
Abbildung 2.5: Der einfache IPClient mit einer ausgeführten HTTPAnforderung
2.9.2 Das HTTP-Protokoll Das HTTP-Protokoll wird für die Kommunikation mit Webservern verwendet. Über dieses Protokoll kann ein Client Daten vom Server abfragen, Prozesse starten und Dateien uploaden. Der Server sendet angeforderte Daten ebenfalls über HTTP zum Client. Das HTTP-Protokoll besteht aus einem HTTP-Header und einem optionalen Datenbereich. Der HTTP-Header enthält einen der möglichen HTTP-Befehle (Get, Put, Post etc.) mit Argumenten und weitere Daten, wie z.B. Informationen über den Browser. Der Datenbereich enthält das HTML-Dokument bzw. Daten, die der Client zum Server hochladen will. Das HTTP-Protokoll wird im RFC 2616 beschrieben. Eine HTTP-Sitzung besteht aus einem »Request« (der Anforderung) und einem »Response« (der Antwort). Ein Client fordert den Server in einem Request auf, entweder Ressourcen (HTML-Dokumente, Bilder etc.) zu liefern, Programme auszuführen oder Dateien hochzuladen. Der Server antwortet mit einem Response auf diese Anforderung. 70
2.9 Die wichtigsten höheren Protokolle
Basiswissen
Grundsätzlicher Aufbau einer HTTP-Nachricht
HTTP-Nachrichten bestehen aus einzelnen Zeilen, die jeweils mit einem Carriage Return/Linefeed (CRLF = Unicode-Zeichen 13 + 10) abgeschlossen sind. Die erste Zeile enthält einen HTTP-Befehl (der auch als »HTTP-Methode« bezeichnet wird). Danach folgen einzelne Felder, die zusätzliche Informationen zum Befehl liefern. Dieser HTTP-Header wird mit einer leeren Zeile abgeschlossen. Danach folgt der optionale Datenbereich. In diesem Bereich sendet der Server angeforderte Ressourcen, z.B. ein HTML-Dokument, zum Client. Bei einem Datei-Upload sendet der Client diese Datei ebenfalls im Datenbereich zum Server. Tabelle 2.3 beschreibt die wichtigsten Befehle der HTTP-Version 1.1. Eine vollständige Auflistung finden Sie in der Referenz. Methode
Bedeutung
GET URI HTTP-Version
Über GET fordert ein Client eine Ressource an, deren Adresse in einem URI angegeben ist. GET kann mit If-Feldern im Header erweitert werden. If-Modified-Since bewirkt beispielsweise, dass nur Dokumente abgerufen werden, die ein neueres Datum besitzen, als in diesem Feld angegeben ist.
HEAD URI HTTP-Version
HEAD ist zunächst identisch mit GET mit dem Unterschied, dass der Server keine Daten in der Antwort übertragen darf. Damit können Informationen über die angeforderte Ressource ausgelesen werden ohne die Ressource selbst zu übertragen.
POST URI HTTP-Version
POST wird verwendet, um Daten vom Client zum
Tabelle 2.3: Die wichtigsten Methoden von HTTP 1.1
Server zu übertragen, die zu dem angegebenen URI gehören. Ein ausgefülltes HTML-Formular wird z.B. über POST an ein Programm auf dem Server übertragen (das im URI angegeben ist) und dort ausgewertet. PUT URI HTTP-Version
Mit PUT kann ein Client Daten an den Server übertragen, die dieser unter dem im URI angegebenen Dateinamen speichern soll (vorausgesetzt, der Benutzer besitzt das Schreibrecht auf dem Ordner).
71
Basiswissen
Header-Felder, die der Befehlszeile folgen, übermitteln spezifische Informationen oder erweitern den Befehl. Einige Header-Felder werden in Anforderungen und Antworten gemeinsam verwendet, andere gelten nur für Anforderungen oder Antworten. Tabelle 2.4 beschreibt die (für Internetprogrammierer) wichtigsten gemeinsamen Felder, Tabelle 2.5 die wichtigsten Felder einer Anforderung und Tabelle 2.6 die wichtigsten einer Antwort. Tabelle 2.4: Die wichtigsten allgemeinen HTTPHeader-Felder
Tabelle 2.5: Die wichtigsten HTTP-HeaderFelder einer Anforderung
72
Feld
Bedeutung
Connection
In diesem Feld kann der Sender spezifizieren, ob er die Verbindung nach dem Senden der Daten noch aufrechterhalten oder schließen wird. Mit dem Wert Keep-Alive signalisiert der Sender, dass er die Verbindung aufrechterhält, was immer dann verwendet wird, wenn der Empfänger Daten zum Sender zurücksenden soll. Mit dem Wert Close signalisiert der Sender, dass die Verbindung seinerseits geschlossen wurde und der Empfänger keine Daten zurücksenden sollte.
Content-Type
spezifiziert den MIME-Typ der gesendeten Daten. Für HTML-Dokumente wird in diesem Feld beispielsweise der Typ text/html angegeben.
Date
Dieses Feld speichert das Datum, an dem die Nachricht generiert wurde.
Feld
Bedeutung
Accept
definiert die akzeptierten Medientypen für die Antwort.
Accept-Charset
definiert die akzeptierten Zeichensätze für die Antwort.
Accept-Encoding
definiert die akzeptierten Verschlüsselungen für die Antwort.
Accept-Language
definiert die akzeptierten Sprachen für die Antwort.
Authorization
enthält die (einfach verschlüsselten) Logindaten des Benutzers für geschützte Webs.
Cookie
sendet ein zuvor vom Webserver an den Browser gesendetes Cookie zum Webserver zurück. Ein Cookie ist eine Informationseinheit, die ein HTML-Dokument auf dem Rechner des Anwenders ablegen kann und die im HTTP-Header zum Webserver zurückgesendet wird, wenn der Anwender dieses Dokument noch einmal abruft. Cookies erlauben das persistente Speichern von Daten in einer Internetanwendung und werden häufig verwendet, um den Anwender zu identifizieren.
2.9 Die wichtigsten höheren Protokolle
Basiswissen
Feld
Bedeutung
From
kann verwendet werden, um die E-Mail-Adresse des Benutzers oder spezielle Logging-Informationen an den Server zu übertragen.
Host
enthält die Hostadresse und den Port des Servers.
If-ModifiedSince
definiert, dass die angeforderte Ressource nur dann übertragen werden soll, wenn diese seit dem angegebenen Datum modifiziert wurde.
Referer
definiert die URL des Aufrufers. »Referer« ist übrigens falsch geschrieben und müsste eigentlich »Referrer« heißen.
User-Agent
enthält Informationen über den Client wie z.B. den Browsertyp und die Version.
Feld
Bedeutung
Location
Location wird verwendet, um den Client auf eine andere URL umzulenken (Redirect). Der Client erhält eine Antwort mit dem Status »302 - Object moved« mit der Angabe der URL der Ressource, auf die umgeleitet wurde, im Feld Location. Der Client reagiert normalerweise darauf, indem er die Ressource über die neue URL neu anfordert.
Server
Dieses Feld enthält Informationen über den Server.
Set-Cookie
Dieses Feld teilt dem Browser mit, dass er ein Cookie speichern soll.
WWW-Authenticate
Dieses Feld wird in einer HTTP-Nachricht mit dem Status 401 (Access denied) gesendet und definiert die möglichen Authentifizierungs-Methoden für den Client.
Tabelle 2.5: Die wichtigsten HTTP-HeaderFelder einer Anforderung (Forts.)
Tabelle 2.6: Die wichtigsten HTTP-HeaderFelder einer Antwort
Die Anforderung
Im Header einer Anforderung (dem Request) sendet ein Client in der ersten Zeile einen HTTP-Befehl zum Server. Bei den meisten Befehlen wird die verwendete HTTP-Version mit angegeben. In den folgenden Zeilen übergibt der Client in einzelnen HTTP-Feldern zusätzliche Informationen, die z.B. definieren, welche Grafikdateitypen, Sprachen und Codierungen akzeptiert werden, und Felder mit Informationen zum Client und zum Server.
73
Basiswissen
Das folgende Beispiel zeigt eine typische HTTP-Anforderung in einem Intranet. Angefordert wird die Datei default.htm im Stammordner des Webservers auf dem Rechner Zaphod. Client ist der Internet Explorer 6.02 (der sich allerdings als Version 5.01 ausgibt): GET /default.htm HTTP/1.1 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/msword, application/ vnd.ms-powerpoint, */* Accept-Language: de Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0) Host: Zaphod Connection: Keep-Alive
Der Client muss nicht alle Felder spezifizieren, wie es im Beispiel oben der Fall ist. Normalerweise genügt die Angabe der Befehlszeile und des Host: GET /default.htm HTTP/1.1 Host: Zaphod
Die Angabe des Host ist Pflicht bei HTTP 1.1. Geben Sie dieses Feld nicht an, erhalten Sie die HTTP-Statusmeldung »400 – Bad Request« zurück. Bei Befehlen, die zu einer Antwort führen, sollte der Client die Verbindung aufrechterhalten und dem Server dies über das Connection-Feld mitteilen: GET /default.htm HTTP/1.1 Host: Zaphod Connection: Keep-Alive
Ruft der Client ein Dokument ab, das bereits in seinem Cache gespeichert ist, enthält die HTTP-Anforderung normalerweise im If-Modified-Since-Feld eine Angabe darüber, dass nur neuere Dokumente zurückgeliefert werden sollen:
74
2.9 Die wichtigsten höheren Protokolle
Basiswissen
GET /default.htm HTTP/1.1 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/msword, application/ vnd.ms-powerpoint, */* Accept-Language: de Accept-Encoding: gzip, deflate If-Modified-Since: Sat, 23 Feb 2002 15:20:30 GMT If-None-Match: "30f488578c16c11:87d" User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0) Host: Zaphod Connection: Keep-Alive
Die Antwort
Auf eine Anforderung (Request) antwortet der Server mit einem Response. Diese Antwort besteht wieder aus einem Header und einem Datenbereich. Der Header beginnt mit einer Statuszeile, die die HTTP-Version und Informationen über den Erfolg bzw. über Fehler enthält, die bei der Abarbeitung der Anforderung aufgetreten sind. Danach folgen Header-Felder, die z.B. den Server identifizieren und das auf dem Server aktuelle Datum enthalten. Antwortet der Server z.B. auf eine GET-Anforderung, sieht die Antwort des Servers etwa so aus wie im folgenden Beispiel, falls alles in Ordnung ist: HTTP/1.1 200 OK Server: Microsoft-IIS/5.0 Content-Location: http://zaphod/Default.htm Date: Sat, 23 Feb 2002 15:48:30 GMT Content-Type: text/html Accept-Ranges: bytes Last-Modified: Sat, 23 Feb 2002 15:47:18 GMT ETag: "c0f7ee5f81bcc11:abc" Content-Length: 297
75
Basiswissen
Zaphods Web Zaphods Web Willkommen auf Zaphods Web.
Hier finden Sie Beispiele für die verschiedenen Techniken der Internetprogrammierung.
Treten Sie ein.
Treten Fehler auf, antwortet der Server mit einer HTTP-Fehlermeldung (deren HTML-Text Sie in der Administration des IIS selbst definieren können). Die Anforderung einer Datei, die nicht vorhanden ist, wird z.B. mit dem HTTP-Fehler 404 beantwortet: HTTP/1.1 404 Object Not Found Server: Microsoft-IIS/5.0 Date: Sat, 23 Feb 2002 15:55:03 GMT Connection: close Content-Length: 3238 Content-Type: text/html ...
Der hier nicht dargestellte Teil der Antwort enthält den Rest des HTML-Dokuments (das bei
Beachten Sie aber, dass das Caching sehr von Vorteil ist, wenn Ihr HTML-Dokument Bilder dynamisch nachlädt (siehe Seite 290). Machen Sie sich auf einiges gefasst
Besonders, wenn Sie Cross-Browser-Lösungen entwickeln wollen, sollten Sie immer eine Packung (pflanzliche) Beruhigungstabletten in der Nähe haben. Sonst gehen Sie Gefahr ein, dass Sie Ihren Computer in einem der wahrscheinlich auftretenden Wutanfälle arg beschädigen. Eine Lösung, die wunderbar im Netscape 4 funktioniert, muss noch lange nicht auch im Internet Explorer und im Netscape 6 laufen. Dasselbe gilt natürlich auch umgekehrt. Bei meinen Versuchen ist es immer wieder vorgekommen, dass ein theoretisch mögliches Feature aus irgendeinem Grund in einem Browser nicht funktionierte. Ich bin dann einfach so vorgegangen, dass ich das Dokument kopiert und dann Schritt für Schritt die unwesentlichen Teile des Dokuments entfernt habe, bis der Fehler nicht mehr auftrat oder das Dokument nur noch aus den Teilen bestand, die fehlerhaft waren. Dieses meist sehr vereinfachte Dokument brachte dann, besonders im Vergleich mit anderen funktionsfähigen Programmen, die Lösung des Problems. Da Sie dieses Buch jetzt in der Hand halten, hat das Ganze wohl auch soweit funktioniert, sodass mein Computer das DHTML-Kapitel überlebt hat ... 5.2.3 DHTML im Internet Explorer 4 bis 5 Im Internet Explorer können Sie ab der Version 4 auf alle Elemente einer HTML-Seite zugreifen (was beim Netscape 4 z.B. nicht der Fall ist). Neben den Auflistungen forms, images, links und anchors, die alle Browser kennen, ist beim Internet Explorer bis Version 5 die all-Auflistung wichtig. Über diese Auflistung greifen Sie auf beliebige
272
5.2 Arbeiten mit dem DOM
DHTML
HTML-Elemente zu. Sie können den Namen des Elements über die allgemeine Objektsyntax direkt anhängen: document.all.Elementname
Alternativ können Sie die Auflistung auch als solche verwenden, indem Sie den Namen als String-Index angeben: document.all["Elementname"]
Über diese Syntax können Sie auf die Eigenschaften der Elemente zugreifen oder deren Methoden aufrufen. Der folgende Quelltext ändert z.B. den Text eines span-Elements mit dem Namen hello: document.all.hello.innerText = "This is DHTML";
Wenn das Element auf einem Formular angelegt ist, müssen Sie den Formularnamen mit angeben. Die folgende Zeile ermittelt den Wert einer Textbox auf einem Formular: alert(document.all.frmInput.txtLogin.value);
Ein wichtiger Trick ist, dass Sie all dann auch weglassen können: alert(document.frmInput.txtLogin.value);
Der Trick ist deshalb wichtig, weil auch der Netscape diese Syntax unterstützt. Das Cross-Browser-Coding wird damit wenigstens für Formularauswertungen wesentlich einfacher. Die Arbeit mit den anderen Auflistungen des Internet Explorers ist identisch. Interessant sind diese Auflistungen, wenn Sie in einer Schleife alle Elemente eines bestimmten Typs durchgehen wollen. Das folgende Beispiel geht alle img-Elemente durch und definiert für diese Elemente einen Rahmen und eine Hintergrundfarbe:
273
DHTML
var i; for (i=0; i 2) imageIndex = 1; document.getElementById("image1").src = "banner" + imageIndex + ".gif"; } DHTML-Demo
Abbildung 5.4: Das Ergebnis des DHTMLBeispiels
5.2.6 Cross-Browser-Coding Wenn Sie Webseiten entwickeln, die in mehreren Browsern funktionieren, müssen Sie auf die Eigenheiten dieser Browser eingehen. Wenn Sie nur den Internet Explorer ab Version 5.5 und den Netscape ab Version 6.0 berücksichtigen wollen, ist die Programmierung sehr einfach, 285
DHTML
weil diese beiden Browser den W3C-Standard unterstützen. Einfach ist auch, wenn Sie nur den Internet Explorer ab Version 4 unterstützen, weil die neuen Versionen kompatibel sind (document.all funktioniert auch in den neuen Versionen). Wollen Sie den Internet Explorer 4 bis 6 und den Netscape 6 berücksichtigen, müssen Sie eigentlich nur darauf achten, dass der Internet Explorer 4 bis 5.0 document.all verwendet (und dass der Netscape innerText nicht unterstützt). Mit dem Netscape 4 wird es aber schwierig, weil dieser Browser ja leider nur Layer mit ganz eigenen Attributen in DHTML kennt. Da der Netscape 4 für Divs und Spans nicht die gängigen Ereignisse, wie z.B. onMouseOver und onMouseOut, unterstützt, ist DHTML in diesem Browser schon arg eingeschränkt. Leider verwenden viele Benutzer lieber die Version 4.5 oder 4.7 als die Version 6. Wenn Sie im Scriptcode lediglich die Eingaben von Steuerelementen auswerten wollen, die auf einem Formular angelegt sind, müssen Sie nichts weiter machen. Verwenden Sie dazu einfach die folgende Syntax: document.Formularname.Steuerelementname.Eigenschaft
Diese Syntax wird vom Netscape und vom Internet Explorer (so weit ich weiß ab Version 4) unterstützt. Wenn Sie jedoch auf beliebige HTML-Elemente zugreifen wollen, müssen Sie normalerweise zunächst im Code herausfinden, welcher Browser verwendet wird. Sie können dazu die umständliche Variante wählen, indem Sie den Browsernamen und die Version über das navigator-Objekt abfragen: browser = navigator.appName; version = navigator.appVersion
Leider ist die Versionsangabe oft etwas kompliziert aufgebaut. Der Internet Explorer 6 gibt z.B. den (je nach Umfeld unterschiedlichen) String »4.0 (compatible; MSIE 6.0b; Windows NT 5.0; .NET CLR 1.03215)« zurück, der Netscape 6 den String »5.0 (Windows; de-DE)« (5.0 ist hier kein Schreibfehler). Die tatsächliche Version herauszufinden, ist also nicht allzu einfach.
286
5.2 Arbeiten mit dem DOM
DHTML
Deshalb verwenden Internetprogrammierer lieber eine etwas andere Variante: /* Browsertyp ermitteln */ var ns4 = false, ns6 = false, ie4 = false, ie6 = false; if (document.all && document.getElementById) ie6 = true; else if (document.getElementById) ns6 = true; else if(document.all) ie4 = true; else if (document.layers) ns4 = true;
Diese Variante überprüft einfach, ob die spezifischen document-Eigenschaften existieren. Beachten Sie, dass diese vereinfachte Variante andere Browser wie z.B. Opera nicht berücksichtigt. Das folgende einfache Beispiel setzt die Farbe eine HTML-Elements um, wenn der Anwender die Maus über das Element bewegt. Um das Netscape 4-Layer-Problem zu umschiffen, wende ich einen Trick an: Ich erzeuge einfach einen Layer, der mit dem name-Attribut bezeichnet wird, und einen span, der mit dem id-Attribut bezeichnet wird. Der Netscape 4.x verwendet den Layer und das name-Attribut, der Internet Explorer verwendet den span und das id-Attribut. Was der Netscape 6.x verwendet, kann ich nicht sagen. Es funktioniert aber .
287
DHTML
/* Browsertyp ermitteln */ var ns4 = false, ns6 = false, ie4 = false, ie6 = false; if (document.all && document.getElementById) ie6 = true; else if (document.getElementById) ns6 = true; else if(document.all)ie4 = true; else if (document.layers) ns4 = true;
function changeColor(color) { /* Hintergrundfarbe des Span verändern */ if (ie4) document.all.hello.style.backgroundColor = color; else if (ns4) document.layers.hello.bgColor = color; else if (ie6 || ns6) document.getElementById( "hello").style.backgroundColor = color; }
DHTML-Cross-Browser-Demo
288
5.2 Arbeiten mit dem DOM
DHTML
Hello Cross-Browser-DHTML
Abbildung 5.5: Ein Cross-Browser-DHTMLDokument im Netscape 6
Bei Ihren DHTML-Anwendungen sollten Sie beachten, dass Sie in ASP und ASP.NET den Browsertyp und die Version ermitteln können. Damit haben Sie die Möglichkeit, spezifischen DHTML-Code dynamisch zu erzeugen. Für den Netscape 4.x können Sie dann z.B. LayerElemente, für die anderen Browser Divs oder Spans erzeugen.
289
DHTML
5.3 DHTML-Programmiertechniken 5.3.1 Bilder dynamisch laden Das dynamische Laden von Bildern ist einfach. Sie ändern dazu einfach das src- bzw. background-Attribut des Elements, das das Bild speichert. Das folgende Beispiel verwendet einen Timer, um die Bilder clip1.gif bis clip7.gif in Abständen von einer Sekunde dynamisch nachzuladen. Das Programm funktioniert im Netscape und im Internet Explorer ab Version 4. Dynamische Bilder var intervalId = 0; var imageIndex = 0; function changeImage() { imageIndex++; if (imageIndex > 7) imageIndex = 1; var imageFile = "clip" + imageIndex + ".gif"; if (document.all) // IE 4 bis 5.0 document.all.image.src = imageFile; else if (document.getElementById) // IE ab 5.5; NS ab 6,0 document.getElementById("image").src = imageFile; else if (document.images) // NS 4 und evtl. andere document.images["image"].src = imageFile; }
290
5.3 DHTML-Programmiertechniken
DHTML
Das andauernde Laden des Bildes ist auch im Internet kein Problem, weil der Browser einmal geladene Daten in seinem Cache zwischenspeichert. 5.3.2 Hover-Effekte und einfache Menüs Ein Hover-Effekt ist, wenn der Anwender die Maus auf ein Element bewegt, dieses dann dynamisch verändert wird und seinen alten Zustand wieder herstellt, wenn der Anwender das Element verlässt. Für Hover-Effekte müssen Sie auf die Ereignisse onmouseover und onmouseout reagieren. Leider unterstützt der Netscape 4 diese Ereignisse nur für layer-Elemente. Deshalb folgt hier zunächst der einfachere Quelltext für den Internet Explorer ab Version 4 und den Netscape ab Version 6, die mit span-Elementen arbeiten können. Internet Explorer und Netscape 6
Das folgende Beispiel ändert die Hintergrundfarbe von span-Elementen, wenn der Anwender die Maus auf das Element bewegt (Abbildung 5.6). Das Beispiel arbeitet mit einer Stilvorlage für die span-Elemente, die ein Menü darstellen sollen, damit der Stil dieser Elemente einfacher geändert werden kann. Damit das Menü auch Sinn macht, wird das onclick-Ereignis der span-Tags mit einem Programm verknüpft, das ein neues HTML-Dokument lädt: body {background-color:#F0F0F0} .menu { position:absolute;
291
DHTML
left:10; width:100px; height:20; border:1px solid blue; color:white; background-color:green; text-align:center; cursor:default; } Text-Hover-Effekte /* Funktion zum Hovern */ function hover(layer, active) { if (active) { layer.style.backgroundColor = "lime"; layer.style.color = "black"; } else { layer.style.backgroundColor = "green"; layer.style.color = "white"; } }
292
5.3 DHTML-Programmiertechniken
DHTML
Über uns Feedback Kontakt
293
DHTML
Abbildung 5.6: Ein Hover-Effekt über einem spanElement
Das Beispiel verwendet zwei wichtige »Tricks«: Einmal wird beim Aufruf der Funktion zum Hovern in den Ereignisattributen der this-Zeiger übergeben, der eine Referenz auf das Objekt verwaltet, in dem die Funktion ausgeführt ist. Die Verwendung dieses Zeigers erspart recht viel Arbeit, weil die Referenz dann innerhalb der Funktion nicht mehr extra ermittelt werden muss (was der Fall wäre, wenn Sie statt this die Id des Objekts übergeben würden). Der andere »Trick« ist, dass die Stilvorlage den Mauscursor auf den Wert default setzt. Damit vermeiden Sie, dass der Anwender den Text in den span-Elementen selektieren kann und dass der Mauscursor automatisch über dem Text auf den Eingabecursor umgeschaltet wird. Für den Internet Explorer könnten Sie übrigens auch den Wert hand verwenden, der den für Elemente, die einen Klick erlauben, üblichen Hand-Cursor anzeigt. Leider unterstützt der Netscape 6 diesen Wert nicht. Annäherung an den Netscape 4
Der Netscape 4 hat so seine Probleme mit den CSS-Eigenschaften. Er stellt zwar den Rahmen irgendwie dar und auch die Hintergrundfarbe, wertet aber den Rahmen so aus, dass dieser das Element vergrößert und dass die Hintergrundfarbe nicht mehr bis zum Rand gezeichnet wird. Außerdem werden die Elemente nicht korrekt positioniert (Abbildung 5.7). Die Höhenangabe in der Stilvorlage wird vom Netscape 4 einfach ignoriert, die Höhe der Elemente richtet sich lediglich nach dem Inhalt. Der unschöne Rand entsteht durch die Definition des Randes
294
5.3 DHTML-Programmiertechniken
DHTML
in der Stilvorlage. Die Verschiebung der Elemente liegt übrigens daran, dass diese sich überlappen. Wenn Sie die Randdeklaration in der Stilvorlage entfernen, werden die Elemente nicht mit einer geringeren Höhe dargestellt und korrekt platziert. Leider wird dann der Text rechtsbündig ausgegeben und die Hintergrundfarbe füllt nicht mehr das gesamte Element (Abbildung 5.8). Abbildung 5.7: Der Netscape 4 stellt die spanElemente nicht korrekt dar.
Abbildung 5.8: Ohne Rand-Stildeklaration werden die spans wenigstens korrekt positioniert.
295
DHTML
Jetzt müssen Sie (bzw. ich) nur noch dafür sorgen, dass die Layer durchgehend gefüllt werden und auf die Ereignisse onmouseover und onmouseout reagieren, die der Netscape 4 für span- und div-Elemente leider nicht unterstützt. Die Lösung dieses Problems ist aber eigentlich ganz einfach: Sie legen um das span-Element einfach ein layer-Element und spezifizieren für dieses Element die Position, Breite, eventuell die Höhe, die Hintergrundfarbe und die Ereignisattribute separat. Um Probleme mit der Benennung der Elemente zu vermeiden, werden die span-Tags nur über das id-Attribut und die layer-Tags nur über das name-Attribut benannt. Da der Netscape 4-Layer keine Ausrichtung des Textes erlaubt, wird dieser einfach über ein center-Tag ausgerichtet. Weil der NetscapeLayer leider auch nicht die Veränderung der Textfarbe ermöglicht, lasse ich diese einfach auch für die anderen Browser weg. Um die Auswertung eines Mausklicks auf einem Element zu ermöglichen, muss das onclick-Ereignis für die layer-Tags des Netscape 4 registriert werden, was über das onload-Ereignis des body-Elements geschieht. Dabei müssen Sie beachten, dass Sie nur Funktionen mit der onclickEreigniseigenschaft der Layer verknüpfen können, keinen JavaScriptCode (diese Erkenntnis hat mich etwa zwei Stunden gekostet). Als zusätzliches Feature wird gleich auch noch ein Rahmen simuliert, in dem die einzelnen Layer mit etwas Abstand positioniert werden und ein separater Layer unter die Menülayer gelegt wird. Für den Hintergrund-Layer wird ein layer-Element in einem span-Element geschachtelt. Wie Sie ja bereits wissen, wertet der Netscape 4 die Positionierungsangaben in einem absolut positionierten span- oder divElement korrekt aus, aber nicht die Angaben zur Breite und Höhe. Sorry übrigens, dass der Quellcode so lang ist. Ich weiß, dass das in einem Buch nicht gerade übersichtlich ist. Aber das Cross-BrowserCoding ist eben nicht allzu einfach ...
296
5.3 DHTML-Programmiertechniken
DHTML
body {background-color:#F0F0F0} .menu { position:absolute; left:10; width:100px; height:18; background-color:green; cursor:default; } Text-Hover-Effekte /* Ereignisfunktionen für die Änderung der location beim Klick auf die Layer */ function gotoAbout() { location.href = "about.htm"; } function gotoFeedback() { location.href = "feedback.htm"; } function gotoContact() { location.href = "contact.htm"; } /* Funktion zur Registrierung der onclickEreignisse der Netscape-Layer */ function initLayer()
297
DHTML
{ if (document.layers && !document.getElementById) { /* Nur für den Netscape 4, der Netscape 6 wertet onclick der span-Elemente aus */ document.menu1.captureEvents(Event.CLICK); document.menu2.captureEvents(Event.CLICK); document.menu3.captureEvents(Event.CLICK); document.menu1.onclick = gotoAbout; document.menu2.onclick = gotoFeedback; document.menu3.onclick = gotoContact; } } /* Funktion zum Hovern */ function hover(layer, active) { /* Browserabhängige Veränderung des Layers */ /* Ermitteln der Farben */ if (active) backgroundColor = "lime"; else backgroundColor = "green"; /* Farben umsetzen */ if (document.all || document.getElementById) { /* Internet Explorer oder Netscape 6 */ layer.style.backgroundColor = backgroundColor; } else if (document.layers) {
298
5.3 DHTML-Programmiertechniken
DHTML
/* Netscape 4 */ layer.bgColor = backgroundColor; } } Über uns Feedback Kontakt
Den Wert 63 für die Höhe des Hintergrund-Layers habe ich übrigens durch Versuche ermittelt. Der eigentlich logische Wert 64 führte zu einem etwas zu großen unteren Rand. Dieses einfache Menü funktioniert nun im Netscape 4, im Netscape 6 und im Internet Explorer (und wahrscheinlich auch noch in anderen Browsern), wie Abbildung 5.9 zeigt.
300
5.3 DHTML-Programmiertechniken
DHTML
Abbildung 5.9: Ein einfaches Cross-BrowserMenü mit Layern
Das im Netscape 4 in diesem Beispiel auftretende Problem, dass der Mauscursor über dem Text automatisch zu einem Eingabecursor wechselt und die Auswahl des Textes erlaubt, ist eins der wenigen Probleme, die ich in der Kürze der mir zur Verfügung stehenden Zeit nicht lösen konnte (ich weiß aber, dass es geht, existierende Menüs beweisen dies). Aber für Sie soll ja auch noch etwas Arbeit übrig bleiben ... 5.3.3 Hover-Effekte mit Bildern Hover-Effekte sind sehr effektvoll, wenn diese mit Bildern ausgeführt werden. Fährt der Anwender mit der Maus über das Bild, wird einfach ein anderes Bild angezeigt. Das Prinzip ist einfach: Sie ändern nur die src-Eigenschaft des img-Tags (oder die entsprechende Eigenschaft eines anderen Tags). Um nicht wieder die Positionierungs- und Klick-Probleme des Netscape 4 auf den Tisch zu bringen, zeigt der folgende Quellcode nur die Lösung für den Internet Explorer und den Netscape 6. Das Beispiel legt drei Bilder nebeneinander an, reagiert auf eine Mausbewegung auf dem Element und das Verlassen des Elements damit, dass jeweils ein anderes Bild geladen wird, und wertet den Klick auf ein Bild aus. Aus Vereinfachungsgründen werden die Bilder einfach in je ein a-Tag eingeschlossen, das mit der Ziel-URL verknüpft ist:
301
DHTML
Bild-Hover-Effekte
302
5.3 DHTML-Programmiertechniken
DHTML
Abbildung 5.10: Ein einfaches Menü mit Bilddateien
5.3.4 Bilder vorladen Eine beliebte Technik zum beschleunigten Anzeigen von großen Bilddateien (im Internet ist schon 20 KB groß!) ist, die Bilder beim Laden des Dokuments in einem Array vorzuladen. Das Ganze hat lediglich den Zweck, dass der Browser die Bilder cacht. Wenn Sie danach einem HTML-Element über das src- bzw. background-Attribut eines der bereits vorgeladenen Bilder zuweisen, wird das Bild aus dem Cache geladen und ist wesentlich schneller sichtbar. Der einzige Nachteil ist, dass der Start der Seite etwas länger dauert. 303
DHTML
Zum Vorladen der Bilder verwenden Sie ein Array vom Typ Image (die DHTML-Klasse für img-Elemente). In die src-Eigenschaft der im Array verwalteten Objekte schreiben Sie dann einfach die URL der Bilddatei. Wenn Sie diese URL später im Programm einem HTMLElement zuweisen, das Bilder anzeigen kann, wird das Bild von Browser aus dem Cache gelesen: Bilder vorladen /* Array für die Bilder */ var images = new Array(7); /* Bilder vorladen */ for (var i = 0; i < 7; i++) { images[i] = new Image(); images[i].src = "clip" + (i + 1) + ".gif"; } /* Vorgeladene Bilder in einer Timer-Funktion verwenden */ var intervalId = 0; var imageIndex = 0; function changeImage() { imageIndex++; if (imageIndex > 7) imageIndex = 1; var imageFile = "clip" + imageIndex + ".gif"; if (document.all) // IE 4 bis 5.0
304
5.3 DHTML-Programmiertechniken
DHTML
document.all.image.src = imageFile; else if (document.getElementById) // IE ab 5.5; NS ab 6.0 document.getElementById("image").src = imageFile; else if (document.images) // NS 4 und evtl. andere document.images["image"].src = imageFile; }
5.3.5 HTML-Elemente dynamisch erzeugen Über die createElement-Methode des document-Objekts können Sie im Programm dynamisch neue HTML-Elemente erzeugen und diese in das Dokument einhängen. Diese Technik wird häufig verwendet, um komplizierte Dokument-Strukturen wie z.B. Menüsysteme dynamisch erzeugen zu können. Mit einer dynamischen Erzeugung ersparen Sie sich eine Menge Arbeit, die bei der ansonsten statischen Deklaration der einzelnen Elemente notwendig wäre, und erreichen damit zusätzlich eine sehr hohe Flexibilität und Erweiterbarkeit des Programms. Um zu verstehen, wie das dynamische Erzeugen funktioniert, müssen Sie wissen, wie der Browser ein HTML-Dokument verwaltet. Im Prinzip ist das ganz einfach: Der Browser verwaltet alle HTML-Elemente in einem Baum, der beim window-Objekt beginnt. Das document-Objekt ist dem window-Objekt direkt untergeordnet, im document-Objekt sind die HTML-Elemente untergebracht. Elemente, die Child von einem anderen Element sind, werden dem Parent-Element untergeordnet.
305
DHTML
Ein HTML-Dokument, das folgendermaßen aussieht, Dynamisches Erzeugen von HTML-Elementen HTML-Elemente dynamisch erzeugen Inhalt:
wird im Objektbaum beispielsweise so abgebildet wie in Abbildung 5.11.
306
5.3 DHTML-Programmiertechniken
DHTML
Abbildung 5.11: Ein HTMLObjektbaum, dargestellt durch die Dokumentgliederung von Visual Studio .NET
Internet Explorer ab Version 5.5 und Netscape ab Version 6
Beim Internet Explorer ab Version 5.5 und Netscape ab Version 6 können Sie die W3C-Methode createElement verwenden, um HTMLElemente zu erzeugen. Diese Elemente können Sie dann beliebig in den Objektbaum einhängen. Dazu stehen Ihnen die in Tabelle 5.7 beschriebenen W3C-Methoden zur Verfügung. Methode
Beschreibung
document.createElement(Tag)
Erzeugt ein HTML-Element. Im Argument übergeben Sie den Tagnamen als String (z.B. »h1« für ein Header-Tag). Der Internet Explorer erlaubt zwar auch die Übergabe eines komplett formulierten HTMLElements (z.B. »Test«), der Netscape 6 wertet eine solche Angabe aber nicht korrekt aus.
Element.appendChild(newChild)
fügt einem Element ein neues Child-Element hinzu. Das im Argument übergebene neue Element wird an das Ende der eventuell bereits existierenden ChildListe angehängt. Beachten Sie, dass diese Methode nicht direkt zum document-Objekt gehört, sondern zum body-Objekt, das dem Dokument untergeordnet ist. Wenn Sie dem Dokument direkt Elemente unterordnen wollen, müssen Sie also document.body.appendChild verwenden.
Tabelle 5.7: Methoden zur dynamischen Erweiterung von HTML-Dokumenten für den Internet Explorer ab Version 5.5 und den Netscape ab Version 6
307
DHTML
Tabelle 5.7: Methoden zur dynamischen Erweiterung von HTML-Dokumenten für den Internet Explorer ab Version 5.5
Methode
Beschreibung
Element.insertBefore (newChild, refChild)
fügt einem Element ein neues Child-Element hinzu. Im ersten Argument übergeben Sie das neue Child-Element. Im zweiten Argument können Sie eine Referenz auf ein anderes, bereits existierendes Child-Element angeben, vor dem das neue dann platziert wird. Wenn Sie das neue Element an das Ende der Child-Liste platzieren wollen, geben Sie hier null an. Beachten Sie, dass das zweite Argument beim Internet Explorer zwar optional ist, aber nicht beim Netscape 6. Wenn Sie das Dokument auf der höchsten Ebene erweitern wollen, müssen Sie diese Methode als Methode des body-Objekts aufrufen, das dem document-Objekt untergeordnet ist.
Das folgende Beispiel erweitert beim Klick auf einen Schalter ein HTML-Dokument um eine Überschrift und um zwei Einträge in einer Liste: Dynamisches Erzeugen von HTML-Elementen function createElements() { /* Neues Header-Tag vor einem anderen einfügen */ // Element erzeugen var obj = document.createElement("h1"); obj.innerHTML = "DHTML macht Spaß " + "(wenn's funktioniert)"; // und in das Dokument einhängen // document.body.appendChild(obj); document.body.insertBefore(obj, document.getElementById("header1"));
308
5.3 DHTML-Programmiertechniken
DHTML
/* li-Elemente in eine Liste einfügen */ // Objekt hinten anhängen obj = document.createElement("li"); obj.innerHTML = "Cross-Browser-Coding"; document.getElementById("contents").insertBefore( obj, null); // Objekt vor einem anderen Child einfügen obj = document.createElement("li"); obj.innerHTML = "Grundlagen"; document.getElementById("contents").insertBefore( obj, document.getElementById("c1")); } Inhalt
309
DHTML
Abbildung 5.12: Das Beispieldokument vor der Erweiterung
Abbildung 5.13: Das Beispieldokument nach der Erweiterung
310
5.3 DHTML-Programmiertechniken
DHTML
Internet Explorer 4 bis 5.0
Der Internet Explorer 4 bis 5.0 kennt das DOM des W3C noch nicht. Mit diesem Browser können Sie aber die Methode insertAdjacentHTML verwenden, die einen HTML-Quelltext vor, in oder hinter einem Element ausgibt. Methode
Beschreibung
Element.insertAdjacentHTML(where, htmlText, )
Diese nur im Internet Explorer verfügbare Methode können Sie für den Internet Explorer bis Version 5 verwenden, der die W3C-Methoden appendChild und insertChild nicht kennt, um HTML-Elemente (über deren Quelltext) zu erzeugen. Im ersten Argument geben Sie über einen String an, wo der neue HTML-Text eingefügt werden soll, den Sie im zweiten Argument übergeben: »beforeBegin«: Fügt den Text vor dem Starttag des Elements ein, »afterBegin«: Fügt den Text direkt nach dem Starttag des Elements ein, also am Beginn des ElementInhalts, »beforeEnd«: Fügt den Text vor dem Endetag des Elements ein, also am Ende des Inhalts, »afterEnd«: Fügt den Text hinter dem Endetag des Elements ein.
Tabelle 5.8: Die Methode zur dynamischen Erweiterung von HTML-Dokumenten für den Internet Explorer 4 bis 5.0
Die Funktion des Beispiels aus dem vorherigen Abschnitt sieht dann für den Internet Explorer (auch für die Version 6!) so aus : function createElements() { /* Neues Header-Tag vor einem anderen einfügen */ document.all.header1.insertAdjacentHTML( "beforeBegin", "DHTML macht Spaß " + "(wenn es funktioniert)"); /* li-Elemente in eine Liste einfügen */ // Objekt hinten anhängen document.all.contents.insertAdjacentHTML(
311
DHTML
"beforeEnd", "
Cross-Browser-Coding"); // Objekt vor einem anderen Child einfügen document.all.contents.insertAdjacentHTML( "afterBegin", "
Grundlagen"); }
Netscape 4
Der Netscape 4 fällt mal wieder etwas aus dem Rahmen. Soweit ich herausgefunden habe, können Sie nur layer- und option-Elemente dynamisch im Dokument erzeugen. Das bestätigten einige Newsgroup-Antworten auf meine entsprechende Frage. Sie erzeugen dazu eine Instanz der Klasse Layer bzw. Option. Diese Klassen und deren Konstruktoren sind in der JavaScript 1.3-Dokumentation dokumentiert, allerdings fehlt die Beschreibung des Konstruktors des wichtigen Layer-Objekts. Die ebenfalls instanzierbare Image-Klasse dient übrigens lediglich dem Vorladen von Bildern (siehe Seite 303). Ich gebe nicht gerne auf, aber nach sechs Stunden Suche im Internet (u.A. bei developer.netscape.com) denke ich nicht mehr, dass ich in der Zeit, die mir für dieses Buch noch bleibt, eine Antwort auf die offenen Fragen finde. Was ich über Newsgroup-Beiträge herausgefunden habe, zeigt der folgende Quellcode: Dynamisches Erzeugen von HTML-Elementen - NS 4 function createElements() { /* Neuen Layer direkt unter dem Dokument
312
5.3 DHTML-Programmiertechniken
DHTML
erzeugen */ var layer1 = new Layer(200); with (layer1) { visibility = "show"; bgColor = "yellow"; left = 20; top = 100; document.open(); document.write("DHTML mit dem " + "NS4 ist kompliziert"); document.close(); } /* Neuen Layer unter dem statischen Layer erzeugen */ var layer2 = new Layer(200, document.menu); with (layer2) { visibility = "show"; bgColor = "yellow"; left = 20; top = 30; height = 200; width = 200; document.open(); document.write("Neuer Child-Layer"); document.close(); } /* Listenfeld füllen */ var option = new Option("Ford Puma", "puma", false, false);
313
DHTML
document.frm.cars.options[ document.frm.cars.options.length] = option ; option = new Option("Porsche Carrera", "porsche", false, false); document.frm.cars.options[ document.frm.cars.options.length] = option ; }
Das Beispiel füllt beim Laden des Dokuments ein Listenfeld, erzeugt einen Layer direkt unter dem Dokument und einen als Child-Layer eines statisch deklarierten Layers (Abbildung 6.14). Das erste Argument des Layer-Konstruktors gibt wohl die Minimalbreite des Layers an. Ist die angegebene Breite größer als der Inhalt, wird diese auf die Breite des Inhalts reduziert (!?). Im zweiten Argument können Sie das Parent-Objekt übergeben. Übergeben Sie das zweite Argument nicht, wird der Layer direkt dem Dokument untergeordnet. Das erste Argument des Option-Konstruktors spezifiziert den Text des Listeneintrags, das zweite den Wert. Im dritten Argument übergeben Sie für einfache Listen einen booleschen Wert, der aussagt, ob das Listenelement beim ersten Anzeigen der Liste selektiert ist. Das vierte Argument wird bei Mehrfachauswahllisten (mit dem Attribut multiple) verwendet, um den Eintrag als Voreinstellung zu selektieren. 314
5.3 DHTML-Programmiertechniken
DHTML
Beachten Sie, dass diese Einstellungen nur beim ersten Laden der Liste berücksichtigt werden. Lädt der Anwender das Dokument erneut über die Neu-Laden-Funktion des Browsers, bleiben die alten AnwenderEinstellungen bestehen (und leider auch die alten Einträge in der Liste). Abbildung 5.14: Ein teilweise dynamisch erzeugtes Dokument im Netscape 4
Einige Probleme konnte ich dabei nicht lösen:
Die Breite und Höhe der Layer ließ sich nicht einstellen. Der im ersten Argument übergebene Wert definiert lediglich eine Minimalbreite. Ich konnte nur einem statischen Layer Child-Layer zuordnen. Wurde der Parent-Layer ebenfalls dynamisch erzeugt, zeigte der Netscape 4 die Child-Layer nicht an.
315
DHTML
Das Problem mit der Layer-Breite und Höhe können Sie lösen, indem Sie den dynamisch erzeugten Layer mit dem HTML-Quelltext eines inneren Layer beschreiben (was wohl in Menüprogrammen so gemacht wird): function createElements() { /* Neuen Layer unter dem statischen Layer erzeugen */ with (new Layer(80, document.menu)) { visibility = "show"; bgColor = "yellow"; left = 10; top = 10; document.open(); document.write("Neuer Child-Layer"); document.close(); } }
Existierende, fertige Menüsysteme, die auch im Netscape 4 funktionieren, beweisen, dass auch die anderen Probleme lösbar sind. Schauen Sie einmal das HV Menu bei www.dynamicdrive.com/dynamicindex1/index.html an. Dieses (geniale) Menü funktioniert ohne Probleme auch im Netscape (und erzeugt auf wundersame Weise dynamisch Layer). 5.3.6 Eingabevalidierung Unter dem alten ASP war es häufig sehr wichtig, Eingaben bereits im Client zu überprüfen. Die serverseitige Überprüfung der Eingaben nach dem Absenden des HTML-Formulars führte unter ASP immer zu recht viel Arbeit und vor allen Dingen zu so genannten Server-Roundtrips (eine Seite wird zum Server gesendet, dieser sendet eine Fehlermeldungsseite zum Client zurück). Unter ASP.NET stehen Ihnen zur flexiblen Validierung allerdings einfach anzuwendende Validierungssteuerelemente zur Verfügung (die ich in Kapitel 11 beschreibe). Diese
316
5.3 DHTML-Programmiertechniken
DHTML
Steuerelemente sind sogar in der Lage, zur Überprüfung automatisch passenden JavaScript-Code zu erzeugen. Da Sie aber in einigen speziellen Fällen auch eigene Validierungen programmieren müssen, zeige ich auf den folgenden Seiten, wie Sie diese implementieren. Zur clientseitigen Validierung benötigen Sie einen kleinen »Trick«. Das Formular darf erst dann abgesendet werden, wenn alle Eingaben in Ordnung sind. Dazu platzieren Sie auf dem Formular keinen Submit-, sondern einen einfachen Schalter, der bei Betätigung eine JavaScriptFunktion aufruft. Das Formular müssen Sie benennen, damit die Funktion darauf zugreifen kann: Ihr Name:
In der Funktion überprüfen Sie die Eingaben, wie ich es für die einzelnen Steuerelemente gleich noch beschreibe. Sind alle Eingaben in Ordnung, senden Sie das Formular über dessen submit-Methode ab: function doSubmit() { /* Überprüfung der Eingaben */ var message = ""; if (document.frmInput.txtName.value == "") message += "Geben Sie bitte Ihren Namen ein.\n";
317
DHTML
/* Wenn alles OK ist: Form absenden */ if (message == "") document.frmInput.submit(); else /* Meldung anzeigen */ alert(message); }
Für die Fehlermeldungen verwende ich einfach eine Variable, die ich sukzessive fülle. Das "\n" am Ende der jeweiligen Fehlermeldungen bewirkt einen Zeilenumbruch. Diese Variable wird am Ende einfach mit einem Leerstring verglichen, um herauszufinden, ob eine Eingabe nicht in Ordnung war. Bei der Auswertung der Eigenschaften der einzelnen Steuerelemente nutzte ich die Tatsache, dass der Internet Explorer und der Netscape den direkten Zugriff auf diese Elemente über den Namen des Formulars erlauben. Probleme sind damit, wenigstens für die Versionen 4 bis 6 dieser Browser, nicht zu erwarten. Ich zeige hier nur kurz, wie Sie die einzelnen Steuerelemente auswerten. In den Buchbeispielen finden Sie eine komplett programmierte Auswertung. Das Beispielformular
Damit Sie die Auswertungs-Beispiele für die einzelnen HTML-Elemente nachvollziehen können, zeige ich hier kurz, wie das Formular aufgebaut ist, dessen Eingaben ich auswerte. Die Positionierung der einzelnen Elemente habe ich über Spans realisiert: Ihr Name:
318
5.3 DHTML-Programmiertechniken
DHTML
Ihr Alter: Adresse: ist für mich mit Ihr bevorzugtes Reisefahrzeug: Flugzeug Zug Auto Motorrad Ihre liebsten Automarken: Ford Puma Fiat Panda Porsche 911 Audi TT Ihr Geschlecht: Weiblich Männlich Weiß nicht Ihre Hobbies: Snowboarden Motorrad fahren Inlinern
320
5.3 DHTML-Programmiertechniken
DHTML
Abbildung 5.15: Das BeispielEingabeformular
Die Validierungsfunktion
Die Funktion zur Validierung beginnt folgendermaßen: function doSubmit() { /* Variable für die eventuelle Fehlermeldung */ var message = "";
321
DHTML
Textfelder
Bei Textfeldern (unabhängig davon, ob es sich um einfache Textfelder, Passwortfelder oder mehrzeilige Textfelder handelt) lesen Sie einfach die value-Eigenschaft aus: if (document.frmInput.txtName.value == "") message += "Geben Sie bitte Ihren Namen ein.\n";
Die Überprüfung nummerischer Eingaben ist etwas komplexer. Textfelder geben bei leeren Eingaben einen Leerstring zurück. Bei Vergleichen mit Zahlen oder in arithmetischen Berechnungen konvertiert JavaScript einen Leerstring in die Zahl 0. Wenn Sie also die Zahl 0 als gültige Zahl zulassen wollen, müssen Sie auch auf eine leere Eingabe überprüfen: var alter = document.frmInput.txtAlter.value; if (alter == "" || alter 150) message += "Geben Sie bitte ein gültiges " + "Alter ein.\n";
Listenfelder
Für Listenfelder mit Einfachauswahl überprüfen Sie die selectedIndexEigenschaft. Ist kein Eintrag ausgewählt, speichert diese Eigenschaft den Wert -1, ansonsten den Index des ausgewählten Elements: if (document.frmInput.lstReisefahrzeug.selectedIndex < 0) message += "Wählen Sie bitte ein Reisefahrzeug " + "aus.\n";
Bei Mehrfachauswahl-Listenfeldern müssen Sie die einzelnen Einträge durchgehen und überprüfen, ob der Anwender den Eintrag selektiert hat. Dazu lesen Sie die selected-Eigenschaft aus. Ich verwende zur Überprüfung eine boolesche Variable, die ich am Anfang auf false setze. var ok = false, i; for (i=0; i < document.frmInput.lstAutos.length; i++)
322
5.3 DHTML-Programmiertechniken
DHTML
if (document.frmInput.lstAutos.options[i].selected) ok = true; if (ok == false) message += "Wählen Sie bitte mindestens " + "ein Auto aus.\n";
Optionbuttons
Optionbuttons müssen Sie immer separat auswerten (die value-Eigenschaft einer Optionbutton-Gruppe gibt einfach immer undefined zurück!?). Da diese aber normalerweise über einen gemeinsamen Namen gruppiert sind, können Sie die einzelnen Optionbuttons einer Gruppe auch in einer Schleife durchgehen: ok = false for (i=0; i ist das Wurzelelement der Datei (XML schreibt das Vor-
handensein eines einzelnen Wurzelelements vor). Alle weiteren Elemente sind Childelemente des Wurzelelements. Der Bereich von bis ist eine Sektionsgruppe, die alle Einstellungen einer Konfigurationssektion zusammenfasst. In der machine.config sind neben system.web noch andere Sektionsgruppen wie z.B. system.net definiert, die andere Einstellungen speichern. Innerhalb dieser Sektionsgruppe werden einzelne Einstellungen vorgenommen. Dies geschieht manchmal in der Kurzform:
Das customErrors-Element wird hier direkt mit Attributen definiert und mit /> abgeschlossen. Andere Elemente, wie authorization, werden nicht über Attribute definiert, sondern über weitere Elemente, die in das Tag eingeschlossen werden:
664
10.8 Konfiguration einer ASP.NET-Anwendung
Arbeiten mit ASP.NET-Seiten
Die verfügbaren Einstellungen sind sehr vielfältig. Mir fehlt hier der Platz, um alle möglichen Elemente der Konfigurationsdatei zu erläutern. Ich beschreibe deshalb nur die wichtigsten. Bei den speziellen ASP.NET-Themen dieses Buchs finden Sie immer wieder Hinweise auf entsprechende Konfigurations-Einstellungen. Achten Sie bei der Bearbeitung der Datei darauf, dass Sie die korrekte Groß-/Kleinschreibung verwenden. Die Bezeichner der Konfiguration nutzen das von C# her bekannte »camelCasing«: Worte werden grundsätzlich kleingeschrieben. Bei zusammengesetzten Wörtern wird der erste Buchstabe großgeschrieben. ASP.NET wertet die Konfigurationsdatei unter Berücksichtigung der Groß-/Kleinschreibung aus. Die korrekte Schreibweise ist deswegen sehr wichtig. Wenn Sie die Konfigurationsdatei in einem einfachen Editor bearbeiten wollen, müssen Sie im encoding-Attribut des xml-Tag den Wert »Windows-1252« angeben, damit die Seite die Standard-WindowsZeichentabelle verwendet. Visual Studio .NET setzt standardmäßig »UTF-8« ein. Mit dieser Codierung können Sie den Text der Datei nur in einem UTF-8-fähigen Editor bearbeiten. Visual Studio .NET erzeugt in einer Webanwendung immer eine voreingestellte Konfigurationsdatei mit recht guten Erläuterungen der einzelnen Elemente. Die Beschreibung der einzelnen Elemente der Konfiguration finden Sie in der .NET Framework-Dokumentation unter .NET FRAMEWORK-SDK / REFERENZ / SCHEMA FÜR KONFIGURATIONSDATEIEN / ASP.NET-EINSTELLUNGSSCHEMA. Allgemeine Einstellungen
Im httpRuntime-Element können Sie einige allgemeine Einstellungen für die Anwendung vornehmen. Das Beispiel zeigt die Voreinstellungen der wichtigsten Attribute:
665
Arbeiten mit ASP.NET-Seiten
maxRequestLength steuert die maximale Größe der Anforderung (in KB).
Dieses Attribut benötigen Sie, wenn Sie Dateien uploaden (wie es ab Seite 1030 beschrieben wird). Beim Uploaden müssen Sie die maximale Größe des Request manchmal höher einstellen, als es per Voreinstellung der Fall ist. Die Voreinstellung (in machine.config definiert) ist 4096. executionTimeout steuert den Zeitrahmen, der für die Ausführung
einer Seite möglich ist (in Sekunden). Für Anwendungen mit Seiten, die sehr viel Programmcode beinhalten oder die evtl. lange auf das Öffnen einer Datenbankverbindung warten, müssen Sie die maximale Ausführungszeit höher einstellen, damit ASP.NET die Ausführung nicht vorzeitig abbricht. Wenn Sie im Attribut useFullyQualifiedRedirectUrl »true« eingeben, werden URLs in Weiterleitungen (Redirect) immer mit dem vollen Servernamen qualifiziert (z.B. wird aus der URL »/Shop/Default.asp« die URL »http://Servername/Shop/Default.asp«). Einige für ASP.NET mögliche Endgeräte wie Handys arbeiten nur korrekt mit voll qualifizierten URLs. Konfiguration der Seiten
Das folgende Beispiel (das wieder die Voreinstellungen verwendet) zeigt die möglichen Einstellungen für die Konfiguration von ASP.NETSeiten:
Mit dem Attribut buffer können Sie festlegen, ob eine Seite beim Erzeugen erst gepuffert und dann gesendet wird, wenn die Erzeugung beendet ist, oder ob die Seite sukzessive an den Browser gesendet wird. Wenn Sie die Pufferung für große Seiten abschalten, simulieren Sie eine schnellere Antwort für den Anwender, reduzieren aber auch die Performance der Seite.
666
10.8 Konfiguration einer ASP.NET-Anwendung
Arbeiten mit ASP.NET-Seiten
Über das Attribut enableSessionState können Sie die Verwaltung des Sitzungsstatus für die Seiten, die von der Konfiguration betroffen sind, abschalten. Falls Sie keine Sitzungsdaten speichern wollen, sollten Sie dies machen, um die Performance der Seiten zu erhöhen. Über enableViewState können Sie die Speicherung des Viewstate (Seite 576) abschalten. Falls die von der Konfiguration betroffenen Seiten keinen Viewstate benötigen, sollten Sie diesen abschalten, um die Performance zu erhöhen. Wenn Sie enableViewStateMAC einschalten, verwaltet ASP.NET einen zusätzlichen »Message Authentication Code« (MAC) auf der Seite, über den die Integrität der übertragenen Daten überprüft werden kann. Weil ein Einschalten dieses Attributs die Performance erheblich verringern kann, sollten Sie dieses Attribut nur dann einschalten, wenn Sie annehmen, dass die Daten auf dem Weg zwischen Client und Server auch wirklich beschädigt werden können. Das Attribut autoEventWireup definiert, ob ASP.NET die Ereignisse der Seite automatisch an Ereignisbehandlungsmethoden weiterreicht, wie ich es bereits auf der Seite 594 beschrieben habe. Eigene Daten in der Konfigurationsdatei
Die Konfigurationsdatei eignet sich auch hervorragend zum Speichern eigener Daten. So können Sie beispielsweise die Informationen, die für die Verbindung mit einer Datenbank notwendig sind, in der Konfigurationsdatei verwalten und im Programm dynamisch einlesen. Änderungen dieser Daten sind dann später sehr einfach möglich. Ihre eigenen Daten fügen Sie im Element appSettings über add-Elemente hinzu. Sie erzeugen damit im Ergebnis eine Auflistung vom Typ NameValueCollection. Deshalb müssen Sie einen Schlüssel und einen Wert übergeben. Das folgende Beispiel definiert eigene Konfigurationseinträge für eine Datenbankverbindung:
667
Arbeiten mit ASP.NET-Seiten
Im Programm können Sie diese Einträge dann über die AppSettingsEigenschaft der ConfigurationSettings-Klasse auslesen. Diese und andere Klassen zu Anwendungskonfiguration finden Sie im Namensraum System.Configuration: string server = ConfigurationSettings.AppSettings["server"]; string userId = ConfigurationSettings.AppSettings["UserId"]; bool trustedConnection = ConfigurationSettings.AppSettings[ "trustedConnection"] == "true" ? true : false;
Diese einfache Konfiguration reicht für die Praxis häufig bereits aus. Sie müssen beim Lesen der Einstellungen natürlich darauf reagieren, dass ungültige Daten gespeichert sein können. Schön wäre auch noch, wenn geänderte Daten problemlos wieder zurückgeschrieben werden können. Das scheint aber mit den Mitteln des System.Configuration-Namensraums nicht möglich zu sein, wie Diskussionen in Newsgroups belegen. Sie können die XML-Datei aber über die Klassen des System.XMLNamensraums auch direkt bearbeiten. Aber das ist ein anderes Thema ... Beachten Sie, dass auch beliebige Unterordner des Anwendungsordners eigene Anwendungskonfigurationsdateien verwalten können. Die unterschiedliche Konfiguration verschiedener Seiten wird damit erheblich vereinfacht.
10.9
Andere ASP.NET-Seiten öffnen
Die Beispiele der vorherigen Abschnitte öffnen immer wieder dieselbe ASP.NET-Seite, wenn der Anwender das Formular bestätigt. Genau wie bei ASP müssen Sie aber häufig einfach andere Seiten öffnen. Meine erste Idee, die andere Seite einfach im Attribut action des formTag anzugeben, war nicht erfolgreich: ASP.NET überschreibt das
668
10.9 Andere ASP.NET-Seiten öffnen
Arbeiten mit ASP.NET-Seiten
action-Attribut eines serverseitig ausgeführten Formulars mit dem
Namen der Seite, in der das Formular definiert ist. Das ist auch logisch, denn die Ereignisbehandlungsmethoden müssen ja ausgeführt werden. Die Lösung des Problems ist, dass Sie in der Ereignisbehandlungsmethode des Steuerelements, das die »Umleitung« bewirken soll, eine klassische Umleitung programmieren. Die Werte, die Sie in der anderen Seite auswerten wollen, müssen Sie dann – auch wieder klassisch – entweder über Session-Variablen oder über URL-Argumente übergeben. Auf der folgenden Seite soll der Anwender seinen Namen eingeben und dann das Formular bestätigen: private void HandleOK(object sender, EventArgs e) { Response.Redirect( "Auswerten.aspx?Name=" + this.userName.Text); } Andere ASP.NET-Seiten aufrufen Das ist die Seite, in der die Eingaben erfolgen. Dein Name:
669
Arbeiten mit ASP.NET-Seiten
Die Ereignisbehandlungsmethode für das Click-Ereignis des Button leitet den Request zur Auswertungsseite um und übergibt die Eingabe in einem URL-Argument. Die Auswertunggseite liest dieses Argument im Load-Ereignis aus und schreibt den Wert in ein Label: private void Page_Load(object sender, EventArgs e) { this.hello.Text = "Hallo " + Request.QueryString["Name"]; } Andere ASP.NET-Seiten aufrufen Das ist die Seite, die die Eingaben auswertet.
670
10.9 Andere ASP.NET-Seiten öffnen
Arbeiten mit ASP.NET-Seiten
Wahrscheinlich gibt es auch noch einen anderen, vielleicht eleganteren Weg, dieses Problem zu lösen. Die Diskussionen in den Dotnet-Newsgroups deuten aber auf die hier vorgestellte Lösung. Falls Sie eine bessere Lösung finden, schreiben Sie mir eine Mail.
10.10 Smarte Navigation Die smarte Navigation ist ein besonderes Feature von ASP.NET, das zurzeit nur vom Internet Explorer ab Version 5.5 unterstützt wird. Bei dieser Art der Navigation werden bei einem Postback nur die Teile der Seite aktualisiert, die auch wirklich aktualisiert werden müssen (was intern über ein iframe-Tag realisiert wird). Ohne smarte Navigation verhalten sich Web-Anwendungen etwas störrisch, wenn der Anwender das Formular bestätigt und die Seite daraufhin neu angezeigt wird:
Die neu erzeugte Seite wird wieder vom Anfang der Seite an angezeigt, die letzte Scrollposition geht also verloren, der Eingabefokus wechselt wieder auf das erste Element in der Tabulatorreihenfolge, der Bildschirm wird komplett neu aufgebaut, der letzte Stand der Seite wird in der Verlaufliste des Browsers gespeichert, sodass der Anwender über die Zurück-Funktion zum vorherigen Stand zurückgehen kann.
Smarte Navigation bewirkt, dass diese Nachteile (größtenteils) aufgehoben werden. Die aktuelle Position der Seite im Browser bleibt nach einem Submit bestehen. Der Bildschirm flimmert weniger, weil nur der geänderte Teil der Seite aktualisiert wird. Schließlich wird auch der letzte Stand der Seite nicht in der Verlaufsliste des Browsers gespeichert. Eine ASP.NET-Seite verhält sich damit ein wenig mehr wie eine WindowsAnwendung. Leider wechselte der Eingabefokus in meinen Tests doch immer wieder auf das erste Eingabeelement in der Tabulatorreihenfolge.
671
Arbeiten mit ASP.NET-Seiten
Sie schalten die smarte Navigation für einzelne Seiten entweder über das SmartNavigation-Attribut der Seite ein:
Oder Sie verwenden dazu die SmartNavigation-Eigenschaft der PageKlasse (z.B. im Load-Ereignis der Seite): this.SmartNavigation = true;
Alternativ können Sie die smarte Navigation für alle Seiten der Anwendung in der Konfigurationsdatei (web.config) einschalten:
Obwohl die smarte Navigation zurzeit nur mit dem Internet Explorer ab Version 5.5 funktioniert, können Sie diese Navigation trotzdem einschalten, auch wenn andere Browser verwendet werden sollen. Das Page-Objekt setzt einfach die normale Navigation ein, wenn der Browser die smarte Navigation nicht unterstützt.
10.11 ASP-Seiten umleiten (Redirect) In manchen Situationen ist es notwendig, den Anwender auf ein anderes Dokument umzuleiten. In einer geschützten Anwendung kann eine Umleitung z.B. sinnvoll sein, wenn der Anwender eine Seite abrufen will, aber noch nicht eingeloggt ist. Sinnvoll ist ein Umleiten aber auch für Fehlermeldungen: Tritt ein Fehler auf, wird zu einer Fehlerausgabe-Seite umgeleitet.
672
10.11 ASP-Seiten umleiten (Redirect)
Arbeiten mit ASP.NET-Seiten
Das Umleiten ist eigentlich ganz einfach: Response.Redirect(URL) führt zu einer Umleitung zur angegebenen URL. Der folgende Quellcode leitet den Anwender zur Addison-Wesley-Startseite um: Response.Redirect("http://www.addison-wesley.de");
Um Umleitungen zu verstehen, sollten Sie wissen, wie diese funktionieren. Fordert ein Client ein ASP-Dokument an, das die Anforderung umleitet, sendet der IIS eine spezielle Umleitungs-Antwort an den Client zurück: HTTP/1.1 302 Object moved Server: Microsoft-IIS/5.0 Date: Fri, 05 Oct 2001 00:24:01 GMT Location: Login.htm Content-Length: 156 Content-Type: text/html Cache-control: private Objekt verschoben Objekt verschobenKlicken Sie hierauf, um das Objekt anzuzeigen.
Ist der Browser schlau (also modern), fordert er das im Location-Feld des HTTP-Headers angegebene Dokument dann einfach an. Ältere Browser-Versionen zeigen aber auch schon einmal das HTML-Dokument im Inhaltsbereich der HTTP-Nachricht an.
10.12 Cookies Über das Session-Objekt können Sie Daten sitzungsweit speichern. Manchmal müssen Daten aber auch zwischen einzelnen Sitzungen des Anwenders verfügbar sein. Sie können Daten persistent in einer Datenbank speichern, dann benötigen Sie aber eine Verbindung der aktuellen HTTP-Anforderung mit den gespeicherten Daten, wenn Sie keinen Login in die Anwendung erzwingen. Dazu können Sie einfach Cookies verwenden. Ein Cookie (deutsch: »Plätzchen«!?) ist eine Spei-
673
Arbeiten mit ASP.NET-Seiten
chereinheit, die auf dem Rechner des Benutzers abgelegt wird. Es gibt temporäre Cookies, die der Browser nur im Arbeitsspeicher verwaltet (die Sitzungs-Id wird z.B. so gespeichert), und persistente Cookies, die auf der Festplatte abgelegt werden. Persistente Cookies besitzen ein Ablaufdatum. Der Browser überprüft beim Start oder beim Beenden, ob Cookies abgelaufen sind, und löscht diese dann automatisch. Cookies werden von einigen Anwendern abgelehnt. Ein früher Artikel in einer bekannten Computerzeitschrift hat fälschlicherweise behauptet, Cookies könnten von beliebigen Internetanwendungen ausgelesen werden. Böswillige Anwendungen könnten so z.B. das Surf- und Einkaufverhalten eines Benutzers herausfinden. Das stimmt aber so nicht. Cookies werden immer mindestens mit der Adresse des Webservers assoziiert. Andere Webserver können diese Cookies nicht lesen. Hacker kommen natürlich an die Daten heran, wenn der Rechner nicht geschützt ist, aber das ist ein anderes Thema. Ganz unproblematisch sind Cookies allerdings nicht, einige Internetfirmen arbeiten mit fiesen Tricks. Bei www.cookiecentral.com finden Sie weitere Informationen dazu. Der Benutzer kann die Speicherung von Cookies im Browser verhindern. Einige ältere oder einfache Browser unterstützen vielleicht auch keine Cookies. Sie können also im Internet nie sicher sein, dass der Browser übertragene Cookies auch speichert. Fehler entstehen dadurch nicht. Beim Lesen erhalten Sie einfach einen leeren Eintrag zurück. Sie müssen aber darauf reagieren. Nur in Intranets können Sie Cookies voraussetzen, da Sie den teilhabenden Personen mitteilen können, dass diese für Ihre Anwendung Cookies ermöglichen sollten. Wie werden Cookies zwischen Webserver und Browser ausgetauscht?
Cookies werden vom Webserver aus einfach im HTTP-Header an den Browser gesendet. Der folgende HTTP-Header aus einem Response beinhaltet zwei Cookies: HTTP/1.1 200 OK Server: Microsoft-IIS/5.0 Date: Thu, 04 Oct 2001 19:35:55 GMT Content-Length: 253
674
10.12 Cookies
Arbeiten mit ASP.NET-Seiten
Content-Type: text/html Set-Cookie: Nachname=Bayer; expires=Fri, 04-Oct-2002 20:35:54 GMT; path=/asp-beispiele Set-Cookie: Vorname=J%FCrgen; expires=Fri, 04-Oct-2002 20:35:54 GMT; path=/asp-beispiele Cache-control: private .
Der Browser wertet den Header aus und speichert die Cookies auf dem Rechner. Der Netscape speichert das Cookie des Beispiels z.B. so: Trillian Nachname Trillian Vorname
FALSE
/asp-beispiele
FALSE
1033772424
/asp-beispiele
FALSE
1033772424
Bayer FALSE
J%FCrgen
Fordert der Browser Dokumente vom Webserver an, sendet er die mit dem Webserver assoziierten Cookies wieder im HTTP-Header zum Server. Cookies, bei denen ein Pfad angegeben ist (wie im Beispiel oben), werden nur gesendet, wenn die Anforderung an diesen Pfad geht. GET /asp-beispiele/CookieDemo.asp HTTP/1.1 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/msword, */* Accept-Language: de Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0) Host: Trillian Connection: Keep-Alive Cookie: Nachname=Bayer; Vorname=J%FCrgen
675
Arbeiten mit ASP.NET-Seiten
Der Vorgang beim Speichern von Cookies ist also eigentlich ganz einfach. Wenn Sie Ihre Cookies einmal anschauen wollen, öffnen Sie für den Netscape die Datei Cookies.txt im Benutzer-Ordner von Netscape (normalerweise \Programme\Netscape\Users\default). Der Internet Explorer speichert Cookies im Ordner \Dokumente und Einstellungen\\Cookies. Speichern von Cookies
Zur Speicherung von Cookies erzeugen Sie ein Objekt der Klasse HttpCookie, definieren dessen Eigenschaften und fügen dieses Objekt der Cookies-Auflistung des Response-Objekts hinzu. Das folgende Beispiel erzeugt ein Cookie mit dem Wert einer Textbox: HttpCookie cookie = new HttpCookie("firstName", this.firstName.Text); cookie.Expires = DateTime.Now.AddDays(365); Response.Cookies.Add(cookie);
Wichtig ist z.B. die Einstellung des Verfallsdatums, wenn das Cookie persistent gespeichert sein soll. Das Beispiel setzt das Verfallsdatum auf jetzt + 365 Tage. Das Cookie ist also ein Jahr gültig. Cookies besitzen die in Tabelle 10.20 dargestellten wichtigen Eigenschaften. Tabelle 10.20: Die wichtigsten Eigenschaften eines CookieObjekts
676
Eigenschaft
Bedeutung
Domain
In dieser lesegeschützten Eigenschaft können Sie eine Domäne angeben, für die das Cookie dann gelten soll. Geben Sie einen vollen Namen an, gilt das Cookie nur für diesen einen Rechner. Sie können aber auch Teildomänen angeben. Domain="galaxysolutions.de" bewirkt dann z. B., dass alle Anforderungen an www.galaxysolutions.de, search.galaxysolutions.de etc. das Cookie erhalten. Wenn Sie diese Eigenschaft nicht angeben, verwendet das Response-Objekt den Domänennamen des Webservers bzw. im Intranet den Rechnernamen.
Expires
definiert das Ablaufdatum des Cookie. Wenn Sie diese Eigenschaft nicht setzen, läuft das Cookie sofort ab, wird also nur temporär im Browser gespeichert.
10.12 Cookies
Arbeiten mit ASP.NET-Seiten
Eigenschaft
Bedeutung
HasKeys
Über diese schreibgeschützte Eigenschaft können Sie überprüfen, ob ein Cookie selbst wieder eine Auflistung speichert.
Name
In ASP.NET können Sie über diese Eigenschaft den Namen des Cookie setzen und lesen.
Path
Diese lesegeschützte Eigenschaft definiert den Pfad der Dokumente, die dieses Cookie gesendet bekommen sollen. Wenn Sie nichts eintragen, wird der aktuelle Pfad verwendet. Wenn Sie hier "\" eintragen, wird das Cookie an alle Dokumente des Webservers gesendet.
Secure
Diese lesegeschützte Eigenschaft definiert, ob das Cookie sicher ist.
Tabelle 10.20: Die wichtigsten Eigenschaften eines CookieObjekts (Forts.)
Auslesen von Cookies
Cookies lesen Sie über das Request-Objekt aus. Um herauszufinden, ob ein Cookie existiert, müssen Sie mit null vergleichen. Das folgende Beispiel liest zwei Cookies aus und reagiert auf deren Nichtexistenz: string name = "Unbekannter"; if (Request.Cookies["firstName"] != null && Request.Cookies["lastName"] != null) { name = Request.Cookies["firstName"].Value + " " + Request.Cookies["lastName"].Value; }
10.13 Caching ASP.NET ist in der Lage, das HTML-Ergebnis von aspx-Seiten oder Teile der Seite für eine definierte Zeit zwischenzuspeichern. Wird die Seite innerhalb dieses Zeitraums erneut angefordert, liefert ASP.NET einfach die zwischengespeicherten Daten. Für Seiten, deren Programm zeitaufwändig ist, kann das einen erheblichen Performancevorteil bringen. Sie können dieses Antwort-Caching vollautomatisch ausführen lassen, indem Sie die @OutputCache-Direktive verwenden. Daneben können Sie das Caching-Verhalten der Seite auch im Programm bestimmen. Dazu
677
Arbeiten mit ASP.NET-Seiten
stehen Ihnen die Cache-Eigenschaft der Page-Klasse (zum komplexen Speichern und Lesen der Daten, inklusive der Bestimmung von Abhängigkeiten und Verfallsdaten), die Cache-Eigenschaft des Response-Objekts (zur Bestimmung des allgemeinen Caching-Verhaltens) und die Methoden AddCacheItemDependencies, AddCacheItemDependency, AddFileDependencies und AddFileDependency des Response-Objekts (zur Bestimmung von Abhängigkeiten) zur Verfügung. Sie können auch beide Verfahren gemeinsam einsetzen, beispielsweise um das vollautomatische Caching von einer Datei abhängig zu machen: Ändert sich die Datei, wird die Seite neu erzeugt. Das gesamte, komplexe Verfahren kann ich im Buch nicht beschreiben. Suchen Sie bei msdn.microsoft.com nach »Caching Page Output« um mehr darüber zu erfahren. Das einfache Verfahren verwendet die @OutputCache-Direktive für ein automatisches Caching der Seite. Im Attribut Duration geben Sie an, wie lange das erzeugte Dokument im Cache bleiben soll. Das Attribut VaryByParam wird erst später wichtig. Das folgende Beispiel liest einige Daten der Artikel der Northwind-Datenbank aus: Caching-Demo private void Page_Load(object sender, EventArgs s) { this.info.Text = "Seite erzeugt am " + DateTime.Now.ToShortDateString() + " um " + DateTime.Now.ToLongTimeString() + " Uhr."; /* Alle Produkte der Northwind-Datenbank ermitteln */ SqlConnection connection = new SqlConnection( @"Server=(local)\NetSDK;Database=Northwind;" +
678
10.13 Caching
Arbeiten mit ASP.NET-Seiten
"Trusted_Connection=Yes"); connection.Open(); SqlCommand command = new SqlCommand( "SELECT ProductId, ProductName, UnitPrice " + "FROM Products", connection); SqlDataReader reader = command.ExecuteReader(); /* Reader an DataGrid anbinden */ this.products.DataSource = reader; this.products.DataBind(); } Caching-Demo: Produkte ermitteln
Das Beispiel nutzt ein DataGrid-Steuerelement, das an ein DataReaderObjekt gebunden wird, um die eingelesenen Daten darzustellen. Kapitel 13 geht ausführlich auf diese Objekte ein. Wenn die Seite das erste Mal ausgeführt wird, schreibt ASP.NET das fertige Dokument in den Cache. Wird die Seite danach erneut ausgeführt, ist das Ergebnis wesentlich schneller im Browser als zuvor.
679
Arbeiten mit ASP.NET-Seiten
Abbildung 10.8: Die erzeugte Artikelliste
Innerhalb der im Duration-Attribut angegebenen Zeit wird die Seite nicht erneut ausgeführt. Änderungen, die zwischenzeitlich an den Daten in der Datenbank vorgenommen werden, sind innerhalb dieser Zeit nicht sichtbar. Und das gilt für alle Anforderungen, auch von anderen Rechnern aus. Erst, wenn diese Zeitspanne abgelaufen ist, wird das Ergebnis neu erzeugt. Die Seiten sind zwar nicht gerade hochaktuell, werden dafür aber in den meisten Fällen sehr schnell angezeigt. Probleme treten beim einfachen Caching allerdings auf, wenn die Ausführung der Seite über Formulareingaben oder URL-Argumente gesteuert wird. Die Artikelabfrage könnte beispielsweise so erweitert werden, dass die Kategorie der abzufragenden Artikel in der URL übergeben wird: private void Page_Load(object sender, EventArgs s) { ...
680
10.13 Caching
Arbeiten mit ASP.NET-Seiten
/* Kategorie-ID auslesen */ int categoryId; try { categoryId = Convert.ToInt32( Request.QueryString["CategoryId"]); } catch { categoryId = 1; } ... SqlCommand command = new SqlCommand( "SELECT ProductId, ProductName, UnitPrice " + "FROM Products WHERE CategoryId = " + categoryId, connection); ... }
Ruft nun ein Anwender die Seite mit der Kategorie 1 auf, wird das Ergebnis in den Cache geschrieben. Ruft danach ein anderer Anwender die Seite mit einer anderen Kategorie auf, erhält der das gecachte und damit falsche Ergebnis des ersten Anwenders. ASP.NET bietet dafür aber auch eine Lösung: Sie geben das ausgewertete Argument im VaryByParam-Attribut der @OutputCache-Direktive an:
Nun erzeugt ASP.NET für jede Anforderung der Seite mit einem unterschiedlichen Wert im URL-Argument eine separate Seite im Cache und das Ergebnis ist immer in Ordnung. Das Ganze funktioniert natürlich auch mit Formulareingaben. 681
Arbeiten mit ASP.NET-Seiten
10.14 Übungen 1.
Sie wollen eine aspx-Seite im Load-Ereignise initialisieren. Wie können Sie verhindern, dass diese Initialisierung bei einem wiederholten Aufruf der Seite durch die Bestätigung des Formulars erneut aufgerufen wird?
2.
Was ist der grobe Unterschied zwischen serverseitig ausgeführten HTML- und ASP-Steuerelementen?
3.
Erstellen Sie eine aspx-Seite, die beim Laden überprüft, ob die Uhrzeit nach 17 Uhr ist. Ist es später als 17 Uhr, soll automatisch zur Seite autsch.rtl.de umgeleitet werden.
4.
Eine schwierige Aufgabe: Eine Webanwendung soll nur von Benutzern verwendet werden, die sich registriert haben. Zur Registration sind der Vor- und Nachname und die E-Mail-Adresse notwendig. Ein nicht registrierter Benutzer soll beim Öffnen einer beliebigen Seite der Anwendung automatisch zur Registrationsseite umgeleitet werden. Hat der Benutzer sich erfolgreich registriert (wobei nur geprüft wird, ob er Daten in die entsprechenden Felder eingegeben hat), sollen seine Daten so gespeichert werden, dass sie bei der nächsten Anforderung einer Seite zur Verfügung stehen (allerdings nicht in einer Datenbank). Viel Spaß .
5.
Sie haben in einer aspx-Seite ein ASP.NET-Steuerelement angelegt:
Ü
Das Steuerelement wird im Browser nicht angezeigt. Warum?
682
10.14 Übungen
Steuerelemente
11 Steuerelemente Im Namensraum System.Web finden Sie eine große Anzahl von HTML- und ASP-Steuerelementen. Dieses Kapitel zeigt, wie Sie mit diesen Steuerelementen umgehen. Einige, wie das DataGrid- und das Repeater-Steuerelement, werden allerdings noch nicht in diesem Kapitel, sondern erst im nächsten, bei der Datenbindung, beschrieben. Diese Steuerelemente machen ohne Datenbindung keinen Sinn. Ich beschreibe zunächst die HTML-Steuerelemente, dann die ASPSteuerelemente und zeige schließlich noch, wie Sie eigene Steuerelemente erzeugen.
11.1 Die HTML-Steuerelemente Die HTML-Steuerelemente von ASP.NET repräsentieren HTML-Elemente auf der aspx-Seite. Mit diesen Steuerelementen können Sie recht flexibel programmieren. So können Sie deren Eigenschaften im Programm auswerten und modifizieren und neue HTML-Elemente dynamisch hinzufügen. Die HTML-Steuerelemente besitzen außerdem einige Ereignisse, die Sie im Quellcode auswerten können, wenn die Steuerelemente serverseitig ausgeführt werden. Da Sie zum programmgesteuerten Zugriff besser die konsistenteren ASPSteuerelemente verwenden sollten (siehe ab Seite 688), beschreibe ich die HTML-Steuerelemente hier nur grundlegend. Beachten Sie, dass Sie bei diesen Steuerelementen im Programm mit den ganz normalen Tag-Attributen und CSS-Eigenschaften arbeiten, was schon recht nervig sein kann. Die Tag-Attribute erreichen Sie über die Attributes-Auflistung, die CSS-Eigenschaften über die Style-Auflistung der Steuerelemente. Ein einfaches Setzen der Eigenschaften wie bei den ASP-Steuerelementen ist bei den HTML-Steuerelementen nicht möglich.
683
Steuerelemente
11.1.1 Die Basisklassen HtmlControl und HtmlContainerControl Die Klasse HtmlControl
Die HTML-Steuerelemente, die im Namensraum System.Web.UI. HtmlControls definiert sind, sind von der Klasse HtmlControl abgeleitet. Sie besitzen also immer die Eigenschaften, Methoden und Ereignisse dieser Basisklasse. Die wichtigsten Eigenschaften von HtmlControl finden Sie in Tabelle 11.1, die Methoden in Tabelle 11.2, die Ereignisse in Tabelle 11.3. Tabelle 11.1: Die wichtigsten Eigenschaften der Basisklasse HtmlControl
684
Eigenschaft
Bedeutung
Attributes
Über diese Auflistung können Sie die Werte der einzelnen HTML-Attribute des Elemente lesen und schreiben. Geben Sie dazu den Namen des Attributs als String-Index an.
ClientID
gibt die eindeutige ID zurück, die ASP.NET für das Steuerelement für die Assoziierung clientseitiger JavaScript-Programme mit dem Element vergeben hat.
Controls
gibt eine ControlCollection-Auflistung zurück mit Verweisen auf alle Steuerelemente, die Child des Steuerelements sind.
Disabled
bestimmt, ob das Steuerelement aktiviert oder deaktiviert ist.
EnableViewState
Über diese Eigenschaft können Sie bestimmen, ob das Steuerelement seinen Status im Viewstate speichert.
ID
spezifiziert die ID des Steuerelements.
Page
gibt eine Referenz auf die Seite, die das Steuerelement enthält, zurück.
Parent
gibt eine Referenz auf das Steuerelement, in dem das Steuerelement eingebettet ist (sofern dies der Fall ist), zurück.
Style
Über diese Auflistung erreichen Sie die CSS-Eigenschaften des Steuerelements. Geben Sie dazu den Namen der Eigenschaft als Schlüssel an. Über die Keys-Eigenschaft des Style-Objekts können Sie eine Auflistung der verfügbaren CSS-Eigenschaften ermitteln.
TagName
Über diese Eigenschaft können Sie den Namen des Tags auslesen (z.B. »a« für einen Anker oder »div« für ein div-Element).
Visible
definiert, ob das Steuerelement sichtbar ist.
11.1 Die HTML-Steuerelemente
Steuerelemente
Methode
Beschreibung
DataBind
Diese Methode wird verwendet, wenn ein Steuerelement an eine Datenquelle angebunden wurde, um die Daten anzuzeigen. Datenbindung wird in Kapitel 13 behandelt.
FindControl
Über diese Methode können Sie den Parent (das übergeordnete Element) des Steuerelements nach einem anderen ChildSteuerelement durchsuchen.
HasControls
gibt true zurück, wenn das Steuerelement selbst ein Parent für andere Steuerelemente ist.
Ereignis
Beschreibung
DataBinding
wird aufgerufen, wenn die Seite eine Datenquelle an das Steuerelement bindet, damit dieses die enthaltenen Daten anzeigt. Datenbindung wird in Kapitel 13 behandelt.
Disposed
wird am Ende des Render-Vorgangs der Seite aufgerufen, kurz bevor das Steuerelement aus dem Speicher entfernt wird.
Init
wird aufgerufen, wenn die Seite das Steuerelement vor dem Rendern erzeugt.
Load
wird aufgerufen, wenn das Steuerelement in die Seite geladen wird. Im Load-Ereignis können Sie im Gegensatz zum InitEreignis auf den Viewstate des Elements und auf Formulareingaben zugreifen.
PreRender
wird aufgerufen, bevor das Steuerelement seinen Inhalt rendert.
Unload
wird am Ende des Render-Vorgangs der Seite aufgerufen, wenn das Steuerelement entladen wird.
Tabelle 11.2: Die Methoden der Basisklasse HtmlControl
Tabelle 11.3: Die Ereignisse der Basisklasse HtmlControl
Die Klasse HtmlContainerControl
Die Klasse HtmlContainerControl ist von HtmlControl abgeleitet und dient als Basisklasse für alle HTML-Elemente, die ein schließendes Tag besitzen und damit Behälter für andere Elemente sein können. Diese Klasse besitzt zusätzlich die in Tabelle 11.4 aufgelisteten Eigenschaften.
685
Steuerelemente
Tabelle 11.4: Die Eigenschaften der Basisklasse HtmlContainerControl
Eigenschaft
Bedeutung
InnerHtml
verwaltet den HTML-Quellcode des Inhalts des Elements.
InnerText
verwaltet den reinen Text des Inhalts des Elements (ohne Tags).
11.1.2
Übersicht über die Steuerelemente
Die HTML-Steuerelemente werden in ASP.NET über Klassen repräsentiert, die Sie im Namensraum System.Web.UI.HtmlControls finden. Ich stelle diese Steuerelemente hier nur kurz vor, weil Sie HTML ja sicher recht gut kennen (wenn nicht, lesen Sie vielleicht einfach den Artikel über HTML, den Sie auf der Buch-CD finden). Die linke Spalte der Tabelle zeigt den Namen des Steuerelements, wie er in der Toolbox von Visual Studio aufgelistet wird. Die angegebenen Klassen können Sie verwenden, wenn Sie diese Steuerelemente dynamisch erzeugen wollen (siehe Seite 749). Tabelle 11.5: Übersicht über die HTML-Steuerelemente
Visual Studio-Name
Klasse
Beschreibung / HTML-Darstellung
Generic (nicht in der Toolbox von Visual Studio)
HtmlGenericControl
Ein generisches Steuerelement kann jedes beliebige Tag repräsentieren. Solch ein Steuerelement können Sie verwenden, um beliebige HTML-Elemente dynamisch zu erzeugen (siehe Seite 749). Dieses Steuerelement erlaubt als einziges das Beschreiben der Eigenschaft TagName.
Anchor (nicht in der Toolbox von Visual Studio)
HtmlAnchor
Anker:
Form (nicht in der Toolbox von Visual Studio
HtmlForm
Label
HtmlGenericControl mit TagName
= "div"
686
Formular:
11.1 Die HTML-Steuerelemente
div-Element:
Steuerelemente
Visual Studio-Name
Klasse
Beschreibung / HTML-Darstellung
Button
HtmlInputButton mit TagName = "but-
Normaler Schalter:
ton" (im Konstruktor übergeben) Reset Button
HtmlInputButton mit TagName =
"reset" (im Konstruktor übergeben) Submit Button
HtmlInputButton mit TagName = "sub-
mit" (im Konstruktor übergeben) Text Field
HtmlInputText
Tabelle 11.5: Übersicht über die HTML-Steuerelemente (Forts.)
Reset-Schalter:
Submit-Schalter:
Textfeld:
Text Area
HtmlTextArea
Mehrzeiliges Textfeld:
File Field
HtmlInputFile
Dateiauswahlfeld:
Password Field
HtmlInputText mit TagName = "pass-
word" (im Konstruktor übergeben) Checkbox
HtmlInputCheckBox
Passwort-Textfeld:
Checkbox:
Radio Button
HtmlInputRadioButton
ein Radio Button:
Hidden
HtmlInputHidden
Verstecktes Feld:
Table
HtmlTable
Tabelle:
687
Steuerelemente
Tabelle 11.5: Übersicht über die HTML-Steuerelemente (Forts.)
Visual Studio-Name
Klasse
Beschreibung / HTML-Darstellung
HtmlTableRow
Tabellenzeile:
HtmlTableCell mit TagName = "th" oder
Tabellenzelle:
"td"
oder
| |
Flow Layout Panel
HtmlGenericControl mit TagName
= "div"
Grid Layout Panel
Image
div-Element mit dem Visual Studio-spezifischen Attribut ms_positioning = "FlowLayout". Dieses Element dient als Container, auf dem Sie andere Elemente im FlowLayout anlegen können.
= "div"
ein div-Element mit dem Visual Studiospezifischen Attribut ms_positioning = "GridLayout". Dieses Element dient als Container, auf dem Sie andere Elemente im Grid-Layout anlegen können.
HtmlImage
Bild-Element:
HtmlGenericControl mit TagName
Listbox Dropdown Horizontal Rule
>1
HtmlSelect mit Size
Mehrzeiliges Listenfeld:
HtmlSelect mit Size
Einzeiliges Listenfeld:
=1
HtmlGenericControl mit TagName
Linie:
= "hr"
11.2 Die ASP-Steuerelemente Die ASP-Steuerelemente sind wesentlich flexibler und einfacher anzuwenden als die HTML-Steuerelemente. Ich beschreibe diese Steuerelemente deshalb wesentlich intensiver als deren HTML-Brüder und -Schwestern. Die Steuerelemente, deren Verwendung nur mit Datenquellen Sinn macht (DataGrid, DataList und Repeater), werden nicht in diesem Kapitel, sondern erst in Kapitel 13 beschrieben.
688
11.2 Die ASP-Steuerelemente
Steuerelemente
11.2.1
Eigenschaftswerte in HTML und im Programm
Wenn Sie bei der Deklaration eines ASP-Steuerelements Eigenschaftswerte voreinstellen, verwenden Sie die normalen HTML-Einheiten. Ein Farbwert kann beispielsweise mit einer benannten Farbe wie »red« oder mit einem hexadezimal dargestellten Farbwert wie #FF0000 angegeben werden. Wenn Sie aber im Programm auf Eigenschaften zugreifen, verwenden Sie meist Werte aus Aufzählungen oder Klassen. Eine Farbe wird beispielsweise über Eigenschaften oder Methoden der Klasse System.Drawing.Color oder System.Drawing.ColorTranslator definiert. Die Deklaration eines Label mit blauem Hintergrund kann demnach folgendermaßen erfolgen:
Im Programm kann die Farbe über die Eigenschaft Blue der ColorKlasse gesetzt werden: this.hello.BackColor = System.Drawing.Color.Blue;
ASP-Steuerelemente sind echte .NET-Klassen und entsprechen damit den .NET-Regeln. Eine so intensiv objektorientierte Umgebung wie .NET lässt typunsichere Zuweisungen, wie die eines Strings zur Definition einer Farbe, nicht zu. Im HTML-Code der Seite können Sie natürlich Fehler machen. So können Sie beispielsweise eine nicht existierende Farbe setzen:
Beim Kompilieren der Seite führen solche ungültigen Zuweisungen dann zu einem Parser-Fehler. Im Beispiel Fall ist das der Fehler »GalaxyBlue ist kein gültiger Wert für Int32«. Int32 wird übrigens deswegen als Datentyp angegeben, weil Farbwerte intern als 32-Bit-Integerwert gespeichert werden.
689
Steuerelemente
Positions- und Größenangaben
Für die Einstellung der Position eines Steuerelements existieren eigenartigerweise keine Eigenschaften. Die Position können Sie lediglich über CSS-Eigenschaften definieren: this.demoLabel.Style["position"] = "absolute"; this.demoLabel.Style["left"] = "50px"; this.demoLabel.Style["top"] = "50px";
Dabei müssen Sie darauf achten, dass Sie den Positionierungs-Stil einstellen. Im Beispiel verwende ich eine absolute Positionierung. Im HTML-Quellcode setzen Sie natürlich auch die CSS-Eigenschaften:
Die Breite und Höhe eines Steuerelements können Sie im Programm über die Eigenschaften Width und Height einstellen. Sie sollten beachten, dass die Steuerelemente Größenangaben über Width und Height für die Netscape-Browser (und wahrscheinlich auch für andere) in der Version 1.0 des .NET Frameworks nicht in die entsprechenden CSS-Eigenschaften und auch nicht in Tag-Attribute umsetzen. Für diese Browser bleiben Größenangaben also unberücksichtigt. Verwenden Sie deshalb lieber gleich die CSS-Eigenschaften zur Einstellung der Breite und der Höhe. Maßangaben besitzen bei den ASP.NET-Steuerelementen den Datentyp System.Web.UI.WebControls.Unit, der eine Klasse ist. Wenn Sie im Programm eine Maßangabe setzen wollen, müssen Sie entweder die Eigenschaft Empty (= keine Angabe) oder eine der Methoden dieser Klasse verwenden.
690
11.2 Die ASP-Steuerelemente
Steuerelemente
Methode
Beschreibung
Unit Parse( string s [, CultureInfo culture])
Über diese Methode können Sie einen im ersten Argument übergebenen beliebigen Wert als Maßangabe verwenden. Sie können die unter HTML möglichen Einheiten angeben (px, em, cm etc.). Im optionalen zweiten Argument können Sie ein Objekt der Klasse CultureInfo übergeben, das die bei der Konvertierung zu verwendende Kultur spezifiziert. Voreinstellung ist die für den aktuellen Thread eingestellte Kultur.
Unit Percentage( double n)
erzeugt einen Prozentwert, dessen Wert im Argument n übergeben wird.
Unit Pixel( double n)
erzeugt einen Pixel-Wert.
Unit Point( double n)
erzeugt einen Punkt-Wert.
Tabelle 11.6: Die Methoden der Unit-Klasse zur Erzeugung von Maßangaben
Die Definition einer Maßangabe ist damit vielleicht etwas kompliziert, aber .NET ist eben konsequent objektorientiert. Und Werte wie »100px« oder »2cm« passen nicht in das OOP-Konzept. Wenn Sie diese Werte aber trotzdem über die in HTML möglichen Einheiten angeben wollen, können Sie ja die Parse-Methode verwenden: demoLabel.Width = Unit.Parse("4cm"); demoLabel.Height = Unit.Parse("6mm");
Im HTML-Code der Seite verwenden Sie die unter HTML möglichen Einheiten wie px, cm und mm:
Farbangaben
Eigenschaften, die Farbwerte speichern, besitzen den Typ System.Drawing.Color. Color ist eine Klasse, die konstante Eigenschaften für die gängigen Farben besitzt. Wenn Sie eine Farbe im Programm ändern wollen, können Sie direkt eine der Farbeigenschaften zuweisen: demoLabel.BackColor = Color.Red;
691
Steuerelemente
Der Namensraums System.Drawing wird vom Compiler für ASP.NETAnwendungen automatisch importiert, weswegen Sie diesen Namensraum nicht angeben müssen. Sie können aber auch einen beliebigen Farbwert verwenden. Diesen können Sie als RGB-Wert über die Methode FromArgb angeben: demoLabel.BackColor = Color.FromArgb(255,128,0);
Im ersten Argument definieren Sie den Rotanteil, im zweiten den Grünanteil und im dritten den Blauanteil mit Werten zwischen 0 und 255. Das Beispiel erzeugt ein (schönes) Orange. Alternativ können Sie den Farbwert auch als Integer-Wert übergeben (ein RGB-Wert ist eigentlich ein Integer-Wert, der die drei Farben in den ersten drei Byte definiert). Über die Methode FromName können Sie einen Farbwert auch als String (in der englischen Schreibweise) definieren: demoLabel.BackColor = Color.FromName("Orange");
Wenn Sie andere Farbwerte, wie z.B. einen 32-Bit-Windows-Farbwert oder eine HTML-Farbangabe, als Farbe verwenden wollen, können Sie die Methoden der Klasse System.Drawing.ColorTranslator zur Übersetzung dieser Farbwerte verwenden. Einen HTML-Farbwert stellen Sie dann beispielsweise so ein: demoLabel.BackColor = ColorTranslator.FromHtml("#FFBB00");
Wenn Sie Farbwerte bei der Deklaration eines Steuerelements voreinstellen wollen, verwenden Sie die von HTML her bekannten benannten Farben (z.B. »Orange«) oder die hexadezimale Darstellung der Farbe (z. B: #FFBB00):
692
11.2 Die ASP-Steuerelemente
Steuerelemente
Rahmenangaben
Die Eigenschaften, die die Definition eines Rahmens betreffen, sind vom Datentyp System.Web.UI.WebControls.BorderStyle, der eine Aufzählung der folgenden Werte ist: Dashed, Dotted, Double, Groove, Inset, None, Outset, NotSet, Ridge, Solid. Wenn Sie z.B. den Rahmen eines Label im Programm auf gepunktet setzen wollen, verwenden Sie einen dem folgenden ähnlichen Quellcode: demoLabel.BorderStyle = BorderStyle.Dotted;
Im HTML-Code verwenden Sie zur Definition des Rahmens Strings, die mit den Namen der Elemente der BorderStyle-Aufzählung identisch sind:
Die Schriftart
Schriftarten werden im Programm immer über ein Objekt der Klasse FontInfo definiert. Diese Klasse besitzt die Eigenschaften Name (der Name der Schrift), Names (Liste mehrerer Namen), Size (Größe), Bold (fett), Italic (kursiv), Overline (überstrichen), Strikeout (durchgestrichen) und Underline (unterstrichen). Die Eigenschaft Names ist ein Array aus Schriftarten-Namen. Wenn Sie mehrere Namen angeben wollen, verwenden Sie diese Eigenschaft an Stelle der Name-Eigenschaft. Sie müssen dazu ein String-Array erzeugen. Beim Erzeugen können Sie das Array direkt initialisieren: demoLabel.Font.Names = new string[] {"Verdana", "Times"};
Die Size-Eigenschaft ist vom Typ FontUnit. Diese Struktur besitzt die Eigenschaften Large, Larger, Medium, Small, Smaller, XLarge, XSmall, XXLarge und XXSmall, über die Sie die Schriftartgröße relativ zur Default-Größe des Dokuments bestimmen können: demoLabel.Font.Size = FontUnit.Large;
693
Steuerelemente
Alternativ können Sie die Größe über die Point-Methode auch in Punkten setzen: demoLabel.Font.Size = FontUnit.Point(12);
Wenn Sie eine andere Einheit verwenden wollen, müssen Sie die Parse-Methode verwenden, der Sie einen String mit dem Wert und
der Einheit übergeben. Als Einheit können Sie eine der in HTML gültigen Einheiten angeben: demoLabel.Font.Size = FontUnit.Parse("2em");
In der Deklaration des Steuerelements verwenden Sie die folgenden Attribute, um die Schriftart einzustellen: Font-Name, Font-Names, Font-Size, Font-Bold, Font-Italic, Font-Overline, Font-Strikeout, Font-Underline. Alle Attribute bis auf Font-Name, Font-Names und Font-Size sind boolesche Attribute, die die Werte True und False akzeptieren. In FontNames können Sie eine kommabegrenzte Liste mit den zu verwendenden Schriftartnamen angeben. Das Attribut Font-Size akzeptiert die unter HTML üblichen Einheiten wie pt (Punkte), px (Pixel), mm, cm etc. 11.2.2
Das alte Browserproblem im neuen .NET
In der Version 1.0 des .NET Frameworks werden die Eigenschaften der ASP-Steuerelemente konsequent leider nur für den Internet Explorer umgesetzt. Die Netscape-Browser erhalten dagegen oft unvollständigen HTML-Code. Größenangaben, Hintergrundfarben und Rahmenangaben werden beispielsweise für den Netscape 6, der ja CSS beherrscht, nicht in CSS-Eigenschaften umgesetzt. Das folgende Beispiel soll das Problem demonstrieren. Die problematischen Deklarationen sind kursiv formatiert:
Wie Abbildung 11.2 zeigt, lässt die Darstellung im Netscape 6 zu wünschen übrig. Abbildung 11.1: Die Beispielseite im Internet Explorer
Abbildung 11.2: Die Beispielseite im Netscape 6
695
Steuerelemente
Der HTML-Quellcode für die Darstellung der Steuerelemente, den der Internet Explorer erhält, ist in Ordnung: E-Mail-Adresse:
Der Netscape erhält hingegen unvollständigen Code: E-Mail-Adresse:
Scheinbar erkennen die Steuerelemente nicht, dass es sich um den Netscape 6 handelt, der ja prinzipiell dieselben CSS-Fähigkeiten besitzt wie der Internet Explorer. Das ist für Programmierer, die für das Internet entwickeln, natürlich ein großes Hindernis. Schließlich setzen noch viele Anwender den Netscape 4 oder 6 ein. Da bleibt dann nur noch, direkt CSS zu verwenden und auf die problematischen Eigenschaften der Steuerelemente zu verzichten. 11.2.3
CSS verwenden
Statt die Layout-Eigenschaften der Steuerelemente individuell einzustellen, können Sie auch wie unter HTML gewohnt CSS-Stilvorlagen einsetzen. Neben der Inline-Deklaration über das style-Attribut sind natürlich auch Stilvorlagen möglich, die innerhalb des Dokuments oder in externen Dokumenten deklariert sind. Externe Dokumente binden Sie wie gewohnt ein. Wenn Sie CSS-Klassen verwenden, was unter ASP.NET wahrscheinlich die beste Lösung ist, weil einige ASP-Steuerelemente je 696
11.2 Die ASP-Steuerelemente
Steuerelemente
nach Einstellung in unterschiedliche HTML-Tags umgesetzt werden, geben Sie die Klasse des Steuerelements im Attribut CssClass an. Die folgende CSS-Datei soll in eine aspx-Seiten eingebunden werden: @charset "iso-8859-1"; body { background-color:#F0F0F0; } .HeaderLabel { border:thin solid black; background-color:blue; color:white; padding:3px; width:100%; text-align:center; font-family:verdana; font-size:12pt; font-weight:bold; margin-bottom:10px; } .CaptionLabel { width:200px; border:thin solid black; padding:2px; margin-bottom:5px; background-color:gray; color:blue; font-family:verdana; font-size:10pt; font-weight:bold; } .InputField { font-family:verdana; font-size:10pt; margin-bottom:5px; }
697
Steuerelemente
Das Einbinden der CSS-Datei geschieht wie gewohnt über das linkTag. In den Tags der Steuerelemente geben Sie die CSS-Klasse über das Attribut CssClass an: CSS verwenden
698
11.2 Die ASP-Steuerelemente
Steuerelemente
Die Verwendung von CSS besitzt (wie schon unter HTML) den Vorteil, dass Sie den Stil Ihrer Seiten global in einer oder mehreren CSSDateien verwalten und über diese Dateien sehr schnell ändern können. 11.2.4
Die Basisklasse WebControl
Alle Webform-Steuerelemente sind von der Basisklasse WebControl abgeleitet. Sie besitzen deshalb die Eigenschaften, Methoden und Ereignisse dieser Klasse. In Tabelle 11.7 finden Sie die wichtigsten Eigenschaften, Tabelle 11.8 beschreibt die Methoden dieser Klasse und Tabelle 11.9 die Ereignisse. Eigenschaft
Beschreibung
AccessKey
In dieser Eigenschaft können Sie einen Tastatur-Shortcut für das Steuerelement definieren.
Attributes
Attributes ist eine Auflistung aller Attribute des Steuerelements. Über die Add-Methode können Sie neue, benutzerdefi-
Tabelle 11.7: Die wichtigsten Eigenschaften der Basisklasse WebControl
nierte Attribute hinzufügen (die Sie vielleicht bei der Auswertung einer Seite wieder auslesen). Über den Indizierer, dem Sie den Namen des Attributs übergeben, erreichen Sie die gesetzten Attribute und können deren Wert lesen und schreiben. So können Sie Attributwerte definieren, für die ein Steuerelement keine eigenen Eigenschaften anbietet. BackColor
spezifiziert die Hintergrundfarbe.
BorderColor
definiert die Farbe des Rahmens.
BorderStyle
definiert den Stil des Rahmens.
BorderWidth
gibt die Breite des Rahmens an.
ClientID
gibt die eindeutige ID zurück, die ASP.NET für das Steuerelement für die Assoziierung clientseitiger JavaScript-Programme mit dem Element vergeben hat.
Controls
Controls referenziert eine Auflistung aller Child-Steuerelemente, die das Steuerelement (als Container) beinhaltet.
699
Steuerelemente
Tabelle 11.7: Die wichtigsten Eigenschaften der Basisklasse WebControl (Forts.)
Eigenschaft
Beschreibung
CssClass
In dieser Eigenschaft können Sie einen CSS-Klassennamen definieren um das Steuerelement mit einer CSS-Klasse zu verbinden und damit deren Stileigenschaften einzusetzen.
Enabled
legt fest, ob das Steuerelement aktiviert ist.
EnableViewState
Über diese Eigenschaft können Sie festlegen, ob das Steuerelement seinen Status im Viewstate speichert.
Font
definiert die Schriftart.
ForeColor
spezifiziert die Vordergrundfarbe.
Height
gibt die Höhe des Steuerelements an.
ID
gibt die ID des Steuerelements zurück.
Parent
Parent ist eine Referenz zum Parent-Steuerelement, falls das
Steuerelement auf einem solchen angelegt ist.
Tabelle 11.8: Die Methoden der Basisklasse WebControl
700
Style
Über diese Auflistung erreichen Sie die CSS-Eigenschaften des Steuerelements. Geben Sie dazu den Name der Eigenschaft als Schlüssel an. Über die Keys-Eigenschaft des Style-Objekts können Sie eine Auflistung der verfügbaren CSS-Eigenschaften ermitteln.
TabIndex
spezifiziert den Tabulator-Index mit einer Integer-Zahl, die bei 0 beginnt.
ToolTip
Über diese Eigenschaft können Sie einen Tooltip für das Element einrichten.
Visible
definiert, ob das Steuerelement sichtbar ist.
Width
spezifiziert die Breite des Steuerelements.
Methode
Beschreibung
DataBind
Diese Methode wird verwendet, wenn ein Steuerelement an eine Datenquelle angebunden wurde, um die Daten anzuzeigen. Datenbindung wird in Kapitel 13 behandelt
FindControl
Über diese Methode können Sie den Parent des Steuerelements nach einem anderen Steuerelement durchsuchen, das auch zu diesem Parent gehört.
HasControls
gibt true zurück, wenn das Steuerelement selbst ein Parent für andere Steuerelemente ist
11.2 Die ASP-Steuerelemente
Steuerelemente
Ereignis
Beschreibung
DataBind
wird aufgerufen, wenn das Steuerelement an eine Datenquelle gebunden wird.
PreRender
wird aufgerufen, wenn das Steuerelement beginnt, seinen Inhalt zu rendern.
11.2.5
Tabelle 11.9: Die wichtigsten Ereignisse der Klasse WebControl
Das Label-Steuerelement
Das Label ist ein sehr einfaches Steuerelement zur Darstellung von Text. Es wird in HTML über einen Span erzeugt. Die wichtigste Eigenschaft dieses Steuerelements ist Text, die den Inhalt verwaltet. Über die von WebControl geerbten Eigenschaften können Sie die Farbe und den Rahmen des Label bestimmen. Eine Eigenschaft für den inneren Abstand des Rahmens zum Text besitzt das Label nicht, dafür müssen Sie CSS-Eigenschaften verwenden. Das folgende Beispiel erzeugt ein einfaches Label mit Rahmen, Textund Hintergrundfarbe und definiertem inneren Abstand:
Abbildung 11.3: Ein ASP-Label
Serverseitige Ereignisse besitzt das Label keine (obwohl Sie ja in DHTML clientseitig Ereignisse wie onClick, onMouseOver und onMouseOut auswerten können). Eigenschaft
Beschreibung
Text
speichert den dargestellten Text.
Tabelle 11.10: Die wichtigste Eigenschaft der Label-Klasse (ohne die von WebControl geerbten)
701
Steuerelemente
11.2.6
Die TextBox
Die TextBox wird verwendet, um einfache Textfelder, Passwortfelder und mehrzeilige Textfelder zu erzeugen. Dieses Verhalten der TextBox steuern Sie über die Eigenschaft TextMode. Möglich sind hier die Werte SingleLine, MultiLine und Password. SingleLine ist die Voreinstellung. Das folgende Beispiel erzeugt eine einzeilige, eine mehrzeilige und eine Passwort-Textbox:
Abbildung 11.4: Die drei verschiedenen Textboxen
Die wichtigste Eigenschaft ist wie beim Label die Eigenschaft Text, die den dargestellten bzw. eingegebenen Text speichert. Über MaxLength bestimmen Sie, wie viele Zeichen der Anwender maximal eingeben darf. Die Eigenschaft Wrap bestimmt bei mehrzeiligen Textboxen, ob der Text am rechten Rand automatisch umbricht. Ist Wrap = False, erzeugt die Textbox automatisch einen horizontalen Rollbalken, wenn der Text breiter ist als die sichtbare Fläche der Textbox. Das wichtigste Ereignis ist TextChanged, das aufgerufen wird, wenn der Text vom Anwender geändert wurde.
702
11.2 Die ASP-Steuerelemente
Steuerelemente
Eigenschaft
Beschreibung
AutoPostBack
bestimmt mit true, dass bei jeder Änderung des dargestellten Textes das Formular automatisch abgesendet wird und die Seite damit das TextChanged-Ereignis abarbeitet. Damit können Sie erreichen, dass das Steuerelement quasi sofort auf Änderungen reagiert und nicht erst, wenn der Anwender das Formular bestätigt. Beachten Sie, dass das dazu im Hintergrund verwendete OnChange-Ereignis in JavaScript erst dann aufgerufen wird, wenn der Anwender die Textbox verlässt.
Columns
In dieser Eigenschaft können Sie die Breite des Steuerelements in Zeichen bestimmen. Diese Eigenschaft funktioniert, anders als Width, auch im Netscape.
MaxLength
bestimmt die maximale Zahl an eingebbaren Zeichen.
ReadOnly
bestimmt, ob der Inhalt der Textbox nur gelesen oder auch beschrieben werden kann.
Rows
In dieser Eigenschaft können Sie die Höhe der Textbox in Zeilen bestimmen. Diese Eigenschaft funktioniert, anders als Height, auch im Netscape.
Text
speichert den dargestellten Text.
TextMode
bestimmt die Art der Textbox mit den Werten SingleLine, MultiLine und Password, die Teil der Aufzählung TextBoxMode sind.
Wrap
legt fest, ob der dargestellte Text automatisch am rechten Rand umbricht.
Ereignis
Beschreibung
TextChanged
wird aufgerufen, nachdem der Inhalt der Textbox vom Anwender oder vom Programm geändert wurde.
11.2.7
Der Button
Tabelle 11.11: Die wichtigsten Eigenschaften der TextBox-Klasse (ohne die von WebControl geerbten)
Tabelle 11.12: Das wichtigste Ereignis der TextBox-Klasse (ohne die von WebControl geerbten)
Der Button stellt einen einfachen (Submit)-Schalter dar. Die wichtigste Eigenschaft ist Text, die die Beschriftung des Buttons verwaltet. Der folgende Quelltext erzeugt einen einfachen Schalter:
Abbildung 11.5: Ein ASP-Button
Wenn Sie auf der Seite Validierungs-Steuerelemente (Seite 738) einsetzen, können Sie über die Eigenschaft CausesValidation für einzelne Schalter festlegen, ob eine Betätigung des Schalters eine Validierung auslöst. Das wichtigste Ereignis ist Click. Dieses Ereignis wird aufgerufen, nachdem der Anwender den Schalter betätigt hat. Der Name dieses Ereignisses ist vielleicht etwas irreführend: Click wird auch aufgerufen, wenn der Anwender den Schalter über die Tastatur betätigt. An Stelle von Click können Sie auch das Ereignis Command auswerten, das in den Eigenschaften CommandName und CommandArgument des Ereignisargument-Objekts die Werte der gleichnamigen Eigenschaften des Schalters übergibt. So können Sie eine Ereignisbehandlungsmethode problemlos für mehrere Schalter gleichzeitig einsetzen: void HandleCommand(object Sender, CommandEventArgs e) { if (e.CommandName == "Login") { if (e.CommandArgument == "OK") // Eingaben überprüfen und einloggen // Im Beispiel nicht implementiert ; else if (e.CommandArgument == "Register") Response.Redirect("Register.aspx"); } else if (e.CommandName == "Home") Response.Redirect("Home.htm"); }
704
11.2 Die ASP-Steuerelemente
Steuerelemente
...
Das Command-Ereignis wird hauptsächlich bei datengebundenen Steuerelementen eingesetzt, wenn in den einzelnen Datenzeilen ein oder mehrere Schalter zur Bearbeitung der Daten angelegt werden. Im nächsten Kapitel erfahren Sie mehr darüber. Tabelle 11.13 und Tabelle 11.14 beschreiben die wichtigsten Eigenschaften und Ereignisse des Button-Steuerelements. Eigenschaft
Beschreibung
CausesValidation
legt fest, ob eine Betätigung des Schalters eine Validierung auslöst. Für Schalter, die zum Abbrechen einer Aktion gedacht sind, setzen Sie diese Eigenschaft normalerweise auf false, damit ein Abbruch keine Validierung der Eingaben bewirkt.
Tabelle 11.13: Die wichtigsten Eigenschaften der Button-Klasse (ohne die von WebControl geerbten)
705
Steuerelemente
Tabelle 11.13: Die wichtigsten Eigenschaften der Button-Klasse (ohne die von WebControl geerbten) (Forts.)
Tabelle 11.14: Die wichtigsten Ereignisse der Button-Klasse (ohne die von WebControl geerbten)
Eigenschaft
Beschreibung
CommandArgument
In dieser Eigenschaft können Sie in Zusammenhang mit CommandName die Bedeutung des Schalters definieren, wenn Sie eine einzige Ereignisbehandlungsmethode für das Command-Ereignis mehrerer Schalter verwenden. Diese Technik wird bei verschiedenen datengebundenen Listensteuerelementen eingesetzt (siehe Kapitel 13).
CommandName
spezifiziert im Zusammenhang mit CommandArgument die Bedeutung des Schalters.
Text
speichert den dargestellten Text.
Ereignis
Beschreibung
Click
wird aufgerufen, nachdem der Anwender den Schalter betätigt hat.
Command
wird aufgerufen, nachdem der Anwender den Schalter betätigt hat. Im Gegensatz zum Click-Ereignis wird im zweiten Argument ein CommandEventArgs-Objekt übergeben, aus dessen Eigenschaften CommandName und CommandArgument Sie die Werte der korrespondierenden Eigenschaften des aufrufenden Schalters auslesen können.
11.2.8
Der LinkButton
Der LinkButton repräsentiert einen Hyperlink, der allerdings nicht als normaler Hyperlink verwendet wird, sondern als eine Art Submit-Button. Deshalb fehlen diesem Steuerelement auch Eigenschaften für die Ziel-URL und den Namen des von ASP.NET zur Darstellung dieses Steuerelements erzeugten Ankers. Das folgende Beispiel erzeugt einen LinkButton:
Abbildung 11.6: Ein LinkButton
Die wichtigste Eigenschaft ist Text, die den als Verweis dargestellten Text speichert.
706
11.2 Die ASP-Steuerelemente
Steuerelemente
Eigenschaft
Beschreibung
CausesValidation
legt wie beim Button fest, ob eine Betätigung des Schalters eine Validierung auslöst.
CommandArgument
In dieser Eigenschaft können Sie wie beim Button-Steuerelement in Zusammenhang mit CommandName die Bedeutung des LinkButton für das Command-Ereignis definieren.
CommandName
spezifiziert im Zusammenhang mit CommandArgument die Bedeutung des LinkButton.
Text
speichert den dargestellten Text.
Ereignis
Beschreibung
Click
wird aufgerufen, nachdem der Anwender den Schalter betätigt hat.
Command
wird aufgerufen, nachdem der Anwender den Schalter betätigt hat. Im Gegensatz zum Click-Ereignis wird im zweiten Argument ein CommandEventArgs-Objekt übergeben, aus dessen Eigenschaften CommandName und CommandArgument Sie die Werte der korrespondierenden Eigenschaften des aufrufenden LinkButton auslesen können.
Tabelle 11.15: Die wichtigsten Eigenschaften der LinkButtonKlasse (ohne die von WebControl geerbten)
Tabelle 11.16: Die wichtigsten Ereignisse der LinkButtonKlasse (ohne die von WebControl geerbten)
Die Bestätigung des Formulars beim Klicken realisiert ASP.NET über eine automatisch erzeugte und mit dem onClick-Ereignis verbundene JavaScript-Funktion. Aus dem folgenden Quellcode: LinkButton void HandleClick(object sender, EventArgs e) { this.info.Text = "LinkButton wurde betätigt"; }
707
Steuerelemente
resultiert dieser HTML-Code: LinkButton OK
Das wichtigste Ereignis dieses Steuerelements ist dann auch das ClickEreignis. 11.2.9
Der ImageButton
Ein ImageButton ist ein Schalter, der ein Bild darstellt und der wie ein normaler Schalter verwendet werden kann. In der Eigenschaft ImageUrl geben Sie die URL des Bildes an. ImageAlign bestimmt dessen Ausrichtung im Steuerelement. Über die Eigenschaft AlternateText bestimmen Sie einen alternativen Text, der von Browsern angezeigt wird, die keine Bilder anzeigen können:
Abbildung 11.7: Ein ImageButton
CausesValidation besitzt dieselbe Bedeutung wie beim Button. Wenn der Anwender den Link betätigt, wird das Click- und das Command-
Ereignis ausgelöst.
709
Steuerelemente
Tabelle 11.17: Die wichtigsten Eigenschaften der ImageButtonKlasse (ohne die von WebControl geerbten)
Tabelle 11.18: Die wichtigsten Ereignisse der ImageButtonKlasse (ohne die von WebControl geerbten)
Eigenschaft
Beschreibung
AlternateText
bestimmt den Alternativtext für Browser, die keine Bilder darstellen können.
CausesValidation
legt wie beim Button fest, ob eine Betätigung des Schalters eine Validierung auslöst.
CommandArgument
In dieser Eigenschaft können Sie wie beim Button-Steuerelement in Zusammenhang mit CommandName die Bedeutung des LinkButton für das Command-Ereignis definieren.
CommandName
spezifiziert im Zusammenhang mit CommandArgument die Bedeutung des ImageButton.
ImageAlign
definiert die Ausrichtung des Bildes mit Werten der Aufzählung ImageAlign: NotSet, Left, Right, Baseline, Top, Middle, Bottom, AbsBottom, AbsMiddle oder TextTop.
ImageUrl
spezifiziert die URL der anzuzeigenden Bilddatei.
Ereignis
Beschreibung
Click
wird aufgerufen, nachdem der Anwender den Schalter betätigt hat.
Command
wird aufgerufen, nachdem der Anwender den Schalter betätigt hat. Im zweiten Argument können Sie wie beim Button die Werte der Eigenschaften CommandName und CommandArgument auswerten.
11.2.10 Das HyperLink-Steuerelement Das HyperLink-Steuerelement ist ein einfacher Verweis (Hyperlink). Die wichtigsten Eigenschaften sind Text (die Beschriftung) und NavigateUrl (die URL des Ziels). Außer den von WebControl geerbten Basisereignissen besitzt dieses Steuerelement keine eigenen. Da die Eigenschaft Name fehlt, ist die Erzeugung eines Ankers im Dokument, der durch andere Hyperlinks angesprungen werden kann, nicht direkt mit diesem Steuerelement möglich.
710
11.2 Die ASP-Steuerelemente
Steuerelemente
Verwenden Sie zu diesem Zweck einen selbst erzeugten, normalen HTML-Anker:
Eigenschaft
Beschreibung
Text
speichert den dargestellten Text, wenn kein Bild angezeigt wird.
ImageUrl
speichert die URL eines anzuzeigenden Bildes, wenn kein Text angezeigt wird.
Tabelle 11.19: Die wichtigsten Eigenschaften der HyperLinkKlasse (ohne die von WebControl geerbten)
11.2.11 CheckBox und RadioButton Das CheckBox- und das RadioButton-Steuerelement sind sich sehr ähnlich. Wie Sie ja bereits wissen, kann der Anwender innerhalb einer Gruppe von CheckBox-Elementen auch mehrere auswählen, in einer RadioButton-Gruppe allerdings immer nur einen. Die wichtigsten Eigenschaften beider Steuerelemente sind Text (die Beschriftung) und Checked (eingeschaltet). RadioButtons können Sie über die Eigenschaft GroupName gruppieren: Ihre Lieblingsmusik:
Abbildung 11.8: Eine RadioButton-Gruppe
711
Steuerelemente
Checkboxen werden nicht gruppiert: Ihre Hobbies:
Abbildung 11.9: Checkboxen in ASP.NET
Bei der Auswertung fragen Sie die Eigenschaft Checked ab: // Auswerten der RadioButtons if (music1.Checked) // Trance else if(music2.Checked) // R & B else if(music3.Checked) // Drum & Bass else if(music4.Checked) // Alternative // Auswerten der Checkboxen if (hobby1.Checked) // Inlinern if (hobby2.Checked)
712
11.2 Die ASP-Steuerelemente
Steuerelemente
// Carven if (hobby3.Checked) // Snowboarden if (hobby4.Checked) // Joggen
Beachten Sie, dass Sie Gruppen von RadioButtons oder CheckBoxen viel einfacher über das RadioButtonList- oder das CheckBoxList-Steuerelement erzeugen und auswerten können (ab Seite 731). Eigenschaft
Beschreibung
AutoPostBack
bestimmt mit true, dass bei jeder Änderung des Zustandes der Checkbox bzw. des RadioButtons das Formular automatisch abgesendet wird und die Seite damit das CheckedChanged-Ereignis abarbeitet. Damit können Sie erreichen, dass das Steuerelement quasi sofort auf Änderungen reagiert und nicht erst, wenn der Anwender das Formular bestätigt.
Checked
gibt an, ob die CheckBox bzw. der RadioButton eingeschaltet ist.
GroupName
Diese nur beim RadioButton vorhandene Eigenschaft gibt an, zu welcher Gruppe der OptionButton gehört.
Text
speichert den dargestellten Text.
TextAlign
spezifiziert die Ausrichtung der Beschriftung mit Werten der Aufzählung TextAlign. Sie können Left oder Right angeben.
Ereignis
Beschreibung
CheckedChanged
wird aufgerufen, wenn der Anwender den Zustand der Checkbox bzw. des RadioButtons geändert hat.
Tabelle 11.20: Die wichtigsten Eigenschaften der CheckBox- und der RadioButtonKlasse (ohne die von WebControl geerbten)
Tabelle 11.21: Das wichtigste Ereignis der CheckBox- und der RadioButtonKlasse (ohne die von WebControl geerbten)
713
Steuerelemente
11.2.12 Das Image-Steuerelement Das Image-Steuerelement zeigt einfach ein Bild im HTML-Dokument an (in einem img-Tag). Das Steuerelement ist hilfreich, wenn Sie im Programm Eigenschaften des Bildes verändern, beispielsweise dynamisch eine andere URL angeben wollen. Die wichtigsten Eigenschaften sind ImageUrl (URL der Bilddatei) und ImageAlign (Ausrichtung des Bildes). Wenn Sie die Höhe und die Breite des Bildes nicht angeben, wird dieses in der Originalgröße ausgegeben. Wenn Sie die Breite und/oder Höhe angeben, wird das Bild u.U. automatisch skaliert.
Serverseitige Ereignisse besitzt dieses Steuerelement nicht. Tabelle 11.22: Die wichtigsten Eigenschaften der Image-Klasse (ohne die von WebControl geerbten)
Eigenschaft
Beschreibung
AlternateText
bestimmt den Alternativtext für Browser, die keine Bilder darstellen können.
Font
spezifiziert die Schriftart für den Alternativtext.
ImageAlign
definiert die Ausrichtung des Bildes mit Werten der Aufzählung ImageAlign: NotSet, Left, Right, Baseline, Top, Middle, Bottom, AbsBottom, AbsMiddle oder TextTop.
ImageUrl
gibt die URL der anzuzeigenden Bilddatei an.
11.2.13 Panel Das Panel-Steuerelement ist ein div-Element, für das Sie die Größe, den Stil und ein Hintergrund-Bild definieren können. Da Sie (natürlich) neben Text auch andere Steuerelemente auf (bzw. in) einem Panel platzieren können, können Sie das Panel gut als Layout-Element einer Seite einsetzen. Das folgende Beispiel verwendet ein Panel, um zwei Eingabefelder hervorzuheben:
714
11.2 Die ASP-Steuerelemente
Steuerelemente
Abbildung 11.10: Ein Panel mit eingeschlossenen Steuerelementen
Das Panel-Steuerelement eignet sich auch dazu, eine Gruppe von Steuerelementen oder Text dynamisch im Programm sichtbar oder unsichtbar zu schalten. Wenn Sie die Visible-Eigenschaft eines Panels auf false setzen, sind auch alle enthaltenen Steuerelemente unsichtbar. In einer ASP.NETSeite, deren Steuerelemente nicht absolut oder relativ positioniert sind, benötigt ein unsichtbares Panel keinen Platz auf der Seite, sodass kein Leerraum entsteht, wenn das Panel nicht sichtbar ist. Neben den von WebControl geerbten Eigenschaften (die ja besonders bei einem Panel für die Definition des Aussehens wichtig sind) besitzt das Panel die wichtigen Eigenschaften HorizontalAlign (horizontale Ausrichtung des Inhalts) und Wrap (automatischer Umbruch des Inhalts am rechten Rand). Serverseitige Ereignisse besitzt dieses Steuerelement nicht. 715
Steuerelemente
Tabelle 11.23: Die wichtigsten Eigenschaften der Panel-Klasse (ohne die von WebControl geerbten)
Eigenschaft
Beschreibung
BackImageUrl
gibt die URL einer als Hintergrund zu verwendenden Bilddatei an.
HorizontalAlign
gibt die horizontale Ausrichtung des Inhalts mit den Werten der Aufzählung HorizontalAlign an. Sie können die folgenden Werte verwenden: Center, Justify (Blocksatz), Left, NotSet (nicht gesetzt) und Right. Die Voreinstellung ist NotSet.
Wrap
bestimmt mit true, dass der Inhalt automatisch am rechten Rand umbrochen wird. Die Voreinstellung ist true.
11.2.14 Das Calendar-Steuerelement Das Calendar-Steuerelement ist ein mächtiges Steuerelement zur Darstellung eines Kalenders. Abbildung 11.11: Das CalendarSteuerelement in einer formatierten Variante
Über verschiedene Eigenschaften können Sie die optische Gestaltung des Kalenders und die Steuerungsmöglichkeiten beeinflussen. Das Steuerelement reagiert automatisch auf eine Auswahl des Benutzers dadurch, dass zum einen die Ansicht des Kalenders auf die Auswahl umgestellt und zum anderen das Ereignis SelectionChanged aufgerufen wird. In diesem Ereignis können Sie auf den Wechsel des ausgewählten Datums reagieren. Das Calendar-Steuerelement besitzt eine sehr große Anzahl Eigenschaften. Viele davon betreffen das Aussehen. So finden Sie in der (Objekt-)Eigenschaft DayStyle z.B. Eigenschaften zur Einstellung des Stils des Datumsbereichs des Kalenders.
716
11.2 Die ASP-Steuerelemente
Steuerelemente
Die wichtigste Eigenschaft ist SelectedDate. Diese Eigenschaft gibt das ausgewählte Datum zurück. Das wichtigste Ereignis ist SelectionChanged, das aufgerufen wird, wenn der Anwender die Auswahl (also das Datum) ändert. Das folgende Beispiel reagiert auf eine Änderung des Datums mit dem Schreiben des Datums in eine Textbox (inputDate): private void HandleSelectionChanged(object sender, EventArgs e) { this.inputDate.Text = this.calendar.SelectedDate.ToShortDateString(); }
Wenn Sie dem Anwender ermöglichen wollen, auch eine volle Woche oder den vollen Monat auszuwählen, oder die Auswahl verbieten wollen, setzen Sie die Eigenschaft SelectionMode auf einen Wert der Aufzählung CalendarSelectionMode. Mit der Einstellung DayWeek kann der Anwender ein einzelnes Tag oder eine volle Woche auswählen, DayWeekMonth ermöglicht zusätzlich die Auswahl eines gesamten Monats, None führt dazu, dass keine Auswahl möglich ist. In den Modi DayWeek und DayWeekMonth blendet das Steuerelement an der linken Seite eine Spalte ein, über die die Auswahl der Woche bzw. des Monats möglich ist. Etwas einschränkend ist, dass nur einzelne Tage oder die gesamte Woche bzw. der gesamte Monat ausgewählt werden können. Die Auswahl mehrerer Tage, die nicht eine Woche oder einen Monat ausmachen, ist – wahrscheinlich aufgrund der HTML-Einschränkungen – nicht möglich.
717
Steuerelemente
Abbildung 11.12: Ein Kalender mit Wochen-Auswahlmöglichkeit
Bei der Auswertung mehrerer Daten gehen Sie die Auflistung SelectedDates durch. Das folgende Beispiel schreibt die ermittelten Daten in eine Textbox (inputDates): private void HandleSelectionChanged(object sender, EventArgs e) { this.inputDates.Text = ""; for (int i=0; i
Abbildung 11.13: Ein Kalender mit Bildern als Verweis
Die Formatierung können Sie sogar noch weiter verfeinern: Im Ereignis DayRender können Sie die Darstellung jeder einzelnen Zelle für den Tagesbereich beeinflussen und beispielsweise für die einzelnen Tage auch Bilder anzeigen. Das soll aber nur ein Tipp sein ... Bevor ich mich in weiteren Beschreibungen dieses Steuerelements verliere, finden Sie in Tabelle 11.25 die wichtigsten Eigenschaften und in Tabelle 11.26 die wichtigsten Ereignisse. Den Rest finden Sie sicherlich selbst heraus.
720
11.2 Die ASP-Steuerelemente
Steuerelemente
Eigenschaft
Beschreibung
CellPadding
definiert den Abstand zwischen dem Inhalt der einzelnen Zellen und deren Rahmen in Pixel. Die Voreinstellung ist 2.
CellSpacing
definiert den Abstand zwischen den einzelnen Zellen in Pixeln. Die Voreinstellung ist 0.
DayNameFormat
gibt an, wie die Tagesnamen ausgegeben werden. Der Typ dieser Eigenschaft ist DayNameFormat, das ist eine Aufzählung mit den Werten FirstLetter (nur der erste Buchstabe), FirstTwoLetters (die ersten zwei Buchstaben), Full (der volle Name) und Short (der abgekürzte Name).
FirstDayOfWeek
gibt an, welcher der erste Tag der Woche ist. In den USA beginnt die Woche beispielsweise mit dem Sonntag. Diese Eigenschaft besitzt den Typ FirstDayOfWeek, eine Aufzählung mit den Werten Default (der in den Systemeinstellungen eingestellte Tag), Monday, Tuesday, Wednesday, Thursday, Friday, Saturday und Sunday.
NextMonthText
In dieser Eigenschaft geben Sie den HTML-Inhalt des Verweises an, über den der Anwender den nächsten Monat anzeigen lassen kann. Die Voreinstellung ist »>«, was zu einem unschönen einfachen > führt. Sie können ohne Probleme auch ein img-Tag einsetzen, um ein Bild für den Verweis zu verwenden.
NextPrevFormat
Über diese Eigenschaft können Sie einstellen, ob das Steuerelement den in NextMonthText und PrevMonthText angegebenen HTML-Text als Verweis zur Auswahl des vorherigen und nächsten Monats anzeigt (CustomText) oder den vollen (FullMonth) oder abgekürzten (ShortMonth) Monatsnamen. Diese Werte sind in der Aufzählung NextPrevFormat gespeichert.
PrevMonthText
spezifiziert ähnlich NextMonthText den HTML-Inhalt des Verweises, über den der Anwender den vorherigen Monat anzeigen kann. Auch in dieser Eigenschaft können Sie zur optischen Verbesserung ein img-Tag ablegen. Die Voreinstellung ist »<«.
SelectedDate
Diese Eigenschaft speichert das ausgewählte Datum als DateTime-Wert (allerdings ohne Zeitanteil), wenn Sie über die Eigenschaft SelectionMode nur die Auswahl eines Datums zulassen.
Tabelle 11.25: Die wichtigsten Eigenschaften der Calendar-Klasse (ohne die Eigenschaften zur Formatierung der Bereiche und ohne die von WebControl geerbten)
721
Steuerelemente
Tabelle 11.25: Die wichtigsten Eigenschaften der Calendar-Klasse (ohne die Eigenschaften zur Formatierung der Bereiche und ohne die von WebControl geerbten) (Forts.)
722
Eigenschaft
Beschreibung
SelectedDates
Wenn Sie die SelectionMode-Eigenschaft auf DayWeek oder DayWeekMonth setzen, kann der Anwender mehrere Daten auswählen. Diese erreichen Sie über die Auflistung SelectedDates, die einzelne DateTime-Werte referenziert.
SelectionMode
spezifiziert die Auswahlmöglichkeiten mit den Werten der Aufzählung CalendarSelectionMode: Day: Der Anwender kann einzelne Tage auswählen. DayWeek: Der Anwender kann einen einzelnen Tag oder eine volle Woche auswählen. DayWeekMonth: Der Anwender kann einen einzelnen Tag, eine volle Woche oder den gesamten Monat auswählen. None: Der Anwender kann keine Auswahl treffen.
SelectMonthText
In dieser Eigenschaft können Sie den HTML-Text angeben, der für den Verweis zur Auswahl eines vollen Monats verwendet wird, wenn die Eigenschaft SelectionMode auf DayWeekMonth gesetzt ist. Zur optischen Verbesserung können Sie auch hier ein img-Tag einsetzen. Die Voreinstellung ist »>>«.
SelectWeekText
spezifiziert ähnlich SelectMonthText den HTML-Text für den Verweise zur Auswahl einer gesamten Woche. Die Voreinstellung ist »<<«.
ShowDayHeader
Diese boolesche Eigenschaft bestimmt, ob die Wochentag-Überschriftszeile angezeigt wird. Die Voreinstellung ist true.
ShowGridLines
Diese boolesche Eigenschaft bestimmt, ob das Steuerelemente Gitternetzlinien anzeigt. Die Voreinstellung ist false.
ShowNextPrevMonth
bestimmt, ob die Verweise zur Auswahl des vorherigen und des nächsten Monats angezeigt werden. Die Voreinstellung ist true.
ShowTitle
bestimmt, ob die Titelzeile angezeigt wird. Die Voreinstellung ist true.
TitleFormat
Diese Eigenschaft bestimmt, ob die Titelzeile nur den Monat anzeigt (Month) oder den Monat und das Jahr (MonthYear). Der Typ dieser Eigenschaft ist die Aufzählung TitleFormat. Die Voreinstellung ist MonthYear.
11.2 Die ASP-Steuerelemente
Steuerelemente
Eigenschaft
Beschreibung
TodaysDate
Über diese Eigenschaft vom Typ DateTime können Sie das Datum definieren, das als das heutige Datum verwendet wird. Wenn Sie keine Änderungen vornehmen, verwendet das Steuerelement das aktuelle Systemdatum.
VisibleDate
spezifiziert das Datum, dessen Monat im Steuerelement sichtbar ist. Wenn Sie nichts einstellen, verwendet diese Eigenschaft das Datum, das über TodaysDate definiert ist.
Ereignis
Beschreibung
DayRender
wird aufgerufen, wenn die Zelle eines einzelnen Tages erzeugt wird. Über die Eigenschaften Cell und Day des übergebenen Ereignisargument-Objekts (vom Typ DayRenderEventArgs) erhalten Sie Zugriff auf die Tabellenzelle (vom Typ TableCell) und den Kalendertag (vom Typ CalendarDay). Über dieses Ereignis können Sie den Inhalt und die Formatierung der einzelnen Zellen selbst bestimmen. Das kann schon dann interessant sein, wenn Sie bestimmte Daten optisch hervorheben wollen.
SelectionChanged
wird aufgerufen, wenn die Auswahl des Datums bzw. des Datumsbereichs geändert wird.
VisibleMonthChanged
wird aufgerufen, wenn der angezeigte Monat wechselt. Über die Eigenschaften NewDate und PreviousDate des übergebenen Ereignisargument-Objekts (vom Typ MonthChangedEventArgs) erhalten Sie Informationen zum vorherigen und nächsten Datum.
Tabelle 11.25: Die wichtigsten Eigenschaften der Calendar-Klasse (ohne die Eigenschaften zur Formatierung der Bereiche und ohne die von WebControl geerbten) (Forts.) Tabelle 11.26: Die wichtigsten Ereignisse der Calendar-Klasse (ohne die von WebControl geerbten)
11.2.15 Das AdRotator-Steuerelement Das AdRotator-Steuerelement ist dazu gedacht, bei jeder Anzeige einer Seite ein wechselndes Werbebild anzuzeigen, das gleichzeitig ein Verweis auf eine andere Webseite ist (wie Sie das bereits aus dem Internet kennen). Der Name dieses Steuerelements ist übrigens aus dem englischen »advertising« abgeleitet, was übersetzt »werben« heißt. Über eine XML-Dateien steuern Sie, welche Bilder in welcher Relation dargestellt werden. Das Steuerelement wählt die Bilder mehr oder weniger zufällig aus, hält sich dabei aber an die Regeln, die Sie in der XMLDatei definieren. Das folgende Listing zeigt eine typische Steuerungsdatei: 723
Steuerelemente
videos.gif http://www.galaxyvideo.de Galaxy Video 50 Topic1 training.gif http://www.galaxysports.de Galaxy Sports 50 Topic2
Für jedes anzuzeigende Bild legen Sie ein Ad-Element an. Das ChildElement ImageUrl verwaltet die URL des anzuzeigenden Bildes, das Element NavigateUrl die URL des erzeugten Verweises. Im Element AlternateText geben Sie einen Alternativtext für Browser an, die keine Bilder anzeigen können. Das Element Impression steuert, wie häufig das Bild in Relation zu anderen Bildern angezeigt wird. Sind die Impression-Werte aller Ad-Elemente gleich groß, werden die entsprechenden Bilder gleich häufig angezeigt. Im Element Keyword geben Sie eine Kategorie an. Über die Eigenschaft KeywordFilter des Steuerelements können Sie später eine dieser Kategorien angeben, um nur die Werbungen einer bestimmten Kategorie anzuzeigen. Im Steuerelement geben Sie diese XML-Dateien in der Eigenschaft AdvertisementFile an. In der Eigenschaft Target definieren Sie, wo die Ziel-URL geöffnet wird (z.B. »_blank« für ein neues Fenster, »_self« für das aktuelle Fenster):
724
11.2 Die ASP-Steuerelemente
Steuerelemente
Beachten Sie, dass das jeweils andere Bild immer nur angezeigt wird, nachdem die Seite komplett neu angefordert wurde. Eigenschaft
Beschreibung
AdvertisementFile
gibt die URL der XML-Datei an, die die Steuerungsdaten beinhaltet.
KeywordFilter
In dieser Eigenschaft können Sie die anzuzeigenden Werbebilder filtern, indem Sie eines der im Keyword-Attribut des Ad-Elements in der XML-Datei spezifizierten Schlüsselwörter angeben.
Target
spezifiziert das Ziel des Verweises mit den für das HTMLTarget-Attribut üblichen Werten (Framename, _self , _blank etc.).
Ereignis
Beschreibung
AdCreated
wird aufgerufen, wenn das Steuerelement beim Rendern der Seite ein Bild zur Anzeige vorbereitet. In diesem Ereignis können Sie die Eigenschaften des Werbeobjekts beeinflussen, was aber wahrscheinlich eher selten notwendig sein sollte (dafür können Sie ja die XML-Datei verwenden).
Tabelle 11.27: Die wichtigsten Eigenschaften der AdRotatorKlasse (ohne die von WebControl geerbten)
Tabelle 11.28: Das wichtigste Ereignis der AdRotatorKlasse (ohne die von WebControl geerbten)
11.2.16 Das Table-Steuerelement Das Table-Steuerelement erzeugt eine HTML-Tabelle. Die Eigenschaften des Table-Steuerelements entsprechen meist den Attributen des HTML-table-Tag. Die Eigenschaft GridLines steuert, wo die Tabelle Gitternetzlinien ausgibt (Horizontal, Vertical, Both). Im Table-Element werden für jede darzustellende Zeile TableRow-Elemente angelegt, in denen für jede Spalte TableCell-Elemente angelegt werden. Eine einfache Tabelle mit zwei Zeilen und zwei Spalten ohne Formatierung sieht beispielsweise so aus:
725
Steuerelemente
Vorname Nachname Fred-Bogus Trumper
Abbildung 11.14: Eine einfache Tabelle
Über verschiedene Eigenschaften der Tabelle, der Zeilen und der Spalten können Sie die Tabelle formatieren. Tabelle 11.29 listet zunächst die wichtigsten Eigenschaften des Table-Steuerelements auf. Serverseitige Ereignisse besitzt dieses Steuerelement mit Ausnahme der von WebControl geerbten nicht. Tabelle 11.29: Die wichtigsten Eigenschaften der Table-Klasse (ohne die von WebControl geerbten)
726
Eigenschaft
Beschreibung
BackImageUrl
gibt die URL einer als Hintergrund zu verwendenden Bilddatei an.
CellPadding
definiert den Abstand zwischen dem Inhalt der einzelnen Zellen und deren Rahmen in Pixel. Die Voreinstellung ist -1 (nicht gesetzt).
CellSpacing
definiert den Abstand zwischen den einzelnen Zellen in Pixeln. Die Voreinstellung ist -1 (nicht gesetzt).
GridLines
definiert mit den Werten der GridLines-Aufzählung, ob die Tabelle Gitternetzlinien anzeigt. Sie können die folgenden Werte einstellen: None, Horizontal, Vertical und Both.
11.2 Die ASP-Steuerelemente
Steuerelemente
Eigenschaft
Beschreibung
HorizontalAlign
gibt die horizontale Ausrichtung des Inhalts mit den Werten der Aufzählung HorizontalAlign an. Sie können die folgenden Werte verwenden: Center, Justify (Blocksatz), Left, NotSet (nicht gesetzt) und Right. Die Voreinstellung ist NotSet.
Rows
Rows ist eine Auflistung der Zeilen der Tabelle in Form von TableRow-Objekten. Über diese Auflistung können Sie die
Tabelle 11.29: Die wichtigsten Eigenschaften der Table-Klasse (ohne die von WebControl geerbten) (Forts.)
Zeilen im Programm auslesen, manipulieren und neue Zeilen erzeugen.
Eine Tabellenzeile besitzt die in Tabelle 11.30 beschriebenen wichtigen Eigenschaften. Bis auf die von WebControl geerbten Ereignisse besitzt die TableRow-Klasse keine weiteren Ereignisse. Eigenschaft
Beschreibung
Cells
Cells ist eine Auflistung von TableCell-Objekten, die die
Spalten der Zeile repräsentieren. HorizontalAlign
gibt wie bei der Table-Klasse die horizontale Ausrichtung der Zellen mit den Werten der Aufzählung HorizontalAlign an.
VerticalAlign
gibt die vertikale Ausrichtung der Zellen in der Zeile an. Diese Eigenschaft besitzt den Typ VerticalAlign, das ist eine Aufzählung mit den Werten NotSet, Top, Middle und Bottom.
Tabelle 11.30: Die wichtigsten Eigenschaften der TableRow-Klasse (ohne die von WebControl geerbten)
Eine Tabellenzelle besitzt natürlich auch wieder eigene Eigenschaften (Tabelle 11.31). Eigene Ereignisse besitzt die TableCell-Klasse nicht. Das ist aber keine Einschränkung, weil Sie in einer Tabellenzelle alle anderen HTML-Elemente anlegen können (und deshalb auch solche, die Ereignisse auswerten können). Eigenschaft
Beschreibung
ColumnSpan
gibt an, über wie viele Spalten der Tabelle sich die Zelle erstreckt. Die Voreinstellung ist 0, was bedeutet, dass diese Eigenschaft nicht gesetzt ist.
HorizontalAlign
gibt wie bei der Table-Klasse die horizontale Ausrichtung der Zellen mit den Werten der Aufzählung HorizontalAlign an.
Tabelle 11.31: Die wichtigsten Eigenschaften der TableCell-Klasse (ohne die von WebControl geerbten)
727
Steuerelemente
Tabelle 11.31: Die wichtigsten Eigenschaften der TableCell-Klasse (ohne die von WebControl geerbten) (Forts.)
Eigenschaft
Beschreibung
RowSpan
gibt an, über wie viele Zeilen der Tabelle sich die Zelle erstreckt. Die Voreinstellung ist 0, was bedeutet, dass diese Eigenschaft nicht gesetzt ist.
Text
definiert den Inhalt der Zelle.
VerticalAlign
gibt wie bei der TableRow-Klasse die vertikale Ausrichtung der Zellen in der Zeile an.
Wrap
legt fest, ob der dargestellte Text automatisch am rechten Rand umbricht.
Im Prinzip ist eine ASP-Tabelle damit genauso »einfach« zu deklarieren wie eine HTML-Tabelle. Was mich ein wenig verwundert hat, ist, dass es scheinbar kein Äquivalent zum colgroup-Tag gibt, über den in HTML die Breiten der Spalten sehr einfach eingestellt werden können. In ASP.NET müssen Sie die Breiten wohl in jeder Zelle oder, was wahrscheinlich auch ausreicht, nur in den Zellen der Überschriftszeile separat angeben. Eine etwas schöner formatierte Tabelle sieht dann im Quelltext bereits etwas aufwändiger aus: Vorname Nachname Fred-Bogus Trumper
728
11.2 Die ASP-Steuerelemente
Steuerelemente
Abbildung 11.15: Eine formatierte Tabelle
Im Programm greifen Sie über die Rows-Eigenschaft auf die einzelnen Zeilen zu. Rows ist eine Auflistung von TableRow-Elementen, deren wichtigste Eigenschaft (neben den normalen Eigenschaften einer Tabellenzeile) Cells ist, eine Auflistung von TableCell-Objekten. Tabellen werden häufig im Quellcode dynamisch erzeugt. Deswegen zeige ich hier, wie das prinzipiell ausgeführt wird. Eigentlich ist das Ganze recht einfach, wenn Sie sich mit Auflistungen auskennen. Die Arbeit wird erleichtert, wenn Sie eine leere Basistabelle anlegen:
Im Programm fügen Sie dann einfach neue Zeilen und den Zeilen neue Zellen hinzu. Das folgende Beispiel erzeugt eine Tabelle für das kleine Einmaleins mit zehn Zeilen und zehn Spalten, wobei die Hintergrundfarbe jeder Zeile abwechselnd definiert wird: private void Page_Load(object sender, EventArgs e) { /* Tabelle dynamisch füllen */ TableRow row; TableCell cell; for (int rowIndex = 0; rowIndex < 10; rowIndex++) { // Neue Zeile erzeugen row = new TableRow(); // Farbe für jede Zeile wechselnd definieren if (rowIndex % 2 == 0)
729
Steuerelemente
row.BackColor = System.Drawing.Color.White; else row.BackColor = System.Drawing.Color.WhiteSmoke; for (int colIndex = 0; colIndex < 10; colIndex++) { // Neue Zelle erzeugen cell = new TableCell(); cell.Width = Unit.Pixel(20); // Das kleine 1*1 ablegen int result = (rowIndex + 1) * (colIndex + 1); cell.Text = result.ToString(); // Zelle der Zeile hinzufügen row.Cells.Add(cell); } // Zeile der Tabelle hinzufügen demoTable.Rows.Add(row); } }
Abbildung 11.16: Eine dynamisch erzeugte Tabelle
730
11.2 Die ASP-Steuerelemente
Steuerelemente
11.2.17 Listen-Steuerelemente ASP.NET definiert einige Steuerelemente, die eine Liste von Einträgen speichern. Dazu gehören das DropDownList- (einzeilige Liste), das ListBox- (mehrzeilige Liste), das CheckBoxList- (Liste von Checkboxen) und das RadioButtonList-Steuerelement (Liste von RadioButtons). Abbildung 11.17: Die ListenSteuerelemente
Die Klassen dieser Steuerelemente sind von der Klasse ListControl abgeleitet. Die ListControl-Basisklasse
Die ListControl-Basisklasse, die selbst von WebControl abgeleitet ist, definiert das Verhalten der Liste der Listen-Steuerelemente.
731
Steuerelemente
Tabelle 11.32: Die wichtigsten Eigenschaften der ListControlKlasse (ohne die für Datenbindung verwendeten)
Eigenschaft
Beschreibung
AutoPostBack
bestimmt, ob eine Änderung des aktuellen Eintrags durch den Benutzer zu einer Bestätigung des Formulars und damit zur Ausführung des SelectedIndexChanged-Ereignisses führt.
Items
Items ist eine Auflistung der gespeicherten Elemente und referenziert ListItem-Objekte. Ein ListItem-Objekt besitzt die
folgenden Eigenschaften: Attributes: referenziert eine Auflistung der HTML-Attribute des Elements, die nicht über Eigenschaften repräsentiert werden. In dieser Auflistung können Sie auch eigene Attribute speichern, was manchmal ein recht netter Programmiertrick ist. Selected: definiert, ob der Listeneintrag selektiert ist. Text: enthält den Text des Eintrags. Value: enthält den Wert des Eintrags (wie bei HTML: Text und Wert können separat angegeben werden).
Tabelle 11.33: Das wichtigste Ereignis der ListControl-Klasse (ohne die für Datenbindung verwendeten)
SelectedIndex
verwaltet bei Einfachauswahl-Listen den Index des selektierten Eintrags.
SelectedItem
gibt bei Einfachauswahl-Listen eine Referenz auf den selektierten Eintrag zurück.
Ereignis
Beschreibung
OnSelectedItemChanged
wird aufgerufen, wenn der Benutzer einen anderen Eintrag selektiert hat.
Deklaration
Die Deklaration der Listen-Steuerelemente ähnelt sich sehr. Innerhalb des Steuerelements-Tags werden einzelne ListItem-Elemente deklariert. Das folgende Beispiel erzeugt die Elemente, die in Abbildung 11.17 dargestellt sind: Ihr favorisiertes Auto (Listbox):
732
11.2 Die ASP-Steuerelemente
Steuerelemente
Ihr favorisierter Autor (DropDownList): Musik, die Sie mögen (CheckboxList): Club Trance Trip Hop Ihr Geschlecht (RadioButtonList): Weiblich Männlich Weiß nicht
733
Steuerelemente
Spezielle Eigenschaften und Ereignisse der Steuerelemente
Die ListBox-, CheckBoxList- und die RadioButtonList-Klasse besitzen neben den von ListControl geerbten Eigenschaften noch eigene, die in Tabelle 11.34 und Tabelle 11.35 beschrieben werden. Tabelle 11.34: Die wichtigsten Eigenschaften der Listbox-Klasse (ohne die von WebControl und ListControl geerbten) Tabelle 11.35: Die wichtigsten Eigenschaften der CheckBoxListund der RadioButtonListKlasse (ohne die von WebControl und ListControl geerbten)
Eigenschaft
Beschreibung
Rows
definiert die Anzahl der sichtbaren Zeilen. Die Voreinstellung ist 4.
SelectionMode
gibt mit einem Wert der Aufzählung ListSelectionMode an, ob die Liste nur eine einfache Auswahl erlaubt (Single, Voreinstellung) oder eine Mehrfachauswahl (Multiple).
Eigenschaft
Beschreibung
CellPadding
legt den Abstand des Inhalts eines Eintrags von seinem Rand fest. Der Wert -1 (Voreinstellung) bedeutet, dass diese Eigenschaft nicht gesetzt ist.
CellSpacing
legt den Abstand der einzelnen Einträge voneinander in Pixeln fest. Der Wert -1 (Voreinstellung) bedeutet, dass diese Eigenschaft nicht gesetzt ist.
RepeatColumns
In dieser Eigenschaft können Sie festlegen, dass die einzelnen Einträge in mehreren Spalten angelegt werden.
RepeatDirection
legt die Ausrichtung der Liste mit einem Wert der Aufzählung RepeatDirection fest: Vertical (Voreinstellung) oder Horizontal.
RepeatLayout
legt mit den Werten der Aufzählung RepeatLayout fest, ob die Elemente in einer Tabelle angelegt werden (Table) oder im normalen Fluss der Seite (Flow).
TextAlign
spezifiziert die Ausrichtung der Beschriftung mit Werten der Aufzählung TextAlign. Sie können Left oder Right angeben.
Dynamisches Hinzufügen von Elementen
Bei allen Listen-Steuerelementen können Sie die Liste im Programm über die Add-Methode der Items-Auflistung füllen. Diese Auflistung speichert Objekte der Klasse ListItem.
734
11.2 Die ASP-Steuerelemente
Steuerelemente
Wenn Sie die Liste im Quellcode füllen, können Sie wahlweise einzelne ListItem-Objekte erzeugen und der Add-Methode übergeben oder einfach nur Strings übergeben. Das folgende Beispiel füllt ein DropDownList-Steuerelement im Load-Ereignis der Seite: private void Page_Load(object sender, EventArgs e) { if (this.IsPostBack == false) { this.favoritePerson.Items.Add("Zaphod"); this.favoritePerson.Items.Add("Trillian"); this.favoritePerson.Items.Add("Ford"); this.favoritePerson.Items.Add("Arthur"); } }
Handelt es sich um ein CheckBoxList- oder RadioButtonList-Steuerelement, das Sie dynamisch im Programm füllen, müssen Sie ein wenig aufpassen, wenn die Elemente der Seite absolut positioniert werden. Diese Steuerelemente werden natürlich automatisch vergrößert, wenn sie gefüllt werden, und überlagern dann u.U. andere Elemente der Seite. Bei einem RadioButtonList-Steuerelement ist nach dem Füllen keiner der Schalter eingeschaltet. Für die Praxis ist dies meist nicht sinnvoll, da Sie normalerweise erwarten, dass eine Option gewählt wird. Schalten Sie also eine Option ein, indem Sie SelectedIndex auf deren Index setzen: // Voreinstellung des ersten Eintrags music.SelectedIndex = 0;
Löschen der Liste
Mit der Clear-Methode der Items-Ausflistung können Sie die Liste im Programm löschen: this.favoritePerson.Items.Clear();
735
Steuerelemente
Auswerten von Einfachauswahl-Listen
Das DropDownList-, das RadioButtonList- und das ListBox-Steuerelement mit SelectionMode = Single erlauben nur eine Einfachauswahl. Wenn Sie diese auswerten wollen, ermitteln Sie einfach den Index des selektierten Elements. Diesen Index können Sie dann verwenden, um den selektierten Eintrag auszulesen. Das folgende Beispiel schreibt das Ergebnis der Auswertung in eine Listbox (result): this.result.Items.Clear(); int i = this.favoritePerson.SelectedIndex; if (i > -1) this.result.Items.Add("Ihre Lieblingsperson ist " + "die Person Nummer " + i + ": " + this.favoritePerson.Items[i].Value); else this.result.Items.Add("Sie haben keine " + "Lieblingsperson");
Den selektierten Eintrag erhalten Sie aber auch über die Eigenschaft SelectedItem: result.Items.Add("Ihre Lieblingsperson ist " + "die Person Nummer " + i + ": " + this.favoritePerson.SelectedItem.Value)
Auswerten von Mehrfachauswahl-Listen
Zu den Mehrfachauswahl-Listen gehören das CheckBoxList- und das Listbox-Steuerelement mit SelectionMode = Multiple. Diese Listen müssen Sie etwas anders auswerten, weil ja mehrere Auswahlen möglich sind. Gehen Sie dazu die Liste sequenziell durch und überprüfen Sie über die Selected-Eigenschaft des ListItem-Elements, ob dieses selektiert ist: this.result.Items.Add("Sie mögen:"); for (i = 0; i < this.music.Items.Count; i++) {
736
11.2 Die ASP-Steuerelemente
Steuerelemente
if (this.music.Items[i].Selected) this.result.Items.Add( this.music.Items[i].Text); }
11.2.18 Spezielle Steuerelemente Literal und PlaceHolder
Das Literal-Steuerelement ist nichts weiter als ein serverseitiger Container für reinen HTML-Text. Die Text-Eigenschaft ist dann (neben EnableViewState) auch die einzige wichtige Eigenschaft dieses Steuerelements. Nutzen können Sie das Literal-Steuerelement, wenn Sie reinen Text oder selbst erzeugten HTML-Quelltext an einer definierten Stelle in der Seite ausgeben wollen. Naturgemäß können Sie dieses Steuerelement auch nicht positionieren. Das PlaceHolder-Steuerelement dient als Platzhalter. Wenn Sie Steuerelemente dynamisch erzeugen und keine absolute Positionierung (die ja die Stile Left und Top einsetzt) verwenden wollen, können Sie den Platzhalter auf der Seite platzieren und die Steuerelemente innerhalb des Platzhalters erzeugen. Xml
Das Xml-Steuerelement können Sie verwenden, um den Inhalt eines XML-Dokuments anzuzeigen oder um ein Dokument über ein XSLoder XSLT-Stylesheet auf dem Server zu transformieren1. Die wichtigsten Eigenschaften dieses Steuerelements, das übrigens nicht von WebControl, sondern direkt von der Control-Klasse abgeleitet ist, stellt Tabelle 11.36 dar.
1 Prinzipiell speichert ein XML-Dokument strukturierte Daten, ähnlich einer Datenbank. Diese Daten können über Stylesheets so formatiert werden, dass sie in einer lesbaren und optisch ansprechenden Form dargestellt werden.
737
Steuerelemente
Tabelle 11.36: Die wichtigsten Eigenschaften des Xml-Steuerelements
Eigenschaft
Beschreibung
Document
eine Referenz zu einem XmlDocument-Objekt, das das XMLDokument repräsentiert
DocumentContent
Diese Eigenschaft gibt den Inhalt des XML-Dokuments als String zurück.
DocumentSource
der physische oder virtuelle Pfad zum Dokument
Transform
eine Referenz zu einem XmlTransform-Objekt, das die Daten des XSL- bzw. XSLT-Stylesheets verwaltet, das für die Transformierung verwendet wird
TransformFromSource
eine Pfadangabe zu einem XSL- oder XSLT-Dokument, das zur Transformierung verwendet werden soll
11.2.19 Die Validierungs-Steuerelemente Die Validierungs-Steuerelemente sind in der Praxis enorm hilfreich. Die Überprüfung von Benutzereingaben ist eine sehr wichtige Aufgabe bei der Programmierung. Ohne die Validierungs-Steuerelemente bleiben Ihnen dazu nur zwei Möglichkeiten: Das serverseitige Überprüfen im ASP.NET-Programm und das clifentseitige Überprüfen mit JavaScript. Beides erfordert einen hohen Programmieraufwand. Die Validierungs-Steuerelemente nehmen Ihnen diese Arbeit ab. Das Prinzip dieser Steuerelemente ist einfach: Sie sehen aus wie einfache Label, werden auf einem Webformular an geeigneter Stelle platziert und mit einem Steuerelement verknüpft, das sie überwachen sollen. Die verschiedenen Validierungs-Steuerelemente überprüfen unterschiedliche Gegebenheiten:
RequiredFieldValidator : überprüft, ob im verknüpften Eingabefeld
eine Eingabe erfolgt ist.
CompareValidator : überprüft, ob zwei Steuerelemente denselben
Wert besitzen.
RangeValidator : überprüft, ob eine Eingabe in einem angegebenen
Wertebereich liegt.
RegularExpressionValidator : vergleicht die Eingabe mit dem Mus-
ter eines regulären Ausdrucks.
738
11.2 Die ASP-Steuerelemente
Steuerelemente
CustomValidator : ermöglicht den Einsatz einer JavaScript-Funktion
oder einer serverseitigen Methode zur Überprüfung von Eingaben. Bestätigt der Anwender das Formular, überprüfen die ValidierungsSteuerelemente ihre verbundenen Eingabeelemente auf die Einhaltung der Gültigkeitsregel. Wird diese Regel verletzt, brechen die Validierungs-Steuerelemente die Aktion ab und zeigen einen Fehlertext an. Das folgende Beispiel zeigt, wie einfach das Ganze ist. Ein RequiredFieldValidator-Steuerelement ist mit einer Textbox verknüpft und überprüft, ob dort Eingaben erfolgt sind: Name:
Wenn die Seite ausgeführt wird, ist das Valdierungs-Steuerelement zunächst unsichtbar (Abbildung 11.18). Abbildung 11.18: Ein Formular mit ValidierungsSteuerelement vor dem Absenden des Formulars
739
Steuerelemente
Verletzt der Anwender die Regeln und versucht, das Formular abzusenden, werden die betroffenen Validierungs-Steuerelemente sichtbar (Abbildung 11.19). Abbildung 11.19: Ein ValidierungsSteuerelement ist sichtbar geworden, weil der Anwender die Regeln verletzt hat.
Alternativ können Sie alle Fehlermeldungen gemeinsam in einem ValidationSummary-Steuerelement ausgeben lassen. Dazu ist zunächst nichts weiter notwendig, als dieses Steuerelement auf der aspx-Seite zu platzieren und die Ausgabe der Fehlermeldung für die einzelnen Validierungs-Steuerelemente zu unterdrücken: Name:
Ihr Alter:
740
11.2 Die ASP-Steuerelemente
Steuerelemente
Wie Sie dem Beispiel entnehmen können, können Sie auch mehrere Validierungs-Steuerelemente für ein Steuerelement anlegen. Das Alter wird z.B. auf Vorhandensein und auf einen Bereich überprüft. Die Anzeige der Fehlermeldungen der einzelnen Validierungs-Steuerelemente ist über DisplayMode="None" ausgeschaltet. Werden nun
741
Steuerelemente
Regeln verletzt, zeigt das ValidationSummary die einzelnen Fehlermeldungen an. Abbildung 11.20: Anzeige von Fehlermeldungen durch das ValidationSummarySteuerelement
Die Überprüfung der Regeln erfolgt für alle Validierungs-Steuerelemente mit Ausnahme des CustomValidator-Steuerelements im Browser, sofern Sie nicht die Eigenschaft EnableClientScript ausschalten und wenn der Browser in der Lage ist, JavaScript-Code auszuführen. ASP.NET erzeugt (für den Internet Explorer) ziemlich komplexen Code, den ich hier gar nicht darstellen will (erstaunlicherweise ist der Code für den Netscape 4.7 wesentlich einfacher). Schauen Sie sich einfach den Quelltext der erzeugten HTML-Seite an. Die Eigenschaft CausesValidation
Alle Steuerelemente, über die der Anwender ein Formular absenden kann, besitzen die Eigenschaft CausesValidation. Standardmäßig steht der Wert dieser Eigenschaft auf true. Wenn Sie den Wert auf False setzen, führt das dazu, dass bei der Betätigung dieses Steuerelements keine Validierung erfolgt. Dieses Verhalten ist sehr hilfreich, wenn Sie Steuerelemente auf der Seite platzieren, über die der Anwender die Aktion abbrechen kann. Bei einem Abbruch soll ja eigentlich keine Validierung erfolgen. Das folgende Code-Fragment stellt den unteren Teil des Beispiels dar, dieses Mal aber mit einem Abbrechen-Schalter:
742
11.2 Die ASP-Steuerelemente
Steuerelemente
Die Klasse BaseValidator
Alle Validierungs-Steuerelemente sind von der Basisklasse BaseValidator abgeleitet und erben deswegen die in Tabelle 11.37 zusammengefassten wichtigen Eigenschaften. BaseValidator selbst ist von der Klasse Label abgeleitet (Seite 701), besitzt also alle Elemente eines Label-Steuerelements und damit auch die von WebControl (Seite 699) geerbten. Eigenschaft
Bedeutung
ControlToValidate
In dieser Eigenschaft geben Sie die Id des Steuerelements an, das überprüft werden soll.
Display
legt das Verhalten der Fehlermeldung fest. Möglich sind die Werte None (keine Fehlermeldung), Static und Dynamic. Wenn Sie Display auf Dynamic setzen, wird das Element zunächst in der Breite 0 angelegt. Wird der Fehler ausgegeben, wird das Element vergrößert, was dazu führt, dass rechts davon angelegte Elemente verschoben werden, wenn Sie keine absolute Positionierung verwenden.
EnableClientScript
In dieser Eigenschaft können Sie festlegen, ob eine clientseitige Validierung erfolgen soll, falls dies möglich ist.
Enabled
legt fest, ob das Steuerelement aktiviert ist, also seiner Aufgabe nachgeht.
ErrorMessage
spezifiziert die Fehlermeldung.
IsValid
Über diese Eigenschaft können Sie herausfinden, ob das verbundene Steuerelement valide Daten enthält.
Tabelle 11.37: Die wichtigsten Eigenschaften der Klasse BaseValidator (ohne die von Label und WebControl geerbten)
Über die einzige wichtige Methode Validate können Sie eine Validierung im Programm initiieren. RequiredFieldValidator
Ein RequiredFieldValidator-Steuerelement überprüft, ob in dem verbundenen Steuerelement Eingaben erfolgt sind. Die wichtigste Eigen-
743
Steuerelemente
schaft, neben den von BaseValidatorgeerbten, ist InitialValue, die den Startwert des zu überprüfenden Steuerelements definiert. Tabelle 11.38: Die wichtigste Eigenschaft der Klasse RequiredFieldValidator (ohne die von BaseValidator geerbten)
Eigenschaft
Bedeutung
InitialValue
In dieser Eigenschaft können Sie einen String eingeben, der als Startwert im zu vergleichenden Steuerelement erscheint, wenn die aspx-Seite neu geladen wird.
CompareValidator
Ein CompareValidator-Steuerelement vergleicht den Wert zweier Steuerelemente. Solche Validierungen benötigen Sie z. B., wenn der Benutzer ein Passwort eingeben und noch einmal wiederholen soll. Neben den Eigenschaften der BaseValidator-Klasse ist die Eigenschaft ControlToCompare wichtig. Hier geben Sie an, mit welchem Steuerelement verglichen werden soll. Die Operator-Eigenschaft steuert die Art des Vergleichs. Tabelle 11.39: Die wichtigsten Eigenschaften der Klasse CompareValidator (ohne die von BaseValidator geerbten)
744
Eigenschaft
Bedeutung
ControlToCompare
verwaltet die Id des Steuerelements, mit dessen Inhalt verglichen werden soll.
Operator
In dieser Eigenschaft legen Sie die Vergleichsoperation mit den Werten der ValidationCompareOperatorAuflistung fest: Equal: Vergleich auf Gleichheit. Voreinstellung, NotEqual: Vergleich auf Ungleichheit, GreaterThan: Vergleich darauf, ob der Wert des zu überprüfenden Steuerelements größer ist, GreaterThanEqual: Vergleich darauf, ob der Wert des zu überprüfenden Steuerelements größer oder gleich ist, LessThan: Vergleich darauf, ob der Wert des zu überprüfenden Steuerelements kleiner ist, LessThanEqual: Vergleich darauf, ob der Wert des zu überprüfenden Steuerelements kleiner oder gleich ist, DataTypeCheck: Vergleich darauf, ob der Datentyp der Eingabe im zu überprüfenden Steuerelement mit dem Datentyp des Vergleichswerts identisch ist.
ValueToCompare
In dieser Eigenschaft können Sie einen Wert eintragen, mit dem verglichen werden soll.
11.2 Die ASP-Steuerelemente
Steuerelemente
RangeValidator
Ein RangeValidator-Steuerelement überprüft, ob eine Eingabe in einem angegebenen Wertebereich liegt. Den minimalen und maximalen Wert geben Sie in den Eigenschaften MinimumValue und MaximumValue an. In der Type-Eigenschaft legen Sie den Datentyp fest: String, Integer, Double, Date oder Currency. Eigenschaft
Bedeutung
MaximumValue
legt den maximalen Wert des gültigen Bereichs fest.
MinimumValue
legt den minimalen Wert des gültigen Bereichs fest.
Type
legt den Datentyp für den Vergleich mit einem der Werte der ValidationDataType-Aufzählung fest: String, Integer, Double, Date oder Currency.
Tabelle 11.40: Die wichtigsten Eigenschaften der Klasse RangeValidator (ohne die von BaseValidator geerbten)
RegularExpressionValidator
Das RegularExpressionValidator-Steuerelement ermöglicht die Verwendung eines regulären Ausdrucks für die Überprüfung. Diesen geben Sie in der Eigenschaft ValidationExpression an. Das folgende Beispiel überprüft eine Eingabe darauf, ob es sich um eine gültige Postleitzahl handelt: Postleitzahl:
745
Steuerelemente
Beachten Sie, dass ich im Beispiel wieder zusätzlich ein RequiredFieldValidator eingerichtet habe, um eine leere Eingabe abzufangen. Tabelle 11.41: Eigenschaft Bedeutung Die wichtigste Eigenschaft der ValidationExpression legt das Muster fest, mit dem verglichen werden soll. Klasse RegularExpressionValidator (ohne die CustomValidator von BaseValidator geerbten) Mit Hilfe des CustomValidator-Steuerelements können Sie eigene Vali-
dierungen implementieren. Sie können dazu eine clientseitige JavaScriptFunktion einsetzen, deren Namen Sie in der Eigenschaft ClientValidationFunction angeben. Alternativ können Sie die Überprüfung der Eingabe im serverseitigen Ereignis ServerValidate vornehmen. Im Gegensatz zur JavaScript-Variante ist dann natürlich ein Server-Roundtrip notwendig. Der folgende Quelltext zeigt, wie eine clientseitige Validierung funktioniert. Die Funktion CheckPrimeNumber überprüft, ob die Eingabe eine Primzahl ist. Dieser Funktion werden zwei Argumente übergeben: eine Referenz auf das Steuerelement und eine Referenz auf ein Objekt der Klasse EventArgs. Dieses Objekt besitzt die Eigenschaften Value (die Eingabe) und IsValid. Über IsValid legen Sie fest, ob die Eingabe gültig ist: /* Validierungsfunktion für den CustomValidator */ function CheckPrimeNumber(source, args) { var i; args.IsValid = false; if (isNaN(args.Value)) return; for (i=2; i
Daten im Viewstate speichern
In einigen Fällen muss ein Steuerelement einen Status speichern, der durch äußere Einflüsse geändert werden kann. Da das Page-Objekt, wie Sie bereits wissen, bei jedem Aufruf der aspx-Seite neu erzeugt und nach dem Rendern wieder zerstört wird, kann ein Status nicht einfach nur in Eigenschaften des Steuerelements verwaltet werden. Statusdaten speichern Sie im Viewstate der Seite. Die Eingaben in Steuerelementen, die eine Eingabe erlauben, gehören übrigens nicht dazu, da ASP.NET die Eingabedaten über den Postback zur Verfügung stellt, wie ich es bereits auf Seite 786 beschrieben habe. Als Beispiel soll ein Label dienen, das eine Zahl darstellt und diese Zahl bei einem Klick um den Wert 1 hochzählt. Um den Quellcode zu vereinfachen, wird das Label von der Klasse Control abgeleitet (in der Praxis wäre die Klasse WebControl geeigneter). In einem ersten Versuch wird der Viewstate nicht berücksichtigt: using System; using System.Web.UI;
796
11.4 Benutzerdefinierte Steuerelemente
Steuerelemente
using System.Web.UI.WebControls; namespace Addison_Wesley.Controls { public class CountLabel: Control, IPostBackEventHandler { public int Number = 0; protected override void Render( HtmlTextWriter writer) { writer.Write(" 0) { infoLabel.Text += "Beim Aktualisieren " +
885
ASP.NET-Datenzugriff
" sind Fehler aufgetreten:"; /* Fehler durchgehen */ for (int i=0; i -1) infoLabel.Text = "Gefunden: " + dataView[index]["CompanyName"] + ", " + dataView[index]["City"]; else infoLabel.Text = "Nicht gefunden";
Wenn Sie mehrere Zeilen im Suchergebnis erwarten, können Sie die FindRows-Methode einsetzen, die ein Array aus DataRowView-Objekten mit den gefundenen Zeilen zurückgibt. Der folgende Quelltext ermittelt im DataView alle Kunden aus (good old) Germany. Dazu muss zunächst nach dem Feld Country sortiert werden: dataView.Sort = "Country";
Dann können Sie suchen: dataView.Sort = "Country"; DataRowView[] rows = dataView.FindRows("Germany"); if (rows.Length > 0) { infoLabel.Text = "Gefunden:"; for (int i=0; i
904
13.1 Einfachwert- und Listen-Datenbindung
ASP.NET-Datenbindung
Weil der Ausdruck ein beliebiger Ausdruck sein kann, können Sie neben Methoden natürlich auch Variablen und Eigenschaften und eine Kombination daraus einsetzen. Die aspx-Seite führt die Ausdrücke der Einfachwert-Datenbindung nicht automatisch aus. Alle Steuerelemente und das Page-Objekt stellen Ihnen eine Methode DataBind zur Verfügung, über die Sie die Datenbindung ausführen können. Damit besitzen Sie eine große Flexibilität. Sie können beispielsweise die Datenbindung für einzelne Steuerelemente separat aufrufen. In der Praxis wird es wohl normalerweise ausreichen, über Page.DataBind die Datenbindung der gesamten Seite zu initiieren. Rufen Sie diese Methode dazu im Load-Ereignis der Seite auf: private void Page_Load(object sender, EventArgs e) { /* Datenbindung initiieren */ this.DataBind(); }
Wenn Sie die DataBind-Methode der Seite aufrufen, werden alle Datenbindungs-Ausdrücke der Seite ausgeführt. Wenn Sie nur einzelne Steuerelemente, die speziell für Datenbindung entwickelt wurden (wie z.B. die Listbox und das DataGrid), aktualisieren wollen, können Sie auch deren eigene DataBind-Methode aufrufen. Diese Methode finden Sie allerdings nicht bei einfachen Steuerelementen wie den HTMLSteuerelementen. Die Einfachwert-Datenbindung können Sie für beliebige Eigenschaften von Steuerelementen einsetzen. HTML-Steuerelemente müssen dazu noch nicht einmal serverseitig ausgeführt werden:
Das folgende Beispiel verwendet diese Technik, um die URL eines Image-Steuerelements bei jeder Anforderung der Seite zufällig zu ermitteln:
905
ASP.NET-Datenbindung
Einfachwert-Datenbindung private void Page_Load(object sender, EventArgs e) { /* Datenbindung initiieren */ this.DataBind(); } private string GetImageUrl() { /* Zufallszahl zwischen 1 und 5 erzeugen */ Random rnd = new Random(); int i = rnd.Next(1, 5); /* Bilddateiname zurückgeben */ return "cereal" + i + ".gif"; } Einfachwert-Datenbindung
906
13.1 Einfachwert- und Listen-Datenbindung
ASP.NET-Datenbindung
Wenn der für die Datenbindung verwendete Ausdruck Anführungszeichen enthält, meldet ASP.NET bei der Ausführung der aspx-Seite den Fehler »Servertag wurde falsch formatiert«. Dieser Fehler wird z.B. erzeugt, wenn ein ASP-Button seinen Text aus einer Auflistung auslesen soll:
ASP.NET kommt scheinbar nicht mit den Anführungszeichen innerhalb des Attributwerts zurecht. Zur Lösung des Problems ersetzen Sie die zum Tag gehörenden Anführungszeichen durch einfache Apostrophe:
13.1.2
Listen-Datenbindung
Acht der ASP-Steuerelemente können nicht nur einen einzelnen Wert, sondern gleich eine Liste von Daten anzeigen. Dazu gehören die folgenden:
Das HtmlSelect-Steuerelement zeigt eine einfache Datenliste in einem HTML-select-Element an. Die ASP-Listbox zeigt ebenfalls eine einfache Datenliste in einem HTML-select-Element an. Das ASP-DropDownList-Steuerelement zeigt eine einfache Datenliste in einem HTML-select-Element an, das als DropDown-Liste dargestellt wird. Das ASP-CheckBoxList-Steuerelement erzeugt einzelne CheckBoxSteuerelemente für die Elemente einer einfachen Datenliste und verwendet die Werte der Elemente als Beschriftung.
907
ASP.NET-Datenbindung
Das ASP-RadioButtonList-Steuerelement arbeitet ähnlich dem CheckBoxList-Steuerelement, erzeugt eben lediglich RadioButtonSteuerelemente. Das ASP-DataGrid-Steuerelement ist ein umfangreiches tabellenförmiges Steuerelement mit Zeilen und Spalten, das die Daten einer mehrspaltigen Datenquelle in einer HTML-Tabelle darstellt. Dieses Steuerelement kann Daten automatisch darstellen, Sie können aber auch die Formatierung und Darstellung der Daten fast grenzenlos über verschiedene Techniken wie Stilvorlagen oder benutzerdefinierte Spalten (u.a. mit Steuerelementen) anpassen. Über verschiedene spezielle Spalten und damit verbundene Ereignisse ermöglicht das DataGrid das relativ einfache Sortieren, Editieren, Hinzufügen und Löschen von Daten (das Sie allerdings selbst programmieren müssen). Daneben ist auch das relativ einfache seitenweise Anzeigen von Daten möglich, das verhindert, dass eine zu lange Liste dargestellt wird. Das ASP-DataList-Steuerelement ist quasi ein vereinfachtes DataGrid-Steuerelement, das zunächst nur eine einspaltige Liste darstellt, in deren Zeilen Sie beliebige Steuerelemente unterbringen können. Für mehrspaltige Datenquellen können Sie diese Steuerelemente einfach nebeneinander oder untereinander oder in einer selbst definierten HTML-Tabelle ausgeben. Das DataList-Steuerelement ermöglicht zudem die Ausgabe der Datenzeilen in mehreren Spalten, die horizontal oder vertikal angelegt werden können. Ähnlich dem DataGrid-Steuerelement ermöglicht auch das DataList-Steuerelement das Editieren, Anfügen und Löschen von Daten. Das ASP-Repeater-Steuerelement ist quasi wieder ein vereinfachtes DataList-Steuerelement, das lediglich seinen Inhalt für jede Datenzeile wiederholt.
Von diesen Steuerelementen sind das Repeater-, das DataList- und das DataGrid-Steuerelement in der Lage, eine mehrspaltige Liste anzuzeigen. So können Sie beispielsweise das Ergebnis einer Datenbankabfrage ohne viel Aufwand in einem DataGrid-Steuerelement auf einer aspxSeite anzeigen lassen:
908
13.1 Einfachwert- und Listen-Datenbindung
ASP.NET-Datenbindung
Demo: Listen-Datenbindung private void Page_Load(object sender, EventArgs e) { /* Verbindung zur Datenbank öffnen */ SqlConnection connection = new SqlConnection( @"Server=(local)\NetSDK;Database=Northwind;" + "Trusted_Connection=Yes"); connection.Open(); /* Products-Tabelle einlesen */ SqlCommand command = new SqlCommand( "SELECT ProductID, ProductName, UnitPrice " + "FROM Products", connection); SqlDataReader reader = command.ExecuteReader(); /* DataReader anbinden */ products.DataSource = reader; /* Datenbindung initiieren */ this.DataBind(); /* Verbindung schließen */ connection.Close(); }
909
ASP.NET-Datenbindung
Listen-Datenbindung-Demo
Abbildung 13.1: Beispiel für die Listen-Datenbindung mit einer mehrspaltigen Liste
Das HtmlSelect-Steuerelement, die ASP-Listbox, das ASP-DropDownList-Steuerelement, das ASP-CheckBoxList-Steuerelement und das ASP-RadioButtonList-Steuerelement zeigen lediglich eine einspaltige Liste wie beispielsweise ein Array an: Demo: Listen-Datenbindung private void Page_Load(object sender, EventArgs e)
910
13.1 Einfachwert- und Listen-Datenbindung
ASP.NET-Datenbindung
{ /* Array erzeugen */ string[] names = {"Zaphod", "Ford", "Trillian", "Arthur"}; /* Array anbinden */ nameList.DataSource = names; /* Datenbindung initiieren */ this.DataBind(); } Listen-Datenbindung-Demo
Wenn Sie eine Liste mit mehreren Spalten (z.B. ein DataReaderObjekt mit den Daten einer Datenbankabfrage) als Datenquelle für diese Steuerelemente verwenden, müssen Sie in der Eigenschaft DataTextField festlegen, welche Spalte angezeigt werden soll. Die gemeinsamen Eigenschaften, Methoden und Ereignisse
Die Steuerelemente für Listen-Datenbindung besitzen einige gemeinsame Eigenschaften, Methoden und Ereignisse, die die Datenbindung steuern.
911
ASP.NET-Datenbindung
Abbildung 13.2: Beispiel für die Listen-Datenbindung mit einer einspaltigen Liste
Tabelle 13.1: Die gemeinsamen Eigenschaften der Steuerelemente für ListenDatenbindung
912
Eigenschaft
Beschreibung
DataTextField
beschreibt für Steuerelemente, die nur eine einfache Liste anzeigen können bei mehrspaltigen Datenquellen, welche Spalte angezeigt werden soll.
DataValueField
bestimmt, welche Spalte einer mehrspaltigen Datenquelle als Wert der einzelnen Einträge verwendet werden soll. Dieser Wert wird beim Rendern der Seite in das value-Attribut des HTML-Tags eingetragen. Damit können Sie z.B. im value-Attribut jedes Eintrags eine eindeutige Artikelnummer speichern, während Sie in der Liste den Artikelnamen anzeigen. Damit erleichtern Sie den späteren Zugriff auf die Daten.
DataTextFormatString
In dieser Eigenschaft können Sie einen Formatier-String angeben, der dann zur Formatierung verwendet wird, wenn die Daten angezeigt werden. Gültig sind die unter .NET üblichen Formatierstrings. »{0:C}« steht z.B. für einen Currency-Wert, »{0:dd.mm.yy}« für ein Datum in der kurzen Form. Der Platzhalter {0} steht wie bei .NET üblich für den zu formatierenden Wert.
13.1 Einfachwert- und Listen-Datenbindung
ASP.NET-Datenbindung
Eigenschaft
Beschreibung
DataMember
Datenquellen können aus mehreren Sätzen (Rowsets) bestehen, die unterschiedliche Daten speichern. Das kommt eher selten vor, z.B. beim Aufruf von Stored Procedures, die mehrere SELECT-Abfragen ausführen. Ein Satz Daten enthält dann z.B. eine Liste der Artikel einer Bestelldatenbank, ein weiterer enthält eine Liste der Artikel-Kategorien. Besteht die Datenquelle aus mehreren Sätzen, geben Sie in DataMember den zu verwendenden Satz an.
Methode
Beschreibung
DataBind
fordert das Steuerelement auf, sich mit den Daten der Datenquelle zu füllen.
FindControl
Über diese Methode können Sie eine Referenz auf ein Element ermitteln, das bei bestimmten Steuerelementen wie dem DataRepeater auf dem Steuerelement (automatisch) angelegt wurde. Damit können Sie zum Beispiel den Wert einer bestimmten Tabellenzelle auslesen. Normalerweise wird diese Methode in Ereignisbehandlungsmethoden wie der für das DataBinding-Ereignis genutzt, die für jede Zeile der Datenquelle aufgerufen werden, um auf die einzelnen Steuerelemente der Zeile zuzugreifen.
Ereignis
Beschreibung
DataBinding
Dieses Ereignis wird für jede Zeile der Datenquelle aufgerufen, bevor die Zeile in das Steuerelement eingetragen wird. In der Ereignisbehandlungsmethode können Sie den Inhalt der Zeile manipulieren und damit die Darstellung der Daten anpassen.
SelectedIndexChanged
Dieses Ereignis wird aufgerufen, wenn der Benutzer oder das Programm den aktuell ausgewählten Eintrag in der Liste geändert haben (sofern die Liste eine Selektion zulässt) und die Seite aufgrund eines Postback neu aufgerufen wurde.
Tabelle 13.1: Die gemeinsamen Eigenschaften der Steuerelemente für ListenDatenbindung (Forts.)
Tabelle 13.2: Die wichtigsten gemeinsamen Methoden der Steuerelemente für Listen-Datenbindung
Tabelle 13.3: Die wichtigsten gemeinsamen Ereignisse der Steuerelemente für Listen-Datenbindung
913
ASP.NET-Datenbindung
Mögliche Datenquellen
Ein Objekt, das als Datenquelle für Steuerelemente verwendet werden soll, muss lediglich eine der Schnittstellen ICollection, IEnumerable oder IListSource implementieren. Arrays, die Auflistungs-Klassen des Collection-Namensraums (ArrayList, Hashtable, SortedList etc.) und die Datenzugriffs-Klassen des System.Data-Namensraums (DataSet, SqlDataReader, OleDbDataReader, DataTable und DataView) implementieren mindestens eine dieser Schnittstellen. Diese Klassen können also für die Datenbindung eingesetzt werden. Für die Datenbindung ist es im Prinzip unerheblich, ob ein Array, eine Auflistung, eine ADO.NETDatenquelle oder ein anderes Objekt an das Steuerelement gebunden ist. Die einzige Voraussetzung ist, dass das Objekt eine der genannten Schnittstellen implementiert. Datenquellen anbinden
Das Anbinden einer Datenquelle an ein Steuerelement ist einfach. Um ein Steuerelement anzubinden, schreiben Sie eine Referenz auf das Objekt, das die Daten speichert, in die Eigenschaft DataSource und rufen danach die DataBind-Methode auf. Das folgende Beispiel erzeugt ein ArrayList-Objekt, füllt dieses und bindet es dann an eine ASP-Listbox an: private void Page_Load(object sender, EventArgs e) { /* ArrayList erzeugen und füllen */ ArrayList al = new ArrayList(); al.Add("Nitty-Gritty Programmierung"); al.Add("Nitty-Gritty Visual Basic 6"); al.Add("Nitty-Gritty Visual Basic .NET"); al.Add("Nitty-Gritty C#"); al.Add("Nitty-Gritty Internetprogrammierung"); al.Add("Goto Internetprogrammierung"); al.Add("Programmieren lernen");
914
13.1 Einfachwert- und Listen-Datenbindung
ASP.NET-Datenbindung
/* ArrayList an die Listbox anbinden */ bookList.DataSource = al; /* Listbox auffordern, die Daten einzulesen */ bookList.DataBind();
Einige Auflistungen des .NET Frameworks, wie die Hashtable-Klasse, assoziieren jeden Eintrag der Liste mit einem Schlüssel. Über den Schlüssel können Sie auf die einzelnen Einträge zugreifen, indem Sie diesen im Indizierer der Klasse angeben. Zur Anbindung dieser Auflistungen an ein Steuerelement verwenden Sie die Values- oder die KeysEigenschaft, je nachdem, ob Sie die Schlüssel- oder die Wertliste an das Steuerelement binden wollen. Das folgende Beispiel erzeugt eine Hashtable-Auflistung, füllt diese mit Namen und bindet die gespeicherten Werte an eine Listbox an: private void Page_Load(object sender, EventArgs e) { /* Hashtable erzeugen und füllen */ Hashtable ht = new Hashtable(); ht.Add(1, "Zaphod"); ht.Add(2, "Trillian"); ht.Add(3, "Ford"); ht.Add(4, "Arthur"); /* Bei einer Hashtable muss entweder die Eigenschaft * Values oder Keys gebunden werden */ personList.DataSource = ht.Values; /* Listbox auffordern, die Daten einzulesen */ personList.DataBind(); }
915
ASP.NET-Datenbindung
Wenn Sie einmal aus Versehen die Values- oder die Keys-Eigenschaft bei Auflistungen mit Schlüsseln nicht angeben, zeigt die Liste den Namen des Datentyps der einzelnen Einträge an (im Beispiel wäre das System.Collections.DictionaryEntry). Das liegt daran, dass das Steuerelement die ToString-Methode der einzelnen Einträge aufruft, um die String-Repräsentation zu ermitteln. Die Klasse DictionaryEntry gibt in ToString einfach den Klassennamen zurück. Auflistungen eigener Objekte anbinden
Wenn Sie Auflistungen eigener Objekte an Steuerelemente binden wollen, sollten Sie darauf achten, dass die Klasse der aufgelisteten Objekte die von object geerbte ToString-Methode überschreibt. Ein gebundenes Listensteuerelement für einfache Listen (im Gegensatz zu den Steuerelementen DataGrid, DataList und DataRepeater) ruft für jeden Eintrag einer normalen Auflistung einfach die ToString-Methode des Objekts auf, auf das der Eintrag verweist. Das folgende Beispiel deklariert eine Klasse zur Speicherung von Personendaten, die die ToString-Methode überschreibt, speichert vier Instanzen dieser Klasse in einer ArrayList-Auflistung und bindet diese an eine Listbox an: Listen-Datenbindung: Eigene Objekte
/* Klasse für die Objekte, die aufgelistet werden sollen */ class Person { public string FirstName; public string LastName; public Person(string firstName, string lastName) { this.FirstName = firstName; this.LastName = lastName;
916
13.1 Einfachwert- und Listen-Datenbindung
ASP.NET-Datenbindung
} /* Die ToString-Methode wird überschrieben, * damit die Liste korrekte Daten anzeigt */ public override string ToString() { return this.FirstName + " " + this.LastName; } }
private void Page_Load(object sender, EventArgs e) { /* ArrayList erzeugen und mit eigenen Objekten füllen */ ArrayList al = new ArrayList(); al.Add(new Person("Zaphod", "Beeblebrox")); al.Add(new Person("Ford", "Prefect")); al.Add(new Person("Tricia", "McMillan")); al.Add(new Person("Arthur", "Dent"));
/* ArrayList an die Listbox anbinden */ personList.DataSource = al;
/* Listbox auffordern, die Daten einzulesen */ personList.DataBind(); }
Listen-Datenbindung
917
ASP.NET-Datenbindung
Meine liebsten virtuellen Freunde:
Abbildung 13.3: Eine Liste eigener Objekte
13.1.3
Stileigenschaften
Bei den speziellen datengebundenen Steuerelemente wie dem DataGrid- und dem DataList-Steuerelement können Sie die Formatierung der angezeigten Daten über Eigenschaften vom Typ TableItemStyle bestimmen. Das DataGrid-Steuerelement besitzt beispielsweise dazu die Eigenschaften HeaderStyle, FooterStyle, ItemStyle, PagerStyle und SelectedItemStyle, die den Stil verschiedener Bereiche des Steuerelements bestimmen. Die Klasse TableItemStyle besitzt dazu die in Tabelle 13.4 beschriebenen Eigenschaften. Tabelle 13.4: Die für die Formatierung wichtigen Eigenschaften der Klasse TableItemStyle
918
Eigenschaft
Bedeutung
BackColor
spezifiziert die Hintergrundfarbe.
BorderColor
definiert die Farbe des Rahmens.
BorderStyle
spezifiziert die Art des Rahmens.
BorderWidth
definiert die Breite des Rahmens.
CssClass
In dieser Eigenschaft können Sie einen CSS-Klassennamen definieren um den Steuerelementbereich mit einer CSS-Klasse zu verbinden und damit deren Stileigenschaften einzusetzen.
Font
definiert die Schriftart.
13.1 Einfachwert- und Listen-Datenbindung
ASP.NET-Datenbindung
Eigenschaft
Bedeutung
ForeColor
spezifiziert die Vordergrundfarbe.
Height
gibt die Höhe des Bereichs an.
HorizontalAlign
gibt die horizontale Ausrichtung des Inhalts mit den Werten der Aufzählung HorizontalAlign an. Sie können die folgenden Werte verwenden: Center, Justify (Blocksatz), Left, NotSet (nicht gesetzt) und Right. Die Voreinstellung ist NotSet.
VerticalAlign
gibt die vertikale Ausrichtung der Zellen in der Zeile an. Diese Eigenschaft besitzt den Typ VerticalAlign, das ist eine Aufzählung mit den Werten NotSet, Top, Middle und Bottom.
Width
definiert die Breite des Bereichs.
Wrap
legt fest, ob der dargestellte Text automatisch am rechten Rand umbricht.
Tabelle 13.4: Die für die Formatierung wichtigen Eigenschaften der Klasse TableItemStyle (Forts.)
Da es sich bei diesen Eigenschaften um Objekteigenschaften handelt, werden diese in der Steuerelementdeklaration als untergeordnete Tags deklariert. Die Deklaration eines DataGrid-Steuerelements mit formatieren Bereichen sieht beispielsweise so aus wie im folgenden Listing:
Abbildung 13.4 zeigt das Ergebnis einer Seite, die ein so formatiertes DataGrid-Steuerelement an ein DataReader-Objekt mit den Daten der Artikeltabelle der Northwind-Datenbank gebunden hat. Abbildung 13.4: Ein DataGridSteuerelement mit formatierten Bereichen
920
13.1 Einfachwert- und Listen-Datenbindung
ASP.NET-Datenbindung
Tabelle 13.5 beschreibt die Bedeutung der einzelnen Stileigenschaften. Eigenschaft
Bedeutung
AlternatingItemStyle
Wenn Sie diese Eigenschaft definieren, wird jeder zweite Dateneintrag mit dem hier definierten Stil ausgegeben. Die anderen Dateneinträge werden mit dem in ItemStyle definierten Stil ausgegeben. Damit können Sie alternierende Datenzeilen mit unterschiedlicher Formatierung erreichen.
EditItemStyle
Diese Eigenschaft bestimmt den Stil einer Datenzeile, die in den Editiermodus umgeschaltet wurde (siehe Seite 951).
FooterStyle
bestimmt den Stil des Fußbereichs.
HeaderStyle
bestimmt den Stil des Kopfbereichs.
ItemStyle
bestimmt den Stil der Datenzeilen. Wenn AlternatingItemStyle nicht definiert ist, werden alle Datenzeilen mit dem in ItemStyle definierten Stil ausgegeben, ansonsten nur jede zweite.
PagerStyle
Das DataGrid-Steuerelement ermöglicht die seitenweise Anzeige der Daten über die Eigenschaft AllowPaging. Ist diese Eigenschaft true, wird nur die in PageSize angegebene Anzahl Datensätze im Steuerelement (quasi auf einer Seite) angezeigt. Das DataGrid-Steuerelement stellt dann im unteren Teil eine Zeile mit Verweisen zur Anzeige der einzelnen Seiten zur Verfügung. Die PagerStyle-Eigenschaft bestimmt den Stil dieser Zeile.
SelectedItemStyle
Wenn das Steuerelement eine Selektion einer Datenzeile erlaubt (siehe Seite 967), definiert diese Eigenschaft den Stil einer selektierten Datenzeile.
Tabelle 13.5: Die Bedeutung der Stileigenschaften
Stileigenschaften unter Visual Studio definieren
Unter Visual Studio können Sie Stileigenschaften recht einfach erzeugen. Aktivieren Sie dazu das Steuerelement und klicken Sie auf den Verweis AUTOM. FORMATIERUNG im Eigenschaftenfenster. Im erscheinenden Dialog können Sie aus einer Vielzahl vorgefertigter Formatvorlagen eine passende auswählen (Abbildung 13.5).
921
ASP.NET-Datenbindung
Abbildung 13.5: (Halb-)Automatische Erzeugung einer Vorlage für ein DataGrid
Alternativ und zur nachträglichen Feineinstellung können Sie die Stileigenschaften (neben den anderen Eigenschaften des Steuerelements) im »Eigenschaftengenerator« bearbeiten, den Sie über den Verweis EIGENSCHAFTENGENERATOR im Eigenschaftenfenster öffnen. Abbildung 13.6: Der Eigenschaftengenerator für die Eigenschaften eines DataGridSteuerelements
922
13.1 Einfachwert- und Listen-Datenbindung
ASP.NET-Datenbindung
13.2 Das DataGrid-Steuerelement Das DataGrid-Steuerelement zeigt eine mehrspaltige Datenquelle in Tabellenform an. Es bietet viele Möglichkeiten, mehrspaltige Daten anzuzeigen. Dieses Steuerelement wird hauptsächlich mit den DatenKlassen von ADO.NET verwendet. Diese Klassen lesen normalerweise die Daten einer beliebigen Datenquelle ein. Da ein DataSet-Objekt auch im Programm dynamisch mit Daten gefüllt werden kann (siehe Kapitel 12), können Sie das DataGrid sehr gut zur Darstellung nahezu beliebiger Daten verwenden. Das folgende Beispiel erzeugt ein DataReader-Objekt (das verwendet wird, um genau einmal durch eine Datenquelle zu gehen), füllt dieses mit den Daten der Artikeltabelle der Nordwind-Datenbank und bindet es an ein DataGrid-Steuerelement an: /* Öffnen der Verbindung zur Northwind-Datenbank */ SqlConnection connection = new SqlConnection( @"Server=(local)\NetSDK;Database=Northwind;" + "Trusted_Connection=Yes"); connection.Open(); /* Products-Tabelle einlesen */ SqlCommand command = new SqlCommand( "SELECT ProductID, ProductName, UnitPrice " + "FROM Products", connection); SqlDataReader reader = command.ExecuteReader(); /* DataReader an das DataGrid anbinden */ productGrid.DataSource = reader; /* Datenbindung initiieren */ Page.DataBind();
923
ASP.NET-Datenbindung
/* Verbindung schließen */ connection.Close();
Sofern das DataGrid-Steuerelement wie im folgenden Beispiel formatiert ist,
sieht das Ergebnis aus wie in Abbildung 13.7.
924
13.2 Das DataGrid-Steuerelement
ASP.NET-Datenbindung
Abbildung 13.7: Ein DataGridSteuerelement, das mit den Daten einer Abfrage gefüllt wurde
13.2.1
Die Eigenschaften und Ereignisse des DataGridSteuerelements
Das DataGrid-Steuerelement besitzt eine Vielzahl an Eigenschaften und einige Ereignisse. Ich beschreibe diese bereits hier, damit Sie eine Übersicht erhalten. Einige dieser Elemente werden erst in den nächsten Abschnitten, bei der Beschreibung der Möglichkeiten des DataGrid, näher erläutert. Eigenschaft
Bedeutung
AllowCustomPaging
Wenn Sie hier true eintragen, können Sie die seitenweise Anzeige von Daten, die eingeschaltet ist, wenn AllowPaging true ist, selbst bestimmen und optimieren. Das seitenweise Anzeigen von Daten wird ab Seite 929 beschrieben.
AllowPaging
spezifiziert, ob das Steuerelement die Daten seitenweise anzeigen soll (siehe Seite 929). Die Voreinstellung ist false.
AllowSorting
spezifiziert, ob das Sortieren der Daten möglich sein soll (siehe Seite 939). Die Voreinstellung ist false.
Tabelle 13.6: Die wichtigsten Eigenschaften des DataGridSteuerelements (außer den von WebControl geerbten)
925
ASP.NET-Datenbindung
Tabelle 13.6: Die wichtigsten Eigenschaften des DataGridSteuerelements (außer den von WebControl geerbten) (Forts.)
Eigenschaft
Bedeutung
AlternatingItemStyle
definiert den Stil jeder zweiten (alternierenden) Datenzeile. Stile werden ab Seite 918 beschrieben.
AutoGenerateColumns
spezifiziert, ob das Steuerelement die Spalten der Liste selbst erzeugen soll oder ob diese über die Columns-Eigenschaft vom Programmierer definiert werden. Benutzerdefinierte Spalten werden ab Seite 942 beschrieben. Die Voreinstellung ist false.
BackImageUrl
gibt die URL eines als Hintergrund zu verwendenden Bildes an.
CellPadding
definiert den Abstand zwischen dem Inhalt der einzelnen Zellen und deren Rahmen in Pixel. Die Voreinstellung ist -1, was bedeutet, dass das entsprechende HTML-Attribut nicht ausgegeben wird.
CellSpacing
definiert den Abstand zwischen den einzelnen Zellen in Pixeln. Die Voreinstellung ist -1.
Columns
Columns ist eine Auflistung der Spalten der Liste. Die Auflistung verwaltet DataGridColumn-Referenzen, die auf von dieser Klasse abgeleitete Objekte zeigen. Die Definition von Spalten im DataGrid wird ab Seite 942 beschrieben.
Controls
liefert eine Auflistung aller Child-Steuerelemente des DataGrid-Steuerelements.
926
CurrentPageIndex
Wenn seitenweises Anzeigen ermöglicht ist (Seite 929), definiert diese Eigenschaft den Index der aktuell anzuzeigenden Seite.
DataKeyField
In dieser Eigenschaft können Sie den Namen eines Datenfeldes angeben, das das Schlüsselfeld der zugrunde liegenden Daten ist. Das Steuerelement füllt beim Binden der Daten die DataKeys-Auflistung mit den Schlüsselwerten der einzelnen Datensätze. Damit können Sie beim Editieren der Daten (Seite 978) sehr einfach den Schlüssel des zu editierenden Datensatzes ermitteln.
DataKeys
referenziert eine einfache Auflistung der Schlüsselwerte der Datenquelle, deren Feld über DataKeyField spezifiziert wurde.
DataMember
spezifiziert die Sub-Datenquelle des Steuerelements, wenn die Datenquelle aus mehreren Sätzen von Daten besteht.
DataSource
spezifiziert die Datenquelle des Steuerelements.
13.2 Das DataGrid-Steuerelement
ASP.NET-Datenbindung
Eigenschaft
Bedeutung
EditItemIndex
spezifiziert den Index des Datensatzes, der zum Editieren geöffnet ist.
EditItemStyle
definiert den Stil einer Zeile, die in den Editiermodus umgeschaltet wurde. Der Editiermodus wird ab Seite 951 behandelt. Stile werden ab Seite 918 beschrieben.
FooterStyle
definiert den Stil der Fußzeile.
GridLines
definiert mit den Werten der GridLines-Aufzählung, ob die erzeugte HTML-Tabelle Gitternetzlinien anzeigt. Sie können die folgenden Werte einstellen: None, Horizontal, Vertical und Both.
HeaderStyle
definiert den Stil der Überschrifts-Zeile. Stile werden ab Seite 918 beschrieben.
HorizontalAlign
gibt die horizontale Ausrichtung des Inhalts mit den Werten der Aufzählung HorizontalAlign an. Sie können die folgenden Werte verwenden: Center, Justify (Blocksatz), Left, NotSet (nicht gesetzt) und Right. Die Voreinstellung ist NotSet.
Items
referenziert eine Auflistung von DataGridItem-Objekten, über die Sie die einzelnen Zeilen des DataGrid erreichen. Diese Klasse wird auf Seite 971 beschrieben.
ItemStyle
definiert den Stil der einzelnen Datenzeilen. Stile werden ab Seite 918 beschrieben.
PageCount
definiert die Anzahl der Seiten beim seitenweisen Anzeigen der Daten (Seite 929).
PagerStyle
definiert den Stil der Navigationszeile, die beim seitenweisen Anzeigen der Daten (Seite 929) automatisch angezeigt wird. Stile werden ab Seite 918 beschrieben.
PageSize
definiert die Anzahl der auf einer Seite angezeigten Datensätze beim seitenweisen Anzeigen der Daten (Seite 929).
SelectedIndex
gibt den Index des selektieren Datensatzes an. Das Selektieren wird ab Seite 967 behandelt.
SelectedItem
gibt das DataGridItem-Objekt zurück, das aktuell selektiert ist.
SelectedItemStyle
definiert den Stil einer Zeile, die vom Anwender selektiert wurde, falls das Selektieren ermöglicht ist. Stile werden ab Seite 918 beschrieben.
Tabelle 13.6: Die wichtigsten Eigenschaften des DataGridSteuerelements (außer den von WebControl geerbten) (Forts.)
927
ASP.NET-Datenbindung
Tabelle 13.6: Die wichtigsten Eigenschaften des DataGridSteuerelements (außer den von WebControl geerbten) (Forts.)
Tabelle 13.7: Die wichtigsten Ereignisse des DataGrid-Steuerelements
928
Eigenschaft
Bedeutung
ShowFooter
definiert, ob eine Fußzeile angezeigt wird. Der Inhalt der Fußzeile kann über ein Template (Seite 960) definiert werden.
ShowHeader
gibt an, ob die Kopfzeile der Tabelle ausgegeben wird.
VirtualItemCount
definiert die Gesamtanzahl der anzuzeigenden angezeigten Datensätze beim benutzerdefinierten seitenweisen Anzeigen der Daten (Seite 929).
Ereignis
Bedeutung
CancelCommand
wird aufgerufen, wenn der Anwender beim Editieren den Befehl zum Abbrechen aufgerufen hat, sofern dieser Befehl zur Verfügung steht. Einen entsprechenden Befehl können Sie in einer Template-Spalte erzeugen, indem Sie die Eigenschaft CommandName eines Schalter-Steuerelements auf "Cancel" setzen.
DeleteCommand
wird aufgerufen, wenn der Anwender beim Editieren den Befehl zum Löschen aufgerufen hat, sofern dieser Befehl zur Verfügung steht. Einen entsprechenden Befehl können Sie in einer Template-Spalte (Seite 960) über ein Schalter-Steuerelement mit CommandName = "Delete" erzeugen.
EditCommand
wird aufgerufen, wenn der Anwender den Befehl zum Editieren aufgerufen hat, sofern dieser Befehl zur Verfügung steht. Einen entsprechenden Befehl können Sie in einer Template-Spalte erzeugen, indem Sie die Eigenschaft CommandName eines Schalter-Steuerelements auf "Edit" setzen.
ItemCommand
wird aufgerufen, wenn der Anwender einen Button oder LinkButton, der in einer der Spalten angelegt wurde, betätigt hat.
ItemCreated
wird für die einzelnen Zeilen des DataGrid aufgerufen, wenn diese erzeugt werden.
ItemDataBound
wird aufgerufen, nachdem eine Zeile im DataGrid an die Datenquelle gebunden wurde.
PageIndexChanged
wird beim seitenweisen Anzeigen der Daten (Seite 929) aufgerufen, nachdem der Anwender einen Befehl zum Wechsel der Datenseite aufgerufen hat.
13.2 Das DataGrid-Steuerelement
ASP.NET-Datenbindung
Ereignis
Bedeutung
SelectedIndexChanged
wird aufgerufen, wenn der Index der selektieren Datenzeile geändert wurde.
SortCommand
wird aufgerufen, wenn der Anwender den Befehl zum Sortieren aufgerufen hat, sofern dieser Befehl zur Verfügung steht.
UpdateCommand
wird aufgerufen, wenn der Anwender den Befehl zum Aktualisieren aufgerufen hat, sofern dieser Befehl zur Verfügung steht. Einen entsprechenden Befehl können Sie in einer Template-Spalte erzeugen, indem Sie die Eigenschaft CommandName eines Schalter-Steuerelements auf "Update" setzen.
13.2.2
Tabelle 13.7: Die wichtigsten Ereignisse des DataGridSteuerelements (Forts.)
Seitenweise Anzeige der Daten
Per Voreinstellung zeigt das DataGrid-Steuerelement alle Datensätze der Datenquelle in einer nach unten quasi unbegrenzten Liste an. Wie im Internet üblich, erlaubt das DataGrid-Steuerelement aber auch das seitenweise Anzeigen der Daten. Dabei können Sie festlegen, wie viele Datenzeilen auf einer Seite dargestellt werden. Das Steuerelement fügt automatisch eine Zeile hinzu, über die der Benutzer die Seite wechseln kann. Problemlos ist das seitenweise Anzeigen lediglich, wenn die Datenquelle die Schnittstelle ICollection implementiert, was nur bei Arrays, einigen Auflistungen wie der ArrayList-Klasse und bei einigen Datenzugriffs-Klassen wie der DataTable- und der DataView-Klasse der Fall ist. Der Grund für die Voraussetzung dieser Schnittstelle ist wahrscheinlich, dass das Steuerelement über die Count-Eigenschaft der ICollection-Schnittstelle ermittelt, wie viele Seiten dargestellt werden müssen. (Halb-)Automatisches seitenweises Anzeigen
Wenn Sie eine Datenquelle anbinden, die ICollection implementiert, können Sie das seitenweise Anzeigen über die folgenden Schritte realisieren:
Setzen Sie im Steuerelement die Eigenschaft AllowPaging auf true, Definieren Sie in der Eigenschaft PageSize die Anzahl der auf einer Seite anzuzeigenden Datensätze. Die Voreinstellung ist 10.
929
ASP.NET-Datenbindung
Legen Sie über die Mode-Eigenschaft der PagerStyle-Eigenschaft fest, wie die Navigationszeile dargestellt wird. Möglich sind hier die Werte der Aufzählung PagerMode. Der Wert NextPrev sorgt dafür, dass je ein Hyperlink für die vorherige und die nächste Seite angezeigt wird. Den Text des Hyperlinks können Sie in den Eigenschaften NextPageText und PrevPageText der PagerStyle-Eigenschaft definieren. Wie beim Calendar-Steuerelement können Sie hier auch img-Tags ablegen, um Bilder anzuzeigen. Mit dem Wert NumericPages in der PagerStyle-Eigenschaft erreichen Sie, dass das Steuerelement die Seitennummern als Verweis anzeigt (was wohl in der Praxis die beste Lösung ist). Sorgen Sie dafür, dass beim Laden der Seite die Datenquelle nur eingelesen und gebunden wird, wenn die Seite nicht aufgrund eines Postback aufgerufen wurde. Dazu fragen Sie einfach die IsPostback-Eigenschaft der Seite ab. Da Sie die Datenbindung im nächsten Schritt im PageIndexChanged-Ereignis aufrufen müssen, implementieren Sie diese idealerweise in einer privaten Methode. Erzeugen Sie eine Ereignisbehandlungsmethode für das PageIndexChanged-Ereignis. Dieses Ereignis wird aufgerufen, nachdem der Anwender einen der Verweise zum Ändern der angezeigten Seite angeklickt hat. Sie müssen in diesem Ereignis die aktuelle Seite festlegen. Dazu schreiben Sie die Eigenschaft NewPageIndex des übergebenen DataGridPageChangedEventArgs-Objekts in die Eigenschaft CurrentPageIndex des DataGrid-Steuerelements. Zusätzlich müssen Sie die Datenbindung erneut initiieren, damit das Steuerelement die aktuelle Seite korrekt darstellen kann.
Das folgende Beispiel liest die Products-Tabelle in ein DataSet-Objekt ein und bindet dieses an das DataGrid-Steuerelement. Die im DataSetObjekt verwalteten DataTable-Objekte erlauben das seitenweise Anzeigen der Daten, weil sie die ICollection-Schnittstelle implementieren. Um zu erreichen, dass die Datenbindung gezielt ausgeführt wird, wird diese in eine private Methode BindProductsTable ausgelagert. Diese Methode wird beim Laden der Seite nur dann aufgerufen, wenn es sich nicht um einen Postback handelt (was über die Eigenschaft IsPostback des Page-Objekts ermittelt wird). Da die Datenbindung beim Wechsel einer Seite auf dem DataGrid ebenfalls neu initiiert werden muss, wird
930
13.2 Das DataGrid-Steuerelement
ASP.NET-Datenbindung
diese Methode daneben noch in der Methode für das PageIndexChanged-Ereignis aufgerufen: DataGrid mit seitenweiser Anzeige /* Ereignisbehandlungsmethode für das * PageIndexChanged-Ereignis */ private void HandlePageIndexChanged(object source, DataGridPageChangedEventArgs e) { productGrid.CurrentPageIndex = e.NewPageIndex; BindProductsTable(); } /* Methode zum Anbinden des DataGrid an die * Products-Tabelle */ private void BindProductsTable() { /* Öffnen der Verbindung zur * Northwind-Datenbank */ SqlConnection connection = new SqlConnection( @"Server=(local)\NetSDK;Database=Northwind;" + "Trusted_Connection=Yes"); connection.Open(); /* Products-Tabelle einlesen */
931
ASP.NET-Datenbindung
SqlDataAdapter dataAdapter = new SqlDataAdapter( "SELECT ProductID, ProductName, UnitPrice " + "FROM Products", connection); /* DataSet erzeugen und füllen */ DataSet dataSet = new DataSet(); dataAdapter.Fill(dataSet, "Products"); /* DataTable an das DataGrid anbinden */ productGrid.DataSource = dataSet.Tables["Products"]; /* Datenbindung initiieren */ Page.DataBind(); /* Verbindung schließen */ connection.Close(); } /* Ereignisbehandlungsmethode für das * Load-Ereignis der Seite */ private void Page_Load(object sender, EventArgs e) { if (!Page.IsPostBack) { BindProductsTable(); } }
932
13.2 Das DataGrid-Steuerelement
ASP.NET-Datenbindung
Abbildung 13.8: Ein DataGridSteuerelement mit seitenweiser Anzeige
933
ASP.NET-Datenbindung
Problematischer wird das seitenweise Anzeigen mit Datenquellen, die ICollection nicht implementieren. Dazu gehört leider auch die für das Lesen von Daten wichtige DataReader-Klasse. Für diesen Fall müssen Sie ein benutzerdefiniertes seitenweises Anzeigen programmieren. Benutzerdefiniertes seitenweises Anzeigen
Beim normalen seitenweisen Anzeigen wird immer die komplette Datenquelle eingelesen, die im DataGrid-Steuerelement angezeigt werden soll. Das kann ineffizient werden, wenn die Datenquelle sehr viele Datensätze verwaltet. Das benutzerdefinierte seitenweise Anzeigen ermöglicht deshalb, nur die Datensätze einzulesen, die auf einer Seite dargestellt werden sollen. Ein anderer Grund für den Einsatz des benutzerdefinierten seitenweisen Anzeigens ist die Verwendung einer Datenquelle, die nicht die Schnittstelle ICollection implementiert und die deshalb nicht für das normale seitenweise Anzeigen eingesetzt werden kann. Zur Einstellung des benutzerdefinierten seitenweisen Anzeigens definieren Sie wie beim normalen Anzeigen die Eigenschaften AllowPaging und PageSize. Zusätzlich setzen Sie die Eigenschaft AllowCustomPaging auf true. In der Eigenschaft VirtualItemCount müssen Sie dem DataGrid dann mitteilen, wie viele Datensätze die Datenquelle beinhalten würde, wenn Sie diese komplett einlesen würden. An Hand dieses Werts berechnet das DataGrid die Anzahl der darzustellenden Seiten. Beim ersten Laden der aspx-Seite lesen Sie dann nur die Datensätze ein, die auf der ersten Seite dargestellt werden müssen. Im Ereignis PageIndexChanged lesen Sie abhängig von der übergebenen Seite die Datensätze ein, die auf der jeweiligen Seite dargestellt werden sollen. Das Ganze ist in der Praxis recht kompliziert, was besonders dann gilt, wenn Sie Datenquellen mit Hilfe von ADO.NET einlesen. Um die Eigenschaft VirtualItemCount ermitteln zu können, müssen Sie zunächst beim ersten Laden der Seite die Anzahl der Datensätze ermitteln. Unter ADO.NET können Sie dies über eine SQL-Abfrage (»SELECT Count(*) FROM Tabelle«) erledigen. Schwieriger ist die Logik zur Abfrage der Daten für das seitenweise Anzeigen. Sie müssen hierbei dafür sorgen, dass nur die Datensätze ermittelt werden, die auf der aktuellen Seite dargestellt werden sollen. Bei zehn Datensätzen auf einer Seite wären das beispielsweise die Datensätze 1 bis 10 für die erste Seite und die Datensätze 11 bis 20 für die Seite 2. Die Beispiele der .NET Framework-Dokumentation (beispielsweise das zur AllowCustomPaging-Eigenschaft) arbeiten mit im 934
13.2 Das DataGrid-Steuerelement
ASP.NET-Datenbindung
Programm erzeugten DataTable-Objekten, die dynamisch mit Beispieldaten gefüllt werden. Damit ist die Logik recht einfach implementiert, aber sehr praxisfern. In der Praxis macht das benutzerdefinierte seitenweise Anzeigen nur dann Sinn, wenn Sie mit ADO.NET eine externe Datenquelle einlesen. Und da beginnen die Probleme: ADO.NET erlaubt, soweit ich weiß, nicht das Abfragen der Datensätze mit einer bestimmten Datensatznummer. Sie können zwar das PrimärschlüsselFeld, das viele Datenbanktabellen besitzen, in der Abfrage berücksichtigen. Dieses Feld wird normalerweise mit einem eindeutigen nummerischen Index belegt und beginnend bei einem Startwert für jeden Datensatz hochgezählt. Das Feld ProductId der Products-Tabelle der Northwind-Datenbank ist ein Beispiel dafür. Sie können aber nie sicher sein, dass die Primärschlüssel auch in einer durchgehenden Folge gespeichert sind. Wenn ein Datensatz beispielsweise gelöscht wird, fehlt ein Primärschlüssel-Wert in der Liste der Zahlen. Sie können also nicht einfach den Primärschlüssel bei der Ermittlung der Datensätze einer Seite auswerten. Spätestens dann, wenn Datensätze in der Tabelle gelöscht wurden, beginnen die Probleme. Außerdem können Primärschlüsselwerte oft auch frei vergeben werden, sodass ein Teil der Datensätze vielleicht die Id 1000 bis 1100 und ein anderer die Id 2000 bis 2100 besitzt. Dann funktioniert keine Logik zur Abfrage der Daten nach deren Primärschlüssel. Ich habe dieses Problem für die Products-Tabelle der Northwind-Datenbank folgendermaßen gelöst: Da die Primärschlüssel der Datensätze (im Feld ProductId) beginnend bei 1 einigermaßen aufsteigend definiert sind, lese ich beim ersten Start der Seite den letzten PrimärschlüsselWert ein. Diesen verwende ich als Anzahl der virtuellen Datensätze. In der Methode zum Anbinden der Daten, die um ein Argument für die aktuelle Seite erweitert wurde, ermittle ich den Start- und den EndPrimärschlüsselwert für die darzustellende Seite. Mit diesen Werten filtere ich die Abfrage der Daten (über eine SQL-WHERE-Klausel). Das Beispiel setzt übrigens wieder ein DataReader-Objekt ein, da das benutzerdefinierte seitenweise Anzeigen den Einsatz dieses Objekts ermöglicht. Die Veränderungen gegenüber dem vorherigen Beispiel habe ich fett formatiert. In der Deklaration des DataGrid-Steuerelements ist zunächst AllowCustomPaging auf true gesetzt:
Das Programm ist so erweitert, dass die BindProductsTable-Methode nun mit einem Argument arbeitet, das die aktuelle Seite definiert. Die SQL-Anweisung zum Lesen der Daten ist um eine WHERE-Klausel erweitert, die die Id der abzufragenden Datensätze entsprechend der Seite einschränkt: private void BindProductsTable(int page) { /* Öffnen der Verbindung zur * Northwind-Datenbank */ SqlConnection connection = new SqlConnection( @"Server=(local)\NetSDK;Database=Northwind;" + "Trusted_Connection=Yes"); connection.Open(); /* Products-Tabelle abhängig von der übergebenen * Seite einlesen */ string sql = "SELECT ProductID, ProductName, " + "UnitPrice FROM Products";
936
13.2 Das DataGrid-Steuerelement
ASP.NET-Datenbindung
if (page > 0) { /* Ermitteln der Id des ersten und des * letzten Datensatzes, der auf der aktuellen * Seite dargestellt werden soll. */ int startId = (page * productGrid.PageSize) + 1; int endId = startId + productGrid.PageSize; sql += " WHERE ProductId >= " + startId + " AND ProductId
1169
Peer-To-Peer- und Mehrschichten-Programme
Für die Angabe der Lease-Zeiten verwenden Sie die Zeichen »ms« (Millisekunden), »s« (Sekunden), »m« (Minuten), »h« (Stunden) und »d« (Tage). Die Remote-Klassen werden im wellknown-Tag registriert. Das Argument mode steuert den Aktivierungsmodus mit den Werten SingleCall oder Singleton. Im Argument type geben Sie den Typ in der Form Typname, Assemblierungsname an. Der Typname muss voll qualifiziert angegeben werden (also inklusive Namensraum). Der Name der Assemblierung ist der Dateiname. Das Argument objectUri spezifiziert schließlich den Namen des Endpunktes für die so registrierte Klasse. Im channel-Tag spezifizieren Sie die zu verwendenden Kanäle. Im Beispiel wird der Kanal über ein Template angegeben (ref="tcp"). In der Basis-Konfigurationsdatei des .NET Frameworks (machine.config) finden Sie die Definition dieser Templates. Alternativ können Sie den Kanal auch über den vollen Namen der Klasse und den »stabilen Namen« (Strong Name) der Assemblierung registrieren. Dies ist allerdings nicht allzu einfach, da der stabile Name einer Assemblierung aus verschiedenen Informationen besteht:
Die Registrierung einer clientseitig aktivierbaren Klasse erfolgt über das activated-Tag:
Anders als bei normalen Anwendungs-Einstellungen liest die CLR die Remoting-Konfiguration nicht automatisch aus der Anwendungs-Konfigurationsdatei ein. Der Server muss dazu die Configure-Methode der
1170
16.3 Remoting
Peer-To-Peer- und Mehrschichten-Programme
RemotingConfiguration-Klasse aufrufen, der der Name der Konfigurationsdatei übergeben wird. Sie können den Dateinamen als String übergeben. Wenn Sie allerdings die Anwendungskonfigurationsdatei für die Einstellungen verwenden, sollten Sie den Dateinamen der Anwendung im Programm ermitteln und lediglich .config an diesen Namen anhängen (falls sich der Dateiname der Anwendung einmal ändert): [STAThread] static void Main(string[] args) { /* Remoting über die Konfigurationsdatei * konfigurieren */ // Dateiname der ausführenden Assemblierung ermitteln // und .config anhängen Assembly assembly = Assembly.GetExecutingAssembly(); string configFilename = assembly.Location + ".config"; // Remoting konfigurieren RemotingConfiguration.Configure(configFilename); /* Die Konsolenanwendung anhalten, damit diese * nicht gleich wieder beendet wird */ Console.WriteLine("Betätigen Sie Return, " + "um den Server zu beenden."); Console.ReadLine(); }
Client-Konfiguration
Die Client-Konfiguration nehmen Sie idealerweise wieder in der Konfigurationsdatei der Anwendung vor. Eine typische Konfiguration stellt die Lease-Zeiten ein, registriert die zu verwendenden Kanäle, definiert Typen, die remote aktiviert werden sollen, und gibt das Ziel für die Remote-Aktivierung an:
1171
Peer-To-Peer- und Mehrschichten-Programme
Die Angaben im wellknown-Tag müssen Sie genau prüfen. Wenn Sie hier falsche Angaben machen, führt das nicht zu einem Fehler. Der Client instanziert die falsch registrierte Klasse dann nicht im Server, sondern in sich selbst (mit Hilfe der Bibliothek, die er ja besitzt). Überprüfen Sie also Ihre Client/Server-Anwendung, indem Sie den Client einmal aufrufen, ohne dass der Server läuft. Startet der Client ohne
1172
16.3 Remoting
Peer-To-Peer- und Mehrschichten-Programme
Probleme, müssen Sie die Konfiguration überprüfen. Kommt es zu der Ausnahme, dass der »Zielcomputer die Verbindung verweigerte«, ist (ausnahmsweise) alles in Ordnung. Für die Registrierung einer clientseitig aktivierbaren Klasse geben Sie die URL des Servers im url-Argument des client-Tag an und registrieren den Dienst über das activated-Tag:
Der Client muss die Konfiguration wie der Server zunächst einlesen. Instanzen werden dann ganz einfach über new erzeugt. Das Erzeugen der Instanz und alle Methodenaufrufe sollten in eine Ausnahmebehandlung eingebettet werden: static void Main(string[] args) { /* Remoting über die Konfigurationsdatei * konfigurieren */ Assembly assembly = Assembly.GetExecutingAssembly(); string configFilename = assembly.Location + ".config"; RemotingConfiguration.Configure(configFilename); try { /* Instanz der Remote-Klasse erzeugen */ SSALibrary.Demo demo = new SSALibrary.Demo(); /* Methode aufrufen */ Console.WriteLine("Servername: " + demo.GetMachineName()); } catch (TargetInvocationException ex)
1173
Peer-To-Peer- und Mehrschichten-Programme
{ Console.WriteLine(ex.InnerException.Message); return; } catch (Exception ex) { Console.WriteLine(ex.Message); return; } }
16.3.10 Einstellen der Lease-Zeit Für clientseitig aktivierte Objekte und serverseitig aktivierte SingletonObjekte ist die Einstellung der Lease-Zeit recht wichtig. Die Voreinstellung der Lease-Zeit ist fünf Minuten für die erste Aktivierung und zwei Minuten für die Erneuerung bei jedem folgenden Aufruf einer Methode. Die Lease-Zeit kann einmal in der Remote-Klasse selbst vorgenommen werden. Eine andere Möglichkeit ist, dass der Client die LeaseZeit bei Bedarf erneuert. Daneben können Sie noch Sponsor-Objekte einsetzen, die die Lease-Zeit automatisch erneuern. Die Klassen zur Verwaltung der Lease-Objekte finden Sie im Namensraum System. Runtime.Remoting.Lifetime. Einstellung in der Remote-Klasse
Die Remote-Klasse kann die geerbte Methode InitializeLifetimeService überschreiben, um die Leasing-Werte einzustellen. Sie holen sich dazu eine Referenz auf die ILease-Schnittstelle der Instanz. Ein Lease kann nur im Status der Initialisierung eingestellt werden, weswegen Sie den aktuellen Status des Lease abfragen müssen. Dann können Sie über die Eigenschaften InitialLeaseTime und RenewOnCallTime die Zeitspannen für den Lease bestimmen. Die Angaben erfolgen in Form einer Instanz der TimeSpan-Klasse. InitializeLifetimeService gibt eine Referenz auf das Lease-Objekt zurück:
1174
16.3 Remoting
Peer-To-Peer- und Mehrschichten-Programme
/* Klasse, die remote vom Client instanziert wird */ public class RemoteClass: MarshalByRefObject { /* Überschreiben der Methode * InitializeLifetimeService zur Einstellung * des Lease */ public override object InitializeLifetimeService() { // Lease holen ILease lease = (ILease)base.InitializeLifetimeService(); // Ein Lease kann nur im Initialstatus // modifiziert werden, deswegen wird überprüft, // ob dieser Status aktiv ist. if (lease.CurrentState == LeaseState.Initial) { // Einstellen der initialen Lease-Zeit lease.InitialLeaseTime = TimeSpan.FromMinutes(1); // Einstellen, dass bei einem Methodenaufruf // die Lease-Zeit um eine Minute erneuert wird lease.RenewOnCallTime = TimeSpan.FromMinutes(1); } return lease; } // Methode zum Remote-Aufruf public string GetTime() { return DateTime.Now.ToLongTimeString(); } }
1175
Peer-To-Peer- und Mehrschichten-Programme
Eine besondere Einstellung ist ein unendliches Leasing. Dazu geben Sie einfach null zurück. Beachten Sie, dass die erzeugten Objekte dann nicht freigegeben werden, solange der Server läuft. Werden Klassen im Single-Call-Modus instanziert, verwaltet der Server immer mehr Objekte, wenn Clients zwischenzeitlich beendet und neue Clients gestartet werden. Die Instanzen beendeter Clients belegen dann auf dem Server unnötig Speicherplatz. Einstellung über die Konfigurationsdatei
Wie Sie ja bereits gesehen haben, können Sie die Lease-Zeiten auch über die Remote-Konfigurationsdatei des Servers oder des Clients einstellen. Wenn der Server allerdings Lease-Zeiten einstellt, werden die clientseitigen Einstellungen nicht verwendet. Damit ist sichergestellt, dass der Server bei Bedarf die Lease-Zeiten kontrollieren kann. Clientseitige Erneuerung des Lease
Der Client kann einen Lease bei Bedarf erneuern. Das ist einmal über Sponsor-Objekte möglich, wie ich es im nächsten Abschnitt erläutere. Eine andere Möglichkeit ist die direkte Erneuerung. Dazu ermitteln Sie eine Referenz auf die ILease-Schnittstelle des Remote-Objekts über die GetLifetimeService-Methode und rufen die Renew-Methode dieser Schnittstelle auf. Dabei übergeben Sie ein TimeSpan-Objekt, das die neue Zeitspanne definiert. Sie müssen beachten, dass der Lease auch null sein kann (bei Leases mit unendlicher Lebensdauer): // Lease auslesen ILease lease = (ILease)remoteObject.GetLifetimeService(); if (lease != null) { // Lease um fünf Minuten erneuern lease.Renew(TimeSpan.FromMinutes(5); }
Sponsor-Objekte
Für eine automatische Erneuerung eines Lease bieten sich SponsorObjekte an, die für den Lease registriert werden. Wie Sie ja bereits wissen, fragt der Lease-Manager nach dem Ablauf der Lease-Zeit die regis-
1176
16.3 Remoting
Peer-To-Peer- und Mehrschichten-Programme
trierten Sponsor-Objekte zur Erneuerung ab. Eine Sponsor-Klasse wird von MarshalByRefObject abgeleitet und implementiert die Schnittstelle ISponsor. Ein einfacher Sponsor, der die Lease-Zeit lediglich um 20 Sekunden erhöht, wird so programmiert: class ClientSponsor: MarshalByRefObject, ISponsor { /* Implementierung der Renewal-Methode * der ISponsor-Schnittstelle */ public TimeSpan Renewal(ILease lease) { Console.WriteLine("Sponsor wurde aufgefordert, " + "den Lease zu erneuern."); if (lease == null ) { Console.WriteLine("Lease is null"); } else { Console.WriteLine("CurrentLeaseTime: {0}, CurrentState: {1}", lease.CurrentLeaseTime, lease.CurrentState); } // Lease um 20 Sekunden erneuern return TimeSpan.FromSeconds(20); } }
Der Sponsor muss dann für den Lease des Remote-Objekts registriert werden, was Sie für den Server oder für den Client ausführen können. Für ein Objekt können auch mehrere Sponsoren registriert sein. Der Lease-Manager fragt die Sponsoren der Reihe nach ab. Reagiert ein Sponsor nicht innerhalb der in der Eigenschaft SponsorshipTimeout des Lease definierten Zeitspanne, wird er aus der Liste gestrichen und der nächste abgefragt.
1177
Peer-To-Peer- und Mehrschichten-Programme
Zur Registrierung eines Sponsor-Objekts ermitteln Sie den Lease über die GetLifetimeService-Methode der RemotingServices-Klasse. Dabei müssen Sie wieder beachten, dass der Lease auch null sein kann. Den Sponsor registrieren Sie dann über die Register-Methode des LeaseObjekts: static void Main(string[] args) { /* Remoting konfigurieren */ ChannelServices.RegisterChannel(new TcpChannel(0)); object[] activationAttributes = {new UrlAttribute( "tcp://localhost:1001/DemoServer")}; ClientSponsor sponsor = null; try { /* Instanz erzeugen */ Library.Demo demo = (Library.Demo) Activator.CreateInstance( typeof(Library.Demo), null, activationAttributes); /* Sponsor registrieren */ ILease lease = (ILease) RemotingServices.GetLifetimeService(demo); sponsor = new ClientSponsor(); if (lease != null) lease.Register(sponsor); else Console.WriteLine("Lease ist null.\r\n"); /* Methode aufrufen */ Console.WriteLine("Servername: " + demo.GetMachineName());
1178
16.3 Remoting
Peer-To-Peer- und Mehrschichten-Programme
} catch (TargetInvocationException
ex)
{ Console.WriteLine(ex.InnerException.Message); return; } catch (Exception ex) { Console.WriteLine(ex.Message); return; } Console.WriteLine("Beenden mit Return."); Console.ReadLine(); }
Das Beispiel wartet nach dem Aufruf der Methode auf die Betätigung einer Taste. Zwischenzeitlich wird in regelmäßigen Abständen der Sponsor aufgerufen, der den Lease dann erneuert. Eigenartigerweise wurde der Sponsor in meinen Versuchen nicht aufgerufen, wenn der Client über eine Konfigurationsdatei konfiguriert wurde. Die Konfiguration des Servers über eine solche Datei führte hingegen zu keinen Problemen. 16.3.11 Remoting mit Ereignissen Einige Remote-Server müssen ihre Clients von Zeit zu Zeit über einen Zustand benachrichtigen. Ein Chat-Server muss beispielsweise alle eingehenden Nachrichten an die einzelnen Clients verteilen. Für solche Benachrichtigungen werden unter .NET üblicherweise Ereignisse (Events) eingesetzt. Delegates, die einfache Form von Events, wären ebenfalls technisch möglich. Das Problem bei reinen Delegates ist aber, dass ein Client die Delegate-Eigenschaft des Servers so beschreiben kann, dass alle regist-
1179
Peer-To-Peer- und Mehrschichten-Programme
rierten Delegate-Methoden anderer Clients entfernt werden (womit diese dann nicht mehr benachrichtigt werden). Bei Ereignissen kann ein Client nur Delegate-Methoden hinzufügen und nur seine eigenen wieder entfernen. Ereignisse sind also wesentlich sicherer. Die Bibliothek(en)
Bevor ich zeige, wie Sie die Bibliothek(en) für das Remoting entwickeln, beschreibe ich ein Problem, das dabei entsteht (und das wir lösen müssen). Wenn sich Ihr Gehirn beim Nachvollziehen der Lösung dieses Problems verknotet, machen Sie sich nichts daraus: Meines hat das bei der Arbeit an diesem Thema auch gemacht. Beim Entknoten haben sich aber auch gleich noch einige andere Konten gelöst, weswegen ich jetzt viel freier denken kann ... Zum Problem: Der Server ruft beim Remoting mit Ereignissen Methoden im Client auf. Dazu muss er den Typ und die Assemblierung kennen, in der diese implementiert sind. Sind die Methoden in der Assemblierung des Client implementiert, muss der Server die Client-Assemblierung lokalisieren können. Viele Remoting-Beispiele mit Ereignissen funktionieren deshalb nur dann, wenn die Assemblierung des Client im Ordner des Servers gespeichert ist. Wird der Client aus einem anderen Ordner heraus oder von einem anderen Rechner aus aufgerufen, resultiert dies dann in einer Ausnahme vom Typ FileNotFoundException. Da es nicht sinnvoll und oft auch nicht möglich ist, die Client-Assemblierung im Ordner der Server-Anwendung zu speichern, müssen Sie eine andere Technik verwenden. Bei dieser wird für jedes Ereignis ein zwischengeschaltetes Objekt verwendet. Ich bezeichne diese Klasse in Anlehnung an das zugrunde liegende Beispiel von Mike Woodring als EventShim (was immer er damit auch meint, ein »Shim« ist normalerweise ein »Plättchen« ...). Die Klasse dieses Objekts wird in der Bibliothek deklariert. Damit ist dieser Typ im Client und im Server bekannt. Der Client instanziert das EventShim-Objekt und übergibt dabei eine Referenz auf einen Delegate, der die Ereignisbehandlungsmethode im Client verwaltet. Das EventShim-Objekt speichert diese Referenz in einer privaten Eigenschaft und erzeugt in seinem Konstruktor einen eigenen Delegate für seine Ereignisbehandlungsmethode. Der Client erhält eine Referenz auf diesen Delegate und gibt diese an die Delegate-Eigenschaft der Instanz der Remote-Klasse weiter. 1180
16.3 Remoting
Peer-To-Peer- und Mehrschichten-Programme
Client-Anwendung Ereignis-Methode
Server-Anwendung RemoteObjekt Delegate
Abbildung 16.4: Remoting mit Ereignissen über ein EventShimObjekt
Delegate EventShimObjekt DelegateReferenz EreignisMethode
DelegateReferenz Das Remote-Objekt ruft über die Delegate-Referenz den Delegate und damit die Methode im EventShim-Objekt auf.
Die Ereignismethode im EventShimObjekt ruft über die DelegateReferenz den Delegate und damit die Ereignismethode im Client auf.
Ruft das Remote-Objekt nun über ihre Delegate-Referenz die damit verbundene Methode im EventShim-Objekt auf, gibt diese den Methodenaufruf direkt an den Client weiter. Das EventShim-Objekt arbeitet damit als Mittler zwischen Server und Client. Der Server ist zufrieden, weil er den Typ für den Aufruf der Ereignisse ermitteln kann. Der Client ist auch zufrieden, denn er erhält die Aufrufe einfach weitergereicht. Die EventShim-Klasse wird von MarshalByRefObject abgeleitet, damit die Remoting-Infrastruktur den Delegate aufrufen kann. Die Instanz der Klasse verbleibt im Client, weswegen es auch kein Problem ist, dass diese Instanz Referenzen auf clientseitige Delegates verwendet. Sie sollten noch beachten, dass die EventShim-Instanz nicht vom LeaseManager freigegeben werden darf. Deshalb sollten Sie die InitialLifetimeService-Methode so überschreiben, dass die Instanz unendlich lange lebt (bis der Client beendet wird). Das folgende Beispiel implementiert eine Anwendung, über die Nachrichten auf dem Netz an alle angeschlossenen Clients versendet werden können.
1181
Peer-To-Peer- und Mehrschichten-Programme
Abbildung 16.5: Ein einfacher Chat-Server mit zwei Clients
Die Bibliothek enthält zunächst die Deklaration des Delegate für das Ereignis: using System; using System.Runtime.Remoting.Lifetime; namespace NetMessageLib { /* Delegate für das Ereignis der Remote-Klasse */ public delegate void MessageEvent( string machineName, string message);
Dann folgt die Deklaration der EventShim-Klasse. Diese enthält zur Vereinfachung eine statische Methode Create, die der Client aufruft. Diese Methode erzeugt das EventShim-Objekt und gibt eine Referenz auf den im EventShim-Objekt erzeugten Delegate zurück. Beachten Sie, dass die Ereignisbehandlungsmethode der EventShim-Klasse öffentlich sein muss, damit die CLR diese aufrufen kann:
1182
16.3 Remoting
Peer-To-Peer- und Mehrschichten-Programme
public class EventShim: MarshalByRefObject { /* Eigenschaft, die eine Referenz auf den * Delegate des Client verwaltet */ private event MessageEvent target = null; /* Konstruktor zur Übergabe einer * Delegate-Referenz */ public EventShim(MessageEvent target) { this.target = target; } /* Methode für das Ereignis */ public void MessageEventHandler( string machineName, string message) { /* Aufruf des Ziel-Delegate im Client */ target(machineName, message); } /* Methode zur Erzeugung einer Instanz dieser * Klasse */ public static MessageEvent Create( MessageEvent target) { /* Neue Instanz der EventShim-Klasse erzeugen, * die eine Referenz auf die Delegate-Methode * des Client verwaltet */ EventShim eventShim = new EventShim(target); /* Neue Instanz des Delegate mit der in der * EventShim-Instanz enthaltenen * Ereignismethode erzeugen */
1183
Peer-To-Peer- und Mehrschichten-Programme
return new MessageEvent( eventShim.MessageEventHandler); } /* Lease-Einstellungen so überschreiben, dass * das Objekt unendlich lange im Speicher * verbleibt(bis die Client-Anwendung * geschlossen wird) */ public override Object InitializeLifetimeService() { return null; } }
Die eigentliche Remote-Klasse muss für das Beispiel im SingletonModus instanziert werden und überschreibt deswegen die Lease-Einstellungen so, dass das Objekt so lange lebt wie die Server-Anwendung. Eine Methode ermöglicht das Senden von Nachrichten an alle angeschlossenen Clients. Diese Methode ruft das Ereignis auf, das über eine Eigenschaft verwaltet wird: public class NetMessage: MarshalByRefObject { /* Eigenschaft für das Ereignis */ public event MessageEvent MessageEventHandler = null; /* Lease-Einstellungen so überschreiben, dass * das Objekt unendlich lange im Speicher * verbleibt(bis die Server-Anwendung * geschlossen wird) */ public override Object InitializeLifetimeService() { return null;
1184
16.3 Remoting
Peer-To-Peer- und Mehrschichten-Programme
} /* Methode zum Senden einer Nachricht an alle * verbundenen Clients */ public void SendMessage(string machineName, string message) { /* Ereignis aufrufen */ if (this.MessageEventHandler != null) this.MessageEventHandler( machineName, message); } } }
Der Server
Der Server muss nicht speziell programmiert werden. Er macht in diesem Beispiel nichts weiter, als den Dienst zu registrieren: using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; namespace Server { class Server { [STAThread] static void Main(string[] args) { /* Remoting konfigurieren */ ChannelServices.RegisterChannel(
1185
Peer-To-Peer- und Mehrschichten-Programme
new TcpChannel(1001)); RemotingConfiguration. RegisterWellKnownServiceType( typeof(Library.NetMessage), "NetMessageEndPoint", WellKnownObjectMode.Singleton); Console.WriteLine("NetMessage-Server läuft."); Console.WriteLine(": Beenden."); Console.ReadLine(); } } }
Der Client
Der Client muss nun eine Methode für das Ereignis zur Verfügung stellen, dafür einen neuen Delegate erzeugen und diesen an das ebenfalls zu erzeugende EventShim-Objekt übergeben. Beachten Sie, dass diese Methode öffentlich sein muss, damit sie von der CLR aufgerufen werden kann: using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; using System.Runtime.Remoting.Lifetime; namespace Client { public class Start { /* Methode für das Ereignis der Remote-Klasse */ public static void MessageEventHandler( string machineName, string message)
1186
16.3 Remoting
Peer-To-Peer- und Mehrschichten-Programme
{ Console.WriteLine(machineName + ": " + message); } [STAThread] static void Main(string[] args) { /* Auswerten der Befehlszeile */ if (args.Length < 2) { Console.WriteLine( "Aufruf: Client.exe "); return; } /* Remoting konfigurieren */ ChannelServices.RegisterChannel( new TcpChannel(0)); NetMessageLib.NetMessage netMessage = null; try { /* Instanz der Remote-Klasse erzeugen */ netMessage = (NetMessageLib.NetMessage) Activator.GetObject( typeof(NetMessageLib.NetMessage), "tcp://" + args[0] + ":1001/NetMessageEndPoint"); /* Delegate-Instanz über die EventShim* Klasse erzeugen und der Delegate* Eigenschaft der Remote-Klasse zuweisen */ netMessage.MessageEventHandler +=
1187
Peer-To-Peer- und Mehrschichten-Programme
NetMessageLib.EventShim.Create( new NetMessageLib.MessageEvent( Start.MessageEventHandler)); } catch (Exception ex) { Console.WriteLine(ex.Message); return; } Console.WriteLine(": " + "Nachricht senden."); Console.WriteLine(": " + "Beenden."); string message = ""; do { message = Console.ReadLine(); if (message == "") return; else { /* Nachricht senden */ netMessage.SendMessage(args[1], message); } } while (true); } } }
Beachten Sie, dass der erste Aufruf eines Ereignisses im Client recht lange dauern kann, wenn der Client auf einem entfernten Rechner ausgeführt wird. Ab dem zweiten Aufruf ist die Geschwindigkeit (wenigstens im Intranet) aber sehr hoch.
1188
16.3 Remoting
Peer-To-Peer- und Mehrschichten-Programme
16.3.12 Asynchroner Aufruf von Methoden Methoden, deren Ausführung auf dem Server viel Zeit benötigt, können Sie asynchron aufrufen. Der Aufruf der Methode kehrt dann direkt zurück und das Programm wird weiter ausgeführt. Um das erfolgreiche Beenden und eventuelle Rückgabewerte auszuwerten, können Sie im Client eine Callback-Methode implementieren, die von der CLR dann automatisch aufgerufen wird, wenn die aufgerufene Methode beendet wurde. Das Beispiel, das ich für den asynchronen Aufruf verwende, basiert auf einer Server-Anwendung, die eine Klasse für mathematische Berechnungen exportiert. Die Bibliothek sieht folgendermaßen aus: using System; public class MathCalculations: MarshalByRefObject { /* Methode zur Berechnung einer großen Primzahl */ public long CalcPrimeNumber(long maxNumber) { for (long number = maxNumber; number > 0; number--) { bool found = true; for (long i = 2; i < number; i++) { if ((number % i) == 0) { found = false; break; } } if (found) return number; }
1189
Peer-To-Peer- und Mehrschichten-Programme
return 0; } }
Die Serveranwendung konfiguriert wie üblich das Remoting der Flexibilität wegen aus einer Konfigurationsdatei (die hier nicht dargestellt wird): using System; using System.Runtime.Remoting; public class Server { public static void Main() { /* Konfiguration des Remoting */ RemotingConfiguration.Configure("server.exe.config"); Console.WriteLine("Server läuft. " + "Beenden mit ."); Console.ReadLine(); } }
Zur Demonstration, dass während der Abarbeitung asynchron aufgerufener Methoden auch Methoden normal aufgerufen werden können, implementiert der Client eine Methode zur Fakultätsberechnung: using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Messaging; public class Start
1190
16.3 Remoting
Peer-To-Peer- und Mehrschichten-Programme
{ /* Methode zur Berechnung der Fakultät */ public static long Faculty(int number) { long result = 1; for (long i = 1; i 0)
Die Ermittlung des Suchstrings für den Autor ist dann einfach. Dazu existiert ja bereits die Methode getQuery: string contentQuery = getQuery("Contents", this.contentQuery.Text, this.operatorList.SelectedItem.Value); string titleQuery = getQuery("Title", this.titleQuery.Text, this.operatorList.SelectedItem.Value);
1228
17.11 Kapitel 14
Lösungen zu den Übungen
string authorQuery = getQuery("Author", this.authorQuery.Text, this.operatorList.SelectedItem.Value);
Diese Eingabe muss natürlich beim Zusammensetzen des SQL-Strings berücksichtigt werden: if (contentQuery != null) sql += " AND CONTAINS(Contents, '" + contentQuery + "')"; if (titleQuery != null) sql += " AND CONTAINS(Doctitle, '" + titleQuery + "')"; if (authorQuery != null) sql += " AND CONTAINS(Author, '" + authorQuery + "')";
Zur Auswertung der ausgewählten Dokumenttypen gehe ich folgendermaßen vor: /* Ermitteln der Dokumenttypen */ string documentTypeQuery = ""; // Durchgehen aller Checkboxen for (int i=0; i 0) host = args[0]; if (args.Length > 1) { try { port = Convert.ToInt32(args[1]); } catch {} } /* Überprüfen der Eingaben */ if (host == null || port