Visual Basic .NET
Duncan Mackenzie Kent Sharkey Übersetzung: Dorothea Reder
Visual Basic .NET 쐽
Schritt für Schritt programmieren lernen
쐽
Von den .NET- und Sprachgrundlagen bis zu komplexen Anwendungen
쐽
Mit vielen praktischen Beispielen
Markt + Technik Verlag
Die Deutsche Bibliothek – CIP-Einheitsaufnahme Ein Titeldatensatz für diese Publikation ist bei Der Deutschen Bibliothek erhältlich. Die Informationen in diesem Produkt 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. Autorisierte Übersetzung der amerikanischen Originalausgabe: „Teach Yourself VB.NET in 21 Days“ 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 Software-Bezeichnungen, die in diesem Buch erwähnt werden, sind gleichzeitig auch eingetragene Warenzeichen oder sollten als solche betrachtet werden. Authorized translation from the English language edition, entitled SAMS TEACH YOURSELF VB.NET 21 DAYS by MACKENZIE, DUNCAN and SHARKEY, KENT, published by Pearson Education, Inc, publishing as Sams Publishing, Copyright © 2002 All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from Pearson Education, Inc. GERMAN language edition published by PEARSON EDUCATION DEUTSCHLAND, Copyright © 2002 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 04 03 02
ISBN 3-8272-6272-0 © 2002 by Markt+Technik Verlag, ein Imprint der Pearson Education Deutschland GmbH, Martin-Kollar-Straße 10–12, D–81829 München/Germany Alle Rechte vorbehalten Übersetzung: Dorothea Reder, Köln Fachlektorat: Michael und Eva Kolberg, München Lektorat: Melanie Kasberger,
[email protected] Korrektur: Christel Metke, Köln Herstellung: Philipp Burkart,
[email protected] Einbandgestaltung: Grafikdesign Heinz H. Rauner, Gmund Satz: reemers publishing services gmbh, Krefeld, (www.reemers.de) Druck und Verarbeitung: Kösel, Kempten Printed in Germany
Inhaltsverzeichnis Über die Autoren. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
17
Einleitung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Überblick. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Für wen ist dieses Buch gedacht? . . . . . . . . . . . . . . . . . . . . . . . . . Was Sie hier lernen werden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Was Sie hier nicht lernen werden. . . . . . . . . . . . . . . . . . . . . . . . . Anforderungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Was Sie bereits können müssen . . . . . . . . . . . . . . . . . . . . . . . . . . Die Kapitel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Website . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die CD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . In diesem Buch verwendete Konventionen . . . . . . . . . . . . . . . . . Feedback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
19 19 20 21 21 22 22 23 23 24 24 25
Woche 1
Überblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
28
Tag 1
Willkommen bei Visual Basic .NET . . . . . . . . . . . . . . . . . . . . . . . . . . .
29
1.1
30 31 32 36 37 40 40 41 43 44 45 45 49 50 50 56 57
1.2 1.3 1.4
1.5
1.6 1.7
Computerprogrammierung verstehen . . . . . . . . . . . . . . . . . . . . . Die Rolle des Betriebssystems. . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Rolle der Programmiersprachen . . . . . . . . . . . . . . . . . . . . . . Warum Computerprogramme schreiben? . . . . . . . . . . . . . . . . . . Eine kurze Geschichte von Visual Basic . . . . . . . . . . . . . . . . . . . Was ist .NET? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .NET-Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .NET-Framework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .NET-Dienste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .NET-Geräte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ihre erste Visual Basic .NET-Anwendung . . . . . . . . . . . . . . . . . . Vorbereitung zum Codieren. . . . . . . . . . . . . . . . . . . . . . . . . . . . . Wo ist meine IDE? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine einfache Aufgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Den Code schreiben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zusammenfassung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5
Inhaltsverzeichnis
1.8
Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
57 57 58
Tag 2
Mit Visual Basic .NET arbeiten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.1 Die Visual Studio-IDE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Erste Schritte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das Hauptfenster der Visual Studio-IDE . . . . . . . . . . . . . . . . . . . Werkzeugsammlung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Lösungen und Projekte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Dateien . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2 Ihre erste Windows-Anwendung . . . . . . . . . . . . . . . . . . . . . . . . . . Das Projekt erstellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Benutzeroberfläche entwickeln . . . . . . . . . . . . . . . . . . . . . . . Das Projekt ausführen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das Projekt erstellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eigenen Code hinzufügen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3 Zusammenfassung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.4 Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.5 Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Übung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
59 60 60 63 69 82 84 86 86 86 88 88 91 93 93 94 94
Tag 3
Einführung in die Programmierung mit Visual Basic .NET . . . . . . . 3.1 Variablen und Zuweisungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Was ist eine Variable? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verfügbare Variablentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Einfache Variablen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Variablen deklarieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zuweisung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Konstanten. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2 Vorschläge für Benennungsstandards . . . . . . . . . . . . . . . . . . . . . . 3.3 Einfache Berechnungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Operatoren verwenden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eingebaute Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.4 Schreiben Sie Ihre eigenen Routinen . . . . . . . . . . . . . . . . . . . . . Subroutinen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Gültigkeitsbereich . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.5 Beispielanwendung: Einen Future Value berechnen . . . . . . . . . 3.6 Zusammenfassung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
95 96 96 97 97 103 103 105 106 107 108 108 109 114 114 115 116 118 124
6
Inhaltsverzeichnis
3.7 3.8
Tag 4
Tag 5
Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Übung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
124 125 125 126
Den Programmfluss steuern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.1 Mit Steueranweisungen eine Auswahl treffen . . . . . . . . . . . . . . . Die If-Anweisung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die If-Anweisung erweitern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Einzeilige und sofortige If-Anweisungen . . . . . . . . . . . . . . . . . . . 4.2 Boolesche Ausdrücke und boolesche Logik . . . . . . . . . . . . . . . . . Vergleichsoperatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Logische Operatoren. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Kurzschluss . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.3 Der Umgang mit mehreren Auswahlmöglichkeiten: die Select Case-Anweisung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.4 Schleifen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . For...Next. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . While...End While . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Do-Schleife . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Abbruchbedingung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Endlosschleifen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Konsequenzen für die Leistung . . . . . . . . . . . . . . . . . . . . . . . . . . 4.5 Umsetzung Ihres neuen Wissens . . . . . . . . . . . . . . . . . . . . . . . . . Aus einer Datei lesen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein einfaches Spiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.6 Komplexe Schleifen durch Rekursion vermeiden . . . . . . . . . . . . 4.7 Zusammenfassung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.8 Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.9 Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Übung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
127 128 129 132 136 137 137 139 140
Anwendungsarchitektur in .NET . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.1 Was ist eine Anwendungsarchitektur?. . . . . . . . . . . . . . . . . . . . . . Die Rolle des Softwarearchitekten . . . . . . . . . . . . . . . . . . . . . . . . Welche Teile eines Systems werden als Anwendungsarchitektur verstanden? . . . . . . . . . . . . . . . . . . . . . . 5.2 Mögliche Architekturen in .NET . . . . . . . . . . . . . . . . . . . . . . . . . Die drei Elemente einer jeden Anwendung . . . . . . . . . . . . . . . . Wie viele Schichten? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
165 166 166
141 143 143 147 149 150 152 153 154 155 157 159 161 162 162 163 163
168 172 172 173
7
Inhaltsverzeichnis
5.3
5.4 5.5 5.6 Tag 6
Strukturierte Ausnahmebehandlung. . . . . . . . . . . . . . . . . . . . . . . Was ist eine strukturierte Ausnahmebehandlung? . . . . . . . . . . . . Fehler und Ausnahmen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Der Try-Block . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Der Catch-Abschnitt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Try...End Try-Blöcke schachteln . . . . . . . . . . . . . . . . . . . . . . . . . Der Finally-Abschnitt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ausnahmen auslösen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Debugging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Quelle von Bugs. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Learning by Doing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Modi im Leben eines Programms . . . . . . . . . . . . . . . . . . . . . Schritt für Schritt durch Ihren Code . . . . . . . . . . . . . . . . . . . . . . Variablen beobachten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Andere Debugging-Werkzeuge. . . . . . . . . . . . . . . . . . . . . . . . . . . Zusammenfassung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
188 188 189 190 190 197 198 199 200 200 202 205 207 211 216 217 217 218 218 218
Mit Objekten arbeiten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.1 Für den Anfang: Was ist ein Objekt? . . . . . . . . . . . . . . . . . . . . . . Klassen und Instanzen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Referenzen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.2 Das Konzept in Code umwandeln . . . . . . . . . . . . . . . . . . . . . . . . Eigenschaften . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine Objektinstanz erzeugen . . . . . . . . . . . . . . . . . . . . . . . . . . . . Code in Klassen einkapseln . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
221 222 222 223 224 224 227 229
6.2
6.3 6.4 6.5
8
175 175 178 178 181 185 186 186 186
Was zu tun ist, wenn ein gutes Programm nicht funktioniert, und wie Sie das verhindern. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187 6.1
Tag 7
Wo passt da .NET hinein? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine Client-Technologie wählen . . . . . . . . . . . . . . . . . . . . . . . . . Welche Architektur ist die richtige? . . . . . . . . . . . . . . . . . . . . . . . Wichtige Faktoren bei Ihrer Entscheidung . . . . . . . . . . . . . . . . . Beispielszenarien. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zusammenfassung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Inhaltsverzeichnis
7.3
7.4 7.5 7.6
Themen für Fortgeschrittene . . . . . . . . . . . . . . . . . . . . . . . . . . . . Überladen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Vererbung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Konstruktoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Namensräume . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Gemeinsam genutzte Objekte und Elemente . . . . . . . . . . . . . . . Zusammenfassung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Übung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
233 233 235 242 243 246 247 247 248 248 249
Rückblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251
Woche 2
Überblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
Tag 8
Einführung in das .NET-Framework . . . . . . . . . . . . . . . . . . . . . . . . . . . 255 8.1 8.2
8.3
8.4 8.5 8.6
Tag 9
Was ist das .NET-Framework? . . . . . . . . . . . . . . . . . . . . . . . . . . . Wichtige Klassen im .NET-Framework . . . . . . . . . . . . . . . . . . . . Console . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Umgebung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Random . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Math . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Sammlungsklassen im .NET-Framework. . . . . . . . . . . . . . . . . . . Wie Sie im .NET-Framework finden, wonach Sie suchen . . . . . Die Regeln der Jagd . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Suche nach der richtigen Klasse . . . . . . . . . . . . . . . . . . . . . . Zusammenfassung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Übung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
256 257 257 264 265 266 267 271 272 272 276 277 277 277 278
Mit Windows Forms eine Benutzeroberfläche erstellen . . . . . . . . . . . 279 9.1 9.2
Überblick über Windows Forms . . . . . . . . . . . . . . . . . . . . . . . . . . Eine Windows Forms-Anwendung erstellen . . . . . . . . . . . . . . . . Das Projekt einrichten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Steuerelemente zum Formular hinzufügen. . . . . . . . . . . . . . . . . Steuerelemente benennen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ereignisbehandlung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
280 282 282 283 284 285
9
Inhaltsverzeichnis
Mehrere Ereignisbehandler für ein einzelnes Ereignis . . . . . . . . Mit dem Code-Editor Objekte und Ereignisse finden. . . . . . . . . Mehrere Ereignisse mit einem Ereignisbehandler . . . . . . . . . . . Mehr über Steuerelemente. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Optionsschaltergruppen erzeugen . . . . . . . . . . . . . . . . . . . . . . . . Dem Beispiel Filer ein Kontrollkästchen hinzufügen . . . . . . . . . Eingabevalidierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Klasse MessageBox verwenden . . . . . . . . . . . . . . . . . . . . . . . Parameter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ergebnisse erhalten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Unsichtbare Steuerelemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . Timer. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . NotifyIcon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ErrorProvider. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Dialog-Steuerelemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eigene Dialogfenster erstellen . . . . . . . . . . . . . . . . . . . . . . . . . . . Das Dialogfenster erstellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das Ergebnis des Dialogfensters einrichten . . . . . . . . . . . . . . . . . Das Dialogfenster anzeigen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zusammenfassung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Übung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
287 287 288 289 290 292 294 297 298 299 302 303 304 305 306 311 311 312 314 316 316 317 317 317
Mit Web Forms eine Benutzeroberfläche erstellen . . . . . . . . . . . . . . . 10.1 Das Web-Programming-Modell . . . . . . . . . . . . . . . . . . . . . . . . . . ASP.NET. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Wie sich die Webprogrammierung von der Windows-Programmierung unterscheidet . . . . . . . . . . . . . . . . . . 10.2 Standardsteuerelemente von Web Forms verwenden . . . . . . . . . 10.3 Komplexere Web-Forms-Steuerelemente . . . . . . . . . . . . . . . . . . . 10.4 Validator-Steuerelemente verwenden . . . . . . . . . . . . . . . . . . . . . . 10.5 Zusammenfassung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.6 Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.7 Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Übung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
319 320 322
9.3
9.4 9.5
9.6
9.7
9.8 9.9 9.10
Tag 10
10
322 324 334 337 342 342 343 343 343
Inhaltsverzeichnis
Tag 11
Einführung in Datenbanken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.1 Eine Datenbank ist die Lösung für alle Probleme im Leben . . . Die Entscheidung fällt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Der Schritt zu einer echten Datenbank . . . . . . . . . . . . . . . . . . . . 11.2 Einführung in SQL. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Datensätze mit der SELECT-Anweisung abrufen . . . . . . . . . . . . Neue Datensätze hinzufügen . . . . . . . . . . . . . . . . . . . . . . . . . . . . Datensätze bearbeiten. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Unerwünschte Datensätze entfernen . . . . . . . . . . . . . . . . . . . . . . Wie es mit SQL weitergeht. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.3 Gängige Datenbankprobleme und -lösungen . . . . . . . . . . . . . . . Aktualisierungsinkonsistenz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Felder mit mehreren Werten . . . . . . . . . . . . . . . . . . . . . . . . . . . . Joins: Mehrere Tabellen auf einmal abfragen . . . . . . . . . . . . . . . Referenzintegrität . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Primärschlüssel erstellen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.4 Die Beispieldatenbank erstellen . . . . . . . . . . . . . . . . . . . . . . . . . . Die notwendigen Dateien herunterladen . . . . . . . . . . . . . . . . . . Access 2000 oder Access 2002. . . . . . . . . . . . . . . . . . . . . . . . . . . . MSDE und SQL Server 2000. . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.5 Das Setup mit System.Data testen . . . . . . . . . . . . . . . . . . . . . . . . 11.6 Zusammenfassung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.7 Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.8 Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Tag 12
Mit .NET auf Daten zugreifen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 375 12.1
12.2
12.3
Überblick über den Datenzugriff in .NET. . . . . . . . . . . . . . . . . . ADO und OLE DB. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ADO.NET . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Standardaufgaben bei Datenbanken . . . . . . . . . . . . . . . . . . . . . . Eine Verbindung zur Datenbank herstellen . . . . . . . . . . . . . . . . Eine SQL-Anweisung ausführen . . . . . . . . . . . . . . . . . . . . . . . . . Daten abrufen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Mit Datenmengen arbeiten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Daten in ein DataSet laden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Durch Daten navigieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Daten bearbeiten (Hinzufügen, Bearbeiten, Löschen) . . . . . . . . Die Datenbank aktualisieren . . . . . . . . . . . . . . . . . . . . . . . . . . . .
345 346 347 348 349 349 352 352 353 354 355 355 359 360 363 363 367 368 368 368 369 371 372 372 373 373 376 376 377 378 379 385 386 388 389 391 394 400
11
Inhaltsverzeichnis
Mit mehreren Tabellen arbeiten . . . . . . . . . . . . . . . . . . . . . . . . . Ansichten. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Datenbindung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Datenbindung mit Windows Forms . . . . . . . . . . . . . . . . . . . . . . . Zusammenfassung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Übung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
412 414 416 417 421 421 422 422 422
Tag 13
Der Server-Explorer. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.1 Was ist der Server-Explorer? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Was ist ein Dienst? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.2 Dienste erkunden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Datenverbindungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine Verbindung zur Datenbank herstellen . . . . . . . . . . . . . . . . 13.3 Mit Diensten arbeiten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Dienste einsehen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine Verbindung zu einem anderen Server herstellen . . . . . . . . 13.4 Programme schreiben, die Dienste verwenden . . . . . . . . . . . . . . Mit dem Server-Explorer Datenzugriffscode schreiben. . . . . . . . Auf Leistungsmonitore und Ereignisprotokolle zugreifen . . . . . . 13.5 Zusammenfassung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.6 Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.7 Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
423 424 425 426 427 431 431 432 433 434 434 438 449 449 450 450 450
Tag 14
Einführung in die objektorientierte Programmierung. . . . . . . . . . . . . 451
12.4 12.5 12.6 12.7
14.1
14.2
14.3
12
Überblick über die objektorientierte Programmierung (OOP) . . Lineare und objektorientierte Programmierung . . . . . . . . . . . . . Codeorganisation mit Objekten . . . . . . . . . . . . . . . . . . . . . . . . . . Wichtige Konzepte der OOP . . . . . . . . . . . . . . . . . . . . . . . . . . . . Klassen, Objekte und Instanzen . . . . . . . . . . . . . . . . . . . . . . . . . . Eigenschaften . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Vererbung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Konstruktoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine Anwendung mit der OOP entwerfen . . . . . . . . . . . . . . . . . . Objekte identifizieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
452 453 455 456 456 458 459 460 465 467 468
Inhaltsverzeichnis
14.4 14.5 14.6
Eigenschaften und Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . Objekte modellieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zusammenfassung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Übung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
469 470 471 472 472 472 473
Rückblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 475
Woche 3
Überblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 478
Tag 15
Objekte in Visual Basic .NET erzeugen . . . . . . . . . . . . . . . . . . . . . . . . 15.1 Objekte erzeugen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . In Visual Basic .NET eine neue Klasse deklarieren. . . . . . . . . . . Eigenschaften hinzufügen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Methoden erstellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ereignisse hinzufügen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Interfaces definieren und verwenden . . . . . . . . . . . . . . . . . . . . . . 15.2 Die erzeugten Objekte verwenden . . . . . . . . . . . . . . . . . . . . . . . . Namensräume . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine Bibliotheks-DLL erstellen und verwenden . . . . . . . . . . . . . 15.3 Zusammenfassung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15.4 Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15.5 Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Übung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
479 480 480 483 490 495 498 506 507 509 510 511 511 511 512
Tag 16
Windows Forms für Fortgeschrittene . . . . . . . . . . . . . . . . . . . . . . . . . . . 16.1 Menüs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Einem Formular ein Menü hinzufügen . . . . . . . . . . . . . . . . . . . Tastatur und Menü . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Code hinzufügen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Verbesserungsvorschläge. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16.2 Multiple Document Interface-Programme . . . . . . . . . . . . . . . . . Was ist das Multiple Document Interface? . . . . . . . . . . . . . . . . . Das Elternformular einfügen . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das MDI und Menüs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16.3 Kompliziertere Windows Forms-Steuerelemente. . . . . . . . . . . . . TreeView . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
513 514 514 516 518 520 522 522 523 525 532 532
13
Inhaltsverzeichnis
16.4 16.5 16.6
Tag 17
17.2
17.3 17.4 17.5
Ströme und Dateien . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Was ist ein Strom?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Dateien und Verzeichnisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Aus einer Textdatei lesen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . In eine Textdatei schreiben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Mit den Grafikklassen zeichnen . . . . . . . . . . . . . . . . . . . . . . . . . . Die Grafikklassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Worauf kann ich zeichnen? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Formen zeichnen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Bilder speichern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zusammenfassung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
550 550 551 553 555 571 572 579 584 588 591 591 592 592 593
Der letzte Schliff . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 595 18.1
18.2
18.3
14
535 537 545 546 546 546 547
Mit dem .NET Framework arbeiten . . . . . . . . . . . . . . . . . . . . . . . . . . . 549 17.1
Tag 18
ListView. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Splitter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zusammenfassung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Eine Anwendung dokumentieren . . . . . . . . . . . . . . . . . . . . . . . . Erstellen Sie die einfachste Lösung . . . . . . . . . . . . . . . . . . . . . . . Vermeiden Sie Vorbedingungen . . . . . . . . . . . . . . . . . . . . . . . . . Kommentieren Sie nur das Verwirrende, nicht das Offensichtliche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Dokumentieren Sie das System, nicht nur den Code . . . . . . . . . Tipps und Konventionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Benennung von Variablen, Steuerelementen und Objekten . . . Kommentarblöcke. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Quellcodekontrolle verwenden . . . . . . . . . . . . . . . . . . . . . . . Code auschecken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Code einchecken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Änderungen einsehen und rückgängig machen (Rollback) . . . . Sicherheitsüberlegungen bei der Verwendung von Visual SourceSafe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
596 597 600 601 601 604 604 606 608 609 613 614 616
Inhaltsverzeichnis
18.4 18.5 18.6
Zusammenfassung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
616 617 617 617
Tag 19
Die Anwendung bereitstellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19.1 Einführung in die Bereitstellung einer Anwendung . . . . . . . . . . Ein einfaches Setup-Programm erstellen . . . . . . . . . . . . . . . . . . . Konfigurationsdateien . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19.2 Bereitstellung mehrerer Projekte . . . . . . . . . . . . . . . . . . . . . . . . . 19.3 Zusammenfassung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19.4 Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19.5 Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Übung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
619 620 622 630 632 636 636 637 637 637
Tag 20
Einführung in XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 639 20.1
Was ist XML?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Elemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Attribute. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Schemata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Mit XML arbeiten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das Document Object Model . . . . . . . . . . . . . . . . . . . . . . . . . . . Reader und Writer. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . XML-Code lesen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . XML-Code schreiben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zusammenfassung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Übung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
640 645 645 647 649 650 653 655 660 663 663 664 664 664
Mit Visual Basic .NET Webdienste erstellen . . . . . . . . . . . . . . . . . . . . 21.1 Was ist ein Webdienst? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21.2 Das Simple Object Access Protocol . . . . . . . . . . . . . . . . . . . . . . . Das Protokoll . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Web Service Description Language (WSDL). . . . . . . . . . . . Discovery . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21.3 Einen einfachen Webdienst erstellen . . . . . . . . . . . . . . . . . . . . . . Das Projekt einrichten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
665 666 668 668 669 672 674 675
20.2
20.3 20.4 20.5
Tag 21
15
Inhaltsverzeichnis
21.4
21.5
21.6 21.7 21.8
Den Code hinzufügen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Den Webdienst kompilieren. . . . . . . . . . . . . . . . . . . . . . . . . . . . . Einen Webclient erstellen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das Projekt erstellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Den Code hinzufügen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein komplexerer Webdienst . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Den Dienst erstellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Den Webdienst prüfen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Den Client erstellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Den Code hinzufügen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zusammenfassung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Übung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
678 679 681 682 683 686 687 691 691 695 699 700 701 701 701
Rückblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 703 Anhang A
Quiz-Anworten und Beispiellösungen für die Übungen. . . . . . . . . . . 705
Anhang B
Auf der Buch-CD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 745 Stichwortverzeichnis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 747
16
Über die Autoren Duncan Mackenzie ist MCSD, MCSE und MCT, und arbeitet in Redmond, Washington, für Microsofts MSDN-Gruppe (http://msdn.microsoft.com). Er ist ein begeisterter Visual Basic .NET-Programmierer mit der lästigen Angewohnheit, gelegentlich einen Artikel zu schreiben. Außerdem hat er Visual Basic-Schulungen abgehalten und viele VB-Programmierkurse für Fortgeschrittene und Profis gegeben. Duncan Mackenzie hat eine Reihe von Büchern über Microsoft-Technologien geschrieben oder daran mitgearbeitet, unter anderem die Platinum Edition Using Visual Basic 6.0 und die Word 2000 VBA Programmer's Reference. Häufig hält er Vorträge auf Konferenzen, die sich mit der MicrosoftEntwicklung befassen. Kent Sharkey ist MCSD, MCSE, MCT und MCP+SB. Derzeit arbeitet er als Technical Evangelist in Microsofts .NET Solutions Group, in der er für das .NET-Framework und Visual Studio .NET zuständig ist. Bevor er zu Microsoft kam, arbeitete er jahrelang als Ausbilder und Consultant, wobei er sich auf den Entwurf und das Schreiben von n-SchichtAnwendungen mit Visual Basic konzentrierte. Er hat mehrere Bücher über Visual Basic geschrieben bzw. daran mitgearbeitet, unter anderem MCSD Fast Track: Visual Basic Exam 70 -175; MCSD Fast Track: Visual Basic Exam 70 -176; MCSD Fast Track: 4 in 1; und Beginning Visual Basic 6.0 Application Development. Er hält regelmäßig Vorträge auf Entwicklertagungen, die sich mit der Microsoft-Entwicklung befassen.
17
Einleitung Willkommen zu einem interessanten und informativen 21-Tage-Kurs über Visual Basic .NET, der neuesten Version der weltweit beliebtesten Programmiersprache Basic. Dieses Buch soll Sie in das .NET-Framework und Visual Basic .NET einführen und die Grundlagen vermitteln, damit Sie möglichst bald eigene Anwendungen programmieren können.
Überblick Das .NET-Framework beinhaltet die zentralen Konzepte und Verfahren, die Microsofts neuester Gruppe von Entwicklungswerkzeugen zugrunde liegen. Es ist der Grundstein, auf dem die nächste Generation von Servern, Anwendungen und webbasierten Diensten in aller Welt aufbauen wird. Da man .NET natürlich nicht in einer Stunde, aber auch nicht in 21 Tagen restlos erklären kann, wird sich dieses Buch auf bestimmte Aspekte davon konzentrieren. Sie sollen damit möglichst rasch in die Lage versetzt werden, konkrete Anwendungen zu programmieren. Wir werden Ihnen die Kernelemente von Visual Basic .NET vorstellen und alles erläutern, was Sie für den Anfang über das .NET-Framework wissen müssen, damit Sie direkt mit dem Programmieren beginnen und während der Programmierarbeit weiter lernen können. Einen wesentlichen Aspekt von .NET sollten Sie sich gleich zu Beginn klar machen: Es ist nun nicht mehr so wichtig wie in der Vergangenheit, welche Programmiersprache Sie verwenden. Das Framework (die zugrunde liegenden Verfahren, auf denen der .NET-Komplex basiert) ist die eigentliche .NET-Plattform, auf die Sie mit allen .NET-Sprachen (Visual Basic, C#, C++ und anderen) gleichermaßen zugreifen können. Dies ist sowohl für die Sprache Visual Basic selbst als auch für Visual Basic-Entwickler eine gute Neuigkeit. Visual Basic ist nun nicht mehr eine Programmiersprache zweiter Klasse, bei der bestimmte komplexere Funktionsmerkmale des Betriebssystems nur Programmierern zur Verfügung stehen, die C++ verwenden. Sie können also mit Visual Basic jetzt jedes nur denkbare System erstellen. Diese Programmiersprachenunabhängigkeit bedeutet auch, dass Sie selbst entscheiden können, in welcher Sprache Sie (oder Ihr Unternehmen) programmieren möchten. Wenn sie die Wahl haben, werden sich viele Programmierer – genau wie die Autoren dieses Buches – für Visual Basic .NET entscheiden. Denn schließlich wurde Visual Basic trotz der Beschränkungen der früheren Version zur beliebtesten Programmiersprache der Welt,
19
Einleitung
da seine Syntax und sein Stil so leicht zu verwenden sind. Mit .NET ist Visual Basic nun für jedes Projekt eines jeden Unternehmens geeignet. Auch wenn Sie mehr als nur Visual Basic .NET lernen möchten – C# ist zum Beispiel wirklich interessant –, sind Sie mit diesem Buch gut beraten. Es wird Sie in die Verwendung des .NET-Frameworks einführen und dieses Wissen lässt sich leicht auf andere .NET-Sprachen übertragen. Das vorliegende Buch ist als eine Folge von Lektionen entworfen, die sich jeweils auf einen wichtigen Aspekt der Anwendungserstellung (z.B. den Datenbankzugriff) oder ein Thema konzentrieren, mit dem Sie vertraut sein müssen, damit Sie erfolgreich programmieren können. Sie können diese Lektionen zwar in beliebiger Reihenfolge durcharbeiten, aber wenn Sie noch wenig Programmiererfahrung haben, ist es sinnvoller, das Buch erst einmal von Anfang an zu lesen. Die meisten Lektionen – selbst die erste – enthalten einigen Beispielcode und Übungen, bei denen Sie selbst Code schreiben müssen. Damit Sie den größtmöglichen Nutzen aus diesem Buch ziehen, sollten Sie alle Beispiele selbst ausprobieren und alle Übungen bearbeiten. Nichts kann Sie so schnell mit diesen Themen vertraut machen wie eigenes Programmieren.
Für wen ist dieses Buch gedacht? Das eigentliche Zielpublikum dieses Buches sind Personen, die mit dem Programmieren im Allgemeinen und mit Visual Basic .NET im Besonderen noch keine oder nur wenig Erfahrung haben. Es kann aber auch für andere Leser nützlich sein. Wenn Sie bereits mit einer der bisherigen Versionen von Visual Basic programmiert haben, dann werden Sie in diesem Buch einige gute Beispiele und Erklärungen zu allen neuen Eigenschaften der aktuellen Programmversion finden. Sie werden feststellen, dass sich Visual Basic .NET in vielen Punkten von Visual Basic 6.0 unterscheidet, und die ausführlichen Erläuterungen in diesem Buch werden Ihnen den Übergang erleichtern. Falls Sie zwar ein erfahrener Programmierer, mit Visual Basic bisher aber nicht vertraut sind, können Sie vielleicht große Teile der ersten Lektionen überspringen. Unabhängig von Ihrer Programmiererfahrung sollten Sie jedoch die Einführung (Tag 1) lesen, damit Sie das Gesamtkonzept von .NET verstehen, und auch das Kapitel über die IDE (Tag 2) nicht auslassen, sodass Sie mit Visual Studio .NET schneller arbeiten können. Den Rest des Buches können Sie dann so lesen, wie es Ihnen behagt: Eventuell können Sie einige Abschnitte zu allgemeinen Programmierkonzepten nur kurz überfliegen und sich dafür auf die Tage konzentrieren, die erklären, wie Sie mit Visual Basic .NET komplexere Aufgaben – wie z.B. die Erzeugung von Objekten, den Datenbankzugriff und die Erstellung von Internetanwendungen – ausführen können.
20
Einleitung
Was Sie hier lernen werden Unser Ziel besteht darin, Sie in 21 Tagen dahin zu bringen, dass Sie viele einfache Visual Basic .NET-Anwendungen selbst schreiben und in einem Team mitarbeiten können, das eine große Windows- oder Webanwendung erstellt. Dieses Buch erklärt, wie Sie mit Visual Basic .NET viele verschiedene Arten von Anwendungen wie z.B. Client/Server und Webanwendungen erstellen. Außerdem werden Sie viel über das .NET-Framework und einige .NET-Server – wie etwa SQL Server und Internet Information Services – erfahren. Im Bereich des Anwendungsentwurfs und der Anwendungsarchitektur werden Sie die objektorientierte Seite von Visual Basic .NET kennen lernen, also die Erstellung von Klassen und die Verwendung von Objekten, die Grundlagen der Vererbung, des Überschreibens und Überladens sowie andere komplexe Merkmale der Objektorientierung. Hinsichtlich der Ausgabe- oder Schnittstellenebene der Anwendungsentwicklung werden Sie erfahren, wie Sie mit den neuen Formularmöglichkeiten von Visual Basic .NET »Windows«-Anwendungen erstellen und wie Sie mit Web Forms Schnittstellen anlegen, die auf Webseiten beruhen. Außerdem werden Sie lernen, wie man einen einfachen Webdienst einrichtet und dem Internet zur Verfügung stellt, sodass man ihn mit jeder Programmiersprache und jeder Plattform verwenden kann, die auf das Internet zugreifen kann und XML versteht.
Was Sie hier nicht lernen werden Weiter oben haben wir zwar von Programmiersprachenunabhängigkeit und der Bedeutung der zugrunde liegenden Verfahren gesprochen. Dieses Buch ist aber in erster Linie ein Buch über Visual Basic .NET. Die einzige .NET-Sprache, mit der wir uns hier befassen, ist daher Visual Basic. Wir werden zwar kurz auf die Verwendung von Datenbanken wie z.B. SQL Server eingehen, aber vollständige Informationen darüber, wie Sie einen Datenbankserver einrichten und verwalten, finden Sie in entsprechenden Büchern. Wir erheben nicht den Anspruch, in diesem Buch alles zu besprechen, was mit Visual Basic .NET zu tun hat. Visual Basic .NET ist eine Programmiersprache von großen Ausmaßen und mit zahlreichen Funktionsmerkmalen. Es wäre kein Problem, ein Buch einfach mit einer Auflistung der Syntax dieser Sprache zu füllen. Wir wollen Visual Basic .NET hingegen so ausführlich besprechen, dass Sie es nutzbringend einsetzen können und genug darüber wissen, um eigene Anwendungen zu entwerfen, die die neuen Merkmale von .NET verwenden.
21
Einleitung
Anforderungen Die jeweils aktuellsten Informationen über die Systemanforderungen finden Sie unter der Adresse http://msdn.microsoft.com/vstudio. In diesem Abschnitt möchten wir Ihnen jedoch einige allgemeine Hinweise dazu geben, was Sie benötigen werden. Die erste Anforderung ist natürlich ein System, auf dem Visual Studio .NET ausgeführt werden kann. Ein solches System muss die folgenden Grundvoraussetzungen erfüllen: 쐽
Betriebssystem: Windows XP Professional, Windows 2000 (Datacenter Server, Advanced Server, Server oder Professional) oder Windows NT 2.0 Server. Die Installation von Visual Studio kümmert sich auch um die Installation aller Dinge, die für Visual Studio .NET erforderlich sind: die benötigten Service-Pakete, die aktualisierten Dateien für den Datenzugriff und den Internet Explorer 6.
쐽
Hardware: Pentium II 450 Mhz oder einen gleichwertigen Prozessor, 128 MB RAM, eine Videokarte, die die Auflösung 800x600 und 256 Farben unterstützt, und mindestens 1 GB freier Festplattenspeicher. Für die Installation benötigen Sie ein CD ROMLaufwerk, wobei Sie die CDs allerdings auch über eine LAN-Verbindung von einem anderen Rechner aus installieren können.
Für die Beispiele in diesem Buch benötigen Sie nicht nur Visual Studio, sondern auch Zugriff auf einen Webserver, der entweder mit dem Rechner identisch ist, auf dem Visual Studio läuft, oder auf dem das .NET Framework SDK installiert ist. Am besten ist es, wenn auf dem Hauptentwicklungsrechner ein Webserver läuft, wie z.B. mit Windows 2000 oder Windows NT. Eine wichtige Anforderung für die Beispiele in diesem Buch ist auch die Möglichkeit, auf eine Datenbank zuzugreifen, vorzugsweise auf SQL Server 7.0 oder 2000. Wenn Sie den SQL Server nicht haben, können Sie statt dessen die Microsoft Data Engine (MSDE) verwenden, die ähnlich wie die Vollversion von SQL Server funktioniert. Falls Sie nur über eine Access-Datenbank verfügen, wird auch diese ausreichen. Allerdings müssen Sie dann einige der Beispiele in den Lektionen über den Datenbankzugriff so abändern, dass sie auch ohne SQL Server funktionieren.
Was Sie bereits können müssen Obwohl dieses Buch für Programmieranfänger gedacht ist, gehen wir dennoch von einigen Grundkenntnissen aus. Sie sollten mit der Verwendung von Windows-Rechnern und mit dem Betriebssystem vertraut sein, unter dem Sie .NET ausführen. Außerdem sollten Sie wissen, wie Sie Dateien kopieren, ausgeben und in Notepad öffnen, sowie die einfachen Arten der Textbearbeitung kennen – beispielsweise das Ausschneiden, Kopieren und Ein-
22
Einleitung
fügen von Abschnitten. Darauf werden wir in den Lektionen nicht eingehen. Auch wie Sie Ihren Rechner mit dem Internet verbinden und Websites aufrufen, sollten Sie bereits wissen. Abgesehen von diesen Grundkenntnissen über Computer erwarten wir jedoch keine Vorkenntnisse; Sie brauchen kein Programmierer zu sein und müssen auch nicht wissen, wie man Datenbanken erstellt. Das, was Sie auf diesen Gebieten bereits wissen, wird Ihnen wahrscheinlich nützlich sein, aber unsere Beispiele und Erklärungen sind so entworfen, dass Sie auch für jemanden verständlich sind, der noch niemals Code geschrieben hat.
Die Kapitel 21 Tage sind eine lange Zeit. Wir haben die Lektionen daher nicht nur auf einzelne Tage verteilt, sondern das Buch außerdem noch in Wochenabschnitte aufgegliedert. In der ersten Woche werden wir uns darauf konzentrieren, Sie in die allgemeinen Konzepte von .NET und des Programmierens einzuführen, und uns mit der grundlegenden Syntax und den Verfahren befassen, mit denen Sie in Visual Basic .NET Programme erstellen. In der zweiten Woche gehen wir ausführlich auf das .NET-Framework ein, damit Sie diese wesentliche Grundlage ihrer gesamten Programmierarbeit gründlich verstehen. Außerdem besprechen wir die grundlegenden Aspekte der Erstellung von Programmen: das Einrichten einer Benutzeroberfläche (mit Windows Forms und Web Forms) und die Arbeit mit Datenbanken. Die letzte Woche führt Sie in kompliziertere Themen der .NET-Programmierung ein. Dazu gehören das Erzeugen von Objekten, die Bereitstellung einer Anwendung auf anderen Rechnern sowie die Arbeit mit XML und Webdiensten. Wie bereits erwähnt, sollten Sie versuchen, diese Lektionen der Reihe nach durchzuarbeiten. Sie können aber auch jederzeit zu einem anderen Thema springen, das Sie unbedingt sofort nachlesen möchten.
Die Website Zur amerikanischen Ausgabe dieses Buchs gibt es eine Website, die Sie unter der Adresse http://www.samspublishing.com finden. Navigieren Sie von dort zum Buch Sams Teach Yourself Visual Basic .NET in 21 Days, beispielsweise, indem Sie nach der Buchnummer 0672320665 oder nach den Namen eines der Autoren suchen lassen. Auf dieser Website steht unter Downloads der gesamte Code aus diesem Buch zum Herunterladen zur Verfügung. Außerdem finden Sie dort ergänzende Links und weiterführendes Material, das Ihnen unseres Erachtens dabei helfen kann, Visual Basic .NET besser zu verstehen. Natürlich müssen Sie die Website nicht besuchen, um das Buch und die Beispiele zu verstehen, aber
23
Einleitung
bei den längeren Beispielen können Sie sich einige Tipparbeit ersparen, wenn Sie die Codedateien aus dem Internet herunterladen. Laden Sie diese Datei auf Ihren Rechner herunter und extrahieren sie sie in ein Verzeichnis auf Ihrem Rechner. (Sie benötigen dafür WinZip, das Sie sich unter http://www.winzip.com besorgen können.) Damit erstellen Sie in dem gewählten Verzeichnis die Unterverzeichnisse \Day01, \Day02 bis \Day21, in denen Sie die in den einzelnen Kapiteln verwendeten Codedateien finden. Auf dieser Website finden Sie auch mehrere Bonusprojekte. Diese führen Sie nicht nur Schritt für Schritt durch die Erstellung eines vollständigen und funktionierenden Programms, sondern veranschaulichen auch die Verwendung der Bedingungen, Schleifen, Arrays, Variablen und sogar der Verfahren der Fehlerbehandlung, die wir in den einzelnen Lektionen besprechen werden. Lesen Sie das Bonusmaterial, erstellen Sie das Projekt und experimentieren Sie mit dem Ergebnis. Versuchen Sie dann, den Code so zu verändern, dass das Programm anders funktioniert; erstellen Sie also Ihre eigene Variante des Programms.
Die CD Der gesamte Code steht Ihnen auch auf der Begleit-CD zu diesem Buch im Ordner \Beispiele zur Verfügung. Die Dateien zu den einzelnen Kapiteln finden Sie in den Unterordnern \Kap01, \Kap02 bis \Kap21. Die zusätzliche Datei code.zip enthält diese Codedateien nochmals in komprimierter Form. Im Ordner /Bonus sind die bei der Drucklegung des Buches schon verfügbaren Bonusprojekte zusammengestellt. Außerdem finden Sie auf dieser CD einige zusätzliche Programme zur Verwendung mit Visual Basic .NET sowie eine Sammlung zusätzlicher Programme aus anderen wichtigen Bereichen der Software. In einigen Fällen können Sie die Software für eine vom jeweiligen Autor bestimmte Zeit benutzen. Nach Ablauf der Testzeit müssen Sie die Software beim Autor des Programms registrieren lassen oder von Ihrer Festplatte entfernen. Die Programme sind in der Regel sehr preisgünstig und Sie sollten die Arbeit der Autoren honorieren.
In diesem Buch verwendete Konventionen Dieses Buch verwendet einige Konventionen, die Ihnen dabei helfen sollen, die Informationen richtig einzuordnen.
24
Einleitung
Die Hinweise bieten nützliche Zusatzinformationen, die Sie entweder gleich lesen oder zu denen Sie später zurückkommen können, falls Sie nicht vom Thema abschweifen möchten. Die Tipps liefern Informationen, die Ihre Programmierarbeit mit VB effizienter gestalten können.
Warnungen lenken Ihre Aufmerksamkeit auf Probleme oder Nebenwirkungen, die in besonderen Situationen auftreten können.
Das Symbol Neuer Begriff weist auf die Stellen hin, an denen ein neuer Terminus zum ersten Mal verwendet und definiert wird. Die entsprechenden Begriffe sind zusätzlich typographisch hervorgehoben. Das Symbol Eingabe kennzeichnet bei Codebeispielen die Codeabschnitte, die der Anwender eingeben muss.
Das Symbol Analyse weist auf eine Erläuterung zum Wie und Warum eines Codebeispiels hin. Codebeispiele sind in Courier-Schrift angegeben.
Feedback Wir haben hart gearbeitet, damit dieses Buch Sie auf sinnvolle Art beim Erlernen von .NET unterstützt und ein wertvoller Neuzugang in Ihrer Bibliothek von Entwicklerhandbüchern ist. Wenn Sie der Meinung sind, wir hätten mit einem bestimmten Thema mehr oder weniger Zeit verbringen sollen, sonstige Verbesserungsvorschläge oder Fragen haben, so teilen Sie dies doch bitte, was die deutschsprachige Ausgabe angeht, dem Verlag Markt+Technik und dem Fachlektor mit. Schreiben Sie dazu bitte eine E-Mail an
[email protected]. Wir werden dann versuchen, Ihre Anregungen in zukünftigen Büchern und bei der nächsten Überarbeitung dieses Buches zu berücksichtigen.
25
Tag 1
Willkommen bei Visual Basic .NET
29
Tag 2
Mit Visual Basic .NET arbeiten
59
Tag 3
Einführung in die Programmierung mit Visual Basic .NET
95
Tag 4
Den Programmfluss steuern
127
Tag 5
Anwendungsarchitektur in .NET
165
Tag 6
Was zu tun ist, wenn ein gutes Programm nicht funktioniert, und wie Sie das verhindern
187
Tag 7
Mit Objekten arbeiten
221
Tag 8
Einführung in das .NET-Framework
255
Tag 9
Mit Windows Forms eine Benutzeroberfläche erstellen
279
Tag 10
Mit Web Forms eine Benutzeroberfläche erstellen 319
Tag 11
Einführung in Datenbanken
345
Tag 12
Mit .NET auf Daten zugreifen
375
Tag 13
Der Server-Explorer
423
Tag 14
Einführung in die objektorientierte Programmierung
451
Tag 15
Objekte in Visual Basic .NET erzeugen
479
Tag 16
Windows Forms für Fortgeschrittene
513
Tag 17
Mit dem .NET Framework arbeiten
549
Tag 18
Der letzte Schliff
595
Tag 19
Die Anwendung bereitstellen
619
Tag 20
Einführung in XML
639
Tag 21
Mit Visual Basic .NET Webdienste erstellen
665
W O C H E
W O C H E
W O C H E
Überblick In dieser Woche werden wir die folgenden wichtigen Themen behandeln: 쐽
.NET, Visual Basic und die Programmierkonzepte (Tag 1)
쐽
Wie Sie Visual Studio .NET einrichten und verwenden, um mit der eigentlichen Programmierung beginnen zu können (Tag 2)
쐽
Die Syntax von Visual Basic und Programmiertechniken für Datentypen, Prozeduren und Variablen (Tag 3), die Steuerung des Programmablaufs mit Schleifen und Bedingungen (Tag 4) sowie die Fehlerbehandlung (Tag 6)
쐽
Wie Sie eine Visual Basic .NET-Lösung organisieren und aufbauen (Tag 5)
쐽
Wichtige Konzepte der objektorientierten Entwicklung, mit denen Sie in diesem Buch arbeiten werden (Tag 7)
Heute beginnt eine wichtige Woche. Sie legt das Fundament, auf dem die ganze übrige Visual Basic .NET-Programmierung aufbauen wird. Der Tag 1 bereitet Sie darauf vor, Visual Basic zu erkunden und zu erlernen. Außerdem werden Ihnen die Grundkonzepte des Programmierens sowie die .NET-Plattform vorgestellt und kurz die Geschichte von Visual Basic beschrieben. Am Tag 2 werden Sie lernen, wie Sie die zahlreichen Eigenschaften der Entwicklungsumgebung von Visual Basic nutzen können, um Projekte zu erstellen. Am Tag 3 und am Tag 4 fangen Sie mit dem Programmieren an: Sie erhalten alle notwendigen Informationen über die Syntax und über Konzepte (Variablen, Schleifen, If-Anweisungen usw.), die in jedem Visual Basic-Programm vorkommen werden, das Sie in Zukunft schreiben. Am Tag 5 stellen wir Ihnen die Projekttypen vor, die Sie in Visual Basic .NET erstellen können, und erklären, wie sich diese verschiedenen Projekttypen in die Gesamtarchitektur eines Systems einfügen. Tag 6 und Tag 7 kehren schließlich wieder zum praktischen Programmieren zurück. In diesen Lektionen erfahren Sie, wie Sie mit Fehlern umgehen und wie Sie in Ihren Programmen Objekte verwenden. In dieser Woche erhalten Sie alle grundlegenden Informationen, die Sie für den Rest des Buches benötigen, und alle die Einzelheiten, auf denen die weiteren Themen aufbauen werden.
28
Willkommen bei Visual Basic .NET
Willkommen bei Visual Basic .NET
Heute werde ich Sie in die Welt der Programmierung mit Visual Basic einführen, indem ich die folgenden Fragen beantworte:
Die Themen heute 쐽
Was ist Programmierung und warum sollten Sie sich damit abgeben?
쐽
Wie passt Visual Basic da hinein?
쐽
Was ist .NET?
Heutzutage sind Computer gang und gäbe und viele Menschen, die ich kenne, arbeiten den ganzen Tag damit. Trotzdem lautet die Frage, die mir am häufigsten gestellt wird: »Was tut ein Computerprogrammierer eigentlich?« Diese Frage wurde mir so häufig gestellt, dass ich in der heutigen Lektion ein wenig darüber sprechen werde, was Programmierung ist und warum Sie sich damit befassen sollten.
1.1
Computerprogrammierung verstehen
Obwohl wir von Computern häufig in Bezug auf ihre Hardware sprechen (man hört oft Kommentare wie »Ich habe einen Pentium III, 600 MHz und 256 MB RAM«), ist Hardware alleine nicht sehr nützlich. Die CPU (Central Processing Unit oder Hauptcomputerchip, wie der Pentium III in meinem Computer), kann z.B. viele wichtige Aufgaben erfüllen, etwa einfache mathematische Berechnungen und das Verschieben von Daten zwischen den verschiedenen Teilen des Systems. Alleine für sich ist sie aber nicht einmal in der Lage, eine Datei von der Festplatte zu lesen. Ein Computerprogramm ist eine Reihe von Anweisungen an alle diese Hardwareelemente, die geschrieben wurden, um etwas zu tun, das diese Elemente alleine nicht bewerkstelligen können. Alle elementaren Operationen im Zusammenhang mit der Verwendung von Festplatten, Speicher, einem Monitor und einem Drucker sind komplex. Ein Programm, das mit den elementaren Operationen interagieren müsste, würde die meiste Zeit nur mit diesen Dingen verbringen und nur ein geringer Prozentsatz der Zeit verbliebe für den tatsächlichen Zweck des Programms. Beispielsweise würde ein Programm, das die Zahlungen für eine Hypothek direkt über die Hardware berechnet, wahrscheinlich hunderte oder tausende Zeilen enthalten, um die Anzeige auf dem Bildschirm und dergleichen zu behandeln, aber nur ein paar Zeilen für die tatsächliche Berechnung. Auf diese Weise wurde in der Vergangenheit programmiert. Das war nicht sehr produktiv, weil so wenig Zeit auf den tatsächlichen Zweck der Anwendung verwendet wurde. Was benötigt wurde, war eine Methode, um all diese Details so zu behandeln, dass Programme sich auf ihre eigentlichen Aufgaben konzentrieren konnten.
30
Computerprogrammierung verstehen
Die Rolle des Betriebssystems Um eine allgemeine Funktionalität zu bieten, die unmittelbar auf der Computerhardware aufsetzt, wurden Betriebssysteme geschaffen. Diese Betriebssysteme sind selbst Computerprogramme, existieren aber (nur), um die ganzen Einzelheiten der Speicherverwaltung, Platteneingabe/-ausgabe (I/O) und andere Aufgaben der unteren Ebene zu handhaben. Wenn ein Betriebssystem auf einem Computer existiert, können andere Programme geschrieben werden, die sich um die Details dieser unteren Ebene nicht mehr kümmern müssen: Wenn diese Programme eine Datei öffnen oder eine Diskette formatieren müssen, können sie das Betriebssystem bitten, diese Funktion für sie durchzuführen. Wenn Sie sich eine grafische Darstellung dazu ansehen (siehe Abbildung 1.1), können Sie sich die Beziehungen zwischen der Computerhardware und dem Betriebssystem oder zwischen dem Betriebssystem und anderen Programmen als mehrere Schichten mit unterschiedlicher Funktionalität vorstellen. Benutzeranwendung (einschließlich Ihrer Programme!)
Betriebssystem Gerätetreiber
Gerätetreiber
Hardwarekomponenten
Abbildung 1.1: Das Betriebssystem wird zur Schnittstelle zwischen der Computerausrüstung und Ihrem Programm, wodurch Sie hardwarespezifischen Code vermeiden.
Oft sind diese Beziehungen nicht so klar definiert. Ein Programm muss vielleicht direkt auf die Hardware zugreifen (ohne über das Betriebssystem zu gehen), um eine Hardwarefunktion zu benutzen, die das Betriebssystem nicht zur Verfügung stellt, oder um die Leistung zu verbessern. Dies war früher bei den ersten PC-Betriebssystemen der Fall, bei denen viele Programme direkt mit der Hardware interagieren mussten. Diese Einschränkungen bedeuteten mehr Arbeit für diejenigen, die Software für den PC schreiben wollten, da jedes Programm seine eigenen Drucker und andere Details handhaben musste. Da spätere Versionen dieser Betriebssysteme eine verbesserte Funktionalität boten, wurde es einfacher, Programme für diese Systeme zu schreiben. Schließlich ersetzte Windows (praktisch) alle diese Systeme. Eine der größten Verbesserungen, die Windows bot, bestand darin, dass das Betriebssystem nun wie eine Benutzerschnittstelle zu anderen Programmen funktionierte. Wenn ein Computerprogramm unter Windows beispielsweise ein Dialogfenster auf dem Bildschirm anzeigen muss (wie das in Abbildung 1.2), bittet es das Betriebssystem einfach, ein Dialogfenster anzuzeigen und eine bestimmte Nachricht zu liefern. Um auf die ganze Funktionalität zuzugreifen, die das Betriebssystem bietet, wird eine Reihe von Application Programming Interfaces (APIs) zur Verfügung gestellt. Diese APIs repräsentieren
31
Willkommen bei Visual Basic .NET
die gesamte exponierte Betriebssystemfunktionalität und umgekehrt können wir sie in unseren Programmen verwenden. Abbildung 1.2: Windows behandelt die Anzeige von GUI-Elementen (Graphical User Interface) wie diesem Dialogfenster als Teil der Dienste, die es Programmen zur Verfügung stellt.
Das Ergebnis dieser Verbesserungen ist, dass jedes einzelne Programm einen immer geringeren Teil der generischen Operationen des Computers handhaben muss und sich daher auf seinen wahren Zweck konzentrieren kann. Ein weiterer großer Vorteil des Entfernens von hardwarespezifischem Code aus den Anwendungen ist, dass nur das Betriebssystem bei Veränderungen an der Hardware (neuer Drucker, neue Festplatte, schnellere CPU) aktualisiert werden muss, um das neue Gerät zu nutzen. Die Programme, die auf dem Computer laufen, sollten davon nicht berührt werden. Was bedeutet das für Sie als Visual Basic-Programmierer? Es bedeutet, dass Sie in der Lage sind, Computerprogramme zu schreiben, um bestimmte Aufgaben (wie das Hypothekenbeispiel weiter oben) zu erledigen, ohne etwas darüber wissen zu müssen, wie Windows Bilder auf dem Monitor zeichnet, auf dem Drucker etwas ausdruckt oder Dateien auf der Festplatte speichert.
Die Rolle der Programmiersprachen Nun haben Sie einen Eindruck davon, was das Betriebssystem bietet, aber was ist mit den Programmen selbst – wie werden sie erstellt? Zu Beginn der Lektion habe ich ein Computerprogramm als eine Reihe von Anweisungen für die Computerhardware definiert. Da die Hardware selbst nur zu relativ einfachen Operationen in der Lage ist, müssen auch die Anweisungen einfach sein. Das Endergebnis des Programms ist ein Code, den die Hardware verstehen kann und der meistens Maschinensprache oder nativer Code genannt wird. Die Anweisungen bestehen, nachdem sie vom Betriebssystem in den Speicher geladen wurden, aus Befehlen wie »Verschiebe Speicherinhalt von hier nach dort« oder »Führe mit den Werten eine mathematische Funktion aus«. Tausende dieser Befehle bilden ein vollständiges Programm. Es ist zwar möglich, direkt mit diesem nativen Code Programme zu schreiben, indem auf der Festplatte eine Datei voll mit Anweisungen erstellt wird, aber es würde enorm viel Arbeit bedeuten, auf diese Weise auch nur das einfachste Programm zu erzeugen. Um diese Arbeit zu vermeiden und es den Programmierern zu ermöglichen, sich auf die eigentliche Aufgabe ihrer Programme zu konzentrieren, wurden Programmiersprachen einer höheren Ebene entwickelt. Mit diesen Sprachen können Sie mächtigere und komplexere
32
Computerprogrammierung verstehen
Anweisungen geben, die dann in die vielen entsprechenden Anweisungen der Maschinensprache übersetzt werden. Aus einer einzelnen Zeile einer solchen Sprache könnten leicht zehn Zeilen Maschinensprachenanweisungen werden. Der Prozess des Übersetzens von einer Computersprache der höheren Ebene in Maschinensprache oder nativen Code nennt man Kompilieren. Die Programme, die diese Übersetzung vornehmen, heißen Compiler. Im Laufe der Jahre wurden viele solcher Sprachen entwickelt. FORTRAN, COBOL, APL, Pascal, C und BASIC sind nur ein paar Beispiele, denn es gibt hunderte unterschiedlicher Programmiersprachen. Jede Sprache hat ihre eigenen Befehle und ständig werden neue Befehle geschaffen, um die Programmierarbeit noch mehr zu vereinfachen. So wie sich die Computer mit der Zeit entwickelt haben, so haben sich auch die Sprachen entwickelt, mit denen diese programmiert werden. Dabei wurden neue Funktionen zu bestehenden Sprachen hinzugefügt oder neue Sprachen wie C++ (die, wie Sie vermutlich schon geraten haben, auf der Sprache C basiert) und Java geschaffen. Im Allgemeinen war es das Ziel bei der Verbesserung von Programmiersprachen, die Programmierproduktivität zu erhöhen, um die Programmierer in die Lage zu versetzen, die gewünschten Programme so einfach wie möglich zu erstellen. Es ist aber eine grobe Vereinfachung, zu sagen, dass Programmiersprachen nur reifen, um die Entwicklung schneller zu machen. Die Entwicklungsgeschwindigkeit ist nur eine der Motivationen für Verbesserungen. Andere Ziele und Resultate sind z.B. schnellere oder stabilere Anwendungen (weniger Abstürze) oder auch die Erstellung von Anwendungen, die leichter zu installieren sind. Ursprünglich bestanden die meisten Programmiersprachen nur aus dem Compiler. Man konnte also das Programm selbst mit einem Texteditor – wie Notepad – erstellen, es anschließend durch diesen Compiler laufen lassen und ihm den gewünschten Namen der Quelldatei übergeben. Dieser erzeugte dann das Endergebnis, ein ausführbares Programm, sofern es keine Fehler gab. Der Programmierer führte anschließend das kompilierte Ergebnis aus, testete es auf Fehler und kehrte dann zum Texteditor zurück, um Veränderung am Code vorzunehmen. Der Code wurde erneut kompiliert und der Kreislauf wiederholte sich. Dieser Entwicklungsprozess (siehe Abbildung 1.3) war nicht sprachspezifisch, er war für alle Programmierer normal. Mit der Verbesserung der Programmiersprachen wurde auch dieser Entwicklungszyklus optimiert, was zur Entwicklung von fortgeschritteneren Compilern und dem Konzept der integrierten Entwicklungsumgebung (Integrated Development Environment, IDE) führte. Der Sinn einer IDE ist es, die Arbeitsschritte des Bearbeitens, der Fehlerbehebung und des Kompilierens in der Softwareentwicklung in einer einzelnen Schnittstelle für den Programmierer zusammenzufassen (siehe Abbildung 1.4).
33
Willkommen bei Visual Basic .NET
Code erzeugen/bearbeiten
Fehler finden
Kompilieren
Ausführen
Abbildung 1.3: Compiler verwandeln Quellcode von Sprachen der höheren Ebene in Anweisungen, die der Computer verstehen kann.
Ungeachtet der verschiedenen Schnittstellen ist die eigentliche Technologie des Entwicklungsprozesses immer noch sehr ähnlich: In den meisten Fällen wird der Code nach wie vor kompiliert (nähere Informationen finden Sie im folgenden Hinweis) und der Programmierer schreibt noch immer Textdateien, aber die Arbeitsumgebung ist viel benutzerfreundlicher geworden.
Abbildung 1.4: Visual Studio ist eine IDE, die ein einzelnes Interface für jede beliebige Anzahl von Sprachen zur Verfügung stellt, darunter auch Visual Basic.
Zusätzlich zu kompilierten Sprachen (bei denen der Quellcode vor der Ausführung durch einen Compiler laufen muss) gibt es noch andere Arten von Sprachen, die interpretierte Sprachen genannt werden. In diesen Sprachen ist der Quellcode nicht kompiliert. Statt dessen führt ein spezielles Programm den Code aus, indem es die Quelle liest und den entsprechenden Code ausführt. Im Allgemeinen laufen interpretierte Sprachen langsamer als die, die kompiliert werden, weil bei interpretierten Sprachen das Parsen (das Durchlesen der
34
Computerprogrammierung verstehen
Quelle auf Befehle hin) bei jeder Ausführung des Programms erfolgen muss, während bei kompilierten Sprachen das Parsen nur einmal zur Kompilierzeit auftritt. Visual Basic war einmal eine interpretierte Sprache, was aber heute nicht mehr der Fall ist. Die Erfindung von IDEs stiftete eine gewisse Verwirrung bei der Unterscheidung, ob es sich um eine Funktion der verwendeten Sprache oder eine Funktion der IDE handelt. Das trifft vor allem auf die Sprache Visual Basic zu, für die die IDE viele Funktionen bietet, mit denen ein Programmierer leicht fortgeschrittene Funktionalitäten schaffen kann. Diese Funktionen erzeugen oft Visual Basic-Code und dieser Code macht die eigentliche Arbeit. In diesem Fall hat die IDE die Sprache – und nicht die Funktionalität – hinzugefügt, aber beide werden häufig als ein und dasselbe angesehen. Wenn ich Sie heute in das Konzept von Visual Studio .NET einführe, werden Sie sehen, dass eine einzige IDE viele Sprachen unterstützen kann. Dies kann eine wertvolle Hilfe sein, um die Unterschiede zwischen der IDE und der Programmiersprache zu verstehen. Eine weitere wichtige Entwicklung in der Geschichte der Programmiersprachen war die Erschaffung der »visuellen« Sprachen wie Visual Basic. Diese Sprachen werden visuell genannt, weil sie die Erstellung eines Programms über eine grafische Oberfläche ermöglichen. Die gängigste Funktion einer solchen Sprache ist die Fähigkeit, Schaltflächen, Text und andere Elemente auf einem Bildschirm zu platzieren, um eine Benutzeroberfläche zu erzeugen (siehe Abbildung 1.5). Wiederum wird unter der Oberfläche oft richtiger Code erzeugt, aber der Programmierer kann die grafischen Teile seiner Anwendung viel leichter erstellen. Dazu später mehr.
Abbildung 1.5: Visuelle Entwicklungswerkzeuge ermöglichen es Ihnen, grafische Benutzeroberflächen zu erzeugen und dann den erforderlichen Code zu generieren.
35
Willkommen bei Visual Basic .NET
1.2
Warum Computerprogramme schreiben?
Ich habe bereits erwähnt, dass die häufigste Frage, die mir gestellt wird, lautet: »Was macht eigentlich ein Computerprogrammierer?« Nachdem ich das allgemeine Konzept dieses Berufs erklärt habe, wird mir meist die zweithäufigste Frage gestellt: »Warum macht man das?« Eine Antwort wie »Weil es Spaß macht, Code zu schreiben« scheint die Leute nicht so richtig zu befriedigen. Wenn wir also lernen, wie man Programme schreibt, lohnt es sich, zu überlegen, was wir schreiben und warum wir es schreiben. Zunächst gilt es, bei der Computerprogrammierung die Art der Programme zu verstehen, die wir erstellen werden. Wenn sie nach Beispielen für Anwendungen gefragt werden, nennen die meisten Menschen Programme wie Microsoft Word oder Excel; ein paar werden noch Computerspiele erwähnen und einige wenige zählen Windows selbst in der Liste auf. All dies sind definitiv Computeranwendungen und jemand muss sie programmieren. Aber die größte Programmkategorie ist eine, die fast nie erwähnt wird. Die gängigsten Softwareprogramme sind die Systeme, die wir die ganze Zeit sehen. Wenn Sie nicht gerade an der Programmierung interessiert (oder davon besessen) sind, denken Sie nicht oft über die Monate oder Jahre an Programmierzeit nach, die dafür aufgewendet werden. Diese Programme, wie z.B. das Ausleihsystem Ihrer Videothek oder das Programm, mit dem die Behörden Führerscheindaten speichern, wurden entweder für einen einzelnen Kunden oder eine kleine Marktnische entwickelt. Bis auf ihre Klientel unterscheiden sich solche Programme nicht von Standardsoftware wie Microsoft Word. Ihre individuelle Natur bedeutet aber, dass pro Kunde sehr viel mehr Programmierarbeit anfällt. Die Rolle des Programmierers deckt viele unterschiedliche Aspekte der Softwareentwicklung ab, besonders in der Welt der kundenspezifischen Software. Aber der Lebenszyklus der Softwareentwicklung (Software Development Life Cycle, SLDC) umfasst auch verschiedene andere wichtige Schritte (siehe Abbildung 1.6).
Anforderungen
Bug-Fixes & Pflege
Bereitstellung
36
Entwurf
Implementierung
Abbildung 1.6: Der Lebenszyklus eines Softwareentwicklungsprojekts umfasst verschiedene unterschiedliche Stadien.
Eine kurze Geschichte von Visual Basic
Bevor das Codieren beginnt, muss klar sein, welche Anforderungen das System erfüllen soll. Diese Informationen bilden einen allgemeinen Plan für die Computeranwendung, den Entwurf. Diese Vorlage, im Grunde eine Übersetzung der Bedürfnisse des Kunden in die Teile eines Computersystems, wird zur Richtlinie, an der sich der Programmierer orientiert. Das gewünschte Ergebnis ist ein Computersystem, das den Bedürfnissen entspricht, die der Anwender ursprünglich festgelegt hat. Das vollständige System muss dann für die Anwender eingesetzt, gepflegt und verbessert werden. Bugs – Probleme im System – müssen behoben und neue Funktionen hinzugefügt werden. Dieser Zyklus bestimmt das Berufsleben und die Arbeit des Softwareentwicklers. Aber für die meisten Programmierer kann die Frage, warum sie programmieren, mit einfachen Worten beantwortet werden: Sie können Code schreiben, der etwas Nützliches bewirkt. Der eigentliche Sinn der Programmierung ist es, die Computerhardware nützliche Dinge tun zu lassen, und das motiviert auch die meisten Softwareentwickler. Mein Co-Autor und ich haben im Verlauf dieses Buchs dieses Konzept im Hinterkopf behalten und wir werden sicherstellen, dass die Programme, die wir Ihnen zeigen, zusätzlich zu der Lernerfahrung eine nützliche Funktion erfüllen. Das Ziel der Entwicklung von nützlichen Anwendungen bringt viele Menschen zur Sprache Visual Basic, da diese Sprache von vielen als schnellster Weg von der Idee zur Anwendung angesehen wird.
1.3
Eine kurze Geschichte von Visual Basic
Die Geschichte von Visual Basic beginnt eigentlich mit der Erfindung von BASIC (Beginner's All-purpose Symbolic Instruction Code) im Jahre 1964, einer Sprache, die leicht zu lernen ist und von Programmieranfängern verwendet wird. Diese Sprache fand starken Anklang und in den folgenden 15 Jahren entwickelten viele Personen und Unternehmen Compiler und Interpreter für BASIC. Im Jahre 1975, als Microsoft noch in den Kinderschuhen steckte, war eines der ersten erfolgreichen Produkte dieser Firma eine BASIC-Version. Microsoft BASIC und sein Nachfolger, Quick BASIC (oder QBASIC, wie es auch genannt wird), wurden zu den am weitesten verbreiteten BASIC-Versionen für den PC. Sie haben heute immer noch eine recht große Anhängerschar (wenn Sie daran interessiert sind, finden Sie unter http:// www.qbasic.com Ressourcen und Links zu dieser Sprache). Quick BASIC stand für Windows bei dessen Veröffentlichung zur Verfügung, aber es erforderte recht viel Mühe, eine Oberfläche im Windows-Stil zu codieren, sodass Quick BASIC für die Programmierung in dieser Umgebung nicht besonders geeignet war. Microsoft brachte aber ein neues Produkt heraus, das die beliebte und leicht zu erlernende Sprache BASIC mit einer Entwicklungsumgebung kombinierte, die es den Programmierern ermöglichte, die Benutzeroberfläche für ein Programm grafisch zu erzeugen. Dieses
37
Willkommen bei Visual Basic .NET
Produkt war, wie Sie wahrscheinlich schon erraten haben, Visual Basic 1.0. Es fand zuerst wenig Anklang, aber es bot eine schnelle Umgebung für die Entwicklung einer grafischen Benutzeroberfläche (Graphical User Interface, GUI). Was Sie vielleicht überrascht, ist, dass Visual Basic ursprünglich mit einer DOS-Version startete, obwohl es schnell in Windows überging (siehe Abbildung 1.7).
Abbildung 1.7: Die erste Version von Visual Basic für Windows bot viele der Schlüsselfunktionen, die bei vielen aktuellen Entwicklungswerkzeugen gängig sind. Die wichtigste Funktion ist sein visuelles Drag&Drop-Formular für die Entwicklung.
Die Popularität von Visual Basic wuchs mit der Zeit. Eine einzelne Funktion erwies sich als entscheidend für seinen Erfolg: Die Firma Microsoft, die einzelnen Programmierer und andere konnten damit kundenspezifische Schnittstellenkomponenten erstellen, die zu den Programmen hinzugefügt wurden. Schnell traten Unternehmen auf den Plan, die Komponenten erstellten und damit eine Vielfalt von Funktionen zu Visual Basic-Anwendungen hinzufügten, z.B. das Erstellen von Diagrammen, das Bearbeiten von Bildern, Modemverbindungen und noch viele andere Funktionen. Diese Komponenten ermöglichten es den Visual Basic-Programmierern, mächtigere Anwendungen zu erstellen, indem sie die verschiedenen Komponenten mit ihrem eigenen Code kombinierten. Dies erhöhte die Entwicklungsgeschwindigkeit mit Visual Basic ungemein und machte es zu einer der beliebtesten Programmiersprachen. Jede nachfolgende Version fügte neue Funktionen hinzu, wodurch Visual Basic mehr und mehr zu einem voll ausgereiften Entwicklungswerkzeug wurde. Eine besonders wichtige Veränderung betraf die Version 5 im Jahre 1997. In den Vorläuferversionen war Visual Basic eine interpretierte Sprache und daher im Vergleich zu Visual C++, Delphi und anderen kompilierten Sprachen recht langsam. Visual Basic 5.0 machte es möglich, entweder kompilierte oder interpretierte Programmversionen zu erstellen, und die Leistung verbesserte sich deutlich.
38
Eine kurze Geschichte von Visual Basic
Eine weitere wichtige Veränderung in Visual Basic war die Fähigkeit, Komponenten zu erzeugen. Bei der Programmierung findet man oft ein Codesegment – wie etwa die oben erwähnte Hypothekenberechnungsroutine –, das an verschiedenen Stellen in Ihrem Programm und möglicherweise sogar in vielen verschiedenen Programmen verwendet werden könnte. Die gemeinsame Nutzung dieses Codes innerhalb eines Programms wird im Allgemeinen dadurch erreicht, dass er als Prozedur geschrieben wird: als ein Codeblock, der nur einmal in das Programm eingegeben wird, aber von jeder Stelle in der Anwendung aufgerufen werden kann. Eine gemeinsame Nutzung über mehrere Programme hinweg kann erreicht werden, indem der Code der Prozedur einfach in jede neue Anwendung, die Sie erstellen, kopiert wird, aber das führt zu einem Problem. Immer wenn Sie oder jemand anderes eine Veränderung an diesem Codeblock vornehmen (um einen Bug zu beheben oder die Arbeitsweise des Codes zu verbessern), müssen Sie den Code wieder in jede einzelne Anwendung kopieren, die ihn benutzt. Eine bessere Methode für die gemeinsame Nutzung von Code ist das Erzeugen einer Codebibliothek, die getrennt vom verwendenden Programm in einer Datei gespeichert wird und unabhängig von diesem verändert werden kann. Solch eine Bibliothek wird Komponente genannt und häufig in der Form einer .dll-Datei erzeugt. Die Verwendung einer Bibliothek ist die bevorzugte Methode, Code gemeinsam zu nutzen. Als Visual Basic neue Versionen entwickelte, wurde seine Fähigkeit, diese Komponenten zu erzeugen und zu verwenden, stetig verbessert. Version 4.0 (veröffentlicht 1996) war die erste Version, die die Erzeugung von Komponenten ermöglichte, was mittlerweile eine extrem wichtige Funktion in Entwicklungsprojekten ist. Wir werden Komponenten ausführlicher am Tag 14 unter dem Thema »Einführung in die objektorientierte Programmierung« erörtern. Zu Visual Basic wurden zwar viele zusätzliche Funktionen hinzugefügt, aber alles wurde auf der bestehenden Grundlage aufgebaut. Das ist nicht ungewöhnlich. Die meisten Entwicklungswerkzeuge gehen diesen Weg, der aber den ungünstigen Nebeneffekt hat, dass sich Müll anhäuft. Neue Versionen eines Tools versuchen, mit den ganzen Unvollkommenheiten der früheren Versionen kompatibel zu bleiben. Eine Sprache von Grund auf neu zu schreiben, ist fast undenkbar. Die erforderliche Arbeit wäre ernorm und der Anwender sieht die fehlende Kompatibilität zum bereits bestehenden Code garantiert nicht gern. Der Vorteil eines solchen Schrittes wäre natürlich eine vollständig reine und neue Implementierung, die das Gute behalten und die schlechten Teile der bestehenden Sprache für immer über Bord werfen könnte. Das ist genau das, was Microsoft mit dem Schritt von Visual Basic 6.0 zu Visual Basic .NET getan hat. Es hat die Sprache neu geschrieben, um eine saubere Version zu schaffen, die den Müll beseitigt, der sich im Lauf eines Jahrzehnts der Sprachverbesserung angesammelt hat. Das bedeutet viel Neues für diejenigen, die in den Vorläuferversionen der Sprache geübt waren, aber das Endergebnis lohnt die Mühe. Diese radikale Veränderung führt dazu, dass jetzt der richtige Zeitpunkt ist, um mit Visual Basic anzufangen. Das Konzept, das in diesem Buch gelehrt wird, wird viel länger gültig bleiben als das Material, das sich mit einer früheren Version von Visual Basic beschäftigt.
39
Willkommen bei Visual Basic .NET
Die Entwicklung von Visual Basic 6.0 zu Visual Basic .NET hat viele Vorteile. Der bedeutendste Ansporn dafür war das Bedürfnis, sich der neuen .NET-Umgebung anzupassen. Im Verlauf der heutigen Lektion werden Sie mehr über .NET lernen: was es ist und wie Visual Basic ins Bild passt.
1.4
Was ist .NET?
Auf den ersten Blick scheint .NET nur ein Marketingkonzept zu sein, eine Möglichkeit, eine weitere Versionsnummer für Visual Basic zu vermeiden, aber es ist mehr als das. .NET stellt eine ganze Palette von Technologien und Konzepten dar, die eine Plattform bilden, auf der Sie Anwendungen entwickeln können. Visual Basic .NET hat auch eine richtige Versionsnummer, nämlich 7.0 – die Nummer wird nur nicht häufig verwendet. So wie Windows 2000 eigentlich Windows NT Version 5.0 ist, wird der einfachere oder eingängigere Name im Allgemeinen der sein, der häufiger verwendet wird. Erwarten Sie aber nicht, häufig von Visual Basic 7.0 zu hören. Es gab bei Microsoft sogar eine Geldstrafe dafür, von Windows 2000 als NT 5.0 zu sprechen (zwar nur 25 Cents, aber das summierte sich!). Zu Beginn der heutigen Lektion habe ich erklärt, wie das Betriebssystem für Anwendungen eine Ebene der Grundfunktionalität bietet – wie die Fähigkeit, Dateien von der Festplatte oder der Diskette zu lesen. .NET kann man sich ähnlich vorstellen: Es ist eine Schicht, die unter Ihren Programmen existiert und eine Reihe von Grunddiensten und -funktionen bietet. Diese Schicht umfasst eine Reihe von Anwendungen und Betriebssystemen, die .NETServer genannt werden, eine Basismenge von Objekten, die .NET-Framework heißen, und eine Reihe von Diensten, die alle .NET-Sprachen unterstützen und Common Language Runtime (CLR) genannt werden. Jeder dieser Teile von .NET (siehe Abbildung 1.8) wird anschließend einzeln abgehandelt.
.NET-Server Ein Hauptziel des .NET-Konzepts besteht darin, Ihnen das Erstellen von verteilten Systemen zu ersparen, in denen die Arbeit an mehreren verschiedenen Orten durchgeführt wird. Zum größten Teil machen diese Systeme ihre Arbeit im Back-End, also auf der Server-Ebene. Microsoft bietet eine Reihe von Softwareprodukten, die zusammen als .NET Enterprise Servers bekannt sind. Sie sollen die Back-End-Funktionen liefern, die von einem verteilten System benötigt werden. Zu diesen Produkten gehören
40
Was ist .NET?
쐽
das Server-Betriebssystem Microsoft Windows (Server, Advanced Server und Datacenter Server),
쐽
Software für Clustering und Lastenverteilung wie z.B. Microsoft App Center und Microsoft Cluster Server,
쐽
ein Datenbankserver namens Microsoft SQL Server,
쐽
Microsoft Exchange Server als Systemlösung für E-Mail, Kooperation und formlose Informationsspeicherung,
쐽
eine Daten-Transformations-Engine namens Microsoft BizTalk Server, die auf XML basiert (mehr zu XML lernen Sie an Tag 20),
쐽
ein Server namens Host Integration Server für den Zugriff auf Legacy-Systeme wie z.B. AS/400s
쐽
und noch Einiges mehr ... Benutzer-Anwendung (einschließlich Ihrer Programme!)
.NET-Framework
.NET-Server Windows, BizTalk, Exchange, SQL, App Center… .NET-Geräte
Hardwarekompomenten
Abbildung 1.8: .NET ist mehr als nur eine einzige Sache. Es ist eine Sammlung von Software und Konzepten, die zusammenarbeiten, um die Realisierung von Geschäftslösungen zu ermöglichen.
Zusammen liefern diese Server die Basisdienste für Ihre .NET-Anwendungen und bilden die Grundlage Ihres Systems. Dieses Buch wird sich von Zeit zu Zeit auf diese Server beziehen und erklären, wo sie in Ihre Systeme hineinpassen können.
.NET-Framework Bei der Entwicklung hin zu Visual Basic .NET haben sich viele Dinge radikal verändert. Eines davon ist die Entwicklung eines neuen Rahmens für alle .NET-Entwicklungswerkzeuge. Dieser Rahmen, bekannt als .NET-Framework, bietet zwei wichtige Dinge: die Basis-Laufzeitumgebung und eine Reihe von Foundation Classes. Die Laufzeitumgebung ähnelt dem Betriebssystem insofern, als sie eine Schicht zwischen Ihrem Programm und dem komplexen Rest des Systems bildet, Dienste für Ihre Anwen-
41
Willkommen bei Visual Basic .NET
dung ausführt und den Zugriff auf die Funktionalität der unteren Schichten vereinfacht. Die Foundation Classes bieten viel Funktionalität, indem sie Technologien wie Internetprotokolle, Dateisystemzugriff, XML-Bearbeitung und andere einhüllen und abstrahieren. Die wahre Macht dieses Frameworks wird ausführlicher am Tag 8, Einführung in das .NET-Framework, behandelt. Für den Augenblick sollten Sie sich merken, dass das .NETFramework in vielerlei Hinsicht dem Betriebssystem gleicht und dass es eigene APIs bietet, um es den Programmierern leicht zu machen, Nutzen aus ihm zu ziehen. Abbildung 1.9 stellt die Beziehung des Frameworks zu Ihrem Code und den zugrunde liegenden Diensten des Betriebssystems dar. Benutzer-Anwendungen (einschließlich Ihrer Programme!)
.NET-Framework
Betriebssystem Gerätetreiber
Gerätetreiber Hardwarekomponenten
Abbildung 1.9: Das .NET-Framework bietet eine weitere Schicht der Abstraktion über dem Betriebssystem, ebenso wie es das Betriebssystem für die Computerhardware tut.
Damit eine Programmiersprache die Laufzeitumgebung und andere Funktionalitäten des .NET-Frameworks nutzen kann, muss der Compiler Code erzeugen, der einen bestimmten Standard einhält. Microsoft bietet diesen Standard, die Common Language Specification, als Möglichkeit, um jeden Compiler .NET-fähig zu machen. Microsoft hat einerseits die Programmiersprachen Visual Basic, Visual C++ und C#-Compiler entwickelt, die auf das .NET-Framework abzielen, aber andererseits die Common Language Specification auch außerhalb von Microsoft verfügbar gemacht, damit andere Unternehmen Compiler für andere Sprachen erstellen können. Das Ergebnis ist, dass zusätzlich zu den Sprachen, die von Microsoft angeboten werden, viele andere Sprachen existieren (wie COBOL, APL, Smalltalk usw.), die auf genau dem gleichen Fundament aufgebaut sind wie Visual Basic .NET. An diesem Punkt ist es wichtig, zwischen den Funktionen zu unterscheiden, die von der Sprache geboten werden, und denen, die das .NET-Framework bietet. Visual Basic und COBOL nutzen beide alle Funktionen des Frameworks, aber als unterschiedliche Sprachen haben sie auch ihre eigenen Funktionen und ihre eigene Syntax. Im Laufe dieses Buchs werden Sie etwas über die Visual Basic-spezifischen Funktionen lernen und über solche, die vom Framework geboten werden.
42
Was ist .NET?
.NET-Dienste .NET umfasst bestimmte Konzepte, die über die Details der Programmierung hinausgehen und beschreiben, wie Systeme gebaut sein sollten und wie sie interagieren können. Ein solches Schlüsselkonzept sind die Webdienste: Funktionalität, die in konsistenter Weise über das Internet geliefert wird. Diese Dienste ermöglichen es einem Unternehmen, Funktionalitäten so zur Verfügung zu stellen, dass ihre Ausführung vollständig in der Unternehmensumgebung stattfindet. Ein Beispiel für einen derartigen Dienst wäre ein Abrechnungsservice, in dem ein Unternehmen Server und Anwendungen zur Verfügung stellt, die die Bezahlung von Rechnungen handhaben. Das Unternehmen bietet anderen diesen Dienst über einen Webdienst an (siehe Abbildung 1.10). Das ist etwas anderes, als einfach eine Website anzubieten: Es ist eine Schnittstelle, auf die andere Anwendungen oder Websites mittels Code zugreifen können.
Windows 2000 Server auf denen Webdienste laufen
en
An
L-
g fra
XM
or ntw
XM
ten
Client-Anwendungen
A L-
XM
L-
An
XM
LA
n
fra ge n
tw nfragen XML-A o r te
n
XML-Antworten Große Anzahl verfügbarer Dienste
Webserver
XML-Anfragen XML-Antworten
Webdienste können von jedem Betriebssystem bereitgestellt werden und mit jedem Entwicklungswerkzeug erstellt werden, das HTTP unterstützt.
Remote-Anwender
Abbildung 1.10: Mit einem Webdienst können einerseits Unternehmen softwarebasierte Dienste über das Internet zur Verfügung stellen und andererseits können damit andere Websites und/oder Anwendungen diese Dienste nutzen.
Es gibt viele mögliche Verwendungszwecke für diese Technologie, aber das Konzept der Webdienste besteht darin, dass es bestimmte Grundlagen oder Foundation Services gibt, die viele Anwendungen benötigen, etwa für Authentifizierung, Zeitrechnung, Nachrichten (E-Mail) und mehr. Wenn man solche Funktionalitäten als Webdienste hat, kann jeder sie
43
Willkommen bei Visual Basic .NET
nutzen, um die Entwicklungszeit des eigenen Systems zu verringern. Microsoft selbst bietet als Teil der .NET-Initiative einige dieser Basisdienste für die Authentifizierung (Microsoft Passport, www.passport.com), Nachrichten (Hotmail, www.hotmail.com) und anderes an. Am Tag 21, Webdienste mit Visual Basic .NET erstellen, werden wir eine ausführliche Einführung in die Webdienste geben und näher darauf eingehen, wie Sie sie in Ihren Anwendungen nutzen können.
.NET-Geräte Heutzutage gibt es eine große Palette von Systemen, mit denen Sie Zugang zum Internet, zu Ihrem Firmennetzwerk oder zu Ihren persönlichen Informationen bekommen. Egal ob es sich um richtige PCs, TV-basierte Internetterminals, Thin Clients oder Personal Digital Assistants (PDAs) handelt: Mit all diesen Geräten kann ein Anwender Zugriff auf eine .NET-Anwendung bekommen. Dieser Trend in Richtung einer großen Vielfalt von Geräten zwingt Sie als Programmierer, die allgemeine Grundannahme eines einzelnen Clienttyps, der für gewöhnlich ein PC ist, zu vergessen, und sich statt dessen vorzustellen, dass ein Client über eines von vielen möglichen Geräten verbunden wird. Diese Geräte können als .NET-Geräte klassifiziert werden – eine Kombination aus Hardware- und Softwarefunktionen, die mit .NET-Diensten und Anwendungen arbeiten sollen. Derzeit umfasst die Palette der verfügbaren .NET-Geräte Computer, auf denen Windows läuft (Windows 9x, Millennium Edition und Windows 2000 mit dem installierten .NET-Framework), und Geräte, auf denen Windows CE läuft. (Das .NET Compact Framework steht für diese Plattform zur Verfügung und ermöglicht eine Unterstützung der .NET-Funktionen.) In diesem Bereich wird es ein ständiges Wachstum geben. Machen Sie sich keine Sorgen, wenn es immer noch Teile von .NET gibt, die Sie verwirren. Für den Rest des Tages wird die Lektion dieses Buches Einzelheiten erläutern, die alles deutlicher machen sollen. Behalten Sie vorerst die Beziehung zwischen .NET und Ihren Programmen im Hinterkopf – eine Schicht von Funktionalität, die bereits gegeben ist und auf die Sie von Ihrem Code aus leicht zugreifen können. Wenn Sie sich dieses Konzept merken und wissen, dass das Endergebnis eine schnellere Entwicklung vom Konzept hin zum endgültigen Programm ist, haben Sie für die heutige Lektion genug über .NET gelernt.
44
Ihre erste Visual Basic .NET-Anwendung
1.5
Ihre erste Visual Basic .NET-Anwendung
Es ist an der Zeit, Ihren ersten Visual Basic .NET-Code zu schreiben. Bevor wir mit diesem und anderen Beispielen weitermachen, müssen Sie dafür sorgen, dass Visual Basic .NET installiert ist. In diesem Abschnitt werde ich Ihnen dabei helfen, Visual Basic oder Visual Studio (das Visual Basic enthält) auf Ihrem Computer einzurichten. Wenn Sie es bereits installiert haben, können Sie diesen Abschnitt überschlagen und mit »Wo ist meine IDE?« weitermachen.
Vorbereitung zum Codieren Bevor Sie mit Visual Basic .NET arbeiten können, müssen Sie es installieren. Sie sollten entweder mehrere CDs oder eine DVD haben, von denen aus Sie installieren. Sie können auch von einem Netzwerkplatz aus installieren, aber der Prozess ist im Grunde in allen drei Fällen der gleiche. Das Erste, was angezeigt wird, wenn Sie Disk 1 einlegen oder SETUP von der DVD oder vom Netzwerk aus ausführen, ist ein Dialogfenster, das drei getrennte Schritte aufführt (siehe Abbildung 1.11). Als ersten Schritt müssen Sie die Betriebssystemkomponenten auf Ihrem Rechner auf die Ebene bringen, die .NET erfordert. Dieser Vorgang nennt sich Windows Component Update (WCU) und wenn Sie auf die erste Option im SETUP-Dialogfenster klicken (siehe Abbildung 1.11), werden Sie aufgefordert, den WCU-Datenträger bei Bedarf einzulegen. Nun beginnt die Installation. Was Sie während des WCU-Setups sehen, hängt von Ihrem System ab, da nur die Elemente aktualisiert werden, die nicht bereits auf dem letzten Stand sind. Die möglichen Installationen umfassen Windows 2000 Service Pack 2, MDAC 2.7 (aktualisierte Komponenten für den Datenzugriff) und Internet Explorer 6.0. Ein Element, das für die meisten Systeme installiert wird, ist das .NET-Framework selbst, das die Visual Studio .NET-IDE zwar nicht enthält, aber alle erforderlichen Framework-Klassen und unterstützenden Dateien liefert, um .NET-Anwendungen auszuführen. Je nachdem, welche Komponenten erforderlich sind, muss das Installationsprogramm Ihren Computer eventuell mehrmals neu starten. Wenn die Installation vollständig ist, werden Sie zum ursprünglichen Setup-Dialogfenster zurückgeführt, das Sie in Abbildung 1.11 sehen. Um Visual Studio .NET selbst zu installieren, klicken Sie auf Schritt 2, um den nächsten Teil der Installation zu starten. Sie werden aufgefordert, Ihren Produktschlüssel einzugeben und das End User License Agreement (EULA), die Lizenzvereinbarung für Endbenutzer, zu akzeptieren oder abzulehnen, bevor die Hauptinstallation beginnen kann.
45
Willkommen bei Visual Basic .NET
Abbildung 1.11: Der Installationsprozess von Visual Studio .NET ist in drei Hauptschritte gegliedert.
Nachdem Sie die erforderlichen Informationen eingegeben und dem EULA zugestimmt haben, kommen Sie zu einem Bildschirm mit Optionen, auf dem Sie auswählen können, welche Elemente von Visual Studio .NET Sie installieren möchten (siehe Abbildung 1.12). Wenn Sie vorhaben, vor allem in Visual Basic .NET zu programmieren, schlage ich vor, dass Sie die gleichen Optionen auswählen, die Sie in Abbildung 1.12 sehen. Die erste Auswahl, die erwähnt werden sollte, ist die Auswahl von Visual Basic und Visual C# in den SPRACHTOOLS-Optionen. Visual C# ist nicht erforderlich, aber Sie werden feststellen, dass in dieser Sprache viele Codebeispiele zur Verfügung stehen, und es ist wahrscheinlicher, dass Sie mit C# experimentieren, als mit C++. Sie können auch C++ auswählen, aber das würde ich nur tun, wenn Sie wirklich davon ausgehen, dass Sie es verwenden werden, da es mehr Festplattenspeicher in Anspruch nimmt als beide anderen Sprachen zusammen. Als Nächstes stellen Sie sicher, dass Sie alle Optionen unter den Punkten ENTERPRISE DEVELOPMENT TOOLS, SERVERKOMPONENTEN und TOOLS FÜR DAS NEUVERTEILEN VON ANWENDUNGEN ausgewählt haben. Ich werde nicht näher darauf eingehen, was diese Optionen enthalten, aber wenn Sie sie alle auswählen, stellen Sie sicher, dass Ihnen alle diese Funktionen in Ihrer Version von Visual Studio zur Verfügung stehen (andere Visual Studio-Versionen haben andere Funktionsoptionen). Wählen Sie schließlich MSDN aus und lassen Sie entweder die Standardeinstellung VON DER QUELLE AUS ISTALLIEREN, die Zugriff auf Ihr CD-, DVD- oder Netzwerkinstallationsverzeichnis benötigt, unverändert oder wechseln Sie zur Installation mit einem bestimmten Pfad, wenn Sie genug Speicherplatz zur Verfügung haben (siehe Abbildung 1.14).
46
Ihre erste Visual Basic .NET-Anwendung
Abbildung 1.12: Sie können auswählen, welche Komponenten von Visual Studio .NET Sie installieren möchten.
Die Ausführung von MSDN vom Installationsort aus ist langsamer als die Installation auf Ihrem lokalen Rechner, aber die Entscheidung basiert im Allgemeinen auf dem vorhandenen Speicherplatz und nicht auf der Geschwindigkeit. Wenn Sie mit der Auswahl fertig sind, klicken Sie auf den Link JETZT INSTALLIEREN! im unteren Bereich des Dialogfensters, um die Installation zu starten. Verschiedene Textstellen, die die Funktionen von .NET beschreiben, werden während des Installationsprozesses angezeigt, der schließlich mit dem Abschlussbildschirm endet (siehe Abbildung 1.14). Wenn während der Installation etwas schief läuft, wird es in diesem Fenster angezeigt. Wenn Sie dann zum ersten Setup-Bildschirm zurückkehren, steht Ihnen auch die dritte Auswahl zur Verfügung. Wenn Sie auf diese Option, SERVICE RELEASES, klicken, öffnet sich ein weiteres Dialogfenster (siehe Abbildung 1.15), in dem Sie auswählen können, ob Sie Updates aus dem Web oder von der CD oder DVD installieren möchten. Wenn Sie eine Verbindung zum Internet haben, empfehle ich Ihnen, die erste Option auszuwählen, um sicherzustellen, dass Sie die aktuellste Liste der Updates haben. Visual Studio .NET ist nun installiert und sollte zur Ausführung bereit sein, sodass Sie zum nächsten Abschnitt übergehen können, in dem Sie ein wenig Code schreiben werden.
47
Willkommen bei Visual Basic .NET
Abbildung 1.13: Die MSDN-Bibliotheken, die sehr viele nützliche Dokumentationen, Artikel und Beispiele enthalten, können lokal installiert werden, um eine höhere Geschwindigkeit zu erzielen.
Abbildung 1.14: Wenn bei der Installation etwas schief läuft, informiert dieser Bildschirm Sie über das Problem.
48
Ihre erste Visual Basic .NET-Anwendung
Abbildung 1.15: Das Visual Studio .NET-Installationsprogramm kann Updates aus dem Internet herunterladen und installieren.
Wo ist meine IDE? Ihr erstes Programm wird mit Notepad erstellt und mit der Befehlseingabeaufforderung ausgeführt werden. Ja, Sie haben richtig gelesen: mit der Befehlseingabeaufforderung. Ich habe weiter oben in der heutigen Lektion erläutert, dass viele Programmiersprachen nur aus einem Compiler bestanden. In einer solchen Sprache würden Sie die Programme als Textdateien schreiben und dann den Compiler aufrufen, um diesen Code (vorausgesetzt, Sie haben keine Fehler gemacht) in eine ausführbare Datei umzuwandeln. In den meisten Sprachen hat sich das nie verändert. Der Zweck einer IDE besteht darin, Ihnen eine einzige, einheitliche Arbeitsumgebung zur Verfügung zu stellen, aber sie geht immer noch die gleichen Schritte (Bearbeiten, Kompilieren, Ausführen) durch wie der manuelle Prozess. In der Zeit vor Visual Basic .NET konnten Sie Ihre Programme auf der Befehlszeile kompilieren, aber das war im Grunde nur eine Ausführung der IDE in einem nicht sichtbaren Modus. Visual Basic .NET hat die beiden Funktionen des Kompilierens und Bearbeitens getrennt, indem es den Compiler als vollständig separates Programm zur Verfügung stellt. Wenn Sie die IDE verwenden, müssen Sie die Kompilierung nicht sehen. Die IDE erledigt das für Sie. Der Hauptunterschied zwischen dem Compiler in Visual Basic .NET und vorherigen Versionen ist, dass der Visual Basic .NET-Compiler vollkommen unabhängig von der IDE ist. Alles, was Sie in der IDE tun können, lässt sich auch mit dem Befehlszeilen-Compiler und Notepad erledigen. Da die IDE selbst ein großes und kompliziertes Programm ist, ist es einfacher, sich auf die Sprache zu konzentrieren und gelegentlich auf der
49
Willkommen bei Visual Basic .NET
Befehlszeilenebene zu arbeiten. Die morgige Lektion – Tag 2, Mit Visual Basic .NET arbeiten – wird die IDE einführen und die Grundlagen liefern, damit Sie mit ihr Programme bearbeiten, ausführen und kompilieren können.
Eine einfache Aufgabe Um Ihr erstes Programm zu schreiben, benötigen Sie ein Ziel, etwas, das Ihr Programm tun kann. Ich habe nicht vor, in diesem Buch »Hello World!«-Programme zu schreiben, die überhaupt keinen Zweck haben. Das ist allerdings schwierig, wenn Sie versuchen, Ihre ersten Codezeilen zu schreiben. Der Begriff »Hello World!«-Programm beschreibt ein Beispielprogramm, das nichts Nützliches tut oder, genauer gesagt, nicht Nützlicheres als die Ausgabe der Nachricht »Hello World!«. Sie werden mit einem Programm beginnen, das Sie aus der Befehlszeile ausführen können und das ein wenig Text zurückgibt. Dann werde ich Ihnen zeigen, wie dieses Programm fast alles illustriert, was Sie wissen müssen, um ein nützliches Hilfsprogramm zu schreiben, das Versionsinformationen für Ihr Betriebssystem ausgibt (z.B. hat die Release-Version von Windows 2000 die Versionsnummer 5.00.2195). Das mag als Befehlszeilen-Hilfsprogramm für Sie nützlich sein oder nicht; zumindest zeigt es aber einige Grundkonzepte der Programmierung.
Den Code schreiben Da Sie in einem Texteditor (wie Notepad) statt in der IDE beginnen werden, bleibt Ihnen nichts erspart und die Datei ist am Anfang leer. Das bedeutet, dass Sie die Basisstruktur für Ihr Programm einrichten müssen, bevor Sie mit dem Schreiben von Code beginnen können, der korrekt kompiliert wird. Für den Anfang erzeugen Sie eine neue Textdatei irgendwo auf Ihrem Computer. Für die Beispiele im ersten Kapitel dieses Buches erstellen Sie auf Ihrem Computer ein Verzeichnis namens C:\Beispiele\Kap01\. Um die neue Textdatei anzulegen, klicken Sie mit der rechten Maustaste auf den entsprechenden Ordner und wählen NEU, TEXTDOKUMENT aus (siehe Abbildung 1.16). Dadurch wird eine Datei namens Neue Textdatei.txt erzeugt. Benennen Sie diese Datei so um, dass sie einen Inhalt anzeigt, indem Sie den Namen in Step1.vb ändern. Wenn die Datei umbenannt ist und sich im richtigen Verzeichnis befindet, können Sie weitermachen und damit beginnen, Ihren eigentlichen Code einzurichten. Öffnen Sie die Datei in Notepad, indem Sie sie mit der rechten Maustaste anklicken und den Befehl ÖFFNEN MIT auswählen. Dadurch öffnet sich ein Dialogfenster, über das Sie Notepad oder andere Texteditoren auswählen können (z.B. WordPad).
50
Ihre erste Visual Basic .NET-Anwendung
Abbildung 1.16: Mit dem Kontextmenü des Explorers haben Sie eine direkte Möglichkeit, eine Textdatei und viele andere Dokumente zu erzeugen.
Nachdem Sie ein Programm ausgewählt haben, klicken Sie auf OK und die Datei wird geöffnet. Zu diesem Zeitpunkt sollte die Datei leer sein, Sie müssen alles selbst hinzufügen, damit sie funktioniert.
Schritt für Schritt Versuchen Sie immer, wenn Sie Code schreiben, und vor allem, wenn Sie etwas Neues ausprobieren, das Problem in mehrere Schritten zu unterteilen. Auf diese Art wissen Sie, wenn ein Problem auftritt, welche Teile des Programms funktionieren und welche Teile nicht. Im ersten Schritt werden Sie sicherstellen, dass Sie tatsächlich eine Anwendung mit dem Befehlszeilen-Compiler erzeugen können. Dafür benötigen Sie einigen Code, aber dieser muss nicht wirklich etwas tun. Er muss nur das richtige Format haben. Der folgende Code stellt die Basisstruktur eines Visual Basic .NET-Programms dar; geben Sie ihn in Ihrem Notepad-Fenster ein und speichern Sie ihn als Step1.vb. Public Class Step1 Shared Sub Main() End Sub End Class
Wenn Sie versuchen, Ihren Code als Step1.vb zu speichern, können am Ende vielleicht ein paar merkwürdige Ergebnisse auftreten. Da Notepad versucht, Textdateien zu erzeugen, haben Sie je nach Ihren Systemeinstellungen für Dateinamenerweiterungen am Ende möglicherweise eine Datei namens step1.vb.txt auf Ihrer Festplatte. Um das zu vermeiden, stellen Sie sicher, dass die Option DATEINAMENERWEITERUNG BEI BEKANNTEN DATEITYPEN AUSBLENDEN (im Windows Explorer unter EXTRAS, ORDNEROPTIONEN, ANSICHT) nicht ausgewählt ist.
51
Willkommen bei Visual Basic .NET
Als Nächstes laden Sie die Befehlseingabeaufforderung, indem Sie unter START, PROGRAMME, MICROSOFT VISUAL STUDIO .NET 7.0, VISUAL STUDIO .NET TOOLS die Option VISUAL STUDIO .NET COMMAND PROMPT auswählen, wie Sie es in Abbildung 1.17 sehen können. Es ist für die Funktion dieses Beispiels wichtig, dass Sie über diese Option auf die Befehlszeile zugreifen, da nur durch diese Befehlszeile die richtigen Pfade eingerichtet werden, um die .NET-Werkzeuge wie den Visual Basic-Compiler (vbc.exe) auszuführen. Wenn Sie eine reguläre Befehlszeile verwenden möchten, müssen Sie die entsprechende PfadEinstellung auf Ihrem Rechner hinzufügen (führen Sie eine Suche nach vbc.exe durch, um das Verzeichnis zu finden, das sich in Ihrem Pfad befinden muss). Dadurch wird eine Befehlskonsole geöffnet (siehe Abbildung 1.18). Von dort aus navigieren Sie zu dem Verzeichnis, das Ihre Datei Step1.vb enthält. Sie sind nun bereit, Ihr Programm zu kompilieren und eine ausführbare Datei zu erzeugen, aber Sie können noch mehr tun, indem Sie einen weiteren Schritt hinzufügen, bevor Sie kompilieren.
Abbildung 1.17: Das Auswählen der Visual Studio .NET-Befehlseingabeaufforderung ist die beste Methode, um mit .NET auf einer Befehlszeile zu arbeiten. Abbildung 1.18: Die Verwendung des BefehlszeilenCompilers erfolgt über die Konsole (oder die DOS-Eingabeaufforderung, wie sie häufig genannt wird).
52
Ihre erste Visual Basic .NET-Anwendung
An der Befehlseingabeaufforderung geben Sie vbc ein und drücken auf (¢). Sie sollten die folgende Ausgabe sehen: Microsoft (R) Visual Basic.NET Compiler version 7.00.9254 for Microsoft (R) .NET CLR version 1.00.2914.16 Copyright Microsoft Corp 2001. All rights reserved. Visual Basic Compiler Options
Danach folgen viele Hilfeinformationen über die Verwendung des Visual Basic-Compilers. Wenn das funktioniert, haben Sie eine Reihe von Dingen bestätigt: 쐽
Auf Ihrem System ist Visual Basic .NET installiert.
쐽
Sie verfügen über die entsprechenden Berechtigungen, den Compiler auszuführen.
쐽
Das System konnte die Datei vbc.exe finden.
Indem Sie zuerst nur vbc eingeben, können Sie es als Problemquelle beim Kompilieren eines richtigen Programms eliminieren. Sie können nun Ihr gespeichertes Programm Step1.vb kompilieren, indem Sie den Befehl vbc Step1.vb /t:exe eingeben und auf (¢) drücken. Wenn alles funktioniert, sollten Sie dies erhalten: Microsoft (R) Visual Basic.NET Compiler version 7.00.9254 for Microsoft (R) .NET CLR version 1.00.2914.16 Copyright Microsoft Corp 2001. All rights reserved.
Das bedeutet, dass der Kompiliervorgang erfolgreich war, und wenn Sie sich den Ordner nun mit Windows oder einem DIR-Befehls ansehen würden, könnten Sie feststellen, dass es eine neue Datei namens Step1.exe gibt. Wenn Sie diese ausführbare Datei laufen lassen (indem Sie an der Befehlseingabeaufforderung Step1 eingeben und auf (¢) drücken), wird kein sichtbares Ergebnis erzeugt. Dies war zu erwarten, da der Code von Step1.vb nicht wirklich etwas gemacht hat. Die Tatsache, dass er überhaupt kompiliert wurde und gelaufen ist, zeigt, dass der Code bisher korrekt ist. Der Aufruf von vbc kann viele optionale Switches entgegennehmen (zusätzliche Befehle, die auf der Befehlszeile hinzugefügt werden, normalerweise durch Schrägstriche [/] getrennt). Das können Sie sehen, wenn Sie vbc alleine ausführen. In diesem Fall haben Sie aber nur zwei Switches benutzt. Indem Sie den Namen der Quelldatei step1.vb angeben, haben Sie vbc mitgeteilt, welcher Code kompiliert werden soll. Die zweite Option / t:exe gibt den Typ der kompilierten Anwendung an, der erstellt werden soll. Die Optionen für /t (die für »Target« steht und auch als /target eingegeben werden kann) können winexe, exe, eine Bibliothek oder ein Modul sein. Alle diese Optionen werden später behandelt, aber vorerst sind nur die ersten beiden teilweise relevant: winexe erzeugt eine WindowsAnwendung und exe erzeugt eine Befehlszeilen- oder Konsolenanwendung (was Ihr Ziel ist). Wenn Sie vbc ausführen, verwendet es standardmäßig /t:exe.
53
Willkommen bei Visual Basic .NET
Wir wollen eine Ausgabe sehen Nun versuchen wir, die Versionsnummer des Betriebssystems zurückzugeben und Text auszugeben. Bevor Sie überhaupt etwas in einem Visual Basic .NET-Programm machen können, müssen Sie dem Compiler sagen, welche Teile des .NET-Frameworks Sie verwenden werden. In diesem Fall werden Sie nur das Toplevel-Objekt des Frameworks – System genannt – verwenden. Fügen Sie also die Zeile Imports System zum oberen Teil der Codedatei hinzu. Sie können nun auf dieses Objekt System verweisen und seine Funktionen in Ihrem Code verwenden. Eines der Elemente oder Teile der Framework-Klasse System ist das Objekt Console. Mit diesem Objekt können Anwender über die Befehlszeilenschnittstelle lesen und schreiben. Für dieses Beispiel verwenden Sie die Anweisung Console.WriteLine, um Text in das Befehlsfenster zurückzugeben. Es ist egal, welchen Text Sie zurückgeben; dies ist nur ein Test für die Ausgabe von Informationen. In Ihrem Code haben Sie den Text Hier kommt die Versionsinformation zum Betriebssystem hin. verwendet, um deutlich zu machen, dass dies nur ein Platzhalter ist. Speichern Sie diese neue, modifizierte Codedatei (die wie der folgende Code aussehen sollte) als Step2.vb und wechseln Sie dann zum Befehlsfenster zurück, um sie zu kompilieren: Imports System Public Class Step2 Shared Sub Main() System.Console.WriteLine("Hier kommt die Versionsinformation zum Betriebssystem hin.") End Sub End Class
Die compile-Anweisung ist fast genau die gleiche wie im vorherigen Schritt, mit der Einschränkung, dass Sie den neuen Quelldateinamen angeben werden: vbc Step2.vb /t:exe. Die Ausführung dieser compile-Anweisung erzeugt eine Datei namens Step2.exe, die Sie dann an der Befehlseingabeaufforderung ausführen, indem Sie Step2 eingeben und auf (¢) drücken. Wenn das Kompilieren funktioniert hat und es keine Probleme im Code gab, sollten Sie die folgende Ausgabe für Ihre Befehle sehen: C:\Beispiele\Kap01>vbc step2.vb /t:exe Microsoft (R) Visual Basic.NET Compiler version 7.00.9254 for Microsoft (R) .NET CLR version 1.00.2914.16 Copyright Microsoft Corp 2001. All rights reserved. C:\Beispiele\Kap01>step2 Hier kommt die Versionsinformation zum Betriebssystem hin.
54
Ihre erste Visual Basic .NET-Anwendung
Die Versionsinformationen-Funktionalität hinzufügen Sie wissen nun, dass der Code bis zu diesem Punkt korrekt ist (weil er funktioniert). Das ist ein guter Anfang für den nächsten Schritt. Nun müssen Sie die tatsächlichen Betriebssystemversionsinformationen holen und dann diese statt des generischen Textstückes ausgeben. Sie müssen wiederum das .NET-Framework verwenden, um diese Information zu bekommen. Dies geht über eine andere seiner Funktionen und den Zugriff auf die Information über das aktuelle Betriebssystem – wie z.B. Windows 2000. Diese Information ist über das gleiche System-Objekt verfügbar, aber statt System.Console benutzen Sie System.Environment.OSVersion. Modifizieren Sie den Code von Step2.vb so, dass er wie der folgende Code aussieht, und speichern Sie die Datei als Step3.vb. Imports System Public Class Step3 Shared Sub Main() System.Console.WriteLine(System.Environment.OSVersion.ToString()) End Sub End Class
Das Kompilieren des Codes vbc step3.vb /t:exe unterscheidet sich nicht vom Kompilieren der vorherigen beiden Beispiele. Diese Kompilierung erzeugt eine ausführbare Datei namens step3.exe. Sie führen diese aus, indem Sie Step3 Enter im offenen Befehlszeilenfenster eingeben, wodurch die folgende Ausgabe erzeugt wird: C:\Beispiele\Kap01>step3 Microsoft Windows NT 5.0.2195.0
Beachten Sie, dass diese Ergebnisse von einem Windows 2000 (NT 5.0)-Rechner stammen; von anderen Computern werden andere Ergebnisse erzeugt. Nun haben Sie eine kleine ausführbare Datei, die die Version des Betriebssystems zurückgibt. Zumindest für den Netzwerkadministrator ist dies sicherlich ein nützliches Programm. Die Anwendung heißt zurzeit step3.exe, aber ihr Name kann leicht so geändert werden, dass ihr wahrer Zweck besser zum Ausdruck kommt.
Der Befehlszeilen-Compiler In den vorherigen Beispielen haben Sie den Befehlszeilen-Compiler verwendet, um zwei Dinge anzugeben: das Ziel des Kompiliervorgangs (/t:exe) und die zu kompilierende Quelldatei (z.B. step1.vb). Der Compiler hat mit diesen beiden Optionen gut gearbeitet, aber es gibt viele zusätzliche Optionen, die als Teil dieses Befehls zur Verfügung stehen. Hier sehen Sie ein paar der Befehlszeilen-Switches, die besonders nützlich sind, wenn Sie beginnen, mit Visual Basic .NET zu experimentieren.
55
Willkommen bei Visual Basic .NET
쐽
/target: (kann als /t aufgerufen werden) gibt an, welche Art von Ausgabe vom Compiler erzeugt werden soll. Die Option winexe erzeugt eine Windows-Anwendung. Die Option exe, die wir in unserem Beispiel benutzt haben, erzeugt eine Befehlszeilen- oder Konsolenanwendung und ist der Standard, wenn kein /t-Switch verwendet wurde. Mit den verbleibenden zwei Optionen, library und module, wird eine Ausgabe erzeugt, die nicht direkt verwendet werden kann, sondern Teil einer anderen Anwendung sein soll. Sie werden in den folgenden Tagen mehr über diese Ausgabetypen lernen.
쐽
/out: wird verwendet, um den Namen der zu erzeugenden Datei anzugeben. In unseren Beispielen haben wir dies ausgelassen, wodurch vbc mit dem Namen Ihrer Quelldatei (Step1.vb) die Ausgabedatei (z.B. Step1.exe) erzeugt hat.
쐽
/help (oder /?) ist äquivalent zur Ausführung von vbc ohne Optionen. In beiden Fäl-
len ist das Ergebnis eine detaillierte Auflistung aller verfügbaren Switches. 쐽
/verbose lässt den Compiler während der Kompilierung eine detailliertere Ausgabe
erzeugen und kann bei der Fehlerbehebung helfen. 쐽
/reference: (kurz /r:) wird verwendet, um dem Compiler anzu-
zeigen, dass Ihr Code außer dem, was standardmäßig enthalten ist, zusätzliche Dateien benötigt. Wenn Sie z.B. mit den System.Web-Teilen des .NET-Frameworks arbeiten wollten, müssten Sie am Anfang Ihres Quellcodes die Zeile Imports System.Web einfügen und dann /r:system.web.dll angeben, wenn Sie Ihren Code kompilieren.
1.6
Zusammenfassung
Die heutige Lektion hat einen Teil des Hintergrunds der Programmierung von Visual Basic und .NET abgedeckt, bevor ein kleines Programm erstellt wurde. Nun sind Sie dafür gerüstet, die Sprache selbst zu lernen und komplexere Programme zu schreiben.
56
Fragen und Antworten
1.7 F
Warum unterscheidet sich Visual Basic .NET so sehr von den vorhergehenden Versionen von Visual Basic? A
F
Fragen und Antworten
Visual Basic hat sich mit der Zeit weiterentwickelt. Jede Version war der vorhergehenden sehr ähnlich, aber an bestimmten Punkten ist es nötig, alles wie bei .NET neu zu schreiben, um sicherzustellen, dass die Sprache mit anderen Veränderungen in der Industrie Schritt hält, wie dem Internet oder verteilter Programmierung.
Ich habe von einer anderen .NET-Sprache gehört: C#. Sollte ich diese statt oder zusätzlich zu Visual Basic .NET lernen? A
1.8
C# ist eine gute neue Sprache, ein einfachere, leichter zu verwendende Form von C++. Aber nein, Sie müssen sie nicht lernen. Alle .NET-Sprachen sind in Bezug auf ihre Fähigkeiten gleich; wenn Sie eine bestimmte Anwendungsart mit C# schreiben können, dann können Sie sie auch in Visual Basic .NET schreiben. Sie werden feststellen, dass viele Beispiele von Microsoft in C# erscheinen, ganz einfach weil es eine Sprache ist, die sowohl Visual Basic-Programmierer als auch Visual C++-Programmierer relativ leicht lesen können.
Workshop
Der Workshop soll Ihnen dabei helfen, mögliche Fragen vorherzusehen und das Gelernte zu wiederholen. Außerdem soll er Sie dazu anregen, über die praktische Umsetzung Ihres Wissens nachzudenken. Die Antworten und Lösungen finden Sie im Anhang A.
Quiz 1. Welches Microsoft-Produkt war der Vorgänger von Visual Basic? 2. Warum haben alle .NET-Sprachen (Visual Basic .NET, C# usw.) bestimmte gemeinsame Funktionen (das .NET-Framework, Methoden der Objekterzeugung und -verwendung u.a.)? 3. Wie nennt man den Prozess der Umwandlung von Quellcode (Ihr Programm) in nativen Maschinencode (wie eine .exe-Datei)?
57
Willkommen bei Visual Basic .NET
4. Sehen Sie sich den folgenden Code an: Public Class MyClass Shared Sub Main() End Sub End Class
Wenn dieser Code in einer Datei namens MySourceCode.vb gespeichert würde und Sie dann den Befehl vbc /t:exe MySourceCode.vb ausführen würden, welche Datei würde dann erzeugt?
Übungen 1. In den heutigen Beispielen haben Sie beim Kompilieren keinen Ausgabedateinamen angegeben, was dazu führte, dass der Compiler standardmäßig den Namen der Quelldatei verwendete. Kompilieren Sie step3.vb erneut und geben Sie den Ausgabedateinamen WhatOS.exe an. 2. Um die Versionsinformationen zu bekommen, haben Sie das Objekt System.Environment benutzt, das noch mehr mächtige Eigenschaften hat. Finden Sie mit der HilfeDokumentation, die mit Visual Basic installiert wird (siehe den Abschnitt »Den Code schreiben« weiter oben in der heutigen Lektion), heraus, was sonst noch verfügbar ist und schreiben Sie dann ein weiteres Programm, um eine oder mehrere dieser zusätzlichen Eigenschaften auszugeben.
58
Mit Visual Basic .NET arbeiten
Mit Visual Basic .NET arbeiten
Am Tag 1, Willkommen bei Visual Basic .NET, haben Sie einige allgemeine Informationen über das Programmieren, Visual Basic und .NET erhalten, die Ihnen den benötigten Hintergrund geben, um nun die ersten Anwendungen zu erstellen. Heute werden Sie mit Visual Basic eine richtige Windows-Anwendung erstellen und dabei die folgenden Punkte behandeln:
Die Themen heute: 쐽
Die Verwendung der Visual Studio-IDE
쐽
Das Arbeiten mit Lösungen, Projekten und Dateien
쐽
Das Erstellen und Ausführen eines Windows-Beispielprojekts
Ich beginne mit einem Überblick über die Visual Studio(VS)-IDE. Der Begriff IDE wurde bereits in der gestrigen Lektion kurz erwähnt, aber ich werde hier eine kleine Auffrischung über die Bedeutung und den Zweck einer IDE einbauen.
2.1
Die Visual Studio-IDE
Eine IDE (Integrated Development Environment) soll eine umfassende Arbeitsumgebung für Entwickler sein. Im Allgemeinen ist Quellcode einfacher Text und kann in jedem Texteditor (z.B. Notepad) eingegeben und bearbeitet werden, und Compiler können ohne große Probleme von der Befehlszeile aus verwendet werden, sodass eine IDE technisch gesehen eigentlich nicht erforderlich ist. Das haben Sie im vorherigen Kapitel gesehen. Trotzdem gibt es nur wenige Programmierer, die in einer Sprache arbeiten würden, die nicht irgendeine Form von IDE hat. Die Visual Studio-IDE vereinfacht sogar so simple Aufgaben wie Bearbeitung und Kompilierung Ihres Codes und bietet viele zusätzliche Features die ohne sie einfach nicht verfügbar wären.
Erste Schritte Bevor Sie die Visual Studio-IDE benutzen können, muss sie auf Ihrem Rechner installiert sein. Der entsprechende Prozess wurde in der gestrigen Lektion ausführlich vorgestellt. Wenn Sie die Installation zuerst noch abschließen müssen, sollten Sie den entsprechenden Abschnitt lesen, bevor Sie weitermachen. Anschließend starten Sie das Programm über START, PROGRAMME, MICROSOFT VISUAL STUDIO .NET 7.0, VISUAL STUDIO .NET 7.0.
60
Die Visual Studio-IDE
Profileinstellungen Das Erste, was Sie sehen, wenn Sie Visual Studio .NET starten, ist eine webseitenartige Benutzeroberfläche (mit der Beschriftung Visual Studio Startseite), in der Sie gebeten werden, Ihre Profileinstellungen zu bestätigen. Sollten diese Einstellungen nicht automatisch angezeigt werden, klicken Sie auf den Link MEIN PROFIL im linken Bereich des Bildschirms. Das Konzept eines Profils ist in dieser Version von Visual Studio neu: Es ähnelt dem Konzept der Benutzereinstellungen, mit denen Sie eine Reihe von unterschiedlichen Optionen über ein einzelnes Profil einstellen können. Die Grundeinstellungen, die vorhanden sind, sollen Ihnen die Umstellung auf diese Umgebung je nach Ihren Vorkenntnissen erleichtern. In meinem Setup habe ich die Einstellungen ausgewählt, die Sie in Abbildung 2.1 sehen. Innerhalb weniger Gebrauchsstunden hatte ich die Einstellungen aber so geändert, dass sie sich von jedem der Standardprofile ein wenig unterschieden. Höchstwahrscheinlich werden Sie am Ende das Gleiche tun.
Abbildung 2.1: Die Visual Studio .NET-IDE kann vom Benutzer definiert werden, um den Übergang von anderen Entwicklungstools, einschließlich der früheren Version von Visual Basic, leichter zu machen.
Wählen Sie vorerst das Profil VISUAL BASIC DEVELOPER aus. Sie können später zu diesem Dialogfenster zurückkehren, um die Einstellungen zu verändern. Klicken Sie dann auf den Link BEGINNEN im linken Bereich des Bildschirms, um die Seite mit den Profileinstellungen zu verlassen. Was Sie sich dann ansehen, bezeichnet man als den Hauptarbeitsbereich der Visual Studio-IDE. Es ist ein allgemeiner Ort, der diverse Inhalte hat, wie z.B. Code, den Sie bearbeiten, oder Webseiten, die Sie entwerfen. Er umfasst auch einen eingebauten Webbrowser, der standardmäßig geöffnet ist; mit ihm wird eine webartige Schnittstelle zu Visual Studio angezeigt.
61
Mit Visual Basic .NET arbeiten
Auf dieser Startseite (siehe Abbildung 2.2) haben Sie verschiedene nützliche Optionen. Die erste, BEGINNEN, ist die Seite, die immer dann angezeigt wird, wenn Sie Visual Studio öffnen. Diese Seite ist als Einführungskonsole für Ihre Arbeit gedacht und bietet deshalb sowohl eine Liste der zuletzt geöffneten Projekte als auch einen Link, um ein neues Projekt zu erzeugen.
Abbildung 2.2: Die Startseite von Visual Studio bietet eine funktionale Ansicht der zuletzt geöffneten Projekte und Zugriff auf eine Vielzahl von Informationsquellen.
Fürs Erste sollten Sie eine der Navigationsoptionen entlang des linken Seitenbereichs auswählen. Diese umfassen: 쐽
Einzelheiten zu den neuen Funktionen von Visual Studio .NET. Der Punkt NEUIGKEITEN verbindet Sie mit verschiedenen Online-Ressourcen wie z.B. Newsgroups (ONLINECOMMUNITY),
쐽
eine Live-Seite mit Neuigkeiten über Visual Studio und andere Entwicklungsthemen (HEADLINES, siehe Abbildung 2.3)
쐽
einen direkten Link zur Durchführung einer Websuche (ONLINESUCHE),
쐽
einen Link zurück zur Profilauswahlseite, die Sie standardmäßig bei der ersten Ausführung der IDE gesehen haben (MEIN PROFIL).
All dies sind wertvolle Ressourcen für einen Visual Studio-Entwickler, wodurch diese Startseite für viele ein guter Einstieg ist. Wenn Sie aber etwas zu diesen Optionen hinzufügen oder die gesamten Seiten ersetzen möchten, finden Sie den vollständigen Quellcode dieser und anderer Standardseiten unter \Programme\Microsoft Visual Studio.NET\...\HTML.
62
Die Visual Studio-IDE
Abbildung 2.3: Informationen, die für einen Visual Basic .NET-Programmierer relevant sind, finden sich oft auf der Website msdn.microsoft.com, deren Highlights automatisch in Bereichen der Visual Studio-Startseite angezeigt werden.
Eine Warnung muss ich aber aussprechen: Die Standardseiten sind nicht einfach und können leicht irreparabel beschädigt werden. Machen Sie eine Kopie des Verzeichnisses als Sicherungskopie, bevor Sie damit beginnen, sie zu verändern!
Das Hauptfenster der Visual Studio-IDE So nützlich diese Startseite auch ist, sie ist nur eines der vielen verschiedenen Fenster, die als Teil von Visual Studio zur Verfügung stehen. Die meisten davon werden Sie in der heutigen Lektion kennen lernen. Wenn Sie das Profil VISUAL BASIC DEVELOPER ausgewählt haben, sehen Sie bereits einige Fenster: den bereits erwähnten eingebauten Webbrowser, den Projektmappen-Explorer, die Eigenschaften und die Toolbox entlang der linken Seite. Die anderen Fenster, die ich in der heutigen Lektion vorstellen werde, die aber standardmäßig im aktuellen Profil verborgen bleiben, sind: 쐽
Objektbrowser
쐽
Befehlsfenster und Ausgabefenster
쐽
Aufgabenliste
쐽
Klassenansicht
쐽
Server-Explorer
Es gibt noch andere Fenster, die ich später in diesem Buch erläutern werde, wenn sie von Bedeutung sind.
63
Mit Visual Basic .NET arbeiten
Gängige Fensterfunktionen Alle Funktionen dieser Fenster – einschließlich des ganzen Konzepts der vielen kleinen Fenster – drehen sich darum, den vorhandenen Platz möglichst effizient zu nutzen. In jeder IDE und vor allem in Visual Studio .NET ist die Anzahl der verfügbaren Optionen und Werkzeuge fast grenzenlos, aber der Platz auf Ihrem Bildschirm ist es nicht. Eine Lösungsmöglichkeit für dieses Problem besteht darin, jedem Entwickler einen 21-ZollMonitor (oder noch größer) zu geben. Aus irgendeinem seltsamen Grund hat sich das immer noch nicht durchgesetzt, sodass andere Methoden entwickelt wurden. Eine dieser Methoden besteht darin, die verfügbaren Optionen für den Entwickler auf verschiedene Fenster aufzuteilen. Dieses Verfahren nutzt auch Visual Studio .NET. Nun muss die Platzierung dieser Fenster sowohl einfach als auch flexibel genug für die Entwickler sein, damit sie ihre ideale Arbeitsumgebung herstellen können. Jedes dieser einzelnen Fenster wird Tool-Fenster genannt. Die Elemente eines Tool-Fensters haben alle einige Funktionen gemeinsam. Sie können z.B. angedockt oder abgedockt sein, verborgen werden, zu Fenstern im Registerformat kombiniert werden usw. Außerdem können alle auf verschiedene Arten skaliert werden. Andocken/Abdocken Wenn das Profil VISUAL BASIC DEVELOPER ausgewählt ist, sind nach dem ersten Starten die Fenster PROJEKTMAPPEN-EXPLORER und EIGENSCHAFTEN direkt rechts vom umgebenden Visual Studio und das Fenster TOOLBOX direkt links davon angeordnet (siehe Abbildung 2.4). Das Platzieren eines Fensters bündig mit dem Rand eines anderen Fensters wird als Andocken des Fensters bezeichnet. Wenn das Fenster angedockt ist, ist es mit einer oder zwei Seiten (zwei Seiten, wenn es in einer Ecke ist) an diesen Rand gebunden. Jedes Tool-Fenster in Visual Studio .NET kann an beliebige Ränder in Visual Studio angedockt werden. Um ein angedocktes Fenster in einen anderen Bereich des Bildschirms zu bewegen, können Sie mit der Maus auf die Titelleiste des Fensters klicken und dann bei gedrückter Maustaste das Fenster auf die neue Position ziehen. Während Sie das Fenster ziehen, erscheint auf dem Bildschirm ein Umriss des Fensters, damit Sie sehen, wo das Fenster platziert würde, wenn Sie die Maustaste jetzt loslassen würden. Um das Fenster an einer anderen Seite der IDE anzudocken, ziehen Sie es, während Sie die Maustaste gedrückt halten, an den gewünschten Rand der IDE und lassen die Maustaste los, wenn sich der Umriss des Fensters an der entsprechenden Stelle befindet (siehe Abbildung 2.5). Während des Ziehens ist Ihnen vielleicht aufgefallen, dass der Umriss so aussieht, als wäre er an keinen Bereich der IDE angedockt. Wenn Sie an diesem Punkt loslassen würden, wäre das Fenster abgedockt, also ein so genanntes unverankertes Fenster (siehe Abbildung 2.6). Es ist dann nicht mehr an irgendeinen Rand der IDE gebunden. Ein unverankertes Fenster können Sie wieder andocken, indem Sie der oben beschriebenen Prozedur folgen.
64
Die Visual Studio-IDE
Abbildung 2.4: Wenn Visual Studio .NET auf das Profil VISUAL BASIC DEVELOPER gesetzt ist, ordnet es seine Fenster nahe an der Visual Basic 6.0-IDE an.
Abbildung 2.5: Der Umriss eines gezogenen Fensters ändert sich, um anzuzeigen, wie das Ergebnis aussehen würde, wenn Sie das Fenster an einem bestimmten Punkt loslassen würden.
Es kann ein wenig kompliziert sein, ein angedocktes Fenster wieder genau an die Stelle zu bekommen, von der Sie es weggezogen haben. Daher hier ein kleiner Tipp: Statt ein Fenster zu ziehen, um es an- oder abzudocken, klicken Sie doppelt auf die Titelleiste. Auf diese Art wird ein angedocktes Werkzeugfenster abgedockt oder ein unverankertes Fenster wieder zurück an seinen ursprünglichen Platz gesetzt.
65
Mit Visual Basic .NET arbeiten
Abbildung 2.6: Wenn Sie genug Platz auf dem Bildschirm haben, können Sie Ihre Fenster unverankert lassen.
Verbergen Ob angedockt oder unverankert, die Fenster nehmen auf Ihrem Bildschirm Platz ein. Es ist aber möglich, einzelne Fenster, die nicht sichtbar sein sollen, zu verbergen oder zu schließen. Jedes Tool-Fenster hat die Schaltfläche X (siehe Abbildung 2.7), die dem üblichen Schließen-Symbol anderer Fenster gleicht. Durch das Anklicken dieser Schaltfläche wird das Tool-Fenster geschlossen und vom Bildschirm entfernt.
Abbildung 2.7: Tool-Fenster können mit der kleinen X-Schaltfläche geschlossen werden. Dann bleiben sie verborgen, bis Sie sie wieder benötigen.
Um das Fenster zurückzuholen, müssen Sie eine Menüoption verwenden. Wenn Sie z.B. die X-Schaltfläche des Projektmappen-Explorers anklicken, verschwindet er. Um ihn zurückzuholen, müssen Sie im Menü ANSICHT den Punkt PROJEKTMAPPEN-EXPLORER auswählen (oder (Strg)+(R) drücken). Das ist nicht sehr schwierig, besonders für ein Fenster, das Sie nicht häufig benötigen. Aber was ist mit den Fenstern, die Sie vielleicht im Moment nicht benötigen, aber ansonsten häufig benutzen? Die Visual Studio .NET-IDE bietet Ihnen eine Möglichkeit, mit der Sie den Bildschirmplatz, der von diesen Fenstern beansprucht wird, verfügbar machen und trotzdem leicht wieder auf die Fenster zugreifen
66
Die Visual Studio-IDE
können – die Funktion AUTOMATISCH IM HINTERGRUND. Jedes angedockte Tool-Fenster hat ein Symbol in seiner Titelleiste, das aussieht wie eine kleine Pinwand-Nadel (siehe Abbildung 2.8).
Abbildung 2.8: Jedes Tool-Fenster hat eine kleine PinwandNadel, mit der Sie das Fenster an einer freien Position verankern können.
Dies ist eine Kippschaltfläche, die steuert, ob das Fenster automatisch verborgen werden soll, wenn es nicht verwendet wird. Standardmäßig ist das Server-Explorer-Fenster so eingerichtet, dass es diese Funktion verwendet. Er erscheint daher nur als graue Registerkarte oder Schaltfläche an der linken Seite der IDE. Wenn Sie den Mauszeiger über die Registerkarte bewegen, wird der volle Server-Explorer sichtbar und erscheint in der Ansicht. Jedes Tool-Fenster, bei dem die Option AUTOMATISCH IM HINTERGRUND aktiviert ist, wird automatisch zu einer Registerkarte entlang der Seite der IDE minimiert, an die es angedockt ist. Das verborgene Fenster wird immer dann angezeigt, wenn der Anwender die Maus über die Registerkarte bewegt, was bedeutet, dass das Fenster nur dann Platz beansprucht, wenn es benötigt wird. Mit dieser Funktion und der Option ALLE AUTOMATISCH AUSBLENDEN des Menüs FENSTER habe ich Visual Studio nach meinen Wünschen konfiguriert (siehe Abbildung 2.9). Auf diese Art und Weise haben Sie den größtmöglichen Platz für das Hauptfenster, in dem der Code erscheint, wenn Sie arbeiten. Eine solch extreme Benutzeroberfläche mag unnötig sein, wenn man an einem großen Monitor arbeitet, aber als Laptop-Benutzer finde ich sie perfekt. Registerkarten Mit einer weiteren platzsparenden Funktion können mehrere Tool-Fenster in einem einzelnen Bereich des Bildschirms zusammengefasst werden, wo sie automatisch zu einzelnen Registerkarten in einem Fenster mit mehreren Registerkarten werden (siehe Abbildung 2.10).
67
Mit Visual Basic .NET arbeiten
Abbildung 2.9: Mit AUTOMATISCH AUSBLENDEN können Sie Ihren zentralen Arbeitsbereich maximieren, in dem Sie sowohl Code als auch visuelle Objekte bearbeiten werden.
Im Profil VISUAL BASIC DEVELOPER wurden bereits verschiedene Fenster miteinander kombiniert (der Projektmappen-Explorer teilt sich den Platz mit dem Fenster KLASSENANSICHT und die Fenster EIGENSCHAFTEN und DYNAMISCHE HILFE sind ähnlich konfiguriert), aber jede Kombination von Tool-Fenstern ist möglich.
Abbildung 2.10: Mit Registerkarten (unten links) können Sie viele verschiedene ToolFenster geöffnet haben, ohne deswegen mehr von Ihrem wertvollen Bildschirmplatz zu belegen.
68
Die Visual Studio-IDE
Um ein Fenster zu einer Gruppe von Registerkarten hinzuzufügen oder mit Registerkarten zu erzeugen, ziehen Sie einfach ein Tool-Fenster (indem Sie auf seine Titelleiste klicken und ziehen) auf ein anderes und lassen die Maustaste los, wenn sich der Umriss ändert und ein Fenster mit Registerkarten angezeigt wird. Dann wird eine kleine Registerkartenerweiterung unten an dem Umriss sichtbar. Das Entfernen eines Fensters erfolgt ähnlich. Ziehen Sie einfach eine der Registerkarten von der Gruppe weg, bis der Umriss die Registerkarte nicht mehr anzeigt, ziehen Sie sie dann an den gewünschten neuen Platz und lassen Sie die Maustaste los. Skalieren Jedes Tool-Fenster kann skaliert werden. Wenn das Fenster angedockt ist, kann es nur entlang seiner nicht verankerten Ränder skaliert werden. Um ein Fenster zu skalieren, bewegen Sie den Mauszeiger über einen Rand des Fensters, bis sich der Zeiger in ein Anzeigeelement verwandelt, das die zulässigen Richtungen für das Skalieren anzeigt. Wenn der Zeiger anzeigt, dass sich die Maus in der richtigen Position befindet, klicken Sie und ziehen den Rahmen des Fensters auf die gewünschte Größe. Beachten Sie, dass das Skalieren zwischen zwei angedockten Fenstern beide Fenster ändert, da das eine schrumpfen muss, um dem anderen Platz zum Ausdehnen zu geben.
Werkzeugsammlung Eines der häufig verwendeten Fenster, die Werkzeugsammlung, enthält eine Reihe von verschiedenen Codestücken, Benutzeroberflächenelementen und anderen Elementen, die Sie in Ihre Projekte einfügen können. Die Auswahl der angezeigten Elemente hängt davon ab, was derzeit im Hauptfenster der IDE bearbeitet wird. Wenn z.B. nichts oder nur der Webbrowser im Hauptfenster ausgewählt ist, ist das einzige verfügbare Element in der Werkzeugsammlung das Zeigersymbol. Mit diesem Symbol, das immer vorhanden ist, können Sie die Auswahl eines jeden anderen Elements im Werkzeugsammlungsfenster aufheben. Wenn Sie z.B. den HTML-Code der Visual Studio-Startseite bearbeiten (klicken Sie mit rechts in den Webbrowser und wählen Sie QUELLDATEI ANZEIGEN aus), werden zusätzliche Registerkarten im Werkzeugsammlungsfenster hinzugefügt. Im Fall der HTML-Bearbeitung wurde eine HTML-Registerkarte hinzugefügt (siehe Abbildung 2.11), die verschiedene Elemente enthält, die unterschiedliche HTML-Tags darstellen. Jedes Element mit Ausnahme des Zeigerelements kann auf zwei Arten verwendet werden: 쐽
Klicken Sie das Element an und ziehen Sie es in das Bearbeitungsfenster (lassen Sie los, wenn Sie die Maus an die gewünschte Position bewegt haben).
쐽
Klicken Sie das Element doppelt an, wodurch das Element zum Bearbeitungsfenster am derzeit ausgewählten Einfügepunkt hinzugefügt wird (dort, wo sich der Cursor im Bearbeitungsfenster befand, als das Element doppelt angeklickt wurde).
69
Mit Visual Basic .NET arbeiten
Abbildung 2.11: Wenn Sie HTML bearbeiten, sind alle gängigen HTMLBenutzeroberflächenelemente über die Werkzeugsammlung verfügbar.
Diese beiden Verwendungen der Elemente stehen für jeden Dokumenttyp zur Verfügung, der bearbeitet wird. Ist aber das aktuelle Dokument eine grafische Benutzeroberfläche (also jedes visuelle Nicht-Textdokument wie z.B. ein Windows-Formular), dann verhalten sich diese beiden Optionen etwas unterschiedlich. Außerdem steht eine dritte Option zur Verfügung: 쐽
Das Anklicken und Ziehen des Elements in das visuelle Dokument funktioniert genauso wie bei Text, aber statt eines Cursor-Einfügepunktes sehen Sie den tatsächlichen Umriss des Elements, während Sie die Maus über das Dokument bewegen.
쐽
Das Doppelt-Anklicken funktioniert ebenfalls, aber da ein visuelles Dokument nicht immer einen aktuell ausgewählten Einfügepunkt hat, wird das neue Element im Allgemeinen einfach im Zentrum des Dokuments erzeugt.
쐽
Die dritte Option, die bei der Textbearbeitung nicht zur Verfügung steht, besteht darin, das Element durch einmaliges Anklicken auszuwählen, wodurch es in der Werkzeugsammlung hervorgehoben wird, es dann anzuklicken und auf das visuelle Dokument zu ziehen. Die Größe und der Ort, an dem das Element zum Dokument hinzugefügt werden soll, werden umrissen und das neue Element wird entsprechend erzeugt (siehe Abbildung 2.12). Die obige Beschreibung ist ein wenig vage, deshalb werden Sie sich vielleicht fragen, ob die Verwendung der Werkzeugsammlung schwieriger ist, als es scheint. Nun, diese Verschwommenheit liegt in der Natur der Visual StudioIDE, die einer großen Palette an Programmiersprachen dienen soll, von denen heute noch nicht einmal alle zur Verfügung stehen.
70
Die Visual Studio-IDE
Abbildung 2.12: Sie können visuelle Elemente wie Textfelder in ein Formular malen, nachdem Sie sie in der Werkzeugsammlung ausgewählt haben.
Mit diesem Gedanken im Hinterkopf ist das genaue Verhalten der IDE (oder jedes ihrer Teile, wie etwa der Werkzeugsammlung) schwer zu beschreiben. Sie können sich aber darauf verlassen, dass die IDE unabhängig von der verwendeten Sprache immer in der allgemeinen Form funktionieren wird, die oben beschrieben wurde. Weiter unten in der heutigen Lektion werde ich eine Beispielanwendung der Werkzeugsammlung mit einem visuellen Dokument genauer erläutern, wenn Sie ein Windows-Formular als Teil Ihrer ersten Anwendung erstellen, die die IDE benutzt. Zusätzlich zu den vorhandenen Elementen für die HTML-Bearbeitung, Formularentwicklung und viele andere Arbeiten kann die Werkzeugsammlung jedes beliebige Textstück enthalten. Sie können also Ihre eigenen Elemente erzeugen, die Textabschnitte darstellen. Mit dieser nützlichen Funktion können Sie ein Textstück (in der Regel Code), das Sie voraussichtlich häufig verwenden werden, einfach verfügbar machen. Um dieses Meisterstück der Produktivität zu vollbringen, wählen Sie den gewünschten Text im Bearbeitungsfenster aus (wobei Sie den Text normalerweise erst eingeben müssen) und ziehen ihn in das Werkzeugsammlungsfenster. Die jeweilige Registerkarte gibt vor, wo Ihr neues Element erscheinen wird. Wie Sie in der Abbildung 2.13 sehen, wird das Element einen langweiligen und recht bedeutungslosen Standardnamen wie etwa »HTMLFragment« haben, aber Sie können ihn mit rechts anklicken und den Befehl ELEMENT UMBENENNEN auswählen, um eine vernünftige Beschreibung anzugeben. Voilà! Nun haben Sie ein neues benutzerdefiniertes Element in der Werkzeugsammlung, das Sie jederzeit benutzen können, indem Sie es einfach in das Bearbeitungsfenster ziehen.
71
Mit Visual Basic .NET arbeiten
Abbildung 2.13: Code, HTML oder andere Textschnipsel können in der Werkzeugsammlung platziert werden und dann (über das Ziehen in den Code und HTMLBearbeitungsfenster) wie jedes andere Steuerelement aus der Werkzeugsammlung verwendet werden.
Wie der Rest der IDE hat auch die Werkzeugsammlung viele zusätzliche Optionen, auf die ich nicht näher eingehen werde, so z.B. die Fähigkeit, mehr Registerkarten hinzuzufügen (Registerkarten sind die diaartigen Teile des Werkzeugsammlungsfensters), Registerkarten umzubenennen und die Ansicht einer jeden Symbolleiste der Abschnitte zu verändern. Auf diese anderen Funktionen können Sie über das Kontextmenü (erscheint bei Rechtsklick) der Werkzeugsammlung zugreifen und sie sind auch in den IDE-Hilfedateien dokumentiert.
Befehlsfenster und Ausgabefenster Führen Sie manchmal die Befehlskonsole (oder das DOS-Fenster, wie manche sie nennen) aus, um eine Aufgabe zu erledigen? Viele Entwickler können einige Aufgaben über die Tastatur und ein Befehlszeilen-Interface schneller erledigen, als wenn sie mit der Maus durch Symbole, Menüs und Dialogfenster navigieren. Da die Programmiererproduktivität das ultimative Ziel ist, lohnt es sich, jede Methode auszuprobieren, die schneller sein könnte. Visual Studio .NET enthält ein Fenster, das zwei Methoden umfasst, um über die Konsole mit der IDE zu interagieren. Die eine Methode, das Fenster AUSGABE, war eine Zeit lang Teil von Visual Basic und die andere, das BEFEHLSFENSTER, gibt es seit vielen Jahren in FoxPro und sie wurde schließlich zu Visual Studio hinzugefügt. Im Grunde sind dies eigentlich zwei Fenster, aber sie wurden kombiniert, um alles etwas verwirrender zu machen. Sie können sie sich als zwei Fenster vorstellen (ich werde das auf alle Fälle tun), sobald Sie zwei wichtige Dinge gelernt haben: wie der Modus des Fensters gewechselt wird (von Command zu Immediate und wieder zurück) und wie man feststellt,
72
Die Visual Studio-IDE
in welchem Modus das Fenster derzeit läuft. Das Wichtigste zuerst – machen wir das Fenster sichtbar. Wählen Sie ANSICHT, ANDERE FENSTER, BEFEHLSFENSTER aus dem Menü aus und das neue Fenster erscheint. Dieses Fenster, das nun den Titel BEFEHLSFENSTER trägt, sollte nur eine leere Zeile enthalten, vor der die Eingabeaufforderung > steht (fast wie die Befehlskonsole oder die DOSEingabeaufforderung, die Sie kennen). Dieses Fenster befindet sich nun im BefehlsModus, manchmal auch als Command-Modus bezeichnet. Sie können jeden beliebigen Befehl eingeben und ihn durch Drücken von (¢) ausführen. Um mit dem Fenster in den Unmittelbar-Modus (auch als Immediate-Modus bezeichnet) zu wechseln, geben Sie einfach an der Eingabeaufforderung den Befehl immed ein (und drücken (¢) oder (¢)). Nun befindet sich das Fenster im Immediate-Modus, der sich vom vorherigen Zustand durch den Zusatz -Unmittelbar in der Titelleiste des Fensters und das Fehlen der Eingabeaufforderung > im eigentlichen Fenstertext unterscheidet. Um zum Befehlsmodus zurückzukehren, geben Sie >cmd ein und drücken auf (¢) (ja, Sie müssen hierbei die Eingabeaufforderung > selbst eingeben). Da Sie nun wissen, wie man zwischen den beiden Modi hin- und zurückwechselt, können Sie sich mit dem Zweck und der Verwendung des jeweiligen Modus beschäftigen. Mit dem Befehlsmodus dieses Fensters können Sie die IDE mit eingegebenen Befehlen steuern – Sie geben z.B. Datei.NeueDatei auswählen ein. Ein Konsolen-Interface kann häufig schneller sein als eine grafische Benutzeroberfläche und diese doppelte Funktionalität kann die Arbeit innerhalb der IDE beschleunigen. Es stehen sehr viele Befehle zur Verfügung, und Sie finden sie am schnellsten, wenn Sie die Namen der sichtbaren Menüs durchgehen. Geben Sie einen Namen ein (wie Bearbeiten oder Datei), fügen Sie einen Punkt hinzu und schon erhalten Sie eine Dropdown-Liste der verfügbaren Befehle für diesen Menünamen. Hier sehen Sie eine kurze Liste mit Befehlen, die man kennen muss: Datei.NeuesProjekt Datei.Allespeichern Fenster.Alleautomatischausblenden
Mit dem Fenster AUSGABE können Sie Codeanweisungen sofort auswerten. Damit haben Sie die Möglichkeit, eine einzelne Codezeile einzugeben und sich das Ergebnis anzusehen, ohne ein ganzes Beispielprojekt erzeugen zu müssen. Diese Funktion steht im BreakModus zur Verfügung, d.h. wenn Sie die Ausführung eines Programms anhalten. Lassen Sie uns ein kleines Beispielprojekt erzeugen, bei dem Sie einen so genannten Haltepunkt verwenden, damit die Programmausführung an einer bestimmten Codezeile anhält. (Der Haltepunkt wird an Tag 6, Was zu tun ist, wenn ein gutes Programm ungezogen ist, und wie Sie das verhindern, näher erklärt, weil es eigentlich ein Debugging-Tool ist.) Sie werden für dieses Beispiel das BEFEHLSFENSTER benutzen, damit Sie ein Gefühl dafür bekommen, wie es Ihnen zukünftig nutzen kann. Stellen Sie sicher, dass das Befehlsfenster sichtbar ist und sich im Befehlsmodus befindet, indem Sie aus der Menüleiste ANSICHT, ANDERE FENSTER, BEFEHLSFENSTER auswählen. Das BEFEHLSFENSTER sollte nun sicht-
73
Mit Visual Basic .NET arbeiten
bar sein und die Eingabeaufforderung > sollte zeigen, dass es sich im Befehlsmodus befindet. Geben Sie den Befehl Datei.NeuesProjekt ein und drücken Sie auf (¢). Ein Dialogfenster erscheint, in dem Sie aufgefordert werden, ein neues Projekt zu erstellen. Wählen Sie aus der Liste auf der linken Seite den Ordner VISUAL BASIC-PROJEKTE aus und aus dem Kasten auf der rechten Seite den individuellen Projekttyp, der mit WINDOWSANWENDUNG bezeichnet ist. Klicken Sie auf OK, um das Dialogfenster zu schließen; dadurch wird ein neues leeres Projekt erzeugt. Das Projekt, das Sie erzeugt haben, enthält nur ein einzelnes Windows-Forms-Formular und Sie müssen noch selbst Code hinzufügen. Unter der Oberfläche hat Visual Basic aber bereits ein wenig Code in Ihr Formular eingefügt, und zwar für die Arbeit, die erforderlich ist, um ein neues leeres Formular zu erzeugen und zu initialisieren. Sie können diesen Code sehen, indem Sie das neue Formular (im zentralen Fenster der IDE) mit rechts anklicken und CODE ANZEIGEN auswählen. Dadurch wird im zentralen Fenster eine neue Registerkarte hinzugefügt und ausgewählt, die den Code anzeigt, der diesem Formular zugeordnet ist. Da Sie bisher nichts in das Formular eingegeben haben, ist der Code recht knapp, aber ausreichend für das Beispiel. Wählen Sie die Zeile Form1 = Me aus. Dabei müssen Sie eventuell nach unten scrollen, und gegebenenfalls die Unterebene zu Windows Form Designer generated Code anzeigen lassen. Nun sollten Sie diese Zeile als Haltepunkt markieren, damit die Codeausführung pausiert oder »anhält«, wenn diese Zeile erreicht ist. Sie haben drei verschiedene Möglichkeiten, diese Zeile zu markieren. Sie können entweder in den Rahmen klicken (der hellgraue Bereich an der linken Seite des Codefensters) oder die Codezeile mit rechts anklicken und HALTEPUNKT EINFÜGEN auswählen oder den Tastaturbefehl für diese Funktion nutzen und auf (F9) drücken. Verwenden Sie eine beliebige Methode, fügen Sie den Haltepunkt ein, und Sie werden sehen, dass neben der Zeile ein roter Punkt erscheint. Dieser Punkt symbolisiert den Haltepunkt. Sobald dieser Haltepunkt platziert ist, können Sie das Projekt ausführen, und die Ausführung wird pausieren, wenn sie diese Zeile erreicht. Wie bei dem Haltepunkt gibt es auch drei verschiedene Möglichkeiten, ein Projekt zu starten: Sie können die Schaltfläche der DEBUGGEN-Symbolleiste verwenden (die wie die Play-Taste auf einem CD-Spieler oder Videorecorder aussieht – siehe Abbildung 2.14). Sie können aber auch die Menüoption DEBUGGEN, STARTEN oder den Tastaturbefehl (F5) benutzen. Natürlich liegt es an Ihnen, welche Option Sie verwenden. Viele Programmierer finden, dass auf lange Sicht die Tastaturbefehle die einfachste Methode sind, um auf die gängigeren Funktionen zuzugreifen. Abbildung 2.14: Die Symbolleiste bietet Schaltflächen, um die Programmausführung mit Symbolen zu starten und anzuhalten, die wie die Tasten auf einem Videorecorder gestaltet sind.
74
Die Visual Studio-IDE
Wenn Sie das Programm starten, wird es schnell anhalten und die Codezeile anzeigen, die Sie als Haltepunkt markiert haben. Nun befinden Sie sich im Break-Modus, was durch [break] in der Titelleiste der Visual Studio-IDE angezeigt wird. Der gelbe Pfeil, den Sie im Rahmen des Codefensters sehen können, zeigt die Zeile an, die als Nächstes ausgeführt werden würde. An diesem Punkt können Sie mit dem BEFEHLSFENSTER in den Immediate-Modus wechseln und die Zeile ausprobieren. Wenn Ihr BEFEHLSFENSTER sichtbar war, bevor Sie das Projekt ausgeführt haben, sollte es immer noch vorhanden sein, obwohl sich das Layout leicht verändert haben kann, da bestimmte Fenster automatisch geöffnet werden, wenn Sie sich mitten in einem laufenden Projekt befinden. Wenn das BEFEHLSFENSTER nicht sichtbar ist, öffnen Sie es, indem Sie die Menüoptionen ANSICHT, ANDERE FENSTER, BEFEHLSFENSTER auswählen. Klicken Sie das Fenster an, um es auszuwählen (wodurch Sie es zum aktiven Fenster in der IDE machen), und geben Sie immed ein (gefolgt von der (¢)-Taste), um das Fenster in den Immediate-Modus zu bringen. Nun können Sie jede Visual Basic-Anweisung eingeben und sie wird sofort ausgeführt (daher der Name). Versuchen Sie nacheinander die folgenden Anweisungen: ? Me.Width Me.Width = 50 ? Me.Width ? 3 + 5 ? 3 = 5
Die Verwendung der Auf-/Ab-Tasten im BEFEHLSFENSTER bringt Sie nicht immer zu anderen Zeilen innerhalb des Fensters. Wenn Sie bereits angefangen haben, Text einzugeben, werden Sie statt dessen Befehle durchlaufen, die Sie bereits ausgeführt haben. Wenn Sie (in dem Fenster) eine vorherige Zeile auswählen und ihr Text hinzuzufügen beginnen, wird automatisch unten im Fenster eine Zeile mit Ihren neuen Änderungen erzeugt. Dadurch wird jeder Text vor der letzten Zeile des Fensters effektiv schreibgeschützt. Haben Sie das ? vor einigen der Anweisungen gesehen? Es bedeutet print und ohne dieses Zeichen würde Visual Basic nicht wissen, was es mit Anweisungen tun soll, die einen Wert zurückgeben. Zum Beispiel wird 3 + 5 als 8 ausgewertet, aber ohne die Ausgabeanweisung ist 8 kein zulässiger Visual Basic-Befehl. Andererseits sind Anweisungen wie Me.Width = Me.Width * 2 zulässige Visual Basic-Codestückchen und benötigen das ? am Anfang nicht. Wenn Sie (F5) drücken, damit die Ausführung des Codes über den Haltepunkt hinaus erfolgt, wird das Formular auf dem Bildschirm erscheinen, und zwar breiter als vorher, wenn Sie die Beispielanweisungen korrekt ausgeführt haben. Wie Sie sehen, ist es möglich, Teile Ihres Programms vom IMMEDIATE-Fenster aus zu beeinflussen, das dadurch zu einem hervorragenden Werkzeug zur Fehlerbehebung wird.
75
Mit Visual Basic .NET arbeiten
Dynamische Hilfe Dieses Werkzeugfenster ist als Registerkarte des EIGENSCHAFTEN-Fensters eingerichtet (wenn Sie die Profileinstellungen von VISUAL BASIC DEVELOPER verwenden) und bietet kontextbasierte Dokumentationsreferenzen zu Visual Studio-Hilfedateien. Statt darauf zu warten, dass Sie um Hilfe bitten, agiert dieses Werkzeugfenster vorausschauend, wenn Sie auf die Taste (F1) drücken oder etwas aus dem Hilfemenü auswählen: Je nach Ihrer aktuellen Auswahl oder Aufgabe bietet es eine Liste von verwandten Themen an. In der Abbildung 2.15 liefert das DYNAMISCHE HILFE-Werkzeugfenster eine Reihe von Hilfethemen über das HTML-Tag, das derzeit im Codebearbeitungsfenster ausgewählt ist. Zusätzlich zu direkt verwandten Hilfethemen zeigt das Fenster im Allgemeinen einen Link zu verschiedenen generischen Themen an. Dieses Werkzeugfenster bietet auch über die drei Schaltflächen der Symbolleiste einen direkten Link zu den Bereichen INHALT, INDEX und SUCHE der Hilfedokumentation.
Abbildung 2.15: Das Fenster DYNAMISCHE HILFE versucht, Ihnen hilfreiche Informationen anzuzeigen, bevor Sie danach fragen.
Server-Explorer Dieses Werkzeugfenster (siehe Abbildung 2.16) bietet eine Auflistung der beiden Hauptressourcen: Datenbanken und Server. Die erste Reihe von Ressourcen – DATENVERBINDUNGEN – stellt alle Verbindungen dar, die zwischen Ihrem Projekt und verschiedenen Datenbankservern eingerichtet wurden, und ermöglicht es Ihnen, diese Datenbanken zu untersuchen, um Tabellen, gespeicherte Prozeduren und andere nützliche Informationen zu betrachten.
76
Die Visual Studio-IDE
Abbildung 2.16: Der Server-Explorer bietet eine visuelle Methode, Ressourcen sowohl des lokalen Rechners als auch anderer Rechner zu betrachten und zu benutzen.
Die zweite Reihe der Informationen, SERVER, stellt alle Rechner dar, mit denen Sie sich verbinden können und die ein visuelles Interface zu den Ressourcen bieten, die sie Ihrem Programm zur Verfügung stellen können. Zu diesen Ressourcen gehören Leistungsindikatoren, Ereignisprotokolle, Nachrichtenschlangen und anderes, was mit diesem Werkzeugfenster ganz einfach gefunden werden kann. Der Tag 13, Mit Windows Forms eine Benutzeroberfläche erstellen, geht näher darauf ein, wie mit diesem Werkzeugfenster Serverressourcen gefunden und bearbeitet werden können.
Eigenschaften Die Visual Studio-IDE ermöglicht es Ihnen, mit vielen verschiedenen Elementen, Projekten, Lösungen, Formularen, Klassen und anderen Dingen zu arbeiten. Alle diese Dinge haben Attribute oder Eigenschaften. Die Eigenschaften sind Informationen, die das Element beschreiben, wie z.B. der Name eines Projekts. Eigenschaften werden zwar normalerweise automatisch auf Standardwerte gesetzt, aber dennoch benötigen Sie eine Möglichkeit, um sie zu modifizieren. Diese Funktionalität bietet das Fenster EIGENSCHAFTEN. Immer wenn ein Element in der IDE ausgewählt ist, werden die Attribute dieses Objekts in diesem Werkzeugfenster angezeigt (siehe Abbildung 2.17). Einige der Attribute sind zwar schreibgeschützt (sie können nicht unmittelbar verändert werden), aber selbst dann können Sie sie in diesem EIGENSCHAFTEN-Fenster anklicken und nach Bedarf ändern.
77
Mit Visual Basic .NET arbeiten
Abbildung 2.17: Wenn im Bearbeitungsfenster ein Objekt ausgewählt ist (in diesem Fall ein Textfeld in einem Formular), zeigt das EIGENSCHAFTEN-Fenster alle seine Attribute an.
Projektmappen-Explorer Der Projektmappen-Explorer ähnelt in vielerlei Hinsicht dem Windows Explorer, der unter Windows verwendet wird. Er ist ein Dateiverwaltungs-Interface innerhalb von Visual Studio .NET. In Visual Studio .NET kann der Code, den Sie erzeugen, auf verschiedenen Ebenen organisiert werden: Projektmappen (Solutions), Projekte (Projects) und Dateien (Files). Über das Fenster PROJEKTMAPPEN-EXPLORER können Sie alle Objekte, die gerade geöffnet sind, in ihren entsprechenden Gruppen oder Fenstern betrachten. Dieses Fenster enthält Projekte, d.h. einzelne Anwendungen, und Komponenten, wie z.B. das gesamte System, einschließlich der Komponenten, die sowohl auf dem Client als auch auf dem Server laufen. Innerhalb eines jeden PROJEKT-Fensters befinden sich alle Dateien des betreffenden Projekts (Klassen, Formulare und andere Elemente). Das PROJEKTMAPPEN-EXPLORER-Fenster bietet Ihnen neben der Ansicht der gerade geöffneten Elemente eine große Vielfalt an Funktionalität. Sie können über dieses Fenster 쐽
neue Dateien zu einem Projekt hinzufügen (klicken Sie das Projekt mit rechts an und wählen Sie ERSTELLEN aus),
쐽
Dateien entfernen (klicken Sie mit rechts auf die entsprechende Datei und wählen Sie LÖSCHEN aus),
쐽
ganze Projekte aus einer Gruppe entfernen oder sie der Gruppe hinzufügen.
78
Die Visual Studio-IDE
Klassenansicht Beim Projektmappen-Explorer habe ich bereits erklärt, dass an einem einzelnen Projekt oder einer Projektmappe viele verschiedene Dateien beteiligt sein können. Diese Dateien entsprechen häufig den erzeugten Klassen (wie einer Vehicle-Klasse oder einer AccountKlasse), aber es ist nicht gesagt, dass die Dateiorganisation der konzeptionellen Organisation der Klassen im Projekt ähneln muss. Mit dem Fenster KLASSENANSICHT (siehe Abbildung 2.18) können Sie die Objektstruktur Ihres Projekts betrachten und innerhalb Ihres Codes in dieser Ansicht navigieren.
Abbildung 2.18: Das Fenster KLASSENANSICHT zeigt die Organisation Ihres Projekts nach Objekten an, nicht nach seinen physischen Dateien. Es bietet direkten Zugriff auf die Interna dieser Objekte.
Innerhalb dieses Fensters können Sie die angezeigten Objekte ausdehnen oder zusammenklappen, um auf ihre verschiedenen Eigenschaften und Methoden zuzugreifen. Wenn Sie ein bestimmtes Element in der Klassenansicht doppelt anklicken, gelangen Sie zu der betreffenden Klasse, der Methode, dem Ereignis, der Eigenschaft oder der Prozedur. Wenn das angeklickte Element nicht in Ihrem Codes verfügbar ist, führt die Klassenansicht Sie zur Definition dieses Teils der Klasse innerhalb des Objektbrowsers (siehe nächster Abschnitt). Die Klassenansicht ist nützlich, um sich ein Projekt anhand der definierten Klassen anzusehen und die physischen Details der eigentlichen Dateien zu ignorieren.
Objektbrowser Die gesamte Programmierung in .NET basiert auf Objekten – Objekten, die Ihnen als Teil des .NET-Frameworks zur Verfügung gestellt werden, oder Objekten, die Sie oder andere Mitglieder Ihres Teams erzeugt haben. Alle diese Objekte haben Eigenschaften und
79
Mit Visual Basic .NET arbeiten
Methoden, über die Sie mit ihnen interagieren können, aber woher wissen Sie, was verfügbar ist? Der Objektbrowser soll Ihnen bei der Arbeit mit all diesen Objekten dadurch helfen, dass Sie einen Katalog verfügbarer Objekte durchblättern oder durchsuchen können. Dieser Katalog enthält die Objekte (Klassen), die von jeder referenzierten Klassenbibliothek bereitgestellt werden, sowie die Klassen, die in Ihrem eigenen Projekt enthalten sind. Der Objektbrowser ist zwar in gewisser Weise der Klassenansicht ähnlich, geht aber über ihre Funktionalität hinaus, indem er auch Objekte außerhalb Ihres Projekts einschließt. Dieses Fenster ist besonders als eine Art der Dokumentation oder Referenz nützlich, in der Sie Klassen innerhalb des .NET-Frameworks oder andere Klassenbibliotheken finden und die Einzelheiten dieser Klassen, wie z.B. ihre Eigenschaften und Methoden, betrachten können. In Abbildung 2.19 sehen Sie, wie man mit dem Objektbrowser den Inhalt der Bibliothek System.Data durchblättern kann, wodurch detaillierte Informationen bis hin zu den Parametern angezeigt werden, die für einen bestimmten Methodenaufruf erforderlich sind.
Abbildung 2.19: Mit dem Objektbrowser können Sie die Klassen, die vom .NET-Framework zur Verfügung gestellt werden, sowie alle anderen Bibliotheken durchgehen, die Sie erzeugt oder referenziert haben.
Sie können über das Dialogfenster OPTIONEN Ihre eigenen Schlüsselwörter zu der Liste der erkannten Token hinzufügen. Im Dialogteil UMGEBUNG\AUFGABENLISTE können Sie neue Token hinzufügen und Einstellungen für die Aufgabe angeben, die durch dieses Schlüsselwort angestoßen wird. Schlüsselwörter werden nur dann als Übereinstimmung angesehen, wenn sie in Kommentaren in Ihrem Code gefunden werden.
80
Die Visual Studio-IDE
Aufgabenliste In jedem Entwicklungsprojekt, sogar in fertigen, gibt es höchstwahrscheinlich viele Aufgaben, die noch ausstehen. Bei bestimmten Teilen des Programms muss vielleicht noch die Leistung verbessert werden. Bugs oder fehlende Funktionalität müssen eventuell noch repariert werden. Wenn die ausstehenden Aufgaben einem tatsächlichen Codebereich zugeordnet werden können, ist es unter Programmierern gängige Praxis, diesen Bereich mit Kommentaren zu versehen. Wenn die Programmierer konsequent in diesen Kommentaren Wörter wie TODO oder BUG einfügen, ist es einfacher, den Code nach solchen Schlüsselwörtern zu durchsuchen, um die entsprechenden Codestücke zu finden. Visual Studio .NET hat diesen Prozess formalisiert, indem es eine Aufgabenliste mit Referenzen auf diejenigen Abschnitte Ihres Codes bietet, die eines oder mehrere Schlüsselwörter wie TODO enthalten (Sie können aber jedes beliebige Schlüsselwort angeben). Jeder gefundene Kommentar wird dann in einer leicht zu verwendenden Liste aufgeführt, in der nicht nur der Kommentar selbst genannt ist, sondern auch die Datei und die Zeile, in der er gefunden wurde (siehe Abbildung 2.20).
Abbildung 2.20: Jeder Kommentar, der mit einem bestimmten Schlüsselwort ausgestattet ist, wird in einer Aufgabenliste angezeigt.
Wenn Sie eine Aufgabe doppelt anklicken, werden Sie sofort zum Code geführt, wo Sie an der noch ausstehenden Aufgabe arbeiten können. Zusätzlich zu dieser Funktionalität, die allein schon sehr nützlich ist, kann die Aufgabenliste noch eine Vielzahl anderer Aufgabentypen enthalten. Visual Studio fügt andere Aufgaben automatisch hinzu, wie z.B. Verweise auf Kompilierfehler oder andere bemerkenswerte Elemente. Sie können aber auch zwei andere Aufgabenarten zu dieser Liste hinzufügen: Code-Shortcuts und benutzerdefinierte Aufgaben.
81
Mit Visual Basic .NET arbeiten
Code-Shortcuts ähneln den kommentarbasierten Aufgaben, beziehen sich aber auf eine beliebige Codezeile. Sie benötigen kein besonderes Schlüsselwort. Um einen Code-Shortcut zur Aufgabenliste hinzuzufügen, klicken Sie mit rechts auf das Codebearbeitungsfenster und wählen den Menüpunkt VERKNÜPFUNG FÜR AUFGABENLISTE HINZUFÜGEN aus. Ihrer Aufgabenliste wird eine neue Aufgabe hinzugefügt, wobei die Beschriftung standardmäßig die ausgewählte Codezeile ist (obwohl Sie diese Beschreibung beliebig ändern können und das auch tun sollten). Sie können dann schnell zu dieser Zeile zurückkehren, indem Sie diese Aufgabe einfach doppelt anklicken. Ein Shortcut wird durch einen blauen Pfeil an der linken Seite des Codefensters neben der entsprechenden Codezeile kenntlich gemacht. Sie können ihn entfernen, indem Sie die Codezeile mit rechts anklicken und VERKNÜPFUNG FÜR AUFGABENLISTE ENTFERNEN auswählen, oder indem Sie das Element in der Aufgabenliste auswählen und es direkt löschen. Ein weiterer Aufgabentyp, den Sie erzeugen können, ist eine Benutzeraufgabe. Das ist eine Aufgabe, die ähnlich wie eine Standardaufgabe in Outlook nicht einem bestimmten Codestück zugeordnet ist. Eine Benutzeraufgabe wird hinzugefügt, indem Sie den Abschnitt KLICKEN SIE HIER, UM EINE NEUE AUFGABE HINZUZUFÜGEN der Aufgabenliste anklicken und die Einzelheiten ausfüllen. Beachten Sie, dass bei diesen Aufgaben, im Gegensatz zu anderen Aufgaben, die Datei-/Zeilenfelder nicht ausgefüllt werden und sie daher nur über das Feld BESCHREIBUNG verfügt. Wenn Sie zu einem bestimmten Codeabschnitt eine Bemerkung hinterlegen möchten, ist es nützlicher, einen Code-Shortcut zu erzeugen und die Priorität und Beschreibung so zu ändern, dass mehr Einzelheiten zum tatsächlichen Thema angegeben werden.
Lösungen und Projekte Wie bereits im Abschnitt »Projektmappen-Explorer« erläutert, können Sie Code auf mehreren Ebenen gruppieren. Die erste Ebene, die Projektmappe (Solution), stellt das Gesamtsystem dar, während die einzelnen Komponenten darin durch separate Projekte dargestellt werden. Bevor Sie in der Visual Studio-IDE programmieren können, müssen Sie die Lösung und mindestens ein Projekt einrichten. In diesem Abschnitt werden wir die Grundlagen der Codeorganisation, das Erzeugen von neuen Projekten und das Arbeiten mit bestehenden Projekten und Dateien vorstellen. Es folgt zuerst ein kurzer Überblick über diese Themen und in dem unmittelbar anschließenden Abschnitt können Sie Ihre Fähigkeiten üben, indem Sie eine vollständige Beispielanwendung erstellen.
Ein neues Projekt erstellen Es gibt verschiedene Methoden für die Erzeugung eines neuen Projekts, aber am gängigsten ist die Verwendung der Menüoption DATEI, NEU und PROJEKT. Diese Menüoptionen führen Sie zu einem Dialogfenster, das alle Projekttypen zeigt, die die IDE erzeugen kann
82
Die Visual Studio-IDE
(siehe Abbildung 2.21). Da die Visual Studio-IDE mit mehreren Sprachen arbeitet, zeigt das Dialogfenster Optionen, die auf den installierten Sprachen basieren, und kann eventuell anders als das Dialogfenster in der Abbildung 2.21 aussehen. Vorerst werden Sie Projekte anhand der Auswahlmöglichkeiten unter dem Ordner VISUAL BASIC PROJEKTE erstellen.
Abbildung 2.21: Visual Studio verfügt über ein Dialogfenster namens NEUES PROJEKT, mit dem neue Projekttypen hinzugefügt werden können, wenn Sie zusätzliche Schablonen oder Sprachen installieren.
Um eine Anwendung zu erstellen, die lokal auf Ihrem Rechner läuft und eine WindowsBenutzeroberfläche (mit Dialogfenstern und anderen Windows-Benutzeroberflächenelementen) verwendet, wählen Sie aus der Liste der Projekttypen WINDOWS-ANWENDUNG aus. Um den Erstellungsprozess zu vervollständigen, geben Sie einen Namen für Ihre neue Anwendung ein und ändern, wenn gewünscht, den vorgeschlagenen Pfad. Klicken Sie auf OK und Visual Studio erzeugt Ihr neues Projekt. Es ist gut, Projekten aussagekräftige Namen zu geben, auch wenn Sie nur experimentieren. Andernfalls werden Sie schnell eine ganze Reihe von Projekten mit den Namen WINDOWSAPPLICATION1, WINDOWSAPPLICATION2 usw. haben, wodurch es schwierig wird, irgendetwas wiederzufinden, an dem Sie gearbeitet haben.
Ein bestehendes Projekt öffnen Wenn Sie Visual Studio schließen, wird es Sie fragen, ob Sie Ihre Arbeit speichern möchten, und dann automatisch alle Fenster für Sie schließen. Wenn Sie zu einem früheren Projekt zurückkehren möchten, müssen Sie es in der IDE öffnen. Visual Studio bietet einige einfache Methoden, frühere Projekte zu öffnen. Ein Verfahren nutzt die Menüpunkte DATEI, ÖFFNEN, PROJEKT; ein anderes die Option PROJEKT ÖFFNEN der Visual
83
Mit Visual Basic .NET arbeiten
Studio-Startseite, der HTML-Seite, die die zuletzt bearbeiteten Projekte auflistet. Dort können Sie das zu öffnende Projekt anklicken oder sogar ein neues Projekt über einen zusätzlichen Link erzeugen. Durch das Öffnen eines neuen Projekts werden alle anderen derzeit geöffneten Projekte geschlossen, es sei denn, Sie verwenden die Menüoptionen DATEI, PROJEKT HINZUFÜGEN, wodurch ein neues oder bestehendes Projekt in die derzeit geöffnete Projektmappe eingefügt wird.
Dateien Lösungen und Projekte existieren fast ausschließlich aus organisatorischen Gründen. Der eigentliche Code liegt in einer oder mehreren einzelnen Dateien. Wenn Sie ein neues Projekt erstellen, werden normalerweise bestimmte Dateien für Sie erzeugt, wie etwa ein neues Windows Form (Form1.vb), wenn Sie eine neue Windows-Anwendung erstellen, oder ein neues Modul (Module1.vb) für eine Konsolenanwendung. Diese Dateien werden auf der Festplatte erzeugt und bestehen unabhängig von Ihrem Projekt, wodurch eine einzelne Datei nach Wunsch über mehrere Projekte hinweg gemeinsam genutzt werden kann.
Dateien zu einem Projekt hinzufügen Vielleicht möchten Sie zusätzlich zu den Dateien, die automatisch als Teil Ihres neuen Projekts erzeugt werden, noch Module, Klassen, Formulare oder andere Codedateitypen hinzufügen. Sie können sowohl über das Menü PROJEKT als auch über das Menü, das erscheint, wenn Sie das Projekt im Projektmappen-Explorer-Fenster mit der rechten Maustaste anklicken, beliebig viele Dateien hinzufügen. Unabhängig von den besonderen Menüoptionen, die Sie auswählen, führen alle Auswahlmöglichkeiten mit Ausnahme von VORHANDENES ELEMENT HINZUFÜGEN zum Dialogfenster NEUES ELEMENT HINZUFÜGEN (siehe Abbildung 2.22). Wenn Sie eine bestehende Datei von der Festplatte hinzufügen möchten, statt ein neues Element zu erzeugen, öffnet die Option VORHANDENES ELEMENT HINZUFÜGEN zu diesem Zweck ein Standardfenster zum Öffnen von Dateien.
Alles speichern Bei all diesen verschiedenen Gruppen ist es wichtig zu wissen, wie Sie Ihre Arbeit speichern, insbesondere wenn sie sich in mehr als einer Datei befindet. In der Visual StudioIDE gibt es dazu zwei unterschiedliche Befehle: SPEICHERN und ALLE SPEICHERN. Diese Befehle befinden sich im Menü DATEI und in der Symbolleiste. Mit dem Befehl SPEICHERN speichern Sie nur die derzeit ausgewählte Datei (die im Server-Explorer-Fenster ausgewählt ist) und mit dem Befehl ALLE SPEICHERN speichern Sie alle offenen Dateien, die verändert wurden.
84
Die Visual Studio-IDE
Abbildung 2.22: Ebenso wie das Dialogfenster NEUES PROJEKT ist auch das Interface zum Hinzufügen von neuen Projektelementen ausklappbar.
Wenn Sie wie ich immer fürchten, irgendein Teil Ihrer Arbeit könnte verloren gehen, wird Sie eine der IDE-Optionen besonders interessieren: Im Dialogfenster OPTIONEN (über die Menüpunkte EXTRAS, OPTIONEN) können Sie die Gruppe UMGEBUNG erweitern und die Elemente PROJEKTE UND PROJEKTMAPPEN auswählen, um eine Gruppe von drei Optionsschaltflächen unter der Überschrift BUILD- UND AUSFÜHRUNGSOPTIONEN zu sehen (siehe Abbildung 2.23). Diese Optionen steuern, ob die IDE jede veränderte Datei speichert, bevor sie mit der Ausführung eines Projekts beginnt. Dies ist eine wichtige Einstellung, da die IDE, wenn sie überhaupt jemals abstürzt, es wahrscheinlich dann tun wird, wenn Sie Ihren Code ausführen. Diese Option bietet eine einfache Methode, um sicherzustellen, dass alle Veränderungen jedes Mal gespeichert werden, bevor Sie Ihren Code ausführen.
Abbildung 2.23: Überprüfen Sie diese BUILD- UND AUSFÜHRUNGSOPTIONEN immer, wenn Sie einen anderen Computer benutzen, um zu vermeiden, dass Sie ein paar Stunden Arbeit am Code verlieren.
85
Mit Visual Basic .NET arbeiten
2.2
Ihre erste Windows-Anwendung
Nun, da Sie einige Grundlagen der Verwendung der Visual Studio-IDE gelernt haben, können Sie diese Informationen umsetzen, indem Sie eine Anwendung ohne viel Code schreiben. Diese Anwendung wird sich auf die Verwendung der IDE selbst konzentrieren, es wird also eine recht einfache Anwendung sein. Für dieses Beispiel werden Sie eine Windows-Anwendung erstellen (also eine, die die Windows-Benutzeroberfläche verwendet und lokal auf Ihrem Rechner läuft), in die der Benutzer zwei Zahlen eingeben kann. Die Anwendung wird die beiden Zahlen addieren und das Ergebnis ausgeben.
Das Projekt erstellen Mit den Menüoptionen DATEI, NEU, PROJEKT gelangen Sie zum Dialogfenster NEUES PROJEKT. Unter der Kategorie VISUAL BASIC wählen Sie das Symbol WINDOWS-ANWENDUNG aus und ändern den Projektnamen von WindowsApplication(x) – der durchnummerierte Standardname – in Adder. Das neue Projekt wird bereits ein Formular enthalten, womit Sie alles haben, was Sie benötigen, um mit der Anwendung zu beginnen. Visual Studio erzeugt automatisch einen PROJEKTMAPPEN-Ordner, um Ihr neues Projekt zu speichern. Nennen Sie bitte auch diesen Adder. Klicken Sie auf OK, nachdem Sie den korrekten Namen für das Projekt und die Solution eingegeben haben.
Die Benutzeroberfläche entwickeln Sie benötigen im Formular drei Textfelder und eine einzelne Schaltfläche. Die Position ist nicht wirklich wichtig, aber Ihre Benutzeroberfläche sollte so ähnlich wie in der Abbildung 2.24 aussehen. Klicken Sie im Projektmappen-Explorer FORM1 doppelt an, damit es im Entwicklerfenster im Zentrum der IDE erscheint. Wenn sich das Formular im DesignModus befindet, wählen Sie das Werkzeugsammlungsfenster aus oder öffnen es. Dieses Fenster, das alle verfügbaren Objekte zeigt, die in Ihrem Formular platziert werden können, enthält ein TextBox-Steuerelement und ein Button-Steuerelement. Um eines dieser Steuerelemente in Ihr Formular zu setzen, klicken Sie das Steuerelement an und ziehen es im Formular in Position. Wenn es sich im Formular befindet, wählen Sie das Steuerelement aus und bringen es mithilfe seiner Handles in die gewünschte Form und Größe. Spielen Sie mit dem Skalieren und Bewegen dieser Steuerelemente so lange herum, bis alle drei Textfelder und die einzelne Schaltfläche sich im Formular befinden und so aussehen wie das Beispiel in Abbildung 2.24. Wenn alles an Ort und Stelle ist, ändern Sie einige der Eigenschaften dieser Steuerelemente.
86
Ihre erste Windows-Anwendung
Abbildung 2.24: Ordnen Sie die drei Textfelder und die Schaltfläche auf Ihrem Formular ungefähr so an.
Wählen Sie das erste Textfeld aus (das dem oberen Rand des Formulars am nächsten ist) und zeigen Sie das EIGENSCHAFTEN-Fenster an (drücken Sie (F4) oder wählen Sie aus dem Menü ANSICHT die Option EIGENSCHAFTENFENSTER aus, wenn das Fenster nicht bereits sichtbar ist). Darin sind viele verschiedene Eigenschaften aufgelistet, aber Sie werden nur zwei davon ändern: 쐽
TEXT (in der Gruppe DARSTELLUNG). Stellt den Inhalt des Textfeldes dar. Löschen Sie den Inhalt dieser Eigenschaft, damit das Textfeld leer ist, wenn das Programm startet.
쐽
(NAME) (in der Gruppe ENTWURF). Im Code werden Sie auf das Textfeld mit dem Namen in dieser Eigenschaft verweisen, der standardmäßig ziemlich nichts sagend ist, also etwa Text2 oder Text1 lautet. Ändern Sie ihn z.B. in txtFirstValue.
Fahren Sie mit den anderen beiden Textfeldern fort und ändern Sie auch ihre Text-Eigenschaft in leer und ihre Namen in txtSecondValue und txtResult. Wählen Sie nun die Schaltfläche aus, um ihre Attribute im Fenster EIGENSCHAFTEN anzuzeigen. Auch bei diesem Objekt werden Sie nur zwei Werte ändern: (Name) in btnAdd und Text in Add. Zum Schluss werden Sie, nur weil Sie es können, eine der Eigenschaften des Formulars selbst ändern. Wählen Sie das Formular aus (klicken Sie das Formular an einer Stelle an, wo sich kein Objekt befindet) und scrollen Sie durch die Liste der Eigenschaften, um die Eigenschaft Text in der Gruppe Darstellung zu finden. Bei einem Formular stellt diese Eigenschaft die Überschrift dar (der Text, der in der Titelleiste des Formulars angezeigt wird), die Sie für das Beispiel auf Simple Adder setzen können.
87
Mit Visual Basic .NET arbeiten
Das Projekt ausführen Auch wenn Sie keinen Code eingegeben haben, kann das Projekt trotzdem so laufen, wie es jetzt ist. Mit der Visual Studio-IDE können Sie das Programm innerhalb der IDE ausführen, ohne eine tatsächlich ausführbare Datei zu erzeugen (z.B. eine .exe-Datei). Sie können während der Entwicklung den Code ablaufen lassen und vor allem eine Reihe von Fehlerbehebungsaufgaben durchführen, während das Programm läuft. Mehr Informationen zur Fehlerbehebung bei Anwendungen bekommen Sie an Tag 6. Vorerst ist es wichtig zu erkennen, dass es einen Unterschied zwischen der Ausführung des Codes in der IDE und dem Erzeugen einer wirklich ausführbaren Datei gibt. Das Erzeugen einer ausführbaren Datei oder anderer Arten von Ausgabedateien für Ihr Projekt wird Erstellen (Build) genannt und im nächsten Abschnitt erläutert. Um ein Projekt innerhalb der IDE auszuführen, wählen Sie aus dem Menü DEBUGGEN den Eintrag START aus oder drücken (F5) Sie können auch die Schaltfläche der Symbolleiste anklicken, die wie ein Pfeil nach rechts aussieht (oder wie die Play-Taste auf einem Videorecorder). Versuchen Sie dies nun mit dem Projekt Adder, das Sie bereits geöffnet haben, und Sie werden sehen, dass das erstellte Formular vor der Visual Studio-IDE erscheint. Ohne dass Sie eine einzige Codezeile geschrieben haben, sieht das Formular recht funktionell aus. Sie können es ziehen und minimieren – alles dank des zugrunde liegenden .NET-Frameworks und der IDE, mit der Sie eine Benutzeroberfläche erstellen können und die den Code erzeugt, um alles laufen zu lassen. Sie haben zwar noch keinen Code geschrieben, aber dennoch wurde schon recht viel Code von der IDE erzeugt, der ausgeführt wird, wenn das Projekt läuft.
Das Projekt erstellen Das Erstellen eines Projekts ist das Erzeugen einer ausführbaren Datei oder anderer Ausgabedateien. Bei einer Windows-Anwendung wie im obigen Beispiel bedeutet das, dass Sie Ihren Code in eine .exe-Datei kompilieren, die außerhalb der Visual Studio-IDE laufen kann. Das ist ein wesentlicher Schritt, wenn ein Projekt von anderen Personen als den Entwicklern genutzt werden soll, auch wenn Sie die .exe-Datei während der Entwicklung meistens umgehen können, indem Sie Ihr Projekt innerhalb der IDE laufen lassen.
Einstellungen für die Projekterstellung Um ein Projekt zu erstellen, wählen Sie ERSTELLEN aus dem Menü ERSTELLEN aus (nicht sehr kreative Menünamen, aber leicht zu verstehen), was anscheinend sehr wenig bewirkt. Ohne Ihnen viele Informationen zu geben, haben die Standardeinstellungen von ERSTELLEN die Datei Adder.exe für Sie erzeugt und im Unterverzeichnis bin Ihres Projektordners abgelegt. Wenn Sie den angezeigten Pfad bei der Anlage des Projekts nicht geändert
88
Ihre erste Windows-Anwendung
haben, sollte es sich in Eigene Dateien\VisualStudioProjects\Adder befinden und die ausführbare Datei in \bin\Adder.exe innerhalb dieses Verzeichnisses. Um alle diese Standardeinstellungen zu sehen und vielleicht zu ändern, klicken Sie Ihr Projekt im Fenster PROJEKTMAPPEN-EXPLORER mit der rechten Maustaste an und wählen Sie aus dem dann angezeigten Kontextmenü EIGENSCHAFTEN aus. Das EIGENSCHAFTEN-Dialogfenster für Ihr Projekt enthält zwar noch viele weitere Einstellungsmöglichkeiten, aber nur die für den Erstellungsprozess relevanten werden im Folgenden beschrieben. Unter ALLGEMEINE EIGENSCHAFTEN, ALLGEMEIN: 쐽
ASSEMBLYNAME. Dieser Wert liefert den ersten Teil des Dateinamens für Ihre Ausgabedatei. Im Fall unseres Beispiels ist es Adder, sodass Adder.exe erzeugt wird. Wenn Sie das in MyAdder umändern, wird bei der Erstellung MyAdder.exe erzeugt.
쐽
AUSGABETYP. Sagt der IDE, welcher Dateityp bei der Erstellung dieses Projekts erzeugt werden soll: eine .exe-Datei, wenn WINDOWS-ANWENDUNG oder KONSOLENANWENDUNG ausgewählt wurde, oder eine .dll-Datei, wenn KLASSENBIBLIOTHEK ausgewählt wurde.
쐽
STARTOBJEKT. Zeigt den Teil des Projekts an, der standardmäßig ausgeführt werden soll, wenn die Anwendung ausgeführt wird. Bei dem Beispiel sollte es Adder.Form1 sein, um anzuzeigen, dass das Formular automatisch ausgeführt werden soll. Beachten Sie, dass sich diese Eigenschaft ändert und am Ende falsch definiert sein kann, wenn Sie den AUSGABETYP der WINDOWS-ANWENDUNG – und sei es nur vorübergehend – in etwas anderes ändern.
Brechen Sie das gesamte Projekteigenschafts-Dialogfenster ab (indem Sie die Schaltfläche ABBRECHEN an der Unterseite des Formulars anklicken), wenn Sie das Gefühl haben, etwas geändert zu haben, von dem Sie nicht wissen, wie Sie es wieder reparieren. Alle ALLGEMEINEN EIGENSCHAFTEN und Version-Einstellungen werden in den Ausgabedateien gespeichert, in denen das Projekt erstellt wird, und sind ein sichtbarer Teil der Eigenschaftsinformationen für die fertige ausführbare Datei (siehe Abbildung 2.25). Während Sie Visual Basic lernen, sind die Informationen, die in dieses Dialogfenster eingegeben werden, vermutlich irrelevant. Wenn Sie aber eine Anwendung erstellen, die von Benutzern eingesetzt werden soll, sollten Sie über die Informationen auf dieser Seite sehr sorgfältig nachdenken. Stellen Sie zumindest sicher, dass Sie auf dieser Seite aussagekräftige Versionsnummern angegeben haben, da der Benutzer so prüfen kann, welche Version einer Anwendung er hat. Die Optionen unter ALLGEMEINE EIGENSCHAFTEN, ERSTELLEN enthalten trotz des Namens nur eine einzige Eigenschaft, die unmittelbar relevant für den Erstellungsprozess ist. Der Wert ANWENDUNGSSYMBOL bestimmt die Darstellung der endgültigen .exe-Datei in Windows und ermöglicht es Ihnen, jede gewünschte Symboldatei (.ico) auszuwählen.
89
Mit Visual Basic .NET arbeiten
Abbildung 2.25: Wenn Sie sich die Eigenschaften einer ausführbaren oder .dll-Datei ansehen, können Sie alle Informationen sehen, die angegeben wurden, bevor sie erstellt wurde.
Es gibt zwar noch andere Elemente, die sich auf die Erstellung auswirken, aber die letzten, die ich erwähnen werde, befinden sich unter dem Menüpunkt KONFIGURATIONSEIGENSCHAFTEN, ERSTELLEN. Dort finden Sie verschiedene Einstellungen, die sich auf das Debugging beziehen, ebenso wie AUSGABEPFAD-Einstellungen, die bestimmen, wo die ausführbaren oder die anderen erzeugten Dateien platziert werden.
Erstellungskonfiguration Oben im Eigenschaftsdialogfenster für das Projekt befindet sich eine Dropdown-Liste mit dem markierten Eintrag KONFIGURATION. Die Projektkonfigurationen sind eine nützliche Funktion der Visual Studio-IDE, mit der Sie mehr als eine Gruppe von Einstellungen für das gleiche Projekt erzeugen können. Standardmäßig werden zwei Konfigurationen angeboten (Release und Debug), die anzeigen sollen, ob Sie eine Ausgabe für Testzwecke (Debug) oder für eine tatsächliche Entwicklung (Release) erstellen. Die Einstellungen für diese Standardkonfigurationen sind ein gutes Beispiel für den Zweck der Projektkonfigurationen, die den Zustand von vielen Debugging-Funktionen einrichten und sogar für jede einen anderen Ausgabeort anlegen. Mit dem Konfigurations-Manager (siehe Abbildung 2.26) können Sie so viele verschiedene Gruppen von Einstellungen erzeugen, wie Sie möchten, oder auch eine bestehende Konfiguration entfernen. Im Moment werden Sie wohl die Einstellungen nicht verändern wollen und lieber die Release-Version der Ausgabe auswählen, wenn Sie das Projekt entwickeln, und die Debug-Version, wenn Sie testen.
90
Ihre erste Windows-Anwendung
Abbildung 2.26: Mit dem Configuration Manager können Sie verschiedene Konfigurationen für verschiedene Zwecke erstellen (Testen, Debugging, Benutzerannahme, Release usw.), von denen jede unterschiedliche Build-Einstellungen haben kann.
Eine ausführbare Datei erzeugen Am besten lernen Sie etwas über eine Funktion der IDE, wenn Sie sie anwenden. Das bedeutet, dass es nun an der Zeit ist, das Beispielprojekt zu erstellen und eine ausführbare Datei zu erzeugen. Klicken Sie im Menü ERSTELLEN auf die Option ERSTELLEN und Sie werden kurzzeitig Informationen im Ausgabefenster sehen (das sich unten auf Ihrem Bildschirm befindet, wenn Sie es nicht verschoben haben). Wenn alles gut geht und sich in dem Code, den Sie hinzugefügt oder verändert haben, keine Fehler befinden, erzeugt der Build-Prozess eine ausführbare Datei. Die Datei selbst, die den Namen Adder.exe trägt, wird im Unterverzeichnis bin Ihres Projektordners erzeugt (standardmäßig EigeneDateien\VisualStudioProjects\Adder\). Minimieren Sie die Visual Studio-IDE, verwenden Sie den Windows Explorer, um die Datei zu finden, und klicken Sie sie dann doppelt an, um Ihre neu erzeugte Windows-Anwendung auszuführen. Das Auftauchen des Formulars mit seinen verschiedenen Textfeldern und Schaltflächen zeigt Ihnen, dass das Programm läuft, und es läuft weiter, bis Sie das Formular schließen. Diese ausführbare Datei und das .NET-Framework sind alles, was der Anwender benötigt, damit er Ihr Programm ausführen kann.
Eigenen Code hinzufügen Bis zu diesem Punkt enthält das Beispielprojekt, an dem Sie gearbeitet haben, nur Code, der von der Visual Studio-IDE erzeugt wurde. Das war gut und schön, um ein Formular zu zeigen, erfüllte aber ansonsten keinen Zweck. Wie Sie wahrscheinlich bereits durch die Namen und das Layout des Formulars vermutet haben, wird diese Anwendung die Werte im ersten und zweiten Textfeld addieren und das Ergebnis im dritten und letzten Textfeld zeigen. Um das zu erreichen, müssen Sie zu dem Projekt Code hinzufügen, der ausgeführt wird, wenn der Anwender auf die Schaltfläche ADD klickt.
91
Mit Visual Basic .NET arbeiten
Die Verwendung der IDE macht daraus einen sehr direkten Prozess: Klicken Sie das Formular im Projektmappen-Explorer an, um sicherzustellen, dass die Entwurfsansicht sichtbar ist, und klicken Sie dann doppelt auf die Schaltfläche ADD. So gelangen Sie zur Codeansicht des Formulars und in eine Subroutine, die zur IDE hinzugefügt wurde. Einem Steuerelement wie dieser Schaltfläche oder den Textfeldern oder anderen Formularkomponenten können Ereignisse zugeordnet sein. Die meisten Ereignisse eines Steuerelements stellen Aktionen dar, die auf ihnen ausgeführt wurden, z.B. ein Klick, Doppelklick, eine Auswahl und die Aufhebung einer Auswahl. Es ist möglich, Prozeduren zu erzeugen, die ausgeführt werden, wenn eines dieser Ereignisse eintritt, und standardmäßig werden diese Prozeduren durch ihre Namen gekennzeichnet (der Steuerelementname btnAdd gefolgt von dem Ereignis Click). Es ist möglich, Ereignissen Prozeduren unabhängig von ihrem Namen zuzuordnen, aber in diesem Fall wird die Prozedur btnAdd_Click ausgeführt, wenn der Anwender auf die Schaltfläche klickt. Diese Funktionalität kann mit einer sehr nützlichen Funktion des .NET-Frameworks ausprobiert werden: der Klasse MessageBox. Diese Klasse ist Teil desjenigen Teils des .NET-Frameworks, der Formulare, Schaltflächen, Textfelder und andere Interface-Elemente zur Verfügung stellt, und ist daher für jedes Projekt verfügbar, in dem Sie solche Objekte verwenden. Die Klasse MessageBox ermöglicht es Ihnen, mit einer einzigen Codezeile eine Nachricht in einem Dialogfenster anzuzeigen: MessageBox.Show("Die Schaltfläche wurde gedrückt")
Dieser Code zeigt ein Dialogfenster wie das in der Abbildung 2.27 an. Da diese Klasse so einfach zu verwenden ist, ist sie perfekt als Test- oder Debugging-Werkzeug geeignet. Fügen Sie die Codezeile zur Subroutine btnAdd_Click hinzu und führen Sie das Projekt dann durch Drücken von (F5) aus. Sobald das Formular erscheint, klicken Sie auf die Schaltfläche. Durch jedes Klicken sollte das Nachrichtenfenster erscheinen und Ihnen zeigen, dass der Code in btnAdd_Click immer dann ausgeführt wird, wenn die Schaltfläche gedrückt wird. Abbildung 2.27: Die Klasse Message Box bietet eine einfache Methode, Informationen auf dem Bildschirm anzuzeigen, und wird häufig in der Debugging-/Testphase verwendet.
Da Sie nun gesehen haben, wie Code als Reaktion auf das Anklicken einer Schaltfläche ausgeführt wird, können Sie den echten Code für Ihr Projekt erstellen. Der Code muss zwei Werte addieren, um ein Ergebnis zu erzeugen. Dies klingt einfacher, als es in Wirk-
92
Zusammenfassung
lichkeit ist. Die Werte, die Sie haben möchten, sind der Inhalt der Textfelder, die über die Eigenschaft Text dieser Steuerelemente zur Verfügung stehen, aber bevor Sie sie in einer mathematischen Gleichung benutzen (sie addieren), müssen Sie sie von Zeichenketten (Text) in Zahlen konvertieren. Der folgende Code erreicht das, wenn Sie ihn an die Stelle des Aufrufs von MessageBox setzen, den Sie weiter oben hinzugefügt haben: txtResult.Text = (CInt(txtFirstValue.Text) _ + CInt(txtSecondValue.Text)).ToString
Dieser Code konvertiert den Inhalt der beiden Textfelder in Zahlen (in diesem Fall ganze Zahlen), addiert sie und konvertiert das Ergebnis dann wieder in eine Zeichenkette (Text), sodass es in das dritte Textfeld gesetzt werden kann. Man benötigt ein paar Schritte für etwas, das recht einfach klingt, wenn es zum ersten Mal beschrieben wird. Das Endergebnis mag ein wenig verwirrend erscheinen. Es wird alles klarer werden, wenn Sie mit Tag 3, Einführung in die Programmierung mit Visual Basic .NET, fortfahren
2.3
Zusammenfassung
Das Konzept einer IDE ist es, eine umfassende Umgebung zu bieten, in der Sie Ihre Programme schreiben, bearbeiten, ausführen und die Fehler beheben können. Die Visual Studio-IDE bietet alle diese Funktionen und noch mehr. In der heutigen Lektion haben wir die elementaren Funktionen der IDE und den Zweck jedes der Hauptfenster behandelt. Außerdem wurde die Erzeugung einer einfachen Windows-Anwendung vorgestellt. Von diesem Punkt an werden Sie die IDE häufig verwenden, auch wenn Sie nicht immer mit visuellen Elementen (wie Formularen und Steuerelementen) arbeiten werden. Daher werden Sie immer mehr über die IDE und ihre Funktionen lernen. Es mag jetzt noch ein wenig verwirrend und überwältigend erscheinen, aber die IDE ist das Tool, das Sie für Ihre Programmierung verwenden werden. Mit der Zeit werden Sie damit vertraut werden.
2.4 F
Fragen und Antworten
Muss ich die IDE verwenden oder kann ich bei einem Texteditor und der Befehlszeile bleiben? A
Bei Visual Basic .NET können Sie alles auch nur mit der Befehlszeile und einem Texteditor machen, aber die IDE bietet eine große Anzahl von Funktionen, um die Entwicklung leichter zu machen. IntelliSense, Anweisungsvervollständigung und die farbcodierte Bearbeitung machen die Erstellung von Code leichter und ohne die Debugging-Funktionen kommen Sie nur schwer aus.
93
Mit Visual Basic .NET arbeiten
F
Kann ich meine eigenen Funktionen zur Visual Studio-IDE hinzufügen? A
2.5
Aber sicher! Die IDE unterstützt die Anpassung über mehrere verschiedene Methoden, darunter auch Makros und Add-Ins. Ich werde die Anpassung der IDE in diesem Buch nicht behandeln. Sehen Sie sich fürs Erste einfach die Beispielmakros unter dem Menüpunkt EXTRAS, MAKROS, MAKRO-IDE an.
Workshop
Der Workshop soll Ihnen dabei helfen, mögliche Fragen vorherzusehen und das Gelernte zu wiederholen. Außerdem soll er Sie dazu anregen, über die praktische Umsetzung Ihres Wissens nachzudenken. Die Antworten und Lösungen finden Sie im Anhang A. 1. Wenn Sie alle Dateien sehen wollen, die zu Ihrem Projekt gehören, welches IDEFenster verwenden Sie dann? 2. Welcher ist der Standardspeicherort für neue Visual Studio-Projekte? 3. Wie können Sie ein Symbol für eine Anwendung auswählen, die Sie in Visual Studio erstellen? 4. Wenn sich das BEFEHLSFENSTER im Immediate-Modus befindet, wie können Sie dann in den Befehlsmodus wechseln und wieder zurückwechseln?
Übung Verwenden Sie die Klasse MessageBox wie in der heutigen Lektion, um Nachrichten zu anderen Ereignisprozeduren hinzuzufügen und zu sehen, wann sie aufgerufen werden. Versuchen Sie, txtResult aus der ersten Dropdown-Liste im Codebearbeitungsfenster und dann TextChanged aus dem zweiten Dropdown-Menü auszuwählen, um mit diesem Ereignis zu arbeiten.
94
Einführung in die Programmierung mit Visual Basic .NET
Einführung in die Programmierung mit Visual Basic .NET
Da Sie nun mit der Arbeit in der Visual Basic .NET-Entwicklungsumgebung vertraut sind, ist es an der Zeit, Code zu schreiben. Visual Basic .NET macht es zwar leicht, ein einfaches Programm ohne viel Code zu schreiben, aber jedes Programm, das einfacher als ein Demonstrationsprogramm ist, muss Informationen ausfindig machen und einfache Berechnungen und ähnliche Aufgaben erledigen können. Um Code zu schreiben, der diese Aufgaben bewältigt, müssen Sie Variablen gut verstehen. Wenn Sie wissen, wie Sie Variablen verwenden, und die Typen kennen, haben Sie den Grundstein für Ihr Verständnis von Visual Basic .NET gelegt. Ebenso wie Sie früher einmal einfaches Rechnen gelernt haben, müssen Sie nun einige der einfachen Operatoren kennen lernen, die Sie in Ihren Programmen verwenden können, um mit Zahlen und Zeichenkettenvariablen zu arbeiten.
Die Themen heute 쐽
Die Variablentypen, die Sie mit Visual Basic .NET erzeugen können.
쐽
Einige einfache Operatoren und Funktionen, die in Visual Basic .NET zur Verfügung stehen.
쐽
Die Grundlagen des Schreibens von Code in Visual Basic .NET, darunter auch, wie Prozeduren geschrieben werden.
3.1
Variablen und Zuweisungen
Variablen und Zuweisungen bilden den Kern einer jeden Programmiersprache. Variablen ermöglichen es Ihnen, Informationen für die spätere Verwendung zu speichern, und mit einer Zuweisung bekommen Sie Informationen in eine Variable hinein.
Was ist eine Variable? Eine Variable ist ein Behälter. Sie ist ein Ort, an dem Sie Informationen so lange speichern, bis Sie diese benötigen. Sie werden in Ihren Programmen Variablen verwenden, um temporäre Werte während der Berechnung zwischenzuspeichern, um Anwendereingaben zu speichern und um Informationen vorzubereiten, die dem Anwender später angezeigt werden.
96
Variablen und Zuweisungen
Verfügbare Variablentypen Bei Variablen ist es wie bei Hosen: Eine Größe reicht nicht für alle aus. Sie können zwar eine Variable erstellen und benutzen, die alles speichern kann, aber das ist nicht immer die beste Lösung. Man kann sich leicht vorstellen, dass eine Variable, die Zeichenketten enthält, etwas anderes tun muss, als eine Variable, die Zahlen speichern soll. Zusätzlich erfordern sogar unterschiedliche Zahlenarten unterschiedliche Variablentypen. Einige Zahlen wie 1 oder 5280 haben keine Dezimalstelle, aber 3,14159265358979 und 16,50 sehr wohl. Eine Variable, die zum Speichern einer Zahl mit einer Dezimalstelle erzeugt wurde, muss etwas Bestimmtes tun, um die Nachkommastelle zu verfolgen. Natürlich bedeutet das, dass Dezimalzahlen eventuell mehr Speicher benötigen. Immer wenn ein Computer oder ein Programm mehr Arbeit zu verrichten hat, wird mehr Speicher benötigt. Also ist es wichtig, nicht nur die Art der zu speichernden Informationen im Kopf zu behalten, sondern auch die Menge an Speicher, die der Computer benötigt, um die Variable zu verfolgen. Es gibt drei wichtige Variablentypen, die Sie mit Visual Basic .NET erzeugen können. Die erste Gruppe umfasst Variablen, die einfache Werte wie Zahlen oder Zeichenketten enthalten. Es gibt viele dieser Variablen und jede ist so entworfen, dass sie Werte verschiedener Größe speichern kann. Die zweite Kategorie sind die komplexen Variablen, zu denen einige Kombinationen von einfachen Variablen sowie auch Arrays und benutzerdefinierte Typen gehören. Arrays sind Variablen, die eine Reihe von anderen Variablen enthalten, und benutzerdefinierte Typen ermöglichen es dem Anwender, neue Variablentypen zu erzeugen. Die dritte Kategorie der Variablen sind die Objektvariablen. Benutzerdefinierte Typen (auch bekannt als Strukturen) und Objektvariablen werden am Tag 7, Mit Objekten arbeiten, behandelt. Heute konzentrieren wir uns auf die einfachen Variablen und Arrays.
Einfache Variablen Wie bereits beschrieben, »enthalten« die einfachen Variablentypen Werte wie Zahlen und Wörter. Nun werden Sie vielleicht denken, dass es nur zwei Variablentypen geben muss: Zahlen und Wörter. In Wirklichkeit gibt es aber einige unterschiedliche einfache Variablentypen – jede soll eine andere Größe oder einen anderen Typ von Zahlen oder Zeichenketten enthalten. Versuchen Sie, für jede Situation den besten Variablentyp zu verwenden. Manchmal benötigen Sie nur eine kleine Zahl, wenn Sie z.B. die Monate eines Jahres verfolgen. Bei anderen Gelegenheiten müssen Sie mit großen Zahlen mit vielen Dezimalstellen arbeiten, z.B. wenn Sie ein Programm schreiben, das technische oder wissenschaftliche Berechnungen durchführt.
97
Einführung in die Programmierung mit Visual Basic .NET
Die einfachen Variablen können in vier Untergruppen aufgeteilt werden. Die erste und größte Gruppe besteht aus den ganzen Zahlen, die keine Dezimalstellen haben. Die zweite Gruppe wird für Zahlen verwendet, die Dezimalstellen haben. Zeichenketten und Zeichen bilden die dritte Gruppe und die vierte Gruppe lässt sich am besten als »Sonstige« beschreiben. Sehen wir uns nun jede dieser Gruppen und ihre Verwendungsweise genauer an.
Integer-Variablen Die Integer-Variablen enthalten die bekannten ganzen Zahlen (d.h. Zahlen ohne Dezimalstellen). Integer gehören zu den gängigsten Variablen und zu denen, mit denen Computer am einfachsten umgehen können. Aus diesem Grund sollten sie Ihre erste Wahl als Variablentyp sein, wenn Sie mit Zahlen arbeiten müssen. Die Tabelle 3.1 zeigt eine Reihe verschiedener Integer-Variablen, die unterschiedlich große Zahlen speichern und unterschiedlich viel Speicher belegen. Die belegte Speichermenge wird in Bytes gemessen. (Ein Byte enthält acht Bits, was einfach nur eine nette Umschreibung dafür ist, dass jedes Byte acht Einsen oder Nullen oder eine Kombination aus beidem hat.) Es schadet zwar nicht, eine Variable zu verwenden, die größere Werte als benötigt speichern soll, aber es verschwendet Speicher. Das kann dann dazu führen, dass Ihr Programm langsamer läuft, weil es größere Speichermengen verfolgen muss, auch wenn dieser Speicher niemals belegt wird.
Datentyp
Größe (Bytes)
Wertebereich
Kommentar
Byte
1
0 bis 255
Der kleine Typ Byte unterstützt im Gegensatz zu den anderen Integer-Datentypen keine negativen Zahlen. Das liegt daran, dass Byte den Wert darstellt, den der Computer tatsächlich für jedes Byte im Speicher speichert. Um negative Zahlen zu speichern, verwendet der Computer einen Teil jedes Bytes und speichert den »negativen« Anteil. Byte ist nützlich, wenn man kleine Zahlen verfolgt, die niemals negativ sind, wie z.B. die Tage in einem Monat oder Monate in einem Jahr.
Tabelle 3.1: Integer-Variablentypen
98
Variablen und Zuweisungen
Datentyp
Größe (Bytes)
Wertebereich
Kommentar
Short
2
-32.768 bis 32.767
Eine praktische, kleine Integer-Variable. Sie können Short immer dann verwenden, wenn Sie nicht den vollen Wertebereich eines Integers benötigen, z.B. wenn Sie ein Programm schreiben, um die Anzahl der Angestellten in einem Unternehmen zu zählen, das nur ein paar Tausend Angestellte hat.
Integer
4
-2.147.483.648 bis 2.147.483.647
Die Standard-Integervariable. Meist ist Integer der schnellste Variablentyp, da er dem Computer am wenigsten Arbeit macht. Mit einer Variable dieses Typs könnten Sie z.B. die Anzahl der Schafe in Neuseeland verfolgen (im Jahr 1997 ungefähr 47.394.000).
Long
8
-9.223.372.036.854.775.808 bis 9.223.372.036.854.775.807
Der ideale Variablentyp, wenn Sie mit richtig großen Zahlen arbeiten (d.h. von -9 Trilliarden bis +9 Trilliarden, oder ±9 x 1018, für die, die es interessiert). Long wäre nützlich, wenn Sie die Anzahl der Sterne im Universum verfolgen wollten (schätzungsweise ungefähr 1022).
Tabelle 3.1: Integer-Variablentypen (Forts.)
Zahlen mit Dezimalstellen Ein großer Teil der Zahlenverarbeitung erfolgt ohne Dezimalstellen. Andere Berechnungen, besonders in Wissenschaft und Technik und in der Wirtschaft machen es aber erforderlich, dass Sie auch Dezimalwerte speichern. Die Tabelle 3.2 beschreibt die beiden wichtigsten Dezimalvariablentypen. Welchen Sie verwenden, hängt vom Grad der Genauigkeit ab, die Sie erzielen möchten, und nicht von der Größe der Werte, da sie beide recht große Zahlen speichern können. Falls Sie sich aus der Schule nicht mehr an die wissenschaftliche Notation erinnern: Die hochgestellte Zahl über der 10 gibt an, wie häufig Sie mit 10 multiplizieren (falls die Zahl positiv ist) oder durch 10 dividieren müssen (wenn die Zahl negativ ist). Also bedeutet 106, dass 10 sechsmal mit sich selbst multipliziert wird (1.000.000), und 10-6 ist 0,000001.
99
Einführung in die Programmierung mit Visual Basic .NET
Datentyp
Größe (Bytes)
Wertebereich
Kommentar
Single
4
-3,402823 x 1038 bis -1,401298 x 10-45 bei negativen Zahlen; 1,401298 x 10-45 bis 3,402823 x 1038 bei positiven Zahlen
Machen Sie sich nicht zu viele Gedanken über die Größe der Zahlen im Wertebereich. Single kann sehr große (oder sehr kleine) Zahlen verfolgen. Das wichtige Maß für eine Single-Variable ist die Genauigkeit. Der Name Single deutet an, dass dies der Variablentyp für Gleitkommazahlen mit einfacher Genauigkeit ist. Das ist der Computerausdruck für »Sie ist wirklich nur nützlich, um sieben wichtige Ziffern zu speichern.« Sehen Sie sich jede Zahl im Bereich an. Beachten Sie, dass jede davon über eine Ziffer vor dem Dezimalkomma, sechs danach sowie über den Exponenten (die Zahl über der 10) verfügt. Single ist zwar gut geeignet, große und kleine Zahlen zu speichern, aber dieser Variablentyp ist nicht so genau wie die anderen. Das kann zu Rundungsfehlern führen, wenn Sie viele Berechnungen mit sehr großen oder sehr kleinen Zahlen durchführen. Der Variablentyp Single wäre in einem Programm nützlich, in dem keine hohe Genauigkeit nötig ist.
Double
8
-1,79769313486231 x 10308 bis -4,94065645841247 x 10-324 bei negativen Zahlen; 4,94065645841247 x 10-324 bis 1,79769313486232 x 10308 bei positiven Zahlen
Double ist eine Gleitkommavariable mit doppelter Genauigkeit, kann somit doppelt so viele wichtige Ziffern wie Single, also 15 Dezimalstellen enthalten. Verwenden Sie Double immer dann, wenn Sie Berechnungen mit großen Zahlen durchführen oder wenn Sie die Rundungsfehler vermeiden möchten, die bei Single auftreten können. Beispielsweise können Sie damit Berechnungen in technischen oder wissenschaftlichen Anwendungen durchführen.
Tabelle 3.2: Dezimale Variablentypen
Zeichenketten und Zeichen Zahlen sind schön, wenn Sie Mengen oder Zeit beschreiben müssen, aber Sie müssen in der Programmierung auch häufig mit Wörtern umgehen. Visual Basic .NET bietet Ihnen Variablen zum Speichern von Zeichenketten: Char und String. Char dient dem Speichern eines einzelnen Zeichens (der Name bezieht sich auf das englische Wort für Zeichen,
100
Variablen und Zuweisungen
»character«), während String sehr lange Zeichenketten speichern kann. Die Tabelle 3.3 beschreibt die beiden Datentypen ausführlicher. Datentyp
Größe (Bytes)
Wertebereich
Kommentar
Char
2
ein Zeichen
Geeignet zum Speichern eines einzelnen Zeichens.
String
10 + 2 pro Zeichen
bis zu 2 Milliarden Zeichen
Verwenden Sie String, um den Roman zu speichern, den Sie schon immer schreiben wollten. Wenn Sie davon ausgehen, dass ein Wort durchschnittlich fünf Zeichen hat, und 250 Wörter pro Seite rechnen, kann eine einzige String-Variable 1,7 Millionen Textseiten speichern.
Tabelle 3.3: Zeichenketten-Variablentypen
Warum belegt jede Char-Variable und jedes Zeichen in einem String zwei Bytes? Immerhin gibt es im Deutschen nur 26 Zeichen plus Zahlen und Symbole. Dafür würde man keine zwei Bytes benötigen, mit denen man 65.536 mögliche Werte beschreiben kann. Denken Sie aber daran, dass nicht jede Sprache den gleichen Zeichensatz verwendet. Dieses Szenario entspricht dem einst beliebten ASCII (oder ANSI)-Zeichensatz. In ASCII entspricht ein Byte einem Zeichen und jeder Computer, der ASCII verwendete, positionierte immer die gleichen Zeichen an der gleichen Position in der Liste. Also war der ASCII-Wert 65 immer der Buchstabe A und das Symbol @ hatte den Wert 64. Wenn Sie das Gleiche für alle anderen Symbole machen wollten, die Menschen zum Schreiben verwenden, würden Sie aber mehr Zeichen benötigen. Um das zu erreichen, wurde ein neues System namens Unicode entwickelt. In Unicode wird jedes Zeichen durch zwei Bytes dargestellt. Das ermöglicht die Speicherung sowohl aller Zeichen der ASCII-Darstellung als auch der Zeichen für Russisch, Griechisch, Japanisch und Thai, für mathematische Symbole usw. In Unicode ist 65 immer noch der Buchstabe A. Das japanische Hiragana-Zeichen »No« wird als Zeichen 12398 dargestellt. Visual Basic .NET nimmt für alle Zeichen Unicode-Werte, sodass Char zwei Bytes verwendet und jedes Zeichen in einem String zwei Bytes zum notwendigen Speicher hinzufügt.
101
Einführung in die Programmierung mit Visual Basic .NET
Sonstige einfache Variablentypen Wenn Sie versuchen, Dinge zu kategorisieren, wird das niemals restlos funktionieren. Irgend etwas trotzt der Kategorisierung immer (stellen Sie sich nur den ersten Zoologen vor, der das Schnabeltier sah). Dementsprechend gibt es einige Variablen, die nicht ordentlich in die bereits beschriebenen Kategorien passen. In Visual Basic .NET gibt es zwei »sonstige« Variablentypen, Boolean und Date. Sie werden in der Tabelle 3.4 ausführlicher beschrieben. Datentyp
Größe (Bytes)
Wertebereich
Kommentar
Boolean
2
True oder False
Wenn der Typ nur True oder False enthält, warum besteht er dann aus zwei Bytes? Visual Basic verwendet traditionell 0 für False und -1 für True. Das Definieren dieser beiden Werte erfordert zwei Bytes (wie die kleine Integervariable Short).
Date
8
1. Januar 100 bis 31. Dezem- Die Variable Date kann die meisten ber 9999 Datumsangaben speichern, mit denen Sie zu tun haben werden (außer Sie sind Historiker oder Geologe). Sie kennt außerdem alle Regeln für Daten (wie das Hinzufügen eines Tages in Schaltjahren). Wenn Sie also zum Date-Variablenwert »28. Februar 2000« 1 hinzufügen, erhalten Sie »29. Februar 2000«. Wenn Sie aber das Gleiche für »28. Februar 2001« tun, erhalten Sie »1. März 2001«. Die einzig wirkliche Beschränkung für die Variable Date ist ihre Größe.
Tabelle 3.4: Andere einfache Variablentypen
Sie könnten auch einen anderen Datentyp wie etwa String verwenden, um Daten zu speichern, oder einen Integer, der die Anzahl der Tage nach einem bestimmten Datum angibt. Aber warum sollten Sie sich darum sorgen? Wir alle kennen die Probleme, zu denen das führen kann (vor allem nach all den Stromausfällen, Atomkraftwerksexplosionen und anderen kleinen Fehlern am 1. Januar 2000).
102
Variablen und Zuweisungen
Variablen deklarieren Da Sie nun die verfügbaren Variablentypen kennen, stellt sich die Frage, wie Sie sie in Ihren Programmen erzeugen? Die einfachste Methode ist das Schlüsselwort Dim (kurz für »Dimension«), gefolgt vom Namen der Variablen, dem Schlüsselwort As und schließlich dem Variablentyp: Dim iSomeNumber As Integer
Dies würde die neue Variable iSomeNumber erzeugen, die vier Bytes aufnimmt und eine Zahl in der Größenordnung von ± 2 Milliarden speichern kann. Hier sehen Sie noch einige weitere mögliche Variablendeklarationen: Dim sFirstName As String Dim dblGrossDomesticProduct As Double Dim bLearned As Boolean
Eine neue Funktion in Visual Basic .NET ist die Fähigkeit, einer Variablen bei der Erzeugung einen Wert zu geben. Sie weisen der neuen Variablen einfach in der gleichen Zeile mit der Dim-Anweisung einen Wert zu: Dim dtDateOfMagnaCartaSigning As Date = #June 15, 1215# Dim lPeopleOnEarth As Long = 6000000000
Wenn wir später den Gültigkeitsbereich vorstellen, werden wir uns noch einige andere Methoden für die Variablendeklaration ansehen.
Arrays Die Speicherfähigkeit einer Variablen ist in der Programmierung praktisch und sogar lebenswichtig. Sie müssen aber eventuell eine Reihe von verwandten Elementen innerhalb einer einzigen Variablen gemeinsam speichern. Wenn Sie z.B. ein Programm schreiben, um Schach zu spielen, müssten die Felder auf dem Brett als Sammlung verwandter Elemente dargestellt werden. Mit Arrays erzeugen Sie Variablen, die alle verwandten Elemente zusammen speichern. Für das Schachprogramm würden Sie wahrscheinlich das Schachbrett als Array von Positionen speichern, wobei jede Position die Art der Schachfigur an dieser Stelle speichert (oder keine). Wenn Sie keine Array-Variable verwendet hätten, müssten Sie 64 separate Variablen verwenden. Sie könnten auch eine Liste mit Zeichenketten verfolgen, z.B. von Schülern in einer Klasse. Immer wenn Sie eine Liste von Elementen speichern müssen, verwenden Sie ein Array. Wie beim Deklarieren von einfachen Variablen gibt es einige Unterschiede zwischen einfachen Variablendeklarationen und Array-Deklarationen, da Arrays Sammlungen von Variablen sind. Listing 3.1 zeigt drei mögliche Methoden, um Arrays zu deklarieren.
103
Einführung in die Programmierung mit Visual Basic .NET
Listing 3.1: Arrays deklarieren 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
'Einfache Deklaration Dim iValues(3) As Integer Dim dtDates() As Date Dim I As Integer For I = 1 To 3 iValues(I-1) = I Next 'Die Größe eines bestehenden Arrays ändern. ReDim dtDates(4) 'Fülle die Liste der Daten. dtDates(0)="6/15/1215" 'Unterzeichnung der Magna Charta dtDates(1)="8/28/1962" 'Martin Luther King Jr. hält seine Rede "I have a dream" dtDates(2)="7/20/1969" 'Apollo 11 landet auf dem Mond dtDates(3)="2/14/1946" 'ENIAC wird der Öffentlichkeit enthüllt 'Deklaration mit Initialisierung Dim sMonths() As String = { "Jan","Feb","Mar","Apr","May","Jun", _ "Jul","Aug","Sep","Oct","Nov","Dec"} 'Arrays verwenden Console.WriteLine("") Console.WriteLine("Second value in iValues = { 0} ", iValues(1)) Console.WriteLine("Third date in dtDates = { 0} ", dtDates(2)) Console.WriteLine("Eleventh month of the year = { 0} ", sMonths(10))
Im ersten Beispiel ist iValues als Array mit drei Elementen deklariert. Jedes Element im Array ist ein Integer. Das dürfte sinnvoll sein. Der möglicherweise verwirrende Teil liegt in der Art, wie Sie auf jedes Element des Arrays verweisen. Dies wird in der For...Next-Schleife in den Zeilen 5 bis 7 gezeigt. (Wir werden die For...Next-Schleife morgen behandeln.) Beachten Sie, dass die drei Elemente des Arrays eigentlich von 0 bis 2 durchnummeriert sind. Daher befindet sich das zweite Element des Arrays an der Position 1 und nicht an der Position 2. Es sollte an dieser Stelle angemerkt werden, dass es Computer im Gegensatz zu Menschen vorziehen, beim Zählen mit Null zu beginnen. Der Grund dafür bleibt am besten in ihren kleinen Silikonhirnen verborgen, aber wir sollten uns dessen bewusst sein, vor allem, wenn es um Arrays geht.
104
Variablen und Zuweisungen
Das zweite erzeugte Array, die Monate des Jahres, ist deklariert und initialisiert. Ebenso, wie Sie einfache Variablen bei der Deklaration initialisieren können, können Sie das auch mit Arrays tun. Im Fall von Arrays setzen Sie aber jedes der Array-Elemente in eine durch Kommata getrennte Liste, eingehüllt in geschweifte Klammern, wie Sie es in der Zeile 16 sehen. Auf diese Art wird ein Array mit zwölf Elementen erzeugt, wobei jedes Element eine Zeichenkette enthält. Denken Sie aber daran, dass die Elemente von 0 bis 11 durchnummeriert sind, wenn Sie auf ein Element verweisen möchten – also wäre sMonths(10) nicht »Oct« sondern »Nov«. Die letzte Deklaration ist die eines Arrays mit dynamischer Größe. Die Größe dieses Arrays kann später mit dem Schlüsselwort ReDim auf die richtige Größe geändert werden, wie Sie es in der Zeile 9 sehen. Genau wie bei den anderen Array-Typen werden die Elemente des dynamischen Arrays von 0 bis I minus 1 durchnummeriert. Wenn die Größe des Arrays eingerichtet ist, können Sie es wie jedes andere Array verwenden. Diese Form der Deklaration ist nützlich, wenn die Größe des Arrays vom Wert einer anderen Variablen abhängt, sodass sie zur Laufzeit nicht bekannt ist. Ich habe wiederholt angemerkt, dass alle Arrays in Visual Basic .NET mit 0 beginnen, da Visual Basic vorher nicht unbedingt auf diese Art gearbeitet hat. In Versionen von Visual Basic vor Visual Basic .NET konnten Sie die Deklaration Option Base 1 zu Beginn eines Moduls verwenden, um sicherzustellen, dass alle in diesem Modul erzeugten Arrays mit 1 beginnen. Alternativ konnten Sie beim Deklarieren von Arrays die öffnenden und schließenden Array-Elemente definieren, wie Sie es in der folgenden Deklaration sehen. Keine dieser Optionen steht für Arrays in Visual Basic .NET zur Verfügung. Daher ist die folgende Codezeile in Visual Basic .NET nicht gültig: Dim sngValues (15 To 51) As Single
Zuweisung Die Zuweisung wurde in Visual Basic .NET vereinfacht. Frühere Versionen von Visual Basic (Visual Basic 4.0 bis 6.0) verfügten über zwei verschiedene Methoden, um einer Variablen einen Wert zuzuweisen: eine für einfache Variablen (einschließlich Strukturen und Arrays) und eine für Objektvariablen. Zum Glück entschieden sich die Entwickler von Visual Basic .NET dafür, die separate Zuweisungsmethode für Objektvariablen zu entfernen und sich nur auf die Methode zu stützen, die für einfache Variablen verwendet wurde. Sie weisen Werte einer Variablen zu (entweder einer einfachen oder einer Objekt-
105
Einführung in die Programmierung mit Visual Basic .NET
variablen), indem Sie die Variable auf die linke Seite eines Gleichheitszeichens setzen und auf der rechten Seite den zuzuweisenden Wert angeben, wie Sie es im folgenden Code sehen: iSomeVar = 1234 oObjectVar = New Something()
In Visual Basic Version 4.0 bis 6.0 würden die gezeigten Zuweisungszeilen so aussehen: iSomeVar = 1234 Set oObjectVar = New Something
Die Regeln, wann Set zu verwenden ist, waren aber so verwirrend, dass Microsoft auf das Schlüsselwort Set verzichtete.
Konstanten Konstanten sind eine weitere Klasse von Werten, die Sie in Ihren Visual Basic .NET-Programmen verwenden können. Konstanten sind Werte, die sich nicht verändern, weder während der Lebensdauer Ihres Programms noch sonst irgendwann. Die Monate in einem Jahr, der Wert von Pi und der Datenbankserver, von dem Ihr Programm Daten erhält, sind z.B. Konstantenwerte. Wenn Sie einen Wert deklarieren, können Sie ihn als konstant definieren. Jeder Versuch, den Wert einer Konstanten zu ändern, wird als Fehler gemeldet, noch während Sie sich in der IDE befinden und noch bevor Sie versuchen, die Anwendung auszuführen. Konstanten werden mit einer der beiden folgenden Methoden deklariert: Const PI = 3.1415 As Double Const DSN As String = "Random"
Wenn der Typ einer Konstanten vorher in der Deklaration nicht beschrieben wurde, muss der Compiler den Werttyp verwenden, der am besten passt. Er wählt aber nicht immer den besten möglichen Typ aus. Im Allgemeinen wird Visual Basic .NET die folgenden Variablentypen erzeugen, wenn Sie Konstanten deklarieren und den Typ des Wertes nicht angeben:
106
왘
Long: Für jede nicht deklarierte ganze Zahl
왘
Double: Für jede nicht deklarierte Dezimalzahl. Hinweis: Wenn der Wert zu groß für Double ist, wird er abgeschnitten.
왘
String: Für jeden Zeichenwert
Vorschläge für Benennungsstandards
Sie können bei Konstanten den gewünschten Typ genau so deklarieren, wie Sie es bei der Variablendeklaration tun.
3.2
Vorschläge für Benennungsstandards
Es gibt viele mögliche Namen, die Sie für die Deklaration einer Variablen benutzen können. Das kann zu einem Problem führen, wenn Sie die Variable später sehen. Wenn Sie die Deklaration nicht sehen, haben Sie eventuell Schwierigkeiten, den Typ der Variablen zu erkennen. Ähnlich ist es, wenn Sie Code erben, den ein anderer Entwickler geschrieben hat. Sie brauchen dann eventuell einige Zeit, ehe Sie verstehen, wie dieser Programmierer seine Variablen benannt hat. Gemeinsame Namenskonventionen reduzieren diese Schwierigkeiten, indem sie den Variablentyp identifizieren. Eine häufig verwendete Namenskonvention besteht darin, den Variablennamen eine Vorsilbe in Kleinbuchstaben voranzustellen. Die Vorsilbe identifiziert den Variablentyp. Die Tabelle 3.5 zeigt eine der möglichen Gruppen von Vorsilben. Variablentyp
Vorsilbe
Beispiel
Byte
byt
bytAge
Short Integer
sht
shtCount
i oder int
iSheep
Long
l oder lng
lPopulation
Single
sng
sngThrust
Double
d oder dbl
dblInterest
Char
c
cMiddleInitial
String
s oder str
sName
Boolean
b
bIsOpen
Date
dt
dtHireDate
benutzerdefinierte Typen
zwei oder drei wichtige Zeichen aus dem Namen der Struktur
Erzeugte Variablen, die auf Point- und Rectangle-Strukturen basieren, sollten ptLocation und recSize genannt werden.
Konstanten
Keine Vorsilbe. Für den Namen werden nur Großbuchstaben verwendet, wobei die Wörter durch einen Unterstrich (_) getrennt werden. zwei oder drei bedeutungsvolle Zeichen
PI, TAX_RATE
Aufzählungen
dowWeekDay, colBackColor
Tabelle 3.5: Vorschlag für Namenskonventionen
107
Einführung in die Programmierung mit Visual Basic .NET
Warum dieser Mischmasch aus einfachen, doppelten und dreifachen Zeichen? Ich muss zugeben, dass sich die von mir verwendeten Vorsilben im Laufe der Jahre geändert haben. Meine Grundphilosophie war ursprünglich, nur ein einzelnes Zeichen zu verwenden, um die Verwirrung zu beschränken, die durch die Vorsilben hervorgerufen wird. Einige Vorsilben, die nur aus einem Buchstaben bestehen, würden aber nur zu noch mehr Verwirrung führen. Was wäre z.B. der Datentyp von sValue? Es könnte Short sein, aber auch Single oder String. Für Datentypen, die mit dem gleichen Buchstaben beginnen, habe ich die Vorsilbe auf mehrere Zeichen ausgedehnt. Ich muss aber zugeben, dass ich immer noch dazu neige, s für Zeichenketten zu verwenden, weil sie so häufig verwendet werden. Wenn Sie zum ersten Mal diese Vorsilben verwenden, werden Sie feststellen, dass sie ein wenig ablenken. Die Vorsilben werden aber schnell ganz selbstverständlich und die von ihnen gelieferten Informationen haben unschätzbaren Wert.
3.3
Einfache Berechnungen
Wenn Sie einfach nur ein paar Variablen deklariert und diesen Werte zugewiesen haben, so macht das noch kein Programm. Sie müssen mit den Werten etwas machen, z.B. mathematische Berechnungen durchführen oder etwas Bedeutenderes tun. Um Ihre Programme leichter verständlich zu machen, werden Sie häufig Prozeduren verwenden oder erzeugen müssen. Visual Basic .NET wird einige dieser Prozeduren zur Verfügung stellen, andere werden Sie selbst schreiben. Diese Prozeduren reichen von den Operatoren, die viele der gängigen mathematischen Berechnungen liefern, bis hin zu den komplexen Funktionen, die sich auf Zeichenketten oder Zahlen auswirken können.
Operatoren verwenden In Visual Basic .NET führen Operatoren einfache Berechnungen und ähnliche »Funktionen« durch. Die meisten der Operatoren in Visual Basic .NET sollten Ihnen als gängige algebraische Symbole geläufig sein. Einige von ihnen sind aber nur für die Programmierung da. Die Tabelle 3.6 listet die am häufigsten verwendeten Operatoren auf.
108
Einfache Berechnungen
Operator
Verwendung
Beispiel
=
Weist einen Wert einem anderen zu.
X = 6
+
Addiert zwei Werte.
Y = X + 7 (Y enthält 13)
-
Subtrahiert einen Wert von einem anderen.
Y = X – 4 (Y enthält 2)
*
Multipliziert zwei Werte.
Y = X * 2 (Y enthält 12)
/
Dividiert einen Wert durch einen anderen.
Y = X / 2 (Y enthält 3)
\
Dividiert einen Wert durch einen anderen, gibt aber nur eine ganze Zahl zurück.
Y = X \ 4 (Y enthält 1)
Mod
Kurz für Modulo; gibt den Rest einer Division zurück.
Y = X Mod 4 (Y enthält 2)
&
Kombiniert zwei Zeichenketten.
S = "Hello " & "World" (S ent-
hält »Hello World«) +=
Addiert einen Wert und weist das Ergebnis zu.
-=
Subtrahiert einen Wert und weist das Ergebnis zu. X -= 3 (X enthält 5)
*=
Multipliziert einen Wert und weist das Ergebnis zu.
X *= 6 (X enthält 30)
/=
Dividiert einen Wert und weist das Ergebnis zu.
X /= 5 (X enthält 6)
&=
Kombiniert mit einer Zeichenkette und weist das Ergebnis zu.
World, John«)
^
Erhebt einen Wert zum Exponent eines anderen Wertes.
X += 2 (X enthält 8)
S &= ", John" (S enthält »Hello 3^4 (3 hoch 4, gibt 81 zurück)
Tabelle 3.6: Gängige Operatoren in Visual Basic .NET
Eingebaute Funktionen Zusätzlich zu den Funktionen, die vom .NET-Framework zur Verfügung gestellt werden, verfügt Visual Basic .NET über viele eingebaute Funktionen. Diese Funktionen bieten viele nützliche Fähigkeiten, darunter auch die Umwandlung von einem Datentyp in einen anderen, mathematische Berechnungen, Zeichenkettenbearbeitung usw. Sie sollten einige dieser Funktionen kennen, um in der Lage zu sein, sich in Visual Basic .NET zurechtzufinden.
Umwandlungsfunktionen Einige der wichtigsten Funktionen, die Ihnen in Visual Basic zur Verfügung stehen, sind die Umwandlungsfunktionen. Sie ermöglichen es Ihnen, einen Datentyp in einen anderen umzuwandeln. Mit Visual Basic .NET wurden die Umwandlungsfunktionen sogar noch
109
Einführung in die Programmierung mit Visual Basic .NET
wichtiger, da diese Version von Visual Basic sogar noch strenger bei den Datentypen ist und nicht automatisch einen Typ in einen anderen importiert, wie es frühere Versionen taten. Wenn Visual Basic .NET automatisch Datentypen für Sie umwandeln soll, können Sie die strenge Typenüberprüfung ausschalten, indem Sie Option Strict Off im oberen Bereich Ihrer Dateien einfügen. Sie sollten aber wissen, dass dies zu unerwarteten Ergebnissen in Ihrem Code führen kann (damit meine ich Bugs), wenn Visual Basic .NET eine Variable umwandelt, ohne dass Sie es erwarten. Die Umwandlungsfunktionen in Visual Basic .NET beginnen alle mit dem Buchstaben »C« (wie im englischen Wort »conversion« – für Umwandlung) und enden mit einer abgekürzten Form des neuen Typs. Zusätzlich gibt es eine generische Umwandlungsfunktion namens CType, die in jeden Typ umwandeln kann. Die Tabelle 3.7 beschreibt die wesentlichen Umwandlungsfunktionen. Funktion
Beschreibung
CBool
Wandelt in einen Boolean um. Alles, was als False oder 0 ausgewertet wird, wird auf False gesetzt, alles andere ist True.
CByte
Wandelt in ein Byte um. Jeder Wert, der größer als 255 ist und alle Teilinformationen gehen verloren.
CChar
Konvertiert in ein einzelnes Zeichen. Wenn der Wert größer als 65.535 ist, geht er verloren. Wenn Sie einen String umwandeln, wird nur das erste Zeichen umgewandelt.
CDate
Wandelt in einen Date um. Als eine der mächtigeren Umwandlungsfunktionen kann CDate einige der häufig verwendeten Formate für die Eingabe von Datumsangaben erkennen.
CDbl
Wandelt in einen Double um.
CInt
Wandelt in einen Integer um. Brüche werden auf den nächsten Wert gerundet.
CLng
Wandelt in einen Long um. Brüche werden auf den nächsten Wert gerundet.
CSht
Wandelt in einen Short um. Brüche werden auf den nächsten Wert gerundet.
CStr
Wandelt in einen String um. Wenn der Wert ein Date ist, enthält dieser das kurze Datum-Format.
CType
Wandelt in jeden Typ um. Dies ist eine mächtige Funktion, die es Ihnen ermöglicht, jeden Datentyp in einen beliebigen anderen Typ umzuwandeln. Daher unterscheidet sich die Syntax für diese Funktion leicht von den anderen.
Tabelle 3.7: Umwandlungsfunktionen
110
Einfache Berechnungen
Die Syntax für CType ist: oNewVariable = CType(oOldVariable, NewType) oNewVariable und oOldVariable sind Platzhalter für die Variablen, in die und aus denen Sie konvertieren. NewType ist der Typ, in den Sie umwandeln. Dies kann jede Variable sein, die Sie nach dem As in einer Deklaration setzen, sodass Sie mit dieser Funktion in Aufzählungen, Strukturen und Objekttypen ebenso wie in einfache Typen umwandeln können.
Zeichenketten-Bearbeitungsfunktionen Die meisten der früheren zeichenkettenorientierten Funktionen von Visual Basic wurden in Visual Basic .NET durch die Funktionalität ersetzt, die für die Klasse String intern ist (diese Klasse werden wir uns am Tag 7 genauer ansehen). Sie werden aber möglicherweise einige der in Tabelle 3.8 aufgelisteten Funktionen in älterem Code wiederfinden. Daher sollten Sie mit diesen Funktionen vertraut sein. Funktion
Beschreibung
Beispiel
Len
Gibt die Länge einer Zeichenkette zurück.
enthält 5.)
iValue = Len("Hello") ('iValue
Chr
Gibt das Zeichen basierend auf dem ein- sValue = Chr(56) ('s Value enthält 56.) gegebenen ASCII- oder Unicode-Wert zurück.
Asc
Gibt den ASCII- oder Unicode-Wert zurück.
Left
Right
Gibt Zeichen von einer Zeichenkette zurück, beginnend mit dem am weitesten links gelegenen Zeichen. Auch die Anzahl der zurückzugebenden Zeichen muss angegeben werden. Gibt Zeichen von einer Zeichenkette zurück, beginnend mit dem am weitesten rechts gelegenen Zeichen (das Gegenteil von Left). Auch die Anzahl der zurückzugebenden Zeichen muss angegeben werden.
iValue = Asc("A") ('iValue enthält den
Buchstaben A.) sValue = Left("Hello World", 2) ('sValue
enthält He.)
sValue = Right("Hello World",4) ('sValue enthält orld.)
Tabelle 3.8: Funktionen zur Zeichenkettenverarbeitung
111
Einführung in die Programmierung mit Visual Basic .NET
Funktion
Beschreibung
Beispiel
Mid
Gibt Zeichen zurück, die nicht an einem Ende der Zeichenkette stehen. Mid gibt jede Anzahl von Zeichen zurück. Die Syntax für Mid ist sReturn = Mid(String, Start, Length), wobei Start das Zeichen ist, von dem aus begonnen werden soll und Length die Anzahl der zurückzugebenden Zeichen (von Start an) ist. Schön ist, dass alle Zeichen von Start an zurückgegeben werden, wenn Sie Length auslassen.
sValue = Mid("Hello World", 4, 5)) ('sValue enthält lo Wo.) sValue = Mid("Hello World", 7) ('sValue enthält World.)
Instr
Findet eine Zeichenkette in einer anderen. Das ist nützlich, wenn Sie in einer Datei nach einer Zeichenkette suchen. Die Syntax der Funktion Instr ist iRe-
iValue = Instr(1,"Hello World", "l")) ('iValue enthält 3.) Denken Sie daran,
turn = Instr(StartAtCharacter, SearchString, SearchFor, ComparisonType)
dass die Zeichenkette, nach der Sie suchen, auch aus mehreren Zeichen bestehen kann. Ein Suche nach »World« wie in iValue = Instr(1, "Hello World", "World") würde dann 7 ergeben ('iValue enthält 7.) StartAtCharacter ist die Position innerhalb der Suchzeichenkette SearchString, an der das Programm mit der Suche anfängt (beginnend mit 1). SearchString ist die Zeichenkette, in der gesucht werden soll und SearchFor ist die Zeichenkette, die gesucht wird. ComparisonType bestimmt, ob die Suche auf Groß- und Kleinschreibung achtet. Wenn ComparisonType auf 0 gesetzt ist (binärer Vergleich), achtet die Suche auf Groß-/Kleinschreibung. Wenn es ignoriert oder auf 1 gesetzt wird (Textvergleich), achtet die Suche nicht auf Groß-/Kleinschreibung. iReturn würde dann die Position innerhalb des SearchString enthalten, an der SearchFor beginnt. Wenn die Zeichenkette bei der Suche nicht gefunden wird, ist iReturn gleich 0.
Tabelle 3.8: Funktionen zur Zeichenkettenverarbeitung (Forts.)
112
Einfache Berechnungen
Funktion
Beschreibung
Beispiel InstrRev sucht von der rechten Seite der Zeichenkette an; andernfalls ist InstrRev identisch mit Instr. InstrRev ist nützlich,
wenn Sie nach einer Zeichenkette suchen, die einen Verzeichnispfad enthält und die unteren Verzeichnisse zuerst sehen möchten. iValue = InstrRev("Hello World", "o") 'iValue
enthält 8. LCase
Wandelt eine Zeichenkette in Kleinbuchstaben um.
sValue = LCase("Hello World") 'sValue enthält hello world
UCase
Wandelt eine Zeichenkette in Großbuchstaben um.
sValue = UCase("Hello World") 'sValue enthält HELLO WORLD
LTrim
Entfernt alle vorangestellten Leerstellen von einer Zeichenkette.
sValue = LTrim(" Hello World ") 'sValue enthält "Hello World "
RTrim
Entfernt alle angehängten Leerstellen von einer Zeichenkette.
sValue = RTrim(" 'sValue enthält "
Trim
Entfernt alle vorangestellten und angehängten Leerstellen von einer Zeichenkette.
sValue = Trim(" Hello World ") sValue enthält "Hello World"
Hello World ") Hello World"
Tabelle 3.8: Funktionen zur Zeichenkettenverarbeitung (Forts.)
Andere nützliche Funktionen Einige im Allgemeinen nützliche Funktionen passen nicht in die anderen Kategorien. Zu diesen gehören beispielsweise Funktionen, mit denen Sie den Typ einer Variablen bestimmen können, ebenso wie Bearbeitungsfunktionen für Datumsangaben. Die Tabelle 3.9 beschreibt einige dieser Funktionen. Funktion
Beschreibung
IsArray
Gibt True zurück, wenn der Parameter ein Array ist.
IsDate
Gibt True zurück, wenn der Parameter als Datum zu erkennen ist.
IsNumeric
Gibt True zurück, wenn der Parameter als Zahl zu erkennen ist.
IsObject
Gibt True zurück, wenn der Parameter ein Objekttyp ist.
TypeName
Gibt den Namen des Datentyps des Parameters zurück. Zum Beispiel würde TypeName(sName) den Wert String zurückgeben.
Tabelle 3.9: Verschiedene eingebaute Funktionen
113
Einführung in die Programmierung mit Visual Basic .NET
Funktion
Beschreibung
Now
Gibt das aktuelle Datum und die Uhrzeit zurück.
Today
Gibt das aktuelle Datum zurück, wobei die Zeit auf 0:00:00 (Mitternacht) gesetzt wird.
Tabelle 3.9: Verschiedene eingebaute Funktionen (Forts.)
3.4
Schreiben Sie Ihre eigenen Routinen
Die eingebauten Funktionen sind zwar recht nützlich, aber es wird immer wieder vorkommen, dass Sie eigene Routinen erstellen müssen. Vielleicht müssen Sie eine Reihe von eingebauten Funktionen verwenden, die auf die gleiche Art aufgerufen werden, oder vielleicht müssen Sie eine einzigartige Funktionalität erstellen. In beiden Fällen erleichtert es Ihnen Visual Basic .NET, Ihre eigenen Routinen zu schreiben. In Visual Basic .NET werden zwei Arten von Routinen verwendet. Die eine Art ist eine Routine, die etwas tut, aber keinen Wert zurückgibt. Diese werden Subroutinen genannt (oder kurz Sub). Der andere Typ tut etwas, gibt aber einen Wert zurück. Diese werden Funktionen genannt.
Subroutinen Eine Subroutine ist ein Block von Visual Basic .NET-Code, der Aufgaben ausführt – z.B. die Methode Console.WriteLine, die Sie in vielen Beispielen dieses Buches finden. Sie gibt Informationen auf dem Bildschirm aus, gibt aber nicht wirklich einen Wert zurück. Sie verwenden Subroutinen also, um in Ihren Programmen Aufgaben auszuführen. Im Allgemeinen sollten Sie immer dann darüber nachdenken, Code in eine Subroutine zu setzen, wenn Sie ihn mehr als einmal ausführen. Wenn Sie also Code haben, den Sie eventuell in mehreren Anwendungen verwenden, sollten Sie ihn auch in eine Subroutine setzen. Mit Subroutinen können Sie ein kleines Stück Ihres Programms isolieren, sodass Sie einfach mit einem Namen darauf verweisen, statt den ganzen Codeblock zu wiederholen. Das bedeutet nicht, dass die Subroutine immer genau die gleichen Schritte durchführen wird, aber sie erfüllt eine bestimmte Aufgabe. In einem Rezept kann z.B. stehen »Fügen Sie einen Teil Essig auf drei Teile Öl hinzu«, Einmal mischen Sie vielleicht eine Tasse Essig mit drei Tassen Öl und ein anderes Mal sind es vielleicht nur drei Esslöffel Essig und neun Esslöffel Öl. In beiden Fällen haben Sie die Subroutine CreateVinaigrette ausgeführt.
114
Schreiben Sie Ihre eigenen Routinen
Um Ihre eigenen Subroutinen zu erstellen, verwenden Sie das Schlüsselwort Sub: Sub SubRoutineName(Parameter1 As Type, Parameter2 As Type, ... ParameterN As Type) 'Tue hier irgendetwas. End Sub
In dieser Syntax definiert jeder Parameter einen Wert, der an die Routine übergeben wird. Listing 3.2 zeigt die Deklaration und Verwendung einer Subroutine.
Listing 3.2: Eine Subroutine erstellen und verwenden 1 Sub ShowMessage(ByVal Message As String) 2 Console.WriteLine(Message) 3 End Sub 4 ShowMessage("Hello World from Visual Basic .NET")
Ihre Subroutine beginnt mit dem Schlüsselwort Sub, wie in Zeile 1. Die Subroutine heißt ShowMessage und nimmt einen Parameter entgegen, wenn Sie aufgerufen wird. Die Subroutine endet mit dem Schlüsselwort End Sub (Zeile 3). Dazwischen befindet sich der eigentliche Code, der von der Subroutine ausgeführt wird. In diesem Fall zeigt sie einfach den Inhalt des Parameters im Konsolenfenster an. Zeile 4 zeigt eine Möglichkeit, die Subroutine aufzurufen, und übergibt ihr die Zeichenkette »Hello World from Visual Basic .NET«.
Funktionen Wenn Sie Ihre eigenen Funktionen erstellen, können Sie neue Fähigkeiten innerhalb Ihrer Anwendung erzeugen. Das Erstellen einer neuen Funktion gleicht der Definition einer neuen Subroutine, allerdings definieren Sie hier auch den Rückgabetyp. Innerhalb der Funktion können Sie den zurückzugebenden Wert definieren: Function FunctionName(Parameter1 As Type, ... ParameterN As Type) As ReturnType 'Mache hier irgendetwas. Return ReturnValue End Function
In dieser Syntax definiert jeder Parameter einen Wert, der an die Routine übergeben wird. ReturnType ist der Datentyp, den die Funktion zurückgibt, und ReturnValue ist der Wert, der von der Funktion zurückgegeben wird. Listing 3.3 zeigt die Deklaration und Verwendung einer einfachen Funktion.
115
Einführung in die Programmierung mit Visual Basic .NET
Listing 3.3: Eine Funktion erstellen und verwenden 1 Function Volume(ByVal Length As Integer, _ 2 ByVal Width As Integer, ByVal Height As Integer) As Integer 3 Return Length * Width * Height 4 End Function 5 6 Console.WriteLine(Volume(3,4,5))
Gültigkeitsbereich Der Gültigkeitsbereich ist einer dieser schönen Computerausdrücke und bedeutet: »Wer kann mich sonst noch sehen?« Formal definiert der Gültigkeitsbereich die Sichtbarkeit von Variablen innerhalb eines Programms, d.h., welche Routinen eine bestimmte Variable verwenden können. Sie möchten vielleicht nicht, dass alle Routinen auf alle Variablen zugreifen können. Wenn alle Routinen alle Variablen sehen können, kann das dazu führen, dass eine Routine »aus Versehen« den Wert einer Variablen ändert und so einen Bug in Ihrem Programm erzeugt. Bisher haben wir Variablen mit dem Schlüsselwort Dim innerhalb der Prozeduren deklariert. Sie können Variablen aber auch außerhalb von Prozeduren deklarieren, um die Variable für mehrere Prozeduren verfügbar zu machen. Wenn Sie das tun, können Sie zwei andere Schlüsselwörter verwenden, nämlich Public und Private: 쐽
Public-Variablen stehen in der ganzen Anwendung zur Verfügung. Hierbei handelt es sich um globale Variablen, die global existieren, also überall in der Anwendung. Public-Variablen sollten sparsam verwendet werden, sind aber nützlich, wenn Sie einen Wert benötigen, der an vielen Stellen in Ihrem Programm verwendet wird, wie z.B. die Verbindung zu einer Datenbank oder eine Datei.
쐽
Private-Variablen stehen innerhalb des Moduls oder der Klasse zur Verfügung, in der sie deklariert sind. Private-Variablen werden häufig in Anwendungen verwendet, in denen eine einzelne Variable in mehreren Prozeduren verwendet wird. Indem Sie die Variable mit dem Schlüsselwort Private erzeugen, erlauben Sie allen Prozeduren innerhalb eines Moduls oder einer Klasse, darauf zuzugreifen. Private Variablen sind nützlich, um häufig verwendete Informationen gemeinsam zu nutzen, die für eine Aufgabe erforderlich sind. So können z.B. verschiedene Funktionen auf einen Zwischenwert zugreifen, um eine Berechnung durchzuführen.
116
Schreiben Sie Ihre eigenen Routinen
Wenn Sie eine Variable erzeugen, sollten Sie sie so nahe wie möglich an der benötigten Stelle deklarieren. Wenn Sie eine Variable nur in einer Prozedur verwenden, sollten Sie die Variable in dieser Prozedur deklarieren. Verwenden Sie Private- und Public-Variablen der Modulebene sparsam.
Warum ist der Gültigkeitsbereich wichtig? Der Gültigkeitsbereich ermöglicht es Ihnen, die Daten zu isolieren, die von den Prozeduren Ihrer Anwendung verwendet werden. Viele ältere Versionen von BASIC hatten die Fähigkeit des Gültigkeitsbereiches nicht und alle Variablen waren aus allen Teilen des Programms heraus zugänglich und veränderbar. Stellen Sie sich vor, wie es damals war, ein Programm zu schreiben – oft verwenden Sie eine Variable an einer anderen Stelle im Programm wieder (absichtlich oder versehentlich). Das kann zu einem Bug führen, wenn Sie den Wert an einer Stelle im Programm geändert haben und dann später den veränderten Wert einlesen, obwohl Sie den ursprünglichen Wert erwartet haben.
Gültigkeitsbereich und Prozeduren Ebenso wie Variablen einen Gültigkeitsbereich haben können, haben auch Prozeduren (Subroutinen und Funktionen) einen Gültigkeitsbereich. Bei Prozeduren bedeutet der Gültigkeitsbereich das Gleiche wie bei Variablen: Er beschreibt, an welcher anderen Stelle in Ihrem Programm Sie die Prozedur verwenden können (oder auch außerhalb Ihres Programms, wie Sie sehen werden, wenn Sie mit dem Erstellen von Objekten beginnen). Der Gültigkeitsbereich für Prozeduren wird mit den gleichen Schlüsselwörtern definiert wie der Gültigkeitsbereich für Variablen. Im Allgemeinen hat der Gültigkeitsbereich hier auch die gleiche Bedeutung. 쐽
Public: Die Prozedur kann von jeder anderen Stelle in der Anwendung aus aufgerufen
werden. Dies ist die Standardeinstellung, wenn Sie kein anderes Schlüsselwort angeben. 쐽
Private: Die Prozedur kann nur von einer anderen Prozedur innerhalb des gleichen Moduls oder der gleichen Klasse aufgerufen werden, in der sie definiert ist. Das ist nützlich, wenn Sie eine Reihe von Unterstützungsroutinen schreiben, die in einer Berechnung verwendet werden, die andere Routinen aber nicht verwenden müssen.
Ebenso wie bei Variablen werden zusätzliche Gültigkeitsbereichschlüsselwörter angewendet, wenn Sie Objekte in Visual Basic .NET erstellen. Diese Schlüsselwörter werden wir uns später ansehen.
117
Einführung in die Programmierung mit Visual Basic .NET
3.5
Beispielanwendung: Einen Future Value berechnen
Da Sie sich mit dem Erstellen und Verwenden von Variablen und Prozeduren beschäftigt haben, können Sie nun eine Anwendung schreiben, die eine Investitionsrechnung durchführt. Mit dieser Anwendung können Sie die schönen Vorteile von regelmäßigen Investitionen und Zinseszins erforschen. Diese Beispielkonsolenanwendung umgeht die Komplexität, die eine grafische Benutzeroberfläche mit sich bringen würde. Listing 3.4 zeigt die Ausgabe des Programms. Hier sehen wir das Ergebnis, wenn wir mit einem Saldo von 10.000 Dollar beginnen und 30 Jahre lang pro Monat 200 Dollar einzahlen, bei einem konstanten Zinssatz von fünf Prozent.
Listing 3.4: Der Investitionsrechner 1 2 3 4 5 6 7 8 9 10 11
InvestCalc.exe Initial Balance: 10000 Annual Interest (e.g. for 5%, enter 5): 5 Monthly Deposit: 200 Years of Investment: 30 If you start with $10,000.00, and invest $200.00 per month for 30 years at 5% interest. Your final balance would be: $211,129.17
Der Anwender muss vier Werte in das Programm eingeben: Initial Balance (Anfangssaldo), Annual Interest (Jahreszins), Monthly Deposit (monatliche Einlagen) und Years of Investment (Anzahl Jahre). Dann berechnet das Programm den Abschlusssaldo. Diese Berechnung ist als Future Value (FV)-Berechnung bekannt und Visual Basic .NET enthält sie als eine der eingebauten Funktionen. Die Formel für den Future Value lautet: FV = MonthlyDeposit * (((1 + MonthlyInterest)^Months – 1 ) / MonthlyInterest ) + StartingBalance * ( 1 + MonthlyInterest )^Months
Die folgenden Schritte umreißen diese Prozedur, sodass Sie besser verstehen können, wie sie funktioniert:
118
Beispielanwendung: Einen Future Value berechnen
1. Beginnen Sie, indem Sie ein neues Projekt in Visual Basic .NET erstellen. Wählen Sie eine neue Visual Basic-KONSOLENANWENDUNG aus. Visual Basic .NET erzeugt ein neues Projekt mit einem Modul. 2. Schließen Sie das Dateifenster und benennen Sie die Datei mit dem ProjektmappenExplorer um. Klicken Sie den Dateinamen Module1.vb im Projektmappen-Explorer mit der rechten Maustaste an und wählen Sie den Befehl UMBENENNEN aus. Ändern Sie den Dateinamen in modInvest.vb. 3. Ändern Sie auch den Namen des Startobjekts. Klicken Sie das Projekt im Projektmappen-Explorer mit rechts an und wählen Sie EIGENSCHAFTEN aus. Ändern Sie auf der Seite ALLGEMEINE EIGENSCHAFTEN das STARTOBJEKT in Invest. Es sollte sich in der Dropdown-Liste befinden. 4. Jetzt können Sie programmieren. Sie benötigen mindestens vier Variablen, um die Eingabe des Anwenders zu speichern. Deklarieren Sie die Variablen so, wie Sie es in Listing 3.5 sehen. Die meisten dieser Werte sind Gleitkommazahlen, mit Ausnahme von Years. Diese Deklarationen sollten zwischen den Module-Zeilen und der Sub Main()-Zeile erscheinen, da es sich um Modulebenen-Variablen handelt.
Listing 3.5: Deklarationen für die Future-Value-Berechnung 1 2 3 4
Private Private Private Private
dblAnnualInterest As Double = 0 iYears As Integer = 0 decStartingBalance As Double = 0 decMonthlyDeposit As Double = 0
5. Verwenden Sie die Routine Main, um alle Routinen aufzurufen, die die Anwendereingabe entgegennehmen, die Berechnungen vornehmen und die Ausgabe ausgeben, wie Sie es in Listing 3.6 sehen.
Listing 3.6: Die Main-Routine 1 2 3 4
Shared Sub Main() Dim decResult As Double 'Hole Eingabewerte. GetInputValues()
119
Einführung in die Programmierung mit Visual Basic .NET
5 6 7 8 9 10 11 12
'Rechne. decResult = CalculateFV(dblAnnualInterest, _ iYears, _ decMonthlyDeposit, _ decStartingBalance) 'Gib Ergebnis aus. DisplayResults(decResult) End Sub
In Listing 3.7 ist jede Hauptfunktion der Anwendung eine separate Subroutine oder Funktion. Dadurch können Sie die Techniken zum Entgegennehmen der Eingabe oder Anzeigen der Ausgabe später leichter ändern. 6. Fügen Sie den Code in Listing 3.7 hinzu, damit der Anwender Informationen eingeben kann. Die Prozedur nimmt keine Parameter entgegen und gibt auch keinen Wert zurück. Da dies eine Konsolenanwendung ist, werden Sie die Routine Console.Read verwenden, um die Werte zu erhalten.
Listing 3.7: Die Eingaberoutine Console.Read 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Private Sub GetInputValues() Console.WriteLine() decStartingBalance = CDec( _ GetValue("Initial Balance: ")) dblAnnualInterest = CDbl(_ GetValue("Annual Interest (e.g. for 5%, enter 5): ")) decMonthlyDeposit = CDec(GetValue("Monthly deposit: ")) iYears = CInt(GetValue("Years of investment: ")) Console.WriteLine() End Sub Private Function GetValue(ByVal Prompt As String) As String Console.Write(Prompt) Return Console.ReadLine End Function
Beachten Sie, dass die Subroutine GetInputValues die Funktion GetValue aufruft. Das ist ein Beispiel dafür, wie Unterstützungsroutinen erstellt werden. Statt den Code neu zu schreiben, um den Anwender mehrmals um Eingabe zu bitten, holen Sie den Code hervor und erzeugen eine Prozedur, die diese Aufgabe übernimmt. Der daraus resultierende Code in GetInputValues ist daher vereinfacht.
120
Beispielanwendung: Einen Future Value berechnen
7. Schreiben Sie die Routine, die die Ausgabe anzeigt, nachdem sie berechnet wurde. Sie kann auch in einem Fenster angezeigt werden, aber vorerst verwenden wir die Prozedur Console.WriteLine, die Sie in Listing 3.8 sehen, um die Information anzuzeigen. Diese Prozedur sollte den anzuzeigenden Wert entgegennehmen und nichts zurückgeben.
Listing 3.8: Die Ausgaberoutine Console.WriteLine 1 2 3 4 5 6 7 8 9
Private Sub DisplayResults(ByVal Result As Double) Console.WriteLine() Console.WriteLine("If you start with { 0:c} , ", decStartingBalance) Console.WriteLine(" and invest { 0:c} per month", decMonthlyDeposit) Console.WriteLine(" for { 0} years", iYears) Console.WriteLine(" at { 0} % interest.", dblAnnualInterest) Console.WriteLine() Console.WriteLine("Your final balance would be: { 0:c} ", Result) End Sub
Das ist eine einfache Routine, die aus einer Reihe von Aufrufen von Console.Write Line besteht, um die eingegebenen Werte und das Ergebnis der Berechnung anzuzeigen. 8. Führen Sie die FV-Berechnung durch. Diese Routine sollte die vier Werte als Parameter entgegennehmen und das Ergebnis der Berechnung zurückgeben. Da diese Prozedur einen Rückgabewert hat, ist sie eine Funktion. Listing 3.9 zeigt die Funktion CalculateFV.
Listing 3.9: Die Funktion CalculateFV 1 2 3 4 5 6 7
Private Function CalculateFV(ByVal AnnualInterest As Double, _ ByVal Years As Integer, _ ByVal MonthlyDeposit As Double, _ ByVal StartingBalance As Double) As Double 'Teile durch 1200, um daraus Prozent pro Monat zu machen. Dim decMonthlyInterest As Double = CDec(AnnualInterest / 1200) Dim iMonths As Integer = Years * 12
121
Einführung in die Programmierung mit Visual Basic .NET
8 9 10 11 12 13 14 15 16
Dim decTemp As Double Dim decReturn As Double 'Wir benötigen diesen Wert an verschiedenen Stellen. decTemp = CDec((1 + decMonthlyInterest) ^ iMonths) decReturn = CDec(MonthlyDeposit * _ ((decTemp – 1) / decMonthlyInterest) _ + (StartingBalance * decTemp)) Return decReturn End Function
Wie bei GetInputValues hätten Sie auch hier den Code verwenden können, der den Wert decTemp berechnet hat. Da Sie aber diese Berechnung nur in dieser Routine und danach wahrscheinlich nicht mehr benötigen, tun Sie das nicht. Listing 3.10 zeigt den vollständigen Code für die Beispielanwendung.
Listing 3.10: Der vollständige Investitionsrechner 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
122
Module Invest Private Private Private Private
dblAnnualInterest As Double = 0 iYears As Integer = 0 decStartingBalance As Double = 0 decMonthlyDeposit As Double = 0
Sub Main() Dim decResult As Double 'Hole Eingabewerte. GetInputValues() 'Rechne. decResult = CalculateFV(dblAnnualInterest, _ iYears, _ decMonthlyDeposit, _ decStartingBalance) 'Gib Ergebnis aus. DisplayResults(decResult) End Sub Private Function CalculateFV(ByVal AnnualInterest As Double, _
Beispielanwendung: Einen Future Value berechnen
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
ByVal Years As Integer, _ ByVal MonthlyDeposit As Double, _ ByVal StartingBalance As Double) As Double 'Teile durch 1200, um daraus Prozent pro Monat zu machen. Dim decMonthlyInterest As Double = CDec(AnnualInterest / 1200) Dim iMonths As Integer = Years * 12 Dim decTemp As Double Dim decReturn As Double 'Wir benötigen diesen Wert an verschiedenen Stellen. decTemp = CDec((1 + decMonthlyInterest) ^ iMonths) decReturn = CDec(MonthlyDeposit * ((decTemp – 1) _ / decMonthlyInterest) _ + (StartingBalance * decTemp)) Return decReturn End Function Private Function GetValue(ByVal Prompt As String) As String Console.Write(Prompt) Return Console.ReadLine End Function Private Sub GetInputValues() Console.WriteLine() decStartingBalance = CDec(GetValue("Initial Balance: ")) dblAnnualInterest = _ CDbl(GetValue("Annual Interest (for 5%, enter 5): ")) decMonthlyDeposit = CDec(GetValue("Monthly deposit: ")) iYears = CInt(GetValue("Years of investment: ")) Console.WriteLine() End Sub Private Sub DisplayResults(ByVal Result As Double) Console.WriteLine() Console.WriteLine("If you start with { 0:c} , ", decStartingBalance) Console.WriteLine(" and invest { 0:c} per month", decMonthlyDeposit) Console.WriteLine("for { 0} years", iYears) Console.WriteLine("at { 0} % interest.", dblAnnualInterest) Console.WriteLine() Console.WriteLine("Your final balance would be: { 0:c} ", Result) End Sub End Module
9. Lassen Sie die Anwendung laufen. Die Ausgabe sollte der ähneln, die Sie zu Beginn dieses Abschnitts gesehen haben. Sie können die Anwendung von der IDE aus ausführen, indem Sie auf die STARTEN-Schaltfläche in der Symbolleiste DEBUGGEN klicken. Das Fenster, das sich mit der Anwendung öffnet, wird aber wahrscheinlich zu schnell
123
Einführung in die Programmierung mit Visual Basic .NET
wieder verschwinden, als dass Sie die Antwort sehen könnten. Führen Sie das Programm statt dessen an der Befehlseingabeaufforderung aus, indem Sie dort die .exeDatei aufrufen. Sie können mit diesem Rechner ein beängstigendes Beispiel ausprobieren. Geben Sie als monatliche Einzahlung einen Wert ein, den Sie normalerweise monatlich für ein Laster ausgeben (Zigaretten, technische Spielereien). Der entsprechende Future Value wird wahrscheinlich verflixt hoch sein.
3.6
Zusammenfassung
Die heutige Lektion hat einen großen Bereich der verschiedenen Variablentypen, die Sie in Visual Basic .NET erzeugen können, und ihre Verwendungsweise abgedeckt. Zusätzlich haben wir uns mit Funktionen und Subroutinen beschäftigt, die in Visual Basic .NET eingebaut sind, aber auch mit denen, die Sie selbst erstellen können. Diese beiden Themen sind grundlegend für das Verständnis von Visual Basic .NET und werden in allen Arten von Anwendungen auftauchen. Am Tag 4, Programmfluss steuern, werden wir weiter untersuchen wie in Visual Basic .NET programmiert wird. Dann werden wir uns ansehen, wie Sie die Entscheidungen in Ihren Programmen verwalten können.
3.7 F
Ich habe gelesen, dass Visual Basic .NET »typlose« Programmierung unterstützt. Was ist das? A
F
Wenn Sie den Typ einer Variablen oder Funktion deklarieren, geben Sie bestimmte Regeln an: welchen Informationstyp sie darstellt, wo sie verwendet werden kann usw. Typlose Programmierung tritt dann auf, wenn Sie den Typ nicht deklarieren. Alle Variablen sind dann Objekte, die jede Art von Information speichern können. Visual Basic .NET ist das einzige Mitglied der Visual Studio-Familie, das diese Art der Programmierung unterstützt.
Wenn man eine Prozedur erstellen möchte, sollte es eine Subroutine oder eine Funktion sein? A
124
Fragen und Antworten
Die kurze Antwort lautet: »Es kommt drauf an«. Sie sollten den Prozedurtyp erstellen, der die benötigte Funktionalität bietet. Einige Prozeduren sind offensichtlich. Wenn Sie eine Prozedur erstellen müssen, die einige Berechnungen oder Bearbei-
Workshop
tungen vornimmt und das Ergebnis zurückgibt, sollten Sie eine Funktion erstellen. Bei anderen Routinen, in denen Sie eventuell einen Wert zurückgeben müssen, haben Sie die Wahl. Und es gibt kein richtig oder falsch, sondern nur verschiedene Möglichkeiten. Wenn es keinen offensichtlichen Rückgabewert gibt, ist die Auswahl des zu erzeugenden Prozedurtyps eine Sache der persönlichen Vorlieben und/oder des Unternehmens. Einige Menschen und Unternehmen erstellen immer Funktionen, andere erstellen auch Subroutinen. F
Wie finde ich die Liste aller eingebauten Funktionen? A
Es gibt zwei Möglichkeiten, alles über die eingebauten Funktionen herauszufinden:
왘
Die Onlinehilfe enthält Beschreibungen und Codebeispiele aller eingebauten Funktionen. Sie finden sie, indem Sie die Onlinehilfe nach Visual Basic Language Reference durchsuchen.
왘
Der Objektbrowser: Wenn Sie nur eine kurze Beschreibung einer eingebauten Funktion benötigen, können Sie sie im Objektbrowser finden. Öffnen Sie ihn, indem Sie ANSICHT, ANDERE FENSTER und OBJEKTBROWSER auswählen.
3.8
Workshop
Der Workshop soll Ihnen dabei helfen, mögliche Fragen vorherzusehen und das Gelernte zu wiederholen. Außerdem soll er Sie dazu anregen, über die praktische Umsetzung Ihres Wissens nachzudenken. Die Antworten und Lösungen finden Sie im Anhang A.
Quiz 1. Wie können Sie entscheiden, welcher Variablentyp zu verwenden ist, wenn Sie mit Zahlen arbeiten? 2. Welches ist die richtige Methode, diese Subroutine aufzurufen? Function RaiseToPower(ByVal Number As Integer, ByVal Power As Integer) As Long
A: Dim lValue = RaiseToPower(3,4) B: RaiseToPower 3,4 C: Console.WriteLine(RaiseToPower(3,4)) D: Dim lValue = RaiseToPower 3,4
125
Einführung in die Programmierung mit Visual Basic .NET
3. Wenn eine Variable mit dem Schlüsselwort Private deklariert wird, an welcher Stelle in einem Programm kann sie verwendet werden?
Übung Schreiben Sie die FV-Rechner-Beispielanwendung so um, dass sie einen Darlehensrechner statt eines Investitionsrechners darstellt. Er sollte das Darlehen, den jährlichen Zinssatz, die Dauer des Darlehens in Monaten abfragen und die Höhe der Zahlungen berechnen. Die Formel für diese Berechnung ist: Payment = LoanAmount * (MonthlyInterest * ((1 + MonthlyInterest) ^ Months) / (((1 + MonthlyInterest) ^ Months) – 1))
oder auf Deutsch: Zahlung = KreditBetrag * (MonatsZinsen * ((1 + MonatsZinsen) ^ Monate) / (((1 + MonatsZinsen ) ^ Monate) – 1))
126
Den Programmfluss steuern
Den Programmfluss steuern
Um ein wirklich nützliches Programm zu schreiben, benötigen Sie noch ein paar weitere wichtige Elemente. Bis jetzt haben Sie Programme erstellt, die aus einer linearen Reihe von Anweisungen bestanden. Eine Zeile wurde nach der anderen ausgeführt, wenn das Programm lief. Damit ein Programm wirklich interaktiv ist, benötigen Sie eine neue Anweisungsart: eine Steueranweisung, damit das Programm verschiedene Wege durch den Programmcode nehmen kann – je nachdem, welche Eingabe es erhält. Fast jede Programmiersprache verfügt über Steueranweisungen und Visual Basic .NET ist keine Ausnahme. In der heutigen Lektion werden Sie die beiden Kategorien der Steueranweisungen kennen lernen:
Die Themen heute 쐽
Auswahlanweisungen
쐽
Schleifenanweisungen
In jeder dieser Kategorien werde ich verschiedene Varianten eines jeden Typs erläutern und Sie werden lernen, welche Anweisung für eine bestimmte Situation am besten geeignet ist.
4.1
Mit Steueranweisungen eine Auswahl treffen
Steueranweisungen sind die Teile einer Programmiersprache, die nur existieren, um zu bestimmen, was andere Teile des Programms ausführen. Solche Anweisungen bestimmen dies anhand des Wertes einer Variablen oder anhand einer anderen Bedingung, wodurch das Programm je nach Situation anders reagieren kann. Dass eine solche Verhaltensweise notwendig ist, wird offensichtlich, wenn Sie sich die Programme genauer ansehen, die Pseudocode verwenden. Wenn wir ein Problem mit einer Sprache beschreiben, die sich irgendwo zwischen unserer normalen Sprache und Computercode befindet, dann nennen wir diese »codenahe« Beschreibung Pseudocode. In der heutigen Lektion erhalten Sie einige Beispiele für diese Art Sprache, mit der wir beschreiben, was Ihre Programme tun sollen. Viele Programmierer, einschließlich der Autoren dieses Buches, halten dies für eine nützliche Methode, um den allgemeinen Ablauf Ihrer Programme auf eine Art darzustellen, die auch Nicht-Programmierer verstehen können. Es kann viel einfacher sein, ein ganzes Programm in Pseudocode zu schreiben, bevor der eigentliche Code erstellt wird. (Das wird besonders wichtig, wenn Sie mit komplizierteren Programmen umgehen.)
128
Mit Steueranweisungen eine Auswahl treffen
Stellen wir uns z.B. Code vor, der nur einen kleinen Teil einer Anwendung bildet: die Begrüßung. In der menschlichen Sprache würden Sie Folgendes sagen, um zu beschreiben, was der Code tut: »Begrüße den Anwender abhängig von der Tageszeit mit dem entsprechenden Gruß im Programm.« Für solch ein kleines Codestück wäre das wahrscheinlich schon die gesamte Beschreibung und Sie könnten direkt mit der Programmierung beginnen. Für diese besondere Aufgabe würde der Pseudocode so aussehen: Wenn das Programm startet... Wenn es vor 12.00 Uhr ist, zeige "Guten Morgen!" an Wenn es nach 12.00 Uhr und vor 18:00 Uhr ist, zeige "Guten Tag!" an Zu anderen Uhrzeiten zeige "Guten Abend!" an Mache mit dem Rest des Programms weiter...
Sogar dieses einfache Beispiel zeigt bereits die Notwendigkeit, im Code Entscheidungen zu treffen, damit das Programm unterschiedliche Nachrichten je nach Tageszeit anzeigen kann. Um das in Code umzuwandeln, verwenden Sie eine Steueranweisung, mit der Sie eine Bedingung testen (in diesem Fall die Uhrzeit) und dann die entsprechende Reaktion wählen können.
Die If-Anweisung Die einfachste Steueranweisung – und eine, über die fast jede Programmiersprache verfügt – ist die If-Anweisung. Diese Anweisung hat verschiedene Formate, sieht im Grunde aber so aus: If Then Code, der ausgeführt wird, wenn die Bedingung wahr ist End If
Die ist der Schlüsselteil dieser Anweisung. Sie bestimmt, ob der innere Codeblock ausgeführt wird oder nicht. Diese Bedingung nimmt die Form eines Ausdrucks an, einer Kombination aus Werten und Operatoren, die zur Laufzeit ausgewertet wird. Da die If-Anweisung nur zwei mögliche Aktionen unterstützt – entweder wird der Code ausgeführt oder nicht –, benötigt der Ausdruck nur zwei mögliche Werte. Das bedeutet, dass jeder Ausdruck, der als Bedingung verwendet wird, ein eindeutiges »Ja oder Nein«-Ergebnis haben muss (True oder False), wie in den folgenden Beispielen: 쐽
Es ist vor Mittag.
쐽
Es ist nach Mittag, aber noch vor 18 Uhr.
쐽
Die Anzahl der Schüler übersteigt die Anzahl der Stühle.
129
Den Programmfluss steuern
Jeder dieser Ausdrücke ist entweder wahr oder falsch. Es gibt kein Mittelding und daher sind sie als Bedingung einer If-Anweisung geeignet. Wenn Sie unsicher sind, ob ein bestimmter Ausdruck geeignet ist, probieren Sie ihn einfach in Pseudocode aus. Stellen Sie sich z.B. diese Ausdrücke vor: 쐽
3+5
쐽
John
쐽
Mittwoch
Probieren Sie nun einen dieser Ausdrücke in Ihrem Pseudocode aus: »Wenn 3 + 5, dann beende das Programm.« Was soll das heißen? »3 + 5« ist kein Ausdruck, der als True oder False ausgewertet werden kann, und daher hat die gesamte If-Anweisung keinen Sinn. Ausdrücke, die entweder True oder False zurückgeben, sind als boolesche Ausdrücke bekannt. Ich werde sie etwas später in der heutigen Lektion näher erörtern. Gehen wir nun zurück zum ursprünglichen Pseudocode-Beispiel, das eine Begrüßung zum Programmstart anzeigt. Diesmal wollen wir die englische Sprache verwenden, da hierbei die Wörter den in Visual Basic verwendeten Ausdrücken ähnlicher werden. When the program starts... If it is before noon display "Good Morning!" If it is after noon, but before 6 pm, display "Good Afternoon!" For any other time, display "Good Evening!" Continue with the rest of the program...
Bevor Sie aus diesem Pseudocode ein Visual Basic .NET-Programm machen, lohnt es sich wahrscheinlich, diese für Menschen lesbaren Zeilen ein wenig mehr dem Code anzunähern. Sie können das tun, indem Sie einfach den Text umformulieren, sodass er mehr Einzelheiten liefert: When the program starts... If the current system time is less than 12:00 noon then print out "Good Morning!" If the current system time is equal to or greater than 12:00 noon and the current system time is also less than 6:00 pm then print out "Good Afternoon!" If the current system time is equal to or greater than 6:00 pm then print out "Good Evening!" Continue with the rest of the program...
Um das Programm zu schreiben, müssen Sie nun nur wissen, wie Sie die aktuelle Systemzeit erhalten. Der Rest sollte nicht so schwierig sein. Die aktuelle Systemzeit ist über das Now()-Objekt verfügbar, das auch Informationen über das aktuelle Datum enthält und ver-
130
Mit Steueranweisungen eine Auswahl treffen
schiedene nützliche Eigenschaften wie Hour, Minute, DayofWeek und Seconds bietet. Für unsere Zwecke reicht die Eigenschaft Hour aus, die die aktuelle Zeit als 24-Stunden-Zeit zurückgibt (Werte von 0 bis 23). Wenn man so wörtlich wie möglich von Pseudocode in Visual Basic .NET übersetzt, erhält man das Programm in Listing 4.1. Listing 4.1: Greeting.vb 1 Imports System 2 3 Public Class Greeting 4 ' Führe die Anwendung aus 5 Shared Sub Main() 6 Dim dtCurrent As System.DateTime 7 Dim iHour As Integer 8 9 dtCurrent = dtCurrent.Now() 10 iHour = dtCurrent.Hour 11 12 If (iHour < 12) Then 13 Console.Writeline("Good Morning!") 14 End If 15 If (iHour >= 12) And (iHour < 18) Then 16 Console.WriteLine("Good Afternoon!") 17 End If 18 If (iHour >= 18) Then 19 Console.WriteLine("Good Evening!") 20 End If 21 End Sub 22 23 End Class
Sie können Greeting.vb von der Begleit-CD öffnen und es selbst ausprobieren. Um dieses und die meisten der Beispiele in der heutigen Lektion zu kompilieren, öffnen Sie entweder diese Datei oder erzeugen selbst eine Textdatei, geben den Code aus Listing 4.1 ein und speichern dann die Datei als Greeting.vb. Gehen Sie zur Befehlskonsole (DOS-Eingabeaufforderung), verwenden Sie den Befehl cd (»change directory«, Verzeichnis wechseln), um sicherzustellen, dass Sie im gleichen Verzeichnis arbeiten, in dem sich auch die Datei Greeting.vb befindet, und kompilieren Sie das Programm, indem Sie vbc/r:System.dll/ t:exe Greeting.vb eingeben. Der Compiler wird standardmäßig eine ausführbare Datei erzeugen, die genauso heißt wie die Quelldatei, sodass Sie in diesem Fall am Ende im gleichen Verzeichnis eine neue Datei mit dem Namen Greeting.exe erhalten. Die Ausführung dieser neuen ausführbaren Datei führt zu dem entsprechenden Ergebnis, abhängig von der Tageszeit, zu der es ausgeführt wird. Um zu verstehen, wie diese drei If-Anweisungen funktionieren, sehen wir uns den Code schrittweise an und schauen, was passiert. Sie können das innerhalb der Visual Studio IDE
131
Den Programmfluss steuern
tun, aber wir werden es nur auf dem Papier machen und uns die Rechnerleistung für später aufsparen. Die erste auszuführende Zeile ist die Zeile 9, die die Datumsvariable mit dem aktuellen Datum und der aktuellen Uhrzeit initialisiert. Zeile 10 speichert dann die aktuelle Stunde in einer weiteren Variablen namens iHour. Die erste IfAnweisung wird in der Zeile 12 ausgeführt. An diesem Punkt wird der Ausdruck (iHour < 12) ausgewertet. iHour entspricht dem aktuellen Stundenwert, der 13 ist (laut der Systemuhr). Nun wird der Ausdruck, der auf 13 < 12 reduziert wurde, noch einmal reduziert, nämlich auf den booleschen Wert False (13 ist nicht kleiner als 12, zumindest nicht nach unserem Verständnis von Mathematik). Daher sagt der Wert False der If-Anweisung, dass sie den Codeblock nicht ausführen soll, und die Ausführung geht direkt zu Zeile 14, dem End If. Diese Anweisung ist einfach nur ein handlicher Platzhalter, sodass bis Zeile 15, dem nächsten If, nicht wirklich etwas geschieht. In diesem Fall wird die If-Anweisung (iHour >= 12) And (iHour < 18) zuerst auf (13 >= 12) And (13 < 18) reduziert und dann auf True And True. Wenn wir zwei boolesche Wert mit And verbinden, erhalten wir nur das Ergebnis True, wenn beide Werte True sind, was hier der Fall ist. Also endet die If-Anweisung mit der Bedingung True und daher wird der innere Codeblock (Zeile 16) ausgeführt und die aufregende Nachricht Good Afternoon! in der Konsole ausgegeben. Nun hat unser Programm seine gesamte Arbeit getan, wir haben die richtige Nachricht für die Tageszeit ausgegeben. Der Code bricht die Ausführung aber nicht ab. Es gibt nichts, das ihm sagt, dass es keinen Sinn hat, weiterzumachen, sodass die Ausführung mit dem End If und dann mit der letzten If-Anweisung in Zeile 18 weitermacht. Diese Anweisung hat den Ausdruck (iHour >= 18) als Bedingung, woraus (13 >= 18) wird, was als False ausgewertet wird. Die Ausführung macht fröhlich mit dem letzten End If in der Zeile 20 weiter und dann endet das Programm.
Die If-Anweisung erweitern Listing 4.1 war zwar recht geradlinig und sicherlich nicht das komplizierteste Programm auf Erden, aber es führt zu einer Frage: Warum sollte man noch Code ausführen, wenn man die Antwort gefunden hat? Das ist ein guter Einwand. Die Zeilen 18 bis 20 in Listing 4.1 waren zwar nur ein paar zusätzliche Zeilen für die Ausführung, aber in einem echten Programm könnten noch viel mehr Zeilen übrig sein. In diesem Programm will ich mit der nächsten If-Anweisung nur dann weitermachen, wenn die vorherige False war. Ich hätte das in meinem Pseudocode deutlicher machen können, indem ich ihn so geschrieben hätte:
132
Mit Steueranweisungen eine Auswahl treffen
If the current system time is less than 12:00 noon then print out "Good Morning!" else If the current system time is equal to or greater than 12:00 noon and the current system time is also less than 6:00 pm then print out "Good Afternoon!" else If the current system time is equal to or greater than 6:00 pm then print out "Good Evening!" Continue with the rest of the program...
Dieses Beispiel ist fast mit dem vorherigen Code identisch, aber nun ist klar, dass Sie nur dann mit dem nächsten If weitermachen können, wenn der Wert des Ausdrucks nicht der Bedingung der aktuellen If-Anweisung entspricht. Wie Sie vielleicht schon erraten haben, ist die Fähigkeit, If-Anweisungen auf diese Art zu schachteln, eine Notwendigkeit in der Computerprogrammierung, sodass Visual Basic und die meisten anderen Sprachen eine Methode bieten, um dieses Konzept mit der If-Anweisung auszudrücken. Wir haben uns die einfache Form dieser Anweisung angesehen: If Then Codeblock End If
Wird die Bedingung als True ausgewertet, dann wird der Codeblock ausgeführt. Wenn sie False ist, dann wird der Codeblock übersprungen und die Ausführung wird nach dem End If fortgesetzt. Sie können das ein wenig komplexer machen, indem Sie eine neue Klausel hinzufügen, die Else-Klausel. Nun sieht die Syntax so aus: If Then Codeblock #1 Else Codeblock #2 End If
In dieser neuen Struktur wird Codeblock #1 ausgeführt, wenn die Bedingung als True ausgewertet wird, und das Programm fährt nach dem End If fort. Wird die Bedingung aber als False ausgewertet, so wird Codeblock #2 ausgeführt und das Programm fährt ebenfalls nach dem End If fort. Da es sich bei der Bedingung um einen booleschen Ausdruck handeln muss und da alle booleschen Ausdrücke entweder True oder False sind, muss entweder Codeblock #1 oder Codeblock #2 ausgeführt werden. Diese Anweisung kann niemals dazu führen, dass beide gleichzeitig oder keine von beiden ausgeführt wird. Wird der Code neu geschrieben, um diese neue und verbesserte If-Anweisung zu nutzen, so erhalten Sie Listing 4.2.
133
Den Programmfluss steuern
Listing 4.2: Greeting_IFELSE.vb 1 Imports System 2 3 Public Class Greeting 4 'Führe die Anwendung aus 5 Shared Sub Main() 6 Dim dtCurrent As System.DateTime 7 Dim iHour As Integer 8 9 dtCurrent = dtCurrent.Now 10 iHour = dtCurrent.Hour 11 12 If (iHour < 12) Then 13 Console.Writeline("Good Morning!") 14 Else 15 If (iHour >= 12) And (iHour < 18) Then 16 Console.WriteLine("Good Afternoon!") 17 Else 18 Console.WriteLine("Good Evening!") 19 End If 20 End If 21 End Sub 22 23 End Class
Ist das einfacher? Nein, nicht wirklich, aber Listing 4.2 ist effektiver als Listing 4.1. In dieser neuen Version ist das Programm fertig, wenn es die korrekte Begrüßung ausgegeben hat. Es wird kein unnötiger Code ausgeführt. In diesem speziellen Programm habe ich eine weitere If-Anweisung in jede Else-Klausel eingebettet, die eine weitere Bedingung testet, falls die vorherige falsch war. In der ElseKlausel kann nicht nur solcher Code stehen, aber diese eingebetteten If-Anweisungen werden häufig genutzt und daher enthält Visual Basic eine weitere Verbesserung der elementaren If-Anweisung: die ElseIf-Anweisung. Diese Anweisung kombiniert die Funktionalität der Else-Anweisung mit der Annahme, dass eine weitere If-Anweisung eingebettet ist oder unmittelbar folgt. Die Syntax dieser Anweisung ist eine Verdichtung von dem, was Sie mit If und Else schreiben müssten. Hier sehen Sie die Syntax einer If-Anweisung, in der die Else-Klausel ein eingebettetes If enthält, gefolgt von der Syntax, die mit der ElseIfAnweisung erscheinen würde. If Then Codeblock #1 Else If then Codeblock #2 End If End If
134
Mit Steueranweisungen eine Auswahl treffen
wird If Then Codeblock #1 ElseIf Then Codeblock #2 End If
Beachten Sie das Fehlen des zweiten End If im zweiten Syntaxbeispiel. Das ElseIf wird als eine Klausel angesehen und ist daher immer noch Teil der ursprünglichen If-Anweisung. Es ist immer noch möglich, eine Else-Klausel für die zweite Bedingung zu verwenden oder eine weitere ElseIf-Klausel. Beispiele für beides sehen Sie hier: If Then Codeblock #1 ElseIf Then Codeblock #2 Else Codeblock #3 End If
If Then Codeblock #1 ElseIf Then Codeblock #2 ElseIf Then Codeblock #3 End If
Auch mit einer weiteren ElseIf-Klausel wird alles als Teil der ursprünglichen If-Anweisung angesehen und es ist nur ein einzelnes End If erforderlich. Wenn wir zu unserem ursprünglichen Beispiel zurückkehren, das abhängig von der Tageszeit die entsprechende Begrüßung ausgibt, können Sie den Code mit der ElseIf-Klausel (siehe Listing 4.3) sehr vereinfachen. Listing 4.3: Greetings_ElseIf.vb 1 Imports System 2 3 Public Class Greeting 4 'Führe die Anwendung aus 5 Shared Sub Main() 6 Dim dtCurrent As System.DateTime 7 Dim iHour As Integer 8 9 dtCurrent = dtCurrent.Now 10 iHour = dtCurrent.Hour
135
Den Programmfluss steuern
11 12 13 14 15 16 17 18 19 20 21
If (iHour < 12) Then Console.Writeline("Good Morning!") ElseIf (iHour >= 12) And (iHour < 18) Then Console.WriteLine("Good Afternoon!") Else Console.WriteLine("Good Evening!") End If End Sub End Class
Einzeilige und sofortige If-Anweisungen Sie haben die Blockform der If-Anweisung bereits kennen gelernt, in der Code zwischen den Anweisungen If und End If eingeschlossen ist. Es ist aber auch möglich, eine IfAnweisung in einer einzelnen Zeile auszudrücken. Hier sehen Sie ein Beispiel, in dem eine Bedingung überprüft und eine Reaktion ausgeführt wird, alles in einer einzelnen Zeile: If iHour > 11 Then System.Console.WriteLine("It is Not Morning!")
Dieses einzeilige Konzept kann mit einer Else-Klausel erweitert werden: If iHour > 11 Then DoSomething() Else DoSomethingElse()
Es kann sogar mit mehr als einer Anweisung verwendet werden, die in dem True- oder dem False-Codeblock (oder in beiden) ausgeführt werden, da mehrere Anweisungen durch Doppelpunkte getrennt werden können: If iHour > 11 Then DoSomething(): DoMore() Else DoSomethingElse()
Ich habe diese Anweisung mehr der Vollständigkeit halber angeführt, sie ist nicht wirklich notwendig. Es gibt nichts, was Sie mit einer einzeiligen If-Anweisung tun könnten, das Sie nicht auch mit der regulären Blockform tun können. Wenn Sie den ganzen Code in eine Zeile setzen, wird er dadurch nicht schneller ausgeführt. Es führt in den meisten Situationen nur dazu, dass Quellcode erzeugt wird, der weniger Platz auf der Festplatte beansprucht und viel schwieriger zu lesen ist. Gelegentlich lässt die einzeilige Version Ihren Code schöner aussehen, wie Sie in Listing 4.4 sehen. In dem Listing ist eine große Anzahl von If-Anweisungen erforderlich, jede mit nur einer Codeanweisung, die ausgeführt wird, wenn die Bedingung True ist. Diese Situationen sind zu selten, um die Verwendung einer anderen Syntax für solch eine Steueranweisung zu rechtfertigen. Bleiben Sie bitte bei der anderen Form der If-Anweisung und ersparen Sie Ihren Programmiererkollegen die Kopfschmerzen.
136
Boolesche Ausdrücke und boolesche Logik
Listing 4.4: Einzeilige If-Anweisungen können Code manchmal besser aussehen lassen. 1 2 3 4
If X=5 Then strChar = "A" If X=23 Then strChar = "B" If X=2 Then strChar = "C" ...
4.2
Boolesche Ausdrücke und boolesche Logik
Alle Steueranweisungen stützten sich in irgendeiner Form auf eine Entscheidung, auf den Wert einer Variablen oder Konstanten oder auf eine Tatsache, die in Bezug zur aktuellen Situation steht. Unabhängig davon, welcher Wert tatsächlich überprüft wird, kann das Ergebnis nur True oder False sein. Wie bereits früher erwähnt, erzeugen alle booleschen Ausdrücke eine von zwei Antworten: Ja oder Nein, wahr oder falsch. Am Tag 3, Einführung in die Programmierung mit Visual Basic .NET, haben Sie boolesche Variablen kennen gelernt, einen Variablentyp, der nur True- oder False-Werte enthalten kann. Dies sind die einzigen Ausdrücke und Werte, die als Teil einer Steueranweisung zulässig sind, da eine Steueranweisung mit diesem Wert eine Entscheidung fällen muss. Mit anderen als booleschen Ausdrücken würde das nicht funktionieren, sie führen zu keiner Ja-/Nein- oder True-/ False-Antwort und das Programm wüsste nicht, was zu tun ist. Die beiden einfachsten booleschen Ausdrücke sind True und False. Alle anderen booleschen Ausdrücke sind im Endeffekt, wenn sie ausgewertet werden, auf einen dieser beiden Werte zurückzuführen. Nur wenige Programme verwenden allerdings diese Werte direkt. Statt dessen erzeugen sie kompliziertere Ausdrücke, die Vergleiche zwischen zwei nichtbooleschen Werten, logische Operationen auf booleschen Werten oder eine Kombination von beidem sind.
Vergleichsoperatoren Der gängigste Ausdruckstyp, der in Programmen verwendet wird, ist ein Vergleich: zwei nicht-boolesche Ausdrücke mit einem Operator dazwischen. Die folgenden Vergleichsoperatoren stehen für die Verwendung in Ausdrücken zur Verfügung: 쐽
> (größer als)
쐽
< (kleiner als)
쐽
= (gleich)
쐽
(ungleich)
137
Den Programmfluss steuern
쐽
>= (größer gleich)
쐽
3 AND X < 8 Then oder If (((X+3) * 5) > (Y*3)) AND (SystemIsRunning() OR iHour < 5) Then. Wir werden beide Arten von Ausdrücken, Vergleiche und logische Ausdrücke, später in einigen Übungen zu If-Anweisungen verwenden.
Kurzschluss Ähnlich wie ein Wahlergebnis ist auch das Ergebnis eines booleschen Ausdrucks häufig bereits bekannt, ehe der vollständige Ausdruck ausgewertet wurde. Stellen Sie sich diesen booleschen Ausdruck vor: (X > 1) AND (X < 10). Wenn X gleich 1 (und nicht größer als 1) ist, wissen Sie, dass die rechte Seite irrelevant ist, sobald Sie die linke Seite des Ausdrucks (die False zurückgibt) auswerten. Durch die Natur der AND-Anweisung ist es nicht nötig, die andere Seite auszuwerten. Der gesamte Ausdruck ist False, egal welcher Wert auf der anderen Seite zurückgegeben wird. Das ist etwas, das Sie ohne großes Nachdenken tun würden, wenn Sie einen booleschen Ausdruck auswerten, aber für den Computer ist das nicht immer so klar. Das Verhalten, das wir erwarten, ohne unnötige Abschnitte eines Ausdrucks auszuwerten, wird Kurzschließen genannt, aber Visual Basic .NET arbeitet standardmäßig nicht auf diese Art und Weise. Damit Visual Basic .NET einen booleschen Ausdruck kurzschließt, müssen Sie alternative Formen der AND- und OROperatoren verwenden, nämlich ANDALSO und ORELSE. Sie brauchen sich aber nicht darauf zu verlassen, dass es sich so verhält. Mit einem einfachen Testprogramm (siehe Listing 4.6) können Sie genau sehen, was geschieht. Listing 4.6: ShortCircuiting.vb 1 Public Class ShortCircuiting 2 3 Shared Sub Main() 4 If Test("Left") ANDALSO Test("Right") Then 5 'tue etwas 6 End If 7 End Sub 8
140
Der Umgang mit mehreren Auswahlmöglichkeiten: die Select Case-Anweisung
9 Shared Function Test(sInput As String) As Boolean 10 System.Console.WriteLine(sInput) 11 Test = FALSE 12 End Function 13 14 End Class
Wenn die Funktion Test()wie in Listing 4.6 das Ergebnis False zurückgibt, dann wissen Sie, was der Gesamtausdruck zurückgibt, indem Sie einfach die linke Seite auswerten. Die Ausführung des Codes in Listing 4.6 erzeugt nur eine Ausgabezeile, in diesem Fall »Left". Wenn Test()das Ergebnis True zurückgibt, dann müssen beide Seiten ausgeführt werden, und das Programm gibt »Left« und »Right« aus. Um das Standardverhalten von booleschen Operatoren zu testen, ändern Sie das ANDALSO in AND und sehen sich das Ergebnis an.
4.3
Der Umgang mit mehreren Auswahlmöglichkeiten: die Select Case-Anweisung
Die If-Anweisung kann fast jede Art von Anforderung behandeln, die mit einer Entscheidung zusammenhängt, aber sie ist eigentlich dazu gedacht, mit Auswahlmöglichkeiten umzugehen, die eine einzige Verzweigung haben. Wenn mehrere Werte überprüft werden müssen und für jeden eine andere Aktion vorgenommen werden soll, können If-Anweisungen sehr schwerfällig werden. Stellen Sie sich ein Eingabeprogramm für die Einkommensteuer vor, mit verschiedenen Coderoutinen für die Behandlung von fünf verschiedenen Kundenarten. Die Kundenkategorie basiert auf der Anzahl der Personen in einem Haushalt. Abhängig von diesem Wert möchten Sie die Anwender zur richtigen Routine leiten. Mit If-Anweisungen könnte der Code so aussehen wie in Listing 4.7. Listing 4.7: Mehrere eingebettete If-Anweisungen verwenden 1 2 3 4 5 6 7 8 9 10 11
. . . If lngNumPeople = 1 Then Call SinglePersonTaxReturn() ElseIf lngNumPeople = 2 Then Call TwoPersonTaxReturn() ElseIf lngNumPeople = 3 OR lngNumPeople = 4 Then Call MidSizedHouseholdTaxReturn() ElseIf lngNumPeople > 4 AND lngNumPeople < 10 Then Call BigHouseholdTaxReturn() Else Call ReallyBigHouseholdTaxReturn()
141
Den Programmfluss steuern
12 End If 13 . . .
Wenn Sie mehr als nur ein paar Auswahlmöglichkeiten testen, werden all diese verschiedenen If-Klauseln möglicherweise übermäßig komplex. Um auf mehrere Werte oder auf mehrere Wertgruppen zu testen, verfügt Visual Basic über die Select Case-Anweisung, die die folgende Syntax hat: Select Case Case Codeblock Case Codeblock Case Else Codeblock End Select
Die Verwendung der Select Case-Anweisung anstelle der If-Anweisungen in Listing 4.7 führt zu dem alternativen Code, den Sie in Listing 4.8 sehen. Listing 4.8: Der Befehl Select Case kann Ihren Code stark vereinfachen. 1 Select Case lngNumPeople 2 Case 1 3 Call SinglePersonTaxReturn() 4 Case 2 5 Call TwoPersonTaxReturn() 6 Case 3, 4 7 Call MidSizedHouseholdTaxReturn() 8 Case 5 to 9 9 Call BigHouseholdTaxReturn() 10 Case Else 11 Call ReallyBigHouseholdTaxReturn() 12 End Select
Die Case Else-Klausel wird genauso verwendet wie die Else-Klausel in einer If-Anweisung: In diesem Beispiel wird sie ausgeführt, wenn keine der Bedingungen zutrifft. Beachten Sie, dass in Listing 4.8 immer nur eine Bedingung wahr sein kann. Es gibt keine Überschneidung zwischen den verschiedenen Case-Bedingungen, was auch Sinn macht. Eigentlich werden Überschneidungen aber von Visual Basic in keiner Weise verhindert. Sie dürfen sich überschneidende Bedingungen verwenden, in denen mehr als eine CaseBedingung einem bestimmten Wert entsprechen kann. Wenn das der Fall ist, wird nur die erste zutreffende Bedingung ausgeführt, da das Programm die Select Case-Anweisung beendet, sobald eine Übereinstimmung gefunden und der entsprechende Codeblock ausgeführt wurde. Sich überschneidende Bedingungen verursachen zwar keinen Fehler, aber sie können für einen Programmierer sehr verwirrend sein. Allein schon aus diesem Grund lohnt es sich, sie zu vermeiden.
142
Schleifen
Decken Sie immer jede mögliche Bedingung mit einer Case Else-Klausel ab. Auf diese Weise wird jede unerwartete Eingabe abgefangen, für die Sie keine andere Case-Anweisung angegeben haben. So wird Ihr Programm stabiler.
Verwenden Sie in Select Case keine mehrfachen Bedingungen, wenn ein einzelner Wert mehr als einer dieser Bedingungen entsprechen kann. Diese Codeart ist kein Fehler von Visual Basic, sondern sie führt zu schwer verständlichem Code.
4.4
Schleifen
Bisher haben Sie gelernt, wie der auszuführende Code mit If- und Select-Anweisungen gesteuert wird, aber es gibt noch einen weiteren Punkt: die Notwendigkeit, den gleichen Code mehrmals auszuführen. Diese Anforderung wird durch eine weitere Art von Steueranweisungen behandelt: die Schleifen. In Visual Basic stehen verschiedene Schleifentypen zur Verfügung, von denen jede die meisten Aufgaben bewältigen kann, aber jede wurde im Hinblick auf einen bestimmten Zweck entworfen. Wir beginnen die Erörterung von wiederholten Ausführungen mit der einfachsten Schleife, der For...Next-Schleife.
For...Next Das Ziel einer Schleife ist es, einen Codeblock wiederholt auszuführen und normalerweise dann anzuhalten, wenn eine Bedingung wahr ist. (Obwohl eine Schleife eigentlich nicht immer anhalten muss. Diese Situation nennt man Endlosschleife.) Die For-Schleife führt einen Codeblock so oft aus, wie es angegeben wurde. Die Syntax dieses Steuerelements ist: For = to auszuführender Code Next
Die Zählervariable nach der beendenden Next-Anweisung ist optional, aber mit ihr sehen Sie leichter, zu welcher For-Schleife diese Next-Anweisung gehört und sie ist gleichzeitig guter Programmierstil.
143
Den Programmfluss steuern
Die Zählervariable Die Zählervariable wird bei jedem Schleifendurchlauf erhöht, vom Startwert bis hin zum Endwert. Wenn dieser Endwert erreicht ist, beendet die Schleife die Ausführung und das Programm fährt in der Zeile fort, die direkt auf die Next-Anweisung folgt. Um uns dieses Konzept mit echten Werten anzusehen, erzeugen wir eine Visual Basic .NET-Version des ersten Programms, das ich jemals geschrieben habe (siehe Listing 4.9). Listing 4.9: Geben Sie immer und immer wieder Ihren Namen aus. 1 Dim iCounter As Integer 2 For iCounter = 1 to 10 3 System.Console.WriteLine("Duncan Mackenzie") 4 Next iCounter
Natürlich verwende ich normalerweise Endwerte im Tausenderbereich... Mein Name würde immer wieder auf dem Bildschirm erscheinen! Die Zählervariable ist eine echte Variable und ebenso wie in Listing 4.9 muss sie deklariert werden, ehe Sie sie als Teil Ihrer Schleife verwenden können. Sie müssen außerdem sicherstellen, dass Sie den korrekten Datentyp für diese Variable verwenden. In Listing 4.9 wird die Variable iCounter verwendet, um die Werte 1 bis 10 zu speichern, wobei Integer oder Byte die geeignetsten Datentypen wären. In anderen Situationen gehen Sie vielleicht mit viel größeren Zahlen um und benötigen dann einen Long Integer. Mehr Informationen zu den verschiedenen Datentypen, einschließlich der Wertebereiche, die sie speichern können, finden Sie am Tag 3. Wie bereits erwähnt wurde, wird die Zählervariable bei jedem Durchlauf der Schleife erhöht, was häufig nützlich ist, da Sie diese Variable in Ihrem Code verwenden können. Verwenden Sie den am besten geeigneten Datentyp für die Situation; verwenden Sie keinen Long Integer, wenn auch ein Integer geeignet wäre. Wenn der Wertebereich klein ist oder sehr groß werden könnte, müssen Sie sicherstellen, dass Sie den Datentyp verwenden, der den größten möglichen Zahlenbereich am besten abdeckt. Verändern Sie den Wert der Zählervariablen innerhalb der Schleife nicht. Die eingebaute Funktionalität der For-Schleife erhöht die Zählervariable jedes Mal, wenn der Code innerhalb der Schleife ausgeführt wird. Widerstehen Sie der Versuchung, den Wert der Variablen selbst zu verändern. Dies führt nur zu seltsamen Fehlern und unverständlichem Code. Listing 4.10 zeigt, wie Sie die Zählervariable als Teil Ihres Codes verwenden könnten. Eine kleine Funktion namens WeekdayName erzeugt eine Liste der regulären Arbeitstage der Woche.
144
Schleifen
Listing 4.10: Die Zählervariable verwenden 1 Public Class DaysOfTheWeek 2 3 Shared Sub Main() 4 Dim sDayName As String 5 Dim iFirstDay As Integer 6 Dim iLastDay As Integer 7 Dim iCurrentDay As Integer 8 9 iFirstDay = 2 10 iLastDay = 6 11 For iCurrentDay = iFirstDay to iLastDay 12 System.Console.WriteLine(WeekdayName(iCurrentDay)) 13 Next iCurrentDay 14 15 End Sub 16 17 Shared Function WeekdayName(ByVal iDayNumber As Integer) As String 18 Dim sWeekdayName As String 19 20 Select Case iDayNumber 21 Case 1 22 sWeekdayName = "Sunday" 23 Case 2 24 sWeekdayName = "Monday" 25 Case 3 26 sWeekdayName = "Tuesday" 27 Case 4 28 sWeekdayName = "Wednesday" 29 Case 5 30 sWeekdayName = "Thursday" 31 Case 6 32 sWeekdayName = "Friday" 33 Case 7 34 sWeekdayName = "Saturday" 35 36 Case Else 37 sWeekdayName = "Invalid Day Number" 38 End Select 39 Return sWeekdayName 40 End Function 41 End Class
Beachten Sie, dass in Listing 4.10 der Sonntag als Tag 1 angesehen wird, sodass dieser Code das folgende Ergebnis erzeugen würde:
145
Den Programmfluss steuern
Monday Tuesday Wednesday Thursday Friday
Dieses Beispiel ist noch nicht ganz ausgereift, da es die Liste der Namen nur in Englisch erzeugt und die Einstellungen des Rechners unberücksichtigt lässt. Es gibt andere, etwas kompliziertere Methoden, um diese Funktionalität mit den Ländereinstellungen des Anwenders zu verbinden. Wir werden am Tag 8, Einführung in das .NET-Framework, auf diesen Punkt zurückkommen.
Mit Step eine Inkrementierung bestimmen In den vorangegangenen Beispielen wurde die Zählervariable bei jedem Durchlauf der Schleife um eins erhöht, aber es ist möglich, diese Inkrementierung explizit anzugeben. Nach dem Endwertabschnitt der For-Anweisung können Sie Step angeben und die Zählervariable wird um den von Ihnen gewählten Wert erhöht. Nehmen Sie den Beispielcode in Listing 4.11 als Ausgangspunkt und probieren Sie für First, Last und Increment verschiedene Werte aus, um die Ergebnisse zu sehen. Listing 4.11: ForExample.vb 1 Imports System 2 Public Class ForExample 3 4 Shared Sub Main() 5 Dim iCounter As Integer 6 Dim iFirstValue As Integer 7 Dim iLastValue As Integer 8 Dim iIncrement As Integer 9 10 iFirstValue = 0 11 iLastValue = 100 12 iIncrement = 10 13 For iCounter = iFirstValue to iLastValue Step iIncrement 14 System.Console.WriteLine(iCounter) 15 Next iCounter 16 17 End Sub 18 End Class
Eine interessante und nützliche Funktion der Option Step in der For-Schleife ist, dass Sie einen Wertebereich in umgekehrter Reihenfolge durchlaufen können. Probieren Sie die
146
Schleifen
Werte 10, 0 und 1 für die Variablen First, Last bzw. Increment aus. Es wird nichts ausgegeben, da Last bereits kleiner als First ist, aber wenn Sie den Wert Increment von 1 auf -1 ändern, wird etwas sehr Schönes geschehen. (Nehmen Sie mich nicht einfach beim Wort, probieren Sie es aus!) Ah, Sie erhalten eine Schleife, die genauso häufig ausgeführt wird, wie Sie es möchten, und nun werden die Zahlen rückwärts aufgelistet. Kaum zu glauben, dass es noch besser wird, aber das wird es. Warten Sie, bis wir zur Do-Schleife kommen!
While...End While Die For-Schleife ist zwar nützlich, aber nur eingeschränkt. Sie ist für Situationen gedacht, in denen Sie wissen, wie häufig die Schleife durchlaufen werden soll. Dies ist jedoch nicht immer der Fall. Daher enthält Visual Basic zwei flexiblere Schleifen. Die erste der beiden, die While...End While-Schleife, wird so lange durchlaufen, bis ein besonderer boolescher Ausdruck wahr ist: While auszuführender Code End While Boolescher Ausdruck kann jeder gültige boolesche Ausdruck sein, wie in einer If-Anweisung, und kann daher komplexe Bedingungen unterstützen. Eine While-Schleife kann z.B. einfach die Funktionalität der For-Schleife kopieren, wie es in Listing 4.12 gezeigt wird. Listing 4.12: WhileExample.vb 1 Imports System 2 Public Class WhileExample 3 Shared Sub Main() 4 Dim iCounter As Integer 5 Dim iFirstValue As Integer 6 Dim iLastValue As Integer 7 Dim iIncrement As Integer 8 iFirstValue = 0 9 iLastValue = 100 10 iIncrement = 10 11 While iCounter iTotal
Im Gegensatz zur Wahl zwischen While oder Until hat die Position Ihrer Abbruchsbedingung eine wichtige Auswirkung auf die Ausführung Ihrer Schleife. Wenn Sie die Bedingung an den Anfang der Schleife setzen, wird diese Bedingung vor jedem Durchlauf der Schleife überprüft, auch vor dem allerersten Mal. Wenn diese Bedingung nicht zutrifft,
149
Den Programmfluss steuern
dann wird die Schleife nicht betreten und der Code wird kein einziges Mal ausgeführt. Umgekehrt wird die Bedingung nach jedem Durchlauf des Codes überprüft, wenn Sie sie zur Loop-Anweisung setzen. Unabhängig vom Wert der Bedingung wird der Code immer mindestens einmal ausgeführt. Mit all diesen Optionen gibt es insgesamt vier verschiedene Konfigurationen der Do-Schleifenanweisung, was sie zur flexibelsten Schleife macht. Sie müssen sich immer noch zwischen diesen vier Möglichkeiten entscheiden und daher werden wir ein paar Punkte erläutern, um Ihnen bei der Entscheidung zu helfen: 쐽
Sie können zwischen While und Until wechseln, indem Sie einfach den booleschen Ausdruck negieren.
쐽
Wenn Sie zwischen While und Until wählen, verwenden Sie immer die Version, mit der Sie Ihre Bedingungsanweisung nicht negieren müssen. Das führt zu einem etwas einfacheren booleschen Ausdruck und im Code bedeutet »einfacher« im Allgemeinen auch besser.
쐽
Die Positionierung der Bedingungsanweisung ist sehr wichtig. Wenn Sie sie an den Beginn der Schleife setzen, wird die Schleife gar nicht ausgeführt, wenn die Bedingung nicht zutrifft. Wenn Sie sie ans Ende platzieren, dann wird die Schleife beim ersten Mal ausgeführt, egal was geschieht.
쐽
Wählen Sie zwischen den beiden möglichen Positionen, indem Sie feststellen, was Sie erreichen möchten: Soll die Schleife überhaupt nicht ausgeführt werden, wenn die Bedingung nicht zutrifft, oder soll sie auf alle Fälle einmal ausgeführt werden? Die Do While-Schleife kann anstelle der While-Schleife verwendet werden, da sie genau den gleichen Effekt hat. Es ist nicht ungewöhnlich, dass sich Programmierer vollständig für diese Anweisung entscheiden.
Abbruchsbedingung Die Abbruchsbedingung einer jeden Schleife ist der Ausdruck, der getestet wird, um zu bestimmen, wann die Schleife enden soll. Im Falle einer While- oder Do-Schleife sind diese Abbruchsbedingungen klar definiert und erscheinen zu Beginn oder am Ende. In einer For-Schleife ist die Abbruchsbedingung durch das Setzen der oberen und unteren Grenzen impliziert. In jeder dieser Schleifenmethoden gibt es aber noch die Möglichkeit, das Ende der Schleife mit der Exit-Anweisung anzugeben. Es gibt für jede Schleife eine entsprechende Exit-Anweisung (Exit For, Exit Do und Exit While). Wenn die entsprechende Anweisung ausgeführt wird, wird die Schleife sofort verlassen und das Programm fährt mit der Zeile fort, die auf das Ende der Schleife folgt.
150
Schleifen
Sie werden zwar viele Gelegenheiten finden, bei denen es so aussieht, als wären diese Anweisungen die perfekte Methode, Ihrem Programm Wohlverhalten beizubringen, doch diese gehören zu den vielen Beispielen für schlechte Programmierpraxis. Wenn Sie die Exit-Anweisung angeben, haben Sie eigentlich nur Ihre Abbruchsbedingung um einen zusätzlichen Teil erweitert, aber nicht sehr deutlich. Am besten wäre es, diese zweite Bedingung zur Hauptabbruchsbedingung Ihrer Schleife hinzuzufügen. In den folgenden Beispielen werden Sie einige gängige Fälle von Exit-Anweisungen sehen, und den entsprechenden Code, den Sie statt dessen verwenden können.
Beispiel 1: Mit einer For-Schleife ein Array durchsuchen Ein Array mit einer festen Größe können Sie mit einer For-Schleife durchsuchen und die Schleife mit Exit For beenden, wenn Sie eine Überstimmung gefunden haben. For i = 1 to 100 If arrNames(i) = "Joe" Then System.Console.WriteLine("Found it at #" & i) Exit For End If Next i
Das eigentliche Problem ist, dass Sie erst gar keine For-Schleife hätten verwenden sollen, aber man weiß vorher nicht, wie viele Schleifen man durchlaufen muss. Dieser Code wäre deutlicher, wenn Sie eine Do-Schleife verwendeten, die überprüft, ob die obere Grenze überschritten und ob eine Übereinstimmung gefunden wird: i = 1 Do Until i > 100 or arrNames(i) = "Joe" i = i + 1 Loop
Beispiel 2: Auf einen Fluchtwert hin prüfen In Schleifen, in denen Sie die Eingabe des Anwenders erhalten möchten, Sie es ihm aber auch erlauben möchten, alles abzublasen, werden häufig zwei Abbruchsbedingungen und eine Exit-Anweisung verwendet: iCurrentGuess = 0 iTarget = 5 Do Until iCurrentGuess = iTarget iCurrentGuess = GetNextGuess() If iCurrentGuess = -1 Then Exit Do End If Loop
151
Den Programmfluss steuern
Die wahre Abbruchsbedingung ist wiederum komplizierter, als es diese Schleife erscheinen lässt. Eine geeignete Lösung wäre: iCurrentGuess = 0 iTarget = 5 Do Until (iCurrentGuess = iTarget) Or (iCurrentGuess = -1) iCurrentGuess = GetNextGuess() Loop
Wenn diese Exit-Anweisungen so schlecht sind, werden Sie sich wundern, warum ich sie überhaupt vorstelle. Ich kann zwar nicht darauf zählen, dass Sie sie in Ihrem eigenen Code vermeiden werden, aber die meisten Programmierer arbeiten häufig mit Code, der von anderen geschrieben wurde. Man muss verstehen, was man in diesem Code vorfindet, egal was es auch sei.
Endlosschleifen Jede Schleife kann einen Fehler haben, aber ein besonders nerviger Fehler ist es, wenn eine Schleife ohne Unterbrechung ausgeführt wird. Dadurch sind Sie gezwungen, das Programm zu beenden, um die Schleife anzuhalten. Die Tatsache, dass Sie in Ihrer DoSchleife keine Bedingung angeben müssen, macht sie ein wenig anfälliger für diese Fehlerart. Wenn Sie das Programm ausführen und es nie zu enden scheint, verwenden Sie in einem DOS-Programm die Tastenkombination (Strg)+(C), um es zu beenden. Wenn Sie die Visual Studio-IDE benutzen, erzwingen Sie die Beendigung der Ausführung mit der Tastenkombination (Strg)+(Pause). Stellen Sie sicher, dass Sie für jede erzeugte Schleife eine Abbruchsbedingung haben.
Machen Sie die Abbruchsbedingung nicht zu komplex. Sie muss bei jedem Durchlauf der Schleife ausgewertet werden.
Endlosschleifen entstehen häufig durch eine vergessene Inkrementierung der Zählervariablen (in anderen Schleifen als For), durch das Zurücksetzen einer Variablen, die erhöht werden sollte, oder durch die Verwendung einer Abbruchsbedingung, die niemals eintritt.
152
Schleifen
Konsequenzen für die Leistung Es gibt verschiedene nützliche Tipps, die Ihnen helfen, die beste Leistung aus den Schleifen in Ihrem Code herauszuholen. Denken Sie als Erstes immer daran, dass eine Leistungsoptimierung innerhalb einer Schleife viel vorteilhafter ist als an einer anderen Stelle im Programm. Da Code in einer Schleife immer wieder ausgeführt wird, potenziert sich jede Leistungsverbesserung in diesem Code mit der Anzahl der Iterationen in Ihrer Schleife. Sehen Sie sich als Beispiel den Code in Listing 4.14 an. Listing 4.14: LoopPerformance.vb 1 Public Class LoopPerformance 2 3 Shared Sub Main() 4 Dim i As Integer 5 6 For i = 1 to 1000 7 System.Console.WriteLine(UserName()) 8 Next i 9 End Sub 10 11 Shared Function UserName() As String 12 Dim sName As String 13 sName = System.Environment.UserName 14 UserName = sName 15 End Function 16 17 End Class
Der Benutzername wird durch eine kleine Funktion geliefert, die das .NET-Framework nutzt, um Ihre aktuellen Sicherheitsinformationen herauszufinden. Listing 4.14 zeigt einen häufigen Fehler in Bezug auf die Leistung. Der Aufruf der Funktion UserName geschieht innerhalb der Schleife, was bedeutet, dass die Funktion tausend Mal aufgerufen wird. Dies führt jedes Mal wahrscheinlich zu einer Art von Betriebssystemaufrufs, um den aktuellen Benutzernamen zu erhalten. Da man davon ausgehen kann, dass sich der derzeitige Anwender innerhalb der Schleife nicht ändert, ist es viel effektiver, Ihre Schleife statt dessen wie in Listing 4.15 zu erstellen. Der Wert der Funktion UserName ändert sich nicht und ist daher in diesem zweiten Listing nicht enthalten. Listing 4.15: LoopPerformance_Better.vb 1 Public Class LoopPerformance 2 3 Shared Sub Main() 4 Dim i As Integer 5 Dim sName As String
153
Den Programmfluss steuern
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 End
sName = UserName() For i = 1 to 1000 System.Console.WriteLine(sName) Next i End Sub Shared Function UserName() As String Dim sName As String sName = System.Environment.UserName UserName = sName End Function Class
Ein weiterer Leistungstipp ist immer dann wichtig, wenn Sie einen booleschen Ausdruck verwenden: Stellen Sie sicher, dass die einfachsten Teile des Ausdrucks auf der linken Seite stehen, und verwenden Sie dann das kurzschließende ANDALSO und ORELSE. Mit diesen Versionen des booleschen Operators ist es möglich, nur die linke Seite des Ausdrucks auszuwerten, sodass die linke Seite die schnellere der beiden Klauseln sein sollte.
4.5
Umsetzung Ihres neuen Wissens
Da Sie nun wissen, wie Bedingungsanweisungen wie If und Select Case sowie verschiedene Schleifentypen verwendet werden, können Sie aus diesen Anweisungen einige interessante Beispielprogramme erstellen. Für diese Beispiele benötigen Sie etwas, das über das bisher gelernte Visual Basic hinausgeht: die .NET Framework-Klassen. Diese Klassen, die bis zu einem gewissen Maß in allen vorangegangenen Beispielen vorhanden waren, sind Gruppen von bestehendem Code, der gepackt und Programmen zur Verfügung gestellt wurde, weil Visual Basic eine .NET-Sprache ist. Diese Codegruppen werden als Objekte zur Verfügung gestellt. Das ist im Grunde eine Methode, ein Konzept oder eine Entität ohne Code darzustellen. Immer dann, wenn Sie ein Objekt wie System.Console oder System.Security.Principal verwenden, arbeiten Sie mit einem bestimmten Teil des .NET-Frameworks. Dafür müssen Sie Visual Basic häufig sagen, dass Sie es verwenden möchten, indem Sie eine Zeile wie Imports System.Security.Principal in Ihren Code einfügen. Stellen Sie sich diese Objekte als Teil von .NET vor. Sie können Ihren Programmen eine enorme Funktionalität bringen, sodass nicht jeder einzelne Programmierer diese Funktionalität selbst erstellen muss. Das ist in Visual Basic .NET ein wichtiges Konzept und ich werde es später erneut behandeln, sodass Sie keine Sorgen haben müssen, wenn es an dieser Stelle noch nicht restlos deutlich wurde.
154
Umsetzung Ihres neuen Wissens
Aus einer Datei lesen Eine Datei von der Festplatte zu lesen, ist für viele Programme eine oft erforderliche Funktionalität, die vom .NET-Framework zur Verfügung gestellt wird. In dieser Lektion werden Sie zwei verschiedene Objekte verwenden, die beide Teil des System.IO-Teils des Frameworks sind, nämlich System.IO.File und System.IO.StreamReader. Sie stellen die tatsächliche Datei auf der Festplatte und das eigentliche Auslesen der Datei in den Speicher dar. Das Objekt StreamReader wird mit der Methode OpenFile des Objekts File erzeugt, wobei Sie den Pfad und Dateinamen der zu öffnenden Datei angeben. Mit diesem StreamReaderObjekt können Sie nacheinander jede Zeile der Datei lesen, indem Sie seine ReadLineMethode verwenden, bis Sie zum Ende der Datei gelangen. Um zu überprüfen, dass Sie das Ende der Datei erreicht haben, vergleichen Sie den letzten eingelesenen Wert mit der besonderen Konstanten Nothing. Diese erlaubt es Ihnen, zwischen einer leeren Zeile in einer Datei und dem tatsächlichen Ende der Datei zu unterscheiden. Da Nothing ein besonderer Wertetyp ist, vergleichen Sie Ihre Zeichenkette mithilfe des Operators is mit diesem Wert, statt einen regulären Gleichheitsoperator einzusetzen. In Listing 4.16 initialisieren Sie zuerst alle Ihre Objekte, während Sie das StreamReader-Objekt erhalten, das auf die Datei verweist, in der Sie lesen möchten. Sie benötigen für diese Übung eine Beispieltextdatei, aber jede Ihrer anderen .vb-Beispielprogrammdateien ist ebenfalls geeignet. Listing 4.16: Schritt 1: Einrichten 1 Public Class ReadFromFile 2 3 Shared Sub Main() 4 Dim sFileName As String 5 Dim srFileReader As System.IO.StreamReader 6 Dim sInputLine As String 7 8 sFileName = "MySampleFile.txt" 9 srFileReader = System.IO.File.OpenText(sFileName) 10 11 End Sub 12 End Class
Wenn Sie Ihr StreamReader-Objekt definiert haben, das so initialisiert wurde, dass es auf die Beispieldatei verweist, können Sie mit einer Do While-Schleife (siehe Listing 4.17) die Datei einlesen, und zwar immer eine Zeile nach der anderen. Sie können auch jede Zeile ausgeben, während Sie sie einlesen, damit Sie wissen, was das Programm tut. Listing 4.17: Schritt 2: Fügen Sie diesen Code über der End Sub-Anweisung in ein. 1 2
sInputLine = "something" Do Until sInputLine is Nothing
155
Den Programmfluss steuern
3 4 5
sInputLine = srFileReader.ReadLine() System.Console.WriteLine(sInputLine) Loop
Ehe Sie die Schleife zum ersten Mal betreten, müssen Sie sich davon überzeugen, dass Ihre Bedingung erfüllt ist. Initialisieren Sie also sInputLine, um sicherzustellen, dass es nicht Nothing ist. In Listing 4.18 werden Sie versuchen, sInputLine auszugeben, auch wenn es Nothing ist. Sie könnten aber eine If-Anweisung hinzufügen, um diese Möglichkeit zu überprüfen. Listing 4.18: Eine Überprüfung auf Nothing hinzufügen 1 2 3 4 5 6 7
sInputLine = "something" Do Until sInputLine is Nothing sInputLine = srFileReader.ReadLine() If Not sInputLine is Nothing Then System.Console.WriteLine(sInputLine) End If Loop
Alternativ können Sie eine leicht veränderte Schleifenmethode verwenden (siehe Listing 4.19) und sicherstellen, dass Sie nicht versuchen, noch etwas auszugeben, nachdem das Ende der Datei bereits erreicht wurde. Listing 4.19: Eine bessere Schleife 1 Public Class ReadFromFile 2 3 Shared Sub Main() 4 Dim sFileName As String 5 Dim srFileReader As System.IO.StreamReader 6 Dim sInputLine As String 7 8 sFileName = "MySampleFile.txt" 9 srFileReader = System.IO.File.OpenText(sFileName) 10 sInputLine = srFileReader.ReadLine() 11 Do Until sInputLine is Nothing 12 System.Console.WriteLine(sInputLine) 13 sInputLine = srFileReader.ReadLine() 14 Loop 15 End Sub 16 End Class
Jede Methode funktioniert, aber die endgültige Anwendung in Listing 4.19 hat den einfachsten (und daher auch besten) Code innerhalb der Schleife. Wenn Sie diesen Code ausführen möchten, müssen Sie eine Textdatei mit Beispielinhalt im gleichen Verzeichnis erzeugen, in dem sich auch Ihre kompilierte ausführbare Datei befindet.
156
Umsetzung Ihres neuen Wissens
Ein einfaches Spiel Eine gängige Verwendung für eine Schleife ist, den Anwender wiederholt nach einer Antwort zu fragen, bis Sie die gewünschte erhalten. Das mag nervtötend klingen (ähnlich wie die Fragen eines kleinen Kindes), aber im richtigen Programm kann das sehr nützlich sein. Für dieses Beispiel werden Sie ein einfaches Zahlenratespiel erstellen. Mit diesem Spiel hat mein Vater mich beschäftigt, wenn wir in einem Restaurant auf das Essen warteten, obwohl wir es natürlich nicht so technisch gespielt haben. Als Erstes haben wir eine Unterund Obergrenze auf den oberen und unteren Rand einer Serviette geschrieben, z.B. 1 und 100, und dann wählte mein Vater willkürlich eine Zahl aus (Eltern-willkürlich, nicht Computer-willkürlich) und schrieb sie auf die Rückseite der Serviette. Ich habe dann Zahlen geraten, bis ich die richtige gefunden hatte, und mein Vater sagte mir immer, ob ich zu hoch oder zu niedrig geraten hatte. Ich bezweifle zwar, ob ich dabei sehr methodisch vorgegangen bin, aber diese Information machte es doch leichter. Als Teil des beständigen Strebens, Dinge zu technisieren, die nicht technisiert werden müssen, wollen wir ein Computerprogramm erstellen, um »Rate die Zahl!« zu spielen. Die Grundzüge dieses Programms wurden oben umrissen. Wir können meine kleine Geschichte als Pseudocode für das Programm verwenden. Am besten formulieren wir die Einzelheiten schnell in eine deutlichere Definition dessen um, was das Programm tun soll: 1. Frage den Anwender nach einer numerischen Ober- und Untergrenze. 2. Bestimme innerhalb dieses Bereichs eine Zufallszahl, die das Ziel ist. 3. Bitte den Anwender um eine Vermutung. 4. Wenn der Anwender Recht hat, halte das Spiel an und teile dem Anwender mit, wie viele Versuche er gebraucht hat. 5. Teile dem Anwender andernfalls mit, ob er zu hoch oder zu niedrig liegt, und gehe zu Schritt 3 zurück. Dieser Pseudocode ist nicht komplex, aber er birgt ein neues Konzept, das Sie noch lernen müssen: die Erzeugung von Zufallszahlen. Zum Glück bietet das .NET Framework eine Klasse für genau diesen Zweck, nämlich System.Random. Mit diesem Objekt können Sie eine Zufallszahl erzeugen, die zwischen der Ober- und Untergrenze liegt, indem Sie Code wie den folgenden verwenden: Dim iTargetNumber, iUpperBound, iLowerBound As Integer Dim objRandom As System.Random = New System.Random iUpperBound = 100 iLowerBound = 1 iTargetNumber = objRandom.Next(iLowerBound,iUpperBound + 1)
157
Den Programmfluss steuern
Diese Methode des Generierens von Zufallszahlen gibt Werte zurück, die größer oder gleich der Untergrenze und kleiner als die Obergrenze sind (iLowerBound iTargetNumber Then System.Console.WriteLine("Your Guess is High!") End If Loop System.Console.WriteLine("You did it in " & iGuessCount & " guesses")
Kombiniert bilden diese beiden Listings das vollständige Spiel, das Sie kompilieren und ausprobieren können. Sie sollten das abschließende WriteLine in Listing 4.21 vor dem Spielen entfernen, sonst wäre es ein wenig zu einfach. Es gibt eine sehr methodische und daher langweilige Art, dieses Spiel zu spielen. Sie garantiert, dass Sie das richtige Ergebnis innerhalb einer bestimmten Anzahl von Versuchen finden. Sie können jedes Mal einfach die Mitte des Bereichs tippen und mit den Hoch-/Niedriginformationen einen neuen Bereich (mit der halben Größe) erzeugen, der zwischen der von Ihnen geratenen Zahl und der oberen oder unteren Grenze liegt. Bei dem Beispiel zwischen 1 und 100 findet man mit dieser Methode die Lösung garantiert mit sieben Versuchen (oder weniger), basierend auf einer mathematischen Formel. Kennen Sie die Formel, die Ihnen sagt, wie viele Versuche für jeden beliebigen Wertebereich nötig sind? Die Antwort finden Sie im Übungsabschnitt der heutigen Lektion.
4.6
Komplexe Schleifen durch Rekursion vermeiden
Häufig gibt es mehr als eine Möglichkeit, um ein bestimmtes Problem zu lösen. Obwohl eine Standardschleife funktionieren mag, kann es eine einfachere Methode geben. Wenn ein Problem genau richtig strukturiert ist oder wenn Sie es auf die richtige Art restrukturieren können, nutzen Sie Rekursion als Alternative zu regulären Schleifen. In der Programmierung taucht Rekursion dann auf, wenn ein Programm oder eine Prozedur sich selbst
159
Den Programmfluss steuern
aufruft, um ein Problem zu lösen. Rekursion kann die Probleme lösen, bei denen eine Teilmenge des Problems genau die gleiche Struktur wie das Gesamtproblem hat. Am besten wird dieses Konzept an einem Beispiel erklärt: der Berechnung einer Fakultät. Die Formel für die Fakultät eines Wertes (n!) ist n(n-1)(n-2)…(n-(n-1)). Bei dem Wert 10 wird aus dieser Formel 10 * 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * 1 = 3628800. (Beachten Sie, dass das Ergebnis im Verglich zu n relativ groß ist). Diese Formel kann mit einer For-Schleife ausgedrückt werden, wie Sie in Listing 4.22 sehen. Listing 4.22: Factorial.vb 1 Public Class Factorial 2 Shared Sub Main() 3 Dim sInput As String 4 Dim iInput As Integer 5 Dim iCounter As Integer 6 Dim iFactorial As Integer 7 System.Console.Write("Please Enter A Number: ") 8 sInput = System.Console.ReadLine() 9 iInput = CInt(sInput) 10 iFactorial = 1 11 For iCounter = 0 to (iInput – 1) 12 iFactorial = (iInput – iCounter) * iFactorial 13 Next iCounter 14 System.Console.WriteLine(iFactorial) 15 End Sub 16 End Class
Wenn Sie mit diesem Programm experimentieren, muss Ihr Testwert (in unserem Beispiel 10) kleiner oder gleich 12 sein. Jeder größere Wert erzeugt eine Fakultät, die die Maximalgröße einer Integer-Variablen übersteigt (siehe Tag 3). Wenn Sie Unterstützung für höhere Werte benötigen, verwenden Sie andere Variablentypen wie z.B. Long. Das ist aber nicht die einzige Möglichkeit, das Ergebnis einer Fakultät zu erzeugen, da diese Formel auch als n * (n-1)! ausgedrückt werden kann, also n multipliziert mit Fakultät von n1. Dieser Ausdruck definiert die Formel rekursiv. Die Lösung der Berechnung einer Fakultät enthält eine weitere Fakultät. Das ist eine deutlichere Definition als die Formel n(n-1)(n2)…(n-(n-1)) und wenn Sie Code auf diese Art schreiben (siehe Listing 4.23), erzeugen Sie Code, der auch einfacher als die entsprechende Routine aus Listing 4.22 ist. Listing 4.23: RecursiveFactorial.vb 1 Public Class RecursiveFactorial 2 Shared Sub Main() 3 Dim sInput As String
160
Zusammenfassung
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 End
Dim iInput As Integer Dim iCounter As Integer Dim iFactorial As Integer System.Console.Write("Please Enter A Number: ") sInput = System.Console.ReadLine() iInput = CInt(sInput) System.Console.WriteLine(Factorial(iInput)) End Sub Shared Function Factorial(n as Integer) as Integer If n = 1 Then Return 1 Else Return n * Factorial(n-1) End If End Function Class
Der erzeugte Code mag zwar nicht kürzer als Listing 4.22 sein, aber er ist einfacher und das allein ist schon ein lohnendes Ergebnis. Beachten Sie, dass Sie in der Funktion Factorial() auf n=1 prüfen. Diese Überprüfung stellt sicher, dass die rekursiven Aufrufe irgendwann enden, ebenso wie eine Exit-Bedingung in einer Schleife. Ohne sie würde das Programm die Ausführung niemals beenden, wie bei einer Schleife. Rekursion kann viele Probleme lösen und Sie werden Code sehen, der die Rekursion in vielen verschiedenen Systemen verwendet (und hoffentlich eigene schreiben).
4.7
Zusammenfassung
Heute haben Sie etwas über Steueranweisungen gelernt, die Basis für viele der Computerprogramme, die Sie in Zukunft erstellen werden. Mit diesen Anweisungen können Sie damit beginnen, echte Prozesse in Computerprogrammen nachzubilden. Dazu müssen Sie sie zuerst in Pseudocode umwandeln und dann mit diesem den eigentlichen Code erzeugen, den Sie benötigen.
161
Den Programmfluss steuern
4.8 F
Ein Freund hat mir erzählt, dass While-Schleifen besser sind als Do-Schleifen und dass ich die For-Schleife niemals verwenden sollte! Welche Schleife ist die beste? A
F
Es ist zwar möglich, nur eine Schleife für alle Ihre Programme zu verwenden, aber das bringt keinen Vorteil mit sich. Am Ende zählt nur, dass Ihr Code so klar und einfach ist, wie es nur geht. Verwenden Sie die Schleife, die am besten zum Problem passt. Im Allgemeinen funktioniert eine For-Schleife am besten, wenn Sie eine feste Anzahl von Iterationen benötigen. Verwenden Sie eine Do- oder WhileSchleife, wenn das nicht der Fall ist.
Sind einzeilige If-Anweisungen schneller als die If...End If-Form? A
F
Fragen und Antworten
Nein. Der Visual-Basic-Compiler wandelt beide Formen in das gleiche Ergebnis um, sodass es zwischen diesen beiden Formaten keinen Unterschied in der Ausführungsgeschwindigkeit gibt. Der Hauptunterschied liegt in der Wartungsfreundlichkeit und Lesbarkeit des Codes.
In früheren Versionen von Visual Basic und in Visual Basic for Applications (VBA) habe ich eine Form der If-Anweisung namens Immediate If (IIF) verwendet. Existiert diese in Visual Basic .NET? A
4.9
Ja, tut sie, aber nur als Teil einer besonderen Gruppe von Objekten (Microsoft.VisualBasic), die Zugang zu IIF und vielen anderen Teilen der letzten Version von Visual Basic bieten und die in Visual Basic .NET nicht mehr vorhanden sind. Die Tatsache, dass diese Funktionen in der letzten Version von Visual Basic nicht eingebaut waren, könnte bedeuten, dass sie in zukünftigen Versionen gar nicht mehr zur Verfügung stehen. IIF ist eine sehr nützliche Form der If-Anweisung. Als Funktion kann es mitten in anderen Ausdrücken verwendet werden, wenn z.B. eine Zeichenkette ausgegeben wird. Da zukünftige Versionen von Visual Basic IIF aber eventuell nicht mehr unterstützen werden, sollten Sie die Verwendung nach Möglichkeit vermeiden.
Workshop
Der Workshop soll Ihnen dabei helfen, mögliche Fragen vorherzusehen und das Gelernte zu wiederholen. Außerdem soll er Sie dazu anregen, über die praktische Umsetzung Ihres Wissens nachzudenken. Die Antworten und Lösungen finden Sie im Anhang A.
162
Workshop
Quiz 1. Welche der drei verfügbaren Schleifenmethoden ist am besten für einen festen Wertebereich geeignet? 2. Welche der drei Schleifenmethoden ist die flexibelste? 3. Warum sollten Sie versuchen, den inneren Codeblock in einer Schleife so einfach wie möglich zu gestalten? 4. Angenommen, Sie haben den folgenden booleschen Ausdruck in Ihrem Programm: CalculatedTotalNetWorth(iCustomerID) < 10000 AND dtCurrentDate.Hour > 12. Was könnten Sie tun, um sicherzustellen, dass dieser Ausdruck so effizient wie möglich ausgewertet wird?
Übung Schreiben Sie die andere Seite des Programms NumberGuesser (»Rate die Zahl!«), das wir weiter oben in der heutigen Lektion erstellt haben – ein Programm mit Ober- und Untergrenzen, das festzustellen versucht, welcher Wert vom Anwender ausgewählt wurde. Für jeden Versuch, den das Computerprogramm benötigt, muss der Anwender mit »H« für zu hoch und »L« für zu niedrig reagieren können, oder mit »=« für korrekt. Wenn Sie das methodische Verfahren für die Rateversuche Ihres Programms verwenden, dann können Sie die Anzahl der maximal notwendigen Versuche mit dieser Gleichung bestimmen: (2N >= Obergrenze – Untergrenze), wobei N der maximalen Anzahl der Versuche entspricht. Bei einem Wertebereich von 1 bis 100 ergibt die Gleichung 7, da 26 = 64 ist und 27 = 128.
163
Anwendungsarchitektur in .NET
Anwendungsarchitektur in .NET
In diesem Buch lernen Sie, wie Sie mit Visual Basic .NET Anwendungen erstellen. Bevor Sie mit größeren Projekten beginnen, sollten Sie sich einige Gedanken zur Entwurfsphase des Entwicklungsprozesses machen. Die heutige Lektion erörtert die folgenden Themen aus diesem Bereich:
Die Themen heute 쐽
Was ist Anwendungsarchitektur?
쐽
Welche architektonischen Auswahlmöglichkeiten bietet .NET?
쐽
Welche Überlegungen fließen in die Auswahl einer Anwendungsarchitektur ein?
Die Lektion bietet nicht nur eine eingehende Beschreibung dieser Themen, sondern auch einige Beispielszenarien und eine schrittweise Erklärung, wie Sie für jedes dieser Szenarien eine Architektur finden.
5.1
Was ist eine Anwendungsarchitektur?
Ehe der Bau beginnt und lange bevor ein Gebäude bezugsfertig ist, hat ein Architekt sich mit der Entwurfsphase beschäftigt. Sobald es einen Konsens über das Projekt gibt, werden Skizzen erstellt, die Einzelheiten des geplanten Gebäudes zeigen. Diese Skizzen bilden die Basis der detaillierten Baupläne, die vor dem Baubeginn erstellt werden müssen. Während dieses Prozesses, nicht nur zu Beginn, ist ein Architekt zusammen mit dem ganzen Projektteam für das richtige Ergebnis verantwortlich. Das Konzept eines Architekten und die Projektaspekte, für die er verantwortlich ist, wurden von der Software-Entwicklungsindustrie übernommen. Die angedeutete Ähnlichkeit zwischen den beiden Bereichen wird echten Architekten wohl nicht gefallen und ich mache ihnen keinen Vorwurf deswegen. Wenn Gebäude auf die gleiche Art konstruiert würden wie der Großteil der Softwaresysteme, dann würde ich den Rest meines Lebens unter freiem Himmel verbringen. Ein Großteil der Geschäftssoftware wird ohne ausreichende Planung entwickelt, was zu unstabilen, schwer zu pflegenden und fast immer überteuerten Systemen führt.
Die Rolle des Softwarearchitekten Unabhängig davon, ob es angemessen ist, eine tatsächliche Berufsbezeichnung zu verwenden, lassen sich Parallelen ziehen. Wenn ein Gebäude entworfen wird, nutzen die Architekten ihr Wissen über Funktion und Design, um sich die gesamte Struktur und die Basis eines Gebäudes gemäß den definierten Anforderungen vorzustellen und zu planen. Ein
166
Was ist eine Anwendungsarchitektur?
Software- (oder System-)Architekt entwickelt anhand der Anforderungen einen Plan für die Systemerstellung. In beiden Fällen wird der Plan schließlich in einen detaillierten Entwurf für die Erstellung des Projekts umgewandelt und andere Teams übernehmen die tatsächliche Implementierung. Die Softwareentwicklung ist kompliziert, obwohl die meisten Menschen sie als nicht annähernd so kompliziert ansehen wie das Entwerfen und Bauen eines großen Wolkenkratzers, und die Rolle des Architekten wird zweifellos bei fast jeder Systemgröße benötigt. Egal wie groß die Anwendung ist oder wie klein die einzelnen Teile des Systems sind, wurde doch auf jeden Fall irgendeine Anwendungsarchitektur ausgewählt (eventuell informell) und verwendet. Es mag so scheinen, als würden Sie bei vielen kleineren Projekten ohne einen Architekten auskommen, aber in Wahrheit haben Sie (oder ein anderes Mitglied des Entwicklungsteams) diese Rolle inoffiziell selbst übernommen. Wenn jemand eine Projektrolle nur inoffiziell einnimmt, entsteht das Problem, dass sich das Team nicht bewusst ist, wer für einen bestimmten Aspekt des Projekts verantwortlich ist. Visual Basic oder das Programmieren allgemein mag für Sie neu sein und Sie werden sich in naher Zukunft wahrscheinlich nicht als Systemarchitekt betätigen. Aber das ändert nichts an der Tatsache, dass Sie den Prozess und die architektonischen Entscheidungen verstehen müssen, die in einem Projekt gefällt werden, an dem Sie beteiligt sind. Das gesamte Team sollte während des kompletten Lebenszyklus der Softwareentwicklung am gesamten Prozess beteiligt sein, aber der Architekt ist während der Anfangsphase im Hinblick auf Idee, Umfang, Anforderungsanalyse und Entwurf die wichtigste Person (siehe Abbildung 5.1). Ide
e/U
mf an
Anforderungsanalyse
g
rf
E
u ntw
Abbildung 5.1: Im Lebenszyklus der Softwareentwicklung gehört die Architektur hauptsächlich in die Entwurfsphase.
167
Anwendungsarchitektur in .NET
Nach dieser Anfangsphase verlagert sich der Schwerpunkt auf das Implementierungsteam (Softwareentwickler) und dann auf die Test- und Entwicklungsgruppe. Im Idealfall sollte die Person, die die Rolle des Architekten innehat, hoch qualifiziert sein und genug technisches Wissen haben, um alle verfügbaren Optionen abzuschätzen. Er sollte auch genug geschäftliche Fähigkeiten besitzen, um die Systemanforderungen richtig interpretieren zu können. Häufig wird die Rolle des Architekten von einem der dienstältesten Mitglieder einer Entwicklungsgruppe übernommen.
Welche Teile eines Systems werden als Anwendungsarchitektur verstanden? Die kurze Antwort auf diese Frage ist »alle«, aber das ist ein wenig zu umfangreich, um nützlich zu sein. Die architektonischen Aspekte eines Systems sind die »großen Details«, z.B. welche Art von Benutzeroberfläche verwendet wird, ob es eine Web- oder eine Windows-Anwendung sein soll und wie der Code der Webseite mit der Back-End-Datenbank kommuniziert. Wichtig ist, dass die Systemarchitektur sich mit der Basis und dem Rahmen der Anwendung befasst und nicht mit Implementierungsentscheidungen, die innerhalb dieser Rahmenarchitektur getroffen werden. Ein Architekt ist aber auch im Implementierungsstadium involviert, obwohl es offensichtlich zu sein scheint, dass alle »groben Details« bereits festgelegt wurden und nur noch die kleineren Einzelheiten der Implementierung übrig bleiben. Der Architekt bleibt aus zwei Hauptgründen weiterhin beteiligt: um zu sehen, ob sich die ausgewählte Architektur als richtig erweist (und Angleichungen vorzunehmen, um auftretenden Problemen zu begegnen), und um sicherzustellen, dass der Entwurf wie geplant implementiert wird. Es mag schwer zu glauben sein, aber manchmal schweifen Entwickler von einem Systementwurf ab und bringen unerwünschte Ergebnisse. Um die Frage genauer zu beantworten: In .NET-Systemen sollte die Systemarchitektur einen guten Überblick über jeden Aspekt der Anwendung bieten. Der Schlüssel ist der Grad der Einzelheiten: Die meisten Dinge, die auf der architektonischen Ebene diskutiert wurden, werden im detaillierten Entwurf und in der Implementierung wieder auftauchen. Ein Teil der Systemarchitektur mag z.B. die Sicherheit sein und auf der architektonischen Ebene mag die folgende Aussage ausreichend sein, um das Thema abzuhaken: »Die Anwendung stützt ihre Sicherheitsberechtigungen auf die Windows 2000-Anwender und -Gruppen, die bereits im Unternehmen verwendet werden.« Diese Aussage ist für den Anfang ausreichend. Die nächste Ebene würde detaillierter werden und könnte Informationen darüber enthalten, welche Bereiche der Anwendung gesichert sein werden und wer Zugriff auf die einzelnen Bereiche erhält. In der Implementierungs- oder Erstellungsphase muss ein Programmierer genau feststellen, wie er den Anmeldenamen und die Gruppenzugehörigkeit des aktuellen Anwenders für Windows herausfindet, aber diese Einzelheiten sind sicherlich nicht Teil der System-
168
Was ist eine Anwendungsarchitektur?
architektur. Auch wenn die Architektur alles auf einer sehr hohen Ebene abdeckt, sollten die folgenden Schlüsselbereiche als Teil jeder Anwendungsarchitektur angesprochen werden: 쐽
Logische und physische Verteilung des Codes (welcher Code läuft wo?)
쐽
Technologien, die für die Benutzeroberfläche, Datenbank und Geschäftslogik verwendet werden
쐽
Kommunikationsmethoden zwischen den verschiedenen Komponenten des Systems und zwischen diesem System und anderen innerhalb des Unternehmens
쐽
Sicherheit
쐽
Datenzugriff
쐽
Skalierbarkeit und Verfügbarkeit
Jeder dieser Bereiche ist für sich allein schon wichtig, aber die Architektur ist die Kombination all dieser Faktoren in einem Entwurf, der als Ganzes funktioniert. Der Zweck jedes dieser Bereiche wird in den folgenden Abschnitten erörtert.
Logische und physische Verteilung des Codes Auf der architektonischen Ebene können Entscheidungen über die gewünschte Organisation gefällt werden, mit denen der Code in Gruppen wie »Datenzugriffscode«, »Sicherheitscode«, »Benutzeroberflächencode« usw. eingeteilt werden kann. In Wahrheit liegt die Aufteilung des Codes ganz bei den einzelnen Programmierern und dem Rest des Projektteams. Kein Aspekt von .NET (oder irgendeinem anderen Entwicklungswerkzeug) zwingt Sie dazu, Code in organisierter Weise zusammenzufassen. Lassen Sie sich aber durch die Realität nicht aufhalten. Für den Entwurf ist es wichtig, Code auch dann zu kategorisieren, wenn solch eine Organisation niemals tatsächlich existiert. Für diese Gruppen muss festgestellt werden, wo sie ausgeführt werden: Werden sie alle als Teil einer einzigen Anwendung auf einem einzelnen Rechner ausgeführt oder wird der Datenzugriffscode auf einem unabhängigen Server laufen? Weiter unten in der heutigen Lektion werde ich das Konzept von Ebenen oder Schichten erörtern, in denen die gesamte Funktionalität in einer Anwendung logisch nach Zwecken aufgeteilt ist. Diese Ebenen werden dann so behandelt, als ob sie unabhängige Codegruppen wären, die nach Wunsch herumgeschoben werden können. Die Entscheidungen, wo eine Ebene einer Anwendung ausgeführt werden soll und wie viele Server für jede Ebene zu verwenden sind, bilden ein Element der Systemarchitektur.
169
Anwendungsarchitektur in .NET
Technologien Als eines der leichter verständlichen Themengebiete umreißt dieser Teil der Systemarchitektur die »allgemein« verwendeten Technologien. Der Trick ist hier, genug nutzbare Informationen zu liefern und gleichzeitig Dinge zu vermeiden, die nicht festgelegt werden müssen oder die auf der architektonischen Ebene irrelevant sind. Stellen Sie sich folgende beiden Situationen vor (kümmern Sie sich nicht um die Einzelheiten, es geht nur um das Beispiel): Beispiel 1: Das Projektteam wird die Benutzeroberfläche mit Visual Basic 6.0 (Service Pack 5) und einer ActiveX-Rastersteuerung von Janus entwickeln. Verschiedene Fenster werden entwickelt, jedes mit Menüs, einem Hauptraster und verschiedenen Funktionsschaltflächen. Diese Formulare werden sich über DCOM mit den Servern der mittleren Schicht verbinden, von denen jeder mit Windows 2000 (Service Pack 2) Advanced Server läuft. Die Server der mittleren Schicht stellen fünf Komponenten innerhalb der Windows 2000 COM+Komponentendienstumgebung zur Verfügung. Diese Geschäftskomponenten werden COM-DLLs sein, die mit Visual Basic 6.0 (Service Pack 5) erstellt wurden und deren Eigenschaften auf Public gesetzt sind. Beispiel 2: Die Benutzeroberfläche wird formularbasiert sein und über DCOM mit der mittleren Schicht kommunizieren. Innerhalb der mittleren Schicht werden in COM+ COMGeschäftsobjekte installiert, die die Kommunikation mit der Datenebene behandeln werden. Mit ADO wird die Verbindung von der mittleren Ebene zu den Back-End-Daten hergestellt und alle Informationen werden an die Präsentationsschicht zurückgegeben, nachdem sie die Geschäftsschicht durchlaufen haben. Als ich Beispiel 1 abgeschnitten habe, war es noch nicht annähernd abgeschlossen und musste erst noch so viele nützliche Informationen wie Beispiel 2 liefern. Der Schlüssel ist, sich immer wieder daran zu erinnern, dass es um das Verständnis der Architektur geht. Es verbleiben immer noch detaillierte Design- und Implementierungsphasen in dem Projekt, um die Ebene der untergeordneten Details zu erreichen. Eine weitere gute Regel ist es, niemals zu viel Zeit auf die Beschreibung von bestimmten Technologien zu verschwenden. Versuchen Sie, sich zu konzentrieren und in Bezug auf die Technologie nur das zu erörtern, was dem Projekt wirklich etwas bringt.
Die Kommunikation zwischen den Komponenten Wenn Sie ein verteiltes System erstellen, das auf mehreren Rechnern ausgeführt werden soll, dann muss irgendeine Form von Kommunikation zwischen den verschiedenen Komponenten bestehen. Dafür gibt es viele verschiedene Möglichkeiten, aber auf der architektonischen Ebene müssen Sie nur die Details genau angeben, die entweder bereits
170
Was ist eine Anwendungsarchitektur?
feststehen oder wegen eines anderen Aspekts des Projekts relevant sind. Hier müssen Sie eventuell das Kommunikationsprotokoll angeben, wenn Sie sich mit anderen Systemen verbinden müssen oder wenn die technischen Details für ein Parallelprojekt wie eine Netzwerk-/Firewall-Konfiguration wichtig sind. .NET bietet verschiedene unterschiedliche Optionen für diese Kommunikationsart, einschließlich SOAP.
Sicherheit Der Begriff »Sicherheit« ist ein vager Titel für einen vagen Bereich. Sicherheit ist ein Thema, das allein schon deshalb angesprochen werden muss, weil jede Diskussion über kurz oder lang auf dieses Thema kommt und dort hängen bleibt, wenn es nicht bereits ausführlich geschildert wurde. Die Architektur sollte detailliert angeben, wie die Sicherheit gewährleistet wird (z.B. mit Windows 2000 Active Directory). Sie sollte aber zumindest im Konzept angeben, wie sie implementiert wird (»Interface-Optionen werden verborgen« oder »Sicherheit wird pro Formularseite überprüft«). Es ist nicht notwendig, genau anzugeben, wie die Programmierer diese Funktionen implementieren werden.
Datenzugriff Welche Codebereiche werden auf Daten zugreifen? Wie werden sie das tun? Dieser Abschnitt gibt genau an, wie die Anwendung sich mit den Daten verbindet (z.B. OLEDBProvider, der SQL Server-Sicherheit verwendet). Komplexere Punkte, wie das Design und die Organisation der tatsächlichen Datenbank, werden am besten in der detaillierten Entwurfsphase oder als Teil der tatsächlichen Implementierung behandelt.
Skalierbarkeit und Verfügbarkeit Dies ist ein wichtiges und komplexes Themenpaar und einer der Hauptgründe, warum der Anwendungsarchitektur so viel Aufmerksamkeit gewidmet wird. Diese beiden Themen hängen in vielerlei Hinsicht zusammen. Die verwendeten Techniken und die Architektur für einen Dienst werden häufig auch für einen anderen verwendet. Skalierbarkeit beschreibt die Fähigkeit eines Systems, mit größeren Lasten umzugehen und den gleichen Dienstlevel (Reaktionszeit, Anzahl der Anfragen pro Sekunde usw.) über eine Erhöhung der Hardware-Leistung zu bieten. Denken Sie daran, dass es sich hierbei nicht um ein Maß für die Leistung handelt. Ein System kann skalierbar sein und dennoch extrem langsam. Wie sichergestellt wird, dass eine Anwendung gut skalierbar ist, ist nicht Thema dieser Lektion und dieses Buches, aber am Ende der heutigen Lektion werden Sie einen Hinweis auf andere Ressourcen finden, falls Sie an ausführlicheren Informationen zu diesem Thema interessiert sind.
171
Anwendungsarchitektur in .NET
Die Verfügbarkeit ist ein Maß dafür, wie oft das System läuft und in der Lage ist, Anfragen zu verarbeiten. Nun, da Webanwendungen mit Blick auf die Öffentlichkeit erstellt werden, ist der Bedarf für Systeme groß, die jederzeit verfügbar sind. Häufig wird dieses Konzept als »Betriebszeit« (Uptime) und in Bezug auf die Anzahl der Verfügbarkeit von »Neunen« beschrieben, die ein System bietet. Der Begriff »Neunen« bezieht sich darauf, ob ein System eine Betriebszeit von 99 % (zwei Neunen), 99,9 % (drei Neunen) oder noch höher hat. Als dieses Buch geschrieben wurde, wurden einige Systeme für kommerzielle Verwendung mit fünf Neunen Betriebszeit (99,999 %) dokumentiert. Wenn man das in Zahlen ausdrückt, die eine praktischere Bedeutung haben, würde das heißen, dass eine Site mit fünf Neunen Betriebszeit in einem ganzen Jahr (24 Stunden pro Tag, sieben Tage die Woche) nur fünf Minuten heruntergefahren war. Das erscheint fast lächerlich wenig und es übersteigt meine persönliche Betriebszeit auf der Arbeit um ein Vielfaches, aber wenn Sie an eine Site wie Amazon.com denken, dann ist es vollkommen inakzeptabel, wenn sie nicht verfügbar ist, egal wie kurz es auch sei. Das Erstellen eines Systems mit einer solchen Verfügbarkeit ist eine Kombination aus Entwurf und Prozess. Vom Entwurfsstandpunkt aus muss das System vollständig frei von Speicherlecks oder anderen Mängeln sein, die einen Dauerbetrieb verhindern würden. Es muss außerdem über mehrere Rechner verteilt laufen können, da mehrere Gruppen von Hardware verwendet werden müssen, um eine Redundanz zu erzielen. Vom Prozessstandpunkt aus ist aber alles noch komplexer. Software-Upgrades müssen z.B. nacheinander vorgenommen werden, damit niemals alle Server gleichzeitig heruntergefahren werden. Diskussionen bezüglich der Betriebszeit konzentrieren sich häufig auf das Betriebssystem oder die entsprechende verwendete Technologie von Datenbankserver, Webserver und Komponentenserver, wobei z.B. gesagt wird, dass Windows 2000 eine bestimmte Anzahl von Neunen Betriebszeit bietet. Das ist keine realistische Beschreibung. Das Betriebssystem oder der Datenbankserver sind nur ein Teil des Systems und alles hat Auswirkungen auf die Fähigkeit des Systems, eine hohe Verfügbarkeit zu bieten.
5.2
Mögliche Architekturen in .NET
Es gibt keine feste Anzahl von Architekturen, die erstellt werden können – alle unterschiedlichen Möglichkeiten können zu einer Vielzahl von Varianten kombiniert werden –, aber es gibt bestimmte Architekturen, die die Mehrzahl der .NET-Systeme beschreiben.
Die drei Elemente einer jeden Anwendung Die Unterschiede zwischen allen möglichen Architekturen liegen darin, wie die drei verschiedenen Schichten eines Computersystems verteilt sind. Die drei Schichten werden hier aufgelistet und kurz beschrieben:
172
Mögliche Architekturen in .NET
쐽
Präsentation: Diese Schicht stellt die Schnittstelle zum Anwender und zu anderen Systemen dar; sie ist der auswärts gerichtete Aspekt des Systems.
쐽
Geschäftslogik: Dies ist der gesamte Code, der nicht speziell in die Erstellung der Benutzeroberfläche oder in andere Aspekte der Präsentationsschicht einfließt. Diese Schicht stellt den Kern der Anwendung dar, also den Code, der die eigentliche Arbeit des Systems übernimmt.
쐽
Daten: Die Datenbank (oder andere Datenquellen wie XML) und der Code, der auf sie zugreift, wird als Datenschicht verstanden.
Diese drei Schichten sind die logische Darstellung eines kompletten Systems, aber das System kann viele Formen annehmen: eine einzelne ausführbare Datei, Komponenten, die über viele Server verteilt sind, oder eine einfache Website. Unabhängig von der jeweiligen Anwendung ist es nützlich, alle Systeme in Bezug auf diese drei Schichten (Layer) zu beschreiben.
Wie viele Schichten? Anwendungsarchitekturen wurden meist über die Anzahl der verschiedenen Rechner beschrieben, auf denen Teile des Systems laufen. Die einfachste Architektur ist das einschichtige System, wobei sich das Programm (Präsentation, Geschäftslogik und Daten) auf einem einzelnen Rechner befindet. Die meisten Verbraucheranwendungen, wie z.B. Microsoft Office, und viele Geschäftsanwendungen, die nur von ein paar Anwendern gleichzeitig benutzt werden sollen, verwenden diese Architektur. Nur die Anzahl der Rechner, die gleichzeitig irgendeine Form von Verarbeitung durchführen, ist wichtig. Ein Programm, bei dem sich die Datendateien auf einem Dateiserver befinden, wird immer noch als einschichtige Anwendung angesehen, da die eigentliche Arbeit auf dem einen Rechner vorgenommen wird und der Dateiserver nur einen Ort zum Speichern der Daten im Netzwerk bietet. Andererseits wird es als zweite Schicht angesehen, wenn ein Datenbankserver (wie SQL Server oder Oracle) verwendet wird, da der Server tatsächlich eine Verarbeitung durchführt. Systeme, die über Client-Software verfügen (normalerweise auf mehr als einem Rechner), die sich direkt mit einer Back-End-Datenbank verbindet, werden zweischichtige oder Client/Server-Anwendungen genannt. Client/Server ist eine häufig verwendete Architektur für Geschäftsanwendungen. Sie ermöglicht es vielen Anwendern, mit den gleichen Daten zu arbeiten, und bietet gleichzeitig eine viel bessere Leistung als ein dateibasiertes System wie z.B. Microsoft Access. Die aktuellste Form der Anwendungsarchitektur wird dreischichtig oder n-schichtig genannt und beschreibt Systeme, auf denen Code in drei oder mehr unterschiedlichen Bereichen läuft. Die logische Zerlegung eines solchen Systems führt immer noch zu drei Schichten, aber das tatsächliche physische Layout kann mehr als drei getrennte Gruppen
173
Anwendungsarchitektur in .NET
haben. Im Allgemeinen bedeutet das, dass eine Form von Client-Code und eine BackEnd-Datenbank verwendet werden. Dabei ist der Client-Code z.B. eine interaktive Website oder eine Windows-Anwendung, die Code aufruft, der auf einem oder mehreren anderen Servern läuft und die Geschäftslogik ausführt. Diese Architektur wird immer beliebter, da sie sehr flexibel und daher gut für internetbasierte Anwendungen geeignet ist. Es gibt verschiedene technische Verfahren, um dreischichtige Anwendungen zu erstellen, aber vor .NET war Windows DNA die empfohlene Methode von Microsoft. Die Windows Distributed Network Architecture (oder Distributed interNet Architecture, je nachdem, wo Sie Ihre Definition finden) ist die Prä-.NET-Menge von Technologien und Richtlinien von Microsoft zur Erstellung von dreischichtigen Systemen. Basierend auf dem Grundgedanken, dass jede Anwendung in die drei Schichten Präsentation, Geschäftslogik und Daten aufgeteilt werden kann, umreißt die Windows DNA die »beste« Methode, um verteilte Systeme zu erstellen. In einer Windows DNA-Anwendung wurde Client-Code entweder als Standard-Windows-Anwendung oder als Web-Interface ausgeführt, das mit Active Server Pages erstellt wurde. Der Client-Code enthielt nur Interface-bezogene Logik und griff auf die gesamte Geschäftslogik über den Aufruf von COM-Komponenten zu, die entweder lokal waren oder sich auf einem anderen Server befanden. Diese Komponenten kümmerten sich dann um eine Interaktion mit der Datenbank. Ein Schlüsselsatz des Windows DNA-Modells zum Erstellen von Anwendungen lautet, dass alles durch die drei Schichten fließt, sodass die Präsentationsschicht nur mit den Geschäftsobjekten kommuniziert und die Geschäftsobjekte die gesamte Kommunikation mit der Datenbank übernehmen. Die Präsentationsschicht kommuniziert niemals direkt mit der Datenbank und daher sind die drei Schichten getrennt. Durch diese Trennung kann die Datenbank gewechselt werden, ohne die gesamte Benutzeroberfläche neu zu schreiben oder ein ganz neues Interface zu erstellen und dadurch eventuell die Geschäftsschicht oder die Datenbank zu verändern. Die Flexibilität, die sich aus diesem Modell ergibt, entschädigt vielfach für die zusätzliche Arbeit, die nötig ist, um sicherzustellen, dass die drei Schichten vollständig unabhängig sind. Das Windows DNA-Modell ist zwar recht detailliert, dennoch kann die tatsächliche Implementierung eine von vielen verschiedenen Konfigurationen sein. In einem Fall könnte die gesamte Anwendung auf einem Server laufen und Active Server Pages (Web-Interfaces) kommunizieren mit Komponenten (die wahrscheinlich in Visual Basic 6.0 geschrieben sind), die dann mit einer lokalen Installation von SQL Server arbeiten. Es wird nur ein Rechner verwendet, aber die drei Schichten unterscheiden sich immer noch und das Windows DNA-Modell wird beibehalten. Alternativ kann das Modell auch auf so viele Rechner wie nötig ausgedehnt werden, um die Systemlast zu verteilen. Ein mögliches System könnte aus einer Gruppe von 20 Webservern bestehen, die alle Active Server Pages liefern und mit einem Cluster von Rechnern mit gleichmäßiger Lastenverteilung verbunden sind. Auf diesen Rechnern wird die gleiche Gruppe von COM-Komponenten ausgeführt, die mit einigen Servern verbunden sind, auf denen SQL Server in einer Cluster-Konfiguration läuft. Trotz der wesentlich größeren Stufen der Implementierung wird die gleiche
174
Mögliche Architekturen in .NET
Methode benutzt, was die gerühmte Flexibilität und Skalierbarkeit des Drei-SchichtenModells von Windows DNA zeigt.
Wo passt da .NET hinein? .NET ist ein radikaler Sprung in der Entwicklung, aber es können die gleichen allgemeinen architektonischen Konzepte angewendet werden. Viele .NET-Systeme folgen den Modellen und Beispielen, die bereits erörtert wurden, und können als einschichtige, Client/Server- oder sogar Windows DNA-Anwendung klassifiziert werden. Die Technologie mag sich verändern (ASP.NET statt ASP, .NET-Klassen statt COM-Objekten usw.), aber die Architektur ist immer noch die gleiche. Dennoch gibt es ein paar zusätzliche Auswahlmöglichkeiten, um die Windows DNA-Architektur bei der Verwendung von .NET zu verbessern. Das erste neue Konzept dreht sich um die Kommunikation zwischen den Schichten. In der traditionellen Windows DNA verständigen sich die Webseiten mit den COMGeschäftsobjekten über DCOM (Distributed COM), einem binären Standard, mit dem COM-fähige Anwendungen über eine Netzwerkverbindung verbunden werden. Diese Kommunikationsmethode schränkt die Abstraktion zwischen den Schichten ein, da nur COM-fähige Technologien in der Geschäfts- und Präsentationsschicht verwendet werden können. In der traditionellen Windows DNA können Sie Ihre Geschäftsobjekte neu schreiben, ohne die Präsentations- oder Datenschicht zu ändern. Dies ist jedoch nur dann möglich, wenn Sie eine COM-fähige Sprache verwenden (im Allgemeinen Visual Basic oder VC++). .NET führt eine neue Kommunikationsmethode ein, die eine wahre Abstraktion zwischen den Schichten zulassen kann: das SOAP (Simple Object Access Protocol). Die SOAP-Kommunikation erfolgt vollständig mit XML statt mit einem binären Format und läuft über das verbreitete Protokoll HTTP. Für die Entwicklung von verteilten Anwendungen bedeutet das, dass jede Sprache und jedes Werkzeug verwendet werden kann, um die Schichten Ihres Systems zu erzeugen. Ihr Unternehmen könnte über bestehende Komponenten in Java verfügen, die die Geschäftsschicht Ihres Systems bilden, während das Front-End immer noch .NET ist und entweder ASP.NET oder eine Windows-Anwendung verwendet. Die .NET-Version der Windows DNA ist sogar noch besser, als sie es mit der früheren Zusammenstellung von Entwicklungstechnologien war.
Eine Client-Technologie wählen Beim Entwurf eines Systems wird wohl am meisten die Auswahlmöglichkeiten des ClientTyps beachtet. Das ist im Allgemeinen eine Frage von Thick- oder Thin-Client-Systemen. Die Hauptoptionen unter .NET sind die gleichen, die schon seit einiger Zeit zur Verfügung stehen: entweder ASP.NET, um eine Webanwendung zu erstellen, oder Windows Forms, um eine Windows-Anwendung zu erstellen. Der Trick ist, festzustellen, welche
175
Anwendungsarchitektur in .NET
Methode am besten für ein bestimmtes System oder Projekt geeignet ist. Die folgenden Abschnitte beschreiben beide Technologien und erörtern ihre Vorteile und Probleme. Ob ich Ihnen eine einfache Regel nennen kann, um herauszufinden, welche Technologie wann zu verwenden ist? Nein, aber ich werde Ihnen in der heutigen Lektion noch eine allgemeine Liste von Fragen liefern, mit denen Sie eine Vielzahl von architektonischen Auswahlmöglichkeiten treffen können, unter anderem, welcher Client-Typ zu verwenden ist.
Thin-Client-Systeme (ASP.NET) Active Server Pages werden mit Standard .NET-Sprachen (wie Visual Basic .NET) entwickelt und sollen eingehende Anfragen von Anwendern verarbeiten und die entsprechende Ausgabe an deren Browser zurückgeben. Der Code, den Sie für eine ASP.NET-Seite schreiben, wird auf dem Webserver ausgeführt und nicht beim Client. Das ist der wichtigste Punkt, wenn Sie diese Arten von Interfaces mit anderen vergleichen. ASP.NET hat über den Netzwerkzugang und irgendeine Form von Browser hinaus keine Client-Anforderungen. Sie können ASP.NET-Seiten erstellen, die tolles HTML ausgeben und Ausgaben liefern, die die aktuellste und beste Version des Internet Explorer erfordern, aber das liegt ganz an Ihnen. Sie können Ihre Seiten auch so schreiben, dass sie das schlichteste HTML zurückgeben, das Sie je gesehen haben, sodass sogar der Original-Mosaic-Browser Ihre Webseiten lesen kann. Das Betriebssystem des Clients ist an dieser Stelle kein Thema. Wie beim Web können Ihre Seiten von jeder Internetplattform aus betrachtet werden. Das Fehlen der Client-Anforderungen bedeutet, dass (außer der elementaren Anforderung, dass irgendein Webbrowser vorhanden sein muss) nichts auf dem Computer des Anwenders installiert sein muss, damit Ihre Anwendung läuft. Wenn Sie Ihre Site aktualisieren und die Seiten ändern, die auf Ihrem Server gespeichert sind, wird der nächste Besucher Ihrer Seite statt Aktualisierungs- oder Entwicklungsausgaben die neue Version zu sehen bekommen. Alle diese Vorteile, die davon herrühren, dass keine Anforderungen an den Client gestellt werden, machen die Wahl eines Web-Interfaces relativ einfach. Zwei Punkte bilden hier eine Ausnahme: 쐽
Die Anwender haben mit einem Webbrowser immer noch nicht so viel Erfahrung wie mit einer Windows-Anwendung. Stellen Sie sich einige der Windows-Anwendungen vor, die Sie benutzen, z.B. Microsoft Office, Visual Studio .NET, Adobe Photoshop usw. Sie werden erkennen, dass fast nichts im Internet dieser Benutzeroberfläche ähnelt. Es ist möglich, ein Web-Interface zu erstellen, das dem nahe kommt, aber die erforderliche Arbeit ist viel umfangreicher als für eine vergleichbare Windows-Anwendung.
쐽
Eine ASP.NET-Anwendung funktioniert größtenteils nur dann, wenn der Anwender mit Ihnen verbunden ist. Ihre Funktionalität wird von der Geschwindigkeit der Verbindung des Anwenders beeinflusst.
176
Mögliche Architekturen in .NET
Einige Dinge funktionieren in einem webbasierten System einfach nicht, z.B. eine gleichermaßen funktionale und befriedigende Offline-Leitstung. Outlook kann z.B. online oder offline arbeiten, aber sein Web-Gegenstück, Outlook Web Access, verfügt über absolut keine Offline-Funktionalität. Ein Programm, das mit Dateien oder anderen »lokalen« Aspekten Ihres Systems arbeitet, wird sich nicht gut auf eine webbasierte Benutzeroberfläche übertragen lassen. Am Ende ist es schwierig, Gründe gegen die Verwendung eines Web-Interfaces zu finden, und es gibt viele Vorteile, ein System auf diese Art zu erstellen.
Windows-Anwendungen (Windows Forms) Häufig fällt es mir schwer, zu erklären, was ich mit »Windows-Anwendung« meine, da dies eigentlich der einzige bekannte Anwendungstyp ist, den die meisten Menschen kennen. Im Grunde ist eine Windows-Anwendung alles, was Sie mit einer Kombination aus Fenstern und Dialogfeldern erstellen können, um es auf der Windows-Plattform auszuführen. Fast jede Anwendung, die Sie verwenden – Microsoft Office, Quicken, sogar Solitaire –, ist ein Beispiel für das, was Sie mit den Windows-Forms-Funktionen von .NET erstellen können. Nachdem Sie den vorherigen Abschnitt über ASP.NET gelesen haben, fragen Sie sich vielleicht, warum Sie nicht einfach immer ein Web-Interface verwenden, und damit sind Sie nicht allein. Eine Windows-Forms-Anwendung stellt ein paar Anforderungen an den Rechner des Clients (daher die Bezeichnung als Thick-Client-System): 쐽
Das .NET Framework muss beim Client installiert sein, während es im Fall von ASP.NET nur beim Webserver erforderlich ist.
쐽
Das Framework läuft nur auf Windows-Plattformen.
쐽
Ihre Anwendung muss installiert sein.
쐽
Eine Aktualisierung Ihrer Anwendung bedeutet, dass jeder einzelne Desktop auf irgendeine Art betroffen ist (automatisierte Werkzeuge oder Patches zum Herunterladen verringern diese Belastung vielleicht etwas).
Die Abhängigkeit vom Rechner des Clients hat eine gemeine Nebenwirkung, wenn Sie mit öffentlichen Anwendungen umgehen: Die Leistung Ihrer Anwendung hängt von einem Rechner ab, über den Sie absolut keine Kontrolle haben. Alle diese Anforderungen lassen es so aussehen, als sei die Wahl zwischen Thin und Thick keine wirkliche Entscheidung. In Wirklichkeit gibt es aber einige große Vorteile von Windows-Forms-Anwendungen, die erwähnenswert sind. Der erste große Vorteil ist, dass ein Entwickler, der Windows Forms verwendet, befriedigende und leicht reagierende Benutzeroberflächen entwickeln kann, und das viel schneller als mit ASP.NET. Trotz all der Verbesserungen in der Webtechnologie ist es immer noch schwieriger, ein komplexes Interface auf dem Web zu erstellen, als auf Windows.
177
Anwendungsarchitektur in .NET
Viele Webentwickler würden meinen Ausführungen widersprechen, dass eine ASP.NET-Anwendung schwieriger zu entwickeln ist als eine Windows FormsAnwendung, aber trotz seiner vielen Vorzüge ist das einer der Punkte, an denen Windows Forms schwierig wird. Ein zweiter Vorteil liegt in der Leistung der Benutzeroberfläche: Ein Windows-Interface reagiert im Allgemeinen schneller auf Klicks und andere Aktionen. Ein weiterer Vorteil der Verwendung von Windows Forms ist schließlich, dass es Funktionen bietet, die mit dem Web einfach nicht möglich sind (Offline-Verwendung, schnellere Verarbeitung von lokalen Objekten wie Dateien). Durch die Verwendung von Dynamic HTML, ActiveX-Steuerelementen und anderer komplexer Webtechnologien ist es möglich, einige der bereits beschriebenen Punkte zu verbessern. Dabei gehen allerdings die Vorteile von ASP.NET bis zu einem bestimmten Grad verloren, da Sie damit eine Anwendung erstellen, die Plattformen nur eingeschränkt unterstützt (ActiveX-Steuerelemente laufen nur auf bestimmten Betriebssystemen und Prozessoren und Dynamic HTML wird nicht von jedem Browser unterstützt) und möglicherweise sogar einige besondere Anforderungen an die Entwicklung stellt.
5.3
Welche Architektur ist die richtige?
Schon allein die tatsächliche Auswahl der Architektur, bevor die anwendungsspezifischen Veränderungen vorgenommen werden, ist das Honorar eines erfahrenen Systemarchitekten wert. Wenn Sie an dieser Stelle die falsche Entscheidung treffen, kann das gesamte Projekt dem Untergang geweiht sein. Natürlich können Fehler immer korrigiert werden, aber wir müssen wahrscheinlich viel Arbeit über Bord werfen, wenn eine neue Architektur ausgewählt werden müsste.
Wichtige Faktoren bei Ihrer Entscheidung Der wichtigste Faktor bei der Entscheidung für die beste Architektur sind die tatsächlichen Systemanforderungen. Diese Einzelheiten, die von den geschäftlichen Leitern eines Projekts und von den zukünftigen Anwendern zusammengetragen werden, sollten genau angeben, was das System tun muss. Wenn dies feststeht, besteht der Trick darin, ein System zu entwickeln, das diesen Bedürfnissen gerecht werden kann. Sie können die kurzen Systemanforderungen im Abschnitt »Beispielszenarien« durchgehen, um ein Gefühl für diese Entscheidungen zu bekommen, aber zuerst werde ich einige Fragen auflisten, die Sie sich
178
Welche Architektur ist die richtige?
stellen sollten, wenn Sie Systemanforderungen zusammentragen oder wenn Sie bereits bestehende Anforderungen durchgehen. Aus meinen eigenen Erfahrungen und den schmerzlichen Erfahrungen anderer würde ich Ihnen empfehlen, niemals ein System zu erstellen (oder die Erstellung eines solchen zu beurteilen), das auf Anforderungen basiert, die jemand anders zusammengetragen hat. Mit jemand anders meine ich eine andere Person als Sie oder ein Mitglied Ihres Teams, dessen Arbeit Sie vertrauen. Falsch verstandene Anforderungen führen im Allgemeinen zu falschen Systemen und ganz bestimmt zu falschen Einschätzungen. Nach begangener Tat wird es nicht helfen, die Schuld auf die erhaltenen Anforderungen zu schieben. Stellen Sie also sicher, dass Sie oder ein Mitglied Ihres Teams in die Dokumentation der Systemanforderungen involviert sind. Wenn das nicht möglich ist, dann planen Sie Zeit ein, um die bestehende Anforderungsanalyse zu überarbeiten und alle unklaren Punkte zu klären, indem Sie direkt zur Quelle gehen (zu den Leuten hinter dem Projekt, den Anwendern oder sogar zu einem früheren Softwaresystem, das die gleiche Aktion vornahm).
Client-Plattform Als Teil Ihrer Planung müssen Sie einige Details über die Plattform wissen, auf der Ihr System laufen wird: 쐽
Welche Betriebssysteme sind auf den Zielsystemen installiert?
쐽
Wie sehen die Hardwarespezifikationen der Zielrechner aus (CPU/RAM/Festplattenspeicher)?
쐽
Wie viel Kontrolle hat das Unternehmen/die Organisation über die Zielrechner? Was ist mit Konfiguration, Installation von Software und Antivirenschutz? Die schlimmste Antwort auf diese Frage wäre ein reiner Terminal-Server mit simplen Terminals auf der einen Seite und öffentlichen Computern, die den Anwendern gehören, auf der anderen.
쐽
Befinden sich unter den Client-Computern Laptops? Ist Offline- oder Remote-Zugriff erforderlich? Werden Einwahldienste benötigt oder sind sie bereits vorhanden?
Netzwerke Viele Auswahlmöglichkeiten bei der Architektur, z.B. die Art des Clients, die Datenbankreplikation und die verwendeten Protokolle, hängen von dem Netzwerk ab, auf dem das System laufen wird:
179
Anwendungsarchitektur in .NET
쐽
Welche Verbindungsgeschwindigkeit steht zwischen dem Anwender und den BackEnd-Servern zur Verfügung (oder was würde zur Verfügung stehen, wenn die BackEnd-Server nicht existieren würden)?
쐽
Werden Remote-Access-Dienste benötigt und/oder angeboten? VPN? Einwahldienste?
쐽
Haben die Client-Computer Zugang zum Internet?
쐽
Gibt es zwischen den Anwendern und den Back-End-Servern Firewall-Hardware/Software?
쐽
Welche Netzwerkart (TCP/IP, IPX, NetBEUI usw.) wird verwendet?
Sicherheit Jedes System muss mindestens ein paar Sicherheitsrichtlinien haben. Sogar eine ungeschützte Anwendung sollte bekannt geben, dass sie ungeschützt sein soll. Eine wichtige Information, die Sie benötigen, ist die Frage nach der Sicherheit der Anwendung und ihrer Daten. Ein Bankcomputer und ein Punktesystem für Golfer kümmern sich beide um die Sicherheit, aber diese ist bei der Bank ein wenig wichtiger als bei den Golfern. Über diese Information hinaus sind die folgenden Fragen nützliche Startpunkte, um die Sicherheitsbedürfnisse für die Anwendung zu bestimmen: 쐽
Wie authentifizieren sich die Anwender im bestehenden Netzwerk?
쐽
Gehören die Client-Rechner zu einer NT/Windows 2000-Domain?
쐽
Wenn das System auf die Öffentlichkeit ausgerichtet ist, ist der Zugang anonym oder müssen sich die Anwender einloggen?
쐽
Wird die Anwendung für Passwortänderungen oder andere Sicherheitsverwaltungsaufgaben verantwortlich sein?
쐽
Werden die Anwender verschiedene Zugriffsebenen haben, möglicherweise mit Beschränkungen für die Anwendungsfunktionen, die sie nutzen können?
Andere Überlegungen Andere wichtige Fragen in der Entwicklung einer Architektur werden anschließend angesprochen. In diesen Bereichen ist die Erfahrung des Architekten von besonderer Bedeutung und es gibt oft keine festen Antworten darauf. Es ist eher eine Sache von ausreichenden Informationen, sie in die Planung der Architektur mit einzubeziehen. 쐽
180
Veränderungsrate der Geschäftslogik – Wenn die Geschäftslogik, die das System steuert, häufig geändert wird, wirkt sich das darauf aus, wie häufig der Client in einem Client/Server-System aktualisiert werden muss. Große Veränderungen an der Geschäftslogik erfordern vermutlich auch ein Client-Update.
Welche Architektur ist die richtige?
쐽
Fachkenntnis des Entwicklungsteams – Einige Formen der Entwicklung sind komplexer und daher schwieriger als andere. In den meisten Fällen ist ein Client/Server-System einfacher zu schreiben als ein vollständiges Drei-Schichten-System. Wenn Sie eine Architektur auswählen, können Sie entweder versuchen, die Architektur an den Kenntnissen des Teams auszurichten, oder Sie können die erforderlichen Kenntnisse an der ausgewählten Architektur ausrichten.
쐽
Zukünftige Anforderungen/Pläne – Wenn Sie die Architektur für ein System entwickeln, müssen Sie über die heutigen Anforderungen hinausblicken und an die Anforderungen von morgen denken. Wenn Sie keine Informationen darüber haben, wie die Pläne für die Zukunft aussehen, entwerfen Sie wenigstens ein System, auf das man aufbauen kann. Wenn Sie genaue Details kennen, dokumentieren Sie diese in Ihren Anforderungen und entwerfen Sie das System entsprechend. Stellen Sie sich als Beispiel für diese Art von Informationen ein System vor, das heute von zwei Personen benutzt werden soll, aber in ein paar Monaten der Öffentlichkeit im Web zugänglich gemacht werden soll.
Beispielszenarien Die bisherigen Erklärungen zu den wichtigen Faktoren haben sich darauf konzentriert, was Sie in der Anforderungsanalyse beachten müssen, aber dazu gehört im Allgemeinen das Durchlesen von vielen Beschreibungen im Erzählstil und das Herausfiltern der darin enthaltenen Informationen. In diesem Abschnitt werde ich Ihnen ein paar kleine Beispiele für Systemanforderungen zeigen und die architektonischen Informationen, die Sie daraus entnehmen können.
Videothekensystem Anforderungen: »Eine Videothekenkette benötigt ein neues System, um die Videokassetten, Kunden und Leihvorgänge zu verfolgen. Alle Filialen sind derzeit mit dem Hauptsitz über Modems verbunden. Jeder Laden hat einen oder mehrere Computer, die als Verkaufsterminals agieren. Die Computer sind neuwertige Pentium II. Die Daten über Verkauf, Kunde und Videos jeder Filiale müssen am Hauptsitz nicht in Echtzeit verfügbar sein. Das derzeitige System führt nächtliche Batch-Verarbeitungen durch, was für diesen Zweck häufig ausreichend ist.« Diskussion: Das ist ein recht gängiges Szenario: Mehrere Filialen oder Zweigstellen führen Dateneinträge durch und ein Hauptsitz soll regelmäßig mit den Filialdaten aktualisiert werden (siehe Abbildung 5.2). Nach der kurzen Beschreibung klingt es so, als wären die Rechner
181
Anwendungsarchitektur in .NET
des Clients ausreichend und neu genug, sodass eine Windows-Forms-Anwendung eine Möglichkeit wäre, und eine ASP.NET-Anwendung ist immer eine weitere Option. In diesem Fall ist der entscheidende Faktor die Aussage, dass nächtliche Datenaktualisierungen in Ordnung sind. Mit diesem Kommentar und einer relativ langsamen Verknüpfung zurück zum Hauptsitz würde ich ein System in Erwägung ziehen, bei dem die Daten im Hauptsitz gespeichert werden und in Leerlaufperioden oder nachts, wenn die Filialen geschlossen sind, zu jeder Filiale repliziert werden. Das System jeder Filiale wäre eine Windows-Forms-Anwendung, was den Webserver spart. Eine lokale Datenbank (MSDE oder Access) würde in jeder Filiale installiert und die Daten würden jede Nacht mit einem geplanten Upload zum Hauptsitz gesendet. Die alternative Verwendung einer zentralen Datenbank, bei der alle Filialen ihre Arbeit über eine Einwahlverbindung machen, hätte den Vorteil, dass alle Daten der verschiedenen Filialen aktuell im Hauptsitz zur Verfügung stünden Die Langsamkeit einer Modemverbindung wäre jedoch vermutlich ein wichtiger Punkt. Ein webbasiertes Front-End, das die Funktionen unterstützt, die eine Videothek vermutlich benötigt (es wären weitere Nachforschungen nötig, um das festzustellen), wie das Ausfüllen von Empfangsbestätigungen und Mitgliedsausweisen, wäre schwierig zu erstellen.
Daten Hauptsitz Zweigstelle
Daten
Zweigstelle
Daten
Zweigstelle
Daten
Abbildung 5.2: Die Videothek hat eine gängige Architektur mit vielen Zweigstellen, die mit einer Zentrale verbunden sind. Die wichtigste Entscheidung ist, ob sich die Clients direkt mit der Zentrale verbinden oder irgendeine Form der lokalen Datenspeicherung nutzen sollen.
Öffentlicher Zugang Stellen Sie sich vor, was anders wäre, wenn die folgende Zeile in den Anforderungen der Videothek enthalten wäre: »Im Lauf der Zeit würden wir gerne eine öffentliche Website
182
Welche Architektur ist die richtige?
für unsere Kunden erstellen, auf der sie nach Filmen suchen und überprüfen können, ob ein bestimmter Film in einer bestimmten Filiale vorrätig ist.« Diese Aussage verändert mehr, als Sie vielleicht denken. Als »künftige Anforderung« könnten Sie sie einfach ignorieren und das System basierend auf den restlichen Informationen entwerfen. Das würde zu einem System führen, das neu erstellt werden müsste, sobald das Unternehmen den nächsten Schritt in seinen Plänen macht. Statt dessen sollten Sie versuchen, eine Architektur zu entwickeln, die sowohl die derzeitigen Pläne abdeckt, als auch die zukünftigen Anforderungen erfüllen kann. Ohne genauer ins Detail zu gehen – denn für eine vernünftige Architektur der öffentlichen Website würden Sie mehr Einzelheiten zu den zukünftigen Plänen benötigen – stellen Sie sich die erforderlichen grundlegenden Veränderung vor (siehe Abbildung 5.3). Der Kommentar zu »nächtlichen Batches« ist jetzt vermutlich nicht mehr richtig. Wenn die Website Informationen über die Verfügbarkeit von Filmen bietet, aber einen Tag hinterherhinkt, wäre das nicht sehr nützlich. Echtzeit ist nicht unbedingt erforderlich, aber es müsste eine akzeptable Verzögerung festgelegt werden (15 Minuten vielleicht). Wenn die erwartete Verzögerung nicht zu kurz ist, kann das System vielleicht immer noch mit einer lokalen Datenspeicherung und mit häufigeren Batch-Aktualisierungen entworfen werden. Für ein Computersystem sind 15 Minuten eine lange Zeit und es würde weniger Bandbreite benötigen als eine ständige Verbindung. Mit dieser neuen Anforderung erscheint eine Webanwendung lohnender, da alle Daten in der Hauptstelle gespeichert würden, und ein Teil des Systemcodes könnte von den beiden Systemen gemeinsam genutzt werden. Aber eine Website wäre immer noch nicht so reaktiv wie eine WindowsForms-Anwendung mit einer Datenspeicherung vor Ort.
Angebotsdienst für Immobilien Anforderungen: »Ein großes nationales Immobilienunternehmen möchte seine Immobilienangebote an ein Computersystem anbinden. Die Makler sollen Angebote direkt in das System eingeben und eine öffentliche Website würde es den Kunden ermöglichen, die verfügbaren Angebote durchzusehen und dem entsprechenden Makler eine Nachricht zu senden. Die Makler benötigen diesen Dienst ebenso wie die Möglichkeit, neue Angebote einzugeben, und würden auf beides über ihre Laptops zugreifen. Da die Makler normalerweise den ganzen Tag unterwegs sind, werden sie meistens ihre Laptops benutzen.« Diskussion: Dieser Beschreibung können Sie direkt ein paar Schlüsselelemente entnehmen: Es wird zweifellos eine Website geben, zumindest für das Element, das sich an die Öffentlichkeit wendet. Die Frage lautet: Wie ist die Interaktion mit den Maklern zu handhaben?
183
Anwendungsarchitektur in .NET
Daten Hauptsitz
Zweigstelle
Zweigstelle
Zweigstelle Das Internet
Branch Office Zweigstelle Zweigstelle
Zweigstelle
Abbildung 5.3: Durch die Entscheidung, eine öffentliche Website zu nutzen, wurde die Architektur der Videothek modifiziert, um sicherzustellen, dass die Zentrale über aktuelle Daten verfügt.
Sie müssen auf die Angebote zugreifen und Angebote hinzufügen, aktualisieren und entfernen können, und das alles, während sie den ganzen Tag unterwegs sind. Es wäre möglich, dass sie ihre Systeme synchronisieren, wenn sie im Büro sind, und dann auch die Angebote auf ihre Rechner herunterladen und alle neuen Angebote hochladen. Es ist aber wahrscheinlich, dass sie nur gelegentlich ins Büro kommen und sie so für ungefähr einen Tag ein neues Angebot nicht erhalten oder ihre neuen Angebote nicht hochladen würden. Ist das akzeptabel? Vielleicht benötigen Sie mehr Informationen. Alternativ könnten die Makler drahtlose Modems bekommen (mit einer Mobilfunktechnologie wie CDMA), die sie von jedem Ort aus mit der Zentrale oder dem Internet verbinden können. Mit dieser Option haben Sie nun zwei Möglichkeiten: Sie können immer noch die Offline-Anwendung benutzen, aber die Synchronisation über die drahtlose Verbindung vornehmen (was zu häufigeren Aktualisierungen führt) oder Sie bauen die Funktionalitäten zum Hinzufügen, Bearbeiten und Löschen in einen gesicherten Bereich der öffentlichen Immobilien-Website ein, auf die die Makler mit einem normalen Browser über das drahtlose Modem zugreifen und sich dort einloggen. Durch die (derzeit) niedrige Geschwindigkeit dieser drahtlosen Modems würde ich beide Optionen in Erwägung ziehen und einige Tests durchführen, aber an der öffentlichen Website kann ohne Verzögerungen gearbeitet werden. Wie Sie sehen, ist die Anwendungsarchitektur keine exakte Wissenschaft, vor allem wenn Ihnen nur begrenzte Hintergrundinformationen zur Verfügung stehen. Häufig müssen Sie alle Anforderungensammeln, analysieren und dann zurück zu den verschiedenen Beteiligten gehen und zusätzliche Informationen erfragen. Dabei kann es sich einfach um eine
184
Zusammenfassung
Klärung der ursprünglichen Fragen handeln, oder auch um neue Fragen, die sich aus den gegebenen Antworten ergeben haben.
Persönliche CD-Bibliothek Anforderungen: »Als Übungsprojekt erstellen Sie eine persönliche CD-Bibliothek, die die Anwender auf ihren Rechnern installieren können, um ihre CD-Sammlung auf dem aktuellen Stand zu halten. Die Anwendung unterstützt die Suche nach CD-Titeln und das Verfolgen von Titeln aus dem Internet. Alle Einzelheiten der CD-Sammlung des Anwenders werden gespeichert und das System unterstützt das Generieren von einigen einfachen Berichten, die auf diesen Daten basieren.« Diskussion: Dieses System ist recht einfach und die Architektur für seine Erstellung sollte es auch sein. Das einzig wirklich Unbekannte an diesem System ist das Nachschlagen von CD-Details im Internet, aber das ist nichts, was die Architektur betrifft. Eine Windows-Forms-Anwendung wäre am besten geeignet, aber Sie sollten eine andere Datenbank als SQL Server wählen, da Sie das System ohne zusätzlich erforderliche Software auf den Rechnern der Anwender verteilen möchten. Eine Access-Datei (.mdb) oder eine MSCE-Datenbank wäre für dieses Projekt gut geeignet. Alternativ könnten Sie die Informationen einfach als XML speichern, das von einem DataSet weiterverarbeitet wurde.
5.4
Zusammenfassung
Die Welt von .NET ist neu und mächtig, aber alles benötigt ein Fundament. Der Entwurf und die Architektur Ihres Systems sind für den Erfolg von entscheidender Bedeutung. Trotz der neuen Funktionen und radikalen Veränderungen an der zugrunde liegenden Technologie unterscheidet sich die Architektur eines .NET-Systems nur geringfügig von einem Visual-Basic-6.0-System. Viele der Auswahlmöglichkeiten existieren noch immer. Stellen Sie immer sicher, dass Sie Ihre Wahl auf die vorhandenen Anforderungen stützen, und vergessen Sie nicht, für die Zukunft zu planen! Wie bereits erwähnt, reichen viele Aspekte der Systemarchitektur über den Umfang der heutigen Lektion hinaus, aber MSDN hat einen Bereich seiner Website der Architektur von .NET-Awendungen gewidmet. Unter http://msdn.microsoft.com/library/en-us/dnbda/ html/bdadotnetover.asp finden Sie viele nützliche Ressourcen.
185
Anwendungsarchitektur in .NET
5.5 F
Mir wurde gesagt, Windows DNA sei tot und nicht mehr anwendbar, da nun .NET da ist. Sie sagen, dass es immer noch anwendbar ist, aber ist es nicht veraltet? A
F
Fragen und Antworten
Windows DNA repräsentiert Konzepte und Technologien. Einige der Technologien von Windows DNA, wie z.B. Visual Studio 6.0, wurden durch .NET-Entsprechungen ersetzt, aber das Konzept von Windows DNA (verteiltes n-SchichtenComputing mit Trennung der Präsentations-, Geschäfts- und Datenschicht) ist immer noch gültig. Systeme, die bereits auf dem Windows DNA-Modell aufgebaut wurden, lassen sich leicht in die .NET-Welt übertragen.
Werden in der .NET-Welt alle meine Komponenten mit SOAP (XML über HTTP) statt mit dem alten Binärstandard von DCOM kommunizieren? A
5.6
Sie haben Recht, dass DCOM nicht mehr die bevorzugte Methode ist, aber .NET bietet mehr als nur SOAP als Kommunikationsmittel für zwei Anwendungen. SOAP eignet sich hervorragend für die Kommunikation mit .NET- oder anderen Systemen und ist besonders gut, wenn Sie versuchen, zwischen zwei Netzwerken zu arbeiten, da es leicht durch eine Firewall kommt. Innerhalb Ihres eigenen Systems, zwischen zwei .NET-Komponenten, ist SOAP eine Möglichkeit, aber es gibt noch verschiedene andere Kommunikationsformen, darunter auch Binärformate.
Workshop
Quiz 1. Nennen Sie einen der Hauptvorteile der Verwendung einer webbasierten Benutzeroberfläche gegenüber einer Windows-Anwendung. 2. Wenn eine Anwendung eine Access-Datenbankdatei auf einem Server nutzt, ist das ein Client/Server- oder ein Ein-Schicht-System? 3. Was muss installiert sein, damit ein Computer eine mit .NET erstellte Anwendung ausführen kann (nicht nur eine Website durchblättern, die damit erstellt wurde)?
186
Was zu tun ist, wenn ein gutes Programm nicht funktioniert, und wie Sie das verhindern
Was zu tun ist, wenn ein gutes Programm nicht funktioniert, und wie Sie das verhindern
Es gibt eine universelle Wahrheit, die ganz besonders für die Computerprogrammierung gilt und auch als eines von Murphys Gesetzen bekannt ist: Was schief gehen kann, das geht auch schief. Wenn etwas schief geht, während Ihr Programm läuft, wird vielleicht (nur) Ihr Programm abstürzen. Bei intelligenteren Programmen kann Schlimmes – wie Datenverluste größeren Ausmaßes – passieren. Wenn das bei einem Kunden passiert, geschieht vielleicht noch viel Schlimmeres: Eventuell werden Sie nicht bezahlt. Also ist es das Beste, wenn Sie Probleme verhindern können, ehe sie auftreten. Es gibt zwei Haupttechniken, die Ihnen helfen können, Fehler zu vermeiden. Erstens sollten Sie ermitteln, wann Fehler in Ihren Programmen auftreten, und sie selbst behandeln, anstatt dies den Standardfehlermeldungen zu überlassen. Zweitens sollten Sie sicherstellen, dass Ihre Anwendung so wenige Fehler wie möglich hat. Heute werden Sie zwei wichtige Methoden kennen lernen, mit denen Sie Probleme mit Ihrem Code vermeiden können:
Die Themen heute 쐽
Strukturierte Ausnahmebehandlung
쐽
Debugging
6.1
Strukturierte Ausnahmebehandlung
Ältere Versionen von Visual Basic verwendeten die Fehlerbehandlung, um eine Anwendung zu schützen. Visual Basic .NET ist die erste Version, die ein besseres Konzept in Programmiersprachen unterstützt: die strukturierte Ausnahmebehandlung.
Was ist eine strukturierte Ausnahmebehandlung? Bevor Sie sich damit beschäftigen können, was eine strukturierte Ausnahmebehandlung (Structured Exception Handlung, SEH) ist, sollten Sie sich verstehen, was eine strukturierte Ausnahme ist – oder eine nicht-strukturierte Ausnahme. Andernfalls werden Sie nicht wissen, was Sie da behandeln. Eine Ausnahme ist etwas Ungewöhnliches (und meistens Unerwartetes), das in Ihren Anwendungen geschieht. Es gibt zwei Arten von Ausnahmen – Hardwareausnahmen und Softwareausnahmen. Eine Hardwareausnahme ist natürlich eine, die von Hardware verursacht wird, z.B. wenn Ihr Programm versucht, auf Spei-
188
Strukturierte Ausnahmebehandlung
cher zuzugreifen, auf den es nicht zugreifen sollte, oder wenn ein ähnliches Ereignis eintritt. Eine Softwareausnahme tritt ein, wenn Sie versuchen, einer Variablen einen inkompatiblen Wert zuzuweisen, oder wenn ein ähnlicher Fehler auftritt. Die strukturierte Ausnahmebehandlung ist eine Strategie für die Behandlung von Hardware- oder Softwareausnahmen. Da beide Ausnahmearten gleich behandelt werden, ist der Code, der für die Verarbeitung von Ausnahmen erforderlich ist, leichter zu schreiben und konsistenter. Sie führen die strukturierte Ausnahmebehandlung durch, indem Sie die Codeabschnitte »schützen«, in denen es wahrscheinlich zu Ausnahmen kommt. Wenn Sie z.B. eine Division berechnen wollen, können Sie den Code schützen, der diese Division durchführt. Wenn das Programm versucht, durch null zu teilen – entweder wegen eines Fehlers, den Sie im Programm gemacht haben, oder wegen eines falschen Wertes, den der Anwender eingegeben hat –, kann der geschützte Code mit der daraus resultierenden Ausnahme umgehen. Sie sollten die SEH z.B. auch dann nutzen, wenn Sie aus einer Datenbank lesen, wenn Sie Dateien öffnen, lesen oder in sie schreiben, oder in jeder anderen Situation, in der Sie mit Fehlern rechnen. Meiner Meinung nach kann man die SEH überhaupt nicht »übernutzen«. Die Vorteile, die Sie haben, wenn Sie sich selbst, Ihre Programme und Ihre Anwender vor Fehlern schützen, gleichen die minimale Verlangsamung durch die Verwendung von SEH mehr als aus. Seien Sie aber andererseits auch vernünftig. Wenn es wirklich keine Möglichkeit gibt (und ich meine wirklich absolut keine Möglichkeit), dass ein Fehler auftreten kann, dann ersparen Sie sich die zusätzliche Tipparbeit.
Fehler und Ausnahmen Wie unterscheidet sich also eine Ausnahme von einem Fehler? Wenn Sie mit anderen Versionen von Visual Basic oder auch mit einer der vielen anderen Programmiersprachen vertraut sind, sind Sie wahrscheinlich der Vorstellung eines Fehlers bereits begegnet, aber nicht der einer Ausnahme. Was ist der Unterschied? Warum benötigen Sie ein neues Wort, das so geschwollen klingt wie »strukturierte Ausnahmebehandlung«? Eine Ausnahme ist eigentlich ein Fehler. Allerdings ist ein Fehler noch viel mehr. Ein Fehler kann darin bestehen, dass die Funktion ein unerwartetes Ergebnis zurückgibt oder dass der Programmierer den falschen Algorithmus verwendet hat. Eine Ausnahme ist formeller. Eine Ausnahme bedeutet, um die Definition noch einmal zu wiederholen, dass etwas in Ihrem Code außerhalb des geplanten Ablaufs geschieht. SEH definiert eine bestimmte Art, mit Ausnahmen umzugehen, die sicherstellt, dass nach vollbrachter Tat alles aufgeräumt wird. Eine weitere nützliche Funktion der Ausnahmen besteht darin, dass
189
Was zu tun ist, wenn ein gutes Programm nicht funktioniert, und wie Sie das verhindern
sie mittlerweile allen Sprachen gemeinsam sind, die von der .NET-Plattform unterstützt werden. Das bedeutet, dass es Code geben kann, der in der einen Sprache eine Ausnahme verursacht, dem Sie die Ausnahmebehandlung aber in einer anderen Sprache hinzufügen. Die meisten Fehler in Visual Basic wurden traditionell mit der On Error-Anweisung behandelt. On Error ist unstrukturiert und kann zu verwirrendem Code führen, der in einer Prozedur umherspringt. Wie Sie noch sehen werden, werden Ausnahmen mit einem strukturierteren oder organisierteren Verfahren behandelt.
Der Try-Block Sie schützen einen Codeabschnitt vor all den Fällen, in denen eine Ausnahme eintritt, mit dem Try…End Try-Block. Beginnen Sie den Code, den Sie schützen möchten, mit Try und beenden Sie den Abschnitt mit End Try.
Die Syntax für Try...End Try Der folgende Code zeigt die Syntax für den Try...End Try-Block: Try . Catch Ex As Exception . End Try Try ist der Beginn des zu schützenden Codeabschnitts und End Try beendet den Codeblock. Die Catch-Zeile kennzeichnet den Abschnitt des Blocks, der wirklich mit eventuell auftretenden Ausnahmen umgeht.
Der Catch-Abschnitt Einen Codeabschnitt einfach mit Try...End Try zu markieren, führt eigentlich zu nichts. Wenn in dem geschützten Code eine Ausnahme auftritt, muss sie »abgefangen« und behandelt werden. Sie fangen die Ausnahme mit dem Schlüsselwort Catch ab. Jeder Try...End Try-Block kann einen oder mehrere Catch-Abschnitte enthalten. Jeder Catch-Abschnitt wird normalerweise ein- oder mehrmals von Ausnahmen getroffen. Wenn Sie sich dazu entschließen, in ihren Try-Blöcken mehrere Catch-Abschnitte zu verwenden, sollte jeder Catch-Abschnitt andere Arten von Ausnahmen abfangen. Dadurch kann jeder Ausnahmentyp unterschiedlich behandelt werden. Wenn Sie sich zu mehreren CatchAbschnitten entschließen, sollten Sie auch einen generischen Catch-Abschnitt haben, der all die Ausnahmen abfängt, die durch die anderen nicht behandelt werden. Diese Form des Try...End Try-Blocks würde ähnlich wie im Listing 6.1 aussehen.
190
Strukturierte Ausnahmebehandlung
Listing 6.1: Mehrere Ausnahmen abfangen 1 2 3 4 5 6 7 8 9
Try ' Catch eNoFile As FileNotFoundException ' Behandle hier die FileNotFound-Ausnahme. Catch eIO As IOException ' Behandle hier die Input/Output-Ausnahme. Catch Ex As Exception ' Behandle hier die generischen Ausnahmen. End Try
Dieser Try-Block soll Fehler für eine dateiverarbeitende Prozedur abfangen. Es gibt separate Abschnitte zur Behandlung von FileNotFoundExceptions, IOExceptions und anderen Ausnahmen. Wenn es nur einen einzelnen Catch-Abschnitt gäbe, würden Sie Code benötigen, der die tatsächliche Ausnahme ermittelt. Auf diese Art ist es eindeutiger, welcher Code für welche Ausnahmenart benutzt wird. Lassen Sie uns ein Programm mit einer geplanten Ausnahme erstellen, damit Sie sehen können, wie das funktioniert. Sie werden die Ergebnisse der Ausnahme sehen und erkennen, was sie Ihrem Programm antut. Dann werden Sie die Ausnahmebehandlung hinzufügen und sehen, wie Sie das Programm schützen können. 1. Erzeugen Sie eine neue Konsolenanwendung. Nennen Sie sie Exceptions. 2. Ändern Sie den Namen des erzeugten Moduls in modExceptional. 3. Rufen Sie das Dialogfeld mit den Projekteigenschaften auf, indem Sie den Projektnamen im Projektmappen-Explorer mit der rechten Maustaste anklicken und EIGENSCHAFTEN auswählen. 4. Setzen Sie das Startobjekt auf modExceptional. Die letzten Einstellungen sollten so sein wie in Abbildung 6.1. Für dieses Beispiel werden Sie eine einfache, aber erstaunlich häufige Ausnahme benutzen, nämlich DivisionByZero-Ausnahme, die auftritt, wenn Sie durch null teilen. Sie können diesen Fehler leicht zeigen, indem Sie eine beliebige Zahl durch eine nicht initialisierte Variable teilen. (Das ist eine der häufigeren Ursachen für diese Ausnahme.) Listing 6.2 zeigt, wie der endgültige Code aussehen sollte.
191
Was zu tun ist, wenn ein gutes Programm nicht funktioniert, und wie Sie das verhindern
Abbildung 6.1: Projekteigenschaften für ein Programm mit einer Ausnahme Listing 6.2: Ein Ausnahmeprogramm 1 Module modExceptional 2 3 Sub Main() 4 '***Dieser Code enthält eine Ausnahme, 5 'und wird daher nicht vollständig ausgeführt. 6 Dim dDividend As Decimal = 5 7 Dim dDivisor As Decimal = 0 8 Dim dResult As Decimal = 0 9 'Diese Zeile ist schuld und wird 10 'zu einer DivisionByZero-Ausnahme führen. 11 dResult = dDividend / dDivisor 12 System.Console.ReadLine() 13 End Sub 14 15 End Module
Die Zeilen 6, 7 und 8 deklarieren Ihre Variablen. Sie verwenden hier DecimalS, Sie könnten aber auch andere numerische Variablentypen verwenden. Die Werte werden ebenfalls initialisiert. Vor der Division in Zeile 11 wurde der Divisor mit dem Wert 0, mit dem er initialisiert wurde, niemals geändert. Wenn der Computer also versucht, die Division durchzuführen, kommt es zu einer Ausnahme. Wenn Sie dieses Programm in der IDE ausführen, sollten Sie eine Fehlermeldung ähnlich der in Abbildung 6.2. sehen.
192
Strukturierte Ausnahmebehandlung
Abbildung 6.2: Standard-Ausnahmebenachrichtigung
Wenn Sie UNTERBRECHEN auswählen, wird das Programm immer noch laufen, aber nun im Debug-Modus. Sie werden sich den Debug-Modus später genauer ansehen, also wählen Sie vorerst WEITER aus. Das führt dazu, dass das Programm die Ausführung fortsetzt. In diesem Fall endet das Programm. Wenn Sie das Beispiel erstellen und versuchen, es in einem Fenster mit einer Befehlseingabeaufforderung auszuführen, ist das Endergebnis ähnlich, das Problem aber weniger einfach zu beheben. Sie sollten erstens bei einem Entwicklungscomputer (auf dem Visual Basic .NET installiert ist) das JUST-IN-TIME-DEBUGGING-Dialogfenster sehen. Wählen Sie hier NO aus. Ihr Programm wird die Ausführung fortsetzen und Sie sollten folgendes Ergebnis sehen: 1 2 3 4 5
Unhandled Exception: System.DivideByZeroException: An exception of type System.DivideByZeroException was thrown. at System.Decimal.Divide(Decimal d1, Decimal d2) at Exceptions.modExceptional.Main() in C:\ Work\ Day06\ Exceptions\ modExceptional.vb:line 11
Das ist die Standardnachricht, die Sie erhalten werden, wenn eine Ausnahme auftritt. Viele Ausnahmenachrichten werden viel länger sein, aber sie enthalten alle ähnliche Informationen. Die erste Zeile identifiziert immer den Typ der gerade aufgetretenen Ausnahme. In diesem Fall sehen Sie, dass es sich bei der Ausnahme um eine System.DivideByZeroException handelt. Der Ausnahmetyp ist normalerweise ein guter Hinweis auf die Natur des Fehlers und vermittelt Ihnen vielleicht sogar schon eine Idee, wie die Ausnahme zu beheben ist. Hier teilt Ihnen der Ausnahmentyp System.DivideByZeroException zwei Dinge mit: 왘
Die Ausnahme, die gerade aufgetreten ist, gehört zu den »normalen« Ausnahmen. Das bedeutet, dass sie Teil des System-Namensraums ist (und nicht eines anderen Namensraums wie System.IO.FileNotFoundException).
왘
Die Ausnahme, die gerade aufgetreten ist, betrifft eine Division. Sie sollten in Ihrem Code also dort nach einer möglichen Ursache suchen, wo Sie eine Division durchführen.
193
Was zu tun ist, wenn ein gutes Programm nicht funktioniert, und wie Sie das verhindern
Auch der Rest der Ausnahme liefert Ihnen einige Informationen. Die verbleibenden Zeilen sind eine umgekehrte Liste der tatsächlichen Prozeduren, die ausgeführt wurden, als die Ausnahme eintrat. In der Beispielausgabe sehen Sie, dass die tatsächliche Ausnahme in der Prozedur System.Decimal.Divide auftrat. Diese Prozedur wurde von Exceptions.modExceptional.Main aufgerufen. Der Hauptverwendungszweck für alle diese Informationen ist es, wahrscheinliche Fehlerursachen zu lokalisieren. Wenn Sie eine Ausnahme erhalten, sollten Sie sich zuerst den Code in jeder dieser Prozeduren ansehen. Normalerweise (aber nicht immer) sollte sich der Fehler in einer dieser Prozeduren befinden. Beginnen Sie mit dem ersten Element auf der Liste, das Sie geschrieben haben (in diesem Fall modExceptional.Main), und arbeiten Sie sich nach unten vor. Später in der heutigen Lektion werden Sie einige Werkzeuge kennen lernen, die das einfacher machen. Einige weitere häufig auftretende Ausnahmen sind in der Tabelle 6.1 beschrieben. Dies ist keine vollständige Liste aller möglichen Ausnahmen. Es gibt noch viele andere. Mehr Einzelheiten finden Sie in der Visual Basic .NET-Hilfe. Außerdem können Sie, wie Sie später in der heutigen Lektion sehen werden, Ihre eigenen Ausnahmen erzeugen, um sie – falls nötig – zu dieser Liste hinzuzufügen.
Ausnahmentyp
Wann tritt sie auf?
ArgumentException
Allgemeine Kategorie für Fehler, die auftreten, wenn einer Methode der falsche Typ (oder Wert) übergeben wird.
ArgumentNullException ArgumentOutOfRangeException
ArgumentNullException
Tritt ein, wenn Sie einer Methode eine Null übergeben, obwohl diese sie nicht akzeptiert.
ArgumentOutOfRangeException
Tritt ein, wenn Sie eine Variable übergeben, die entweder zu groß oder zu klein für die Methode ist. Wenn Sie z.B. einer Methode die Zahl -1 übergeben, die eine Zahl für einen Monat erwartet (d.h. zwischen 1 und 12).
DivideByZeroException
Tritt ein, wenn Sie versuchen, durch eine nicht initialisierte Variable oder durch eine Variable zu teilen, die Null enthält. Tritt normalerweise nur durch Programmierfehler auf.
IndexOutOfRangeException
Tritt ein, wenn Sie versuchen, auf ein Array-Element zuzugreifen, das nicht existiert. Tritt normalerweise durch Programmierfehler auf, kann aber auch durch eine ungültige Anwendereingabe verursacht werden.
Tabelle 6.1: Gängige Ausnahmen
194
Strukturierte Ausnahmebehandlung
Ausnahmentyp
Wann tritt sie auf?
NotImplementedException
Wird normalerweise als Platzhalter verwendet, wenn ein Entwickler beginnt, an einem Programm zu arbeiten. Sie können die Shell für Ihre Anwendung erstellen und dann diese Ausnahme von allen Methoden ausgeben. Im Verlauf der Entwicklung ersetzen Sie die Ausnahme durch den tatsächlichen Code. Das stellt sicher, dass Sie jede Ihrer Prozeduren vervollständigen.
OutOfMemoryException
Tritt ein, wenn Ihrem Programm der Speicher ausgeht. Das kann passieren, wenn Sie große Arrays füllen oder eine Schleife ausführen.
OverflowException
Eine recht häufige Ausnahme, die eintritt, wenn Sie versuchen, einen zu großen Wert in eine Variable zu setzen, z.B. wenn Sie die folgende Zuweisung versuchen: Dim iSmallishNumber As Short = 50000
FileNotFoundException
Ein Beispiel für einen Fehler, der im Namensraum System nicht definiert ist. In diesem Fall ist die Ausnahme im Namensraum System.IO definiert (siehe Tag 8, Einführung in das .NET-Framework). Diese Ausnahme tritt ein, wenn Sie versuchen, auf eine nicht existierende Datei zuzugreifen. Das könnte daran liegen, dass sie nicht erzeugt wurde oder der Pfad falsch ist.
Tabelle 6.1: Gängige Ausnahmen (Forts.)
Da Sie nun eine Ausnahme gesehen haben und (hoffentlich) eine genauere Vorstellung davon haben, was das ist, sollten Sie die SEH zu Ihrem Programm hinzufügen, um dem Anwender mehr Informationen zu geben, wenn ein Fehler auftritt. Öffnen Sie – wenn notwendig – das Exceptions-Projekt in Visual Basic .NET. Sie werden das frühere Projekt kopieren und Änderungen vornehmen, um die Ausnahme abzufangen. Gehen Sie dabei folgendermaßen vor: 1. Ändern Sie den Namen des Projekts in Exceptions2. Hierfür wählen Sie zuerst das Projekt im Projektmappen-Explorer aus und gehen dann in des Untermenü DATEI, SPEICHERN VON EXCEPTIONS UNTER. 2. Ändern Sie den Namen der Datei, die den Code enthält, in Exceptions2.vb. Hierfür wählen Sie zuerst die Datei im Projektmappen-Explorer aus und gehen dann in das Untermenü DATEI, SPEICHERN VON EXCEPTION.VB UNTER. 3. Fügen Sie einen Try...End Try-Block um den Code herum ein, der die Berechnung durchführt. Verwenden Sie eine generische Catch-Anweisung für Ausnahmen, um die DivisionByZeroException abzufangen. 4. Fügen Sie Code hinzu, um eine hübsche Nachricht anzuzeigen, wenn die Ausnahme eintritt.
195
Was zu tun ist, wenn ein gutes Programm nicht funktioniert, und wie Sie das verhindern
Der neue Code sollte so ähnlich wie in Listing 6.3 aussehen.
Listing 6.3: Den Try…End Try-Block hinzufügen. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
Module modExceptional Sub Main() '***Dieser Code enthält eine Ausnahme 'und wird daher nicht vervollständigt. Dim dDividend As Decimal = 5 Dim dDivisor As Decimal = 0 Dim dResult As Decimal = 0 'Diese Zeile ist schuld und führt zu einer 'DivisionByZero-Ausnahme. Try dResult = dDividend / dDivisor Catch Ex As Exception System.Console.WriteLine("A division by zero error has occurred.") System.Console.WriteLine("Please check the divisor.") End Try System.Console.ReadLine() End Sub End Module
Die Zeile 11 beginnt den Try-Block und daher ist der Code von dort bis zum ersten Catch-Block in der Zeile 13 geschützt. In diesem Fall ist die einzige ausführbare Zeile diejenige, die die »Division durch Null«-Ausnahme in der Zeile 12 verursacht. Wenn diese Ausnahme eintritt, stoppt das Programm und sucht nach etwas, womit es die Ausnahme behandeln kann. Der Catch-Abschnitt in Zeile 13 ist generisch und fängt jede Art von Ausnahme ab. Die Variable Ex wird verwendet, um die erzeugte Ausnahme zu speichern. Sie können mit den Eigenschaften dieser Variablen mehr Informationen über die Ausnahme erhalten. Das Programm zeigt dann eine Fehlermeldung an und der Code setzt die Ausführung beginnend mit der Zeile fort, die auf die End Try-Anweisung folgt.
196
Strukturierte Ausnahmebehandlung
Try...End Try-Blöcke schachteln Angenommen, Sie sind in einer Situation, in der Sie zwei Codeabschnitte mit Try...End Try-Blöcken schützen möchten, aber unterschiedlich mit den Ausnahmen umgehen wollen. Wenn es sich um zwei separate Codeblöcke handelt, ist das kein Problem. Wenn aber einer der Codeblöcke in einem anderen enthalten ist (siehe Listing 6.4), geht das nicht. In diesem Fall müssen Sie die Try...End Try-Blöcke schachteln, wie Sie es im Listing 6.4 sehen.
Listing 6.4: Try…End Try-Blöcke schachteln 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
Sub WriteToFile(ByVal FileName As String) Dim fsOut As System.IO.FileStream Dim strOut As System.IO.StreamWriter Try 'Öffne die Datei. fsOut = _ New System.IO.FileStream(FileName, _ System.IO.FileMode.OpenOrCreate, _ System.IO.FileAccess.Write) Try 'Schreibe in die Datei. strOut = _ New System.IO.StreamWriter(fsOut) strOut.Write(DateTime.Today.ToString()) Catch eIO As Exception Console.WriteLine("Couldn't write to file: { 0} .", FileName) End Try Catch eFile As Exception Console.WriteLine("Couldn't open file: { 0} .", FileName) End Try End Sub
Das Listing 6.4 ist ein Beispiel für das Schreiben in eine Datei. Damit werden Sie sich am Tag 8 näher beschäftigen. Es gibt in diesem Beispiel aber zwei Try...End Try-Blöcke. Einer der Try-Blöcke (derjenige, der in Zeile 10 beginnt) ist vollständig in dem anderen Try-Block (der in Zeile 4 beginnt) enthalten. Der innere Try-Block ist daher in dem äußeren Block geschachtelt. Wenn beim Schreibvorgang in Zeile 14 eine Ausnahme eintritt, fängt die Catch-Anweisung in Zeile 15 die Ausnahme ab und der Anwender erhält die Nachricht Couldn't
197
Was zu tun ist, wenn ein gutes Programm nicht funktioniert, und wie Sie das verhindern
write to file SomeFile.out. Wenn die Datei aber nicht geöffnet werden kann, fängt der Catch-Block in Zeile 18 die Ausnahme ab und gibt Couldn't open file: SomeFile.Out aus.
Ebenso wie bei If...End If und anderen Blöcken gibt es keine Beschränkung dafür, wie tief Sie diese Blöcke verschachteln können. Manchmal können Sie durch das Schachteln von Blöcken Ihren Code besser organisieren als ohne Verschachtelung.
Der Finally-Abschnitt Wenn Sie Try-Blöcke schreiben, kommen Sie manchmal in Situationen, in denen Sie etwas tun müssen, auch wenn eine Ausnahme auftritt. Wenn Sie z.B. Code schreiben, der Informationen in eine Datei schreibt, sollten Sie die Datei schließen, ob nun während des Schreibvorgangs ein Fehler auftritt oder nicht. Sie fügen diese Funktionalität mit dem Finally-Abschnitt hinzu. Dieser Abschnitt erscheint nach allen Catch-Abschnitten und sollte nur Code enthalten, der immer ausgeführt werden muss. Das ist eine gute Stelle, um Dateien zu schließen, Variablen auf Nothing zu setzen und hinter sich aufzuräumen. Listing 6.5 zeigt den Code aus Listing 6.4 mit einem zusätzlichen Finally-Abschnitt.
Listing 6.5: Den Finally-Abschnitt verwenden 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
198
Sub WriteToFile(ByVal FileName As String) Dim fsOut As System.IO.FileStream Dim strOut As System.IO.StreamWriter Try 'Öffne die Datei. fsOut = _ New System.IO.FileStream(FileName, _ System.IO.FileMode.OpenOrCreate, _ System.IO.FileAccess.Write) Try 'Schreibe in die Datei. strOut = _ New System.IO.StreamWriter(fsOut) strOut.Write(DateTime.Today.ToString()) Catch eIO As Exception Console.WriteLine("Couldn't write to file: { 0} .", FileName) Finally strOut.Close()
Strukturierte Ausnahmebehandlung
19 20 21 22 23 24 25 26
End Try Catch eFile As Exception Console.WriteLine("Couldn't open file: { 0} .", FileName) Finally fsOut.Close() End Try End Sub
Hier werden StreamWriter und File geschlossen, auch wenn ein Fehler auftritt. Das ist zwar nicht unbedingt notwendig, aber es ist gut, aufzuräumen, wenn Sie mit einer Variablen fertig sind.
Ausnahmen auslösen Gelegentlich möchten Sie einen Anwender darüber informieren, dass ein Fehler aufgetreten ist. Dabei kann es sich um etwas handeln, das typisch für Ihre Anwendung ist, oder es kann eine »normale« Ausnahme sein. Sie könnten z.B. zwei Objekte mit den Namen Employee und Customer in Ihrer Anwendung erzeugt haben. Sie möchten eine Ausnahme auslösen, wenn das Programm versucht, eine Instanz von Employee einer Variablen zuzuweisen, die Customer-Objekte speichern soll. Statt eine ganz neue Ausnahme zu erzeugen, können Sie die InvalidCastException erneut verwenden. Sie können dann eine neue InvalidCastException erzeugen und mit ihr die Anwendung informieren, indem Sie die Throw-Anweisung verwenden (siehe Listing 6.6).
Listing 6.6: Die Throw-Anweisung 1 2 3 4 5 6 7 8 9
Dim oCust As Customer = New Customer("Bob", "Sjerunkl") Dim oEmp As Employee = New Employee("Phil", "Barr") Dim oSomething As Object oSomething = oEmp If TypeOf oSomething Is Customer Then oCust = oSomething Else Throw New InvalidCastException("Cannot assign an Employee to a Customer.") End If
199
Was zu tun ist, wenn ein gutes Programm nicht funktioniert, und wie Sie das verhindern
Die wichtige Zeile in dem Beispiel ist die Zeile 8. Hier erzeugen Sie eine neue Instanz eines InvalidCastException-Objekts und »werfen« sie. Dadurch wird eine neue Ausnahme erzeugt, die von einer Catch-Anweisung abgefangen werden muss. Oder der Standard-Ausnahmebehandler kümmert sich darum, was dazu führt, dass der Debugger auf den Plan tritt (sofern eine Entwicklungsumgebung installiert ist) oder eine Fehlermeldung erscheint. Die Ausnahmebehandlung bietet einen strukturierten, klaren Weg, um Ihre Programme vor Fehlern durch Hardwareprobleme, ungültige Anwendereingabe oder vor Ihren eigenen Fehlern zu schützen. Jedes Programm ist robuster, wenn Sie die Ausnahmebehandlung um jeglichen Code herum hinzufügen, der eine Ausnahme auslösen könnte.
6.2
Debugging Genauso wichtig wie der korrekte Umgang mit Fehlern und Ausnahmen ist das Debugging – die Aktivität (und Kunst), Fehler im Code zu finden und zu beheben. Natürlich führt das häufig zu der Frage »Warum gibt es überhaupt Fehler im Code? Ich dachte, diese Entwickler sind so schlau?« Obwohl es richtig ist, dass alle Entwickler schlau sind (Sie lesen ja schließlich dieses Buch, oder?), passieren manchmal Fehler. Ehe wir uns damit befassen, wie Sie Fehler aus Ihren Programmen entfernen können, sehen wir uns erst einmal an, wie diese überhaupt in das Programm kommen.
Die Quelle von Bugs Ein Bug (in einem Computerprogramm) kann Vieles sein, z.B. 쐽
ein Fehler in der Strategie zur Durchführung einer Aufgabe. Diese Bugs werden normalerweise logische Fehler genannt und sind häufig am schwierigsten zu beheben. Sie treten auf, wenn Sie Code falsch schreiben. Vielleicht versuchen Sie, eine Liste mit Namen zu sortieren, aber Ihr Verfahren berücksichtigt nicht alle möglichen Namen. Sollte z.B. jemand mit dem Nachnamen St. Jean vor oder nach jemandem mit dem Namen Santana kommen?
쐽
ein Tippfehler bei der Codeeingabe. Diese Fehler können leicht oder schwer zu beheben sein. Solche Fehler sollten fast vollständig verschwinden, da Visual Basic .NET die Schreibweise von Schlüsselwörtern überprüft. Tippfehler können aber zu einem Problem werden, wenn Sie für Ihre Module nicht Option Explicit gesetzt haben. Dann kann es nämlich passieren, dass Sie später einen Variablennamen falsch schreiben, wodurch Visual Basic .NET automatisch eine neue Variable für Sie erzeugt.
200
Debugging
Diese neue Variable wird (bei numerischen Variablen) mit 0 initialisiert, was wahrscheinlich nicht Ihr Ziel ist. Ohne Option Explicit würde der folgende Code zwar kompiliert, aber er würde nie etwas zurückgeben. 1 Imports System 2 Module Typo 3 4 Sub Main 5 Dim sName As String 6 7 Console.WriteLine("Enter name: ") 8 Name = Console.ReadLine() 9 Console.WriteLine("Entered name was { 0} ", sNmae) 10 End Sub 11 End Module
Wie bereits erwähnt, führt Option Explicit dazu, dass Visual Basic .NET diesen Fehler abfängt. Auch ohne diese Option könnten Sie ihn relativ leicht abfangen. Es gibt aber eine Teilmenge dieser Fehlerart, die Ihr Gehirn aus irgendeinem mir nicht bekannten Grund immer automatisch korrigiert und richtig liest. Ich habe schon minutenlang auf einen Codeabschnitt gestarrt, von dem ich wusste, dass es dort einen Tippfehler gab, aber ich habe ihn nicht gesehen. Immer, wenn ich das falsch geschriebene Wort las, las ich es »richtig«. In solchen Situationen rufen Sie am besten eine andere Person, die Ihnen über die Schulter blickt und den Tippfehler sofort findet, was zu einiger Verlegenheit führt. Nach einigen Frotzeleien gehen beide wieder an die Arbeit zurück. 쐽
ein Fehler, der durch eine Anwendereingabe oder durch die Daten verursacht wurde. Hierbei handelt es sich um Fehler, die im Nachhinein schwer zu beheben sind. Der beste Schutz gegen falsche Eingaben oder Daten ist es, die Wahrscheinlichkeit zu verringern, dass der Anwender fehlerhafte Informationen eingibt. An einer späteren Stelle in diesem Buch werden Sie einige der Steuerelemente kennen lernen, die Sie dafür nutzen können. Außerdem ist es hilfreich, ein »defensiver Programmierer« zu sein. Angenommen, die Daten sind fehlerhaft. Dann sollten Sie die Werte daraufhin prüfen, ob sie wirklich geeignet sind, und Codeabschnitte, die eventuell Ausnahmen auslösen könnten, mit der strukturierten Ausnahmebehandlung schützen. Verwenden Sie Option Explicit für alle Module. Visual Basic .NET kann viele Fehler finden, die Sie aus Versehen in Ihren Code einbauen, indem Sie Variablennamen falsch schreiben.
201
Was zu tun ist, wenn ein gutes Programm nicht funktioniert, und wie Sie das verhindern
Vergessen Sie nicht, außerdem Option Strict einzuschalten. Diese Option stellt sicher, dass Sie immer wissen, wenn ein Wert an eine Variable eines anderen Typs übergeben wird. Automatische Typkonvertierung kann zu lästigen Bugs führen. Wenn Sie sich der Notwendigkeit einer Konvertierung bewusst sind, können Sie die Aufgabe besser ausführen. Es gibt also viele mögliche Quellen für Bugs in Ihren Programmen. Einige davon kontrollieren Sie, andere nicht. So oder so gibt Ihnen Visual Basic .NET eine große Anzahl von Werkzeugen an die Hand, die Ihnen beim Korrigieren oder Debuggen Ihrer Programme helfen sollen.
Learning by Doing Wie bereits erwähnt, ist das Debugging genauso Kunst wie Wissenschaft. Manchmal erfordert es einen gewissen Kniff, um herauszufinden, was einen Fehler verursachen könnte. Daher glaube ich, dass das Debugging am besten zu lernen ist, wenn man nicht nur über die Werkzeuge spricht (die recht abstrakt sein können), sondern ein echtes Programm von Fehlern bereinigt. Dafür werden Sie ein Programm nutzen, das eine Hypothekentabelle berechnet. Es zeigt die verschiedenen monatlichen Zahlungen an, die für Darlehen von verschiedener Dauer und bei verschiedenen Zinssätzen fällig wären. Listing 6.7 zeigt die Ausgabe für eine Hypothek von 100.000 Dollar an.
Listing 6.7: Die gewünschte Ausgabe eines Hypothekentabellenrechners Enter loan amount: 100000 Years Interest 5.00 5.25 5.50 5.75 6.00 6.25 6.50 6.75 7.00 7.25
202
10
15
20
25
30
1060.66 1072.92 1085.26 1097.69 1110.21 1122.80 1135.48 1148.24 1161.08 1174.01
790.79 803.88 817.08 830.41 843.86 857.42 871.11 884.91 898.83 912.86
659.96 673.84 687.89 702.08 716.43 730.93 745.57 760.36 775.30 790.38
584.59 599.25 614.09 629.11 644.30 659.67 675.21 690.91 706.78 722.81
536.82 552.20 567.79 583.57 599.55 615.72 632.07 648.60 665.30 682.18
Debugging
7.50 7.75 8.00 8.25 8.50 8.75 9.00 9.25 9.50 9.75 10.00
1187.02 1200.11 1213.28 1226.53 1239.86 1253.27 1266.76 1280.33 1293.98 1307.70 1321.51
927.01 941.28 955.65 970.14 984.74 999.45 1014.27 1029.19 1044.22 1059.36 1074.61
805.59 820.95 836.44 852.07 867.82 883.71 899.73 915.87 932.13 948.52 965.02
738.99 755.33 771.82 788.45 805.23 822.14 839.20 856.38 873.70 891.14 908.70
699.21 716.41 733.76 751.27 768.91 786.70 804.62 822.68 840.85 859.15 877.57
Listing 6.8 zeigt den ersten Versuch, dieses Programm zu erstellen, und die Ausgabe des Programms.
Listing 6.8: Das Programm benötigt ein wenig Debugging. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
Option Explicit On Imports System Imports Microsoft.VisualBasic.ControlChars Module modMain Sub Main() Dim sInput As String Dim dblReturn As Double 'Hole die Anwendereingabe. Dim dblPayment As Double Console.Write("Enter loan amount:") sInput = Console.ReadLine() dblReturn = CDbl(sInput) 'Erzeuge die Tabelle. OutputMortgageTable(dblReturn) Console.Write("Press ENTER to continue") Console.ReadLine() End Sub Private Sub OutputMortgageTable(ByVal Principal As Double)
203
Was zu tun ist, wenn ein gutes Programm nicht funktioniert, und wie Sie das verhindern
26 Dim iYears As Integer 27 Dim iRate As Integer 28 Dim dblAnnualInterest As Double 29 30 Console.WriteLine(Tab & "Years" & Tab & "10" & Tab & "15" & _ 31 Tab & "20" & Tab & "25" & Tab & "30") 32 Console.WriteLine("Interest") 33 34 For iRate = 500 To 1000 Step 25 35 dblAnnualInterest = iRate / 100 36 37 For iYears = 10 To 30 Step 5 38 Console.Write(Format(dblAnnualInterest, "0.#0") & Tab & Tab) 39 'iYears * 12 für die Monate (monatliche Zahlung) 40 Console.Write(Format(Payment(Principal, _ 41 dblAnnualInterest, iYears * 12), "0.00") & Tab) 42 Next 43 Console.WriteLine() 44 Next 45 46 End Sub 47 48 Public Function Payment(ByVal Principal As Double, _ 49 ByVal AnnualInterest As Double, _ 50 ByVal Periods As Integer) As Double 51 Dim dblMonthlyInterest As Double = AnnualInterest / 1200 52 Return Principal * dblMonthlyInterest * 1 + _ 53 dblMonthlyInterest ^ Periods / 1 + _ 54 dblMonthlyInterest ^ Periods – 1 55 End Function 56 57 End Module Enter loan amount: 100000 Years 10 15 20 25 30 Interest 5.00 415.67 5.00 415.67 5.00 415.67 5.00 415.67 5.00 415.67 5.25 436.50 5.25 436.50 5.25 436.50 5.25 436.50 5.25 436.50 5.50 457.33 5.50 457.33 5.50 457.33 5.50 457.33 5.50 457.33 5.75 478.17 5.75 478.17 5.75 478.17 5.75 478.17 5.75 478.17 6.00 499.00 6.00 499.00 6.00 499.00 6.00 499.00 6.00 499.00 6.25 519.83 6.25 519.83 6.25 519.83 6.25 519.83 6.25 519.83
204
Debugging
6.50 540.67 6.75 561.50 7.00 582.33 7.25 603.17 7.50 624.00 7.75 644.83 8.00 665.67 8.25 686.50 8.50 707.33 8.75 728.17 9.00 749.00 9.25 769.83 9.50 790.67 9.75 811.50 10.00 832.33
540.67 6.50 561.50 6.75 582.33 7.00 603.17 7.25 624.00 7.50 644.83 7.75 665.67 8.00 686.50 8.25 707.33 8.50 728.17 8.75 749.00 9.00 769.83 9.25 790.67 9.50 811.50 9.75 832.33 10.00
6.50
540.67
6.50
540.67
6.50
561.50
6.75
561.50
6.75
582.33
7.00
582.33
7.00
603.17
7.25
603.17
7.25
624.00
7.50
624.00
7.50
644.83
7.75
644.83
7.75
665.67
8.00
665.67
8.00
686.50
8.25
686.50
8.25
707.33
8.50
707.33
8.50
728.17
8.75
728.17
8.75
749.00
9.00
749.00
9.00
769.83
9.25
769.83
9.25
790.67
9.50
790.67
9.50
811.50
9.75
811.50
9.75
832.33
10.00
832.33
10.00
540.67 6.75 561.50 7.00 582.33 7.25 603.17 7.50 624.00 7.75 644.83 8.00 665.67 8.25 686.50 8.50 707.33 8.75 728.17 9.00 749.00 9.25 769.83 9.50 790.67 9.75 811.50 10.00 832.33
Die Modi im Leben eines Programms Wenn Sie mit Visual Basic .NET Programme erstellen, wird das Programm drei verschiedene Modi durchlaufen. Wenn Sie Visual Basic .NET zum ersten Mal starten und an einem Programm arbeiten, ist das der Entwurfsmodus. Sie können das überprüfen, indem Sie sich die Titelleiste des Fensters ansehen. Hinter den Worten MICROSOFT VISUAL BASIC lesen Sie [ENTWURF]. Wenn Sie ein Programm in der IDE ausführen, sehen Sie dementsprechend [AUSFÜHRUNG], was kennzeichnet, dass Sie sich im Ausführungsmodus befinden. Der letzte Modus ist der Break- oder Debug-Modus. Dieser Modus tritt ein, wenn das Programm auf irgendeine Art unterbrochen wird. Das kann sowohl durch eine Ausnahme in Ihrem Programm verursacht werden als auch dadurch, dass Sie die IDE absichtlich in den Break-Modus setzen. In der Abbildung 6.3 sehen Sie die IDE im BreakModus. Beachten Sie, dass die Titelleiste das Zeichen [BREAK] enthält.
205
Was zu tun ist, wenn ein gutes Programm nicht funktioniert, und wie Sie das verhindern
Abbildung 6.3: Visual Basic .NET im Break-Modus
Sie verwenden den Break-Modus, da er bei der Fehlerbehebung Ihrer Programme hilft. In diesem Modus werden viele Werkzeuge verfügbar, mit denen Sie sich den Inhalt von Variablen ansehen, den Programmfluss beobachten oder ändern oder aber Codeblöcke testen können. Diese Werkzeuge werden für Sie beim Aufdecken und Beheben von Bugs in Ihren Programmen unersetzlich werden. Wenn Sie sich absichtlich in den Break-Modus begeben möchten, verwenden Sie das Schlüsselwort Stop (s. Abbildung 6.3). Alternativ können Sie in der Zeile, in der das Programm anhalten soll, einen Haltepunkt platzieren. Das Programm läuft ganz normal, bis es diese Zeile erreicht, wechselt dann in den Break-Modus und kehrt wieder zur IDE zurück. Ihr Programm wird im Hintergrund noch immer ausgeführt, aber es ist angehalten. Sie können auf drei verschiedene Arten einen Haltepunkt in Ihrem Code setzen: 쐽
쐽
Klicken Sie im farbigen linken Rand des Codefensters neben die Zeile, die der Haltepunkt sein soll. Es sollte ein roter Kreis im Rahmen erscheinen. Klicken Sie mit rechts auf die Codezeile und wählen Sie den Befehl HALTEPUNKT aus. Es sollte ein roter Kreis im Rahmen erscheinen.
EINFÜGEN 쐽
206
Wählen Sie DEBUGGEN und NEUER HALTEPUNKT aus dem Menü aus. Damit öffnet sich das Dialogfenster NEUER HALTEPUNKT (siehe Abbildung 6.4). Das ist das flexibelste Mittel, einen Haltepunkt zu erzeugen. Dieses Dialogfenster erlaubt es Ihnen nicht nur, einen Haltepunkt zu setzen, der das Programm an dieser Zeile anhält, sondern auch, Haltepunkte zu erzeugen, wenn eine Variable verändert wird, einen bestimmten Wert erreicht oder einfach, wenn eine bestimmte Bedingung True ist.
Debugging
Abbildung 6.4: Das Dialogfenster NEUER HALTEPUNKT
Mit Haltepunkten das Beispielprogramm untersuchen Laden Sie die erste Version des Programms in die IDE (aus dem Verzeichnis \MORTGAGE), sodass Sie mit den Haltepunkten experimentieren können. Mit dem Modul modMain.vb öffnen Sie den Editor und klicken auf den farbigen Rand neben der folgenden Zeile: dblReturn = CDbl(sInput)
Sie sollten einen roten Kreis neben der Zeile sehen. In diesem Fall starten Sie das Programm in der IDE. Das Konsolenfenster sollte erscheinen, in dem Sie einen Darlehensbetrag eingeben können. Geben Sie 100000 ein und drücken Sie auf (¢). Sie sollten wieder zurück in der IDE sein und ein gelber Pfeil sollte zu dem roten Pfeil hinzugefügt worden sein, wie Sie es in der Abbildung 6.5 sehen. Der gelbe Pfeil zeigt immer die nächste auszuführende Zeile an.
Schritt für Schritt durch Ihren Code Eine der nützlichsten Funktionen, die Ihnen im Break-Modus zur Verfügung stehen, ist die Fähigkeit, den Code Schritt für Schritt durchzugehen. Das ermöglicht es einem Anwender, den Logikfluss in einem Programm zu betrachten und so möglicherweise Fehler in Schleifen oder während Prozeduraufrufen zu finden. Wenn Sie im Break-Modus sind, können Sie Ihren Code Schritt für Schritt und Zeile für Zeile durchgehen. So können Sie die Auswirkung jeder einzelnen Zeile auf die Variablen beobachten oder sicherstellen, dass das Programm tatsächlich die Schritte durchführt, die Sie geplant haben.
207
Was zu tun ist, wenn ein gutes Programm nicht funktioniert, und wie Sie das verhindern
Abbildung 6.5: Ein Programm mit einem Haltepunkt ausführen
Es gibt mehrere Hauptfunktionen dafür, die im Menü DEBUGGEN oder in der DEBUGGENSymbolleiste verfügbar sind: 쐽
EINZELSCHRITT: Das ist der am häufigsten verwendete Schritt-Befehl. Wenn Sie ihn auswählen, wird die nächste verfügbare Aktion durchgeführt. Wenn Sie ihn innerhalb der Prozedur OutputMortgageTable ausführen, sollten Sie sehen, dass sich der gelbe Pfeil zur Zeile OutputMortgageTable(dblReturn)
bewegt. Wenn Sie ihn erneut auswählen, sollten Sie zur Prozedur OutputMortgageTable gelangen. Nach einem weiteren Mal befinden Sie sich in der folgenden Zeile: Console.WriteLine(Tab & "Years" & Tab & "10" & Tab & "15" & _ Tab & "20" & Tab & "25" & Tab & "30")
Beachten Sie, dass die gelbe Linie nicht an den Variablendeklarationsanweisungen anhält. Das liegt daran, dass sie nicht als ausführbarer Code angesehen werden. Der gelbe Pfeil wird nur an solchen Zeilen anhalten, die etwas tun, nicht aber in Zeilen, die neue Variablen deklarieren. 쐽
208
PROZEDURSCHRITT: Dieser Befehl ist nützlich, wenn Sie um Prozeduren herum debuggen, von denen Sie wissen, dass sie keine Bugs haben. PROZEDURSCHRITT behandelt eine Prozedur, als ob es sich nur um eine einzelne Codezeile handeln würde. Wenn sich der gelbe Pfeil z.B. in der Zeile OutputMortgageTable in Sub Main
Debugging
befindet und Sie PROZEDURSCHRITT auswählen, wird der gesamte Code in Output MortgageTable ausgeführt und Sie kommen zur nächsten Zeile in Sub Main. 쐽
AUSFÜHREN BIS RÜCKSPRUNG: Ein weiterer nützlicher Befehl, wenn Sie wissen, dass ein Codeabschnitt keine Bugs hat. Mit AUSFÜHREN BIS RÜCKSPRUNG können Sie den gesamten Code bis zur ausgewählten Zeile ausführen. Um diesen Modus zu verwenden, wählen Sie die Zeile aus, bis zu der der Code ausgeführt werden soll, und wählen AUSFÜHREN BIS RÜCKSPRUNG aus dem Menü DEBUGGEN oder dem Kontextmenü aus.
Mit Schritten das Beispielprogramm untersuchen Das Durchschreiten des Programms zeigt Ihnen manchmal Fehler, die vom Entwickler verursacht wurden. Dabei kann es sich um Schleifen handeln, die zu früh enden oder nicht ausreichend durchgeführt werden, oder um logische Tests, die nicht die erwarteten Ergebnisse produzieren. Um das Beispielprogramm zu durchschreiten, machen Sie Folgendes: 1. Halten Sie das Programm an, indem Sie in der Symbolleiste die Schaltfläche DEBUGGEN BEENDEN oder im DEBUGEN-Menü den Befehl DEBUGGEN BEENDEN auswählen. 2. Entfernen Sie den weiter oben erzeugten Haltepunkt, indem Sie den roten Kreis im Rahmen anklicken, und erzeugen Sie zu Beginn des ersten For-Loop innerhalb der OutputMortgageTable einen neuen Haltepunkt. 3. Starten Sie das Programm. Wenn Sie dazu aufgefordert werden, geben Sie 100000 als Darlehensbetrag ein. Das Programm sollte dann in dieser Zeile in den Break-Modus gehen For iRate = 500 To 1000 Step 25
4. Durchschreiten Sie den Code (oder wählen Sie AUSFÜHREN bis Sie diese Zeile erreicht haben:
BIS
RÜCKSPRUNG aus),
Console.WriteLine()
5. Sehen Sie sich wieder das Konsolenfester an (siehe Abbildung 6.6). Beachten Sie, dass es statt der gewünschten fünf tatsächlich zehn Elemente in der Zeile gibt (eine für jede Spalte von 10 bis 30). Der Wert für Interest Rate wird bei jedem Durchlauf der Schleife wiederholt, obwohl er nur einmal vor der Schleife ausgeführt werden soll. 6. Halten Sie das Programm an und ändern Sie die Zeile, die die Zinssätze für jede Zeile ausgibt, sodass sie sie nur noch vor Beginn der zweiten For-Schleife ausgibt. Das Ergebnis sollte so aussehen:
209
Was zu tun ist, wenn ein gutes Programm nicht funktioniert, und wie Sie das verhindern
Abbildung 6.6: Die Kopfzeile anzeigen
Vorher: For iYears = 10 To 30 Step 5 Console.Write(Format(dblAnnualInterest, "0.#0") & Tab & Tab)
Nachher: Console.Write(Format(dblAnnualInterest, "0.#0") & Tab & Tab) For iYears = 10 To 30 Step 5
7. Klicken Sie auf EINZELSCHRITT, um mit dem Programm fortzufahren. Beachten Sie, dass sich der gelbe Pfeil wieder am Anfang der Prozedur OutputMortgageTable befindet. Visual Basic .NET hat die Prozedur mit den Veränderungen neu kompiliert und neu geladen. 8. Wenn Sie nun den Code wie bei Schritt 4 durchlaufen, sollten Sie sehen, dass in der Zeile nur fünf Werte ausgegeben werden (siehe Abbildung 6.7). Sie haben immer noch die falschen Werte, aber immerhin haben Sie einen der Bugs behoben.
Abbildung 6.7: Erste Fehlerkorrektur
210
Debugging
9. Führen Sie das Programm vollständig aus, indem Sie zuerst über PROZEDURSCHRITT zur Sub Main zurückkehren. Sehen Sie sich das Konsolenfenster an, um die falschen Werte zu sehen (sie sind in jeder Zeile gleich). Kehren Sie dann zur IDE zurück und wählen Sie WEITER aus, um das Programm zu beenden.
Variablen beobachten Mit Visual Basic .NET können Sie nicht nur den Programmfluss in Ihrer Anwendung beobachten, sondern es bietet auch noch Werkzeuge, um den Inhalt von Variablen zu betrachten. So sehen Sie, ob die Variablen den erwarteten Inhalt haben und ob die Berechnungen korrekt sind. Wie beim Durchschreiten gibt es auch beim Beobachten von Variablen verschiedene Werkzeuge. Einige der nützlicheren Werkzeuge werden hier beschrieben und sind in Abbildung 6.8 zu sehen: 쐽
QuickInfo: Wenn Sie den Mauszeiger auf eine Variable in der aktuellen Prozedur bewegen, erscheint ein kleines Fähnchen, das Ihnen den aktuellen Wert anzeigt. Sie können das in der Abbildung 6.8 sehen, in der die Variable dblReturn derzeit den Wert 100.000 enthält. Das ist ein nützliches und handliches Werkzeug, wenn Sie gelegentlich schnell einen Wert überprüfen möchten. Ein weiterer Vorteil dieses Werkzeuges ist, dass Sie einen Teil einer Berechnung auswählen können, um ihren Wert zu bestimmen, ohne die gesamte Berechnung durchführen zu müssen.
쐽
LOKAL-Fenster: Dieses Fenster erscheint normalerweise im Break-Modus unten auf dem Bildschirm. Wenn das nicht der Fall ist, wählen Sie es im Untermenü FENSTER des Menüs DEBUGGEN aus. Das Fenster LOKAL zeigt alle Variablen der aktuellen Prozedur sowie ihren Typ und aktuellen Wert. Das ist nützlich, wenn Sie eine Prozedur durchschreiten, da die Werte rot werden, wenn sie sich ändern. Das Fenster LOKAL ist immer dann zu empfehlen, wenn Sie eine Prozedur debuggen, die die Werte von Variablen verändert.
쐽
ÜBERWACHUNG-Fenster: Dieses Fenster erscheint normalerweise unten auf dem Bildschirm, wenn Sie sich im Break-Modus befinden. Wenn das nicht der Fall ist, wählen Sie es im Untermenü FENSTER des Menüs DEBUGGEN aus. Das ÜBERWACHUNGFenster zeigt Variablen, an denen Sie interessiert sind. Klicken Sie mit rechts auf Variablen und wählen Sie ÜBERWACHUNG HINZUFÜGEN aus, um sie zum Fenster hinzuzufügen. Wenn sie sich im Gültigkeitsbereich befinden, wird der aktuelle Wert angezeigt. Andernfalls erhalten Sie die Nachricht Der Name ist nicht deklariert. Dieses Fenster eignet sich gut, um eine Gruppe von Variablen zu beobachten. Statt sie nur dann zu sehen, wenn sie sich im LOKAL-Fenster befinden, können Sie sie durch die ganze Debugging-Sitzung verfolgen.
211
Was zu tun ist, wenn ein gutes Programm nicht funktioniert, und wie Sie das verhindern
Variablen untersuchen, um das Beispielprogramm zu debuggen Das Layout der Hypothekentabelle sieht nun besser aus und der einzige Bug, der wahrscheinlich noch übrig ist, befindet sich in der Berechnung der einzelnen Variablen. Sie sollten die Funktion Payment debuggen, um sicherzustellen, dass die korrekten Werte verwendet werden. Folgen Sie diesen Schritten: 1. Beenden Sie das Programm, wenn es gerade läuft. Entfernen Sie alle bestehenden Haltepunkte und fügen Sie der ersten Zeile der Funktion Payment einen neuen Haltepunkt hinzu. Die entsprechende Zeile sollte folgendermaßen lauten: Dim dblMonthlyInterest As Double = AnnualInterest / 1200
2. Fügen Sie einen zweiten Haltepunkt am Ende der Zeile End Sub von Sub Main hinzu, damit Sie die Tabelle sehen können, bevor das Programm endet.
Abbildung 6.8: Variablenwerte beobachten
Obwohl diese Zeile mit einer Deklaration beginnt, ist sie wegen der Zuweisung eine ausführbare Zeile.
3. Führen Sie das Programm aus, bis es den Haltepunkt erreicht. Geben Sie als Darlehensbetrag 100000 ein. 4. Wenn das Programm in den Break-Modus kommt, sehen Sie sich das Fenster LOKAL an. Beachten Sie, dass es alle Werte für Principal, AnnualInterest und Periods enthält.
212
Debugging
Entsprechend sollten Sie ein Fähnchen mit dem aktuellen Wert sehen, wenn Sie den Mauszeiger in der IDE auf eine Variable halten (z.B. auf AnnualInterest). 5. Wählen Sie einmal EINZELSCHRITT, um die Zeile auszuführen. Beachten Sie, dass sich die Variable dblMonthlyInterest im LOKAL-Fenster verändert und rot wird. 6. Wählen Sie den Code 1 + dblMonthlyInterest ^ Periods
aus. Halten Sie dann den Mauszeiger auf den ausgewählten Text. Es sollte ein QuickInfo-Fähnchen erscheinen, das angibt, dass der Wert 1 ist. Eine schnelle anderweitige Berechnung bestätigt, dass das richtig ist, wenn Sie zuerst die Potenzierung durchführen und dann 1 addieren. Der richtige Wert muss aber ungefähr 1.647 sein, da zuerst die Addition erfolgen sollte und dann das Ergebnis der Addition zur Potenz der Perioden erhoben werden sollte. Dieser Fehler wird in der zweiten Hälfte der Funktion wiederholt. Fügen Sie Klammern zu der Berechnung hinzu, damit zuerst addiert wird. Testen Sie den Wert dann erneut. 7. Der Berechnung fehlen ganz allgemein einige Klammern. Halten Sie das Programm an und ändern Sie die Zeile mit der Berechnung folgendermaßen: Return Principal * (dblMonthlyInterest * _ ((1 + dblMonthlyInterest) ^ Periods) / _ (((1 + dblMonthlyInterest) ^ Periods) – 1))
Klammern kosten nichts. Wenn Sie eine mathematische Funktion schreiben, geizen Sie nicht mit Klammern: Benutzen Sie so viele wie nötig, um sicherzustellen, dass die Operationen in der richtigen Reihenfolge durchgeführt werden. Alternativ können Sie jeden Schritt in eine eigene Zeile setzen, obwohl das mehr temporäre Variablen in der Berechnung erfordern würde. 8. Halten Sie die Anwendung an, entfernen Sie den Haltepunkt von der Zeile mit der Funktion Payment und lassen Sie die Anwendung erneut laufen. Nun sollten Sie die korrekte Hypothekentabelle sehen, wie sie in der Abbildung 6.9 dargestellt ist.
Das Beispielprogramm erhält den letzen Schliff Es gibt noch ein paar kleinere Veränderungen, die Sie vornehmen können, um das Beispielprogramm aufzuräumen. Der Code, der die Frage an den Anwender bewirkt, könnte z.B. in eine Funktion eingehüllt werden. Das würde es Ihnen erlauben, diesen Code später durch Code zu ersetzen, der eine ähnliche Funktion ausführt, aber mit WINDOWS FORMS oder WEB FORMS oder einem anderen Bildschirm erstellt wurde.
213
Was zu tun ist, wenn ein gutes Programm nicht funktioniert, und wie Sie das verhindern
Abbildung 6.9: Die korrekte Hypothekentabelle
Es ist eine gute Idee, Codeabschnitte zu isolieren, die in Prozeduren betriebssystem- oder benutzeroberflächenspezifisch sind. Wenn Sie die Benutzeroberfläche ändern müssen, müssen Sie dann nur die Prozedur ändern, nicht aber den Code, der sie verwendet. Eine ähnliche Veränderung besteht darin, die Berechnung in der Funktion zur Berechnung der Hypothekenzahlung zu vereinfachen. Da der Ausdruck (1 + dblMonthlyInterest) ^ Periods) in der Berechnung zweimal erscheint, können Sie ihn in eine eigene Berechnung verschieben. Deklarieren Sie eine temporäre Double-Variable, um den Inhalt zu speichern, und ersetzen Sie die Berechnung durch die temporäre Variable. Nachdem Sie diese Veränderungen an dem Beispielprogramm vorgenommen haben, sollte es so ähnlich aussehen wie das Beispiel in Listing 6.9.
Listing 6.9: Der Code nach dem Debugging 1 Option Explicit On 2 Imports System 3 Imports Microsoft.VisualBasic.ControlChars 4 5 Module modMain 6 7 Sub Main() 8 9 Dim sInput As String 10 Dim dblReturn As Double 11
214
Debugging
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 End
'Hole Anwendereingabe Dim dblPayment As Double Console.Write("Enter loan amount:") sInput = Console.ReadLine() dblReturn = CDbl(sInput) 'Erzeuge die Tabelle OutputMortgageTable(dblReturn) Console.Write("Press ENTER to continue") Console.ReadLine() End Sub Private Sub OutputMortgageTable(ByVal Principal As Double) Dim iYears As Integer Dim iRate As Integer Dim dblAnnualInterest As Double Console.WriteLine(Tab & "Years" & Tab & "10" & Tab & "15" & _ Tab & "20" & Tab & "25" & Tab & "30") Console.WriteLine("Interest") For iRate = 500 To 1000 Step 25 dblAnnualInterest = iRate / 100 Console.Write(Format(dblAnnualInterest, "0.#0") & Tab & Tab) For iYears = 10 To 30 Step 5 'iYears * 12 für die Monate (monatliche Zahlung) Console.Write(Format(Payment(Principal, _ dblAnnualInterest, iYears * 12), "0.00") & Tab) Next Console.WriteLine() Next End Sub Public Function Payment(ByVal Principal As Double, _ ByVal AnnualInterest As Double, _ ByVal Periods As Integer) As Double Dim dblMonthlyInterest As Double = AnnualInterest / 1200 Return Principal * (dblMonthlyInterest * _ ((1 + dblMonthlyInterest) ^ Periods) / _ (((1 + dblMonthlyInterest) ^ Periods) – 1)) End Function Module
215
Was zu tun ist, wenn ein gutes Programm nicht funktioniert, und wie Sie das verhindern
Andere Debugging-Werkzeuge Es stehen noch viele andere Werkzeuge zur Verfügung, die Ihnen beim Debugging helfen können. Die meisten davon sind fortgeschrittene Werkzeuge, deren Beschreibung über den Umfang dieses Buches hinausgeht, aber Sie sollten wenigstens wissen, dass es sie gibt. Zu diesen Werkzeugen gehören: 쐽
DIREKT-Fenster: Ist jederzeit über den Punkt FENSTER im Menü DEBUGGEN verfügbar. In diesem Fenster können Sie eine Codezeile eingeben, die sofort ausgeführt wird. Damit können Sie kleine Codefragmente testen. Da es unabhängig von einem Debugging verfügbar ist, sollten Sie das DIREKT-Fenster nutzen, um kleine Berechnungen zu testen, während Sie Ihre Programme schreiben. Das DIREKT-Fenster wird gern dafür benutzt, den Inhalt von Variablen anzuzeigen, also im Fenster »auszugeben«, z.B.: ? dblMonthlyInterest 4.16666666666667E-03
쐽
SCHNELLÜBERWACHUNG-Fenster: Steht im Break-Modus über das Menü DEBUGGEN bzw. über das Kontextmenü zu einer Variablen zur Verfügung. Es öffnet ein Fenster, das den Wert von Variablen zeigt. Diese Funktion wurde gewissermaßen von dem Popup-Fähnchen abgelöst, das den Wert von Variablen zeigt. Trotzdem ist das SCHNELLÜBERWACHUNG-Fenster ebenfalls nützlich, um Ausdrücke und Variablen zu testen.
쐽
AUFRUFLISTE: Steht im Break-Modus über das Menü DEBUGGEN, FENSTER zur Verfügung. Öffnet ein Fenster, das die Liste der Prozeduren zeigt, die derzeit aktiv sind. Wenn Ihre Sub Main z.B. OutputMortgageTable aufgerufen hat, zeigt die Aufrufliste die beiden Prozeduren an (in umgekehrter Reihenfolge). Sie können damit zwischen den beiden navigieren. Damit haben Sie die Möglichkeit, die Zeile in Sub Main zu sehen, die die zweite Prozedur aufgerufen hat. Sie können dieses Fenster verwenden, um Punkte zu debuggen, an denen in einer If-Anweisung die falsche Entscheidung gefällt wird.
쐽
DISASSEMBLY: Steht im Break-Modus vom Menü DEBUGGEN, FENSTER zur Verfügung. Hierbei handelt es sich um ein fortgeschrittenes Werkzeug, das die tatsächlich für das Programm erstellte Maschinensprache zeigt. Dies ist nur dann nützlich, wenn Sie Assembler kennen.
쐽
THREADS: Steht im Break-Modus im Menü DEBUGGEN, FENSTER zur Verfügung. Hierbei handelt es sich um ein fortgeschrittenes Werkzeug, das die aktiven Threads in einem Programm zeigt. Es ist hilfreich, wenn Sie in Visual Basic .NET mit mehreren Threads programmieren.
Visual Basic .NET bietet viele Werkzeuge, die Ihnen helfen, fehlerfreie Programme zu veröffentlichen. Am einfachsten lernen Sie sie kennen, wenn Sie etwas mit ihnen experimen-
216
Zusammenfassung
tieren. So finden Sie auch die Werkzeuge, die Ihnen persönlich am besten helfen. Statt Bugs zu suchen, indem Sie einfach den Quellcode anstarren, schreiten Sie durch Ihren Code, sehen sich die Werte der verwendeten Variablen an und versuchen, die Fehler mit diesen Werkzeugen zu isolieren.
6.3
Zusammenfassung
Alle Programme haben Bugs. Manchmal ist die Person, die das Programm ausführt, der Bug. Bei anderen Gelegenheiten gibt es einen tatsächlichen Bug oder Fehler im Code. Das kann daran liegen, dass der Autor des Programms nicht alle Möglichkeiten der Eingabe getestet hat oder dass er das Programm nicht vor fehlenden Dateien oder Datenbanken geschützt hat. In jedem Fall liegt es an Ihnen, dem Entwickler, sicherzustellen, dass keiner dieser Fehler zu einem Informationsverlust führt. Sie sollten sich bemühen, Ihre Programme so Bug-frei wie möglich zu machen. Die Debugging-Werkzeuge von Visual Basic .NET werden Ihnen dabei helfen. Schreiten Sie mit ihnen durch Ihren Code und vergewissern Sie sich, dass der Fluss wie geplant läuft. Stellen Sie sicher, dass Variablen die korrekten Werte enthalten und dass Berechnungen fehlerfrei sind. Entsprechend können Sie Ihr Programm vor Fehlern schützen, indem Sie an kritischen Abschnitten im Code die strukturierte Ausnahmebehandlung hinzufügen. Mögliche Fehlerpunkte – wenn z.B. der Code Dateien öffnet, liest oder Informationen hineinschreibt, oder Berechnungen anstellt – sollten in Try...Catch...End Try-Blöcke gehüllt werden. Diese Blöcke sollten elegant mit den Fehlern umgehen und es dem Anwender idealerweise erlauben, mit dem Programm fortzufahren. Morgen werden Sie erfahren, wie Sie mit Objekten arbeiten können. Wie Sie sehen werden, haben Sie bereits mit Objekten gearbeitet. Sie werden das Thema aber am Tag 7, Mit Objekten arbeiten, genauer untersuchen.
6.4 F
Fragen und Antworten
Was ist, wenn ich alten Code habe, der On Error verwendet? Muss ich ihn neu schreiben, um die strukturierte Ausnahmebehandlung zu verwenden? A
Nein. Wenn Sie die Referenz Microsoft.VisualBasic einfügen, können Sie auch weiterhin On Error Goto und On Error Resume Next verwenden. Außerdem steht Ihnen das Err-Objekt zur Verfügung.
217
Was zu tun ist, wenn ein gutes Programm nicht funktioniert, und wie Sie das verhindern
6.5
Workshop
Der Workshop soll Ihnen dabei helfen, mögliche Fragen vorherzusehen und das Gelernte zu wiederholen. Außerdem soll er Sie dazu anregen, über die praktische Umsetzung Ihres Wissens nachzudenken. Die Antworten und Lösungen finden Sie im Anhang A.
Quiz Nennen Sie drei Werkzeuge, mit denen Sie den Inhalt einer Variablen sehen können, während Sie sich im Break-Modus befinden.
Übungen 1. Sie haben gerade den folgenden Codeblock geerbt. Er fordert den Anwender auf, zwei Werte einzugeben. Dann berechnet er für Werte zwischen diesen beiden eingegebenen Zahlen eine Multiplikationstabelle und zeigt sie an. Wenn der Anwender z.B. 5 und 9 eingibt, sieht die Ausgabe ungefähr so aus: Multiplication Table (5 to 9) 5 6 7 8 9 5 25 30 35 40 45 6 30 36 42 48 54 7 35 42 49 56 63 8 40 48 56 64 72 9 45 56 63 72 81
Fügen Sie die Ausnahmebehandlung hinzu und debuggen Sie den Code in Listing 6.10, um sicherzustellen, dass er die gewünschte Ausgabe erzeugt.
Listing 6.10: Code für die Multiplikationstabelle 1 Imports System 2 Imports Microsoft.VisualBasic.ControlChars 3 4 Module modTable 5
218
Workshop
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 End
Sub Main() Dim iLow, iHigh As Integer Dim sInput As String 'Erlaube Mehrfachdurchläufe der Tabellengenerierung. Do 'Frage Anwender nach Werten. Console.Write("Low value (maximum of 20, 0 to end): ") sInput = Console.ReadLine() iLow = CInt(sInput) If iLow 0 Then Console.Write("High value (maximum of 20): ") sInput = Console.ReadLine() iHigh = CInt(sInput) OutputTable(iLow, iHigh) End If Loop Until iLow = 0 Console.Write("Press ENTER to continue") Console.ReadLine() End Sub Private Sub OutputTable(ByVal MinValue As Integer, _ ByVal MaxValue As Integer) Dim iCount, iCount2 As Integer Console.WriteLine() Console.WriteLine("Multiplication Table ({ 0} MinValue, MaxValue)
to { 1} )", _
'Schreibe Kopfzeile For iCount = MinValue To MaxValue Console.WriteLine(Tab & CStr(iCount)) Next Console.WriteLine() 'Schreibe jede Zeile der Tabelle. For iCount = MinValue To MaxValue For iCount2 = MinValue To MaxValue Console.Write(CStr(iCount) & Tab & CStr(iCount * iCount2)) Next Console.WriteLine() Next End Sub Module
219
Was zu tun ist, wenn ein gutes Programm nicht funktioniert, und wie Sie das verhindern
2. Wenn Sie im weiteren Verlauf des Buches Probleme mit Ihrem Code haben, nutzen Sie die Debugging-Werkzeuge, um das Programm zu korrigieren. Verwenden Sie Haltepunkte, um Abschnitt zu isolieren, von denen Sie annehmen, dass sich Fehler darin befinden könnten. Schreiten Sie durch den Code und verwenden Sie die Fenster LOKAL und ÜBERWACHUNG, um Variablen zu beobachten. 3. Wenn Sie in Ihren Programmen auf Ausnahmen stoßen, fügen Sie die Ausnahmebehandlung hinzu. Suchen Sie in der Onlinehilfe nach dem Schlüsselwort »Exception«, um die verschiedenen Ausnahmetypen zu sehen. Alternativ können Sie die Liste untersuchen, indem Sie AUSNAHMEN aus dem Menü DEBUGGEN auswählen. 4. Nutzen Sie die Beispielanwendung, um die verschiedenen Haltepunktarten zu untersuchen. Versuchen Sie, Haltepunkte zu setzen, wenn die Zahlung unter einen bestimmten Wert fällt.
220
Mit Objekten arbeiten
Mit Objekten arbeiten
Objekte und ihre Verwendung und Erstellung sind für die Entwicklung in Visual Basic .NET sehr wichtig. Sie haben in diesem Buch zwar bereits mit Objekten gearbeitet, aber die heutige Lektion wird sich besonders auf Objekte konzentrieren und die folgenden Themen abdecken:
Die Themen heute 쐽
Was sind Objekte?
쐽
Wie Objekte arbeiten
쐽
An welcher Stelle Objekte in Ihre Programme passen
Wir beginnen mit der Grunddefinition von Objekten und allen Begriffen und Konzepten, die diese mit sich bringen.
7.1
Für den Anfang: Was ist ein Objekt?
Um den Begriff Objekt zu definieren, könnten wir bis zum Latein des Mittelalters zurückgehen. obiectum bedeutet »Ding, das vor den Verstand gesetzt wird«, aber die einfachste Definition beginnt mit dem Wort »Ding«. Ein Objekt ist eine generische Beschreibung von allem, was wir eventuell erörtern oder womit wir arbeiten möchten. In einem normalen Gespräch soll das Objekt normalerweise nur materielle Objekte beschreiben. In der Programmierung, wo es nur wenig greifbares Material gibt, wird diese Definition aber so erweitert, dass jede Entität damit gemeint ist. Wir können auf ein Auto, eine Person oder ein Gebäude als Objekt verweisen. Sie können diesen Begriff auch verwenden, um etwas weniger Greifbares wie einen Zinssatz zu definieren, oder eine Regel, die auf eingehende E-Mails angewendet wird. Die Verwendung von Objekten erlaubt es Ihren Programmen, sich auf die Entitäten zu konzentrieren, mit denen Sie arbeiten. Das Ziel besteht darin, Systeme zu erstellen, die leichter zu verstehen und zu verbessern sind. Statt mit einem Programm zu arbeiten, bei dem alle Informationen und der Code, der mit einer einzelnen Entität zusammenhängt, in der ganzen Anwendung verteilt ist, vereinigt ein objektorientiertes Verfahren diese Informationen, indem es sie in die Objektdefinition einbringt.
Klassen und Instanzen Um Objekte zu verstehen, müssen Sie sich kurz mit dem Konzept einer Klasse beschäftigen. Klassen beschreiben ebenso wie Klassifikationen eine Gruppe oder einen Typ von Entitäten. Jedes Objekt ist ein Element einer bestimmten Klasse. Klassen liefern also die
222
Für den Anfang: Was ist ein Objekt?
Beschreibung eines Objekts sowie Einzelheiten, die sein Verhalten definieren und angeben, welche Informationsarten über ein Objekt zur Verfügung stehen. Die Klasse Auto würde uns beispielsweise sagen, dass die folgenden Informationen über ein Auto zur Verfügung stehen: Farbe, Geschwindigkeit, Gewicht, Bauart, Modell und Baujahr. Alle diese Dinge sind Attribute des Objekts, also beschreibende Informationsstücke, die als Eigenschaften des Objekts bezeichnet werden. Zusätzlich zu diesen Eigenschaften beschreibt die Klasse auch, was das Objekt tun kann und wie es das tut. Diese Verhaltensweisen werden meistens als Methoden des Objekts bezeichnet und ein Auto-Objekt könnte Methoden wie »LinksAbbiegen«, »VorwärtsFahren«, »RückwärtsFahren« usw. haben. Mit einer solchen Klasse, die Informationen zu den Eigenschaften und Methoden eines Objekts sowie einige andere Einzelheiten wie in einer Schablone liefert, werden dann echte Objekte erzeugt. Kehren wir zu dem Autobeispiel zurück. Die Klasse wäre die Spezifikation des Autos, eine Art Bauplan, die beschreibt, wie es funktioniert und aussieht. Mit dieser Klasse werden dann viele Autos erzeugt, von denen jedes allein existiert, die aber alle auf der gleichen Spezifikation basieren. Jedes einzelne Auto hat die gleichen Eigenschaften, wie z.B. die Eigenschaft »Farbe«, da diese alle von der Spezifikation kommen, aber jedes kann einen anderen Wert für diese Eigenschaft haben (blaues Auto, rotes Auto, gelbes Auto usw.). Alle Autos würden sich auch das gleiche Verhalten oder die gleichen Aktionen teilen (die Methoden des Autos), wie z.B. »VorwärtsFahren« und alle Autos, die nach der gleichen Spezifikation erstellt wurden, würden diese Aktion auf die gleiche Weise durchführen. Wenn wir basierend auf der Spezifikation ein Auto bauen, entspricht das dem Erzeugen eines Objekts basierend auf einer Klasse. Während also Ford Fiesta eine Klasse wäre, wäre Stefans blauer Corsa ein Objekt. Jedes dieser einzelnen Objekte ist eine Instanz der Klasse und es gibt keine Beschränkung für die Anzahl der zu erzeugenden Instanzen. Jede Instanz nutzt die Schablone oder die Beschreibung, die von ihrer Klasse geliefert wird. Das bedeutet, dass jede einzelne Instanz der Auto-Klasse die gleichen Eigenschaften und Methoden hat und sich auf die gleiche allgemeine Art verhält. Jede einzelne Instanz hat aber ihre eigenen Werte für ihre Eigenschaften: Alle Autos haben die Eigenschaft Farbe, aber jedes Auto kann eine andere Farbe haben.
Referenzen In der Programmierung wird ein weiteres Konzept eingeführt: die Objektreferenz. Eine Objektvariable – jede Variable, die als Objekt irgendeines Typs deklariert wurde (Dim myCar As Car) – enthält nicht das Objekt selbst, sondern lediglich eine Referenz auf dieses Objekt. Das unterscheidet sie von regulären Variablentypen wie Integer oder String, in denen die Variable den Wert direkt enthält. Das bedeutet, dass mehr als eine Objektvariable auf das gleiche Objekt verweisen oder zeigen kann. Im Allgemeinen ist dies nicht der Fall. Sie erzeugen ein Objekt und verwenden es mit einer Variablen, aber es ist wichtig, in dieser Hinsicht den Unterschied zwischen Objekten und anderen Variablentypen zu verstehen.
223
Mit Objekten arbeiten
7.2
Das Konzept in Code umwandeln
Lassen Sie uns einige dieser Konzepte in Code umwandeln. Um ein Objekt zu erzeugen, benötigen Sie als Erstes eine Klasse. Es stehen viele Klassen zur Verfügung, die von anderen Programmierern erstellt wurden. Im Grunde ist die gesamte .NET-Architektur eine »Klassenbibliothek«, eine Reihe von bereits existierenden Klassen, die Sie in Ihren Programmen verwenden können. Für unser Beispiel werden wir aber einfach unsere eigene Klasse erstellen, also die Klasse Auto. Der Zweck der heutigen Lektion ist nicht, Ihnen alles beizubringen, was Sie wissen müssen, um Ihre eigenen Klassen zu erstellen, sondern Ihnen genug über das Erstellen von Klassen beizubringen, damit Sie die Klassen verstehen und anwenden können, die bereits vorhanden sind. Sie werden dieses Wissen brauchen, um sich am Tag 8, Einführung in das .NET-Framework, in das .NETFramework zu vertiefen und bei der Entwicklung Ihrer eigenen Objekte am Tag 15, Objekte in Visual Basic .NET erzeugen, darauf aufbauen. Öffnen Sie Visual Studio .NET und erzeugen Sie ein neues LEERES PROJEKT im Ordner VISUAL BASIC PROJEKTE (siehe Abbildung 7.1). Dieses Projekt enthält keine Dateien, also müssen wir eine Datei hinzufügen. Wählen Sie aus dem Menü PROJEKT und KLASSE HINZUFÜGEN aus. Damit wird eine neue leere Klasse zum Projekt hinzugefügt (nennen Sie sie clsCar.vb). Das ist ein guter Anfang für diese Übung. In dieser Datei wurde nun die Hülle einer Klasse mit dem folgenden Code erstellt: Public Class clsCar End Class
Nun haben Sie zwar eine Klasse erzeugt, aber sie ist noch vollständig leer. Innerhalb dieser Codehülle können wir nun die Eigenschaften und Methoden unserer Auto-Objekte beschreiben. Alles, was wir in diese Klasse setzen, wird Teil jeder Objektinstanz, die von ihr erzeugt wird. Fügen wir fürs erste die Eigenschaften Make (Bauart), Model (Modell) und Color (Farbe) hinzu.
Eigenschaften Eigenschaften sind Objektattribute, die Sie abrufen und setzen können und die Sie auf zwei verschiedene Arten zu einer Klasse hinzufügen können. Die erste und einfachste Methode ist, eine Variable als Public zu deklarieren. Wie bereits am Tag 3, Einführung in die Programmierung mit Visual Basic .NET, erörtert, beschreibt Public den Sichtbarkeitsbereich der Variablen. Im Fall einer Klasse steht daher alles, was als Public deklariert wird, für jeden zur Verfügung, der diese Klasse benutzt.
224
Das Konzept in Code umwandeln
Abbildung 7.1: Ein LEERES PROJEKT ohne jegliche Dateien.
In unserer Klasse würde das Einfügen der Zeile Dim Year As Integer keine öffentliche Eigenschaft erzeugen, da Year nur intern für unsere Klasse zur Verfügung stünde. Wenn wir Year als Public Year As Integer deklarieren würden, dann wäre es plötzlich für jede Instanz dieses Objekts öffentlich.
Eigenschaftsanweisungen Alternativ können Eigenschaften mit einer speziellen Property-Syntax deklariert werden: Dim myVar as Public Property () As Get Return myVar End Get Set(ByVal Value As ) myVar = value End Set End Property
Die beiden Teile der Eigenschaftsdefinition, Get und Set, stellen das Abrufen des Eigenschaftswertes und das Setzen dieses Wertes dar. Im Allgemeinen gibt der Get-Code einfach den Wert einer internen Variablen zurück (eine Klassenvariable, die die Eigenschaft repräsentiert – normalerweise mit der Vorsilbe m, um einen Elementwert (member value) anzuzeigen), und der Set-Code setzt einen Wert (der durch das besondere Schlüsselwort value angegeben wird) in genau dieser internen Variablen. Um die Eigenschaft Farbe in clsCar zu implementieren, könnten wir ähnlichen Code wie in Listing 7.1 benutzen.
225
Mit Objekten arbeiten
Listing 7.1: Eigenschaften in Klassen erzeugen 1 Public Class clsCar 2 Dim m_sColor As String 3 Public Property Color() As String 4 Get 5 Return m_sColor 6 End Get 7 Set(ByVal Value As String) 8 m_sColor = value 9 End Set 10 End Property 11 End Class
Codieren Sie nun Eigenschaften für Bauart und Modell und denken Sie daran, auch zwei zusätzliche interne Variablen einzufügen (beide Eigenschaften sollten String als Datentyp verwenden). Diese zweite Methode der Eigenschaftsdeklaration kann ähnliche Ergebnisse hervorbringen, aber sie ist viel flexibler, weil sie es Ihnen ermöglicht, jeden erforderlichen Code zu definieren, um das Setzen und Abrufen eines Eigenschaftswertes richtig zu steuern.
Schreibgeschützte und lesegeschützte Eigenschaften Es ist nicht ungewöhnlich, eine Eigenschaft zu haben, die schreibgeschützt sein soll, wie z.B. eine Versionseigenschaft oder vielleicht ein Erstellungsdatum. Es ist wesentlich unüblicher, aber nicht unmöglich, eine Eigenschaft zu haben, die zwar geändert, aber nicht gelesen werden kann, wie z.B. ein Passwortfeld. In früheren Versionen von Visual Basic konnte man diese schreib- oder lesegeschützten Eigenschaften einfach erzeugen, indem man entweder den Set- oder den Get-Teil der Eigenschaftsdefinition nicht implementierte. Das ist teilweise das Gleiche, was Sie in Visual Basic .NET tun. Wenn Sie eine schreibgeschützte Eigenschaft erstellen, schreiben Sie den Set-Teil der Eigenschaftsdefinition nicht, aber Sie müssen außerdem ReadOnly als zusätzliches Schlüsselwort angeben. Das sehen Sie im folgenden Code bei der Eigenschaft Description: Diese ist ein berechneter Wert und daher würde es nicht viel Sinn haben, wenn man sie schreiben könnte. Sie sollten eine Eigenschaft wie Description nicht setzen, da sie einfach nur das Ergebnis einer Berechnung ist. 1 Public ReadOnly Property Description() As String 2 Get 3 Return m_sColor & " " & m_sMake & " " & m_sModel 4 End Get 5 End Property
226
Das Konzept in Code umwandeln
Bei einer lesegeschützten Eigenschaft lautet das Schlüsselwort WriteOnly und Sie geben nur den Set-Teil der Eigenschaftsdefinition an: 1 Dim m_sPassword As String 2 Public WriteOnly Property Password() As String 3 Set(ByVal Value As String) 4 m_sPassword = Value 5 End Set 6 End Property
Eine Objektinstanz erzeugen Nachdem wir eine Klasse haben, können wir eine Instanz davon in einem anderen Teil unseres Objekts erzeugen. Fügen Sie zu Ihrem Projekt ein Modul namens Main.vb hinzu (wählen Sie aus dem Menü PROJEKT und MODUL HINZUFÜGEN aus) und erstellen Sie darin die Routine Sub Main(). Ihr Modul sollte an diesem Punkt so aussehen wie der Code in Listing 7.2.
Listing 7.2: Ein neues Modul für Testzwecke erstellen 1 Module Main 2 Sub Main() 3 End Sub 4 End Module
Diese neue Prozedur Main() ist der Beginn für unsere Lösung. Der Code darin wird ausgeführt, wenn wir diese Anwendung ausführen. Hier werden wir beginnen, den Code zu schreiben, um mit unserer neuen Klasse zu arbeiten. Um unsere neue Klasse nutzen zu können, müssen wir zuerst eine Variable mit einem geeigneten Typ erzeugen: Dim objCar As clsCar 'oder Chapter7.clsCar, mehr dazu später
Diese Deklaration sieht wie die Deklaration einer regulären Variablen wie String oder Integer aus, aber sie ist anders. Wir haben nun eine Variable, die eine Referenz auf ein Objekt des Typs clsCar aufnehmen kann, die aber derzeit nichts enthält. Wenn wir eine String-Variable deklarieren, wird die Zeichenkette selbst deklariert. Obwohl sie noch über keine Daten verfügt, existiert sie doch. In diesem Fall haben wir aber überhaupt nichts in objCar. Also ist der nächste Schritt, eine Instanz unserer Klasse zu erzeugen, was wir mit dem Schlüsselwort New tun können: objCar = New clsCar()
227
Mit Objekten arbeiten
Nun haben wir also eine Instanz von clsCar erzeugt und diesem neuen Objekt eine Referenz auf die Variable objCar zugewiesen. Nun können wir über objCar auf die Eigenschaften des neuen Objekts zugreifen: 1 objCar.Color = "Red" 2 objCar.Make = "Ford" 3 objCar.Model = "Escort"
Diese Eigenschaften können genauso leicht abgerufen werden: Dim sCarDesc As String sCarDesc = objCar.Color & " " & objCar.Make & " " & objCar.Model
Da Objekte nach Referenz arbeiten, können wir weitere Variablen erzeugen, die alle auf die gleiche Stelle zeigen: 1 2 3 4 5 6 7 8
Dim objCar As clsCar Dim objCar2 As clsCar Dim sMake As String objCar = New clsCar() objCar2 = objCar objCar.Make = "Ford" objCar2.Make = "Chevy" 'objCar2.Make ist äquivalent zu objCar.Make
Vergleichen Sie dies mit einer Nicht-Objektvariablen wie String, bei der sich der eigentliche Wert zwischen zwei Verwendungen ändert: 1 2 3 4 5 6 7
Dim sFirstValue As String Dim sSecondValue As String sFirstValue = "Dog" sSecondValue = sFirstValue sSecondValue = "Cat" ' sFirstValue sSecondValue
Normalerweise hört eine Variable sofort auf zu existieren, sobald sie den Gültigkeitsbereich verlässt (am Tag 3 finden Sie mehr Informationen zu Variablen und ihrem Gültigkeitsbereich). Da mehrere Variablen auf ein einziges Objekt verweisen können, sind die Regeln für die Objektzerstörung etwas anders. Wenn alle Variablen verschwunden sind, die ein Objekt referenzierten, kann auf das Objekt nicht mehr zugegriffen werden und es wird schließlich von den Hintergrunddiensten von .NET zerstört. Dieser Prozess der Speicherbereinigung (Garbage Collection) erlaubt es dem Programm, Objekte beliebig zu erzeugen und freizugeben, da es weiß. Auf diese Weise räumt .NET für sein Programm auf und bietet einen Dienst, damit sich die einzelnen Programme nicht um solche Dinge kümmern müssen.
228
Das Konzept in Code umwandeln
Code in Klassen einkapseln Nun haben Sie im Code die Erzeugung einer Klasse, die Instanziierung von Objekten, die auf dieser Klasse basieren, und die Bearbeitung der Objekteigenschaften gesehen. Stellen Sie sich nun vor, dass eine Klasse im Gegensatz zu einer UDT (wiederum finden Sie am Tag 3 mehr Informationen zu Strukturen und benutzerdefinierten Typen) nicht nur eine Reihe von Werten, sondern auch Verhaltensweisen beschreiben kann. Um diese Implementierung des Verhaltens zu ermöglichen, enthält eine Klasse mehr als nur einfachen Code, um Eigenschaftswerte zu setzen und abzurufen: Sie kann auch Code enthalten, um die Eigenschaftsvalidierung und andere Aktionen durchzuführen. Bei unserer clsCar können wir diese Funktion zeigen, indem wir Validierungscode zu unseren Eigenschaften hinzufügen. Derzeit könnten Sie die Eigenschaft Color auf jeden beliebigen Zeichenkettenwert setzten, auch wenn es gar keine Farbe ist: (objCar.Color="John"). Um unsere Darstellung eines Car-Objekts etwas intelligenter zu gestalten, können wir ein wenig Code hinzufügen, der jeden übermittelten Wert mit einer Farbenliste vergleicht und alles ablehnt, was nicht passt. Dafür müssen wir die Routine für die Eigenschaft Color so umschreiben, wie Sie es in Listing 7.3 sehen.
Listing 7.3: Zur Eigenschaft Color eine Validierung hinzufügen 1 Public Class clsCar 2 Dim m_sColor As String 3 4 Public Property Color() As String 5 6 Get 7 Return m_sColor 8 End Get 9 10 Set(ByVal Value As String) 11 12 Select Case Value.ToUpper() 13 Case "RED" 14 m_sColor = Value 15 Case "YELLOW" 16 m_sColor = Value 17 Case "BLUE" 18 m_sColor = Value 19 Case Else 20 Dim objException As System.Exception
229
Mit Objekten arbeiten
21 objException = New System.ArgumentOutOfRangeException() 22 Throw objException 23 End Select 24 End Set 25 End Property 26 27 End Class
Nun führt der Versuch, die Eigenschaft auf eine ungültige Farbe zu setzen (ungültig nach der internen Liste unseres Codes, für die z.B. die beliebte Farbe »Mauve« (malvenfarben) ungültig ist) zu einer Ausnahme. Mehr Informationen zu Ausnahmen und ausnahmenbasierter Fehlerbehandlung finden Sie am Tag 6, Was zu tun ist, wenn ein gutes Programm ungezogen ist, und wie Sie das verhindern. Wie in der entsprechenden Lektion beschrieben wurde, können wir mit dieser Ausnahme korrekt umgehen, indem wir unseren Testcode (der in Sub Main()enthalten ist) neu schreiben, sodass er eine Try...Catch-Struktur enthält. Diesen modifizierten Code sehen Sie in Listing 7.4.
Listing 7.4: Wir fügen eine Fehlerbehandlung in unseren Code ein. 1 Sub Main() 2 Dim objCar As clsCar 3 Dim sColor As String 4 5 objCar = New clsCar() 6 objCar.Year = 1999 7 8 Try 9 objCar.Color = "Green" 10 Catch objException As System.ArgumentOutOfRangeException 11 'Hoppla! Behandle den Fehler! 12 System.Console.WriteLine(”Whoops!”) 13 End Try 14 sColor = objCar.Color 15 16 objCar.Make = "Ford" 17 objCar.Model = "Escort" 18 System.Console.WriteLine(objCar.Description) 19 End Sub
Neben der an sich bereits mächtigen Eigenschaftsvalidierung kann eine Klasse eine Codefunktion oder Subroutine enthalten, die nicht zu dem Code gehört, der Eigenschaften
230
Das Konzept in Code umwandeln
abruft oder setzt. Diese wird im Allgemeinen als Methode bezeichnet. Methoden sollen einem Objekt Funktionalität verleihen und agieren häufig basierend auf Eigenschaftsinformationen (weil diese zur Verfügung stehen). Für unsere clsCar wäre es z.B. eine nützliche Methode, ein Alter des Autos anzugeben, indem das aktuelle Datum und die Uhrzeit mit einer Eigenschaft des Autos verglichen wird, die sein Herstellungsdatum darstellt. Die Erzeugung der Eigenschaft Herstellungsdatum funktioniert genau wie bei den bisherigen Eigenschaften, nur dass es sich diesmal um ein Datum handelt. Das Hinzufügen dieser Methode ist eigentlich nichts weiter als das Erzeugen einer öffentlichen Funktion in unserer Klassendefinition. Zuerst die neue Eigenschaft: 1 Dim m_dtManufactured As Date 2 3 Public Property Manufactured() As Date 4 Get 5 Return m_dtManufactured 6 End Get 7 Set(ByVal Value As Date) 8 m_dtManufactured = value 9 End Set 10 End Property
Wenn wir diese Funktion als private erstellt hätten, wäre sie zwar aus der Klasse heraus nutzbar, aber von keinem anderen Code aus verfügbar. Alternativ können wir auch Elemente unserer Klasse (Eigenschaften, Funktionen, Subroutinen) als Friend deklarieren. Diese Deklaration stellt sicher, dass Code innerhalb des gleichen Assemblers auf diese Teile der Klasse so zugreifen kann, als wären sie öffentlich, und dass sie für jeden Code, der für den Asseümbler dieser Klasse extern wäre, verborgen (privat) blieben. (Assembler werden ausführlich als Teil der Entwicklung am Tag 19, Die Anwendung bereitstellen, behandelt. Stellen Sie sich Assembler vorerst als die restlichen Teile der gleichen Solution vor.) Nachfolgendes Listing zeigt die neue Methode.
Listing 7.5: Die Methode GetAge 1 Public Function GetAge() As Long 2 Dim lngDays As Long 3 Dim dtCurrent As Date
231
Mit Objekten arbeiten
4 5 6 7 End
dtCurrent = System.DateTime.Today lngDays = dtCurrent.Subtract(m_dtManufactured).Days Return lngDays Function
Nachdem wir unserer Klasse diesen Code hinzugefügt haben, können wir ihn wie in Listing 7.6 durch jede Instanz (obj.Car.GetAge()) aufrufen.
Listing 7.6: Wir verwenden unsere neue Methode 1 2 3 4
Dim objCar As clsCar objcar = New clsCar() objCar.Manufactured = #1/30/2000# System.Console.WriteLine(objCar.GetAge())
In gewisser Weise wäre die gerade erzeugte neue Methode besser als Eigenschaft geeignet (die wir dann Age nennen würden, da es sich um ein Attribut und nicht um eine Aktion handelt), denn sie führt eigentlich keine Aktion durch, sondern gibt nur einen Wert zurück. Für unsere Car-Klasse wäre eine bessere Beispielmethode etwas Aktionsbezogenes, wie etwa die Methode StartEngine, von der Sie eine einfache Implementierung in Listing 7.7 sehen.
Listing 7.7: Unsere neue aktionsorientierte Methode 1 Public Sub StartEngine() 2 System.Console.WriteLine("Vroom, Vroom...!!!") 3 End Sub
Wenn wir diesen Code zu clsCar hinzufügen würden, stünde uns eine eher aktionsorientierte Methode zur Verfügung. Durch eine Kombination von Eigenschaften (einige mit Code, andere ohne) und Methoden ist es möglich, komplexe Objekte zu erzeugen. Denken Sie daran, dass die Objekte, die das .NET-Framework bilden (wie System.Console und andere) nach genau den gleichen Regeln erstellt sind und die gleichen Charakteristika wie Objekte haben, die Sie erzeugen können. Der einzige wirkliche Unterschied zwischen dem .NET-Framework und Ihren eigenen Objekten besteht darin, dass Sie diese im Framework nicht schreiben müssen!
232
Themen für Fortgeschrittene
7.3
Themen für Fortgeschrittene
Mit Eigenschaften und Methoden können Sie zwar bereits mächtige und komplexe Objekte erzeugen, aber die Objektunterstützung in .NET hat neben diesen Grundelementen noch viel mehr Funktionen. Diese fortgeschrittenen Funktionen erleichtern es Ihnen, Konzepte und Entitäten in ihrem Code darzustellen. Dadurch entsteht ein System, das leichter zu verwenden, zu pflegen und zu erweitern ist. Die Objektunterstützung in .NET ist ein weites Feld. Wir werden hier nur einen kurzen Überblick über fünf Hauptgebiete geben: Überladen, Vererbung, Konstruktoren, Namensräume und statische Klassenelemente.
Überladen Dieser Abschnitt beschreibt die Grundlagen des Überladens. Weitere Einzelheiten werden am Tag 15 erörtert, wenn wir unsere eigenen Objekte erzeugen. Das Überladen erlaubt es einer einzelnen Funktion oder Subroutine, mit einer Vielzahl unterschiedlicher Parameter aufgerufen zu werden. So kann z.B. eine einzelne Methode Parameter in unterschiedlichen Kombinationen annehmen oder verschiedene Datentypen verwenden. Wenn wir zu unserem clsCar-Beispiel zurückkommen, können wie die Methode GetAge so entwerfen, dass sie auf viele verschiede Arten funktionieren kann. Die bestehende Implementierung nimmt keine Parameter entgegen und gibt den Unterschied zwischen dem aktuellen Datum und dem Herstellungsdatum in Tagen zurück. Aber vielleicht möchten wir es einem Anwender unseres Objekts auch ermöglichen, den Unterschied zwischen dem Herstellungsdatum und jedem beliebigen Datum zu erfragen und außerdem die Zeiteinheit anzugeben, in der dieser Zeitraum gemessen wird. Um das ohne das Konzept des Überladens zu tun, müssten wir für jeden möglichen Aufruf eine andere Funktion erstellen (siehe Listing 7.8).
Listing 7.8: Viele Variationen einer einzelnen Methode erstellen 1 Public Function GetAge() As Long 2 Dim lngDays As Long 3 Dim dtCurrent As Date 4 dtCurrent = System.DateTime.Today 5 lngDays = dtCurrent.Subtract(m_dtManufactured).Days 6 Return lngDays 7 End Function
233
Mit Objekten arbeiten
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
Public Function GetAgeAtDate(ByVal dtPointInTime As Date) As Long Dim lngDays As Long lngDays = dtPointInTime.Subtract(m_dtManufactured).Days Return lngDays End Function Public Function GetAgeInUnits(ByVal sUnit As String) As Long Dim lngUnits As Long Dim dtCurrent As Date Dim tsDifference As System.TimeSpan dtCurrent = System.DateTime.Today tsDifference = dtCurrent.Subtract(m_dtManufactured) Select Case sUnit Case "Hours" lngUnits = tsDifference.Hours Case "Days" lngUnits = tsDifference.Days Case "Minutes" lngUnits = tsDifference.Minutes Case "Years" lngUnits = tsDifference.Days \ End Select Return lngUnits End Function
365
All diese Funktionen sind eigentlich nur Varianten von GetAge, aber jede unterschiedliche Parameterliste und jeder entsprechend unterschiedliche Code benötigt einen eigenen Funktionsnamen. Durch Überladen vermeiden wir diese Einschränkung und können alle diese Funktionen mit dem gleichen Namen GetAge erzeugen. Um diese Funktion zu nutzen, müssen wir nur das Schlüsselwort Overloads vor jeder (einschließlich der ursprünglichen) Funktionsdeklaration hinzufügen (vor dem Public) und jeden Funktionsnamen in den gleichen Namen ändern: Public Overloads Function GetAge() As Long End Function Public Overloads Function GetAge(ByVal dtPointInTime As Date) As Long End Function Public Overloads Function GetAge(ByVal sUnit As String) As Long End Function
234
Themen für Fortgeschrittene
In unserem Beispielcode, der diese Funktion verwendet, können wir nun zwischen den drei möglichen Deklarationen dieser Funktion wählen (siehe Abbildung 7.2). Jede Funktionsdeklaration muss in gewisser Weise anders sein – z.B. hinsichtlich der Anzahl der Parameter, des Datentyps der Parameter oder des Datentyps des Rückgabewertes –, sonst ist sie nicht zulässig. Mit Überladen ist gemeint, dass die gleiche Aktion oder Anfrage auf verschiedene Arten verwendet werden kann, und dass es Ihnen erlaubt ist, dies in Ihrem Objekt zu modellieren, ohne mehrere unterschiedliche Methoden (GetAge, GetAgeFromDate usw.) erstellen zu müssen. Diese Technik wird im gesamten .NET-Framework verwendet, damit Sie Funktionen mit einer Vielzahl unterschiedlicher Parametergruppen aufrufen können. Stellen Sie sich z.B. die Methode System.Console.WriteLine vor, die mit einer von 18 verschiedenen Parameterlisten aufgerufen werden kann. Wie das Framework zeigt, ist das eine nützliche Methode, um Objekte zu vereinfachen und den Programmenviele Optionen zu bieten.
Abbildung 7.2: Alle verfügbaren Versionen einer Funktion werden durch IntelliSense gezeigt, wenn Sie mit Visual Studio .NET einen Client erzeugen.
Vererbung Für viele ist dies eine der aufregendsten Funktionen von Visual Basic .NET – eine Funktion, die für objektorientierte Systeme eine der Grundlagen ist und die bis zu dieser Version in Visual Basic fehlte. Ohne weiter darauf eingehen zu wollen, muss ich gestehen, dass ich es irgendwie vor .NET viele Jahre lang geschafft habe, Systeme ohne Vererbung
235
Mit Objekten arbeiten
zu erstellen. Dennoch ist das Hinzufügen der Vererbung zu Visual Basic ein wichtiger Schritt und lohnt eine kleine Erörterung. Wie bereits weiter oben in der heutigen Lektion besprochen, bieten Objekte Möglichkeiten, Konzepte oder Entitäten in Code darzustellen, und die Objektfunktionen von Visual Basic sollen Ihnen helfen, die nützlichste mögliche Darstellung zu finden. In vielen Situationen ist eine Entität das, was wir ein Unterobjekt einer elementareren Entität nennen würden. Viele Beispiele dafür stehen in Programmierbüchern; das Konzept ist also nichts Neues. Stellen Sie sich vor, dass Ihre Klasse von Objekten Autos darstellen soll, etwa einen Ford Fiesta, einen Toyota Corolla oder einen VW Golf. Die Klasse hätte verschiedene Eigenschaften, wie Türen (die Anzahl der Türen eines Autos), MaxGeschwindigkeit, Farbe und andere. Diese allgemeine Klasse Auto enthält verschiedene Unterklassen von Objekten, wie Kleinbus oder Kabriolett. Diese Klassen hätten natürlich alle Eigenschaften Ihrer Elternklasse Auto, wie Türen, MaxGeschwindigkeit und Farbe, aber sie könnten auch eigene, einzigartige Eigenschaften haben. Ein Kleinbus könnte z.B. Eigenschaften haben, die die Größe und die Raumaufteilung beschreiben. Diese Beziehung zwischen Auto und seinen Unterklassen Kleinbus und Kabriolett wäre eine Eltern-Kind-Beziehung und die Methode, die in unserem System diese Beziehung darstellt, wird Vererbung genannt. Die Klasse Kleinbus erbt von Ihrer Oberklasse Auto. Diese Beziehung bedeutet, dass zusätzlich zu allen Eigenschaften und Methoden, die in der Kindklasse erzeugt werden, das Kind auch die Eigenschaften und Methoden der Elternklasse besitzt. Das vorherige Beispiel begann bei der Klasse Auto und ging dann abwärts. Nehmen wir den gleichen Anfangspunkt Auto und gehen wir für ein detaillierteres Beispiel der Vererbung nach oben. Für den Anfang haben wir vielleicht eine Oberklasse Gefährt (Vehicle), die jede Art von Gefährt (Boot, Auto, LKW, Flugzeug) darstellt und die Eigenschaften MaxGeschwindigkeit (MaxSpeed), AnzahlDerPassagiere (NumberOfPassengers), Farbe (Color) und Beschreibung (Description) hat. Wir können diese Klasse ganz einfach in Visual Basic-Code darstellen, wie Sie in Listing 7.9 sehen.
Listing 7.9: Unsere Klasse Vehicle 1 Public Class Vehicle 2 3 Public Property MaxSpeed() As Long 4 Get 5 End Get 6 Set(ByVal Value As Long)
236
Themen für Fortgeschrittene
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 End
End Set End Property Public Property NumberOfPassengers() As Long Get End Get Set(ByVal Value As Long) End Set End Property Public Property Color() As String Get End Get Set(ByVal Value As String) End Set End Property Public Function Description() As String End Function Class
Der Code, der in den verschiedenen Prozeduren in dieser Klasse steht, ist für unser Beispiel nicht wirklich relevant, sodass wir ihn vorerst auslassen. Wenn wir zu einer Hauptroutine springen würden, um unser Objekt auszuprobieren (z.B. der Methode Sub Main() unseres Projekts, wie in früheren Beispielen), würden wir sehen, dass wir Objekte vom Typ Gefährt erzeugen und mit ihren Eigenschaften arbeiten können (siehe Listing 7.10).
Listing 7.10: Wir arbeiten mit unserer Klasse Vehicle 1 Module UseVehicle 2 Sub Main() 3 Dim objVehicle As Vehicle 4 objVehicle = New Vehicle() 5 6 objVehicle.Color = "Red" 7 objVehicle.MaxSpeed = 100 8 End Sub 9 End Module
237
Mit Objekten arbeiten
Indem wir nun eine zusätzliche Klasse zu unserem Projekt hinzufügen (siehe Listing 7.11), können wir eine Klasse (Car) erzeugen, die von Vehicle erbt, genau wie die echte Klasse des Objekts Car eine Unterklasse oder ein Kind der Klasse des Objekts Vehicle ist. Da wir eine Klasse erzeugen, die nur mit Autos umgehen soll, können wir einige Eigenschaften hinzufügen (NumberOfDoors und NumberOfTires) die für diese Unterklasse von Vehicle speziell sind.
Listing 7.11: Eine Kindklasse erzeugen 1 Public Class Car 2 Inherits Vehicle 3 4 Public Property NumberOfTires() As Integer 5 Get 6 7 End Get 8 Set(ByVal Value As Integer) 9 10 End Set 11 End Property 12 13 Public Property NumberOfDoors() As Integer 14 Get 15 16 End Get 17 Set(ByVal Value As Integer) 18 19 End Set 20 End Property 21 22 End Class
Der Schlüssel zu diesem Code ist die Zeile Inherits Vehicle, die Visual Basic sagt, dass diese Klasse ein Kind der Klasse Vehicle ist und daher alle Eigenschaften und Methoden dieser Klasse erben soll. Wiederum wird kein Code in irgendeine der Eigenschaftsdefinitionen gesetzt, weil sie zu diesem Zeitpunkt nicht wirklich relevant sind. Wenn wir diesen Code haben, können wir ohne weiteres den Effekt der Inherits-Anweisung sehen. Wenn wir zu unserer Prozedur Main() zurückkehren, können wir ein Objekt des Typs Car erzeugen und sehen, dass es sowohl über seine eigenen Eigenschaften als auch über die der Elternklasse verfügt (siehe Abbildung 7.3).
238
Themen für Fortgeschrittene
Abbildung 7.3: Klassen enthüllen alle ihre öffentlichen Eigenschaften und Methoden, zusammen mit den öffentlichen Eigenschaften und Methoden der Klasse, auf der sie basieren.
Wenn eine geerbte Klasse den Methoden oder Eigenschaften einer Basisklasse neue hinzufügt, dann erweitert sie diese. Zusätzlich zu der Erweiterung ist es auch möglich, dass eine Kindklasse einen Teil oder die gesamte Funktionalität der Basisklasse überschreibt. Das geschieht, wenn ein Kind eine Methode oder Eigenschaft implementiert, die auch in der Eltern- oder Basisklasse definiert ist. In diesem Fall wird der Code der Kindklasse anstelle des Codes der Elternklasse ausgeführt, wodurch Sie spezialisierte Versionen der Basismethode oder -eigenschaft erzeugen können. Damit eine Kindklasse einen Teil der Elternklasse überschreiben kann, muss dieser Teil in der Definition der Elternklasse als Overridable markiert sein. In der Version von Vehicle, die wir weiter oben aufgeführt haben, hatte keine der Eigenschaften das Schlüsselwort Overridable und daher können Kindklassen keine eigenen Implementierungen angeben. Als Beispiel dafür, wie das Überschreiben in der Eltern- und Kindklasse eingerichtet wird, markiert der Code in Listing 7.12 die Funktion Description() als überschreibbar und überschreibt sie dann in der Kindklasse Car. Beachten Sie, dass irrelevante Abschnitte der beiden Klassen zu Gunsten der Klarheit wegen weggelassen wurden.
Listing 7.12: Die Schlüsselwörter Overridable und Overrides verwenden 1 Public Class Vehicle 2
239
Mit Objekten arbeiten
3 'Code wurde zu Gunsten der Klarheit entfernt … 4 5 Public Overridable Function Description() As String 6 Return "This is my generic vehicle description!" 7 End Function 8 End Class 9 10 Public Class Car 11 Inherits Vehicle 12 13 'Code wurde zu Gunsten der Klarheit entfernt … 14 15 Public Overrides Function Description() As String 16 Return "This is my Car Description" 17 End Function 18 19 End Class
Wenn Sie eine Methode oder Eigenschaft überschreiben, wie wir es in Listing 7.12 getan haben, können Sie auf das ursprüngliche Element der Elternklasse zurückverweisen, indem Sie das eingebaute Objekt MyBase verwenden. Um z.B. auf die bestehende Methode Description() der Klasse Vehicle zu verweisen, könnten wir MyBase.Description() aus der Methode Description von Car aufrufen. Mit dieser Funktionalität können Sie durch das Überschreiben zusätzliche Funktionalität bieten, ohne auch noch den gesamten ursprünglichen Code zu wiederholen. Sie können Code nicht nur als Overridable markieren: Es ist auch möglich, eine Methode oder Eigenschaft als MustOverride und eine Klasse als MustInherit zu markieren. Das Schlüsselwort MustOverride gibt an, dass jedes Kind dieser Klasse seine eigene Version dieser Eigenschaft oder Methode mitbringen muss. Das Schlüsselwort MustInherit bedeutet, dass diese Klasse nicht alleine verwendet werden kann, sondern von einer anderen Klasse erben muss. Es ist wichtig zu wissen, dass eine Klasse selbst als MustInherit gekennzeichnet sein muss, wenn sie eine Methode enthält, die als MustOverride gekennzeichnet ist. Die Vererbung ist ein großes Thema und wir haben noch nicht alles darüber gesagt, aber mit den Informationen der heutigen Lektion sind Sie in der Lage, einige Anwendungen zu entwerfen, die diese Objektfunktion ausnutzen.
Die Basis aller Basisklassen Wenn Sie sich die Liste dessen ansehen, was von dieser neuen Instanz der Klasse Car enthüllt wird, werden Sie mehr sehen als nur die Eigenschaften von Vehicle und Car. Die Methoden ToString() und GetType() werden von diesem Objekt enthüllt, sind aber nicht Teil seiner Klasse oder seiner Elternklasse. Diese Methoden sind eigentlich ein weiteres
240
Themen für Fortgeschrittene
Ergebnis der Vererbung. Während Car von Vehicle erbt, erben Vehicle und Car (und jede andere Klasse in .NET) von der Basisklasse System.Object. Diese oberste Basisklasse hat ein paar Methoden, die automatisch Teil jeder Klasse sind. Ein weiteres Ergebnis der Vererbung betrifft die Datentypen. Wie bereits am Tag 3 besprochen, hat jede Variable einen bestimmten Datentyp. Objekte sind da keine Ausnahme. Wenn wir eine Variable mit dem Typ Car deklarieren, ist das eine strenge Form der Datentypisierung, wie bei Integer und String. Wenn wir einen Funktionsparameter mit diesem Typ deklarieren, dann kann nur dieser Objekttyp an die Funktion übergeben werden. In einer Vererbungssituation kann die Kindklasse wie eine Instanz der Elternklasse agieren. Das bedeutet in unserem Beispiel, dass wir unser Car-Objekt in Variablen und Prozedurargumente setzen können, die den Typ Vehicle haben. Listing 7.13 zeigt ein Beispiel dafür.
Listing 7.13: Eine Kindklasse agiert als Instanz der Elternklasse. 1 Sub Main() 2 Dim objVehicle As Vehicle 3 Dim objCar As Car 4 objCar = New Car() 5 6 objCar.NumberOfDoors = 4 7 objCar.NumberOfPassengers = 6 8 9 objVehicle = objCar 10 11 objVehicle.Color = "Red" 12 objVehicle.MaxSpeed = 100 13 End Sub
Die Instanz von Car, die von objCar repräsentiert wird, wurde einfach in die Variable objVehicle gesetzt und konnte von da an genau wie ein Vehicle-Objekt behandelt werden. Die Tatsache, dass ein Kindobjekt so verwendet werden kann, als wäre es eine Instanz der Elternklasse, ermöglicht die Erstellung von generischem Code, der mit einer Klasse oder jeder ihre Unterklassen arbeiten kann. Das ist nur eine von vielen Möglichkeiten, wie mit der Vererbung bessere Lösungen geschaffen werden können. Dies war ein kurzer Überblick zum Thema Vererbung, in Bezug auf Design und Implementierung eigentlich eine ausführlichere Erörterung verdient. Aus diesem Grunde werden Vererbung und andere objektorientierte Themen im Rest des Buches weiter behandelt.
241
Mit Objekten arbeiten
Konstruktoren Wenn Sie ein Objekt verwenden möchten, müssen Sie entweder mit einer bestehenden Instanz arbeiten oder eine eigene erzeugen. Eine Instanz erzeugen Sie mit dem Schlüsselwort New, das einen Speicherbereich für die neue Instanz der angegebenen Klasse einrichtet. Konstruktoren bieten eine Möglichkeit, der Klasse zur Erstellungszeit Informationen zu liefern, damit sie sich selbst initialisieren oder an diesem Punkt andere Konfigurationsaufgaben durchführen kann. Wenn eine Klasse einen Konstruktor hat, was bei vielen .NET-Framework-Klassen der Fall ist, dann können Sie normalerweise zur Erstellungszeit Parameter beim Aufruf von New angeben. Der folgende Code zeigt, wie ein neues Ausnahmeobjekt (mehr Informationen zu Ausnahmen und anderen Fehlerbehandlungsthemen finden Sie am Tag 6) erzeugt und eine Fehlernachricht als Parameter an seinen Konstruktor übergeben wird. Diese Fehlermeldung wird automatisch von dem neuen Objekt als Wert einer seiner Eigenschaften verwendet. Dim exError As System.Exception Dim sMessage As String sMessage = "This will be the error message." exError = New System.Exception(sMessage)
Einen Konstruktor für Ihre Vehicle-Klasse zu schreiben ist recht einfach. Sie müssen zuerst eine Methode namens New kreieren, die öffentlich ist und keine Parameter hat. Public Sub New() End Sub
Mit diesem fertigen Konstruktor (der nichts tut) werden Sie kaum einen Unterschied in Ihrem Code sehen: Ehe wir Funktionalität in diese Subroutine New() einfügen, geschieht überhaupt nichts Neues. Der Konstruktor kann sogar ohne Parameter verwendet werden, um interne Variablen zu initialisieren, wie z.B. das Manufactured Date in unserer VehicleKlasse (siehe Listing 7.14).
Listing 7.14: Mit einem Konstruktor die Elemente einer Klasse initialisieren. 1 2 3 4
Public Sub New() m_dtManufactured = System.Date.Now() End Sub
Genau wie jede andere Methode eines Objekts können Sie auch diese Methode überladen und mehr als eine Aufrufmöglichkeit bieten. Im Gegensatz zu anderen Methoden, ist für das Überladen des Konstruktors aber nicht das Schlüsselwort Overloads erforderlich. Sie
242
Themen für Fortgeschrittene
können einfach mehrere Versionen der Prozedur New erzeugen und jede wird als verfügbare Version des Konstruktors behandelt. In unserem Fall könnten wir schnell einige nützliche Konstruktoren erzeugen (siehe Listing 7.15), indem wir einfach an die verschiedenen Arten denken, wie jemand unser Objekt eventuell initialisieren möchte.
Listing 7.15: Den Konstruktor einer Klasse überladen, um Objekte zu initialisieren. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Public Sub New() m_dtManufactured = System.Date.Now() End Sub Public Sub New(ByVal sColor As String) m_dtManufactured = system.Date.Now m_sColor = sColor End Sub Public Sub New(ByVal dtManufactured As Date, _ ByVal sColor As String) m_dtManufactured = dtManufactured m_sColor = sColor End Sub
Im Fall einer Kindklasse, die von einer anderen Klasse erbt, möchten Sie vielleicht von Ihrer New-Prozedur aus den Konstruktor der Basisklasse aufrufen. Sie können das ganz einfach tun, indem Sie das Spezialobjekt MyBase und Code wie MyBase.New() verwenden.
Namensräume Ein Namensraum ist ein abstraktes Konzept, das verwendet wird, um eine Reihe von Klassen oder Modulen zusammenzufassen. So können Sie alle diese Dinge innerhalb eines einzelnen Elements der höheren Ebene logisch kategorisieren. Wenn wir also einen Namespace Kapitel7 oben in unserer Klasse haben und einen entsprechenden End Namespace weiter unten, haben wir letztlich eine Gruppierung namens Kapitel7 eingerichtet, die alle untergeordneten Klassen enthält. Nachdem dieser Namensraum existiert, wird er standardmäßig für Objektreferenzen in Code verwendet, der sich im gleichen Namensraum bewegt, kann aber auch explizit angegeben werden (Dim objCar as Kapitel7.Car).
243
Mit Objekten arbeiten
Es gibt viele Gründe für die Verwendung von Namensräumen: Hauptsächlich geht es darum, einen privaten Bereich zu erzeugen, um sicherzustellen, dass Ihre Klassennamen eindeutig sind. Indem wir einen Namensraum definieren, wird aus unserer Klasse Car Kapitel7.Car und diese steht daher nicht länger in Widerspruch zu anderen Klassen, die mit dem gleichen Namen angelegt werden. Ein etwas subtilerer Grund, Namensräume zu verwenden, besteht darin, dass sie den Code wartungsfreundlicher machen. Die Zusammenfassung von Klassen in Namensräumen einer höheren Ebene führt zu Code, der durch ein Kategorisierungsschema klar definiert und daher lesbarer und leichter zu pflegen ist. Für die Beispiele in diesem Buch hätten wir Namensräume erzeugen können, die auf dem Namen der Lektion basieren (z.B. würde der gesamte Code der heutigen Lektion unter dem Namensraum Kap7 angeordnet, und dann wiederum unter einem buchübergreifenden Namensraum wie vb21Tage stehen würden. Unsere Klassen könnten wir einfach als Kap7 anlegen, aber um Mehrdeutigkeiten zu vermeiden, könnten wir auch mit einem voll qualifizierten Namen auf sie verweisen (z.B. vbnet21Tage.Tage7.Car). Diese Methode des Gruppierens von Klassen ähnelt dem Konzept des Gültigkeitsbereichs, der am Tag 3 erörtert wurde: Eine Klasse muss nur innerhalb ihres besonderen Namensraums einzigartig sein. Wenn Sie zufällig eine Klasse erstellen, die sich den Namen mit einer Klasse eines anderen Namensraums teilt, müssen Sie sicherstellen, dass Sie den vollständigen Namen der Klasse angeben, wenn Sie außerhalb ihres eigenen Namensraums darauf verweisen. Namensräume sind hierarchisch geordnet, sodass Sie ein Schema mit mehreren Ebenen für die Gruppierung Ihrer Klassen und Objekte erstellen können, genau wie im .NET-Framework selbst. Es gibt zwei Möglichkeiten, einen Namensraum der unteren Ebene zu erzeugen: Sie definieren entweder den Namensraum mit dem voll qualifizierten Namen (siehe Listing 7.16) oder verschachteln Namensraumdefinitionen (siehe Listing 7.17).
Listing 7.16: Einen mehrteiligen Namensraum deklarieren 1 Imports System 2 Namespace MyApp.Console 3 Module Main 4 Sub Main() 5 Dim objHW As New MyApp.Console.Utilities() 6 objHW.PrintOut() 7 End Sub
244
Themen für Fortgeschrittene
8 9 10 11 12 13 14 15 16 17 18 19 End
End Module Public Class Utilities 'Führe die Anwendung aus. Public Sub PrintOut() Console.WriteLine(Environment.MachineName) Console.WriteLine(Environment.SystemDirectory) Console.WriteLine(Environment.GetLogicalDrives()) Console.WriteLine(Environment.Version.ToString()) End Sub End Class Namespace
Listing 7.17: Mit geschachtelten Namensräumen Objekthierarchien erzeugen 1 Imports System 2 Namespace MyApp 3 Namespace Console 4 Module Main 5 Sub Main() 6 Dim objHW As New MyApp.Console.Utilities() 7 objHW.PrintOut() 8 End Sub 9 End Module 10 11 Public Class Utilities 12 'Führe die Anwendung aus. 13 Public Sub PrintOut() 14 Console.WriteLine(Environment.MachineName) 15 Console.WriteLine(Environment.SystemDirectory) 16 Console.WriteLine(Environment.GetLogicalDrives()) 17 Console.WriteLine(Environment.Version.ToString()) 18 End Sub 19 End Class 20 End Namespace 21 End Namespace
245
Mit Objekten arbeiten
Innerhalb Ihrer Anwendung können Sie Namensräume verwenden, um konzeptionell verwandten Code zusammenzufassen, aber abgesehen von der Auswirkung auf den Gültigkeitsbereich von Klassen sind Namensräume für das Erstellen von Systemen nicht unbedingt erforderlich.
Gemeinsam genutzte Objekte und Elemente Weiter oben, als wir die Beziehung zwischen Klassen und Objekten beschrieben haben, haben Sie gelernt, dass Sie eine Instanz der Klasse beschaffen oder erzeugen müssen, wenn Sie eine Methode oder Eigenschaft aus dieser Klasse verwenden möchten. Im Allgemeinen ist das richtig; Sie können nicht direkt mit einer Klasse arbeiten, sondern nur mit ihren Instanzen. Es gibt aber eine Möglichkeit, bestimmte Funktionsmerkmale durch die Klasse selbst zu enthüllen, unabhängig von den konkreten Instanzen dieser Klasse: das Schlüsselwort Shared. Shared besagt, wie auch die anderen Zugriffsdeskriptoren, die Sie bereits gesehen haben (wie Public, Private und Friend), dass ein Teil einer Klasse ohne das Erzeugen einer Instanz verfügbar ist. Das .NET-Framework nutzt dies in seinen Klassen, wie Sie an der enthüllten Eigenschaft Today der Klasse System.DateTime (Listing 7.18) sehen.
Listing 7.18: Eingebaute Funktionen von Visual Basic 6.0 wurden durch gemeinsam genutzte Methoden in Visual Basic .NET ersetzt. 1 Imports System 2 Module Main 3 Sub Main() 4 Dim dtToday As Date 5 dtToday = DateTime.Today() 6 End Sub 7 End Module
Diese shared-Elemente können Sie in Ihrem eigenen Code erzeugen, wenn Sie möchten, dass eine bestimmte Eigenschaft oder Methode die ganze Zeit zugänglich ist, ohne dass Objekte erzeugt werden müssen. Im Allgemeinen sollten Sie sie aber meiden, wenn Sie sie nicht unbedingt benötigen, da das Vorhandensein von vielen gemeinsam genutzten Elementen fast das Gleiche ist wie das Erzeugen eines Moduls mit Prozeduren und die Bedeutung Ihrer Objekte verringert. Beachten Sie, dass es sich bei Modulen um einen besonderen Typ von Klassen handelt: Alle Elemente eines Moduls werden standardmäßig als shared angesehen.
246
Zusammenfassung
7.4
Zusammenfassung
Das .NET-Framework ist eine Bibliothek von Klassen, die Sie in Ihren Anwendungen benutzen können. Alle basieren auf elementaren objektorientierten Programmierprinzipien, die die .NET-Sprachen gemeinsam haben. Diese Prinzipien (das Konzept von Klassen, Instanzen, Eigenschaften, Methoden, Vererbung usw.) sind auch die Grundlage für die Erstellung eigener Klassen in Visual Basic .NET. Für die Erstellung fast jeder Anwendung müssen Sie das .NET-Framework nutzen und im Allgemeinen auch eigene Klassen erzeugen. Die Verwendung von Objekten ist folglich eine wichtige Fähigkeit für jeden .NET-Programmierer. In der heutigen Lektion haben wir durch das Erstellen unserer eigenen Klasse elementare objektorientierte Konzepte erlernt und gesehen, wie diese Konzepte in eigenen Code implementiert werden. In der nächsten Lektion am Tag 8 werden wir die Klassen einführen, die im .NET-Framework verfügbar sind, und darstellen, wie Sie mit diesen Klassen große .NET Anwendungen schreiben können.
7.5 F
Ich habe noch nie mit einer objektorientierten Programmiersprache gearbeitet. Kann ich nicht einfach bei Prozeduren und Modulen bleiben? A
F
Fragen und Antworten
Sie müssen in Visual Basic nicht unbedingt Objekte erstellen, aber ganz ohne Objekte wie z.B. das .NET-Framework geht es nicht. Aus diesem Grund muss ein Entwickler, der noch keine Objekte kennt, als Erstes mit der Verwendung von Objekten vertraut werden, ehe er damit fortfährt, selbst welche zu erstellen.
Ich habe gelesen, dass man in Visual Basic .NET nicht genau sagen kann, wann ein Objekt zerstört wird, und dass man dadurch in vielerlei Hinsicht eingeschränkt wird. Was ist damit gemeint und stimmt das? A
Wenn man mit Objekten arbeitet, erfolgt ihre Zerstörung nicht immer an einem festgelegten Punkt im Code, sodass es schwierig ist, ein bestimmtes Codestück zu haben, das immer dann läuft, wenn ein Objekt abgeschlossen wird. In einigen Sprachen, darunter auch frühere Versionen von Visual Basic, stand ein Mechanismus zur Verfügung, um Code zu einer »Abschluss-Ereignis«-Prozedur hinzuzufügen. Der Code in einer solchen Prozedur wurde garantiert immer dann ausgeführt, wenn ein Objekt zerstört wurde. In Visual Basic .NET zerstört das Teilsystem der Speicherbereinigung die Objekte, wenn es notwendig ist, aber es ist nicht möglich, Code zu erstellen, der zum Zeitpunkt der Zerstörung ausgeführt wird. Im Allgemeinen sollte Ihnen diese »Einschränkung« nicht besonders viel Ärger bereiten, aber manchmal (besonders, wenn Sie alten Visual Basic-Code mig-
247
Mit Objekten arbeiten
rieren), wird erwartet, dass am Ende des Lebenszyklus eines Objekts irgendein Code läuft, und das lässt sich nicht einfach in eine Visual Basic .NET-Implementierung übertragen.
7.6
Workshop
Der Workshop soll Ihnen dabei helfen, mögliche Fragen vorherzusehen und das Gelernte zu wiederholen. Außerdem soll er Sie dazu anregen, über die praktische Umsetzung Ihres Wissens nachzudenken. Die Antworten und Lösungen finden Sie im Anhang A.
Quiz 1. Wenn eine Klasse eine Methode enthält, die mit MustOverride gekennzeichnet ist: Was muss dann außerdem noch zutreffen? 2. Welches ist das Schlüsselwort, das Sie in einem Konstruktor verwenden können, um den Konstruktor der Basisklasse aufzurufen? 3. Was stimmt nicht bei den überladenen Konstruktoren in diesem Code (siehe Listing 7.19)?
Listing 7.19: Verschiedene Konstruktorversionen, die für die Klasse Vehicle zur Verfügung stehen. 1 Public Class Vehicle 2 Dim m_dtManufactured As Date 3 Dim m_lngNumberOfPassengers As Long 4 Dim m_sColor As String 5 6 Public Sub New() 7 m_dtManufactured = System.DateTime.Now() 8 End Sub 9 10 Public Sub New(ByVal sColor As String) 11 m_dtManufactured = System.DateTime.Now() 12 m_sColor = sColor 13 End Sub 14
248
Workshop
15 16 17 18 19 20 21 22 23 24 End
Public Sub New(ByVal sName As String) End Sub Public Sub New(ByVal dtManufactured As Date, _ ByVal sColor As String) m_dtManufactured = dtManufactured m_sColor = sColor End Sub Class
Übung Erzeugen Sie mit Klassen, Eigenschaften, Methoden und der Vererbung eine Beispielklassenhierarchie mit einigen Beispielklassen, die mit Animal beginnt und dann einige Ebenen hinuntergeht.
249
Rückblick Nachdem Sie nun die Lektionen der ersten Woche durchgearbeitet haben, sollten Sie das Programmieren, Visual Basic und .NET gut verstehen und wissen, wie diese Komponenten zusammenpassen (Tag 1 und Tag 2). Sie haben einige Beispielprogramme in .NET erstellt und diese sowohl mit der IDE als auch mit der Befehlszeile kompiliert. Mit dieser Erfahrung sind Sie nun in der Lage, alle Beispiele in diesem Buch auszuprobieren, und können sogar selbständig weiterexperimentieren. Die Lektion über Datentypen und einfache Programmiertechniken, mit denen Visual Basic .NET-Anwendungen erstellt werden, lieferten eine Einführung in die einfache Programmierung (Tag 3). Am Tag 4 haben Sie Bedingungen (einschließlich der If-Anweisungen) und Schleifen kennen gelernt, also zwei Dinge, die in eigentlich jedem Programm vorkommen. Da Sie mit Visual Basic .NET so viele verschiedene Arten von Programmen schreiben können, die eine große Vielfalt von Projekten und Quelldateien verwenden, hat Tag 5 die verschiedenen Architekturmöglichkeiten behandelt, die Ihnen bei den verschiedenen Systemtypen zur Verfügung stehen. Auch beim besten Programmierer schleichen sich Programmfehler ein. Unabhängig davon, ob der Fehler vermeidbar war, können Sie das Problem mit den am Tag 6 besprochenen Verfahren der Fehlerbehandlung elegant lösen. Da ein Großteil des .NET-Frameworks auf Objekten beruht, haben wir uns am Tag 7 auf die Begriffe und Konzepte konzentriert, die Sie kennen müssen, wenn Sie in Ihren eigenen Programmen mit Objekten arbeiten möchten. Alle diese Informationen sowie die praktischen Codebeispiele sollten Sie nun in die Lage versetzen, das erste Bonusprojekt zu lösen (»Spiel des Lebens/Game of Life«). Dieses Bonusprojekt finden Sie auf der Website zu diesem Buch und auch auf der CD im Ordner \Bonus. Es führt Sie nicht nur Schritt für Schritt durch die Erstellung eines vollständigen und funktionierenden Programms, sondern veranschaulicht auch die Verwendung der Bedingungen, Schleifen, Arrays, Variablen und sogar der Verfahren der Fehlerbehandlung, die wir in den bisherigen Lektionen besprochen haben. Lesen Sie das Material, erstellen Sie das Projekt und experimentieren Sie mit dem Ergebnis. Versuchen Sie dann, den Code so zu verändern, dass das Programm anders funktioniert; erstellen Sie also Ihre eigene Variante des Programms.
251
Tag 1
Willkommen bei Visual Basic .NET
29
T ag 2
Mit Visual Basic .NET arbeiten
59
T ag 3
Einführung in die Programmierung mit Visual Basic .NET
95
Tag 4
Den Programmfluss steuern
127
Tag 5
Anwendungsarchitektur in .NET
165
Tag 6
Was zu tun ist, wenn ein gutes Programm nicht funktioniert, und wie Sie das verhindern
187
Tag 7
Mit Objekten arbeiten
221
Tag 8
Einführung in das .NET-Framework
255
Tag 9
Mit Windows Forms eine Benutzeroberfläche erstellen
279
Tag 10
Mit Web Forms eine Benutzeroberfläche erstellen 319
Tag 11
Einführung in Datenbanken
345
Tag 12
Mit .NET auf Daten zugreifen
375
Tag 13
Der Server-Explorer
423
Tag 14
Einführung in die objektorientierte Programmierung
451
Tag 15
Objekte in Visual Basic .NET erzeugen
479
Tag 16
Windows Forms für Fortgeschrittene
513
Tag 17
Mit dem .NET Framework arbeiten
549
Tag 18
Der letzte Schliff
595
Tag 19
Die Anwendung bereitstellen
619
Tag 20
Einführung in XML
639
Tag 21
Mit Visual Basic .NET Webdienste erstellen
665
W O C H E
W O C H E
W O C H E
Überblick In dieser Woche werden Sie sich eingehend mit der Welt des .NET-Frameworks befassen. Wir beginnen mit einer Lektion über das Framework selbst (Tag 8), wobei wir die Verwendung der Klassen Console, Math, Random und Environment ausführlich besprechen und Ihnen Informationen dazu liefern, welche Möglichkeiten das Framework für Arrays und Listen bietet. Außerdem erklärt diese Lektion, wie Sie die benötigten Funktionsmerkmale im Framework selbst finden – was Sie unbedingt wissen müssen, wenn Sie mit .NET selbst Projekte erstellen. Die nächsten beiden Lektionen, Tag 9 und Tag 10, konzentrieren sich darauf, wie Sie mit Windows Forms (Tag 9) oder Web Forms (Tag 10) eine Benutzeroberfläche für Ihre Anwendungen einrichten. Nur wenige Programme kommen ohne eine Benutzeroberfläche aus, weshalb diese beiden Lektionen überaus wichtig sind. Da fast alle vorhandenen Geschäftsanwendungen irgendeine Datenbank verwenden, widmen wir die folgenden beiden Lektionen ausschließlich diesem Thema. Tag 11 behandelt die grundlegenden Datenbankkonzepte und führt Sie Schritt für Schritt durch die Erstellung einer Datenbank für eine Anwendung, die CDs registriert. Tag 12 zeigt, wie Sie eine Verbindung zu einer Beispieldatenbank herstellen und eine Anwendung einrichten, mit der Sie CDs hinzufügen, bearbeiten oder aus Ihrer Sammlung löschen können. Bei der Arbeit mit Datenbanken lernen Sie den Server Explorer kennen, mit dessen Funktionsmerkmalen wir uns am Tag 13 eingehend befassen werden. In dieser Lektion arbeiten Sie mit Leistungsmonitoren und Ereignisprotokollen und erfahren, wie Sie eine Datenbankverbindung auf einfachere Art herstellen können. Am Tag 14 befassen wir uns schließlich noch mit komplizierteren objektorientierten Eigenschaften von Visual Basic .NET: mit der Vererbung, dem Überschreiben und Überladen. Diese Lektion zeigt, wie mächtig diese Version von Visual Basic wirklich ist und wie Sie diese Funktionen nutzen können, um Systeme einzurichten, die leichter zu warten und zu erweitern sind. Am Ende der zweiten Woche haben Sie so viele Funktionsmerkmale von Visual Basic .NET kennen gelernt, dass Sie komplexe Anwendungen erstellen können. Das zweite Bonusprojekt ermöglicht es Ihnen, einige dieser neuen Verfahren einzuüben.
254
Einführung in das .NET-Framework
Einführung in das .NET-Framework
Das .NET-Framework ist keine neue Funktion speziell von Visual Basic .NET, da es von vielen Programmiersprachen gemeinsam genutzt wird (als dieses Buch geschrieben wurde, waren es 20). Das Framework bietet eine leistungsstarke Gruppe von Komponenten, die Sie in Ihren Programmen verwenden können. Es enthält eine immense Anzahl Klassen, die verschiedene Funktionen durchführen, von der Bearbeitung von Zeichenketten oder Zahlen bis zur Verschlüsselung und zum Netzwerkzugriff. Das Kennenlernen des .NETFrameworks ist ein nie endender Prozess, aber das Wissen um Schlüsselbegriffe und die Art, wie das Framework organisiert ist, sind unschätzbare Fähigkeiten, die Ihnen helfen werden, wenn Sie weitere Anwendungen mit Visual Basic .NET erstellen.
Die Themen heute 쐽
Was ist das .NET-Framework?
쐽
Einige wichtige Klassen im .NET-Framework
쐽
Wie Sie im .NET-Framework finden, wonach Sie suchen
8.1
Was ist das .NET-Framework?
Das .NET-Framework ist der Name, den verschiedene Komponenten und Dienste erhalten haben, die zusammen eine mächtige Entwicklungsumgebung bilden. Es umfasst eine riesige Anzahl Klassen (mehr als 6.000), die den Großteil der Funktionalität bieten, die vorher in Visual Basic oder dem Windows API enthalten waren. Diese erlauben es Ihnen, Windows- und Web-Anwendungen zu schreiben, auf das Netzwerk zuzugreifen, Grafiken zu erzeugen und vieles mehr. Zusätzlich enthält das .NET-Framework die Laufzeitumgebung Common Language Runtime (CLR), die für das Ausführen Ihres Codes verantwortlich ist. Die CLR bietet eine Reihe wichtiger Neuerungen für Entwickler von .NET-Anwendungen in Visual Basic .NET oder in anderen unterstützten Sprachen. Die wichtigste Veränderung ist, dass nun all diese Sprachen in die Microsoft Intermediate Language (MSIL) kompiliert werden. Diese wird dann, wenn sie zum ersten Mal ausgeführt wird, von der CLR in nativen Code umgewandelt. Das Endergebnis ist, dass Sie die Leistung von voll kompiliertem Code bekommen, und nicht von Code, der erst zur Laufzeit interpretiert wird. Außerdem verwenden alle Sprachen, die von der CLR unterstützt werden, dieselben Datentypen. Das bedeutet, dass es für zwei (oder mehr) Sprachen viel einfacher ist, zusammenzuarbeiten. Bisher mussten Sie immer, wenn Sie Informationen von einer Sprache (wie C++) an eine andere (vielleicht Visual Basic) übergeben wollten, irgendwelche Umwandlungen durchführen, um die Variable richtig zu interpretieren. Da beide Spra-
256
Wichtige Klassen im .NET-Framework
chen nun die gleichen Datentypen verwenden werden, ist das trivial. Mit der CLR können die Entwickler die Sprache verwenden, mit der sie sich am wohlsten fühlen, und sind trotzdem in der Lage, mit anderen Sprachen zu kommunizieren. Obwohl die CLR wichtig ist, arbeitet sie im Allgemeinen im Hintergrund. Der wichtige, sichtbare Teil des .NET-Frameworks, mit dem Sie sich beschäftigen sollten, sind einige der Klassen, die das Framework bilden.
8.2
Wichtige Klassen im .NET-Framework
Wenn Sie in Visual Basic .NET Anwendungen erstellen, werden Sie viele Klassen des .NET-Frameworks verwenden, einige allerdings häufiger als andere. Einige der nützlichsten Klassen sind: 쐽
Console: Ermöglicht das Lesen und Schreiben auf der Befehlszeile.
쐽
Environment: Ermöglicht das Lesen und Schreiben von Umgebungsvariablen.
쐽
Random: Ermöglicht die Generierung von Zufallszahlen.
쐽
Math: Umfasst eine Reihe von mathematischen Berechnungen.
쐽
Collections: Bietet eine Anzahl von Klassen für verschiedene Strategien zum Spei-
chern von Elementsammlungen.
Console Bei der Erstellung früherer Anwendungen haben Sie bereits etwas von der Klasse Console gesehen. Die Methoden WriteLine und ReadLine der Klasse Console wurden häufig dafür verwendet, Ausgabe zu schreiben und Eingabe aus den verschiedenen Konsolen- oder Befehlszeilenanwendungen zu lesen, die Sie bisher in diesem Buch erstellt haben. Das ist die wichtigste Verwendung der Klasse Console. Es gibt aber einige weniger häufig genutzte Funktionen von Console, die eine genauere Untersuchung lohnen. Einige wichtige Methoden der Klasse Console sehen Sie in Tabelle 8.1. Methode
Beschreibung
Read
Liest Informationen aus der Befehlszeile (oder eine andere Eingabe). Es ist nicht erforderlich, dass die Zeile mit Enter endet.
ReadLine
Liest Informationen aus der Befehlszeile (oder eine andere Eingabe). Liest alle Zeichen bis ausschließlich Enter.
Tabelle 8.1: Methoden der Klasse Console
257
Einführung in das .NET-Framework
Methode
Beschreibung
SetError
Ändert das Ziel der Anzeige von Fehlermeldungen, während Ihr Programm läuft. Damit können Sie einen einfachen Fehlerprotokollierungsmechanismus für Ihre Anwendung erstellen.
SetIn
Ändert die Eingabequelle für Read und ReadLine. Sie können damit eine Befehlszeilenanwendung so ändern, dass sie von einer Datei oder einem Ort im Netzwerk liest. Siehe auch den Abschnitt »Umleitung« weiter unten in der heutigen Lektion.
SetOut
Ändert das Ziel für die Methoden Write und WriteLine. Damit können Sie das Ziel der Ausgabe für die Protokollierung oder andere Zwecke ändern. Siehe auch den Abschnitt »Umleitung« weiter unten in der heutigen Lektion.
Write
Schreibt Informationen (oder eine andere Ausgabe) in die Befehlszeile. Endet nicht mit einer neuen Zeile.
WriteLine
Schreibt Informationen (oder eine andere Ausgabe) in die Befehlszeile. Beendet die Ausgabe mit einer neuen Zeile.
Tabelle 8.1: Methoden der Klasse Console (Forts.)
Einfachere Ausgabe Da die Klasse Console häufig für die Ausgabe verwendet wird, wäre es schön, wenn sie Ihnen dabei das Leben erleichtern würde. Zum Glück tut sie das. Wenn Sie mit der Methode Write oder WriteLine Informationen anzeigen, können Sie die Fähigkeit dieser Methoden nutzen, für Ihre Variablen Platzhalter zu verwenden. Wenn Sie eine Zeichenkette mit Informationen anzeigen möchten, die in Variablen gespeichert sind, verwenden Sie normalerweise eine Technik namens Verkettung. Verkettung ist ein anderes Wort für »Zeichenketten zusammenfügen«. Statt die Zeichenketten mit dem Symbol + zusammenzufügen, verwenden Sie das Symbol &: Console.WriteLine("Please enter " & ITEM_COUNT & _ "items. Press ENTER between items.")
Wenn Sie eine komplizierte Zeichenkette haben, ist es manchmal schwierig, alle Anführungszeichen und kaufmännischen Und-Zeichen (&) zu überblicken. Statt mit der Verkettung eine komplizierte Zeichenkette zu erstellen, können Sie an den richtigen Stellen Platzhalter verwenden und dann die Variablen einfügen, mit denen diese später gefüllt werden: Console.WriteLine("Please enter {0} ITEM_COUNT)
items. Press ENTER between items.", _
{0} ist ein Platzhalter für die Variable, die Sie nach dem Ende der Zeichenkette einfügen. Das ist in den bisherigen Anweisungen ITEM_COUNT. Sie können in der Zeichenkette
mehrere Platzhalter einfügen (oder sogar den gleichen Platzhalter mehrmals verwenden).
258
Wichtige Klassen im .NET-Framework
Dann werden die Variablen, die Sie hinter der Zeichenkette einfügen, nacheinander in die Zeichenkette gesetzt, beginnend mit dem »nullten« (ersten) Element und fortlaufend durch die Variablen und Platzhalter ({0}, {1}, {2} usw.). Das Listing 8.1 zeigt dies.
Listing 8.1: Die Klasse Console für die Ein- und Ausgabe verwenden 1 Imports System 2 Public Class ConsoleTest 3 Private Const ITEM_COUNT As Integer = 10 4 Shared Sub Main() 5 Dim I As Integer 6 Dim sItems(ITEM_COUNT) As String 7 Console.WriteLine("Please enter {0} items. " & _ 8 Press ENTER between items.", ITEM_COUNT) 8 For I = 0 To ITEM_COUNT-1 9 sItems(I) = Console.ReadLine 10 Next 11 Console.WriteLine() 12 Console.WriteLine("Items in reverse order:") 13 For I = ITEM_COUNT – 1 To 0 Step -1 14 Console.WriteLine(sItems(I)) 15 Next 16 End Sub 17 End Class
Der Code in Listing 8.1 ist recht einfach und soll nur einige der Möglichkeiten zeigen, wie Sie Console bereits seit sieben Tagen verwenden. Die Zeile 1 importiert den Namensraum System. Hier sind die Klasse Console sowie viele andere wichtige Klassen definiert. Daher sollten Sie diesen Namensraum in den meisten Anwendungen importieren. Die Visual Basic .NET-IDE wird das bei Ihren Anwendungen übernehmen, während Sie mit der IDE arbeiten. In der einen Klasse, die in Ihrer Datei definiert ist, deklarieren Sie eine einzelne Konstante namens ITEM_COUNT (Zeile 3) und eine Subroutine namens Main (Zeile 4). Die Konstante wird statt der Zahl 10 gesetzt und bietet die Möglichkeit, den Wert später zu ändern. Es ist viel einfacher, den Wert einer einzelnen Konstanten zu ändern, als nach jedem Vorkommen eines Wertes in der Anwendung zu suchen. Die Verwendung einer Konstanten zeigt auch, dass der Entwickler weiß, dass die Werte miteinander verwandt sind. Wenn Sie die Zahl 10
259
Einführung in das .NET-Framework
überall gesetzt und sich diesen Code sechs Monate später wieder angesehen hätten, würden Sie sich vielleicht nicht mehr daran erinnern, ob alle diese 10er den gleichen Wert darstellen sollten oder nur per Zufall gleich sind. Die Prozedur Shared Sub Main ist, wie Sie bereits häufiger gesehen haben, die erste Methode, die in einer Anwendung ausgeführt wird. Innerhalb der Prozedur deklarieren Sie ein Array von Zeichenketten (Zeile 6) und füllen es mithilfe der Methode Console.ReadLine. Die Zeile 7 muss erwähnt werden. Innerhalb des Elements, das Sie mit Console.WriteLine (oder Console.Write) schreiben, können Sie eine Anzahl von Token einfügen, wie z.B. das Element {0} in der Mitte der Zeichenkette in Zeile 7. Jedes dieser Token wird dann mit Variablen gefüllt. Diese stehen in dem Aufruf an Console.WriteLine hinter der Zeichenkette, wie es auch mit der Konstanten ITEM_COUNT der Fall war. Das Token-Verfahren kann für das Erstellen einer Zeichenkette nützlich sein, aber ob das sinnvoll ist oder nicht, hängt in gewisser Weise vom persönlichen Geschmack ab. Bei dieser Technik ersetzt jeder Wert vom nullten Token an jeweils ein Token in der Reihenfolge, in der die Token erscheinen. Die Variablen können Zahlen, Zeichenketten oder jeder andere Typ sein, der in eine Zeichenkette umgewandelt werden kann. Wenn Sie diese Technik nutzen, um z.B. eine Zeichenkette mit vier Variablen zu schreiben, sieht das so aus: Console.WriteLine("{0} + {1} = {2} : {3}", 1, 1, 2, _ "Bertrand Russell, 1912")
Erfahrene Visual Basic-Programmierer kennen vielleicht ein anderes Verfahren besser: Bei diesem Verfahren erstellen Sie die Zeichenkette, indem Sie die Zeichenketten und Variablen verketten. Die obige Zeichenkette würde mit diesem Code erstellt und angezeigt: Console.WriteLine(value1 & "+" & value2 & "=" & result & ": " & sSource)
Das mag zwar »natürlicher« erscheinen, aber Sie vergessen vielleicht ein Verkettungszeichen (&), wenn Sie Zeichenketten hinzufügen. Ihr Programm würde dann bei der Kompilierung oder bei der Ausführung fehlschlagen. Jede Zeile der Eingabe wird eingelesen (Zeile 9) und in eines der Array-Elemente geschrieben. Beachten Sie, dass diese von 0 bis 9 durchnummeriert sind (Zeile 8). Schließlich werden die Elemente in umgekehrter Reihenfolge auf dem Bildschirm ausgegeben. Dies erfolgt mit der Step-Klausel der For...Next-Anweisung (Zeile 13). Jedes wird reihum an die aktuelle Ausgabe geschrieben, wie Sie in Zeile 14 gesehen haben. Das Listing 8.2 zeigt diese Art der Ausgabe.
260
Wichtige Klassen im .NET-Framework
Listing 8.2: Die Console-Anwendung ausführen. 1 2 3 4 5 6 7 8 9 10 11 12
[c:\ work\ console]Console1.exe Please enter 10 items. Press ENTER between items. Aardvark Bandicoot Cassowary Dugong Echidna Finch Giraffe Hippopotamus Iguana Jackalope
13 14 15 16 17 18 19 20 21 22 23
Items in reverse order: Jackalope Iguana Hippopotamus Giraffe Finch Echidna Dugong Cassowary Bandicoot Aardvark
Umleitung Umleitung (Redirection) bedeutet einfach, »etwas an einen anderen Ort schicken«. Bei der Klasse Console bedeutet das, dass Sie den Ort ändern können, von dem Sie lesen oder an den Sie schreiben. Sie können damit z.B. eine Anwendung schreiben, die entweder von der Befehlszeile oder von einer Datei eine Eingabe entgegennimmt. Dementsprechend könnten Sie Console.WriteLine nutzen, wenn Sie die Anwendung beim Testen debuggen, und sie dann in eine Datei umleiten, um ein einfaches Protokollierungswerkzeug für Ihre Anwendung zu erzeugen. Sie können die Eingabe, Ausgabe oder Fehlerinformationen Ihres Programms umleiten. Diese Informationen können an jedes Ziel umgeleitet werden, für das es einen TextReader (für die Eingabe) oder TextWriter (für Ausgabe und Fehler) gibt. Beachten Sie, dass Sie
261
Einführung in das .NET-Framework
keine dieser beiden Klassen direkt instanziieren können, da sie abstrakt sind. Statt dessen müssen Sie eine der Klassen instanziieren, die TextReader oder TextWriter implementieren oder von ihnen erben (nähere Informationen zur Abstraktion und Vererbung finden Sie unter Tag 7, Mit Objekten arbeiten, und Tag 14, Einführung in die objektorientierte Programmierung). Mit einigen Implementierungen dieser beiden Klassen können Sie aus Dateien, Netzwerken und anderen Medien lesen und darin schreiben. Listing 8.3 zeigt, wie Sie das Beispiel aus Listing 8.2 verändern können, um statt in das Befehlszeilenfenster in eine Datei zu schreiben. Beachten Sie, dass nicht die Befehle an Console.WriteLine geändert wurden, sondern nur das Ziel.
Listing 8.3: Die Console-Ausgabe umleiten. 1 2
Imports System Imports System.IO
3 4
Public Class ConsoleTest Private Const ITEM_COUNT As Integer = 10
5 6 7 8 9 10
Shared Sub Main() Dim I As Integer Dim sItems(ITEM_COUNT) As String Dim oFile As TextWriter = File.CreateText("Output.txt") Dim oOut As TextWriter = Console.Out
11 12 13
Console.WriteLine("Please enter { 0} items. Press ENTER between items.", _ITEM_COUNT) For I = 0 To ITEM_COUNT-1 sItems(I) = Console.ReadLine Next
14 15 16 17 18 19
Console.WriteLine() Console.SetOut(oFile) Console.WriteLine("Items in reverse order:") For I = ITEM_COUNT – 1 To 0 Step -1 Console.WriteLine(sItems(I)) Next
20
oFile.Close()
21 22
Console.SetOut(oOut) Console.WriteLine("Done")
262
Wichtige Klassen im .NET-Framework
23
Console.ReadLine()
24 End Sub 25 End Class
Die Ausführung der Anwendung führt zu dieser Ausgabe: 1 2 3 4 5 6 7 8 9 10 11 12 13 14
[c:\ work\ console]Console2.exe Please enter 10 items. Press ENTER between items. Aardvark Bandicoot Cassowary Dugong Echidna Finch Giraffe Hippopotamus Iguana Jackalope Done
Die einzigen Veränderungen zwischen Listing 8.1 und Listing 8.3 sind die Ergänzungen der Zeilen 2, 8, 9, 15, 20, 21 und 22. Hierbei handelt es sich um die Zeilen, die die Datei erzeugen, die Umleitung der Ausgabe verursachen und die Umleitung beenden. Die Zeile 2 importiert den Namensraum System.IO. Dieser wird benötigt, da er die Definition für die Klassen TextWriter und File enthält, die Sie später verwenden. Die Zeile 8 erzeugt mit der Methode CreateText der Klasse File die Datei Output.txt. Beachten Sie, dass Sie keine Instanz der Klasse File erzeugen mussten, da die Methode CreateText eine Shared-Methode ist. Wenn es eine bestehende Datei mit dem gleichen Namen gibt, wird sie gelöscht. Wenn Sie lieber auf eine bestehende Datei aufbauen möchten, verwenden Sie statt dessen AppendText. Mit der Zeile 9 wird das ursprüngliche Ziel der Ausgabe gespeichert, sodass Sie es in der Zeile 21 zurücksetzen können. Die Zeile 15 verursacht die Umleitung in Ihre Datei. Nach dieser Umleitung wird die gesamte Ausgabe von Console.Write und Console.WriteLine an Ihre Datei statt an das Befehlszeilenfenster gesendet. Die letzte wichtige Zeile ist die Zeile 20. Wenn Sie die Datei nicht schließen, kann ihr Inhalt nicht geschrieben werden und Sie haben am Ende eine Datei mit 0 Byte (eine leere Datei.)
263
Einführung in das .NET-Framework
Der Code im Listing 8.3 sollte die Datei Output.txt mit dem folgenden Inhalt erzeugen: 1 2 3 4 5 6 7 8 9 10 11
Items in reverse order: Jackalope Iguana Hippopotamus Giraffe Finch Echidna Dugong Cassowary Bandicoot Aardvark
Umgebung Zu wissen, wo Sie stehen, entscheidet häufig über Erfolg oder Misserfolg. Und dieses Wissen kommt häufig aus Ihrem Umfeld oder Ihrer Umgebung. Ähnlich befinden Sie sich bei der Programmierung in einer bestimmten Umgebung. Die Umgebung, in der Ihre Programme laufen, besteht aus Informationen über das Betriebssystem und aus einer Vielzahl von Variablen und anderen Einstellungen, die sich auf Ihren Computer auswirken. In dieser Umgebung können Sie für den Anwender Einstellungen abfragen, z.B. den Speicherort seines Verzeichnisses für temporäre Dateien, den Inhalt seines Suchpfads oder sogar die anderen Elemente auf der Befehlszeile. Einige der wichtigen Eigenschaften und Methoden der Klasse Environment sind in der Tabelle 8.2 aufgelistet. Beachten Sie, dass all diese Eigenschaften und Methoden Shared sind. Element
Beschreibung
Eigenschaften CommandLine
Stellt die vollständige Befehlszeile dar, mit der die Anwendung begonnen wurde.
CurrentDirectory
Gibt den Pfad des aktuellen Verzeichnisses zurück.
OSVersion
Gibt Informationen über das aktuelle Betriebssystem zurück, z.B. ob es sich um Windows 9x, Windows NT oder Windows 2000 handelt.
SystemDirectory
Gibt den Pfad des Systemverzeichnisses zurück (\winnt\system32 unter Windows NT oder 2000).
Version
Gibt die Versionsinformationen eines Assemblers zurück.
Tabelle 8.2: Methoden und Eigenschaften der Klasse Environment
264
Wichtige Klassen im .NET-Framework
Element
Beschreibung
Methoden Exit
Beendet eine Anwendung und gibt optional Fehlercode zurück, der vom Betriebssystem verwendet werden kann.
GetCommandLineArgs
Gibt alle Elemente zurück, die in der Befehlszeile aufgelistet waren, als die Anwendung gestartet wurde. Sie werden in Form eines Zeichenketten-Arrays zurückgegeben. Die ausführbare Datei selbst ist das nullte Element im Array.
GetEnvironmentVariable
Gibt den Wert einer angefragten Umgebungsvariablen zurück. Es werden Informationen zurückgegeben, die in der Umgebungsvariablen mit dem Befehl Set gespeichert sind, wie z.B. der Suchpfad (path), das Verzeichnis für temporäre Dateien (temp ) oder andere Einstellungen.
GetLogicalDrives
Gibt die Liste verfügbarer Laufwerke zurück. Sie wird in Form eines Zeichenketten-Arrays zurückgegeben. Das erste (nullte) Element ist normalerweise A:\.
Tabelle 8.2: Methoden und Eigenschaften der Klasse Environment (Forts.)
Die Klasse Environment ist nützlich, wenn Sie Informationen über das System benötigen, das um Ihr Programm herum läuft, z.B. wenn Sie wissen müssen, wohin Sie Ihre temporären Dateien schreiben sollen oder unter welchem Betriebssystem Sie arbeiten.
Random Random ist eine einfache Klasse, die Zufallszahlen erzeugen soll, und zwar entweder mit dem Typ Integer oder Double. Die wichtigen Methoden dieser Klasse sind in der Tabelle
8.3 aufgelistet. Methode
Beschreibung
Next
Gibt eine Integer zwischen 0 und dem höchsten möglichen Wert für einen Integer zurück (ungefähr 2 Milliarden).
Next(MaxValue)
Gibt einen Integer zwischen 0 um dem Wert MaxValue (eine ganze Zahl) zurück.
Next(MinValue, MaxValue)
Gibt einen Integer zwischen den Minimum- und Maximumwerten zurück. Es handelt sich hierbei um die am häufigsten verwendete Variante, wenn Sie 1 als Minimalwert nehmen.
NextDouble
Gibt einen Double zwischen 0 und 1 zurück.
Tabelle 8.3: Methoden der Klasse Random
265
Einführung in das .NET-Framework
Wenn Sie z.B. eine ganzzahlige Zufallszahl zwischen 1 und 100 (einschließlich) generieren möchten, schreiben Sie: Dim oRand As New Random Dim iValue As Integer = oRand.Next(1, 100)
Math Die Klasse Math enthält viele der wichtigen mathematischen Konstanten und Funktionen. Da es sich bei den meisten um gemeinsam genutzte Methoden handelt, können sie ohne die Erzeugung einer Instanz der Klasse Math verwendet werden. Die wichtigsten Methoden werden in Tabelle 8.4 kurz aufgeführt. Wenn Sie sich die Klasse Math in der Hilfe anschauen, werden Sie feststellen, dass noch viel mehr Methoden zur Verfügung stehen. Methode
Beschreibung
Abs
Gibt den absoluten Wert einer Zahl zurück (wenn sie negativ ist, gibt die Methode den positiven Wert der Zahl zurück).
Cos
Gibt den Kosinus eines Winkels zurück (gemessen in Rad, siehe nächster Hinweis).
E
Gibt einen Double zurück, der den Wert von e darstellt (2.7182818284590451).
Max
Gibt das Maximum von zwei Werten zurück.
Min
Gibt das Minimum von zwei Werten zurück.
PI
Gibt einen Double zurück, der den Wert von Pi darstellt (3.1415926535897931).
Round
Rundet eine Zahl auf die nächste ganze Zahl.
Tabelle 8.4: Methoden der Klasse Math
Für diejenigen, die während der Geometriestunden geschlafen haben: Ein Rad ist ungefähr 57,296 Grad. Warum eine solche Zahl? Der Radiant hängt mit dem Umfang eines Kreises zusammen, das genaue Maß ist: # Rad = (# Grad * Pi)/180
Der Grund, warum Rad bei Computern verwendet wird, hat mit Analysis zu tun und liegt daher außerhalb der Themen dieses Buches. Denken Sie einfach daran, Ihre Winkel von Grad in Rad umzuwandeln, ehe Sie irgendeine der Math-Methoden verwenden, die mit Winkeln zu tun haben. Die Klasse Math ist im Allgemeinen nützlich für die Berechnung von Werten. Auch wenn Sie sich nicht mehr daran erinnern (oder nie gelernt haben), was eine hyperbolische Tangente ist, finden Sie sie in der Klasse Math.
266
Wichtige Klassen im .NET-Framework
Sammlungsklassen im .NET-Framework Sie haben den Datentyp Array bereits an Tag 3, Einführung in die Programmierung mit Visual Basic .NET, kennen gelernt. Das .NET-Framework umfasst eine Reihe von anderen Sammlungsklassen, die Array um weitere Fähigkeiten erweitern. Diese Klassen erlauben es Ihnen, eine Liste von Informationen zu speichern, wie es die Arrays tun, und verfügen über zusätzliche Funktionen, z.B. für das Sortieren von Listen und vereinfachtes Hinzufügen oder Abrufen von Listenelementen. Es steht eine Reihe von Sammlungsklassen (die meisten im Namensraum System.Collections) zur Verfügung. Einige davon werden weiter unten in der heutigen Lektion beschrieben. Wenn Sie andere Arten von Sammlungen benötigen, schauen Sie in diesem Namensraum nach, bevor Sie an einer anderen Stelle suchen.
ArrayList ArrayList ist die Sammlung, die dem Array am nächsten ist. Der Hauptunterschied liegt darin, dass die ArrayList ein einfaches Wachstum zulassen soll, wenn Sie mehr Elemente hinzufügen. Im Gegensatz zu den anderen Klassen im Namensraum System.Collections soll ArrayList eine Sammlung von Object-Variablen speichern. Daher können Sie damit jeden Datentyp speichern. ArrayList ist eine gute Wahl, wenn Sie eine sehr dynamische
Sammlung haben, die im Verlauf der Zeit wachsen oder schrumpfen kann, und wenn Sie die Funktionen der anderen Sammlungen nicht benötigen. Sie können mit einem der verfügbaren Konstruktoren eine neue Instanz von ArrayList erzeugen. Eine Version erlaubt es Ihnen, die anfängliche Kapazität der Sammlung zu definieren und eine weitere setzt die Anfangsgröße auf 16: Dim arrList As New ArrayList ' Erzeuge eine neue ArrayList mit anfänglich 16 Elementen. Dim arrList2 As New ArrayList(20) ' Erzeuge eine neue ArrayList mit anfänglich 20 Elementen. Dim arrList3 As ArrayList Set arrList3 = New ArrayList(52) ' Erzeuge eine neue ArrayList mit anfänglich 52 Elementen.
Die anderen wichtigen Eigenschaften und Methoden von ArrayList werden verwendet, um Elemente zur Sammlung hinzuzufügen, abzurufen oder zu löschen. Die Tabelle 8.5 enthält einige wichtige Elemente.
267
Einführung in das .NET-Framework
Element
Beschreibung
Eigenschaften Capacity
Die aktuelle Größe der ArrayList. Wird anfänglich gesetzt, wenn die ArrayList erzeugt wird (Standard ist 16), kann aber wachsen, wenn mehr Elemente hinzugefügt werden.
Count
Die tatsächliche Anzahl der Elemente in der ArrayList.
Item
Gibt ein bestimmtes Element der ArrayList zurück.
Methoden Add
Fügt ein neues Element zur ArrayList hinzu. Wenn dadurch Count über Capacity steigt, wird Capacity erhöht (um die Größe der anfänglichen Capacity – standardmäßig 16).
Clear
Entfernt alle Elemente aus der ArrayList. Count wird auf 0 gesetzt, aber Capacity verändert sich nicht.
IndexOf
Gibt die Position eines gegebenen Objekts in der ArrayList zurück. Das ist nützlich, nachdem Sie einen Sort durchgeführt haben.
Insert
Fügt an einer angegebenen Position ein neues Element in die ArrayList ein.
Remove
Entfernt das angegebene Objekt aus der ArrayList.
RemoveAt
Entfernt das Element an der angegebenen Position.
Sort
Sortiert die Elemente der ArrayList.
ToArray
Kopiert die gesamte oder einen Teil einer ArrayList in ein Array.
TrimToSize
Schrumpft die ArrayList, sodass Count und Capacity beide mit der aktuellen Anzahl von Elementen in der ArrayList identisch sind.
Tabelle 8.5: Methoden und Eigenschaften der Klasse ArrayList
Die ArrayList ist dann am besten als Ersatz für das Array geeignet, wenn Sie zwar wissen, dass sich die Größe verändern wird, aber nicht, wie sehr sie sich ändern wird. Durch das Open-End-Wachstum können Sie nach Belieben Elemente hinzufügen.
Queue und Stack Die Sammlungen Queue und Stack sind sich ähnlich. Beide sind »klassische« Sammlungstypen und bei vielen Programmieraufgaben nützlich. Mit beiden Sammlungen können Sie ganz einfach neue Elemente hinzufügen. Beide entfernen im Allgemeinen das Element aus der Sammlung, wenn Sie es betrachten, da sie die Sammlung temporär speichern sollen. Sie unterscheiden sich darin, wie Elemente zu jeder Sammlung hinzugefügt werden und – was noch wichtiger ist – wie Elemente aus den Sammlungen entfernt werden.
268
Wichtige Klassen im .NET-Framework
Eine Queue ist eine »First In, First Out«(FIFO)-Sammlung. Das bedeutet, dass Elemente aus der Schlange in der gleichen Reihenfolge entfernt werden, wie sie hinzugefügt wurden. Das ist bei den meisten Schlangen so, denn egal, ob es sich um ein Geschäft, ein Restaurant oder einen Grenzübergang handelt: Wer zuerst kommt, mahlt zuerst. Schlangen werden häufig in der Programmierung verwendet, wenn Sie dieses Verhalten erzielen möchten. Wenn z.B. zwei Programme (oder Objekte) kommunizieren, werden die Nachrichten für das empfangende Objekt in eine Schlange gesetzt. Das Objekt kann die Nachrichten dann in der Reihenfolge verarbeiten, in der es sie empfangen hat. Die Tabelle 8.6 beschreibt einige der wichtigen Methoden und Eigenschaften der Queue. Element
Beschreibung
Eigenschaften Count
Die Anzahl der Elemente in der Schlange.
Methoden Clear
Entfernt alle Elemente aus der Schlange.
Dequeue
Holt ein Objekt aus der Schlange und gibt es zurück.
Enqueue
Setzt ein neues Objekt in die Schlange.
Peek
Mit dieser Methode können Sie das nächste Element in der Schlange ansehen, ohne es aus der Schlange zu entfernen. Das ist nützlich, da das Entfernen eines Elements aus der Schlange dazu führen kann, dass eine andere Methode mit der Schlange nicht mehr richtig umgeht.
Tabelle 8.6: Methoden und Eigenschaften der Klasse Queue
Warum werden diese Methoden Dequeue und Enqueue genannt? Warum nicht Add und Remove? Das liegt einfach daran, dass traditionelle Namen beibehalten werden. Programmierer haben den Akt des Hinzufügens eines Elements zu einer Schlange immer als Enqueuing bezeichnet und daher blieb dieser Begriff ebenso wie dequeue hängen. Ein Stack ist eine »First In, Last Out«(FILO)-Sammlung. Das bedeutet, dass Elemente in der umgekehrten Reihenfolge aus dem Stack entfernt werden, in der sie hinzugefügt wurden. Die FILO-Reihenfolge gleicht einem Tellerstapel: Der zuletzt hinzugefügte Teller wird immer zuerst wieder vom Stapel genommen. Stacks sind die klassische Lösung, um mit Problemen umzugehen, für die Sie die Reihenfolge der Operationen umkehren müssen. Viele Berechnungen werden intern mithilfe von Stacks verarbeitet. Die Tabelle 8.7 beschreibt einige der wichtigen Methoden und Eigenschaften des Stack.
269
Einführung in das .NET-Framework
Element
Beschreibung
Eigenschaften Count
Die Anzahl der Elemente im Stack.
Methoden Clear
Entfernt alle Elemente aus dem Stack.
Pop
Entfernt das nächste Element aus dem Stack und gibt es zurück. Analog zu den Schlangenmethoden Enqueue und Dequeue hat ein Stack die Methoden Push und Pop.
Push
Setzt ein neues Objekt in den Stack.
Peek
Mit dieser Methode können Sie das nächste Element im Stack betrachten, ohne es zu entfernen. Queue und Stack sind vom historischen Standpunkt aus teilweise interessante Sammlungen, auch weil sie bestimmte Programmierprobleme lösen. Wenn Sie eine Sammlung mit dem Verhalten dieser beiden Objekte benötigen, sollten Sie sich an sie erinnern.
Tabelle 8.7: Methoden und Eigenschaften der Klasse Stack
SortedList Die Klasse SortedList verfügt über Charakteristika von ArrayList und NameValueCollection sowie über einige zusätzliche und nützliche eigene Fähigkeiten. Ähnlich wie die ArrayList kann die SortedList wachsen. Sie hat die gleichen Eigenschaften Count und Capacity wie die ArrayList. Wie bei der NameValueCollection wird jedes Element durch einen Namen identifiziert. Zusätzlich sind die Werte der SortedList nach den Namen der Elemente sortiert. Daher ist SortedList immer dann nützlich, wenn Sie eine »wachstumsfähige« Liste organisierter Elemente benötigen, z.B. für Konferenzteilnehmer oder für Testergebnisse von Schülern oder Studenten. Die Tabelle 8.8 beschreibt einige wichtige Methoden und Eigenschaften der SortedList. Element
Typ
Beschreibung
Eigenschaften Capacity
Die aktuelle Größe der SortedList. Wird anfangs gesetzt, wenn die SortedList erzeugt wird (standardmäßig 16), kann aber wachsen, wenn mehr Elemente hinzugefügt werden.
Count
Die tatsächliche Anzahl der Elemente in der SortedList.
Tabelle 8.8: Methoden und Eigenschaften der Klasse SortedList
270
Wie Sie im .NET-Framework finden, wonach Sie suchen
Element
Typ
Beschreibung
Methoden Add
Fügt ein neues Element zur SortedList hinzu. Wenn dadurch der Count die Capacity überschreitet, wird die Capacity um den Wert der anfänglichen Capacity erhöht – standardmäßig 16.
Clear
Entfernt alle Elemente aus der SortedList. Count wird auf 0 gesetzt, aber Capacity wird nicht verändert.
IndexOfKey
Gibt die Position für ein gegebenes Objekt in der SortedList zurück. Das ist sehr nützlich, nachdem Sie einen Sort durchgeführt haben.
Item
Eigenschaft
Gibt ein bestimmtes Element aus der SortedList zurück.
Keys
Eigenschaft
Gibt alle Keys zurück, die in der SortedList gespeichert sind.
Remove
Methode
Entfernt das angegebene Objekt aus der SortedList.
RemoveAt
Methode
Entfernt das Element aus der angegebenen Position.
TrimToSize
Methode
Schrumpft die SortedList, sodass Count und Capacity beide gleich der aktuellen Anzahl von Elementen in der SortedList sind.
Values
Eigenschaft
Gibt alle Werte, die in der SortedList gespeichert sind, in der Reihenfolge ihrer Schlüssel zurück.
Tabelle 8.8: Methoden und Eigenschaften der Klasse SortedList (Forts.)
Wenn Sie mit Listen arbeiten, müssen Sie deren Elemente häufig in einer bestimmten Reihenfolge bewahren, da Sie sie dem Anwender in sortierter Reihenfolge anzeigen müssen. In diesen Situationen kann die SortedList nützlich sein.
8.3
Wie Sie im .NET-Framework finden, wonach Sie suchen
Bei mehr als 6.000 Klassen im .NET-Framework kann das Auffinden der richtigen Klasse manchmal zum schwierigsten Teil der Verwendung des Frameworks werden. Aber ebenso wie beim Suchen eines Buches in Ihrer Lieblingsbuchhandlung oder einer Site im Internet, helfen Ihnen auch hier ein paar Regeln dabei, genau die Klasse zu finden, die Sie benötigen, um Ihre Anwendung fertigzustellen. Dieser Abschnitt hilft Ihnen beim Auffinden von Klassen im Framework und zeigt, wie ich selbst dabei vorgehe. Ich hoffe, dass Ihnen das bei Ihrer eigenen Jagd helfen wird.
271
Einführung in das .NET-Framework
Die Regeln der Jagd Die meisten Bibliotheken sind organisiert. Sie verwenden eventuell das Dewey-Dezimalsystem, ein anderes Katalogsystem oder einfach eine alphabetische Organisation (nach Titel oder nach dem Nachnamen des Autors). Versuchen Sie sich vorzustellen, Sie müssten ein Buch in einer Bibliothek finden, die nicht organisiert ist. Sie müssten willkürlich durch die Regale streifen, bis Sie schließlich zufällig auf das gesuchte Buch stoßen. Viele Programmierer versuchen, Klassen im .NET-Framework auf die gleiche Art zu finden. Gelegentlich mögen sie Glück haben, aber viel häufiger werden sie frustriert sein. Das .NET-Framework ist hierarchisch organisiert. Der Name jedes Namensraums wird aus mehreren Teilen gebildet und jeder Teil wird von den anderen durch einen Punkt abgetrennt. Niedrigere Namensräume (mit mehreren Bestandteilen) sind nicht in höheren Namensräumen enthalten, aber damit verwandt. Zum Beispiel ist der Namensraum System.Data.SqlClient mit System.Data verwandt, aber nicht darin enthalten, weil System.Data.SqlClient auf einer niedrigeren Ebene als der Namensraum System.Data existiert. Im .NET-Framework gibt es zwei Namen der obersten Ebene: System und Microsoft. Die System-Namensräume sind diejenigen, die Teil des .NET-Frameworks sind und den Benutzern von Visual Basic .NET ebenso wie den Benutzern der anderen Sprachen zur Verfügung stehen, die das Framework einsetzen. Die Microsoft-Klassen sind im Allgemeinen spezifisch für Visual Studio und auf eine oder mehrere Umgebungen ausgerichtet. Es gibt z.B. den Namensraum Microsoft.VisualBasic, der viele der Funktionen enthält, die in Visual Basic vor dieser Version existierten.
Die Suche nach der richtigen Klasse Um einige der Techniken vorzuführen, mit denen Sie Funktionalität innerhalb des .NETFrameworks finden können, werde ich Ihnen die Suche nach drei Prozeduren zeigen. Ich werde versuchen, Ihnen einige der Fehler zu demonstrieren, die ich machte, als ich nach der betreffenden Funktionalität suchte, sowie das Ergebnis, das ich am Ende hatte. Es mag andere Methoden geben, um Funktionalität ausfindig zu machen, oder andere Antworten innerhalb des .NET-Frameworks, aber dies sind meine persönlichen Antworten.
Welche Farbe hat der Pinsel im Fenster? – Farben benennen Diese Suche betrifft das häufig auftretende Bedürfnis, eine Farbe zu beschreiben. Steuerelemente können farbig sein und die Farbe kann sich zur Laufzeit ändern. Das Ändern der Farbe zur Entwurfszeit ist einfach: Wählen Sie lediglich die Farbe aus der entsprechenden Dropdown-Liste aus (z.B. BackColor oder ForeColor). Um eine Farbe zur Laufzeit zu ändern, müssen Sie aber wissen, wie eine Farbe identifiziert wird.
272
Wie Sie im .NET-Framework finden, wonach Sie suchen
Jede Farbe in Visual Basic .NET kann auf der Basis ihres Rot-, Grün- und Blauanteils beschrieben werden. In Visual Basic .NET variiert der Wert für jede der drei Farben zwischen 0 und 255, was zu insgesamt 16.581.375 verfügbaren Farben führt. Daher können Sie jede Farbe durch ihre Anteile an Rot, Grün und Blau identifizieren. Eine Farbe, die aus jeder dieser drei Farben mit jeweils 50 Prozent Sättigung besteht (der Wert 128), wäre ein helles Grau. Bei anderen Gelegenheiten möchten Sie die Farbe durch ihren Namen identifizieren, z.B. wenn »Rot« bezeichnender ist, als zu wissen, dass die Farbe dem Wert -65536 entspricht. Da Farben so wichtig sind, würde man doch annehmen, dass innerhalb des Namensraums System Farben namentlich auftauchen. Wenn Sie das denken (wie ich bei meiner ersten Suche), dann liegen Sie falsch. Die benannten Farben und die Fähigkeit, neue Farben zu erzeugen, finden Sie im Namensraum System.Drawing. Innerhalb des Namensraums System.Drawing gibt es zwei Farbgruppen: die Struktur Color und die Klasse SystemColors. Die Struktur Color hat zwei Hauptzwecke. Der erste ist der »funktionelle« Aspekt der Struktur. Durch zwei Methoden, die in der Tabelle 8.9 beschrieben sind, können Sie eine Farbe erzeugen und ihren numerischen Wert herausfinden. Methode
Beschreibung
FromARGB
Erzeugt basierend auf den Rot-, Grün- und Blauwerten (jeder zwischen 0 und 255) eine neue Farbe. Es gibt auch noch andere Versionen dieser Funktion. Eine Variante nimmt zusätzlich zu den Werten für Rot, Grün und Blau einen Alpha-Wert entgegen; mit einer anderen wird der Alpha-Wert zu einer bestehenden Farbe hinzugefügt. Der Alpha-Wert stellt den Grad der Transparenz der Farbe dar. Wie die anderen Werte variiert auch Alpha zwischen 0 und 255, wobei 255 vollständig undurchsichtig ist. Die letzte Variante von FromARGB nimmt den ganzzahligen Wert der Farbe entgegen und gibt die Farbe zurück.
ToARGB
Gibt die ganze Zahl zurück, die die Zusammensetzung einer Farbe aus Alpha, Rot, Grün und Blau darstellt.
Tabelle 8.9: Methoden der Struktur Color
Zusätzlich zu der Fähigkeit, Farben zu erzeugen und umzuwandeln, enthält die Struktur Color eine Reihe von benannten Farben. Diese stellen eine große Vielfalt unterschiedlicher Farben dar, viele mit sehr hilfreichen, viel sagenden Namen wie PapayaWhip, Gainsboro und BurlyWood. Diese sind nützliche Shortcuts, wenn Sie etwas farbig darstellen möchten, den genauen numerischen Wert aber nicht kennen. Eine noch größere Zeitersparnis ist die Tatsache, dass Sie keine neue Instanz der Struktur Color erzeugen müssen, bevor Sie diese Farben verwenden. Sie können sie also in einem Programm mit Code wie diesem verwenden: frmMain.BackColor = Color.SeaShell
273
Einführung in das .NET-Framework
Ein Problem bei der Definition Ihrer eigenen Farben, entweder durch RGB (Rot, Grün, Blau)-Werte oder durch die Auswahl einer benannten Farbe ist, dass Ihre Empfindung der Farbe sich von der des Anwenders unterscheiden kann. Ich habe schon Leute getroffen, die ihre Arbeitsoberfläche in den grellsten Farben eingerichtet haben und andere, die die Voreinstellung niemals ändern. Ähnlich habe ich Programmierer getroffen, die Anwendungen mit vielen verrückten Farben erstellt haben, und andere, die alle Farben als Grauschattierungen fest eingeben. Wenn Sie die Farben fest eingeben, sticht Ihr Programm vielleicht auf der Arbeitsoberfläche des Anwenders hervor und ist dadurch vielleicht irritierend. Um das zu verhindern, definieren höfliche Programmierer die Farben der Bildschirmelemente mit den Systemfarben (die in der Systemsteuerung definiert sind). Die Systemfarben stehen in der Klasse SystemColors zur Verfügung, die verschiedene Eigenschaften hat. Diese Eigenschaften stellen die verschiedenen Elemente innerhalb von Windows dar (wie z.B. WindowText, Control oder Menu). Wenn Sie die einzelnen Teile Ihrer Anwendung mit diesen Farben kolorieren, bedeutet das, dass sie die Farben annehmen werden, die der Anwender für dieses Element ausgewählt hat. Die Verwendung der Klasse SystemColors ähnelt der Verwendung der Struktur Color. Sie müssen keine neue Instanz der Klasse erzeugen, ehe Sie die Werte nutzen. Daher können Sie mit dem folgenden Code den Hintergrund eines Formulars mit der definierten Farbe Desktop färben: frmMain.BackColor = SystemColors.Desktop
Farben sind überall in Windows präsent und daher ist es wichtig zu wissen, wie Sie Farben erzeugen.
Wer bin ich? – den Namen Ihres Computers finden Viele Programme müssen den Namen des Computers wissen, auf dem sie laufen. Sie versuchen vielleicht, auf einige Systemfunktionen oder Dienste zuzugreifen, wie Sie es am Tag 13, Der Server Explorer, tun werden. Oder Sie möchten den Computernamen für den Datenbankzugriff protokollieren oder speichern. Zum Glück habe ich viele Methoden gefunden, mit denen Sie den Namen Ihres Computers bestimmen können. Abhängig davon, welche Namensräume Sie bereits importiert haben oder welche Art von Anwendung Sie ausführen, sollte Ihnen mindestens eine Möglichkeit (und vielleicht sogar alle) zur Verfügung stehen. Sie haben bereits eine der drei möglichen, einfachen Methoden kennen gelernt, mit der Sie den Namen eines Computers erhalten, auf dem ein Programm läuft: System.Environment. Unter Windows 2000 und höher ist der Computername als Umgebungsvariable namens COMPUTERNAME gespeichert. Wenn das der Fall ist, können Sie den lokalen Computernamen auf diese Art abrufen:
274
Wie Sie im .NET-Framework finden, wonach Sie suchen
Console.Writeline("Using Environment: {0} ", _ System.Environment.GetEnvironmentVariable(”COMPUTERNAME”))
Sich auf eine Umgebungsvariable zu verlassen, auch wenn diese vom Betriebssystem gesetzt ist, erscheint aber ein wenig zu vertrauensselig. Also suche ich weiter. Das Netzwerk benötigt den Computernamen häufig, also war System.Net mein nächster Anhaltspunkt. Nach einigen fehlgeschlagenen Versuchen fand ich GetHostName in der Klasse DNS. Das ist eigentlich sinnvoll – der DNS (Domain Naming Service) ist ein Service, um Computernamen im Internet (oder in lokalen Netzwerken) zu verfolgen. Daher muss die Klasse DNS wissen, wer der aktuelle Computer ist. Sie können mit der Klasse DNS den Namen eines Computers abrufen, indem Sie in Ihrem Projekt den Namensraum System. Net einfügen und mit folgendem Code darauf zugreifen: Console.WriteLine("Using Net.DNS: { 0} ", DNS.GetHostName)
Diese Lösung funktioniert aber vielleicht nicht, wenn Sie sie auf einem Computer benutzen, der TCP/IP nicht installiert hat (was bei der Popularität des Internets wahrscheinlich auf ungefähr zwei Computer zutrifft). Und daher suchen Sie weiter. Tief im Herzen von System.Windows.Forms findet sich ein wahrer Schatz von einer Klasse: SystemInformation. Und – welch ein Wunder: SystemInformation enthält Informationen über das aktuelle Programm und das Betriebssystem, auf dem es läuft. Wenn Sie sich die Klasse ansehen, stellen Sie fest, dass sie über Eigenschaften verfügt, die alle für Anwendungen wichtigen Maßnahmen darstellen, insbesondere für Windows Forms-Anwendungen: FrameBorderSize, CaptionHeight, MenuHeight und MousePresent. Noch wichtiger für unsere derzeitige Suche ist, dass die Klasse SystemInformation die Eigenschaft ComputerName hat, mit der Sie den Namen des Computers abrufen können, wenn Sie den Namensraum System.Windows.Forms in Ihrer Anwendung wie folgt einfügen: Console.WriteLine("Using SystemInformation: {0} ", _ SystemInformation.ComputerName)
Lassen Sie sich von der Tatsache, dass diese Klasse im Namensraum System.Windows.Forms verborgen ist, nicht aus dem Konzept bringen. Sie können sie in jedem Anwendungstyp benutzen, indem Sie den entsprechenden Namensraum laden. Warum braucht man nun statt einer drei Möglichkeiten, um den Namen des Computers zu erhalten, auf dem das Programm läuft? Da ich nicht zu den Leuten gehöre, die solche Dinge entscheiden, kann ich nur Mutmaßungen anstellen: 쐽
Verschiedene Entwicklergruppen, die an verschiedenen Abschnitten arbeiten, haben verschiedene Methoden gefunden, das gleiche Problem zu lösen.
쐽
Jede der Lösungsmöglichkeiten ist nur in bestimmten Situationen geeignet (wenn z.B. die Umgebungsvariable gesetzt wurde, wenn Sie mit einem TCP/IP-Netzwerk arbeiten, wenn Sie Windows Forms verwenden usw.).
275
Einführung in das .NET-Framework
Beim Durchsuchen des .NET-Frameworks werden Sie häufig mehrere Lösungsmöglichkeiten finden. Nutzen Sie die Lösung, die Ihren Bedürfnissen am besten gerecht wird, bis Sie eine bessere finden.
Ein Tag in der Geschichte Diese Suche wurde durch eine Funktion initiiert, die es in Visual Basic 6 gab: WeekDayName. Diese Funktion gibt den Namen des Wochentags zurück. Sie können für jedes Datum mit diesem Code den Wochentag bestimmen: sDayName = WeekDayName(WeekDay(CDate("May 6, 1937")))
Für's Protokoll: Das Hindenburg-Unglück ereignete sich an einem Donnerstag. Diese Funktion kann man genau wie viele andere, die in älteren Versionen von Visual Basic existierten, recht leicht im Namensraum Microsoft.VisualBasic finden. Dieser Namensraum wird bei der Entwicklung mit der IDE automatisch geladen, wodurch Sie Code laufen lassen können, der für ältere Versionen von Visual Basic geschrieben wurde. Wenn Sie also nur eine Funktion möchten, die »einmal existierte«, finden Sie sie wahrscheinlich in diesem Namensraum.
8.4
Zusammenfassung
Häufig ist es schwierig, zu verstehen, wo das .NET-Framework aufhört und wo Visual Basic .NET beginnt. Der größte Teil der Funktionalität von Visual Basic .NET kommt tatsächlich vom Framework, darunter auch Funktionen, die vorher Teil der Windows-API waren, oder die für Visual Basic-Programmierer vor dieser Version nicht zur Verfügung standen. Es wäre eine Untertreibung, das .NET-Framework als wichtig für Visual Basic .NET-Entwickler zu beschreiben. Am Tag 17, Mit dem .NET-Framework arbeiten, werden Sie noch mehr Zeit mit der .NET-Framework-Klassensammlung verbringen. Außerdem finden Sie noch mehr Informationen zum .NET-Framework im .NET-Bereich des MSDN (Microsoft Developer Network) unter http://msdn.microsoft.com/net. Am Tag 9, Mit Windows Forms eine Benutzeroberfläche erstellen, werden Sie sich eine andere Familie von Klassen im .NET-Framework ansehen: die Klassen, mit denen Sie Windows-Benutzeroberflächen erstellen. Sie werden diese Klassen häufig benutzen, wenn Sie Anwendungen erstellen, die die reichhaltigen Benutzeroberflächen von Standardanwendungen nutzen.
276
Fragen und Antworten
8.5 F
Sollte ich das .NET-Framework oder eine alte Funktion benutzen, von der ich gelesen habe und die von Visual Basic unterstützt wird? A
F
Fragen und Antworten
Wenn Sie in der Vergangenheit in Visual Basic programmiert haben, oder wenn Sie Bücher oder Zeitschriftenartikel lesen, die auf frühere Versionen von Visual Basic abzielen, werden Sie eine Reihe der »alten« Methoden für die Durchführung von Aufgaben finden. Viele dieser Funktionen bestehen innerhalb des Namensraums Microsoft.VisualBasic immer noch. Sie können diesen Namensraum importieren, um einige der älteren Funktionen oder Konstanten zu nutzen, z.B. die Mathefunktionen oder vbCrLf (eine Konstante, die bedeutet: »Füge hier eine neue Zeile ein.«). Besser wäre es aber, die neueren Fähigkeiten des .NETFrameworks zu nutzen.
Warum haben Sie den Namensraum »Tragen-Sie-hier-Ihren-Liebling-ein« nicht besprochen? Ich muss wirklich wissen, wie ich ihn in meinem Programm verwenden kann. A
8.6
Bei 6.000 Klassen, die im .NET-Framework zur Verfügung stehen, kann eine Lektion wie die heutige kaum auch nur einen winzigen Bruchteil mit der Ausführlichkeit erörtern, die diese Klassen erfordern. Am Tag 17 werden Sie noch weitere Teile des .NET-Frameworks kennen lernen. Außerdem werden Sie das Framework auch in den verbleibenden Lektionen dieses Buches benutzen.
Workshop
Die Antworten und Lösungen finden Sie wie immer im Anhang A.
Quiz 1. Wie können Sie die Klasse Console nutzen, wenn Sie eine Windows- oder WebAnwendung schreiben? 2. Warum sollten Sie die Sammlung SortedList der Sammlung ArrayList vorziehen, wenn die ArrayList die Methode Sort hat?
277
Einführung in das .NET-Framework
3. Was wäre das Ergebnis der Ausführung des folgenden Codes? Dim oRand As New System.Random Dim iValue As Integer = oRand.Next(1, 6) + oRand.Next(1, 6) Console.WriteLine(iValue)
Übung Schreiben Sie ein kurzes Konsolenprogramm, das eine Reihe von Wörtern von der Befehlszeile entgegennimmt und sie in alphabetischer Reihenfolge auf dem Bildschirm ausgibt.
278
Mit Windows Forms eine Benutzeroberfläche erstellen
Mit Windows Forms eine Benutzeroberfläche erstellen
Am Tag 5, Programmorganisation, haben Sie etwas über die verschiedenen Methoden gelernt, wie .NET-Anwendungen aufgebaut werden können. Eine wichtige Entscheidung war die Art der zu erstellenden Benutzeroberfläche. Die Auswahl beschränkt sich hier letztlich meist auf eine Anwendung im Web- oder Windows-Stil. In .NET erstellen Sie eine Windows-Anwendung durch die Windows-Forms-Technologie.
Themen heute 쐽
Wie Steuerelemente zu einem Formular hinzugefügt und bearbeitet werden
쐽
Wie Ereignisse der Benutzeroberfläche im Code behandelt werden
쐽
Wie Anwendereingaben entgegengenommen und validiert werden
쐽
Wie Dialogfenster verwendet werden
Zusätzlich zu diesen Themen werden Sie viele der besonderen sichtbaren und unsichtbaren Steuerelemente in Windows Forms kennen lernen.
9.1
Überblick über Windows Forms
Mit Windows Forms erstellt man Anwendungen mit der Benutzeroberfläche, die allen Windows-Betriebssystemen (mit kleinen äußerlichen Abweichungen) gemeinsam ist (vgl. Abbildung 9.1). Mit diesen Klassen und Funktionen können Sie Ihren Anwendungen eine Benutzeroberfläche in diesem Stil hinzufügen, einschließlich der Fenster und Dialogfenster. Bevor Sie sich in die Erstellung und Konfiguration einer Windows-Anwendung vertiefen, sollten wir die Schlüsselbegriffe von Windows Forms wiederholen. Der erste und am schwierigsten zu definierende Begriff ist Fenster. In Zusammenhang mit Computern ist ein Fenster ein Bereich des Bildschirms, den eine Anwendung dafür verwendet, Informationen anzuzeigen. Eine Anwendung kann mehr als ein Fenster haben, aber ein Fenster gehört immer nur zu genau einer Anwendung. Fenster kann man verschieben und man kann ihre Größe ändern (bis hin zur Maximierung und Minimierung). Allerdings kann eine Anwendung diese Optionen auch deaktivieren und dem Fenster eine feste Größe zuweisen oder es an einer festen Position verankern. Die Unterelemente eines Fensters wie z.B. Schaltflächen, Textfelder, Listen und Bilder (siehe Abbildung 9.2) bilden die Schnittstelle einer Anwendung und werden als Steuerelemente bezeichnet. Diese Steuerelemente bilden die Schnittstelle zu der Anwendung, da man sie so verändern kann (anklicken, Text eintippen oder was immer sonst angemessen ist), dass sie dem Programm eine Eingabe liefern. Außerdem zeigen sie dem Anwender Informationen an. Es
280
Überblick über Windows Forms
Abbildung 9.1: Ein Großteil der Windows-Anwendungen hat einen gemeinsamen Benutzeroberflächenstil, da das Betriebssystem die Darstellung der Fenster und andere Interface-Funktionen handhabt.
gibt zwei Fensterzustände: modal und nicht-modal. Bei modalen Fenstern muss der Anwender zuerst etwas machen und das Fenster wieder schließen, bevor er mit einem anderen Teil der Anwendung weitermachen kann. Ein Beispiel hierfür ist ein Fenster, das fragt, ob Sie die Änderungen an einem Dokument speichern möchten. Nicht-modale Fenster ermöglichen dem Anwender eine weniger strukturierte Interaktion: Er kann nach Belieben zwischen allen nicht-modalen Fenstern einer Anwendung wechseln, wie es z.B. bei einem Dokumentfenster in Microsoft Word oder einem Werkzeugsammlungsfenster in Visual Studio .NET der Fall ist. Modale Fenster werden auch als Dialogfenster bezeichnet. In .NET haben sie besondere Eigenschaften, die es erleichtern, Informationen vom Anwender zu bekommen. In der heutigen Lektion werden wir uns ausführlicher mit Dialogfenstern befassen.
Abbildung 9.2: Alle Elemente in einem Fester werden Steuerelemente genannt.
281
Mit Windows Forms eine Benutzeroberfläche erstellen
9.2
Eine Windows Forms-Anwendung erstellen
Sie können z.B. eine einfache Windows Forms-Anwendung erstellen, die eine Anwendereingabe entgegennimmt und zeigt, wie diese Benutzeroberflächenobjekte funktionieren. Bei diesem Beispiel lernen Sie nicht nur etwas über Windows Forms, sondern auch ein wenig über die Dateibearbeitung mit System.IO-Klassen innerhalb des .NET-Frameworks. Zuerst werde ich Sie durch dieses einfache Beispiel führen und dann erörtere ich die einzelnen Themen der Steuerelemente und Ereignisprozeduren.
Das Projekt einrichten Der erste Schritt beim Erstellen eines Visual Basic .NET-Projekts, das Windows Forms nutzt, besteht darin, ein neues Projekt zu erstellen, indem Sie den Projekttyp WINDOWSANWENDUNG auswählen. Es wird ein neues Projekt erzeugt, das ein einzelnes Formularobjekt enthält. Alles ist bereit, sodass Sie loslegen können. Das neue Formular wird standardmäßig Form1 genannt, aber das werden Sie nicht ändern, da dieser Name in Ihrer Anwendung keine Bedeutung hat. Da Sie ein Programm erstellen, das mit Dateien arbeiten wird, benennen Sie das Formular in frmFiler um. Das geht in zwei Schritten: 1. Benennen Sie die Datei um, indem Sie Form1.vb im Projektmappen-Explorer mit der rechten Maustaste anklicken, UMBENENNEN auswählen und dann den neuen Wert frmFiler.vb eingeben. 2. Klicken Sie im Entwurfsfenster mit der rechten Maustaste auf das neue leere Formular und wählen Sie EIGENSCHAFTEN aus. Nun sollte das Fenster EIGENSCHAFTEN angezeigt werden, über das Sie die Eigenschaft (Name) (unter dem Abschnitt ENTWURF der Eigenschaften) finden und von Form1 in frmFiler ändern können. Sie tun dies am besten, bevor Sie mit dem Programmieren beginnen, da Sie dann Situationen vermeiden, in denen Sie schon Code geschrieben haben, der auf Form1 verweist. Beachten Sie an dieser Stelle, dass der Titel des Formulars immer noch Form1 ist, und nicht frmFiler, wie man erwarten würde. Das liegt daran, dass der Titel nicht mit dem Namen des Formulars zusammenhängt, aber die IDE verwendet den Formularnamen als Standardtitel bei der Erzeugung. Rufen Sie einfach erneut das Eigenschaftsfenster des Formulars auf und suchen Sie dort nach der Eigenschaft Text, die den Formulartitel darstellt. Ändern Sie ihn beliebig – Filer wäre schön – und beachten Sie, dass sich der Titel entsprechend ändert.
282
Eine Windows Forms-Anwendung erstellen
Steuerelemente zum Formular hinzufügen Ehe ein Formular etwas nützen kann, müssen Sie einige Steuerelemente darauf positionieren. Alle eingebauten Windows-Forms-Steuerelemente stehen über das Fenster TOOLBOX zur Verfügung. Lassen Sie sich einfach das TOOLBOX-Fenster anzeigen und suchen Sie den Abschnitt WINDOWS FORMS (siehe Abbildung 9.3).
Abbildung 9.3: Der WINDOWS-FORMS-Abschnitt der Werkzeugsammlung bietet eine große Vielfalt an Steuerelementen für Ihre Formulare.
In diesem Abschnitt der Werkzeugsammlung finden Sie eine Vielzahl von Steuerelementen; vorerst benötigen Sie aber nur die Steuerelemente Schaltfläche (Button) und Textfeld (TextBox). Sie fügen ein Steuerelement zum Formular hinzu, indem Sie es entweder doppelt anklicken (wodurch das Steuerelement in der linken oberen Ecke des Formulars eingefügt wird), oder es anklicken und auf das Formular ziehen, oder indem Sie das Steuerelement auswählen (einmal anklicken und loslassen) und es dann anklicken und auf das Formular ziehen, um die gewünschte Position zu umreißen. Die Verwendung jeder dieser Methoden führt zum gleichen Ergebnis: Das Steuerelement wird auf dem Formular platziert. Verwenden Sie eine der gerade beschriebenen Methoden und fügen Sie drei Schaltflächen, zwei Beschriftungen (Label) und zwei Textfelder in das Formular ein. Ordnen Sie die Steuerelemente auf dem Formular ungefähr so an, wie Sie es in Abbildung 9.4 sehen, wobei Sie die Eigenschaft Text der Schaltflächen und Beschriftungen verwenden, damit Ihre Benutzeroberfläche der abgebildeten entspricht.
283
Mit Windows Forms eine Benutzeroberfläche erstellen
Abbildung 9.4: Eine Möglichkeit, die Benutzeroberfläche der Beispielanwendung zu gestalten
Steuerelemente benennen Ehe Sie einen Ereignisbehandler erstellen, benennen Sie alle Ihre Steuerelemente von ihren Standardnamen (Button1, Button2, TextBox1, TextBox2 usw.) um. Da Sie auf die Steuerelemente und ihre Attribute mit den Steuerelementnamen zugreifen, sollten Sie sicherstellen, dass jedes Steuerelement einen aussagekräftigen Namen hat. Sie können jeden beliebigen Namen verwenden, aber ich empfehle Ihnen, irgendeine Art von Namenskonvention zu benutzen. Hier sehen Sie meine Vorschläge, die auf der Namenskonvention basieren, die ich (und viele andere) seit Jahren in Visual Basic verwende: 쐽
txtSource und txtDestination für die beiden Textfelder, von oben nach unten.
쐽
btnCopy, btnMove und btnDelete für die Schaltflächen. In Visual Basic 6.0 und vorher
wurden Schaltflächen Command Buttons genannt (statt nur Buttons wie bei Visual Basic .NET), sodass viele Programmierer bei der Benennung ihrer Schaltflächenelemente das Präfix cmd verwendeten. 쐽
lblSource und lblDestination für die Beschriftungen.
Das Umbenennen der Steuerelemente funktioniert ebenso wie das Umbenennen des Formulars: Wählen Sie das Steuerelement aus, rufen Sie seine Eigenschaften auf und ändern Sie dann die Eigenschaft (Name). Benennen Sie alle Steuerelemente um, ehe Sie zum nächsten Teil des Beispiels gehen.
284
Eine Windows Forms-Anwendung erstellen
Ereignisbehandlung Wenn Sie eine Benutzeroberfläche programmieren, müssen Sie Ereignisse und die ereignisgesteuerte Programmierung verstehen. Immer wenn der Anwender etwas tut, z.B. eine Schaltfläche anklickt, oder einen Wert in ein Textfeld eingibt, wird das als Ereignis bezeichnet. Wenn Sie möchten, dass ein bestimmter Code ausgeführt wird, sobald ein bestimmtes Ereignis eintritt, müssen Sie für dieses Ereignis einen Ereignisbehandler erstellen. Mit der Visual Studio .NET-IDE ist es einfach, einen Ereignisbehandler für ein Steuerelement zu erstellen. Ein häufiges Ereignis, das mit Code behandelt werden sollte, ist das Anklicken einer Schaltfläche in Ihrem Formular. Es wird das Ereignis Click einer Schaltfläche genannt und Sie können den Code, der diesem Ereignis zugeordnet ist, hinzufügen oder bearbeiten, indem Sie die Schaltfläche doppelt anklicken. Damit wechseln Sie in das Codebearbeitungsfenster und zu einer neuen Subroutine namens _Click (siehe Listing 9.1). Der Name der Routine ist nicht wichtig, aber dies ist das Standardformat, das die IDE verwendet. Klicken Sie die Schaltfläche COPY doppelt an, um zum entsprechenden Code zu wechseln. Beachten Sie, dass Ihr neues Click-Ereignis jetzt Button1_Click statt btnCopy_Click heißen würde, wenn Sie Ihre Schaltflächen nicht umbenannt hätten. Listing 9.1: Einen leeren Ereignisbehandler für das Ereignis Copy schreiben 1 Private Sub btnCopy_Click(ByVal sender As System.Object, _ 2 ByVal e As System.EventArgs) Handles btnCopy.Click 3 4 End Sub
Nun wird jeder Code, den Sie in die Routine setzen, ausgeführt, sobald der Anwender die Schaltfläche anklickt. Für dieses Beispiel werden Sie Code für alle Schaltflächen hinzufügen (btnCopy, btnMove und btnDelete), aber Sie müssen mit einer anfangen. Klicken Sie btnCopy also doppelt an und rufen Sie so den zugeordneten Code auf. In diesem Ereignisbehandler fügen Sie Code ein, der die in txtSource angegebene Datei in die in txtDestination angegebene Datei kopiert. Dazu müssen Sie mit den Namensraum System.IO arbeiten. Dieser Namensraum wird im Projekt bereits verwendet, da er Teil der System.dll ist, aber Sie können Ihren Code wesentlich transparenter machen, wenn Sie oben im Code Ihres Formulars die Anweisung Imports System.IO einfügen. Wenn sich diese Anweisung an der richtigen Stelle befindet, können Sie auf Objekte in diesem Namensraum verweisen, ohne die gesamte Referenz eingeben zu müssen. Um eine Datei zu kopieren, können Sie die statische Methode Copy der Klasse System.IO.File verwenden, aber Sie benötigen zuerst die Namen der Quell- und der Zieldatei. Um auf den Inhalt eines Textfeldes zuzugreifen, benutzen Sie seine Eigenschaft Text und verweisen mit seinem Namen auf das entsprechende Objekt.
285
Mit Windows Forms eine Benutzeroberfläche erstellen
Listing 9.2: Mit den statischen Methoden des Objekts File Dateien kopieren 1 Private Sub btnCopy_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnCopy.Click 2 Dim sSource As String 3 Dim sDestination As String 4 sSource = txtSource.Text() 5 sDestination = txtDestination.Text() 6 File.Copy(sSource, sDestination) 7 End Sub
Sobald die benötigten Werte in String-Variablen gespeichert sind, kann das tatsächliche Kopieren der Datei stattfinden. Da Copy eine statische Methode der Klasse File ist, müssen Sie keine Instanz erzeugen, sondern können einfach File.Copy aufrufen. Diese einzelne Zeile (Zeile 6 in Listing 9.2) ist aber nicht alles, da noch die passende Fehlerbehandlung hinzugefügt werden muss. Beim Kopieren einer Datei können viele verschiedene Fehlermeldungen auftreten, die von »Die Zieldatei existiert bereits« bis zu »Nicht genug freier Speicher« reichen können. Egal welcher Fehler eintritt, Sie können den Anwender einfach mit der Methode Show der Klasse MessageBox darüber informieren. Die Klasse MessageBox, die schon weiter vorne in diesem Buch verwendet wurde, ist Teil des Namensraums System.Windows.Forms und eine schnelle und einfache Methode, um ein Dialogfenster aufzurufen. Das Listing 9.3 zeigt ein Beispiel für die Verwendung der Methode MessageBox.Show als Reaktion auf eine Ausnahme. Listing 9.3: Mit der Klasse MessageBox einen Fehler anzeigen 1 2 3 4 5 6 7 8 9 10 11 12
286
Private Sub btnCopy_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnCopy.Click Dim sSource As String Dim sDestination As String sSource = txtSource.Text() sDestination = txtDestination.Text() Try File.Copy(sSource, sDestination) Catch objException As Exception MessageBox.Show(objException.Message) End Try End Sub
Eine Windows Forms-Anwendung erstellen
Mehrere Ereignisbehandler für ein einzelnes Ereignis Der wichtige Teil der Ereignisbehandlerroutine ist nicht ihr Name btnCopy_Click, sondern der Abschnitt Handles btnCopy.Click, der am Ende der Deklaration eingefügt wurde. Diese Anweisung sagt Visual Basic .NET, dass diese Routine der Ereignisbehandler für das Ereignis ist. Im Gegensatz zu früheren Versionen von Visual Basic, die nur den Namen der Routine benutzten, um Code bestimmten Ereignissen zuzuordnen, ist es mit Visual Basic .NET möglich, eine einzelne Prozedur mehrere Ereignisse behandeln zu lassen, oder ein einzelnes Ereignis mit mehreren Ereignisbehandlern zu versehen. Kopieren Sie die gesamte Routine btnCopy_Click und benennen Sie sie in CopyClick um, wobei Sie die Parameterliste und die Anweisung Handles btn.Copy.Click in Ruhe lassen. Die Signatur der Prozedur (die aus ihrer Parameterliste und eventuell einem Rückgabewert besteht) muss genau mit dem übereinstimmen, was Sie in der ursprünglichen Prozedur sehen, damit sie als Ereignisbehandler fungieren kann. Aber der Code kann alles mögliche sein. Ändern Sie nun den Code der zweiten Prozedur so, dass er nur eine Nachricht anzeigt, statt die Datei zu kopieren (siehe Listing 9.4). Führen Sie Ihr Projekt aus und klicken Sie auf die Schaltfläche COPY. Beide Ereignisbehandler werden aufgerufen, kopieren die Datei und zeigen das Dialogfenster MessageBox an. Listing 9.4: Die Anweisung Handles verbindet eine Routine mit einem bestimmten Ereignis. 1 2 3 4
Private Sub CopyClick(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnCopy.Click MessageBox.Show("CopyClick") End Sub
Entfernen Sie die Routine CopyClick, indem Sie den Text auswählen und löschen, und fahren Sie mit den Ereignisroutinen für die anderen beiden Schaltflächen fort.
Mit dem Code-Editor Objekte und Ereignisse finden Statt zum Entwurfsfenster zurückzugehen und jede der anderen beiden Schaltflächen doppelt anzuklicken, können Sie direkt im Codefenster zu der Ereignisbehandlungsroutine Click gehen. Wählen Sie den Objektnamen (z.B. btnMove) aus der ersten Dropdown-Liste (auf der linken Seite) über dem Code-Editor aus und wählen Sie dann das gewünschte Ereignis (Click) aus dem zweiten Dropdown-Menü aus. Das hat die gleiche Wirkung wie das doppelte Anklicken des Objekts im Designer, sodass Ihnen beide Methoden zur Verfügung stehen. Der Code für die Schaltflächen MOVE und DELETE nutzt die entsprechenden statischen Methoden des Objekts File. Das Listing 9.5 zeigt eine Möglichkeit, wie diese beiden Schaltflächen codiert werden können.
287
Mit Windows Forms eine Benutzeroberfläche erstellen
Listing 9.5: Mit den statischen Methoden des Objekts File Dateien verschieben und löschen. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Private Sub btnMove_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnMove.Click Dim sSource As String Dim sDestination As String sSource = txtSource.Text() sDestination = txtDestination.Text() File.Move(sSource, sDestination) End Sub Private Sub btnDelete_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles btnDelete.Click Dim sSource As String sSource = txtSource.Text() File.Delete(sSource) End Sub
Mehrere Ereignisse mit einem Ereignisbehandler Sie können alternativ auch alle drei Click-Ereignisse mit nur einer Prozedur behandeln, indem Sie die Anweisung Handles so verändern, dass sie alle drei enthält. Im Listing 9.6 sehen Sie nur die Deklaration dieser Ereignisbehandlung, die Einzelheiten wollen wir den heutigen Übungen überlassen. Listing 9.6: Mit dem Schlüsselwort Handles mehrere Ereignisse mit einer einzelnen Ereignisprozedur oder mehrere Ereignisprozeduren mit einem einzelnen Ereignis verknüpfen 1 Private Sub DoEverything(ByVal sender As Object, _ 2 ByVal e As System.EventArgs) _ 3 Handles btnCopy.Click, btnMove.Click, btnDelete.Click
Die einzige Beschränkung für die Anzahl von Ereignissen, die Sie mit einer einzelnen Routine behandeln können, besteht darin, dass die Ereignisse alle die gleiche Parametergruppe verwenden müssen. Das Ereignis Click einer Schaltfläche nimmt z.B. zwei Parameter entgegen (ein System.Object und ein System.EventArgs), während das Ereignis ChangeUICues der gleichen Schaltfläche andere Parameter entgegennimmt (ein System.Object und ein System.Windows.Forms.UICuesEventArgs). Daher können die beiden Ereignisse nicht von einer einzigen Routine behandelt werden. Der Zweck dieser Parameter besteht darin, dem Behandler Informationen über das Ereignis zu geben. Einige Ereignisse liefern dabei andere Informationen als andere Ereignisse.
288
Mehr über Steuerelemente
9.3
Mehr über Steuerelemente
Im letzten Beispiel haben Sie Textfelder, Beschriftungen und Schaltflächen zu einem neuen Formular hinzugefügt, aber es stehen noch viele weitere Steuerelemente zur Verfügung. Hier sehen Sie eine Liste der gebräuchlichsten eingebauten Steuerelemente, jeweils zusammen mit einer kurzen Beschreibung: 쐽
Label/LinkLabel: Beide bieten eine Möglichkeit, statischen (nicht bearbeitungsfähigen) Text in einem Formular zu platzieren. Das Steuerelement Link Label fügt noch
zusätzliche Funktionalität hinzu, wodurch ein Teil des Texts oder der gesamte Text wie ein HTML-Link aussieht und über ein zusätzliches Ereignis verfügt (LinkClicked). Dieses Ereignis zeigt an, ob der Anwender den Teil der Beschriftung angeklickt hat, der den Hyperlink enthält. 쐽
Button: Die Standardschaltfläche, die aber einen wesentlich größeren Funktionsumfang hat als in früheren Versionen von Visual Basic. Mit den Eigenschaften dieser Schaltfläche können Sie ein Bild hinzufügen, das Layout des Textes und Bildes ändern, die Überschrift setzen und vieles mehr.
쐽
TextBox: Dieses Element bietet auf einem Formular einen Bereich für die Texteingabe und unterstützt elementare Bearbeitung. Es kann durch das Setzen der Eigenschaft Multiline auf True mehrzeilig gestaltet werden und bietet ein einfaches Kontextmenü (verfügbar mit Rechtsklick), mit dem der Anwender die Funktionen Ausschneiden, Kopieren und Einfügen nutzen kann.
쐽
MainMenu und ContextMenu: Obwohl sich diese beiden Steuerelemente nicht zusammen in der Werkzeugsammlung finden, wird mit ihnen ein Menüsystem für ein Fenster erzeugt. Tag 16, Windows Forms für Fortgeschrittene, erörtert ausführlich, wie Sie für Ihr System mit diesen beiden Steuerelementen Menüs erstellen.
쐽
CheckBox/RadioButton: Mit diesen beiden Steuerelementen werden einfache Ja/Neinoder True/False-Werte angezeigt und eingegeben. Mit beiden werden häufig Optionslisten angezeigt, die ein- oder ausgeschaltet werden müssen. Der Unterschied liegt darin, wie sie eine Auswahl eines anderen Steuerelements des gleichen Typs innerhalb ihrer Gruppe behandeln. In einer Gruppe von Optionsschaltern (RadioButton) soll jederzeit immer nur einer aktiviert sein. Bei den Kontrollkästchen (CheckBox) können dagegen mehrere gleichzeitig aktiviert sein.
쐽
PictureBox: Stellt Ihnen einen Ort zur Verfügung, an dem Sie entweder eine bestehende Grafik anzeigen oder mit der Grafikbibliothek eine Grafik zeichnen können.
Es gibt noch wesentlich mehr Steuerelemente, die aber im Grunde alle gleich funktionieren. Sie platzieren sie auf dem Formular, bearbeiten ihre Eigenschaften entweder im Code oder über das Fenster EIGENSCHAFTEN und schreiben Code, um ihre Ereignisse zu behan-
289
Mit Windows Forms eine Benutzeroberfläche erstellen
deln. Ich werde zwar nicht jedes Steuerelement einzeln erörtern, aber die Verwendung von zweien durchsprechen, um sie Ihnen zu veranschaulichen.
Optionsschaltergruppen erzeugen Wie bereits gesagt, werden mit Optionsschaltern und Kontrollkästchen Einstellungen dargestellt, die entweder ein- oder ausgeschaltet sein können. Zwischen diesen beiden Steuerelementtypen besteht jedoch ein wesentlicher Unterschied. Optionsschalter können nur in Gruppen zu zwei oder mehreren verwendet werden und es kann immer nur ein Optionsschalter in einer Gruppe gleichzeitig ausgewählt sein. Durch das Anklicken wird ein Optionsschalter ausgewählt und die Auswahl des vorher markierten Schalters wird automatisch aufgehoben. Kontrollkästchen werden zwar auch durch Anklicken ein- und ausgeschaltet, aber in einer Gruppe kann eine beliebige Anzahl davon gleichzeitig ausgewählt sein. Stellen Sie sich diese beiden Steuerelementtypen bei einem Multiple-Choice-Test vor: Kontrollkästchen wären für eine Frage geeignet, die »Kreuzen Sie alle richtigen Antworten an« vorgibt, während Optionsschalter nur bei Fragen wie »Kreuzen Sie die beste Antwort an« geeignet sind. Um eine Gruppe von Optionsschaltern zu erzeugen, erstellen Sie zuerst ein neues Formular, da das erste Formular Filer bereits recht voll ist. Da das Projekt, das Filer enthält, bereits geöffnet ist, klicken Sie das Projekt im Projektmappen-Explorer mit der rechten Maustaste an. Wählen Sie im Kontextmenü HINZUFÜGEN und WINDOWS FORM HINZUFÜGEN aus und es wird ein Dialogfenster angezeigt, in das Sie einen Namen für Ihr neues Formular eingeben können (Sie könnten z.B. RadioButtons eingeben). Bestätigen sie, um dieses neue Formular zum aktuellen Projekt hinzuzufügen. Fügen Sie im neuen Formular nun mit der Werkzeugsammlung das Steuerelement GroupBox hinzu. Dieses Steuerelement sieht wie ein Rechteck mit einer Überschrift und schön herausgemeißelten Kanten aus und wird verwendet, um andere Steuerelemente zu gruppieren und zu enthalten. Verschieben und skalieren Sie das Feld, sodass es einen Großteil des Bildschirms ausfüllt. Um eine Gruppe von Optionsschaltern zu erzeugen, müssen Sie in dieses Gruppenfeld nur RadioButton-Steuerelemente hinzufügen. Fügen Sie zum Gruppenfeld vier dieser Steuerelemente hinzu und positionieren Sie sie ordentlich. Sie müssen mit diesen Steuerelementen sonst nichts machen. Sie bilden zusammen eine Gruppe, da sie alle im gleichen Gruppenfeld enthalten sind. Wenn Sie Ihr Projekt an diesem Punkt ausführen würden, würde Ihr neues Formular nicht erscheinen, da das Projekt bei der Erstellung so eingerichtet wurde, dass es das erste Formular anzeigt. Um das Projekt so zu ändern, dass statt dessen das neue Formular angezeigt wird, klicken Sie das Projekt mit der rechten Maustaste im Projektmappen-Explorer Fenster an (genauso wie weiter oben, als Sie eine Datei hinzugefügt haben) und wählen Sie aus dem Kontextmenü EIGENSCHAFTEN aus. Dadurch erscheint ein großes Dialogfenster mit vielen Optionen und Einstellungen (siehe Abbildung 9.5). Der Wert, den wir verändern
290
Mehr über Steuerelemente
müssen, befindet sich auf der ersten Seite (ALLGEMEINE EIGENSCHAFTEN, ALLGEMEIN) und heißt STARTOBJECT. Die Einstellung STARTOBJECT bestimmt, welches Formular oder welcher andere Code ausgeführt wird, wenn das Projekt ausgeführt wird. Derzeit sollte es frmFiler sein (der Name des ersten Formulars in diesem Projekt). Ändern Sie es in RadioButtons oder wie auch immer Sie das zuletzt hinzugefügte Formular genannt haben.
Abbildung 9.5: Mit dem Dialogfenster PROJEKTEIGENSCHAFTEN
können Sie eine Vielzahl von Einstellungen steuern, darunter auch das Startobjekt.
Wenn Sie das Projekt nun ausführen, wird Ihr neues Formular (mit seinen vier Optionsschaltern) angezeigt (siehe Abbildung 9.6).
Abbildung 9.6: Eine Optionsschaltergruppe ist eine wunderbare Möglichkeit, den Anwender eine von mehreren Optionen auswählen zu lassen.
Nachdem dies alles läuft, versuchen Sie, die Optionsschalter anzuklicken und beachten Sie, dass immer nur ein Schalter ausgewählt ist. Um mit Code zu bestimmen, ob ein Optionsschalter ausgewählt ist, können Sie die Eigenschaft Checked nutzen. Um mit Code auf diese Steuerelemente zuzugreifen, können Sie Testcode zu Ihrem neuen Formular
291
Mit Windows Forms eine Benutzeroberfläche erstellen
hinzufügen. Schließen Sie das Formular (mit der Schaltfläche X in der oberen rechten Ecke) und gehen Sie zur Entwurfsansicht Ihres neuen Formulars zurück. Fügen Sie aus der Werkzeugsammlung eine neue Schaltfläche hinzu, ändern Sie ihren Namen in btnTest und ihre Überschrift in »Test« und klicken Sie sie dann doppelt an, um für ihr Standardereignis (Click) in die Code-Ansicht zu wechseln. Der Code in Listing 9.7 zeigt, wie Sie überprüfen können, welche Optionsschalter ausgewählt wurden, und den entsprechenden Wert mit einer MessageBox anzeigen. Fügen Sie diesen Code in das Ereignis Click von btnTest ein und führen Sie dann Ihr Projekt aus, um es auszuprobieren. Listing 9.7: Wenn nur eine Option ausgewählt sein darf, verwenden Sie Optionsschalter. 1 Private Sub btnTest_Click(ByVal sender As System.Object, _ 2 ByVal e As System.EventArgs) Handles btnTest.Click 3 Dim sSelected As String 4 5 If RadioButton1.Checked Then 6 sSelected = "RadioButton1" 7 ElseIf RadioButton2.Checked Then 8 sSelected = "RadioButton2" 9 ElseIf RadioButton3.Checked Then 10 sSelected = "RadioButton3" 11 ElseIf RadioButton4.Checked Then 12 sSelected = "RadioButton4" 13 End If 14 15 MessageBox.Show(sSelected, "Selected Radio Button") 16 End Sub 17 End Class
Dem Beispiel Filer ein Kontrollkästchen hinzufügen Wenn Sie die Optionsschalter im vorherigen Beispiel durch Kontrollkästchen ersetzen würden, würden Sie feststellen, dass Sie beliebig viele Kästchen gleichzeitig auswählen können (aber auch gar keines ist möglich). Der Testcode würde aber noch funktionieren – unter der Annahme, dass Sie die Kontrollkästchen entweder genauso benannt haben, wie die Optionsschalter, oder dass Sie den Code entsprechend den neuen Namen geändert haben –, da Kontrollkästchen ebenfalls über die Eigenschaft Checked verfügen. Ein nützlicheres Beispiel für ein Kontrollkästchen können Sie erstellen, indem Sie das erste Formular, das Sie erstellt haben (Filer), verändern. Bei diesem Formular kam es zu einem Fehler, wenn der Anwender einen bereits existierenden Zieldateinamen angab und dann die Schaltflächen COPY oder MOVE anklickte. Alternativ könnte der Code die bestehende Datei einfach überschreiben, ohne dass eine Fehlermeldung erscheint. Sie können
292
Mehr über Steuerelemente
ein Kontrollkästchen zu dem Formular hinzufügen, um die ein- und ausschaltbare Überschreib-Funktion zu unterstützen. Rufen Sie zuerst das erste Formular (Filer) in der Entwurfsansicht auf und fügen Sie dann aus der Werkzeugsammlung ein neues Steuerelement des Typs CheckBox ein. Ändern Sie die Beschriftung in Overwrite Existing, indem Sie die Eigenschaft Text im Fenster EIGENSCHAFTEN ändern. Hier können Sie auch den Standard- oder Startwert des Kontrollkästchens setzen, indem Sie die Eigenschaft Checked im Fenster EIGENSCHAFTEN setzen. Verschieben Sie das neue Steuerelement unter das Zielfeld und klicken Sie dann die Schaltfläche COPY doppelt an, um zur Codebearbeitung zu wechseln. Sie müssen sowohl an btnCopy_Click als auch an btnMove_Click eine kleine Veränderung vornehmen. Derzeit verwenden der Aufruf von COPY und der Aufruf von MOVE nur zwei Parameter (Quelle und Ziel), aber COPY unterstützt nun eine zusätzliche Funktion (overwrite), die bestimmt, ob eine eventuell bereits bestehende Datei gelöscht werden soll. Dadurch, dass Sie diese neue Eigenschaft mit der Eigenschaft Checked des Kontrollkästchens gleichsetzen, können Sie steuern, wie eine bestehende Datei behandelt wird. Das Listing 9.8 zeigt Ihnen den veränderten btnCopy_Click, der das neue Kontrollkästchen behandelt. Listing 9.8: Ein Kontrollkästchen hinzufügen 1 Private Sub btnCopy_Click(ByVal sender As System.Object, _ 2 ByVal e As System.EventArgs) Handles btnCopy.Click 3 Dim sSource As String 4 Dim sDestination As String 5 sSource = txtSource.Text() 6 sDestination = txtDestination.Text() 7 Try 8 File.Copy(sSource, sDestination, chkOverwrite.Checked) 9 Catch objException As Exception 10 MessageBox.Show(objException.Message) 11 End Try 12 End Sub
Die Behandlung der gleichen Situation im Falle des Befehls Move ist etwas schwieriger. Statt einfach einen Parameter zu ändern, müssen Sie auf den Dateifehler reagieren und entweder den Move-Versuch abbrechen oder die betreffende Datei löschen. Das Listing 9.9 zeigt eine Methode, wie das Ereignis btnMove_Click behandeln können. Listing 9.9: Das Ereignis btnMove_Click behandeln 1 2 3 4 5
Private Sub btnMove_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnMove.Click Dim sSource As String Dim sDestination As String sSource = txtSource.Text()
293
Mit Windows Forms eine Benutzeroberfläche erstellen
6 7 8 9 10 11 12 13 14 15 16 17 18
9.4
sDestination = txtDestination.Text() If File.Exists(sSource) Then If File.Exists(sDestination) Then If chkOverwrite.Checked Then File.Delete(sDestination) Else MessageBox.Show("Move aborted, destination exists") Return End If End If File.Move(sSource, sDestination) End If End Sub
Eingabevalidierung
Wie der Name »Formular« schon erahnen lässt, werden Fenster und Dialogfenster häufig genutzt, um in einer Anwendung eine Dateneingabe zuzulassen. Wenn Informationen in das System eingegeben werden, sollten Sie sie häufig irgendeiner Form der Validierung unterziehen (auf gültige Daten oder die korrekte Anzahl von Stellen einer Telefonnummer prüfen usw.). Das .NET-Framework bietet ein Modell für das Codieren dieser Validierung. Bei einem Formular mit einigen Eingabefeldern (z.B. Textfeldern) gibt es wahrscheinlich auch ein oder zwei Schaltflächen. Mindestens eine dieser Schaltflächen zeigt an, dass der Anwender denkt, dass er mit der Eingabe der Daten fertig ist. Wenn der Anwender also auf OK klickt, sollten Sie Ihren Validierungscode laufen lassen und alle Felder auf ihre Plausibilität hin überprüfen. Wenn der Anwender auf die Schaltfläche ABBRECHEN (CANCEL) klickt, gibt es keinen Grund, sich mit der Validierung der Daten zu beschäftigen, da er dann das Formular löschen möchte. Wenn die Überprüfung auf gültige Daten erst dann erfolgt, wenn der Anwender auf OK klickt (oder auf SAVE, SPEICHERN oder was auch immer für Ihre Anwendung zutrifft), vermeiden Sie damit, dass der Anwender erst gültige Daten eingeben muss, eher er Ihre Anwendung abbrechen kann. Ich habe schon mit vielen Anwendungen gearbeitet, die mich dazu gezwungen haben, eine richtige Telefonnummer einzugeben, ehe ich ein unerwünschtes Dialogfenster schließen konnte, und das, obwohl die Telefonnummer weggeworfen wurde, weil ich auf ABBRECHEN geklickt habe! Verschiedene Aspekte der Klassen von Windows Forms arbeiten zusammen, um diese Validierung zu bieten: die Eigenschaft CausesValidation und die Ereignisse Validating/ Validated, die beide auf jedem Steuerelement existieren. Der allgemeine Prozess für die Verwendung dieser Eigenschaften und Ereignisse besteht darin, bei jedem tatsächlichen
294
Eingabevalidierung
Steuerelement zur Datenaufnahme (Textfeld, Optionsschalter, Kombinationsfeld usw.) CausesValidation auf True zu setzen und dasselbe auch bei jeder Schaltfläche zu tun, mit der die Daten gespeichert oder verwendet werden (OK, SAVE, CONTINUE, NEXT usw.). Bei Schaltflächen wie HELP, CANCEL, PREVIOUS oder anderen Schaltflächen, bei denen es egal ist, ob die Daten gültig sind, wenn sie angeklickt werden, setzen Sie die Eigenschaft CausesValidation auf False. Setzen Sie als Nächstes Code in das Validating-Ereignis, um zu überprüfen, ob ein Feld gültig ist. Wenn der Anwender versucht, den Fokus auf ein Steuerelement zu verschieben, bei dem CausesValidation = True ist, wird das Ereignis Validating für jedes Bearbeitungssteuerelement ausgelöst, auf dem sich seit dem letzten Anklicken eines Steuerelements, bei dem CausesValidation gleich True war, der Fokus befand. Da dies ein wenig komplex war, finden Sie nachfolgend eine schrittweise Übersicht: Sie haben ein Formular, um Adressen einzugeben oder zu bearbeiten (siehe Abbildung 9.7), mit einer Schaltfläche OK, einer Schaltfläche CANCEL und verschiedenen Textfeldern für die Dateneingabe. Für alle Textfelder haben Sie die Eigenschaft CausesValidation auf True gesetzt. Für die Schaltfläche OK haben Sie diese Eigenschaft ebenfalls auf True gesetzt, nicht aber für die Schaltfläche CANCEL.
Abbildung 9.7: Dieses Dialogfenster, mit dem Adressen eingegeben und bearbeitet werden sollen, ist ein Beispiel für ein Dateneingabeformular.
Der Fokus befindet sich derzeit auf dem Textfeld txtStreet und der Anwender drückt auf (ÿ__), um zum Textfeld txtCity zu gelangen. An diesem Punkt wird wegen der Eigenschaft CausesValidation von txtCity das Ereignis Validation von txtStreet ausgelöst. Nun sollte das Ereignis Validating den Inhalt von txtStreet überprüfen und sicherstellen, dass er gültig ist. Es kann die Validierung abbrechen, wenn die Daten nicht korrekt sind. Wenn die Daten korrekt sind (und das Ereignis nicht abgebrochen wird), dann wird das Ereignis Validated von txtStreet aufgerufen, um anzuzeigen, dass die Validierung erfolgreich war. Wenn die Daten nicht korrekt waren und das Ereignis abgebrochen wird, wird auch der Versuch, den Fokus zu verschieben, abgebrochen und der Fokus bleibt auf dem Textfeld txtStreet. Also wird immer dann das Ereignis Validating ausgelöst, wenn der Fokus auf ein Steuerelement kommt, bei dem CausesValidation = True ist. Das Ereignis wird für jedes Steuerelement ausgelöst, das besucht wurde, seitdem Sie das letzte Mal auf ein Steuerelement gestoßen sind, bei dem CausesValidation = True war. Das hängt nicht von der Richtung ab. Wenn also der Anwender mit (ÿ__) oder einem Klick zu txtStreet zurückkehren
295
Mit Windows Forms eine Benutzeroberfläche erstellen
würde, nachdem er sich zu txtCity bewegt hat, dann würde das Ereignis Validating auf txtCity aufgerufen. Immer noch verwirrt? Das Listing 9.10 enthält den Code für die verschiedenen Validierungsereignisse des Adressenformulars und hilft Ihnen vielleicht etwas. Sie können das gesamte Formular auch von der Buch-CD dieses Buches herunterladen, um Zeit zu sparen. Listing 9.10: Die Ereignisse des Adressenformulars validieren 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
296
Private Sub txtZip_Validating(ByVal sender As Object, _ ByVal e As System.ComponentModel.CancelEventArgs) _ Handles txtZip.Validating 'Wandle sender in ein System.Windows.Forms.Control um. Dim ctlSender As Control ctlSender = CType(sender, Control) Dim bValidPostalCode As Boolean = False 'Validiere nur auf das US/Canada-Postleitzahlenformat. 'Überprüfe, ob txtCountry = Canada, nimm andernfalls US an. 'Behandle drei mögliche Formate, 'US short #####, US long #####-####, Cdn A#A#A# Dim sPostalCode As String Dim sPattern As String Dim objRegEx As Regex sPostalCode = ctlSender.Text.Trim.Replace(" ", "") If txtCountry.Text.Trim.ToUpper = "CANADA" Then If sPostalCode.Length = 6 Then sPattern = "[ABCEGHJKLMNPRSTVXY]\ d[A-Z]\ d[A-Z]\ d" End If Else If sPostalCode.Length = 10 Then sPattern = "\ d\ d\ d\ d\ d-\ d\ d\ d\ d" ElseIf sPostalCode.Length = 5 Then sPattern = "\ d\ d\ d\ d\ d" End If objRegEx.IsMatch(sPostalCode, "") End If If sPattern "" Then If objRegEx.IsMatch(sPostalCode, sPattern) Then bValidPostalCode = True End If End If If bValidPostalCode = False Then e.Cancel = True
Die Klasse MessageBox verwenden
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
errAddress.SetError(ctlSender, "Invalid Postal Code") End If End Sub Private Sub GenericValidated(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles txtStreet.Validated, txtCity.Validated, _ txtCountry.Validated, txtZip.Validated, _ txtState.Validated 'Wandle sender in ein System.Windows.Forms.Control um. Dim ctlSender As Control ctlSender = CType(sender, Control) 'Lösche Fehler, wenn einer existiert. errAddress.SetError(ctlSender, "") End Sub Private Sub GenericNotEmpty(ByVal sender As Object, _ ByVal e As System.ComponentModel.CancelEventArgs) _ Handles txtStreet.Validating, txtCity.Validating, _ txtState.Validating 'Wandle sender in ein System.Windows.Forms.Control um. Dim ctlSender As Control ctlSender = CType(sender, Control) If ctlSender.Text.Trim = "" Then e.Cancel = True errAddress.SetError(ctlSender, "Must not be empty") End If End Sub
Wenn die Validierungsfunktionen von .NET mit dem Steuerelement ErrorProvider kombiniert werden, das weiter unten in der heutigen Lektion besprochen wird, wird das Erstellen von Dateneingabeformularen einfacher.
9.5
Die Klasse MessageBox verwenden
Ein Dialogfenster ist eine besondere Form eines Formulars. Es wird modal angezeigt. Der Anwender muss sich mit ihm beschäftigen, ehe er mit einem anderen Teil der Anwendung interagieren kann. Dialogfenster werden normalerweise verwendet, um den Anwender über etwas zu informieren (z.B. über einem Fehler) oder um Informationen zu erhalten. Dialogfenster werden wegen ihrer modalen Natur nicht dazu genutzt, Status- oder Fortschrittsinformationen anzuzeigen. Sie sollten nur dann verwendet werden, wenn Sie sofort mit dem Anwender kommunizieren müssen, ehe Sie etwas anderes tun.
297
Mit Windows Forms eine Benutzeroberfläche erstellen
Sie können selbst Dialogfenster wie jedes andere Windows-Formular erstellen. Ich werde weiter unten in der heutigen Lektion genau erklären, wie das geht. Meistens benötigen Sie aber gar nichts Komplexes, Sie möchten nur eine einfache Frage stellen (mit der Antwort Ja/Nein oder OK/CANCEL) oder dem Anwender eine Nachricht anzeigen. Hier kommt die Klasse MessageBox ins Spiel. Mit dieser Klasse wird Text in einem einfachen Dialogfenster mit einer Reihe von Schaltflächen angezeigt, aus denen der Anwender seine Reaktion auswählen kann (in Abbildung 9.8 sehen Sie ein Beispiel). Sie können keine Instanz dieser Klasse erzeugen, sie enthüllt aber eine einzelne statische Methode namens Show, über die Sie das Dialogfenster mit einem einzigen Aufruf wie gewünscht konfigurieren und anzeigen können.
Abbildung 9.8: Die MessageBox ist ein nützliches Werkzeug, das eine Nachricht zusammen mit einer Vielzahl von Schaltflächenkombinationen anzeigen kann.
Parameter Die Methode Show nimmt sieben verschiedene Parameter entgegen, Sie können aber nur die Werte liefern, über die Sie verfügen. Diese Methode hat zwölf verschiedene Überladungen, um eine breite Palette von Parameterkombinationen zu unterstützen. Jeder mögliche Parameter ist hier aufgelistet, zusammen mit einer kurzen Beschreibung seines Zwecks: 쐽
Text: Dieser Parameter stellt die Nachricht dar, die von der MessageBox angezeigt wird. Er ist nicht optional und in jeder Überladung von Show enthalten.
쐽
Caption: Dieser Parameter ist eine weitere Zeichenkette wie Text und bestimmt, welcher Titel von MessageBox angezeigt wird.
쐽
Buttons: Dieser Parameter steuert die Schaltflächen, die auf der MessageBox gezeigt werden, und nimmt einen der möglichen aufgezählten Werte an. MessageBoxButtons.AbortRetryIgnore lässt das Dialogfenster ABORT-, RETRY- und IGNORE-Schaltflächen anzeigen, MessageBoxButtons.YesNo zeigt JA- und NEIN-Schaltflächen an usw.
쐽
Icon: Steuert, welche Grafik zusammen mit der Nachricht angezeigt wird, wenn es
überhaupt eine gibt. Kann jeder von neun Werten sein, aber die aktuellen Versionen der Betriebssysteme bieten nur Grafiken für vier dieser Werte und daher sind alle neun Optionen einer dieser vier Grafiken zugeordnet. 쐽
298
DefaultButton: Wenn sich in dem Dialogfenster mehr als eine Schaltfläche befindet, kann nur eine davon die Voreinstellung sein. Wenn der Anwender auf (¢) oder (¢) drückt, wenn sich die MessageBox öffnet, wird das so behandelt, als hätte er auf die
Die Klasse MessageBox verwenden
Standardschaltfläche geklickt. Dieser Parameter kann auf Button1, Button2 oder Button3 gesetzt werden, die jeweils einer der entsprechenden Schaltflächen zugeordnet sind. 쐽
Options: Diese Optionen steuern das Aussehen der MessageBox und sind besonders nützlich, wenn Ihre Anwendung für ein anderes Land lokalisiert wird. Zu diesen Optionen gehören die rechtsbündige Ausrichtung des Textes, die Vorgabe, dass der Text von rechts nach links gelesen wird und das Sicherstellen, dass das Nachrichtenfeld nur auf dem primären Desktop des Betriebssystems erscheint.
쐽
OwnerWindow: Dieser Parameter gibt ein Fenster innerhalb Ihrer Anwendung an, vor dem die MessageBox erscheinen soll. Im Allgemeinen wird diese Funktionalität nicht
benötigt, aber sie steht zur Verfügung. Die Abbildung 9.9 und Abbildung 9.10 zeigen einige Beispielaufrufe von MessageBox.Show() und die daraus resultierenden Dialogfeld.
Abbildung 9.9: Die Standard-MessageBox, die nur mit dem Nachrichtentext (dem Minimum) aufgerufen wird.
Abbildung 9.10: Eine etwas angepasstere MessageBox, die den Text, die Überschrift und die Parameter für die Schaltflächen angibt.
Ergebnisse erhalten Nachdem Sie die Schaltflächen ausgewählt haben, die Sie anzeigen möchten, werden Sie wissen wollen, welche der Anwender angeklickt hat (wenn Sie mehr als nur eine OK-Schaltfläche angezeigt haben). Diese Information wird von der Methode Show als DialogResultWert zurückgegeben, den Sie in einer Variablen speichern können. Sie können den Methodenaufruf aber auch direkt in einem Ausdruck oder einer Bedingung verwenden. Listing 9.11 zeigt, wie Sie dem Anwender mit der Klasse MessageBox eine Frage stellen und die beiden Methoden, um das Ergebnis zu behandeln. Listing 9.11: Mit der MessageBox eine einfache Frage stellen und auf die Antwort reagieren 1 2 3
Private Sub btnTest_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnTest.Click Dim drResult As DialogResult
299
Mit Windows Forms eine Benutzeroberfläche erstellen
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
drResult = MessageBox.Show("Whattya wanna do?", _ "Sample", MessageBoxButtons.RetryCancel) If drResult = DialogResult.Retry Then 'Wiederholen Else 'Abbrechen End If Select Case MessageBox.Show("Bad stuff happened", _ "Long Process", MessageBoxButtons.AbortRetryIgnore) Case DialogResult.Abort 'Abbrechen Case DialogResult.Retry 'Wierholen Case DialogResult.Ignore 'Ignorieren Case Else 'Hmm... wie bin ich nur hier hin gekommen? End Select End Sub
Um die Verwendung der MessageBox in einem echten System zu zeigen, können Sie zum ursprünglichen Filer-Formular ein wenig Funktionalität hinzufügen. Dieses Formular, mit dem Sie Dateien kopieren, verschieben und löschen konnten, enthielt keine Bestätigungsnachricht (der Art »Sind Sie sicher, dass Sie c:\test.txt löschen möchten?«), auch wenn Sie es darum baten, eine Datei vollständig zu löschen. Um diese Funktion zum Formular Filer hinzuzufügen, können Sie eine MessageBox benutzen, um die Bestätigung und JA/NEIN-Schaltflächen anzuzeigen und dann entsprechend die Aktion auszuführen oder abzubrechen. Das Listing 9.12 zeigt die drei Ereignisroutinen, die so modifiziert wurden, dass sie den Bestätigungsschritt enthalten. Listing 9.12: Mit der Methode MessageBox.Show um eine Bestätigung bitten 1 2 3 4 5 6 7 8 9 10 11 12
300
Private Sub btnCopy_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnCopy.Click Dim sSource As String Dim sDestination As String sSource = txtSource.Text() sDestination = txtDestination.Text() If File.Exists(sSource) Then Dim sConfirm As String sConfirm = _ String.Format("Are you sure you wish to copy { 0}
to { 1} ?", _
Die Klasse MessageBox verwenden
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
sSource, sDestination) If MessageBox.Show(sConfirm, _ "Confirm Copy", _ MessageBoxButtons.YesNo, _ MessageBoxIcon.Question, _ MessageBoxDefaultButton.Button2) = DialogResult.Yes Then Try File.Copy(sSource, sDestination, chkOverwrite.Checked) Catch objException As Exception MessageBox.Show(objException.Message) End Try End If End If End Sub Private Sub btnMove_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnMove.Click Dim sSource As String Dim sDestination As String sSource = txtSource.Text() sDestination = txtDestination.Text() If File.Exists(sSource) Then Dim sConfirm As String sConfirm = String.Format( _ "Are you sure you wish to move { 0} to { 1} ?", _ sSource, sDestination) If MessageBox.Show(sConfirm, _ "Confirm Move", MessageBoxButtons.YesNo, _ MessageBoxIcon.Question, _ MessageBoxDefaultButton.Button2) = DialogResult.Yes Then If File.Exists(sDestination) Then If chkOverwrite.Checked Then File.Delete(sDestination) Else MessageBox.Show("Move aborted, destination exists") Return End If End If File.Move(sSource, sDestination) End If End If End Sub
301
Mit Windows Forms eine Benutzeroberfläche erstellen
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
9.6
Private Sub btnDelete_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles btnDelete.Click Dim sSource As String sSource = txtSource.Text() If File.Exists(sSource) Then Dim sConfirm As String sConfirm = String.Format( _ "Are you sure you want to delete { 0} ?", _ sSource) If MessageBox.Show(sConfirm, _ "Confirm Delete", MessageBoxButtons.YesNo, _ MessageBoxIcon.Question, _ MessageBoxDefaultButton.Button2) = DialogResult.Yes Then File.Delete(sSource) End If End If End Sub
Unsichtbare Steuerelemente
Alle Steuerelemente, die wir bisher in der heutigen Lektion benutzt haben, waren sichtbar, wenn Sie Ihr Projekt ausführten. Das ist der Standardtyp des Steuerelements, mit dem Sie arbeiten werden. .NET enthält aber auch noch andere Steuerelemente, die in fast jeder Hinsicht den regulären Steuerelementen gleichen, außer dass sie zur Laufzeit keine sichtbare Schnittstelle haben. Diese Steuerelemente haben trotzdem Eigenschaften, Methoden und Ereignisse und sollen einen einfache, modulare Möglichkeit bieten, besondere Funktionalität zu einem Formular hinzuzufügen. Als es noch kein .NET Visual Basic gab, wurden ebenfalls schon unsichtbare Steuerelemente auf Ihrem Formular platziert und verstopften zur Entwurfszeit die Benutzeroberfläche, waren zur Laufzeit aber nicht vollständig unsichtbar. Visual Studio .NET verfügt über eine viel bessere Methode, diese Art von Steuerelementen zu behandeln: Es setzt sie in einen separaten Bereich unter dem Entwurfsbereich des Formulars (siehe Abbildung 9.11). Sie können sie nach wie vor ziehen, auswählen, anklicken und löschen, aber sie geraten nicht in die Entwurfsansicht Ihres Formulars. Es gibt eine Vielzahl dieser Entwurfszeit-Steuerelemente, aber ich werde in diesem Abschnitt nur ein paar der häufiger verwendeten erörtern: die Steuerelemente Timer, NotifyIcon, ErrorProvider und Dialog.
302
Unsichtbare Steuerelemente
Abbildung 9.11: In Visual Studio .NET werden unsichtbare Steuerelemente in einem besonderen Bereich des Designers platziert, um Überlappungen mit den tatsächlichen Elementen der Benutzeroberfläche zu vermeiden.
Timer Das Steuerelement Timer soll es Ihnen ermöglichen, Code in bestimmten Zeitintervallen auszuführen. Wenn dieses Steuerelement aktiviert ist, löst es einfach in regelmäßigen Intervallen sein eigenes Tick-Ereignis aus. Indem Sie Code in einen Behandler für dieses Tick-Ereignis setzen, können Sie jede beliebige Aufgabe regelmäßig ausführen. Die Verwendung des Steuerelements Timer umfasst nur ein paar Schritte: 1. Nachdem Sie dieses Steuerelement zu Ihrem Formular hinzugefügt haben, müssen Sie seine Eigenschaften setzen. Setzen Sie Enabled auf True, um anzuzeigen, dass es aktiv sein soll, und die Eigenschaft Interval auf die Zeitspanne (in Millisekunden, 1000ms entspricht 1s), die zwischen den Ereignissen vergehen soll. 2. Fügen Sie Ihren Code zum Ereignis Tick hinzu (klicken Sie das Steuerelement Timer doppelt an, um schnell auf diesen Ereignisbehandler zuzugreifen). Das ist schon alles. Der Code, den Sie im Tick-Ereignisbehandler platziert haben, wird einmal alle Interval-Millisekunden ausgeführt. Wenn Sie folgendermaßen vorgehen, können Sie dieses Steuerelement in einem eigenen Projekt ausprobieren: 1. Erzeugen Sie ein neues leeres Projekt, eine Windows-Anwendung. Ein leeres Formular namens Form1 wird zum Projekt hinzugefügt. Stellen Sie sicher, dass Sie die Design-Ansicht dieses leeren Formulars betrachten.
303
Mit Windows Forms eine Benutzeroberfläche erstellen
2. Ziehen Sie ein Timer-Steuerelement von der Werkzeugsammlung (unter Windows Forms) auf das Formular. Es wird als Steuerelement mit dem Namen Timer1 im unsichtbaren Steuerelementbereich (der nun angezeigt wird) hinzugefügt. 3. Klicken Sie Timer1 einmal an, um es auszuwählen und betrachten Sie dann das Eigenschaftsfenster, das Ihnen die Werte Enabled und Interval zeigen sollte, die das Verhalten des Timers steuern. 4. Setzen Sie Enabled auf True und Interval auf 1000 (1 Sekunde). 5. Klicken Sie das Steuerelement Timer doppelt an, um in die Codeansicht zu wechseln, und greifen Sie auf die Ereignisbehandlungsroutine für das Ereignis Tick zu. Fügen Sie den Code im Listing 9.13 zum Ereignisbehandler Tick hinzu. Listing 9.13: Code, der dazu führt, dass sich die Überschrift des Formulars wie eine Uhr verhält 1 2 3 4 5 6
Private Sub Timer1_Tick(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Timer1.Tick Me.Text = DateTime.Now.ToLongTimeString() End Sub
Lassen Sie das Projekt laufen (indem Sie auf (F5) drücken oder STARTEN aus dem Menü DEBUGGEN auswählen) und stellen Sie sicher, dass es sich um das Startprojekt handelt, wenn Sie mehr als ein Projekt geöffnet haben. Wenn das Projekt läuft, sollte das Formular erscheinen und seine Überschrift zeigt sekundengenau die aktuelle Zeit an. Experimentieren Sie mit den verschiedenen Werten von Interval, um zu sehen, wie sich diese Eigenschaft auf die Auslösung des Ereignisses Tick auswirkt. Fügen Sie – damit es lustiger wird – eine Schaltfläche im Formular ein und positionieren Sie diese Codezeile im Ereignis Click: Timer1.Enabled = Not Timer1.Enabled
Lassen Sie das Projekt erneut laufen und klicken Sie ein paar Mal auf die Schaltfläche, um zu sehen, was geschieht.
NotifyIcon Wenn ein Programm die ganze Zeit laufen und dies auch für den Benutzer ersichtlich sein soll, ist es üblich, ein Symbol im Systembereich ganz rechts in der Taskleiste zu positionieren. Das mag nicht der geeignete Ort sein, um jede Anwendung dorthin zu setzen, da dieser Bereich schon recht überfüllt ist, aber für bestimmte Dienstprogramme mit besonderen Zwecken ist es nützlich.
304
Unsichtbare Steuerelemente
Als es noch kein .NET gab, benötigte man für das Hinzufügen eines Symbols zum Systembereich verschiedene Aufrufe der Win32-API und ein Verfahren namens Unterklassenbildung, das die Visual Basic-Anwendung jedoch unstabil machte. In .NET müssen Sie nur das Steuerelement NotifyIcon zu Ihrem Formular hinzufügen und seine Eigenschaften definieren. Die wichtigste Eigenschaft, Icon, können Sie auf jede Symboldatei setzen, um zu steuern, was im Mitteilungsbereich erscheint. Wenn Sie möchten, dass ein Menü erscheint, sobald der Anwender das Symbol mit der rechten Maustaste anklickt, können Sie Ihrem Formular das Steuerelement ContextMenu hinzufügen und die Eigenschaft ContextMenu von NotifyIcon setzen. Seien Sie vorsichtig und nutzen Sie dieses Steuerelement nur, wenn es angemessen ist: Da sehr viele Anwendungen in diesem Bereich ein Symbol setzen, verfügt Windows XP mittlerweile über eine Auto-Hide-Funktion.
ErrorProvider Als weitere tolle Funktion für das Entwerfen von Dateneingabeformularen dient das Steuerelement ErrorProvider: Es gibt Ihnen die Möglichkeit, visuell anzuzeigen (siehe Abbildung 9.12), welchen Steuerelementen auf dem Formular Fehler zugeordnet sind.
Abbildung 9.12: Mit dem Steuerelement ErrorProvider auf Ihrem Formular können Dateneingabefehler dem Anwender leicht angezeigt werden.
Um dieses Steuerelement zu benutzen, fügen Sie es einfach zu Ihrem Formular hinzu und setzen seine Eigenschaften. Im Grunde kommen Sie sogar mit noch weniger Arbeit aus, da die Standardwerte für die meisten Zwecke bereits geeignet sind, es sei denn, Sie möchten ein anderes Symbol verwenden. Rufen Sie die Methode SetError des Steuerelements ErrorProvider immer dann auf, wenn Sie anzeigen möchten, dass einem Steuerelement ein Fehler zugeordnet ist: errorProvider1.SetError(txtStreet, "Invalid Street Address")
Wenn der Fehler behoben wurde, können Sie ihn aus dem ErrorProvider löschen, indem Sie den Fehler auf eine leere Zeichenkette setzen: errorProvider1.SetError(txtStreet, "")
Das Dialogfensterbeispiel (ein Dialogfenster für das Eingeben und Bearbeiten einer Adresse), das weiter oben in dieser Lektion kurz behandelt wurde (und von der Website dieses Buches heruntergeladen werden kann), verwendet dieses unsichtbare Steuerelement in seinem Code und ist ein gutes Beispiel, das Sie sich ansehen sollten.
305
Mit Windows Forms eine Benutzeroberfläche erstellen
Dialog-Steuerelemente Der letzte Typ von unsichtbaren Steuerelementen, den wir in der heutigen Lektion erörtern werden, bezieht sich eigentlich auf eine ganze Gruppe von Steuerelementen, die von Dialogfenstern zum Öffnen und Speichern von Dateien bis zu Dialogfenstern für die Auswahl von Schriftarten reichen. Mit diesen Steuerelementen können Sie eine Vielzahl von Windows-Standarddialogfenstern von Ihrem Programm aus nutzen (siehe Abbildung 9.13), was viel angenehmer ist, als eigene zu erstellen.
Abbildung 9.13: Das Dialogfenster ÖFFNEN handhabt die gesamte Kommunikation mit dem Betriebssystem und dem Dateisystem, ohne dass Sie Code schreiben müssen.
Wie bei allen anderen unsichtbaren Steuerelementen, können Sie das gewünschte Dialogsteuerelement auf Ihr Formular oder in den unsichtbaren Steuerelementbereich ziehen. Nachdem sich eine Instanz des Steuerelements auf Ihrem Formular befindet, beginnen Sie damit, Eigenschaften zu setzen, um das Steuerelement für Ihre Anwendung zu konfigurieren. Ich werde Sie durch ein Beispielprogramm führen, das das ÖFFNEN-Dialogfenster (OPEN FILE) und das SPEICHERN-Dialogfenster (SAVE FILE) benutzt, und eigentlich kann ich der Vollständigkeit halber auch direkt auf die Dialogfenster für Schriftart und Farbe eingehen. Das Formular selbst enthält eine Vielzahl von Steuerelementen, darunter auch eine Rich Text Box, vier Schaltflächen und ein Panel-Steuerelement, das die Schaltflächen enthalten soll. Indem wir ein Panel-Steuerelement auf diese Art benutzen und es so definieren, dass es rechts andockt, und danach das Steuerelement Rich Text Box so definieren, dass es das Formular ausfüllt (Fill the Form), erhalten Sie eine Benutzeroberfläche, die korrekt skaliert wird, wenn Sie die Größe des Formulars ändern oder es sogar maximieren. Ich werde nicht in allen Einzelheiten beschreiben, wie diese Steuerelemente eingerichtet werden, aber Sie können das gesamte Formular von der Website dieses Buches herunterladen. Zusätzlich zu den sichtbaren Steuerelementen habe ich auch die vier Dialogsteuerelemente (OPEN, SAVE, FONT und COLOR) auf dem Formular platziert. Jede der vier Schalt-
306
Unsichtbare Steuerelemente
flächen verwendet eines der Dialogfenster, um eine Datei zu öffnen, zu speichern oder die Schriftart oder die Schriftfarbe zu ändern. Ihr Code wird in den folgenden einzelnen Abschnitten aufgelistet. Beachten Sie, dass ich die Dialogsteuerelemente in den Prozeduren für das Anklicken der Schaltflächen eingerichtet habe, obwohl ich viele dieser Eigenschaften mit dem Eigenschaftsfenster des Dialogsteuerelements hätte setzen können. Das mache ich nur, weil es sich um ein Beispiel handelt; jede Eigenschaft eines Steuerelementes, die während der ganzen Lebensdauer des Formulars gleich bleibt, sollte nur einmal definiert werden: wenn Sie Eigenschaften über das Eigenschaftsfenster bearbeiten. Ich werde nun den Code hinter jeder der vier Schaltflächen erörtern und erklären, wie das Dialogsteuerelement jeweils verwendet wurde.
Das Dialogfenster Open Mit dem Ereignisbehandler für die Schaltfläche OPEN (siehe Listing 9.14) kann der Anwender im Dialogfenster OPEN einen Dateinamen auswählen. Diese Datei wird dann in das Rich Text Box-Steuerelement des Formulars eingelesen. Listing 9.14: Ereignisbehandler für die Schaltfläche OPEN 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
Private Sub btnOpen_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnOpen.Click Dim sNewFile As String Dim trNewFile As System.IO.TextReader If MessageBox.Show("Overwrite current contents?", _ "Open New File", MessageBoxButtons.YesNo, MessageBoxIcon.Question, _ MessageBoxDefaultButton.Button2) = DialogResult.Yes Then With dlgOpen .Filter = "Text files (*.txt)|*.txt" .AddExtension = True .CheckFileExists = True .CheckPathExists = True .InitialDirectory = IO.Path.GetDirectoryName(sCurrentFile) .Multiselect = False If .ShowDialog() = DialogResult.OK Then sNewFile = .FileName If IO.File.Exists(sNewFile) Then trNewFile = New IO.StreamReader(sNewFile) rtfContents.Text = trNewFile.ReadToEnd() sCurrentFile = sNewFile End If End If End With End If End Sub
307
Mit Windows Forms eine Benutzeroberfläche erstellen
Die Zeilen 2 bis 7 bestätigen – nachdem die benötigten Variabeln deklariert wurden, dass der Anwender eine neue Datei laden möchte – und überschreiben daher den aktuellen Inhalt des Textfeldes. Da es sich um eine einfache Ja/NeinFrage handelt, wird diese Frage mit einem MessageBox-Dialogfenster gestellt. Wenn der Anwender mit JA antwortet, machen Sie weiter und überschreiben den aktuellen Inhalt. Dann wird das Dialogfenster OPEN in den Zeilen 8 bis 14 vorbereitet. Es wird ein Filter definiert, um die Dateitypen auf Textdateien zu beschränken. Das Dialogfenster soll zusammen mit dem Dateinamen die Erweiterung zurückgeben, um zu überprüfen, ob der Pfad und die Datei existiert. Es soll im gleichen Ordner beginnen, wie die Datei, die zuletzt geladen wurde. Die Eigenschaft MultiSelect wird auch auf False gesetzt, womit Sie anzeigen, dass der Anwender in der Lage sein soll, nur eine Datei gleichzeitig auszuwählen. Die Zeile 16 zeigt das Dialogfenster an und überprüft, ob das Ergebnis in Ordnung ist. Wenn es das ist, stellen die Zeilen 18 bis 20 wieder sicher, dass die Datei existiert und laden sie dann in einem einzelnen Lesevorgang in das RichText-Feld. In einem Produktionssystem sollte hier noch eine Fehlerbehandlung eingefügt werden, da das Öffnen von Dateien wegen Sicherheitsberechtigungen und aus anderen Gründen eine häufige Fehlerquelle ist.
Das Dialogfenster Save Wenn der Anwender einen Zieldateinamen angeben muss, benötigen Sie das Dialogfenster SAVE. Es ist Teil des Betriebssystems und kennt daher Laufwerke, Ordner, gemeinsam genutzte Dateien, Tastenkombinationen und alle anderen Windows-Merkmale. Das Schreiben eines eigenen Dialogfensters wäre schwierig und würde eine permanente Pflege erfordern, da sich die Dateisystemfunktionen des Betriebssystems mit der Zeit ändern. Durch die Verwendung der eingebauten Dialogfenster lässt sich Ihr System leichter in das Betriebssystem integrieren (siehe Listing 9.15). Listing 9.15: Die Verwendung der eingebauten Dialogfenster 1 2 3 4 5 6 7 8 9 10
308
Private Sub btnSave_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnSave.Click Dim sNewFileName As String Dim swOutput As IO.StreamWriter sNewFileName = sCurrentFile If MessageBox.Show("Save As Different File Name?", _ "Save File", MessageBoxButtons.YesNo, MessageBoxIcon.Question, _
Unsichtbare Steuerelemente
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
MessageBoxDefaultButton.Button2) = DialogResult.Yes Then With dlgSave .FileName = sCurrentFile .CheckFileExists = False .CheckPathExists = True .DefaultExt = "txt" .Filter = "Text files (*.txt)|*.txt" .AddExtension = True .InitialDirectory = IO.Path.GetDirectoryName(sNewFileName) .OverwritePrompt = True .CreatePrompt = False If .ShowDialog() = DialogResult.OK Then sNewFileName = .FileName swOutput = New IO.StreamWriter(sNewFileName) swOutput.Write(rtfContents.Text) swOutput.Close() sCurrentFile = sNewFileName End If End With End If End Sub
Die Verwendung des Dialogfensters SAVE gleicht der des Dialogfensters OPEN. Nachdem der Anwender bestätigt hat, dass er die Datei speichern möchte (Zeile 9), werden die Eigenschaften des Dialogfensters gesetzt, um die korrekte Abbildung zu erzeugen. Für den Anfang wird der Dateiname auf den zuletzt verwendeten Dateinamen gesetzt. Dem Dialogfenster wird mitgeteilt, dass die Datei noch nicht (CheckFileExists = False), der Ordner aber wohl existieren muss (CheckPathExists = True). Standardmäßig wird die Datei als .txt-Datei gespeichert (Zeile 16), aber das Dialogfenster hält den Anwender nicht davon ab, sie in einem anderen Format zu speichern. Die beiden interessanten Optionen OverwritePrompt und CreatePrompt sind auf das Dialogfenster OPEN nicht anwendbar, aber für das Speichern sind sie wichtig. OverwritePrompt steuert, ob das Dialogfenster den Anwender warnt, ehe es ihm erlaubt, die Datei mit dem Pfad und Namen einer bestehenden Datei zu speichern. CreatePrompt bestimmt, ob der Anwender gewarnt werden soll, wenn er das genaue Gegenteil tun will (einen Pfad und Namen verwenden, die noch nicht existieren). Ist das Dialogfenster vorbereitet, wird es angezeigt (Zeile 23). Wenn der Anwender durch Anklicken von OK das Dialogfenster verlässt, wird der Inhalt des Rich-Text-Feldes in die Datei geschrieben (Zeilen 24 bis 28).
309
Mit Windows Forms eine Benutzeroberfläche erstellen
Das Dialogfenster Font Die Namen, die verfügbaren Größen und der Schriftschnitt auf dem Rechner eines Anwenders können eine recht große Informationsmenge bilden. Dieses Dialogfenster erledigt das alles für Sie und bietet dafür ein Interface, das wahrscheinlich in vielen Programmen erscheint, mit denen der Anwender arbeitet. Listing 9.16 zeigt, wie das Dialogfenster FONT verwendet werden kann, damit der Anwender Eigenschaften der Schrift auswählen kann. Listing 9.16: Die Verwendung des Dialogfensters FONT 1 2 3 4 5 6 7
Private Sub btnFont_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnFont.Click dlgFont.Font = rtfContents.Font
8
End Sub
If dlgFont.ShowDialog() = DialogResult.OK Then rtfContents.Font = dlgFont.Font End If
Das Dialogfenster FONT ist einfach zu verwenden: Sie laden lediglich die aktuellen Schrifteinstellungen (Zeile 3) und zeigen dann das Dialogfenster an (Zeile 5). Wenn der Anwender auf OK klickt, übernehmen Sie die neuen Schrifteinstellungen und setzen sie zurück in das Ziel (in diesem Fall das RichtText-Feld, Zeile 6). Das war's, und es funktioniert einwandfrei.
Das Dialogfenster Color Wenn Sie nicht gerade eine fortgeschrittene Funktionalität benötigen, ist das Dialogfenster COLOR genau das, was Sie brauchen, um eine Farbauswahl zu ermöglichen (siehe Listing 9.17). Der Anwender kann mit diesem Dialogfenster eine vordefinierte Standardfarbe auswählen oder eine eigene Farbe mischen. Listing 9.17: Eine Anwendung um eine Farbauswahl erweitern 1 2 3 4 5 6
310
Private Sub btnColor_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnColor.Click dlgColor.Color = rtfContents.ForeColor If dlgColor.ShowDialog() = DialogResult.OK Then rtfContents.ForeColor = dlgColor.Color
Eigene Dialogfenster erstellen
7 8 9
End If End Sub
Das Dialogfenster COLOR funktioniert genau wie das Dialogfenster FONT: Sie laden einfach die aktuellen Farbwerte (Zeile 3), zeigen das Dialogfenster an (Zeile 5) und weisen das Ziel an, die im Dialogfenster ausgewählten Werte zu verwenden – wenn der Anwender auf OK geklickt hat (Zeile 6).
9.7
Eigene Dialogfenster erstellen
Die Klasse MessageBox bietet eine Möglichkeit, einfache Dialogfenster anzuzeigen, die eine Nachricht anzeigen und/oder den Anwender bitten, unter vorgegebenen Optionen (JA/NEIN, OK/ABBRECHEN, ABBRECHEN/WIEDERHOLEN/IGNORIEREN) eine Wahl zu treffen. Aber es kommt vor, dass Sie ein Dialogfenster mit komplexeren Fähigkeiten benötigen. Sie können jedes Windows-Formular in ein Dialogfenster verwandeln, ihm das Aussehen und Verhalten eines Dialogfensters geben und es in Ihrem Programm verwenden. Es gibt diverse unterschiedliche Stadien, die ich erörtern werde, während ich Ihnen zeige, wie Sie ein Dialogfenster für Benutzernamen und Passwort erstellen, das Sie als Login-Bildschirm für eine Anwendung verwenden können. Um das Beispiel in diesem Abschnitt verfolgen zu können, erzeugen Sie ein neues Projekt (des Typs Windows-Anwendung).
Das Dialogfenster erstellen Ein Dialogfenster verhält sich nicht nur anders als ein reguläres Windows-Formular, es sieht auch anders aus. Fügen Sie Ihrem Projekt ein neues Formular mit dem Namen LogonDialog hinzu und rufen Sie es in der Entwurfsansicht auf. Klicken Sie das Formular an und rufen Sie das Eigenschaftsfenster auf. Damit dieses Formular wie ein Dialogfenster aussieht, definieren Sie die folgenden Eigenschaften: 쐽
FormBorderStyle = FixedDialog: Damit kann das Dialogfenster nicht skaliert werden.
쐽
Text = "Logon": Dies ist die Überschrift; Logon ist geeigneter als LogonDialog.
쐽
MaximizeBox und MinimizeBox werden beide auf False gesetzt. Es gibt keine Notwendigkeit, ein Dialogfenster zu minimieren oder zu maximieren.
Nun fügen Sie die benötigten Steuerelemente hinzu, um ein LOGIN-Dialogfenster zu erzeugen: zwei Textfelder (Benutzer-ID und Passwort), zwei Beschriftungen (eine für jedes
311
Mit Windows Forms eine Benutzeroberfläche erstellen
Textfeld) und zwei Schaltflächen (OK und CANCEL). Wie Sie diese anordnen, ist für dieses Beispiel nicht so wichtig, aber die Abbildung 9.14 zeigt Ihnen, wie ich es gemacht habe.
Abbildung 9.14: Dieses Formular wurde skaliert, damit es normal, mit nur wenigen Steuerelementen angezeigt wird.
Benennen Sie die Steuerelemente mit den Benennungskonventionen, die Sie im Verlauf der heutigen Lektion kennen gelernt haben, als lblUserid, lblPassword, txtUserid, txt Password, btnOK und btnCancel. Dann betrachten Sie die Eigenschaften für txtPassword und setzen Sie PasswordChar = "*". Wenn PasswordChar gesetzt ist, wird jeder Text, der in das Feld eingegeben wird, als Zeichenkette von *-Zeichen angezeigt. Da nun die beiden Schaltflächen zum Formular hinzugefügt wurden, können Sie zu den Eigenschaften des Formulars zurückkehren und zwei Eigenschaften definieren, die Sie ohne verfügbare Schaltflächen nicht definieren konnten. Setzen Sie AcceptButton und CancelButton auf btnOK und btnCancel. Wenn der Anwender in diesem Dialogfenster auf (¢) drückt, ist das Ergebnis dasselbe, als ob er auf die Schaltfläche OK klicken würde. Wenn der Anwender auf die (Esc)-Taste drückt, hat das die gleiche Wirkung wie das Anklicken der Schaltfläche CANCEL.
Das Ergebnis des Dialogfensters einrichten Wenn das Dialogfenster angezeigt wurde und der Anwender es durch Anklicken von OK oder CANCEL geschlossen hat, muss Ihr Programm zwei Dinge herausfinden: erstens, ob der Anwender OK oder CANCEL angeklickt hat, und zweitens, was der Anwender in den Feldern für Benutzerkennung und Passwort eingegeben hat. Die erste Informationen, welche Schaltfläche gedrückt wurde, ist ähnlich wie das, was ein Aufruf an MessageBox.Show() zurückgibt. Ihr neues Dialogfenster behandelt diesen Punkt auch auf die gleiche Art. Es gibt eine Formulareigenschaft DialogResult, deren Wert an das Programm zurückgegeben wird, das die MessageBox angezeigt hat. Sie können diese Eigenschaft mit einer einzelnen Codezeile wie der folgenden definieren: Me.DialogResult = DialogResult.Cancel
Es gibt aber noch eine andere Methode, diesen Ergebniswert zu definieren, und zwar indem Sie die Eigenschaft DialogResult von btnOK und btnCancel definieren (mit OK bzw. Cancel). Wenn diese Werte gesetzt sind, wird das Dialogfenster automatisch geschlossen, wenn der Anwender die eine oder andere Schaltfläche anklickt und das Ergebnis des Dia-
312
Eigene Dialogfenster erstellen
logfensters wird auf den Dialogergebniswert der Schaltfläche gesetzt. Es ist alles eine Sache der Steuerung und der Stelle, an der Sie Validierung einplanen. Wenn Sie die Eigenschaft CausesValidation von txtUserid, txtPassword und btnOK auf True gesetzt haben (und die von btnCancel auf False), dann können Sie mit dem Ereignis Validating von txtUserid und txtPassword die Eingabe des Anwenders überprüfen. Alternativ können Sie die gesamte Überprüfung im Click-Ereignis von btnOK vornehmen. In diesem Fall sollte btnOK.DialogResult auf None gesetzt sein, sodass das Dialogfenster nicht automatisch geschlossen wird, wenn die Schaltfläche angeklickt wird. Im Code können Sie das Formular immer selbst schließen, indem Sie die Eigenschaft DialogResult direkt setzen. Bei beiden Methoden können Sie die Eigenschaft DialogResult von btnCancel auf Cancel gesetzt lassen, da keine Validierung erfolgen soll, wenn der Anwender auf Cancel klickt. Welche der beiden Methoden ist besser? Bei einem Formular mit zwei Textfeldern ist es wahrscheinlich egal, aber wenn Sie ein größeres Dialogfenster mit viel Validierungsaufwand haben, dann sollten Sie die Validierungsereignisse und die Eigenschaft CausesValidation verwenden. Das Listing 9.18 zeigt den Code für das Ereignis Click von btnOK, bei dem die in die beiden Dialogfenster eingegebenen Daten validiert werden und die Dialogergebniseigenschaft des Formulars definiert wird. Dieser Code geht davon aus, dass für jedes Element im Formular CausesValidation gleich False ist und dass die Eigenschaft DialogResult von btnOK gleich None ist. Listing 9.18: Validierungseigenschaften verwenden 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
Private Sub btnOK_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnOK.Click Dim sUserID As String Dim sPassword As String sUserID = txtUserid.Text sPassword = txtPassword.Text If sUserID.Trim() = "" Then MessageBox.Show("UserID cannot be blank," _ & " please enter a proper UserID.", "Error", _ MessageBoxButtons.OK, MessageBoxIcon.Error) txtUserid.Select() Else If sPassword.Trim() = "" Then MessageBox.Show("Password cannot be blank, " & _ "please enter a proper Password.", "Error", _ MessageBoxButtons.OK, MessageBoxIcon.Error) txtPassword.Select()
313
Mit Windows Forms eine Benutzeroberfläche erstellen
20 21 22 23 24
Else Me.DialogResult = DialogResult.OK End If End If End Sub
Die Routine Click führt keine komplexe Validierung durch, sie überprüft nur, ob der Anwender eine Benutzerkennung und ein Passwort eingegeben hat (Zeilen 9 und 15) und setzt in diesem Fall das Dialogfensterergebnis auf OK (Zeile 21). Das Setzen der Eigenschaft DialogResult des Formulars in der Zeile 21 schließt das Dialogfenster, um das Ergebnis zu verbergen und es an das aufrufende Programm zurückzugeben. Beachten Sie, dass das Dialogfenster am Ende dieses Click-Ereignisses unabhängig vom eingegebenen Benutzer und Passwort geschlossen worden wäre, wenn btnOK.DialogResult (mit dem Eigenschaftsfenster) auf OK gesetzt worden wäre. Setzen Sie die Eigenschaft btnCancel.DialogResult im Eigenschaftsfenster auf Cancel (sodass immer ein Cancel verursacht wird und keine Ereignisbehandlung notwendig ist) und dann ist dieses LOGIN-Dialogfenster fertig.
Das Dialogfenster anzeigen Um unser neues LogonDialog zu nutzen, müssen Sie es anzeigen lassen. Dieses Formular als Startup-Objekt Ihres Projekts einzurichten wäre nicht sinnvoll, da dann zwar das Dialogfenster ausgeführt würde, aber die Anwendung enden würde, sobald der Anwender eine Benutzerkennung und ein Passwort eingegeben hat. Sie müssen LogonDialog aufrufen, ehe Sie Form1, das Startobjekt für dieses Projekt, anzeigen. Genau wie Schaltflächen haben Formulare Ereignisse. Ein bestimmtes Formularereignis, nämlich Load, ist für diesen Zweck perfekt geeignet, da es aufgerufen wird, ehe das Formular angezeigt wird. Sehen Sie sich den Code für Form1 an und wählen Sie BASISKLASSENEREIGNISSE aus dem linken Dropdown-Menü oben im Codebearbeitungsfenster aus. Durch das Auswählen dieser Option wird im rechten Dropdown-Menü die Liste der Ereignisse angezeigt, die dieses Formular unterstützt. Wählen Sie aus dem rechten Dropdown-Menü LOAD aus und schon bearbeiten Sie die Ereignisprozedur Form1_Load. In dieser Prozedur müssen Sie eine Instanz Ihres LogonDisplay erzeugen, es als Dialogfenster anzeigen und dann die Benutzer-ID und das Passwort überprüfen, die der Anwender eingegeben hat. Listing 9.19 zeigt ein Beispiel, wie diese Ereignisprozedur geschrieben werden könnte.
314
Eigene Dialogfenster erstellen
Listing 9.19: Ein Formular als Dialogfenster anzeigen 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
Private Sub Form1_Load(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles MyBase.Load Dim frmLogon As New LogonDialog() Dim bLogonSuccessful As Boolean = False Dim sFailureMessage As String If frmLogon.ShowDialog() = DialogResult.OK Then If frmLogon.txtUserid.Text = "Duncan" Then If frmLogon.txtPassword.Text = "password" Then bLogonSuccessful = True Else sFailureMessage = "Invalid Password!" End If Else sFailureMessage = "Invalid User ID!" End If Else sFailureMessage = "Logon Attempt Cancelled" End If If Not bLogonSuccessful Then MessageBox.Show(sFailureMessage, "Logon Failed", _ MessageBoxButtons.OK, MessageBoxIcon.Error) Me.Close() End If End Sub
Es wird eine neue Instanz von LogonDialog erzeugt (Zeile 3), die benötigt wird, bevor Sie dieses Formular anzeigen können. Es werden zwei Variablen verwendet (Zeilen 4 und 5), um den Erfolg oder Misserfolg des Login-Versuchs zu verfolgen. Zeile 7 nutzt die Methode ShowDialog des Formulars, um es modal anzuzeigen. Durch die Verwendung von ShowDialog wird die nächste Codezeile in dieser Prozedur nicht ausgeführt, bis das Formular verborgen oder geschlossen wird. Wenn Show allein verwendet worden wäre, wäre das Formular nicht-modal angezeigt worden und die Ausführung des Codes wäre fortgesetzt worden, ehe der Anwender den Login-Vorgang abgeschlossen hätte. ShowDialog gibt, wie die Methode Show der Klasse MessageBox, einen DialogResult-Wert zurück. Das haben Sie schon gesehen, als Sie den LogonDialog erstellt haben. Die Zeile 7 überprüft den zurückgegebenen Wert, denn wenn
der Anwender den Dialog abbricht, sollen die Benutzerkennung und das Passwort nicht verarbeitet werden. Dann wird auf die Textfeld-Steuerelemente auf
315
Mit Windows Forms eine Benutzeroberfläche erstellen
dem LogonDisplay zugegriffen und ihre Werte werden mit der fest eingegebenen Benutzerkennung und dem Passwort geprüft (Zeilen 9 und 10). In Ihren echten Programmen werden Sie diese Werte wahrscheinlich anhand irgendeiner Sicherheitsliste oder Datenbank überprüfen. Wenn schließlich alles korrekt ist, wird bLogonSuccessful auf True gesetzt (Zeile 11) oder die entsprechende Misserfolgmeldung wird in sFailureMessage gesetzt (Zeilen 11 bis 19). bei gescheitertem Login wird die Nachricht über den Fehlschlag in einer MessageBox angezeigt (Zeile 23) und Form1 wird geschlossen (Zeile 25), um den Zugriff auf das Programm zu verhindern.
9.8
Zusammenfassung
Windows-Formulare werden verwendet, um .NET-Anwendungen mit einem WindowsInterface zu erstellen. Dies ist eine der beiden Hauptarten von Anwendungen, die Sie erstellen werden (die andere sind die webbasierten Systeme). Mit dem Windows-FormsSystem können Sie jede Oberfläche erstellen, die Ihre Anwendung benötigt, da es über viele wichtige Verbesserungen gegenüber früheren Versionen von Visual Basic verfügt und ein allgemeines System für die Entwicklung von Formularen über die gesamte Breite von .NET bietet. Tag 16 wird näher auf dieses Thema eingeben und mehr Informationen über die Verwendung dieser Formulare in Ihren Programmen liefern.
9.9 F
Kann ich mehr als ein Windows-Formular in einem Projekt oder einer Anwendung haben? A
F
Fragen und Antworten
Ja, sogar beliebig viele. Aber nur ein Formular kann das Startformular sein, sodass Sie Instanzen der anderen Formulare in Ihrem Code erzeugen und dann deren Show-Methode aufrufen müssen, wenn Sie sie anzeigen möchten.
Ich möchte eine MessageBox von einer Console-Anwendung verwenden, die ich erstellt habe, aber es funktioniert nicht. Sie scheint den Klassennamen nicht zu erkennen. A
MessageBox ist ein Teil des Namensraums System.Windows.Forms, sodass
Ihnen Namensräume zur Verfügung stehen müssen, ehe Sie diese Klasse benutzen können. Wenn Sie mit Visual Studio eine Windows-Anwendung erstellen, fügt es die notwendige Referenz zwar automatisch ein, aber nicht bei einer Console-Anwendung. Benutzen Sie den Projektmappen-Explorer, klicken Sie mit der rechten Maustaste auf den REFERENCES-Ordner in Ihrem Projekt und wählen Sie
316
Workshop
VERWEIS HINZUFÜGEN aus. Wählen Sie in der Liste des erscheinenden Dialogfensters System.Windows.Forms.dll aus. Eine andere gute Idee ist es, die Zeile Imports System.Windows.Forms oben in Ihrem Modul oder Ihrer Klassendatei einzufügen, sodass Sie auf MessageBox verweisen können, ohne dass Sie jedes Mal System. Windows.Forms davor setzen müssen.
9.10 Workshop Der Workshop soll Ihnen dabei helfen, mögliche Fragen vorherzusehen und das Gelernte zu wiederholen. Außerdem soll er Sie dazu anregen, über die praktische Umsetzung Ihres Wissens nachzudenken. Die Antworten und Lösungen finden Sie im Anhang A.
Quiz 1. Was ist der Unterschied zwischen einem modalen und einem nicht-modalen Formular? 2. Warum sollten Sie die Eigenschaft CausesValidation der Schaltfläche CANCEL nicht auf True setzen? 3. Welche Anweisung wird am Ende einer Prozedurdeklaration eingefügt, um anzuzeigen, dass es sich um einen Ereignisbehandler handelt?
Übung Wenn eine einzelnen Prozedur mehrere Ereignisse behandeln kann (Sub myhandler() Handles btnOpen.Click, btnClose.Click, btnSave.Click), wie würden Sie diese drei Ereignisprozeduren in eine einzelne Prozedur umschreiben? Hinweis: Sie müssen den Senderparameter (mit CType) in einen System.Windows. Forms.Control oder System.Windows.Forms.Button umwandeln, ehe Sie auf die reguläre Menge von Steuerelementeigenschaften zugreifen können. Listing 9.20: Mit dem Schlüsselwort Handles eine einzelne Prozedur erstellen, die für mehrere Ereignisse aufgerufen wird 1 2 3 4
Private Sub btnCopy_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnCopy.Click Dim sSource As String Dim sDestination As String
317
Mit Windows Forms eine Benutzeroberfläche erstellen
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
318
sSource = txtSource.Text() sDestination = txtDestination.Text() File.Copy(sSource, sDestination) End Sub Private Sub btnMove_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnMove.Click Dim sSource As String Dim sDestination As String sSource = txtSource.Text() sDestination = txtDestination.Text() File.Move(sSource, sDestination) End Sub Private Sub btnDelete_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles btnDelete.Click Dim sSource As String sSource = txtSource.Text() File.Delete(sSource) End Sub
Mit Web Forms eine Benutzeroberfläche erstellen
Mit Web Forms eine Benutzeroberfläche erstellen
Heute werden immer mehr Anwendungen als Browseranwendungen geschrieben. Vor Visual Basic .NET war es schwierig, diese Browseranwendungen mit Visual Basic zu erstellen, aber mit Visual Basic .NET sind sie genauso leicht zu erstellen wie traditionelle Windows-Anwendungen. Heute werden Sie sich ansehen, wie Sie Benutzeroberflächen für Browser erstellen können. Die Werkzeuge in Visual Basic .NET helfen dem Entwickler dabei, Webseiten zu erstellen, die sehr hübsche Benutzeroberflächen in jedem Browsertyp bieten. Insbesondere wird sich die heutige Lektion auf die folgenden Themen konzentrieren:
Themen heute 쐽
Wie sich das Web-Programming-Modell von dem traditionellen Windows-basierten Modell unterscheidet.
쐽
Die Verwendung von Standard-Web-Forms-Steuerelementen
쐽
Die Verwendung von fortgeschrittenen Web-Forms-Steuerelementen
쐽
Die Verwendung von Validator-Steuerelementen
10.1 Das Web-Programming-Modell An manchen Tagen habe ich das Gefühl, dass sich die ganze Welt im Internet tummelt, besonders dann, wenn meine Zugriffsgeschwindigkeit quälend langsam ist, weil jeder browst, chattet und mailt (aber nicht arbeitet). (Ein kleiner Tipp: Irgendwie ist während der Sendung »Raumschiff Enterprise« die beste Zeit, online zu sein – vielleicht nur ein Zufall.) Ganz offensichtlich ist einer der wichtigsten oder zumindest beliebtesten Teile des Internets das World Wide Web (WWW oder einfach Web). Dennoch gab es im Allgemeinen einen Mangel an wirklich guten Programmierwerkzeugen für das Erstellen von »Webprogrammen« statt einfacher Webseiten. Das liegt zumindest teilweise daran, dass sich das Erstellen von Anwendungen für das Web vom Erstellen von Anwendungen für einen Desktop-Computer unterscheidet, bei dem Sie mehr Kontrolle haben. Außerdem müssen Webanwendungen häufiger mit dem Netzwerk umgehen. Was ist also das »Web-Programming-Modell«? Es ist nur ein Begriff, um zu beschreiben, wie Sie ein Programm entwerfen oder aufbauen können, das Webseiten in einem Browser nutzt, damit der Anwender Informationen eingeben kann. Diese Webseiten werden mit HTML (HyperText Markup Language) entworfen. Dieses Buch wird sich nicht mit HTML beschäftigen, aber es gibt jede Menge Bücher auf dem Markt, die das tun. Ein Browser ist eine Anwendung, die HTML lesen und auf dem Bildschirm anzeigen kann. Normalerweise (aber nicht immer) wird der Großteil der Arbeit einer Anwendung
320
Das Web-Programming-Modell
auf dem Webserver gemacht. Der Webserver ist ein weiteres Programm, das auf einem Computer läuft und das weiß, wie HTML auf Anfrage zurückgegeben wird. Bei Windows NT und 2000 heißt dieses Programm Internet Information Server (IIS). Die Informationen werden zwischen dem Browser und dem Server über ein Protokoll, also eine bestimmte Sprache, ausgetauscht, das HTTP heißt (HyperText Transfer Protocol). Der eigentliche Name von IIS hat sich mit den unterschiedlichen Versionen geändert. In Windows NT 4.0 Server wird er Internet Information Server genannt. In Windows 2000 heißt er Internet Information Services, während er in Windows NT 4.0 Professional als Personal Web Server bezeichnet wird. In der Frühzeit des World Wide Web waren Webseiten statisch. Das bedeutet, dass sie sich nie wirklich verändert haben. Alles wurde interessanter, als man damit begann, dynamische, d.h. sich verändernde, Webseiten zu erstellen. Das war die Geburtsstunde des Webprogramms. Mit Webprogrammen kann der Webserver einige Aufgaben ausführen und je nach Ergebnis entsprechenden HTML-Code zurückgeben, statt nur jedes Mal den gleichen HTML-Code zurückzugeben. Der Anwender kann z.B. Verkaufszahlen für einen bestimmten Zeitraum abfragen. Diese Information wird an den Server übergeben. Der Server schlägt wiederum diese Informationen in einer Datenbank nach und schreibt das HTML, um es dem Anwender anzuzeigen. Der ganze Prozess sieht so ähnlich aus wie in der Abbildung 10.1.
Anfrage Lookup Antwort
Datenbank Webserver Browser
Abbildung 10.1: Das Web-Programming-Modell
Alternativ kann der Server (oder der Webdesigner) der Seite selbst Programminformationen hinzufügen und so eine Seite erzeugen, die selbst ein kleines Programm ist. Das wird häufig Dynamic HTML (oder DHTML) genannt. Mit DHTML enthält die Seite JavaScript oder eine andere Sprache. Der Code kann im Browser laufen, ohne dass er Informationen an den Server zurückschicken muss. Die Abbildung 10.2 zeigt dieses Modell in Aktion. Es gibt eine Vielzahl von Techniken, mit denen ein Webprogramm erstellt werden kann. Einige der häufiger verwendeten Techniken, die in der Vergangenheit verwendet wurden, sind ASP (Active Server Pages), Perl (eine weitere Programmiersprache) oder JSP (JavaServer Pages). Die Technik, die von Web Forms genutzt wird, ist eine Verbesserung von ASP, nämlich ASP.NET.
321
Mit Web Forms eine Benutzeroberfläche erstellen
Anfrage Lookup Antwort
Datenbank Webserver Browser
Aktualisieren
Abbildung 10.2: Das Dynamic HTML-Programmiermodell
ASP.NET ASP.NET ist Microsofts Bezeichnung für seine verbesserte Version von ASP. ASP war zwar eine einfache Methode zum Erstellen dynamischer Webseiten, jedoch hatte es ein paar Probleme, die ASP.NET gelöst hat: 쐽
ASP benötigte häufig zu viel Code, um etwas zu vollbringen. ASP.NET benötigt weniger Code (häufig viel weniger) für die gängigen Programmieraufgaben.
쐽
ASP litt wie HTML immer noch unter der beschränkten Anzahl von Steuerelementen. ASP.NET erweiterte es um die Idee von »serverseitigen Steuerelementen«, die entsprechend dem Browser, der sie anfragt, HTML generieren können. Diese Steuerelemente sind zwar im Browser nur HTML, sie können aber viel HTML und Code darstellen und ersparen sich so das Tippen.
쐽
ASP ließ nur Programmierung in einer Sprache wie VBScript zu. VBScript wird zur Laufzeit interpretiert und nicht wie echtes Visual Basic kompiliert. Mit ASP.NET können Sie Webseiten in echtem, voll kompiliertem Visual Basic .NET schreiben.
ASP.NET soll außerdem andere Probleme mit ASP lösen, die für diese Erörterung des Erstellens von Benutzeroberflächen nicht bedeutend sind, wie z.B. Verbesserungen der Skalierbarkeit und Speichernutzung.
Wie sich die Webprogrammierung von der WindowsProgrammierung unterscheidet Wenn Sie mit Web Forms ein webbasiertes Programm entwerfen, gibt es eine Reihe von Unterschieden, an die Sie denken müssen. Dazu gehören auch diese: 쐽
Webbasierte Anwendungen haben meist mehr Code auf dem Server statt auf dem Client. Das bedeutet, dass das Look&Feel Ihres Programms vom Browser kommt, aber die »Intelligenz« befindet sich auf dem Server.
쐽
Webbasierte Anwendungen sind von den Fähigkeiten des Browsers abhängig. Leider haben alle Browser unterschiedliche Fähigkeiten und viele Webentwickler mussten mit diesen Unterschieden kämpfen, als sie ihre Programme entwarfen.
322
Das Web-Programming-Modell
쐽
Wenn Sie eine Webseite empfangen, ist sie meist statisch. Es gibt zwar Möglichkeiten, die Seite zu aktualisieren, ohne zum Server zurückzukehren (d.h. sie dynamisch zu machen), diese Methoden machen das Erstellen der Seite aber komplexer. Daher ist es bei webbasierten Anwendungen schwieriger, animierte Formulare (oder jede Art von Antwort an den Anwender) zu erzeugen.
쐽
Viele Operationen einer webbasierten Anwendung erfordern eine »Netzwerkrundreise«. Das liegt an der Trennung von Code und Design. Damit eine Schaltfläche oder ein anderes Steuerelement etwas tut, ist es häufig notwendig, Informationen an den Server zu senden. Der Sender reagiert dann entsprechend. Sie sollten dies beachten, wenn Sie Webanwendungen erstellen. Dieses Hin und Her kann eine Weile dauern, sodass es durchgeführt werden sollte, wenn es notwendig ist.
Webbasierte Anwendungen sind eingeschränkt, sowohl durch die Beschränkungen des Browsers selbst als auch durch die Anzahl der Browser auf dem Markt. Browser sind in der Art der Steuerelemente, die verwendet werden können, ebenso eingeschränkt wie durch ihre geringen Zeichenfähigkeiten – d.h., Sie können normalerweise nicht auf einer Webseite zeichnen. Wenn der Anwender außerdem eine ältere Version eines Browsers installiert oder bestimmte Funktionen deaktiviert hat, kann die Webseite unterschiedlich reagieren. Das ist einer der Hauptgründe, warum webbasierte Anwendungen meistens den Großteil des Codes auf dem Server platzieren. Außerdem bedeutete das, dass traditionelle webbasierte Anwendungen es erforderlich machten, viel Code hinzuzufügen, um das Aussehen der Webseite je nach Browser zu ändern. Zum Glück halten die Web-Forms-Steuerelemente die meisten dieser Einzelheiten vor Ihnen verborgen. Sie wurden geschrieben, um eine dynamische Ausgabe zu erzeugen (d.h., die Seite kann sich ändern, ohne Informationen an den Server zurückschicken zu müssen), wenn die Steuerelemente feststellen, dass der Browser dynamische Ausgabe verwenden kann. Wenn sie den verwendeten Browser nicht erkennen oder wenn der Browser eine dynamische Aktualisierung nicht unterstützt, wird an den HTML-Browser nur einfacher HTML-Code zurückgegeben. So ist sichergestellt, dass der Client-Browser die entworfene Webseite empfängt, allerdings mit den Beschränkungen des Browsers. Außer den browserspezifischen Beschränkungen muss der Entwickler webbasierter Anwendungen auch daran denken, dass Client und Server getrennt sind, möglicherweise durch große Distanzen und ein Netzwerk. Das bedeutet, dass Operationen, die nur ein paar Sekunden in Anspruch nehmen würden, wenn Client und Server sich sehr nahe wären (oder sogar auf dem gleichen Rechner befänden), viel Zeit benötigen können. Daher können Animationen unsauber laufen oder gar nicht angezeigt werden, bis der Download vollständig ist. Auch die Verbindungsgeschwindigkeit kommt hier ins Spiel. Wenn Sie mit einem langsamen Modem auf die Webseite zugreifen, wird dieser Unterschied sogar noch bedeutender.
323
Mit Web Forms eine Benutzeroberfläche erstellen
Bei all diesen Punkten fragen Sie sich vielleicht: »Warum sollte ich eine Webanwendung erstellen?« Das Erstellen von Webanwendungen hat zwar seine Kehrseiten, aber auch viele Vorteile: 쐽
Installation: Um Ihre Anwendung verfügbar zu machen, müssen Sie nur jemanden auf die URL verweisen. Die Anwendungen ist vom Client sofort verwendbar. Das erspart Ihnen, jeden dieser Client-Rechner zu besuchen oder Ihre Anwender alle die Anwendung installieren zu lassen.
쐽
Neue Versionen und/oder Bug-Fixes: Wenn Sie einen Teil Ihrer Anwendung auf eine neuere Version aufrüsten möchten, müssen Sie die Upgrades nur auf dem Server installieren, nicht auf jedem Client.
쐽
Leistung: Die Leistung von Webanwendungen zu verbessern ist viel einfacher als die Leistung von regulären Anwendungen zu verbessern. Sie können die Leistung von Webanwendungen verbessern, indem Sie mehr Server nutzen und die Anfragen auf die Server verteilen.
쐽
Wissen: Wenn Sie schon ein wenig HTML kennen, können Webanwendungen viel einfacher zu erstellen sein als Windows-Anwendungen. Wenn Sie weder HTML noch Windows kennen, sind sie leichter zu erlernen.
Wenn Sie also eine Anwendung entwerfen, sollten Sie dann ein Windows-basiertes oder ein webbasiertes Programm erstellen? Die einfache (und unbefriedigende) Antwort lautet: »Es kommt drauf an.« Bei vielen Anwendungen sind beide Arten geeignet, aber man beginnt damit, mehr webbasierte Anwendungen zu erstellen. Die Fähigkeit, ganz einfach Upgrades und Bug-Fixes zur Verfügung zu stellen ist verlockend, sodass Sie vielleicht wenigstens in Erwägung ziehen sollten, Ihre Programme zuerst als Webanwendungen zu erstellen. Einige Anwendungen sind aber als Webanwendungen ungeeignet, z.B. alle Programme, die eine beständige Verbindung zwischen Client und Server erfordern. Das Gleiche gilt für Anwendungen, die viele Grafiken benötigen (z.B. Spiele). Wenn es schließlich nur um einen einzigen Computer geht (d.h. keine Client/Server-Anwendung wie ein Datenbankprogramm) oder wenn die Anwendung nur für Sie ist, ist es eventuell sinnvoll, eine Windows-basierte Anwendung zu erstellen.
10.2 Standardsteuerelemente von Web Forms verwenden Das Entwerfen einer Webseite mit Visual Basic .NET hat Ähnlichkeit mit dem Entwerfen einer normalen Visual Basic .NET-Anwendung. Der einzige Unterschied liegt darin, was hinter den Kulissen geschieht. Statt Code hinzuzufügen, um die Steuerelemente zu erzeu-
324
Standardsteuerelemente von Web Forms verwenden
gen und ihre Eigenschaften zu definieren, werden zu der ASP.NET-Seite HTML-Tags hinzugefügt und es wird Code zu einer Visual Basic .NET-Datei hinzugetan, die im Hintergrund arbeitet. Die Steuerelemente, die Ihnen für die Erstellung einer Webanwendung zur Verfügung stehen, ähneln denen, die in Windows-Anwendungen zur Verfügung stehen. Dazu gehören alle gängigen Steuerelemente, mit denen Sie vertraut sind (siehe Abbildung 10.3). Die Tabelle 10.1 beschreibt diese Steuerelemente kurz.
Abbildung 10.3: Standard-Steuerelemente für Web Forms Steuerelement
Beschreibung
Label
Sie verwenden dieses Steuerelement, um Text im Webformular zu platzieren. Sie können auch einfach das Webformular anklicken und mit dem Tippen beginnen. Mit Label haben Sie eine bessere Kontrolle über das Formatieren und es ermöglicht Ihnen, den Text an einer beliebigen Stelle zu platzieren. Schließlich ermöglicht Ihnen das Steuerelement Label auch, den Inhalt in Ihrer Anwendung dynamisch zu ändern. Dies ist bei Text, der zum Formular hinzugefügt wurde, nicht möglich.
TextBox
Sie verwenden dieses Steuerelement, damit der Anwender ein Feld hat, in das er Informationen eingeben kann. Dies wird das Steuerelement sein, das am häufigsten zu einer Webanwendung hinzugefügt wurde.
Button
Sie verwenden dieses Steuerelement, damit der Anwender etwas hat, das er anklicken kann, um eine Aktion auszuführen.
Tabelle 10.1: Standardsteuerelemente von Web Forms
325
Mit Web Forms eine Benutzeroberfläche erstellen
Steuerelement
Beschreibung
LinkButton
Die Aktion ähnelt dem normalen Steuerelement Button, aber LinkButton ist etwas, das die Anwender Ihrer Webanwendung anklicken können. Der Unterschied ist, dass Button wie eine Schaltfläche aussieht, während LinkButton ein Hyperlink ist. (D.h. der Anwender sieht irgendwo einen hübschen, blau unterstrichenen Verweis.)
ImageButton
Die Aktion ähnelt dem normalen Steuerelement Button, aber ImageButton ist etwas, das Ihre Anwender anklicken können, um eine Aktion durchzuführen. Der Unterschied ist, dass ImageButton eine Grafik ist, die Ihre Anwender anklicken können.
Hyperlink
Dieses Steuerelemente ähnelt LinkButton, nur dass LinkButton ein ClickEreignis hat und Hyperlink nicht. Das bedeutet, dass Sie nur Code schreiben können, um mit LinkButton-Klicks umzugehen, während Hyperlink nur genutzt werden kann, um den Anwender woanders hinzuschicken.
DropDownList
DropDownList-Steuerelemente sind in Webformularen recht gängig. Sie sind eine Liste, die anfangs nur eine einzelne Zeile einnimmt. Sie können den Dropdown-Pfeil anklicken, um sie zu öffnen und die gesamte Liste zu sehen. Wenn Sie ein Element aus der Liste auswählen, wird die Liste wieder geschlossen und es wird nur eine einzelne Zeile angezeigt, die Ihre Auswahl enthält. Sie benutzen diese Steuerelemente, wenn Ihr Anwender aus einer Liste ein einzelnes Element aussuchen muss, und wenn Sie Platz auf dem Bildschirm sparen möchten – wenn z.B. ein Land oder ein Länderkennzeichen ausgewählt werden soll.
ListBox
Mit ListBox-Steuerelementen kann ein Anwender eines oder mehrere Elemente aus einer Liste mit Auswahlmöglichkeiten auswählen. Sie unterscheiden sich von DropDownList dadurch, dass die Liste immer sichtbar ist. Ein weiterer Unterschied ist, dass Sie in einer ListBox mehrere Elemente auswählen können. Verwenden Sie ListBox, wenn Sie diese Fähigkeit benötigen (vergleiche aber auch CheckBoxList), wenn der Anwender die Auswahlmöglichkeiten immer sehen soll oder wenn es ausreichend Platz auf dem Bildschirm gibt.
CheckBox
Die CheckBox stellt die Antwort auf eine Ja- oder Nein-Frage dar. Sie ist entweder markiert oder unmarkiert und wird daher verwendet, wenn der Anwender eine Option entweder auswählen soll oder nicht. Sie unterscheidet sich von RadioButton, da die CheckBox unabhängig von anderen CheckBox-Steuerelementen ausgewählt werden kann, während RadioButton-Elemente sich gegenseitig ausschließen.
Tabelle 10.1: Standardsteuerelemente von Web Forms (Forts.)
326
Standardsteuerelemente von Web Forms verwenden
Steuerelement
Beschreibung
CheckBoxList
Die CheckBoxList ist eine Reihe von CheckBox-Steuerelementen. Jedes ist von den anderen unabhängig, aber das Steuerelement CheckBoxList ist eine handliche Art, um eine Reihe von CheckBox-Steuerelementen zu einer Seite hinzuzufügen. Dieses Steuerelement ist besonders nützlich, wenn Sie eine Sammlung von Elementen haben (die vielleicht von einer Datenbank abgerufen wurden), aus denen der Anwender auswählen kann. Die CheckBoxList ist auch ein handlicher Ersatz für die ListBox, wenn der Anwender mehrere Elemente auswählen soll. Sie sollten aber trotzdem die ListBox verwenden, wenn Sie mehr als ungefähr sechs Elemente haben.
RadioButton
RadioButton ähnelt CheckBox insofern, als der Wert dieses Steuerelementes entweder True oder False ist. Es unterscheidet sich von CheckBox darin, dass RadioButton-Steuerelemente normalerweise »in Rudeln auftreten«. Jede CheckBox in einem Formular kann unabhängig auf True oder False gesetzt werden, aber nur ein einziger RadioButton in einer Gruppe darf auf True gesetzt sein. Sie können sich eine CheckBox also als Ja/Nein-Frage vorstellen, während RadioButton (oder vielmehr eine Gruppe von RadioButtons) eher wie eine Multiple-Choice-Frage ist, bei der nur eine Antwort korrekt ist.
RadioButtonList
Die RadioButtonList ist eine Gruppe von RadioButton-Steuerelementen. Es macht das Erstellen dieser Gruppe einfach, wenn Sie bereits über eine Liste von einer anderen Stelle vefügen (wie z.B. einer Datenbank).
Image
Mit dem Steuerelement Image können Sie eine Grafik auf der Seite platzieren.
Panel
Das Steuerelement Panel ähnelt dem Steuerelement Label, da es nur ein Platzhalter für Text ist. Der Unterschied ist, dass das Steuerelement Panel andere Steuerelemente enthalten kann. Als solches ist es ein tolles Steuerelement, wenn Sie Informationen oder Steuerelemente trennen oder hervorheben müssen. Ähnliche oder verwandte Steuerelemente können in einem Panel-Steuerelement zusammengefasst werden, um es von den anderen Steuerelementen abzuheben.
Tabelle 10.1: Standardsteuerelemente von Web Forms (Forts.)
Genau wie Windows-Steuerelemente nutzen Sie die Web-Forms-Steuerelemente, indem Sie sie in der Werkzeugsammlung doppelt anklicken oder indem Sie sie auf Ihr Formular ziehen. In diesem Fall ist das Formular aber eine Webseite. Wir wollen nun eine einfache Web Forms-Anwendung erstellen, um zu sehen, wie diese Steuerelemente das Schreiben von Webprogrammen erleichtern. Starten Sie die Entwicklungsumgebung, wenn sie nicht bereits läuft, und erzeugen Sie eine neue Webanwendung. Nennen Sie sie Madlib. Sie erstellen mit ihr eine einfache Webanwendung, um einige der Standardsteuerelemente in Aktion zu sehen. Wählen Sie DATEI, NEU und PROJEKT aus, um das Dialogfenster NEUES PROJEKT zu öffnen. Öffnen Sie den Ordner VISUAL BASIC PROJEKTE und wählen Sie dann aus den Projektschablonen
327
Mit Web Forms eine Benutzeroberfläche erstellen
WEBPROJEKT aus. Ändern Sie den Namen des Projekts in Madlib und bestätigen Sie, um das Projekt zu erstellen. Ehe Sie eine Webanwendung erstellen können, sollten Sie zuerst die Internet Information Services (oder den Internet Information Server) installieren oder Zugriff darauf haben. Genau wie bei Windows-basierten Anwendungen müssen Sie als Erstes die Steuerelemente, die Sie in Ihrer Anwendung verwenden werden, anordnen (das Ergebnis sehen Sie in Abbildung 10.4). Sie fügen zunächst eine Grafik zu Ihrer Seite hinzu.
Abbildung 10.4: Das MadlibFormular
Ziehen Sie ein Image-Steuerelement auf das Formular. Anfänglich sollte es wie ein leeres Quadrat aussehen (oder vielmehr wie eine fehlende Grafik), da Sie noch den Pfad zur Grafik setzen müssen. Gehen Sie zum Eigenschaftsfenster und suchen Sie die Eigenschaft IMAGEURL. Wenn Sie im Fenster EIGENSCHAFTEN auf die Eigenschaft IMAGEURL klicken, sollte eine Schaltfläche mit drei Punkten erscheinen. Genau wie bei WindowsAnwendungen bedeutet dies, dass ein Dialogfenster Ihnen beim Setzen dieser Eigenschaft helfen wird. Klicken Sie die Schaltfläche an und suchen Sie eine schöne Grafik. (Ich habe mit dem Zeichenprogramm, das in Windows 2000 enthalten ist, eine erstellt, die das Wort Madlib zeigt.) Klicken Sie auf OK. Nun sollte die Grafik im Formular zu sehen sein. Dass Sie Ihr Programm erklären, gilt als guter Stil. Fügen Sie ein Label-Steuerelement und eine einfache Erklärung in die Eigenschaft Text hinzu. Mein Text lautete:
328
Standardsteuerelemente von Web Forms verwenden
A Mad Lib is a game where one player selects a series of words (usually by type of word, or a description). These words are then plugged into a story at specific spots to create a (hopefully) amusing, personalized end result.
Als Nächstes fügen Sie die Steuerelemente für die verschiedenen Elemente, die Sie einsetzen werden, zur Seite hinzu. Die Tabelle 10.2 beschreibt die Steuerelemente und die verwendeten Eigenschaftseinstellungen. Steuerelement
Eigenschaft
Wert
Label
(ID)
lblName
Text
Your first name:
Font Bold
True
TextBox
(ID)
txtName
Label
(ID)
lblDate
Text
A date:
Font Bold
True
TextBox
(ID)
txtDate
Label
(ID)
lblFruit
Text
A kind of fruit:
Font Bold
True
(ID)
cboFruit
Items
Die DropDownList hat ein Dialogfenster, das Ihnen beim Hinzufügen von Elementen hilft. Klicken Sie auf die Eigenschaft ITEMS und dann auf die erscheinende Schaltfläche mit den frei Punkten. Das entsprechende Dialogfenster sehen Sie in Abbildung 10.5. Fügen Sie eine Reihe von Früchten hinzu, indem Sie auf die Schaltfläche HINZUFÜGEN klicken und setzten Sie dann die Eigenschaft Text. Wiederholen Sie das ungefähr zehnmal Ich habe Mango, Orange, Banana, Currant, Berry, Kumquat, Peach, Kiwi, Apricot und Plum hinzugefügt.
(ID)
lblNumber
Text
A number from 100 to 1000:
Font Bold
True
(ID)
txtNumber
Text
500
(ID)
lblEmotion
Text
An emotional state:
DropDownList
Label
TextBox
Label
Tabelle 10.2: Steuerelemente, die im Madlib-Webformular verwendet werden
329
Mit Web Forms eine Benutzeroberfläche erstellen
Steuerelement
Eigenschaft
Wert
Font Bold
True
(ID)
rlstEmotion
Items
Die Eigenschaft Items der RadioButtonList ähnelt der Eigenschaft Items der DropDownList und hat den gleichen Editor. Fügen Sie ein paar Gemütszustände ein. Ich habe Excited, Frustrated, Intrigued, Saddened, Panicky, Ecstatic, Angry, Jealous, Frightened, Happy, Shocked und Blue hinzugefügt.
RepeatColumns
3
(ID)
lblVerb
Text
A verb:
Font Bold
True
TextBox
(ID)
txtVerb
Label
(ID)
lblOccupation
Text
An occupation:
Font Bold
True
TextBox
(ID)
txtOccupation
Button
(ID)
cmdWrite
Text
Write Story
ID
cmdClear
Text
Clear
(ID)
lblResult
Text
Lassen Sie dieses Feld frei (d.h. löschen Sie den Wert der Eigenschaft Text).
BorderStyle
Groove
Width
75%
RadioButtonList
Label
Button
Label
Tabelle 10.2: Steuerelemente, die im Madlib-Webformular verwendet werden (Forts.)
Außerdem sollten Sie gelegentlich eine neue Zeile beginnen, um die Steuerelemente besser auf der Seite anzuordnen. In Abbildung 10.5 sehen Sie ein Beispiel. Wenn Sie mit HTML vertraut sind, sollten Sie die Steuerelemente in eine Tabelle setzen, um sie noch besser formatieren zu können. Es gibt noch eine weitere Technik, mit der Sie Steuerelemente auf einem Webformular setzen können. Wenn Sie sich die Eigenschaften des Webformulars selbst ansehen (suchen Sie in der Dropdown-Liste der Objekte oben im Fenster EIGENSCHAFTEN nach DOCUMENT), werden Sie eine Eigenschaft sehen, die
330
Standardsteuerelemente von Web Forms verwenden
pageLayout heißt. Standardmäßig ist sie gleich LinearLayout. Die Alternative namens GridLayout kann Ihnen helfen, mächtige Webformulare zu erstellen. Wenn pageLayout auf GridLayout gesetzt ist, können Sie Steuerelemente auf
dem Webformular genauso platzieren wie auf einem Windows-Formular.
Abbildung 10.5: Elemente zur DropDownList hinzufügen
Die meisten der verwendeten Eigenschaften sollten keiner Erklärung bedürfen. Einige wenige müssen wir wahrscheinlich dennoch erklären. Viele Steuerelemente, die mit Listen arbeiten, können an eine Sammlung von Elementen »gebunden« werden. Häufig bedeutet das, dass sie an Informationen angehängt werden können, die von einer Datenbank abgerufen wurden, sie können aber auch an andere Sammlungen, z.B. an Arrays, angehängt werden. Steuerelemente, die dazu fähig sind, können leicht identifiziert werden, da Sie über die Sammlung Items verfügen. Diese Sammlung erscheint im EIGENSCHAFTEN-Fenster und ermöglicht es Ihnen, Elemente hinzuzufügen, ohne das Steuerelement an ein Array oder eine andere Sammlung zu binden. Das ist die einfachste Art, die Elemente hinzuzufügen, wenn sie sich nicht verändern werden. Wenn sie sich wahrscheinlich verändern werden, sollten Sie sie in einer Datenbank speichern, zur Laufzeit abrufen und sie dann an das Steuerelement binden. Sie werden dies am Tag 12, »Mit .NET auf Daten zugreifen«, in Aktion erleben. Die RadioButtonList hat eine recht seltene Eigenschaft: RepeatColumns. Sie können diese definieren, um die Anzahl der Spalten zu kontrollieren, mit der die Elementliste angezeigt wird. Das kann eine tolle Möglichkeit sein, um Platz auf dem Bildschirm zu sparen und trotzdem alle Elemente anzuzeigen. Hinter den Kulissen schreibt die RadioButtonList HTML, um das zu erreichen. Dies ist eine der Funktionen, durch die diese Steuerelemente einfacher sind, als eigenes HTML zu schreiben. Ihr nächster Schritt beim Schreiben Ihrer Webanwendung ist das Hinzufügen von Code. Sie werden nur Code zu den beiden Schaltflächen hinzufügen. Sie beginnen mit der
331
Mit Web Forms eine Benutzeroberfläche erstellen
Schaltfläche CLEAR. Diese Schaltfläche wird die Informationen in den verschiedenen TextBox-Steuerelementen und im Ergebnis-Label löschen. Klicken Sie die Schaltfläche CLEAR doppelt an und fügen Sie den Code ein, den Sie in Listing 10.1 sehen.
Listing 10.1: Code für die Schaltfläche Clear 1 2 3 4 5 6 7 8 9 10 11
Private Sub cmdClear_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _Handles cmdClear.Click txtName.Text = "" txtDate.Text = "" txtVerb.Text = "" txtNumber.Text = "" txtOccupation.Text = "" lblResult.Text = "" End Sub
Der Code für diese Schaltfläche ist einfach. Er setzt nur die Eigenschaft Text aller TextBox-Steuerelemente und des Ergebnis-Label auf "". Damit wird der Inhalt dieser Steuerelemente gelöscht. Der Code im Listing 10.2 ist ebenfalls einfach. Die Grundidee ist, dass Sie eine lange Zeichenkette erstellen, die alles enthält, was dann in das Ergebnis-Label geschrieben wird.
Listing 10.2: Code für die Schaltfläche Write Story 1 2 3 4 5 6 7 8
332
Private Sub cmdWrite_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles cmdWrite.Click 'Hier kombinieren wir die Auswahlen, 'die der Anwender vorgenommen hat, zur endgültigen Geschichte. Dim sTemp As String sTemp = "Diary of " & txtName.Text & _
Standardsteuerelemente von Web Forms verwenden
9 10 11 12 13 14 15 16 17 18 19
" for " & DateTime.Today.ToString & "" sTemp &= "On " & txtDate.Text & _ " I started to program in Visual Basic.NET. " sTemp &= "I'm " & rlstEmotion.SelectedItem.Text & "! " sTemp &= "I think I'm going to go out and " & txtVerb.Text sTemp &= " my new " & txtNumber.Text sTemp &= " PicoHertz " & cboFruit.SelectedItem.Text sTemp &= " and become a " & txtOccupation.Text & "." 'Die endgültige Geschichte geht an das Steuerelement Label. lblResult.Text = sTemp End Sub
Der Prozess beginnt in Zeile 7, wo Sie die Zeichenkette deklarieren. Die Zeichenkette wird dann in den Zeilen 8 bis 16 erstellt und das Ergebnis wird in Zeile 18 in die Eigenschaft Text des Steuerelements lblResult gesetzt. Ein Symbol, das vermutlich merkwürdig aussieht, ist das &= in den Zeilen 10 bis 16. Es ist eine neue Funktion von Visual Basic .NET und ein Shortcut, wenn etwas zu einer Zeichenkette hinzugefügt wird. Nehmen wir z.B. den Code in den Zeilen 10 und 11: 10 11
sTemp &= "On " & txtDate.Text & _ " I started to program in Visual Basic.NET. "
Dieser Code ist äquivalent zu: 10 11
sTemp = sTemp & "On " & txtDate.Text & _ " I started to program in Visual Basic.NET. "
Sie können den Operator &= verwenden, wenn Sie Ihren Code kürzer machen möchten und viele Informationen an das Ende einer bestehenden Zeichenkette anhängen möchten. Nachdem Sie den Code hinzugefügt haben, sind Sie bereit, dieses Programm zu kompilieren und damit zu experimentieren. Wählen Sie aus dem Menü ERSTELLEN den Befehl ERSTELLEN aus und lassen Sie das Programm laufen. Es sollte ein Browser gestartet und Ihre Seite geladen werden. Geben Sie ein paar Elemente ein und klicken sie auf die Schaltfläche WRITE STORY, um Ihre Geschichte zu sehen. Abbildung 10.6 zeigt das Webformular in Aktion.
333
Mit Web Forms eine Benutzeroberfläche erstellen
Abbildung 10.6: Das Formular Madlib in Aktion
10.3 Komplexere Web-Forms-Steuerelemente Es ist zwar einfach, ein Formular mit Steuerelementen zu erstellen, die als Teil von HTML zur Verfügung stehen, aber Webformulare werden sogar noch nützlicher (bunter und leichter zu verwenden), wenn Sie einige der fortgeschritteneren Steuerelemente anwenden, wie z.B. Calendar, AdRotator oder Data. Obwohl mit einfacheren Steuerelementen erstellt, machen sie das Erstellen von Benutzeroberflächen leichter. Das Steuerelement Calendar zeigt – welche Überraschung! – einen Monatskalender. Mit diesem Steuerelement kann der Anwender Daten leichter betrachten und auswählen als mit einer TextBox. Außerdem reduzieren Sie mit Calendar das Risiko, dass der Anwender ein Datum in einem ungültigen Format eingibt. Das Steuerelement Calendar hat eine riesige Anzahl von Eigenschaften. Die meisten wirken sich aber darauf aus, wie das Steuerelement Calendar angezeigt wird. Fast alles, was in dem Steuerelement sichtbar ist, kann angepasst werden – die Farben, ob die Wochentags- oder Monatsnamen abgekürzt werden usw. Die Tabelle 10.3 beschreibt einige der nützlicheren Eigenschaften des Steuerelements Calendar.
334
Komplexere Web-Forms-Steuerelemente
Element
Eigenschaft
Beschreibung
SelectedDate
Eigenschaft
Das Datum, das von diesem Steuerelement zurückgegeben wird.
VisibleDate
Eigenschaft
Das Datum, das in dem Steuerelement angezeigt wird. Obwohl es sich hierbei meistens um das Gleiche wie SelectedDate handelt, kann es unterschiedlich sein, besonders wenn Sie versuchen, das Datum über Code zu definieren.
Tabelle 10.3: Wichtige Eigenschaften des Steuerelements Calendar
Lassen Sie uns das Projekt Madlib aktualisieren, damit es das Steuerelement Calendar statt TextBox für das einzugebende Datum verwendet. Sie können entweder die alte Version bearbeiten oder eine Kopie erzeugen, wenn Sie beide Versionen betrachten möchten. Löschen Sie das Textfeld Date und fügen sie ein Steuerelement Calendar ein. An diesem Punkt haben Sie die Wahl, entweder mit den Eigenschaften zu spielen, die sich auf die Erscheinung von Calendar auswirken, oder faul zu sein und den Link AUTOM. FORMATIERUNG auszuwählen, der unten in Fenster EIGENSCHAFTEN erscheint, wenn Sie das Steuerelement Calendar auswählen. Ich habe den Link angeklickt und FARBIG 2 ausgewählt. (»Warum Aufhebens machen und etwas hässlich gestalten, wenn schon ein eingebauter Profi etwas gut aussehen lässt?«, lautet eine meiner Programmierstrategien.) Setzen Sie die Eigenschaft Name von Calendar auf calDate. Sie müssen auch den Code in den Listings 10.1 und 10.2 leicht verändern, da Sie den Namen des Steuerelements geändert haben. Die Listings 10.3 und 10.4 zeigen den neuen, geänderten Code.
Listing 10.3: Der geänderte Code für die Schaltfläche Clear 1 2 3 4 5 6 7 8 9 10 11
Private Sub cmdClear_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles cmdClear.Click txtName.Text = "" calDate.SelectedDate = DateTime.Today txtVerb.Text = "" txtNumber.Text = "" txtOccupation.Text = "" lblResult.Text = "" End Sub
335
Mit Web Forms eine Benutzeroberfläche erstellen
Es wurde nur eine einzige Codezeile geändert. Da Sie die TextBox txtDate nicht mehr haben, können Sie sie nicht auf "" setzen. Statt dessen können Sie Calendar zurücksetzen, um den heutigen Tag auszuwählen (DateTime.Today), wie Sie es in der Zeile 6 tun.
Listing 10.4: Der geänderte Code für die Schaltfläche Write Story 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
Private Sub cmdWrite_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles cmdWrite.Click 'Hier kombinieren wir die Auswahlen, 'die der Anwender vorgenommen hat, zur endgültigen Geschichte. Dim sTemp As String sTemp = "Diary of " & txtName.Text & _ " for " & DateTime.Today.ToString & "" sTemp &= "On " & calDate.SelectedDate & _ " I started to program in Visual Basic.NET. " sTemp &= "I'm " & rlstEmotion.SelectedItem.Text & "! " sTemp &= "I think I'm going to go out and " & txtVerb.Text sTemp &= " my new " & txtNumber.Text sTemp &= " PicoHertz " & cboFruit.SelectedItem.Text sTemp &= " and become a " & txtOccupation.Text & "." 'Die endgültige Geschichte geht an das Steuerelement Label. lblResult.Text = sTemp End Sub
Wiederum gab es nur die eine Veränderung in Zeile 10 von Listing 10.4. Statt das Datum in der TextBox abzurufen, ruft der Code das ausgewählte Datum mit calDate.SelectedDate vom Calendar ab. Ist diese Eigenschaftensache nicht schön sinnvoll? Darum hat man es auch nicht Visual Complex .NET genannt.
336
Validator-Steuerelemente verwenden
10.4 Validator-Steuerelemente verwenden Wenn Sie Dateneingabeformulare für das Web schreiben, müssen Sie häufig sicherstellen, dass das Formular richtig ausgefüllt wird. Das kann bedeuten, dass bestimmte Felder ausgefüllt sind oder dass Felder mit Werten aus speziellen Intervallen gefüllt sind. In der Vergangenheit konnte dafür Code beim Server oder beim Client geschrieben werden. Wenn sich der Code auf dem Server befindet, kann es dazu kommen, dass Informationen unnötig zwischen dem Client und dem Server übergeben werden. Wenn Sie den Code lieber auf dem Client platzieren würden, haben Sie das Problem, dass der Browser ihn eventuell nicht handhaben kann. Visual Basic .NET enthält eine Reihe von Validator-Steuerelementen, die das Validieren von Formularen viel einfacher machen. Die Steuerelemente erledigen die Validierung entweder auf dem Server oder auf dem Client, wenn das Steuerelement feststellt, dass der Browser dazu fähig ist. In Visual Basic .NET stehen fünf Validierungs-Steuerelemente zur Verfügung: 쐽
RequiredFieldValidator: Stellt sicher, dass ein Feld ausgefüllt wurde. Sie können es jederzeit verwenden, wenn Sie sicherstellen möchten, dass der Anwender ein Formular ausfüllt, ehe er es übermittelt.
쐽
CompareValidator: Stellt entweder sicher, dass zwei Felder übereinstimmen, oder dass
ein Feld mit einem Wert verglichen wird. Die Übereinstimmung der Felder ist nützlich, wenn Sie möchten, dass der Anwender sein Passwort zweimal eingibt. Das Vergleichen des Feldes mit einem Wert ist nützlich, wenn Sie möchten, dass der Anwender eine positive Zahl eingibt oder wenn die Eingabe des Anwenders ein bestimmter Informationstyp sein muss (z.B. ein Datum). 쐽
RangeValidator: Stellt sicher, dass der Wert, der in ein Feld eingegeben wurde, sich in
einem bestimmten Bereich befindet. Der Bereich kann zwischen zwei Werten liegen (z.B. ein Start- und Enddatum) oder zwischen zwei Steuerelementen. Sie können z.B. ein Steuerelement verwenden, bei dem der Anwender einen Minimalwert eingibt und ein zweites für den Maximalwert. Der Validator würde dann sicherstellen, dass der Wert, der in ein drittes Steuerelement eingegeben wurde, zwischen diesen beiden Werten liegt. Das ist z.B. nützlich als Teil eines Berichts, bei dem der Anwender ein Datum auswählen soll, das zu entsprechenden Datenbankinformationen passt. 쐽
RegularExpressionValidator: Stellt sicher, dass der eingegebene Wert so »aussieht«
wie gewünscht. Der Wert wird mit einem regulären Ausdruck verglichen. Wenn beide übereinstimmen, wird der Wert als gültig angesehen. Das kann bei Werten nützlich sein, die eine bestimmte Struktur haben müssen, z.B. Telefonnummern, ISBN-Nummern oder Nummern von Ersatzteilen. 쐽
CustomValidator: Mit diesem Validator können Sie Ihren eigenen Code verwenden,
um das Feld zu validieren. Dies ist das flexibelste der Validierungselemente. Sein
337
Mit Web Forms eine Benutzeroberfläche erstellen
Code kann entweder auf dem Server oder beim Client laufen. Es ist nützlich, wenn keine der anderen Varianten für Ihren Zweck geeignet ist oder wenn in einem Prozess die Gültigkeit von Informationen ermittelt werden muss, z.B. wenn der Wert einem Eintrag in einer Datenbank entsprechen muss. Außer diesen fünf Steuerelementen gibt es noch das Steuerelement ValidationSummary, das alle Fehlermeldungen von allen Validator-Steuerelementen auf der gleichen Seite anzeigt. So haben Sie diese Informationen alle an einem Ort zusammengefasst. Die fünf Validierungssteuerelemente haben eine Reihe wichtiger Eigenschaften gemeinsam, etwa die Steuerelemente, die sie überwachen, und die Fehlermeldungen. Die wichtigsten dieser Eigenschaften werden in der Tabelle 10.4 beschrieben. Eigenschaft
Beschreibung
ControlToValidate
Hierbei handelt es sich um die wichtigste Eigenschaft aller Validierungssteuerelemente. Es sollte (mit dem Namen) auf ein anderes Steuerelement im gleichen Formular zeigen. Das ist das Steuerelement, das vom Validator überwacht wird. Mit dem Dropdown-Menü im Fenster EIGENSCHAFTEN wählen Sie das zu überwachende Steuerelement aus.
ErrorMessage
Dies ist die Nachricht, die angezeigt wird, wenn beim Validator ein Fehler auftritt, z.B. wenn das Feld frei gelassen wurde. Die Eigenschaft sollte genug Informationen für den Anwender enthalten, damit er weiß, was falsch ist und wie er den Fehler beheben kann.
Display
Dies ist eine eher merkwürdige Eigenschaft, die definiert, wie das Steuerelement Validator auf der Webseite erscheint. Standardmäßig ist diese Eigenschaft Static, es gibt aber noch zwei andere Möglichkeiten: Dynamic oder None. Wenn der Wert Static ist, dann wird der Raum, der von der ErrorMessage ausgefüllt wird, immer ausgefüllt, auch wenn die ErrorMessage nicht angezeigt wird. Das ist nützlich, wenn Sie garantieren möchten, wie Ihre Webseite gestaltet ist. Dynamic bedeutet, dass das Steuerelement keinen Platz in Anspruch nimmt, bis die Eigenschaft ErrorMessage gezeigt wird. Das ist nützlich, wenn Sie keine leeren Stellen in Ihrem Formular haben möchten. Wenn die Eigenschaft schließlich auf None gesetzt ist, wird die ErrorMessage nie angezeigt. Das ist nur nützlich in Verbindung mit dem Steuerelement ValidationSummary (das den Fehler anzeigt).
Tabelle 10.4: Häufig verwendete Eigenschaften für die Validator-Steuerelemente
Sie können einige dieser Steuerelemente verwenden, um die Madlib-Beispielanwendung zu vervollständigen. Sie können mit dem Steuerelement RequiredFieldValidator sicherstellen, dass der Anwender in bestimmten Feldern Informationen eingegeben hat, und mit dem Steuerelement RangeValidator können Sie sicherstellen, dass im txtNumber-Feld eine entsprechende Zahl eingegeben wurde. Schließlich können Sie alle Fehler mit dem Steuerelement ValidationSummary zusammenfassen.
338
Validator-Steuerelemente verwenden
Kopieren Sie wiederum das alte Projekt bzw. Formular oder öffnen Sie das bestehende Projekt, um es zu bearbeiten. Sie werden die Validator-Steuerelemente zu diesem bestehenden Formular hinzufügen. Ziehen Sie neben jedes TextBox-Steuerelement (txtName, txtNumber, txtVerb und txtOccupation) einen RequiredFieldValidator. Fügen Sie neben dem RequiredFieldValidator, den Sie zum Feld txtNumber hinzugefügt haben, einen RangeValidator ein. Fügen Sie schließlich das Steuerelement ValidationSummary in einer eigenen Zeile zwischen den Schaltflächen und dem Steuerelement lblResult ein. Setzen Sie die Eigenschaften jedes dieser Steuerelemente, wie Sie es in Tabelle 10.5 sehen. Steuerelement
Eigenschaft
Wert
RequiredFieldValidator
ID
reqName
ControlToValidate
txtName
ErrorMessage
Please enter a name
(ID)
reqNumber
ControlToValidate
txtNumber
ErrorMessage
Please enter a number
Display
Dynamic
(ID)
rngNumber
ControlToValidate
txtNumber
ErrorMessage
Please enter a number from 100 and 1000
Display
Dynamic
Type
Integer
MaximumValue
1000
MinimumValue
100
(ID)
reqVerb
ControlToValidate
txtVerb
ErrorMessage
Please enter a verb
Display
Dynamic
(ID)
reqOccupation
ControlToValidate
txtOccupation
ErrorMessage
Please enter an occupation
RequiredFieldValidator
RangeValidator
RequiredFieldValidator
RequiredFieldValidator
Tabelle 10.5: Eigenschaften für die Validator-Steuerelemente im Madlib-Formular
339
Mit Web Forms eine Benutzeroberfläche erstellen
Steuerelement
ValidationSummary
Eigenschaft
Wert
Display
Dynamic
(ID)
valSummary
Tabelle 10.5: Eigenschaften für die Validator-Steuerelemente im Madlib-Formular (Forts.)
Sie sollten sich einen Moment Zeit nehmen, um die drei Eigenschaften des RangeValidator zu beschreiben, die Sie bisher nicht zugewiesen haben. MaximumValue und MinimumValue sollten zwar in Bezug auf irgendetwas Sinn ergeben, das RangeValidator genannt wird, aber die Eigenschaft Type ist nicht so klar. Da mit dem RangeValidator eine Reihe von unterschiedlichen Wertarten getestet werden könnten (wie z.B. ganze Zahlen, Geldwerte oder Daten), identifiziert die Eigenschaft Type den zu vergleichenden Informationstyp. Sie kann einen der folgenden Werte annehmen: 쐽
String: Der Standardwert, durch den das Steuerelement testet, ob der Wert alphabe-
tisch zwischen zwei Extremwerten liegt. 쐽
Integer: Vergleicht den Wert mit den beiden Extremen, um sicherzustellen, dass er passt. Es werden nur Integer-Werte verwendet.
쐽
Double: Das Gleiche wie Integer, umfasst aber auch die Dezimalstellen eines Wertes
und der Extremwerte. 쐽
Currency: Das Gleiche wie Integer, enthält aber die ersten vier Dezimalstellen des
Wertes. 쐽
Date: Vergleicht die Werte, als wären es Daten. Der Wert 27. August 1964 läge in einem Zeitraum zwischen dem 23. November 1963 und dem 1. April 1986, wäre also zulässig.
Nun möchte ich kurz beschreiben, warum die Validator-Steuerelemente so nützlich sind. Damit sie funktionieren, müssen Sie keinen zusätzlichen Code einfügen. Erstellen und betrachten Sie die neue Webseite (siehe Abbildung 10.7). Lassen Sie einige Felder frei und löschen Sie die 500, den Standardwert für das Zahlenfeld. Es sollten rote Fehlernachrichten auf dem Formular erscheinen. Wenn das nicht der Fall ist, klicken Sie die Schaltfläche WRITE STORY an. Sie sollten eine ähnliche Ausgabe sehen, wie das Formular, das in Abbildung 10.8 dargestellt ist. Versuchen Sie, einen Wert außerhalb des zulässigen Wertebereichs für das Zahlenfeld einzugeben. Geben Sie schließlich korrekte Werte für alle Felder ein und klicken Sie die Schaltfläche WRITE STORY an. Alle Fehlermeldungen sollten verschwinden und Sie sollten unsere Geschichte nachlesen können.
340
Validator-Steuerelemente verwenden
Abbildung 10.7: Das Madlib-Formular zeigt die Validierungssteuerelemente an.
Abbildung 10.8: Das Madlib-Formular zeigt die Validierungsfehler an.
341
Mit Web Forms eine Benutzeroberfläche erstellen
10.5 Zusammenfassung Im Laufe der Zeit werden Webanwendungen immer wichtiger und Webformulare machen ihre Erstellung unglaublich einfach. Sie ermöglichen es Ihnen, die gleichen Techniken wie für eine Desktop-Anwendung auch für eine Webanwendung zu erstellen, die in jedem Webbrowser funktioniert. Indem Sie den Code zurück zum Server verschieben (den Sie kontrollieren können), genießen Sie die Vorteile beider Seiten, um die sich Webentwickler bemüht haben – eine reichhaltige Benutzeroberfläche und browserübergreifende Fähigkeiten. Für die meisten wäre es zwar ausreichend, etwas Ähnliches zu haben, wie sie es von Windows gewöhnt sind, aber Webformulare gehen noch einen Schritt weiter und helfen Ihnen dabei, noch einfachere Validierungsroutinen und komplexe Steuerelemente zu erstellen. Morgen werden Sie mit der objektorientierten Programmierung (OOP) beginnen. Sie werden sehen, was OOP mit Visual Basic .NET und mit der Programmierung im Allgemeinen zu tun hat und feststellen, dass Sie sich schon die ganze Zeit mit Objekten beschäftigt haben, sowohl in Form des .NET-Frameworks als auch im Hinblick auf die Formulare und Steuerelemente, die Sie verwendet haben.
10.6 Fragen und Antworten F
Muss mir ein Webserver zur Verfügung stehen, damit ich Web Forms nutzen kann? A
F
Kann ich Windows 98 oder Windows ME nutzen, um Webanwendungen zu erstellen und bereitzustellen? A
F
Diese Betriebssysteme enthalten entweder Personal Web Server (PWS) und stellen ihn auch nicht zur Verfügung. Mit PWS können Sie keine Webanwendungen erstellen. Windows 9.x kann für die Erstellung von Webanwendungen genutzt werden, diese Systeme sind aber keine guten Plattformen für die Bereitstellung. Sie müssen Webanwendungen mit IIS entweder auf Windows NT 4 Server oder Windows 2000 erstellen.
Wie kann ich mehr über das Schreiben von HTML lernen? A
342
Ja, Webanwendungen benötigen einen Webserver, der sich mit ASP.NET auskennt, wie z.B. den Internet Information Server (IIS). IIS umfasst einen kostenlosen Webserver, der Teil von Windows NT Server oder Windows 2000 Server ist.
Web Forms machen die Kenntnis von HTML zwar fast optional, aber es wird Ihnen helfen, wenn Sie ausreichende Kenntnisse in HTML haben. Sehen Sie sich einige der guten Bücher auf dem Markt an, um mehr zu lernen.
Workshop
10.7 Workshop Die Antworten und Lösungen finden Sie wie immer im Anhang A.
Quiz 1. Wenn Sie ganz einfach Text durch Eingeben in eine Webseite einfügen können, warum benötigen Sie dann Label-Steuerelemente? 2. Welche Steuerelemente helfen Ihnen dabei, zwischen Seiten in einer Webanwendung zu navigieren? 3. Wie können Sie auf einem Webformular Grafiken anzeigen?
Übung Erstellen Sie eine Registrierungsanwendung. Bei dieser neuen Webanwendung soll der Anwender Folgendes eingeben können: 쐽
Seinen Namen. Das Feld muss ausgefüllt sein.
쐽
Einen Benutzernamen, den der Anwender auf Ihrer Site benutzt (Alias). Dieses Feld muss ausgefüllt werden und der Eintrag darf nicht länger als zehn Zeichen sein.
쐽
Ein neues Passwort (denken Sie daran, das Passwort geheim zu halten). Der Anwender sollte das Passwort zweimal eingeben, um sicherzustellen, dass er es korrekt eingegeben hat. Beide Felder müssen ausgefüllt werden und übereinstimmen.
Nachdem die Informationen eingegeben wurden, sollte der Anwender auf eine Schaltfläche klicken. Die Informationen werden dem Anwender dann angezeigt.
343
Einführung in Datenbanken
Einführung in Datenbanken
Computer können gut mit Daten arbeiten, vor allem mit großen Datenmengen, und im Verlauf der Geschichte dieser Geräte wurden die meisten Programme genau dafür geschrieben. Obwohl Computer heute für enorm viele Aufgaben verwendet werden, ist die Datenverarbeitung immer noch Teil fast jeder Geschäftsanwendung und auch vieler Nicht-Geschäftsanwendungen. In der heutigen Lektion werden Sie etwas über Datenbanken lernen.
Themen heute 쐽
Wichtige Datenbankbegriffe
쐽
Grundlagen von SQL
쐽
Tipps für den Datenbankentwurf
쐽
Eine kurze Einführung zu Daten in .NET
11.1 Eine Datenbank ist die Lösung für alle Probleme im Leben Im Verlauf der letzten Jahre kannte sich einer der Autoren dieses Buches in seiner eigenen CD-Sammlung immer weniger aus. Gerade kürzlich erkannte er, dass er fast immer nur eine oder zwei bestimmte CDs hörte, weil er gar nicht genau wusste, welche CDs er sonst noch besaß. Wenn er jedoch wusste, dass er eine bestimmte CD hatte, dann wusste er nicht wo. Seine schlimmsten Befürchtungen bezüglich seiner CD-Sammlung wurden Wirklichkeit, als er sich dazu entschied, den großen Stapel CDs aufzuräumen und sie irgendwie zu organisieren. Das Organisieren von CDs ist keine so einfache Aufgabe, da es viele verschiedene Möglichkeiten gibt, die CDs zu ordnen. Vielleicht ist es am besten, sie nach Künstlern zu ordnen (und sicherzustellen, dass die Barenaked Ladies vor Boney M kommen) oder auch nach der Stilrichtung, wobei man die CDs in Gruppen für Pop, Rock, Weihnachtsmusik usw. aufteilt. Es gibt so viele Entscheidungen, die getroffen werden müssen. Soll Brian Adams unter »A« einsortiert werden (mit dem Nachnamen) oder unter »B« (mit dem Vornamen)? Was ist mit neuen CDs? Nachdem Sie genau diese Brian Adams-CD an eine der ersten Stellen in Ihr Regal gestellt haben, was passiert dann, wenn Brian Adams ein neues Album herausbringt? Müssen Sie alle anderen CDs nach vorne schieben, um Platz zu schaffen, oder sollten Sie sie einfach ans Ende stecken und Ihr schönes alphabetisches System zerstören? Am Ende ist es wahrscheinlich einfacher, keine Musik mehr zu kaufen, was in den frühen 70ern anscheinend auch die Entscheidung der Eltern des Autors war.
346
Eine Datenbank ist die Lösung für alle Probleme im Leben
Die Notwendigkeit, eine Entscheidung zu treffen, wurde offensichtlich, als unser freundlicher Autor bei seiner bereits erwähnten CD-Aufräumaktion auf zwei (2!) »Queen: Classic Hits, Volume1«-CDs stieß. Aber niemand – und das geht nicht gegen Queen – braucht zwei gleiche »Greatest Hits«-CDs, egal wie gerne er »Bohemian Rhapsody« hört. Etwas musste geschehen.
Die Entscheidung fällt Alle Entscheidungsmöglichkeiten bedeuteten zu viel Stress und der Autor fasste einen verblüffenden Entschluss: Die CD-Informationen sollten in ein Computerprogramm integriert werden! Eigentlich ist das gar nicht so verblüffend, da er bei ziemlich vielen Dingen zu diesem Entschluss kommt (vielleicht bei zu vielen, wenn man seine Frau oder einen seiner wenigen verbleibenden Freunde fragt). Die Idee, Daten in einem Computer zu speichern, ist aber nicht neu. Eigentlich war das schon immer einer der gängigen Zwecke von Computersystemen. Sie organisieren Daten auf viele Arten, aber der Vorteil der Verwendung eines Computers für die Organisation liegt darin, dass die Daten auf viele verschiedene Arten betrachtet werden können, wenn sie sich erst einmal im System befinden. Das löst viele Probleme, denen sich der arme Autor im weiter oben beschriebenen Szenario gegenüber sah. Wenn eine neue CD eingegeben werden muss, kann sie am Ende der Datendatei eingegeben werden, wird aber – je nach ausgewählter Ansicht – an der richtigen Stelle angezeigt. Wenn Sie die CDs nach Künstler sortiert ansehen möchten, sortieren Sie die Daten einfach nach dem Feld »Künstler«. Wenn Sie sie lieber nach CD-Titel sortieren möchten, lässt sich auch das leicht bewerkstelligen. In diesem Fall nehmen wir einfach an, dass alle Daten in eine Textdatei auf dem Computer eingegeben wurden und jede Zeile der Datei eine CD darstellt. Mögliche Felder und Einträge könnten folgendermaßen aussehen: ArtistName, ArtistFirstName, ArtistLastName, CDTitle, CDReleaseYear, MusicCategories Sting, Sting, –, Brand New Day, 2000, Pop Queen, Queen, –, Classic Hits Volume 1, 1987, Rock/Pop/Best Of Sting, Sting, –, Fields Of Gold, 1994, Pop/Best Of
In vielen Datenbanken sind Leerstellen in Tabellen- oder Feldnamen nicht zulässig, sodass diese Feldnamen entsprechend verändert wurden, um Kompatibilitätsprobleme zu vermeiden. Die erste Zeile dieser Datei, Kopfzeile genannt, zeigt an, welche Informationen in jeder Zeile gespeichert werden. Der Beginn jedes neuen Albums wird durch eine neue Zeile gekennzeichnet. Die Felder werden durch Kommata getrennt, wodurch die Datei zu einer
347
Einführung in Datenbanken
kommagetrennten (oder kommabegrenzten) Datei wird.1 So einfach sie auch ist, diese Datei ist eine Datenbank und könnte von einem Computerprogramm verwendet werden, das Alben verfolgt. Da dies eine Datenbank ist, können Sie damit einige der Begriffe lernen, die Sie von jetzt an verwenden werden, um auf die unterschiedlichen Teile einer Datenbank zu verweisen: Jede CD, die in dieser Datei aufgelistet ist, ist ein einzelner Datensatz, manchmal auch als Zeile bezeichnet, da Datensätze häufig als Zeilen mit Informationen gespeichert oder angezeigt werden.
In jedem Datensatz werden verschiedene Informationen über jede CD aufgezeichnet und jedes dieser Elemente wird Feld genannt (in diesem Fall ArtistName, CDTitle, CDReleaseYear usw.). Wie auch die Datensätze werden Felder häufig als Spalten bezeichnet, da Sie häufig als einzelne Spalten mit Informationen gespeichert und betrachtet werden. Die gesamte Gruppe der Datensätze (alle mit dem gleichen Satz Felder) werden Tabelle genannt und eine Datenbank kann viele dieser Tabellen enthalten, obwohl das Beispiel in der heutigen Lektion nur aus einer besteht.
Der Schritt zu einer echten Datenbank Jetzt kennen Sie also alle Begriffe, die Sie benötigen, um Ihre kleine Datenbank zu beschreiben, die eine einzelne Tabelle namens Disc mit CD-Datensätzen enthält. Nachdem Sie die Begriffe kennen und die Datenbank eingerichtet ist, wie arbeiten Sie mit diesen Daten von Ihren Computerprogrammen aus? Wie bereits gesagt ist die Beispieldatenbank nur eine Textdatei und es ist relativ einfach, eine Textdatei zu lesen. (Ein Beispiel für das Lesen aus einer Datei finden Sie am Tag 4, Den Programmfluss steuern, und mehr Einzelheiten zu den wunderbaren Dateileseklassen im Framework finden Sie am Tag 17, Mit dem .NET Framework arbeiten.) So, wie das Beispiel derzeit aussieht, müssten Sie aber jede Zeile der Daten selber parsen und alle Spalten selber behandeln. Selbst wenn Sie die Datei lesen können, möchten Sie doch auch in der Lage sein, sie in standardmäßigen Formen zu bearbeiten. Zu diesen gehören auch das Suchen von bestimmten Datensätzen, das Hinzufügen neuer Datensätze, das Löschen von Datensätzen und das Bearbeiten aller oder eines Teils der Datensätze. Um diese ganze Funktionalität zu implementieren, müssten Sie viel Code schreiben, aber Sie müssen 1
Anm. d. Übers.: Häufig findet man für solche Werte das Akronym CSV. Es bedeutet »Comma-separated Value«.
348
Einführung in SQL
keine einzige dieser Funktionen behandeln, wenn Sie Ihre Daten in ein Datenbankformat wie Access oder SQL Server setzen. Diese Datenbankverwaltungssysteme können Ihre Informationen speichern und außerdem auf Anfragen, diese Daten abzurufen oder zu bearbeiten, reagieren. Außerdem regeln sie alle Einzelheiten der Speicherung und des Datei-Layouts für Sie. Datenbanksysteme verfügen über eine große Vielfalt an Funktionen, aber der am häufigsten genutzte Dienst, den diese Systeme bieten, ist die Handhabung der Einzelheiten der Datenspeicherung und die gesamte physische Interaktion mit den Daten.
11.2 Einführung in SQL Nachdem Sie Daten in einem Datenbanksystem gespeichert haben, müssen Sie trotzdem noch in der Lage sein, diese Daten vom Programm aus zu bearbeiten, aber Sie müssen die Einzelheiten nicht mehr selbst ausführen. SQL (Structure Query Language) wurde als übergreifende Sprache für den Zugriff auf Datenbanken geschaffen. Diese Sprache enthält Befehle für das Bearbeiten von Datensätzen in Datenbanktabellen und wird bis zu einem bestimmten Grade von fast jeder verfügbaren Datenbanksoftware unterstützt. In SQL stehen viele verschiedene Befehle zur Verfügung. Diejenigen, die ich erörtern werde, drehen sich um die folgenden Punkte: 쐽
Datensätze abrufen
쐽
Neue Datensätze hinzufügen
쐽
Bestehende Datensätze bearbeiten
쐽
Datensätze löschen
Datensätze mit der SELECT-Anweisung abrufen In SQL verwenden Sie die SELECT-Anweisung, um Daten aus einer Datenbank abzurufen. Diese Anweisung sieht in ihrer Grundform so aus: SELECT from
Wenn wir die CD-Datenbank als Beispiel nehmen, würden Sie die folgende SQL-Anweisung ausführen, wenn Sie alle CD-Titel zusammen mit dem Namen des Künstlers abrufen wollten: SELECT ArtistName, CDTitle FROM Disc
Diese Anweisung gibt für jeden Datensatz in der Tabelle Disc eine Zeile mit zwei Feldern zurück. Wenn Sie alternativ alle Spalten in einer Tabelle erhalten möchten, können Sie * statt einer Liste von Feldern verwenden und benutzen dann eine Anweisung wie die folgende:
349
Einführung in Datenbanken
SELECT * FROM Disc
SQL-Anweisungen wie diese werden Sie zwar wahrscheinlich in vielen Beispielen und sogar in Anwendungen sehen, es ist aber keine empfehlenswerte Praxis, alle Felder auf einmal abzurufen. Sie möchten immer die kleinstmögliche Menge an Informationen abrufen und das sind normalerweise nicht alle Felder. Achten Sie immer darauf, die langfristige Leistung Ihres Codes nicht zu verletzen, nur um an der Zeit zu sparen, die Sie benötigen, um Ihr Programme einzutippen. Geben Sie jedes Feld an, das Sie in einer SQL-Anweisung abrufen möchten. Rufen Sie die kleinstmögliche Menge an Daten oder der Anzahl von Feldern und Datensätzen ab, um die bestmögliche Leistung zu erzielen. Verwenden Sie kein *, um alle Felder anzuzeigen, außer Sie sind sich sicher, dass Sie jedes einzelne Feld benötigen, das es in der Tabelle gibt, und alle Felder, die eventuell später hinzugefügt werden.
Die Ergebnisse ordnen In den beiden bisher gezeigten Abfragen wurde die Reihenfolge der zurückzugebenen Datensätze nicht angegeben. Sie können aber zu der SQL-Anweisung eine Klausel hinzufügen, um die Sortierreihenfolge zu definieren. Diese ORDER BY-Klausel nimmt eine Liste von Feldern entgegen und ermöglicht es Ihnen, mehr als ein Feld anzugeben, das für den Sortiervorgang verwendet werden soll. Die folgende SQL-Anweisung ruft von allen Datensätzen in der Tabelle Disc den ArtistName und den CDTitle ab und sortiert sie zuerst nach dem ArtistName und dann innerhalb der Datensätze für einen Künstler nach dem CDTitle: SELECT ArtistName, CDTitle FROM Disc ORDER BY ArtistName, CDTitle
Standardmäßig wird ein Feld, das in der ORDER BY-Klausel angegeben wurde, in aufsteigender Reihenfolge sortiert (aufsteigend im Wert oder nach der alphabetischen Position zum Ende der zurückzugebenden Datensätze hin), also von A nach Z, wenn das Feld Text enthält. Wenn die Sortierreihenfolge umgekehrt sein soll, absteigend von Z nach A, dann geben Sie hinter dem Teil der ORDER BY-Klausel, der in umgekehrter Reihenfolge sortiert werden soll, das Schlüsselwort DESC an. Das folgende Beispiel sortiert die Daten nach Künstler in umgekehrter Reihenfolge und dann nach CD-Titel in der regulären Reihenfolge. SELECT ArtistName, CDTitle FROM Disc ORDER BY ArtistName Desc, CDTitle
Die Standardreihenfolge (aufsteigend) kann auch explizit mit dem Schlüsselwort ASC angegeben werden, ebenso wie das Schlüsselwort DESC für die absteigende Reihenfolge verwen-
350
Einführung in SQL
det wird. Die folgende SQL-Anweisung hat genau die gleiche Wirkung wie die vorherige, gibt aber expliziter an, was geschieht. SELECT ArtistName, CDTitle FROM Disc ORDER BY ArtistName Desc, CDTitle ASC
Geben Sie ASC an, wenn Sie auch DESC verwenden, um Verwirrungen zu vermeiden.
Sortieren Sie nicht nach mehr Feldern als nötig, da sich das auf die Leistung auswirkt.
Kriterien angeben Alle bisher gezeigten SELECT-Anweisungen haben alle Datensätze aus der Tabelle Disc abgerufen, aber was ist, wenn Sie nur einen bestimmten Datensatz oder eine Reihe von Datensätzen abrufen möchten, die bestimmte Kriterien erfüllen? Die SELECT-Anweisung kann um eine weitere Klausel ergänzt werden, die sich darum kümmert: die WHERE-Klausel. In der WHERE-Klausel können Sie so viele Kriterien angeben, wie Sie möchten, um einzuschränken, welche Zeilen zurückgegeben werden. Mit dieser Funktion gibt die SELECTAnweisung nur eine CD von Sting zurück: SELECT CDTitle, CDReleaseYear FROM Disc WHERE ArtistName = 'Sting'
Beachten Sie, dass das Feld, mit dem die Ergebnisse eingeschränkt wurden, keines derjenigen war, die zurückgegeben werden sollten: Sie können jedes beliebige Feld verwenden. Es ist möglich, mehrere Kriterien mit den Operatoren AND/OR zu kombinieren und Klammern zu verwenden, um zu bestimmen, wie diese Kriterien angewendet werden. Mit der folgenden Anweisung können Sie alle Alben abrufen, die nach 1984 veröffentlicht wurden, bei denen ArtistName gleich Sting war oder die älter als 1984 sind und von The Police stammen: SELECT CDTitle, CDReleaseYear FROM Disc WHERE (ArtistName= 'Sting' AND CDReleaseYear => 1984) OR (ArtistName= 'The Police' AND CDReleaseYear < 1984)
Diese Abfrage führt zu einer Frage, die Datenbankadministratoren schon immer geplagt hat: Soll für das Feld ArtistName der Wert Police oder Police, The eingegeben werden? Ich werde diese und einige weitere Datenbankfragen zusammen mit den, nach meiner
351
Einführung in Datenbanken
Kenntnis, besten Lösungsmöglichkeiten weiter unten in der heutigen Lektion im Abschnitt »Gängige Datenbankprobleme und -lösungen« besprechen.
Neue Datensätze hinzufügen Wir besprechen die SQL-Anweisungen in der heutigen Lektion quasi in der falschen Reihenfolge. Sie haben gelernt, wie Daten aus der Datenbank abgerufen werden, ehe Sie gelernt haben, wie Sie sie hineinbringen. Einige Datenbänke sind zwar schreibgeschützt, in den meisten Fällen werden aber regelmäßig neue Datensätze zu den Tabellen hinzugefügt. Der SQL-Befehl, mit dem diese neuen Datensätze hinzugefügt werden, heißt INSERT und hat diese Grundform: INSERT INTO (feld 1, feld 2,...) VALUES (wert 1, wert 2,...)
Wenn wir zu unserem CD-Bibliotheksbeispiel zurückkehren, könnten Sie die folgende INSERT-Anweisung verwenden, um ein neues Album hinzuzufügen: INSERT INTO Disc (ArtistName, ArtistFirstName, ArtistLastName, CDTitle, CDReleaseYear, MusicCategories) VALUES ('Lenny Kravitz', 'Lenny', 'Kravitz', 'Greatest Hits', 2000, 'Rock/Pop/Best Of')
Beachten Sie die einfachen Anführungszeichen um alle Werte mit Ausnahme von CDReleaseYear. Diese Zeichen müssen jeden Textwert einschließen, der in einer SQL-Anweisung verwendet wird, damit man zwischen dem Wert und SQL selbst unterscheiden kann. Da CDReleaseYear kein Textwert ist, muss der Wert nicht in Anführungszeichen gesetzt werden. Es ist möglich, die Liste der Feldnamen zu überspringen, solange die Werte in Ihrer Liste die richtige Reihenfolge haben. Das ist aber ein weiteres Beispiel dafür, wie Sie bei der Codeeingabe zwar ein wenig Zeit sparen können, aber Code erzeugen, der schwieriger zu pflegen ist und daher in Zukunft auch mit größerer Wahrscheinlichkeit Probleme bereiten wird.
Datensätze bearbeiten SQL enthält die UPDATE-Anweisung, mit der Sie an bestehenden Datensätzen Veränderungen vornehmen können, wobei Sie eines oder mehrere Felder von beliebig vielen Datensätzen gleichzeitig ändern können. Diese Anweisung hat die folgende Grundsyntax: UPDATE SET =
Ebenso wie die weiter oben verwendete SELECT-Anweisung kann auch die UPDATE-Anweisung über eine WHERE-Klausel verfügen. Ohne eine solche Klausel wird die Anweisung die Aktualisierung an allen Datensätzen in der Tabelle vornehmen. Also würde die Anweisung
352
Einführung in SQL
UPDATE Disc SET CDTitle='New Title'
jedes einzelne CDTitle-Feld auf 'New Title' setzen, was wohl nicht das beabsichtigte Ergebnis ist. Sie werden zwar häufig nur einen einzelnen Datensatz aktualisieren, es ist aber nützlich, wenn man mehrere Datensätze mit einer einzigen UPDATE-Anweisung verändern kann. Neue Werte, die mit dieser Anweisung gesetzt werden, können auf dem bestehenden Inhalt des Feldes basieren. Stellen Sie sich als Beispiel diese Aktualisierung einer fiktiven Tabelle vor, bei der jeder Angestellte eine Gehaltserhöhung um zehn Prozent erhält: UPDATE Employee SET Salary = Salary * 1.1
In den meisten Fällen werden Sie aber eine WHERE-Klausel angeben wollen, um die betroffenen Datensätze auf eine kleinere Auswahl oder sogar auf ein einziges Element zu reduzieren. Die folgende SQL-Anweisung aktualisiert die Datensätze der Barenaked Ladies in der Tabelle Disc, um den Wert MusicCategories in 'Pop' zu ändern: UPDATE Disc SET MusicCategories = 'Pop' WHERE ArtistName = 'Barenaked Ladies'
Eine solche Überarbeitung der Kategorien und Gruppierungen in einer Datenbank ist etwas, was Sie von Zeit zu Zeit tun sollten, um Verwirrungen zu vermeiden. Die heutigen Beispiele waren zwar einfach, aber die UPDATE-Anweisung ist noch zu viel komplexeren Aktionen in der Lage. Sie können z.B. mehrere Felder auf einmal aktualisieren, wobei Sie jede Zuweisung durch ein Komma von der nächsten trennen, und Sie können Ihre WHERE-Klausel so komplex gestalten wie Sie möchten. UPDATE Disc SET ArtistName = 'Sting', ArtistFirstName= 'Gordon', ArtistLastName = 'Sumner' WHERE ArtistName = 'Sting' OR ArtistName = 'Mr. Sting'#
Unerwünschte Datensätze entfernen Sie haben Ihre Datensätze abgerufen, neue hinzugefügt und bearbeitet. Nun bleibt nur noch eine elementare Operation übrig: das Löschen. SQL verfügt über eine DELETE-Anweisung, die eine einfache Syntax verwendet: DELETE WHERE
In diesem Fall ist die WHERE-Klausel, genau wie bei UPDATE, optional aber wichtig, da Sie ohne diese Klausel alle Datensätze in der angegebenen Tabelle löschen würden. In der Tabelle Disc könnten Sie diese Anweisung verwenden, um bestimmte Datensätze zu löschen oder – noch besser – einen einzelnen Datensatz zu löschen. Die SQL-Anweisung, die Sie hier sehen, entfernt alle Gordon Lightfoot-CDs bis auf eine:
353
Einführung in Datenbanken
DELETE Disc WHERE ArtistFirstName = 'Gordon' AND ArtistLastName = 'Lightfoot' AND CDTitle != 'Summertime Dream'
Wie es mit SQL weitergeht SQL ist wirklich wichtig, aber es ist auch ein großes und komplexes Thema. Sie werden im Verlauf der heutigen Lektion und für den Rest des Buches noch mehr Dinge über SQL lernen, aber diese Beispiele bieten keine vollständige Erörterung des Themas. Fast jedes Datenbankprodukt verwendet bis zu einem bestimmten Grad SQL und es gibt zahlreiche Bücher, die sich ganz dieser einen Sprache widmen. Eine weitere gute Quelle, über die Sie vermutlich schon verfügen, ist das Material in der Microsoft Solution Developers Network (MSDN)-Bibliothek, die als Teil unserer Visual Studio-Installation installiert wurde. Suchen Sie nach den bereits beschriebenen SQLBefehlen, um den SQL-Referenzabschnitt der MSDN-Bibliothek ausfindig zu machen. Wenn Sie außerhalb der Beispiele aus diesem Buch mit SQL üben möchten, sollten Sie dazu mit jedem Datenbankprogramm in der Lage sein, das Ihnen zur Verfügung steht. Wenn bei Ihnen SQL Server installiert ist, führen Sie Query Analyzer aus (siehe Abbildung 11.1), eines der Werkzeuge, die in SQL Server enthalten sind. Es ist als Symbol im SQL Server-Programmordner verfügbar. Dieses Werkzeug soll es Ihnen ermöglichen, Anfragen auszuführen und sich das Ergebnis anzusehen, obwohl Sie vorsichtig sein sollten, irgendetwas anderes als SELECT-Anfragen auszuführen, bevor Sie ein bisschen vertrauter mit der Angabe von WHERE-Klauseln sind.
Abbildung 11.1: Mit dem Query Analyzer können Sie SQL-Anweisungen in Ihrer Datenbank ausführen, aber seine wahre Macht liegt in der Fähigkeit, die Leistung und Struktur Ihrer Anfragen zu analysieren.
354
Gängige Datenbankprobleme und -lösungen
11.3 Gängige Datenbankprobleme und -lösungen Datenbanksoftware wird Ihre Tabellen, Felder und Datensätze auf dem Laufenden halten, aber wie Sie die Daten strukturieren, liegt ganz bei Ihnen, und daher tauchen hier auch die meisten Probleme auf. Es gibt viele verschiedene Methoden, die gleichen Daten zu organisieren, aber nur ein paar davon funktionieren in einem Datenbanksystem gut. Das Ziel ist es, eine Datenbank zu erzeugen, die effizient sucht, die es aber schwierig macht, inkonsistente Daten einzugeben. Inkonsistenz ist das größte Problem der Datenbanken. Wenn sich verschiedene Teile der Datenbank nicht einig sind, sind die Daten nicht verlässlich. Stellen Sie sich eine Beispieltabelle vor, in der Sie absichtlich einige der gängigeren Fehler gemacht haben. In dieser Tabelle können Sie leicht inkonsistente Daten erzeugen, da sie nicht strukturiert ist, um diese Dinge zu vermeiden oder auch nur zu minimieren. Ich werde jedes der Hauptprobleme mit einer solchen Tabelle erörtern und Ihnen zeigen, wie Sie diese Probleme in Ihren Datenbanken vermeiden.
Aktualisierungsinkonsistenz Angenommen, ein Anwender entscheidet sich dafür, eine Zeile in der Tabelle Disc zu aktualisieren, weil der Wert für den Künstler falsch ist. Vielleicht wurde Sting angegeben, obwohl es Bruce Willis sein müsste. Der Anwender könnte die folgende SQL-Anweisung ausführen: UPDATE Disc SET ArtistName = 'Bruce Willis' WHERE ArtistName = 'Sting' and CDTitle = 'The Return Of Bruno'
Das ist eine vollkommen richtige SQL-Anweisung. Wenn aber die ursprüngliche Datenzeile so aussah: ArtistName, ArtistFirstName, ArtistLastName, CDTitle, CDRelaseYear, MusicCategories Sting, Gordon, Sumner, The Return Of Bruno, 1986, Rock/Pop
dann sieht sie nun so aus: Bruce Willis, Gordon, Sumner, The Return Of Bruno, 1986, Rock/Pop ArtistName gibt die richtige Information wieder, ArtistFirstName und ArtistLastName hingegen nicht, und das ist inkonsistent. Dies hätte man vermeiden können, indem alle drei Felder auf einmal aktualisiert worden wären. Das geht aber nur, wenn der Anwender oder die Software sicherstellen, dass die Daten konsistent sind. Sie sollten auf der Entwurfsebene einer Datenbank alles tun, um Inkonsistenz zu vermeiden.
355
Einführung in Datenbanken
Strukturieren Sie Ihre Datenbank, um jegliche Inkonsistenz zu vermeiden.
Verlassen Sie sich nicht darauf, dass Ihre Anwender, einschließlich Ihre eigenen Programme, mit den Daten so umgehen, wie Sie sich das vorgestellt haben.
Eine weitere gängige Inkonsistenz, die bei einer Datenbankstruktur wie der Tabelle Disc auftreten kann, ist, dass bei Daten, die identisch sein sollten, verschiedene Variationen existieren. Hier sehen Sie drei Zeilen aus der Tabelle Disc: Barenaked Ladies, – , – , Maroon, 2000, Pop The Barenaked Ladies, – , – , Gordon, 1993, Pop Barenaked Ladies, – , – , Stunt, 1999, Rock/Pop
Eine SQL-Anweisung, die alle Alben der Barenaked Ladies finden soll, würde wahrscheinlich so geschrieben werden: SELECT * FROM Disc WHERE ArtistName = 'Barenaked Ladies'
Dieser Code würde die erste und die letzte der drei oben aufgelisteten Zeilen zurückgeben, die CD mit dem Titel »Gordon« jedoch nicht finden. Das würde nicht zu Fehlern führen, die dem Anwender angezeigt werden. Der Anwender würde aber einfach keine vollständigen Informationen erhalten. Das dritte Problem bezüglich dieser Künstlerfelder tritt dann auf, wenn ein Künstler seinen Namen ändert. (Diese Situation kommt in der Musik nicht häufig vor, wenn Sie aber mit Unternehmensabteilungen oder Produktnamen in anderen Datenbanken umgehen, wäre das schon eher ein Thema.) Angenommen ein Künstler, nehmen wir einmal Prince, ändert seinen Namen von dem einfachen Wort »Prince« in ein unaussprechliches Symbol (das leider nicht Teil des ASCII-Codes ist und das Sie daher auch nicht in Ihre Datenbank eingeben können). Sie müssten jeden einzelnen Datensatz aktualisieren, der zurzeit den Namen »Prince« aufweist. Natürlich ist das eine einzelne Anweisung: UPDATE Disc SET ArtistName = 'The Artist Formerly Known As Prince' WHERE ArtistName = 'Prince'
Wenn niemand »Prince« falsch geschrieben hat, würde diese SQL-Anweisung alle erforderlichen Datensätze aktualisieren. Dann verlassen Sie sich aber wiederum darauf, dass der Anwender oder die Software weiß, dass sie mehrere Veränderungen vornehmen müssen. Es ist aber möglich, dass sie keine UPDATE-Anweisung wie die vorherige verwenden würden. Sie könnten einfach eine einzelnen Eintrag in den neuen Wert ändern, statt alle Prince-Alben, wodurch die Datenbank wieder inkonsistent wäre. Es wird sogar noch schlimmer, wenn Sie herausfinden, dass Sie am Ende wieder alle Datensätze in »Prince« ändern müssen.
356
Gängige Datenbankprobleme und -lösungen
Alle diese Punkte sind das Ergebnis eines Problems in der Struktur der Datenbank – die Künstlerinformationen werden für jedes einzelne Album in dem Datensatz gespeichert, auch wenn der Name des Künstlers eigentlich eine unabhängige Datenmenge ist. Um die oben beschriebenen Probleme zu vermeiden, sollten Sie die Künstlerinformationen in eine eigene Tabelle ausgliedern und mit dem Album nur einen einzigen Wert speichern, um die beiden Tabellen miteinander zu verbinden. Für diesen Wert könnten Sie das Feld ArtistName verwenden, aber das beschert Ihnen Probleme, wenn sich dieser Wert jemals ändert. Es ist immer am besten, wenn Sie einen Wert wählen, der ganz bestimmt nicht geändert wird, es sei denn, der verknüpfte Datensatz in der Künstlertabelle wird gelöscht. Wenn Sie sich die derzeitigen Künstlerinformationen ansehen – die Werte für ArtistName, ArtistFirstName und ArtistLastName – gibt es da nichts, was sich unter Garantie nicht ändert. Daher fügen Sie für diesen Zweck ein spezielles Feld ein. Es ist häufig der Fall, dass ein zusätzliches Feld erzeugt wird, nur um als eindeutiger Bezeichner für einen Datensatz zu dienen. Die gängigste Methode ist es, ein Feld zu erzeugen, das eine sich selbst inkrementierende Zahl enthält. Wenn Sie dieses Feld zu Ihren Künstlerinformationen hinzufügen, erhalten Sie die folgende Künstlertabelle: ArtistID, ArtistName, ArtistFirstName, ArtistLastName 1, Sting, Gordon, Sumner 2, The Barenaked Ladies, – , 3, Bon Jovi, – , 4, Queen, – , 5, The Police, – , ... 45, Janet Jackson, Janet, Jackson
Jedem Datensatz der Tabelle Disc muss immer noch ein Künstler zugeordnet werden, sodass Sie ein Feld zu dieser Tabelle hinzufügen werden, das für jeden Datensatz einen ArtistID-Wert enthält. Die Tabelle Disc würde dann so aussehen: ArtistID, CDTitle, CDReleaseYear, MusicCategories 1, Brand New Day, 2000, Pop 4, Greatest Hits, 1988, Rock/Pop ...
Die ArtistID wird Schlüssel genannt, da sie verwendet wird, um eine einzelne Entität zu identifizieren. In der Tabelle Artist, in der die ArtistID genau einen bestimmten Künstler identifiziert, wird dieses Feld Primärschlüssel genannt. In der Tabelle Disc wird sie Fremdschlüssel genannt, da sie als Link zurück in eine andere Tabelle verwendet wird (siehe Abbildung 11.2). Eine Tabelle wird normalerweise verschiedene Fremdschlüssel enthalten, die sie mit anderen Tabellen verknüpfen, aber eine Tabelle hat nur einen Primärschlüssel (obwohl mehr als ein Feld zur Bildung eines Primärschlüssels verwendet werden kann).
357
Einführung in Datenbanken
Artist PK
ArtistID ArtistName ArtistFirstName ArtistLastName
Disc FK1 ArtistID CD Title CDReleaseYear MusicCategories
Abbildung 11.2: Die Tabellen Artist und Disc enthalten Schlüsselfelder, die sie miteinander verbinden.
Ein Schlüssel ist ein Wert – im Allgemeinen ein Feld –, der eine Entität identifiziert, z.B. eine Immatrikulationsnummer, den Namen eines Autors oder die Sozialversicherungsnummer. Wenn ein Feld einer Tabelle jeden Datensatz dieser Tabelle eindeutig identifiziert, wird es als Primärschlüssel der Tabelle bezeichnet. Ein Primärschlüssel einer Tabelle, der in ein Feld einer anderen Tabelle gesetzt wurde, um die beiden Entitäten zu verknüpfen, wird in der letzteren Tabelle Fremdschlüssel genannt. Wenn zwei Tabellen über die Verwendung eines Primär- und Fremdschlüssels verbunden sind, sind sie verwandt.
Durch das Verschieben der Daten im Künstlerfeld in eine separate Tabelle müssen sich die Anweisungen ändern, die erforderlich sind, um Informationen hinzuzufügen und abzurufen. Zum Hinzufügen von Daten müssen Sie zwei INSERT-Anweisungen verwenden, eine für die Tabelle Artist und eine für die Tabelle Disc, aber die INSERT-Anweisung für die Tabelle Artist tritt nur einmal pro Künstler auf, nicht für jedes einzelne Album. Die Erzeugung von getrennten, verknüpften Tabellen hat die weiter oben beschriebenen Probleme verschwinden lassen, aber dabei eine neue Möglichkeit hervorgebracht, inkonsistente Daten einzugeben – nämlich ungültige Fremdschlüssel. Wenn ein Anwender oder ein Programm einen Artist-Feldwert entfernt, dann referenzieren alle Alben, die diese ArtistID referenzieren, plötzlich einen Datensatz in der Tabelle Artist, der nicht mehr besteht. Ich werde etwas weiter unten in der heutigen Lektion, im Abschnitt »Referenzielle Integrität«, besprechen, wie man mit diesem Problem umgeht. Die Methode zum Abrufen der Daten, die Sie aus der Datenbank benötigen, hat sich ebenfalls verändert, da die gewünschten Informationen nun über zwei Tabellen verteilt sind. Die Methoden, mit denen Sie an diese Daten gelangen, wird ebenfalls weiter unten erörtert, im Abschnitt »Joins: Mehrere Tabellen auf einmal abfragen«.
358
Gängige Datenbankprobleme und -lösungen
Platzinkonsistenz versus falsche Daten In dem neuen Tabellenlayout werden die Künstlerinformationen in der Tabelle Artist gespeichert, also getrennt von den CD-Informationen, die in der Tabelle Disc gespeichert sind. Das Ziel dieser Trennung ist die Vermeidung inkonsistenter Informationen. Sie sollten Situationen vermeiden, in denen sich zwei unterschiedliche Teile der Datenbank über ein Datenelement nicht einig sind. Es ist sicherlich kein Thema des Datenbankentwurfs, wenn das Feld FirstName für Sting auf George statt auf seinen richtigen Namen Gordon gesetzt ist, solange die gesamte Datenbank konsistent ist und nur den einen Wert George speichert. Das mag ein wenig seltsam erscheinen, da falsche Daten ziemlich wichtig sind, aber das Ziel des Datenbankentwurfs ist es, Fehler durch das Layout der Daten zu vermeiden. Die Software und ihre Anwender können sich selbst darum kümmern, falsche Daten zu vermeiden.
Felder mit mehreren Werten Eines der Felder, nämlich MusicCategories, könnte mehr als einen Wert gleichzeitig enthalten, und zwar in der Kategorie Rock/Pop. Dieser Feldtyp muss aus unserer Datenbank entfernt werden oder Sie werden viele Probleme damit haben. Das erste Problem tritt auf, wenn der Anwender versucht, Daten basierend auf den Informationen in diesem Feld abzurufen: SELECT * FROM Disc Where MusicCategories = 'Rock'
Diese Anweisung würde Alben zurückgeben, bei denen MusicCategories den Wert Rock hat, aber nicht diejenigen mit Rock/Pop, Best Of/Rock oder irgendetwas anderem, das aus mehr als nur der einfachen Kategorie besteht. Sie müssen ein wenig über den Zweck der Datenbank wissen. In diesem Fall war es der Zweck des Feldes MusicCategories, die CDs nach einem oder mehreren Musikarten zusammenzufassen. Wenn der Anwender alle seine Alben suchen möchte, die in die Sparte Pop fallen, oder alle Alben, die Rock und »Best of«-Sammlungen sind (heißt es dann Rock/Best Of oder Best Of/Rock?), dann würde das daraus resultierende Problem diese Kategorisierung ziemlich nutzlos machen. Das Aktualisieren der Daten in einem Feld mit mehreren Werten ist fast genauso schwer. Am Ende müssen Sie Ihren neuen Wert über den bestehenden schreiben und sicherstellen, dass er nicht bereits da war! Die Dinge werden sogar noch komplizierter, wenn Sie sich jemals dazu entscheiden, eine Kategorie umzubenennen, und z.B. Pop in Populär zu ändern, eine Aufgabe, die mit einer UPDATE-Anweisung extrem schwierig zu lösen wäre.
359
Einführung in Datenbanken
Um diese Anweisung mit mehreren Werten zu reparieren und alle damit verbundenen Probleme zu vermeiden, werden Sie die Daten in eine andere Tabelle setzen. Im Gegensatz zu den Künstlerinformationen kann eine einzige neue Tabelle die Verbindung zwischen den Kategorien und den Alben aber nicht handhaben. Jedem Künstler kann eine beliebige Anzahl von Alben zugeordnet werden (oder überhaupt keines). Diese Beziehung nennt man Eins-zu-viele oder korrekter Null-zu-viele. Die Verbindung zwischen Album und Kategorie ist aber komplexer: Eine einzelne Kategorie kann vielen Alben zugeordnet werden und ein einzelnes Album kann vielen Kategorien zugeordnet werden. Das führt zu einer Viele-zu-viele-Beziehung, die Sie mit zwei neuen Tabellen modellieren können. Eine Tabelle enthält die Kategorien selbst und eine andere Tabelle verbindet die Alben mit den Kategorien (siehe Abbildung 11.3). DiscCategory PK PK
DiscID CategoryID
Category PK
Disc PK
DiscID ArtistID CD Title CDReleaseYear
CategoryID CategoryName
Abbildung 11.3: Um eine Viele-zu-viele-Beziehung nachzubilden, benötigen Sie drei Tabellen: die beiden Entitäten, die verwandt sind, und eine spezielle Tabelle, die nur dazu dient, die beiden zu verbinden.
Die daraus resultierenden Tabellen folgen einem Muster, das Sie immer nutzen können, um eine Viele-zu-viele-Beziehung darzustellen. Ich werde im folgenden Abschnitt detailliert ausführen, wie man sowohl die Eins-zu-viele- als auch die Viele-zu-viele-Beziehung abfragt.
Joins: Mehrere Tabellen auf einmal abfragen Da Sie nun Ihre Daten schön in mehrere Tabellen aufgeteilt haben, bedarf es mehr als der einfachen SELECT-Anfragen, die Sie weiter oben verwendet haben, um die gewünschten Daten zu erhalten. Sie haben nun mehrere unterschiedliche Tabellen, aber jede von ihnen ist mit den anderen über Schlüsselfelder verwandt. Mit genau diesen Schlüsselfeldern können Sie nun die Daten von einer Tabelle mit den entsprechenden Daten einer anderen Tabelle verbinden und einen einzelnen Ausgabesatz erzeugen. Es gibt viele Methoden, um die SELECT-Anweisung zu bilden, wenn Sie mit mehreren Tabellen arbeiten möchten, aber hier sehen Sie ein Beispiel: SELECT Artist.ArtistName, Disc.CDTitle FROM Artist, Disc WHERE Artist.ArtistID = Disc.ArtistID
360
Gängige Datenbankprobleme und -lösungen
Sting Nothing Like The Sun Sting Brand New Day The Barenaked Ladies Maroon The Barenaked Ladies Gordon ...
Wenn an einer SELECT-Anweisung mehr als eine Tabelle beteiligt ist, kann es vorkommen, dass der gleiche Feldname in mehreren Tabellen existiert. In solch einem Fall müssen Sie in Ihrer SELECT-Anweisung das Feld als Tabelle.Feld angeben, um sicherzustellen, dass die Datenbank versteht, welches Feld gemeint ist. Sie können diese Syntax jederzeit benutzen, auch wenn Sie nur mit einer Tabelle arbeiten. Diese Anweisung weist die Datenbank an, alle Datensätze aus der Tabelle Disc abzurufen und für jeden dieser Datensätze einen Lookup in der Tabelle Artist durchzuführen (mit der ArtistID, die in der Tabelle Disc gespeichert ist), um den entsprechenden ArtistName zu finden. Das klingt ziemlich arbeitsintensiv, aber Sie haben Glück, denn die eigentliche Arbeit wird von der Datenbank erledigt, die diese Art von Tätigkeiten ungeheuer schnell abwickelt. Sie haben zwar den ArtistName sorgfältig aus der Tabelle Disc entfernt, Sie können aber immer noch die gleichen Ausgabeergebnisse erzielen, indem Sie die Tabellen Disc und Artist verbinden. Das Verbinden von zwei Tabellen in einer Abfrage, um verwandte Daten aus beiden abzurufen, wird Joining genannt.
Die letzte SQL-Anweisung verwendete eine WHERE-Klausel, um die beiden Tabellen zu verbinden, was sicherlich funktioniert, aber es ist nicht die bevorzugte Methode für Joins. Statt dessen werden Sie eine neue Syntax lernen, in der Sie die Join-Informationen als Teil der FROM-Klausel in Ihrer SQL-Anweisung angeben können. Wenn wir die vorherige Anweisung umformulieren, um die korrekte Syntax zu verwenden, erhalten wir: SELECT Artist.ArtistName, Disc.CDTitle FROM Disc INNER JOIN Artist ON (Artist.ArtistID = Disc.ArtistID)
Diese Anweisung erzeugt das gleiche Ergebnis wie das vorherige Beispiel mit der WHEREKlausel und wird als Inner Join bezeichnet. Es ist möglich, Disc JOIN Artist anzugeben, und das »übersprungene« INNER wird einfach vorausgesetzt, aber Access wird diese kürzere Form nicht akzeptieren.
361
Einführung in Datenbanken
Inner und Outer Joins Bei einem Inner Join gibt die Anfrage nur Datensätze zurück, in denen eine Übereinstimmung zwischen den beiden Tabellen gefunden wurde. In diesem Beispiel bedeutet das Folgendes: Wenn ein Künstler in der Tabelle Artist aufgelistet wurde, aber keinen entsprechenden Datensatz in der Tabelle Disc hat, dann wäre dieser Künstler nicht Teil des Ergebnisses. Wenn das nicht das gewünschte Ergebnis ist und Sie statt dessen jeden einzelnen Künstler, der in der Tabelle Artist aufgelistet ist, in Ihren Ergebnissen wiederfinden möchten (auch wenn Sie von einigen dieser Künstler gar keine Alben haben), könnten Sie einen Outer Join verwenden. Ein Outer Join verknüpft die Tabellen mit den gleichen Informationen. Wenn er aber keine passenden Werte in einer der Tabellen findet, gibt er trotzdem die Felder der ersten Tabelle zurück und Nullwerte (leere Werte) für die Felder, die sich in der zweiten Tabelle befinden. Sie können vor dem OUTER-Schlüsselwort LEFT oder RIGHT angeben, um festzulegen, in welche Richtung der Join arbeiten soll. Sie fragen sich, welche Tabelle die erste ist und welche die zweite? Nun, wenn Sie RIGHT OUTER JOIN angeben, dann wird die Tabelle auf der rechten Seite der JOIN-Anweisung als erste Tabelle angesehen. Geben Sie LEFT OUTER JOIN an, dann wird die Tabelle auf der linken Seite als erste Tabelle angesehen. Nehmen wir einmal an, dass Sie in der Beispieldatenbank die Künstler Limp Bizkit in der Tabelle Artist hätten, aber keine Alben in der Tabelle Disc, die von diesen Künstlern stammen. Der INNER JOIN, der im folgenden Code gezeigt wird, würde eine Reihe von Datensätzen hervorbringen, aber die Künstler Limp Bizkit würden in dem Ergebnis nicht erwähnt. SELECT Artist.ArtistName, Disc.CDTitle FROM Disc INNER JOIN Artist ON (Artist.ArtistID = Disc.ArtistID) ORDER BY Artist.ArtistName
Wenn Sie andererseits einen OUTER JOIN verwenden würden, wie Sie im Folgenden sehen, dann würde das Feld ArtistName mit Limp Bizkit zurückgegeben. Da kein entsprechender Datensatz in der Tabelle Disc gefunden werden könnte, wäre CDTitle für diesen Datensatz null. SELECT Artist.ArtistName, Disc.CDTitle FROM Disc LEFT OUTER JOIN Artist ON (Artist.ArtistID = Disc.ArtistID)
Wie Sie an diesen Beispielen gesehen haben, können die Ergebnisse eines INNER JOIN irreführend sein, da sie nur die Werte enthalten, in denen eine Übereinstimmung zwischen den beiden Tabellen gefunden wurde. Trotzdem ist dieser Join-Typ für die meisten Abfragen geeignet.
362
Gängige Datenbankprobleme und -lösungen
Viele-zu-viele-Beziehungen Um die Viele-zu-viele-Beziehung von CDs zu Kategorien nachzubilden, haben Sie am Ende drei unterschiedliche Tabellen: Disc, Category und eine dritte, die extra für diese Beziehung entworfen wurde: DiscCategory. Um eine Abfrage in diesen drei Tabellen durchzuführen, müssen Sie zwei Joins verwenden – einen Join zwischen Disc und DiscCategory und einen zwischen DiscCategory und Category. Den SQL-Code, um z.B. alle CDs abzurufen, die der Kategorie Pop zugewiesen wurden, sehen Sie hier: SELECT Disc.CDTitle FROM Category INNER JOIN (Disc INNER JOIN DiscCategory ON Disc.DiscID = DiscCategory.DiscID) ON Category.CategoryID = DiscCategory.CategoryID WHERE Category.Category = "Pop"
Wenn Sie mehrere verwandte Tabellen haben, können Sie eingebettete JOIN-Anweisungen ähnlich den obigen verwenden, in denen eine Tabelle eigentlich mit dem Ergebnis des Joins der beiden anderen Tabellen vereinigt wird.
Referenzintegrität Wenn Sie mit mehreren Tabellen arbeiten, besteht die Gefahr der Inkonsistenz. Wie bereits erwähnt, kommt es zu inkonsistenten Daten, wenn ein Künstler von einem oder mehreren Disc-Datensätzen mit der ID referenziert und dieser Künstler gelöscht wird. Um das zu vermeiden, machen Sie der Datenbank die Verknüpfungen zwischen Ihren Tabellen bewusst, indem Sie die Schlüssel und die Beziehung definieren (siehe Abbildung 11.4). Wenn die Datenbank über die Beziehungen in Ihrem System Bescheid weiß, kann sie sie verstärken, indem sie die Löschung eines Datensatzes verhindert, wenn er verwandte Datensätze in anderen Tabellen hat. In Ihrem spezifischen System würde das Platzieren der Beziehungen in der Datenbank verhindern (die Datenbank würde einen Fehler zurückgeben), dass ein Anwender einen Datensatz aus der Tabelle Artist löscht, wenn die Tabelle Disc Datensätze mit der ID dieses Künstlers enthält.
Primärschlüssel erstellen In unserem Beispiel hatten Sie am Ende numerische Schlüssel für alle Tabellen (ArtistID, CategoryID, DiscID). Was wir aber noch nicht erörtert haben, ist, dass Sie diese Schlüssel immer dann erhalten oder generieren müssen, wenn Sie einen neuen Datensatz einfügen. Es gibt verschiedene Möglichkeiten, wie Sie diese Generierung handhaben können: über eingebaute Funktionen der Datenbank, über Schlüssel, die zum Einfügezeitpunkt berechnet werden, über die Verwendung von GUIDs statt einfacher Zahlen oder über ein System zur Verwaltung und Inkrementierung von Schlüsseln, das Schlüsselmanager genannt wird.
363
Einführung in Datenbanken
Abbildung 11.4: In Ihrem Datenbankprogramm können Sie, egal ob es Access oder SQL Server ist, Beziehungen herstellen, die die Datenbank dann erzwingen kann.
Datenbankfunktion Die erste Methode zum Verwalten der Schlüssel ist in den meisten Datenbanken eingebaut: eine spezielle Feldart, die ihren Wert automatisch inkrementiert, wenn eine neue Zeile hinzugefügt wird. Diese Felder sind immer numerisch, sie können so definiert werden, dass sie immer um einen bestimmten Wert, normalerweise 1, erhöht werden und mit einem bestimmten Wert beginnen, normalerweise ebenfalls 1. Das ist eine gute Methode, um Schlüssel zu handhaben, da die Datenbank die ganze Arbeit übernimmt, aber unter bestimmten Umständen kann ein automatisch erzeugter Schlüssel zu Problemen führen. Wenn Sie z.B. eine neue CD zur Sammlung hinzufügen müssen und diese CD von einem Künstler wäre, den Sie noch nicht in Ihrer Datenbank haben, müssten Sie zwei INSERTs durchführen – einen in der Tabelle Artist und dann einen in der Tabelle Disc. Bei der Verwendung von automatisch generierten Schlüsseln würden Sie beim ersten INSERT keinen Wert für ArtistID angeben. Statt dessen würde automatisch eine ArtistID generiert. Beim zweiten INSERT (in Disc) würden Sie diesen neuen ArtistID-Wert aber benötigen, um Ihre neue CD mit dem neuen Künstler zu verknüpfen. Wenn Sie nur SQL verwenden, wird Ihnen dieser Wert nicht zurückgegeben. Sie könnten eine weitere Abfrage der Datenbank durchführen, aber Sie müssten Felder wie ArtistName verwenden, um sie wiederzufinden, und das wäre anfällig für Fehler. Dieses Problem ist ein Grund, warum Sie eine alternative Methode zum Erhalt der Schlüssel benötigen. Ein weiterer Grund wird später im Abschnitt »Globally Unique Identifiers (GUIDS)« besprochen.
364
Gängige Datenbankprobleme und -lösungen
Berechnete Schlüssel Als Alternative zu den von der Datenbank generierten Schlüsseln können Sie SQL-Anweisungen verwenden, um die nächste ID in der Folge zu bestimmen, ehe Sie den eigentlichen INSERT durchführen. Dazu gehört die Verwendung eines besonderen SQLOperators´, der Aggregat genannt wird und einen berechneten Wert abruft, der auf allen Feldern basiert, die von einer SELECT-Klausel gefunden wurden. Es gibt mehrere dieser Aggregate. Sie werden eines verwenden, das MAX heißt und den höchsten Wert für ein Feld in allen ausgewählten Datensätzen zurückgibt. Um die nächste ID zu bestimmen, könnten Sie diese Anweisung ausführen (mit der Tabelle Artist als Beispiel): SELECT MAX(ArtistID) FROM Artist
Diese Anweisung würde einen einzelnen Datensatz mit einem einzelnen Feld zurückgeben, das den höchsten ID-Wert enthält, der sich derzeit in der Tabelle befindet. Addieren Sie 1 zu diesem Wert hinzu und schon haben Sie die nächste ID, die Sie dann in einem INSERT verwenden. Dieses Verfahren birgt zwei große Probleme, auch wenn es unter einigen Umständen zulässig ist. Das erste Problem tritt auf, da mehr als ein Programm gleichzeitig mit dieser Datenbank arbeiten könnte – es ist möglich, dass zwei oder mehr Anwender Künstler gleichzeitig hinzufügen. In einer solchen Situation wäre es möglich, dass weitere Anwender die erste SELECT-Anweisung ausgeführt haben, ehe der erste Anwender den INSERT durchgeführt hat, und daher würden alle Anwender den gleichen ID-Wert erhalten. Dieses Problem kann mit Transaktionen verhindert werden, einem weiteren Datenbankkonzept, das ich in der heutigen Lektion nicht näher beschreiben werde. Das zweite Problem lässt sich nicht so leicht umgehen. Dieses Problem tritt auf, wenn ein oder mehrere Datensätze am Ende der Datenbank (also mit den höchsten IDs) gelöscht werden. Jeder neue Datensatz, der hinzugefügt wird, wird zuerst MAX(ArtistID) auswählen und wird dann einen ID-Wert erhalten, der früher einem gelöschten Datensatz zugeordnet war. In einem System, in dem ID-Werte eindeutig sein sollen, ist es nicht immer ratsam, wenn unterschiedliche Datensätze zu unterschiedlichen Zeitpunkten die gleiche ID haben.
Globally Unique Identifiers (GUIDs) In einigen Situationen ist ein inkrementierter Schlüssel – egal ob er von der Datenbank oder durch Ihre eigenen Berechnungen erstellt wird – nicht geeignet. Dies wäre z.B. der Fall bei verteilten Datenbanken, bei denen mehrere Kopien der Datenbank existieren und regelmäßig wieder zusammengeführt werden, wobei in jede der Kopien etwas eingefügt werden kann. Wenn Sie einen sich inkrementierenden Schlüssel verwenden würden, dann
365
Einführung in Datenbanken
würde es zu doppelten IDs kommen, wenn an mehr als einer Stelle ein Datensatz eingefügt wurde. Eine Lösung ist es, eine Art zufällig erzeugter ID zu nutzen, die zufällig genug ist, damit IDs an mehreren Stellen kaum jemals gleich sein können. Die allgemein akzeptierte Methode für das Erzeugen von zufälligen IDs ist die Verwendung eines GUIDs (Globally Unique Identifier), einer 128-Bit-Zufallszahl, die von der Datenbank oder vom Betriebssystem generiert wird. Ein GUID ist zufällig genug, um ihn in einem verteilten Datenbank-Szenario verwenden zu können.
Schlüsselmanager Das letzte ID-Erzeugungsschema, das ich erörtern werden, ist für Systeme geeignet, die nicht verteilt sind (siehe den vorherigen Abschnitt über GUIDs), für die Sie aber den Schlüssel sofort beim Hinzufügen des Datensatzes erhalten müssen. (Das macht einen datenbankgenerierten Schlüssel problematisch.) Diese Methode, Schlüsselmanager-System (Key Manager System) genannt, verwendet eine zusätzliche Tabelle in Ihrer Datenbank, um den derzeit höchsten ID-Wert für alle Ihre anderen Tabellen zu speichern (siehe Abbildung 11.5). Key Manager TableName Disc Artist Category
HighestID 35 7 12
Abbildung 11.5: Die Schlüsselmanager-Methode zum Erzeugen von Schlüsseln erfordert eine zusätzliche Tabelle, die für jede Tabelle, die diese Methode verwenden wird, die höchsten ID-Werte enthält.
Um mit dieser Methode neue IDs zu erhalten, müssen sie die Key Manager-Tabelle nach dem aktuellen ID-Wert abfragen, diesen um 1 erhöhen, in Ihren Datensatz einfügen und die Key Manager-Tabelle mit dem inkrementierten ID-Wert aktualisieren. Die vier einzelnen Aktionen bedeuten, dass diese Methode die Probleme von mehreren Anwendern mit der Berechneter-Schlüssel-Methode gemeinsam hat. Das Problem, dass Schlüssel wegen Löschvorgängen zweimal verwendet werden, wird vermieden, da der ID-Wert, der im Key Manager gespeichert ist, niemals dekrementiert wird und IDs niemals wiederverwendet werden.
Gespeicherte Prozeduren und auto-generierte Schlüssel Bei der Beschreibung des selbst-inkrementierenden Datentyps als Möglichkeit zur Erzeugung von Schlüsseln habe ich erwähnt, dass es schwierig ist, den Schlüssel nach einem INSERT zu erhalten, und dass dies ein Hauptgrund ist, eine andere Methode für die Erzeugung von Schlüsseln zu wählen. Obwohl diese Bemerkungen sicherlich zutreffen, gibt es eine allgemein verwendete Methode zur Beschaffung eines vom System erzeugten Schlüs-
366
Die Beispieldatenbank erstellen
sels nach einem INSERT. Diese Methode steht nur zur Verfügung, wenn Sie Ihre Datensätze mit einer gespeicherten Prozedur einfügen, einem Verfahren, das ich in diesem Buch nicht ausführlich erörtern werde. Wenn Sie mehr Informationen zu dieser Methode des Einfügens von Datensätzen und zum Erhalten der neu erzeugten ID erhalten möchten, suchen Sie im MSDN nach @@IDENTITY.
11.4 Die Beispieldatenbank erstellen Für die Beispiele im Rest der heutigen Lektion, und für den Code, den Sie am Tag 12, »Mit .NET auf Daten zugreifen«, schreiben werden, benötigen Sie Ihre eigene Kopie der Beispieldatenbank. Sie könnten zwar den Prozess der manuellen Datenbankerzeugung durchlaufen – und das ist gar keine so schlechte Idee, wenn Datenbanken neu für Sie sind –, ich werde aber versuchen, es etwas einfacher zu machen, indem ich Ihnen drei verschiedene Datenbankoptionen biete, die Sie auf Ihrem Rechner einrichten können. Die drei Versionen zielen auf drei unterschiedliche Datenbanksysteme ab: Microsoft SQL Server (2000), Microsoft Database Engine (MSDE 2000) oder Microsoft Access. SQL Server und MSDE unterstützen die gleiche Download-Datei, wodurch Sie ein einzelnes Setup für beide Systeme verwenden können. Alle diese Optionen funktionieren, aber das beste Produkt, das Sie für diese und zukünftige Übung benutzen können, ist SQL Server. Es handelt sich dabei um einen beliebten Datenbankserver und auf ihm werden Sie höchstwahrscheinlich auch arbeiten, wenn Sie mit dem Erstellen von echten Systemen beginnen. MSDE ist kostenlos, was ein tolles Anegbot für ein recht komplettes Datenbanksystem ist, aber es stellt Ihnen nur einige Werkzeuge zur Verfügung, mit denen Sie mit Daten von außerhalb Ihres Programms arbeiten können. Access ist in vielen verschiedenen unterschiedlichen Versionen von Office 2000/XP enthalten und bietet eine Vielzahl von Werkzeugen zum Betrachten von Daten, zum Erstellen und Bearbeiten von Tabellen und sogar zum Erstellen Ihrer SQL-Anweisungen (den Query Builder), sodass es keine schlechte Sache ist, wenn Sie bereits darüber verfügen. Schauen Sie nach, welches Produkt Sie haben, und folgen Sie dann den entsprechenden Anleitungen in den folgenden Abschnitten. Wenn Sie Access, MSDE oder SQL Server nicht haben, können Sie einfach die .mdb-Datei herunterladen (die im folgenden Abschnitt über Access behandelt wird) und diese verwenden, ohne dass der Rest der Datenbanksoftware installiert ist.
367
Einführung in Datenbanken
Die notwendigen Dateien herunterladen Die notwendigen Dateien können Sie von der Website für die amerikanische Originalversion dieses Buches herunterladen. Auf der Begleit-CD zu diesem Buch finden Sie sie im Verzeichnis Beispiele/Kap10. In beiden Fällen sollten Sie die folgenden Dateien finden: 쐽
CD.mdf und CD.ldf – Zusammen enthalten diese beiden Dateien alle Daten für die
Verwendung mit SQL Server oder MSDE. 쐽
AttachDB.vbs – Ehe die .mdf- und .ldf-Dateien von Ihrem Code verwendet werden können, muss diese VBScript-Datei (.vbs) ausgeführt werden, um sie mit Ihrem loka-
len SQL Server oder MSDE zu verbinden. 쐽
CD.mdb – Die Access-Datenbank, die Sie verwenden können, wenn Sie kein SQL Ser-
ver oder MSDE haben. 쐽
TestDB.vb – Ein Beispielprogramm in Visual Basic. NET, mit dem Sie die Einrichtung
Ihrer Datenbank testen können. 쐽
Orders.txt – Diese Datei wird von der zweiten Übung am Ende der heutigen Lektion
verwendet.
Access 2000 oder Access 2002 Für Anwender mit Access oder ganz ohne Datenbanksystem ist kein echtes Setup erforderlich; Sie müssen nur den Pfad zu der .mdb-Datei kennen, um sie in den Beispielen zu verwenden. Wenn Sie Access verwenden möchten, um mit den Daten zu arbeiten, ist der einzige wichtige Punkt, ob Sie eine andere Version von Access als die haben, mit der dieses Beispiel erstellt wurde. Wenn das der Fall ist, werden Sie standardmäßig nur in der Lage sein, die verschiedenen Tabellen und anderen Objekten in Ihrer Datenbank über Access zu betrachten, ohne sie bearbeiten zu können. Benutzen Sie ruhig die Datenbankumwandlungswerkzeuge in Access, um die Datenbank auf die gleiche Version aufzurüsten, mit der Sie arbeiten.
MSDE und SQL Server 2000 Für MSDE und SQL Server stellen die beiden Dateien (CD.mdf und CD.ldf) eine getrennte Datenbank dar. Es ist möglich, diese Datenbank in Ihrer eigenen Kopie von SQL oder MSDE wieder zusammenzufügen. Code, der in der Skriptdatei AttachDB.vbs enthalten ist, wird diese beiden Dateien mit Ihrem Datenbanksystem verknüpfen. Die Datei sollte zwar in den meisten Fällen so wie sie ist funktionieren, sie enthält aber die Benutzer-ID und das Passwort für den Systemadministrator im Code, welche auf die Voreinstellung sa und auf
368
Das Setup mit System.Data testen
ein leeres Passwort gesetzt sind. Wenn Ihr System kein leeres Passwort für das sa-Benutzerkonto hat, müssen Sie diese Datei bearbeiten. Diese Skriptdatei soll außerdem im gleichen Verzeichnis wie die .mdf und .ldf-Dateien und auf dem gleichen System wie Ihr Datenbanksystem ausgeführt werden (durch doppeltes Anklicken). Fahren Sie fort und klicken Sie die Datei doppelt an, nachdem Sie alle notwendigen Veränderungen an der Benutzerkennung und dem Passwort vorgenommen haben, die im Code gespeichert sind.
11.5 Das Setup mit System.Data testen Da Sie Ihre Beispieldaten nun heruntergeladen und eingerichtet haben, können Sie eine schnelle kleine Konsolenanwendung in Visual Basic .NET erstellen, um zu bestätigen, dass alles wie beabsichtigt funktioniert. Dafür müssen Sie den Namensraum System.Data mit dem .NET-Framework verwenden. Diese Klassen enthalten alles, was Sie benötigen, um auf die Daten in fast jeder Datenbankart zuzugreifen. Sie sind aufgeteilt in zwei Hauptbereiche – Klassen, die entworfen wurden, um auf Microsoft SQL Server (einschließlich MSDE) zuzugreifen, und Klassen, die entworfen wurden, um auf jede Datenbank zuzugreifen, die einen OLE DB-Treiber hat (was fast jede Datenbank hat). Ich werde nicht allzu genau darauf eingehen, wie diese Klassen funktionieren. Statt dessen wird das folgende Beispiel ganz einfach testen, ob Ihre Datenbank korrekt eingerichtet ist, bevor wir zur morgigen Lektion übergehen, in der wir uns ausführlich mit dem Datenzugriff über .NET beschäftigen werden. Der Code, den Sie verwenden werden, um die Datenbank zu testen, wird mit der OLE DB-Klassengruppe von System.Data.OLEDB eine Verbindung zur Datenbank einrichten. Sie könnten die Klassen System.Data.SqlClient verwenden, wenn Sie SQL Server oder MSDE haben. Da es aber auch möglich ist, dass Sie Access haben, wäre es einfacher, die Klassen zu verwenden, die auf beide Datenbankarten zugreifen können. Bei beiden Methoden der Datenbankverbindung wird die wichtigste Information Verbindungszeichenkette genannt: ein Text, der alle Informationen liefert, die für die Verbindung zu Ihrer Datenbank benötigt werden. Dies ist das einzige Codestück, das verändert werden muss, um Ihrem speziellen Setup zu entsprechen. Listing 11.1 enthält die gesamte Prozedur und ich werde nach dem Listing weitere Informationen über die Verbindungszeichenkettenwerte liefern. Listing 11.1: TestDB.vb 1 2 3 4 5
'Ersetzen Sie die Verbindungszeichenkette durch den 'entsprechenden Wert für Ihr Setup (wird am Tag 11 erklärt) 'und kompilieren Sie sie dann mit: ' "vbc /r:System.DLL /r:System.Data.DLL TestDB.vb" Imports System
369
Einführung in Datenbanken
6 Imports System.Data 7 Module TestDB 8 Sub Main() 9 Dim sConnectionString, sSQL As String 10 sConnectionString = < see below > 11 12 sSQL = "SELECT CDTitle FROM Disc ORDER BY ArtistID Asc" 13 14 Dim connCD As New OleDb.OleDbConnection(sConnectionString) 15 Dim cmdCD As New OleDb.OleDbCommand(sSQL, connCD) 16 Dim drCD As OleDb.OleDbDataReader 17 connCD.Open() 18 drCD = cmdCD.ExecuteReader() 19 Do While drCD.Read() 20 Console.WriteLine(drCD.Item(”CDTitle”)) 21 Loop 22 23 drCD.Close() 24 connCD.Close() 25 Console.ReadLine() 26 End Sub 27 End Module
Wenn Sie die Access-Datenbank (CD.mdb) verwenden, enthält Ihre Verbindungszeichenkette den vollständigen Pfad zu dieser Datei, etwa folgenden: ???Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\ chapter11\ cd.mdb
Achten Sie darauf, diese Verbindungszeichenkette zu ändern, um den richtigen Pfad zu CD.mdb auf Ihrem Rechner zu kennzeichnen. Wenn Sie SQL Server oder MSDE verwenden und AttachDB.vbs bereits erfolgreich ausgeführt haben, sollte die folgende Zeichenkette auf Ihrem Rechner funktionieren: Provider=SQLOLEDB.1;User ID=sa;Initial Catalog=CD;Data Source=(local)
Diese SQL-Verbindungszeichenkette (die in diesem Buch auf zwei Zeilen umbrochen ist, in Ihrem Code aber nur eine Zeile umfassen sollte), geht von einem leeren Passwort für das sa-Benutzerkonto aus, Sie können diese Zeichenkette aber verändern, um ein Passwort anzugeben, wenn dies erforderlich ist: Provider=SQLOLEDB.1;User ID=sa;Password=peanutbutter;Initial Catalog=CD;Data Source=(local)
Setzen Sie die entsprechende Verbindungszeichenkette in das Listing 11.1 ein. Beachten Sie, dass dieser Code als testdb.vb in dem Archiv Chapter11.zip enthalten ist, die Sie weiter oben von der Website dieses Buchs heruntergeladen haben. Kompilieren Sie nun den Code, wie es in den Kommentaren zu Beginn der Datei angegeben ist, und Sie sollten eine
370
Zusammenfassung
.exe-Datei erhalten, mit der Sie Ihre Datenbankverbindung testen können. Wenn alles
funktioniert, sollten Sie die folgende Ausgabe erhalten: Left Of The Middle the tragically hip Road Apples Day for Night Phantom Power Brand New Day Mercury Falling Fields of Gold Classic Queen 20 Greatest Christmas Songs Gordon Born on a Pirate Ship Maroon Tails Firecracker Janet The Velvet Rope Design Of A Decade 1986-1996 Mad Season Music
Mit dieser Ausgabe haben Sie eine Anwendung erstellt, die sich erfolgreich mit einer Datenbank verbindet, und Sie sind bereit, mit Datenbankthemen für Fortgeschrittene weiterzumachen.
11.6 Zusammenfassung Die heutige Lektion behandelte die Einführung in Datenbanken und ihre Rolle beim Erstellen von .NET-Anwendungen. Wenn Sie weiterhin Visual Basic .NET-Systeme erstellen, werden Sie häufig eine Datenbank verwenden, unabhängig von dem Industriezweig, für den Sie Ihre Programme erstellen. Sie werden häufig Programme schreiben, die eine bereits vorhandene Datenbank verwenden. Manchmal wird es zu Ihrem Job gehören, diese Datenbank selbst zu entwerfen und zu implementieren, eine Aufgabe, die recht komplex sein kann. In der morgigen Lektion werden Sie auch weiterhin mit Datenbanken arbeiten. Sie werden von der Theorie zur Praxis kommen und einige Programme schreiben, die System.Data-Klassen verwenden.
371
Einführung in Datenbanken
11.7 Fragen und Antworten F
In der heutigen Lektion haben Sie drei unterschiedliche Arten von Datenbanken erwähnt, aber sind Microsoft-Datenbanken die einzige Art, mit der ich mich verbinden kann? A
F
In der CD-Datenbank haben Sie die Künstlerinformationen aus der Tabelle Disc entfernt, aber dann müssen Sie sie mit einem Join in unserer SQL-Anweisung in fast jede Ausgabe wieder hineinbringen. Wäre es nicht besser, einfach mit jedem DiscDatensatz eine Kopie der Künstlerinformationen zu speichern? A
F
Ganz und gar nicht. Jede Datenbank, für die Sie über einen OLE DB-(oder ODBC-)Treiber verfügen, steht Ihnen zur Verfügung, um sich mit den System.Data.OleDB-Klassen mit ihr zu verbinden. Wenn Sie keinen OLE DB-Provider haben, aber einen ODBC-Treiber, können Sie sich immer noch verbinden, indem Sie über eine zusätzliche Schicht gehen und den OLE DB-Provider für ODBC-Treiber verwenden. Mehr Informationen zu OLE DB finden Sie unter http://www.microsoft.com/data.
Das Speichern von Daten an mehr als einem Ort bedeutet, dass Sie sicherstellen müssen, dass Sie alle möglichen Orte auf einmal aktualisieren, um eine Inkonsistenz zu vermeiden. Die Vorteile, die Sie dadurch erhalten, dass Sie den Join in Ihren Abfragen umgehen, gehen durch die zusätzliche Arbeit verloren, die immer dann notwendig ist, wenn Sie Datensätze in der Tabelle Disc oder Artist aktualisieren.
XML wurde zum Speichern von Daten entworfen. Ersetzt es Datenbanken? A
XML ist eine Möglichkeit, Daten zu speichern, und obwohl es sicherlich ein Ersatz für einige Flat-File-Datenbanksysteme ist, ersetzt es jedoch nicht die relationalen Datenbanksysteme wie SQL Server. XML wird meistens als Methode verwendet, um Daten zwischen Systemen auszutauschen und kann oft die Ausgabe sein, die Sie aus Ihren traditionellen Datenbanksystemen erzeugen.
11.8 Workshop Die Antworten und Lösungen finden Sie im Anhang A.
372
Workshop
Quiz 1. In einer Datenbanktabelle wird ein Datensatz eindeutig durch ein oder mehrere Felder identifiziert. Wie werden diese Felder oder dieses Feld genannt? 2. Wenn man von den beiden Tabellen ausgeht, die in Abbildung 11.6 dargestellt sind, was wäre dann das Ergebnis der hier gezeigten SQL-Anweisung? SELECT Make.MakeName, Model.ModelName FROM Make LEFT OUTER JOIN Model ON Make.MakeID = Model.Make Make MakeID 1 2 3 4 5
MakeName Ford Audi BMW Pontiac Toyota
Model ModelID 1 2 3 4 5 6 7
Make 1 1 4 4 4 5 5
ModelName Mustang Explorer Grand Am Grand Prix Aztek Rav 4 Camry
Abbildung 11.6: Anlage für die Quiz-Frage 2.
3. Wenn Sie wiederum von den Tabellen in Abbildung 11.6 ausgehen, was wäre dann die Ausgabe dieser SQL-Anweisung? SELECT Make.MakeName, Model.ModelName FROM Make INNER JOIN Model ON Make.MakeID = Model.Make
Übungen 1. Wenn Sie Ihre CD-Bibliothek so ausdehnen wollten, dass Sie die Sammlung von mehr als einem Anwender verfolgen können, wie würden Sie Ihre Datenbank verändern? Denken Sie daran, dass Sie Informationen über die Anwender selbst und Eigentumsinformationen für die CDs verfolgen müssen. 2. Wenn man von der Datenmenge ausgeht, die in Orders.txt (Teil der Dowload-Datei Chapter11.zip) enthalten ist, wie würden Sie eine Datenbank entwerfen, die die gleichen Informationen speichern soll?
373
Mit .NET auf Daten zugreifen
Mit .NET auf Daten zugreifen
In der gestrigen Lektion haben wir Ihnen Datenbanken vorgestellt und gezeigt, wie Sie diese für verschiedene Anwendungen verwenden können. Heute werden Sie lernen, wie Ihre Programme mit Daten aus Visual Basic .NET funktionieren.
Themen heute 쐽
Überblick über die Architektur des Datenzugriffs in .NET
쐽
Wie man eine Verbindung zu einer Datenbank herstellt
쐽
Wie man SQL-Anweisungen ausführt
쐽
Verwendung der Datenbindung mit Windows und Webformularen
Zusätzlich zu diesen Themen werden Sie am Ende der heutigen Lektion einige der komplizierteren Konzepte und Verfahren der Arbeit mit Datenbanken von .NET aus kennen lernen.
12.1 Überblick über den Datenzugriff in .NET Datenbanken werden in fast allen Geschäftsanwendungen sowie in vielen PC- und Desktop-Systemen verwendet. In der einen oder anderen Form gab es sie schon vor der Erfindung von Visual Basic. Diese lange Geschichte der Datenbanken und ihrer Verwendung in Computerprogrammen haben dafür gesorgt, dass Visual Basic-Programme schon seit den ersten Versionen auf Daten und Datenbanken zugreifen. Die Verfahren des Datenzugriffs mit Visual Basic haben sich im Laufe der Zeit weiterentwickelt und vor der Einführung von .NET bereits viele andere Versionen durchlaufen.
ADO und OLE DB Es ist nicht besonders sinnvoll, die ganze Geschichte des Datenzugriffs mit Visual Basic durchzugehen. Ein kurzer Blick auf die neuesten Verfahren des Datenzugriffs (vor .NET) lohnt sich hingegen durchaus. Vor .NET betrachtete man die von Visual Basic 6.0-Programmierern intensiv genutzte Datenzugriffsbibliothek ActiveX Data Objects (ADO) als das wichtigste Mittel, um in Visual Basic oder Visual Basic for Applications (VBA) geschriebene Programme mit eigentlich allen Back-End-Datenbanken zu verbinden. ADO war eine COM-Bibliothek, die die Funktionalität der eigentlichen Datenzugriffstechnologie OLE DB kapselte. Sie wurde dafür entworfen, die Arbeit mit dieser neuen Technologie zu erleichtern.
376
Überblick über den Datenzugriff in .NET
Damit Sie nicht Code schreiben müssen, der mit jeder Datenbank funktioniert, wählte ADO/OLE DB einen ähnlichen Ansatz wie ODBC (http://www.microsoft.com/data/odbc): Es bietet eine einzige datenbankunabhängige Programmierschnittstelle und verwendet für die einzelnen Datenbanken spezielle Treiber. Dieses Konzept war schon zuvor beliebt und es gab für fast jedes Datenbanksystem einen ODBC-Treiber. Auch ADO funktioniert nur mit Treibern, obwohl man sie hier OLE DB-Provider nannte. Für viele der beliebtesten Datenbanken stehen Provider zur Verfügung. Da ADO aber jünger ist als ODBC, stehen für ADO auch weniger Treiber zur Verfügung. Der ODBC-Treiber genügt jedoch, da ADO mit einem OLE DB-Provider für ODBC-Treiber ausgeliefert wird. Durch die Kombination der verfügbaren OLE DB-Provider und aller verfügbaren ODBC-Treiber kann man mit ADO Verbindungen zu fast allen existierenden Datenbanksystemen herstellen. Die Gesamtarchitektur von ADO ist so aufgebaut, dass der Code nicht mit dem Datenbanksystem selbst, sondern nur mit ADO arbeitet und ADO die Anfragen mithilfe eines OLE DB-Providers übersetzt und in das Datenbanksystem transportiert (vgl. Abbildung 12.1). ADO verfügt über mehrere Schlüsselobjekte, die die Datenbankverbindung, die Befehle an die Datenbank und die mit den Anfragen erhaltenen Ergebnisse repräsentieren. Alle diese Objekte sind so entworfen, dass Sie leicht als Teil eines Visual Basic-Programms zu verwenden sind.
ADO.NET ADO.NET ist die Datenzugriffstechnologie, die zum .NET-Framework gehört. Für Sie ist es das Mittel, mit dem Sie an die Daten herankommen, wenn Sie in Visual Basic .NET programmieren. Es ist der nächste Schritt nach ADO/OLE DB. Die zugrunde liegende OLE DB-Technologie ist noch immer vorhanden und OLE DB-Provider sind nach wie vor das wichtigste Medium, über das ADO.NET mit einzelnen Datenbanksystemen kommuniziert. Aber oberhalb dieser Ebene erinnert nur noch wenig an die früheren ADOObjekte. In .NET wird der Zugriff auf Datenbanken von Klassen in und unterhalb des Namensraums System.Data behandelt. Dieser Namensraum besteht aus zwei einzelnen Bereichen: den Klassen von System.Data.OleDB und den Klassen von System.Data.SQLClient. Die erste Gruppe, System.Data.OleDB, soll es Ihnen ermöglichen, Verbindungen zu allen Datenbanken herzustellen, für die Sie einen OLE DB-Provider oder (durch den OLE DB-Provider für ODBC) einen ODBC-Treiber haben. Technisch betrachtet sind diese Klassen gleichbedeutend mit der ursprünglichen ADO-Ebene für den Datenzugriff. Die zweite Gruppe, System.Data.SQLClient, ist ausschließlich für die Arbeit mit Microsoft SQL Server entworfen, bietet ansonsten aber eine ähnliche Funktionalität wie die OLE DB-Klassen. Die Abbildung 12.2 zeigt die allgemeine Architektur der .NET-Klassen für den Datenzugriff und veranschaulicht die beiden Hauptsysteme.
377
Mit .NET auf Daten zugreifen
Visual Basic-Anwendungen
ActiveX Data Objects (ADO)
OLE DB Provider SQL
Daten
Oracle
Daten
Sybase
Daten
Abbildung 12.1: ADO ist eine Ebene oberhalb von OLE DB, die mithilfe von OLE DB und der OLE DB-Treiber Verbindungen zu den Datenbanken herstellt.
In den Beispielen der heutigen Lektion werden Sie lernen, wie Sie sowohl mit OLE DBDatenbanken als auch mit Microsoft SQL Server oder MSDE-Systemen arbeiten. Selbst wenn Sie nur SQL Server haben, können Sie mit beiden Verfahren experimentieren, denn Sie können eine Verbindung zu SQL Server nicht nur über den entsprechenden OLE DBProvider herstellen, sondern auch über die neuen .NET-Klassen. Zusätzlich zu den SQLClient- und OleDB-Klassen für den Zugriff auf Datenbanken umfasst System.Data auch eine Vielfalt anderer Klassen, die die Arbeit mit Daten unabhängig von der jeweiligen Datenquelle ermöglichen. In den heutigen Beispiele werden Sie diese anderen Klassen kennen lernen, insbesondere die Klasse DataSet.
12.2 Standardaufgaben bei Datenbanken In diesem Abschnitt behandeln wir die System.Data-Klassen nicht in Form einer Referenz zu den einzelnen Klassen, sondern konzentrieren uns auf die allgemeinen Aufgaben, die Sie durchführen werden.
378
Standardaufgaben bei Datenbanken
System. Data (enthält Objekte für die generische Datenbehandlung)
System. Data.SqlClient (enthält Objekte, die spezifisch für Verbindungen mit Microsoft SQL Server sind)
System. Data.OLEDB (enthält Objekte, die spezifisch für Verbindungen mit OLE DB Provider sind)
System. Data.ODBC* (enthält Objekte, die spezifisch für Verbindungen mit OLE DB Treiber sind)
Abbildung 12.2: ADO.NET ist in zwei Teile zerlegt, OLE DB und SQL, die Verbindungen zu einer Vielzahl von Datenquellen ermöglichen.
Hierzu gehören z.B. die Herstellung einer Datenbankverbindung, die Ausführung einer SQL-Anweisung und das Abrufen von Daten. Diese Aufgaben stellen einen natürlichen Ausgangspunkt für die Behandlung der zugrunde liegenden Objekte dar.
Eine Verbindung zur Datenbank herstellen Bevor Sie mit Daten arbeiten können, müssen Sie eine Verbindung herstellen. Die Datenbankverbindung repräsentiert und speichert alle Einstellungen, die erforderlich sind, damit Sie Ihre Datenbank finden und sich bei ihr anmelden können. Bei früheren Versionen von ADO bestand diese Verbindung aus zwei Hauptteilen: dem Verbindungsobjekt und einer Verbindungszeichenkette. Die Verbindungszeichenkette ist eine Textzeile, die einige oder alle für die Herstellung der Datenbankverbindung notwendigen Informationen enthält. Sie ist auch in ADO.NET noch immer der Schlüssel zur Einrichtung einer Datenbankverbindung, obwohl es hier nun zwei verschiedene Verbindungsobjekte gibt: eines für OLE DB und eines für SQL Server. Bevor Sie damit beginnen können, Code zu schreiben, sollten Sie sich die für Ihre Datenbank passende Verbindungszeichenkette besorgen. Diese ist zwar lediglich ein Textwert, den Sie auch manuell erstellen könnten, aber mit einem kleinen Trick können Sie die richtige Zeichenkette ganz einfach bekommen.
379
Mit .NET auf Daten zugreifen
Als ADO auf Ihrem Rechner installiert wurde – da es im Rahmen der Installation von .NET installiert wird, haben Sie es auf jeden Fall –, wurde ein besonderer Dateityp registriert: der Microsoft-Datenlink. Dateien dieses Typs sind dafür entworfen, Informationen über Datenbankverbindungen zu speichern. Falls Sie eine leere Datei dieses Typs anlegen, wird eine komfortable grafische Benutzeroberfläche bereitgestellt, mit der Sie alle Details der Verbindung einrichten und bearbeiten können. Nachdem Sie mit der grafischen Benutzeroberfläche gearbeitet und die für Ihre Datenbank richtigen Einstellungen gewählt haben, enthält die Datei die Verbindungszeichenkette, die Sie kopieren und in Ihrer Visual Basic .NET-Anwendung verwenden können. Um diesen Trick selbst anzuwenden, gehen Sie folgendermaßen vor: 1. Erstellen Sie eine leere Microsoft-Datenlink-Datei, indem Sie eine neue Textdatei anlegen (klicken Sie mit der rechten Maustaste auf den Desktop und wählen Sie aus dem Kontextmenü NEU und dann TEXTDATEI aus) und ihr einen neuen Namen mit der Erweiterung .udl geben (New.udl wäre perfekt). Wenn Sie die Dateierweiterungen nicht sehen, wird dies schwierig zu bewerkstelligen sein. Die Anzeige der Dateierweiterungen können Sie im Menü ORDNEROPTIONEN aktivieren. Nachdem Sie die Dateierweiterung geändert haben, sollte sich das Dateisymbol ändern und damit einen neuen Dateityp signalisieren. 2. Durch einen Doppelklick auf die neue Datei erscheint ein Dialogfenster mit vier Registerkarten. Hier können Sie die Verbindungsinformationen für Ihre Datenbank einrichten und bearbeiten (siehe Abbildung 12.3)
Abbildung 12.3: Mit dem Dialogfenster DATENLINKEIGENSCHAFTEN können Sie nach und nach die Einzelheiten einer Verbindung einrichten.
380
Standardaufgaben bei Datenbanken
3. Beginnen Sie bei der Registerkarte PROVIDER und setzen Sie die für Ihre Datenbank richtigen Informationen. 왘
Für die Access-Datenbank CD.mdb wählen Sie den PROVIDER MICROSOFT JET 4.0.
왘
Für SQL Server und die MSDE-Datenbank wählen Sie den MICROSOFT OLE DB PROVIDER FOR SQL SERVER.
4. Auf der Registerkarte VERBINDUNG sehen Sie – je nachdem, welchen der zwei möglichen Provider Sie auf der ersten Registerkarte gewählt haben – verschiedene Optionen. Für Access brauchen Sie nur den Pfad zu der Datei CD.mdb einzugeben (siehe Abbildung 12.4).
Abbildung 12.4: Die Registerkarte VERBINDUNG sieht je nach gewähltem Provider anders aus; die Optionen für Access umfassen den Pfad zu der Datenbankdatei.
Für SQL müssen Sie den Servernamen eingeben. Falls Ihr SQL Server mit Ihrem Rechner identisch ist, geben Sie statt dessen (local) ein. Außerdem liefern Sie den Benutzernamen und das Passwort und geben an, zu welcher Datenbank (in diesem Fall CD) Sie eine Verbindung herstellen möchten. Abbildung 12.5 zeigt die Registerkarte VERBINDUNG, nachdem wir sie für einen lokale SQL Server-Datenbank konfiguriert haben, die den Benutzernamen »sa« und ein Passwort verwendet, das zwar eingegeben wurde, aber nicht zu lesen ist. 5. Es gibt zwar noch zwei weitere Registerkarten, aber Sie haben bereits alle erforderlichen Informationen angegeben. Klicken Sie auf die Schaltfläche VERBINDUNG TESTEN, um zu sehen, ob Sie die richtigen Informationen eingegeben haben. Wenn der Test erfolgreich verläuft, schließen Sie das Dialogfenster, indem Sie auf OK drücken.
381
Mit .NET auf Daten zugreifen
Abbildung 12.5: Bei SQL Server bestehen die Verbindungsdetails aus dem Servernamen, dem Datenbanknamen sowie dem Benutzernamen und dem Passwort, die für die Herstellung der Verbindung erforderlich sind.
Nun öffnen Sie die .udl-Datei mit Notepad oder einem anderen Texteditor, damit Sie ihren Inhalt prüfen können. Der Inhalt sollte ungefähr wie nachfolgend angegeben aussehen. Die Einzelheiten hängen allerdings davon ab, welche Informationen Sie im Dialogfenster eingegeben haben. [oledb] ; Alles hinter dieser Zeile ist eine OLE DB; Initialisierungszeichenkette Provider=Microsoft.Jet.OLEDB.4.0; Data Source=C:\ CD.mdb; Persist Security Info=False
Die letzte Zeile dieser Datei, die mit Provider beginnt, ist eine OLE DB-Verbindungszeichenkette, die Sie in Ihrem Visual Basic .NET-Programm benötigen. Wenn Sie eine Access-Datenbank verwenden, dann sollte Ihre Zeichenkette bis auf den Pfad mit der oben gezeigten Zeichenkette übereinstimmen. Das folgende Beispiel zeigt den Inhalt einer .udl-Datei, die für SQL Server konfiguriert wurde. Damit diese Zeichenkette auf Ihrem System funktioniert, müssen Sie wahrscheinlich die Werte für userid, password und Data Source ändern: [oledb] ; Alles hinter dieser Zeile ist eine OLE DB; Initialisierungszeichenkette Provider=SQLOLEDB.1;Password=password; Persist Security Info=True;User ID=sa; Initial Catalog=CD;Data Source=(local)
382
Standardaufgaben bei Datenbanken
Achten Sie darauf, dass die letzte Zeile eigentlich eine einzige lange Textzeile ist, die wir zu Gunsten der besseren Übersichtlichkeit in mehrere Zeilen zerlegt haben. Nun haben Sie die Verbindungszeichenkette und wissen, dass sie richtig ist, nachdem die Schaltfläche VERBINDUNG TESTEN auf der Grundlage dieser Informationen einen erfolgreichen Test durchgeführt hat. Jetzt können Sie damit beginnen, den Code zu schreiben, mit dem Sie eine Verbindung zu Ihrer Datenbank herstellen. Um eine Verbindung einzurichten, müssen Sie eine neue Instanz entweder des System.Data.OleDB.OLEDBConnection-Objekts oder des System.Data.SQLClient.SQLConnectionObjekts erzeugen. Wenn Sie eine Verbindung zu MSDE oder zu SQL Server herstellen möchten, müssen Sie die Verbindungszeichenkette etwas abändern, bevor Sie sie mit dem SQLConnection-Objekt verwenden können. Da die Zeichenkette mit OleDB und daher mit allen Datenbanktypen verwendet wird, enthält sie einen Provider-Abschnitt. Für eine Verbindung zu einer SQL-Klasse brauchen Sie keinen Provider anzugeben. Falls ein Provider angegeben ist, sollten Sie diesen entfernen, bevor Sie die Verbindung herstellen. Das Listing 12.1 erzeugt eine neue Instanz von OLEDBConnection und liefert die Verbindungszeichenkette im Konstruktor. Dadurch stellt das Listing ein Verbindungsobjekt her, das bereits mit allen Informationen eingerichtet ist, die es für die Kommunikation mit der Datenbank benötigt. Listing 12.1: Eine Verbindungszeichenkette 1 Module Module1 2 Private Const sConnection As String = "Provider=SQLOLEDB.1;" & _ 3 "Password=password;" & _ 4 "Persist Security Info=True;" & _ 5 "User ID=sa;" & _ 6 "Initial Catalog=CD;" & _ 7 "Data Source=(local)" 8 9 Sub Main() 10 Dim objConn As New System.Data.OleDb.OleDbConnection(sConnection) 11 End Sub 12 End Module
Nun haben Sie das Verbindungsobjekt erzeugt und mit den Details Ihres Systems geladen. Bisher hat jedoch noch keine Kommunikation mit dem Datenbankserver stattgefunden. Um die Verknüpfung zu der Datenbank einzurichten, müssen Sie die Methode Open des OleDBConnection-Objekts aufrufen: objConn.Open()
Diese Zeile ist die erste, die eine Interaktion mit der Datenbank auslöst. Sie sollten Sie in eine Struktur zur Fehlerbehandlung hüllen. Das Listing 12.2 zeigt ein vollständiges Beispiel dafür, wie Sie die Verbindung mit einer einfachen Fehlerbehandlung öffnen:
383
Mit .NET auf Daten zugreifen
Listing 12.2: Eine Verbindung öffnen 1 Module Module1 2 Private Const sConnection As String = "Provider=SQLOLEDB.1;" & _ 3 "Password=password;" & _ 4 "Persist Security Info=True;" & _ 5 "User ID=sa;" & _ 6 "Initial Catalog=CD;" & _ 7 "Data Source=(local)" 8 Sub Main() 9 Dim objConn As New System.Data.OleDb.OleDbConnection(sConnection) 10 Try 11 objConn.Open() 12 Catch myException As System.Exception 13 Console.WriteLine(myException.Message) 14 End Try 15 Console.ReadLine() 16 End Sub 17 End Module
Am Tag 6 erhalten Sie weitere Informationen zur Fehlerbehandlung. Wenn Sie diesen Code selbst ausprobieren möchten, erstellen Sie einfach mit Visual Basic .NET eine neue Konsolenanwendung und fügen diesen Code in Module1.vb ein. Vergessen Sie nicht, den Wert für sConnection aus Listing 12.2 durch die Zeichenkette zu ersetzen, die Sie weiter oben in der heutigen Lektion erstellt haben. Sie können auch versuchen, die Werte wie z.B. das Passwort in der Verbindungszeichenkette zu ändern, damit der Verbindungsversuch scheitert. Die Fehlerbehandlung sollte eine Ausgabezeile liefern, die zeigt, was in dem Programm schiefgelaufen ist. Um eine Verbindung zu öffnen, können Sie die SQLClient-Klassen mit einigen kleinen Änderungen verwenden. Löschen Sie zuerst den Provider-Abschnitt aus der Verbindungszeichenkette und ändern Sie dann die Deklaration von objConn so, dass sie nicht mehr auf die Klasse OLEDB, sondern auf das System.Data.SQLClient.SQLConnection-Objekt verweist. Das Listing 12.3 zeigt den vollständigen Code, der im Wesentlichen dasselbe Ergebnis liefert: Listing 12.3: Die Fehlerbehandlung ist beim Umgang mit Datenbanken sehr wichtig. 1 Imports System 2 Module Module1 3 Private Const sConnection As String 4 sConnection = "Password=password;" & _ 5 "Persist Security Info=True;" & _ 6 "User ID=sa;" & _ 7 "Initial Catalog=CD;" & _ 8 "Data Source=(local)"
384
Standardaufgaben bei Datenbanken
9 Sub Main() 10 Dim objConn As New Data.SqlClient.SqlConnection(sConnection) 11 Try 12 objConn.Open() 13 Catch myException As Exception 14 Console.WriteLine(myException.Message) 15 End Try 16 Console.ReadLine() 17 End Sub 18 End Module
Die Herstellung einer Verbindung ist in der Regel zwar der erste Arbeitsschritt, aber für sich genommen ist sie nicht sonderlich nützlich. Damit Sie mit Daten arbeiten können, müssen Sie einige weitere Datenbankobjekte verwenden.
Eine SQL-Anweisung ausführen Nachdem Sie nun eine Verbindung hergestellt haben, müssen Sie noch wissen, wie Sie Datensätze hinzufügen, löschen, ändern und abrufen können. Für diese Aufgaben können Sie ein Befehlsobjekt (SQLCommand oder OleDBCommand) verwenden, um eine SQL-Anweisung darzustellen und dann auszuführen. Bevor Sie ein Befehlsobjekt verwenden können, benötigen Sie die auszuführende SQL-Anweisung sowie ein Verbindungsobjekt (SQLConnection oder OleDBConnection). Das Verbindungsobjekt muss noch nicht geöffnet sein, wenn Sie das Befehlsobjekt erzeugen. Dies ist erst dann erforderlich, wenn Sie den Befehl tatsächlich ausführen. Wenn Sie die SQL-Anweisung und die Verbindung haben, brauchen Sie nur noch eine neue Instanz des richtigen Befehlsobjekttyps zu erzeugen. Sie können die SQL-Anweisung und das Verbindungsobjekt an den Konstruktor übergeben, wenn Sie das Objekt erzeugen (siehe Listing 12.4) oder nachdem Sie dies getan haben (siehe Listing 12.5). Listing 12.4: Anfragen an die Datenbank ausführen 1 Module Module1 2 Private Const sConnection As String = "Provider=SQLOLEDB.1;" & _ 3 "Password=;" & _ 4 "User ID=sa;" & _ 5 "Initial Catalog=CD;" & _ 6 "Data Source=(local)" 7 Sub Main() 8 Dim sSQL As String 9 sSQL = "INSERT INTO CATEGORY (CategoryID, Category)" & _ 10 "VALUES (7, 'Electronic')" 11 Dim objConn As New _ 12 System.Data.OleDb.OleDbConnection(sConnection)
385
Mit .NET auf Daten zugreifen
13 Dim objCmd As New _ 14 System.Data.OleDb.OleDbCommand(sSQL, objConn) 15 Try 16 objConn.Open() 17 Catch myException As System.Exception 18 Console.WriteLine(myException.Message) 19 End Try 20 21 Console.Write("Press Return to Execute Query: { 0} ", sSQL) 22 Console.ReadLine() 25 26 If objConn.State = ConnectionState.Open Then 27 Try 28 objCmd.ExecuteNonQuery() 29 Catch myException As System.Exception 30 Console.WriteLine(myException.Message) 31 Console.ReadLine() 32 End Try 33 End If 34 35 End Sub 36 End Module Listing 12.5: Eigenschaften einzeln setzen 1 Dim objCmd As New OleDbCommand() 2 objCmd.Connection = objConn 3 objCmd.CommandText = sSQL
Für die Ausführung der INSERT-Anweisung ist die Methode ExecuteNonQuery im Befehlsobjekt (Zeile 28) zuständig. Allerdings ist diese Methode nur für SQL-Befehle geeignet, die keine Zeilen zurückgeben.
Daten abrufen Wenn Sie eine SQL-Anfrage ausführen möchten, die Datenzeilen zurückgibt, also z.B. SELECT, dann müssen Sie die Methode ExecuteReader verwenden. Diese Methode gibt es für beide Arten von Reader-Objekten und sie gibt das jeweils passende Objekt zurück: OleDBDataReader oder SQLDataReader. Nachdem das Reader-Objekt zurückgegeben wurde, können Sie damit die Daten mit einer Schleife vorwärts durchlaufen. Mit dieser Schleife rufen Sie die Werte einiger oder aller Felder und Zeilen im Ergebnis Ihrer Anfrage ab. Das Reader-Objekt hat viele Methoden und Eigenschaften, aber die Schlüsselelemente sind Read() sowie die Sammlung von Methoden zum Abrufen von Daten: GetString, GetDouble, GetDateTime usw. All diese Methoden nehmen eine Ordinalzahl entgegen, die angibt, an welches Feld sie ihre Daten zurückgeben sollen.
386
Standardaufgaben bei Datenbanken
Die Methode Read leitet den Daten-Reader an den nächsten Datensatz weiter und gibt einen booleschen Wert zurück, der anzeigt, ob weitere Zeilen zur Verfügung stehen. Wenn Sie sich bei einem Datensatz befinden (nachdem Sie Read() aufgerufen haben), können Sie mit den verschiedenen Get-Methoden die Werte der einzelnen Felder abrufen. Der Code im Listing 12.6 wurde so erweitert, dass er die Ausführung einer neuen SELECT-Anfrage sowie eine Schleife umfasst, mit der Sie sich durch das Ergebnis der Anfrage bewegen und es an die Konsole ausgeben können: Listing 12.6: Auf das Ergebnis einer Datenbankanfrage zugreifen 1 Module Module1 2 Private Const sConnection As String = "Provider=SQLOLEDB.1;" & _ 3 "Password=;" & _ 4 "User ID=sa;" & _ 5 "Initial Catalog=CD;" & _ 6 "Data Source=(local)" 7 8 Sub Main() 9 Dim sSQL As String 10 sSQL = "SELECT Artist.ArtistID, " & _ 11 "Artist.ArtistName, Disc.CDTitle " & _ 12 "FROM Artist INNER JOIN Disc ON " & _ 13 "Artist.ArtistID = Disc.ArtistID;" 14 Dim objConn As New _ 15 System.Data.OleDb.OleDbConnection(sConnection) 16 Dim objCmd As New _ 17 System.Data.OleDb.OleDbCommand(sSQL, objConn) 18 Dim objReader As _ 19 System.Data.OleDb.OleDbDataReader 20 21 Try 22 objConn.Open() 23 Catch myException As System.Exception 24 Console.WriteLine(myException.Message) 25 End Try 26 27 Console.Write("Press Return to Execute SQL: { 0} ", sSQL) 28 Console.ReadLine() 29 30 If objConn.State = ConnectionState.Open Then 31 Try 32 objReader = objCmd.ExecuteReader() 33 Do While objReader.Read() 34 Console.WriteLine("{ 0} { 1} – { 2} ", _ 35 objReader.GetInt32(0), _
387
Mit .NET auf Daten zugreifen
36 objReader.GetString(1), _ 37 objReader.GetString(2)) 38 Loop 39 Console.ReadLine() 40 Catch myException As System.Exception 41 Console.WriteLine(myException.Message) 42 Console.ReadLine() 43 End Try 44 End If 45 End Sub 46 End Module
Daten-Reader sind eine schnelle Möglichkeit, um Daten nur vorwärts und schreibgeschützt abzurufen. Zugleich sind sie auch vollständig verbundene Datenmengen. Dies bedeutet, dass jede Zeile direkt aus der Datenbank abgerufen wird, wenn Sie einen DatenReader erstellen und seine Zeilen mit einer Schleife durchlaufen. Während Sie einen Daten-Reader ausführen, macht Ihre Verbindung zur Datenbank etwas und kann deshalb nicht für andere Zwecke verwendet werden. Dies ist eigentlich kein Problem, sondern sogar einer der Hauptgründe dafür, warum diese Reader so schnell sind (nichts wird im Speicher des Clients abgelegt). Manchmal benötigen Sie jedoch Daten, die Ihnen eine bessere Kontrolle ermöglichen. Mit DataSet steht Ihnen als Alternative zu Daten-Readern ein verbindungsloses Modell für die Arbeit mit Daten zur Verfügung.
12.3 Mit Datenmengen arbeiten Im vorhergehenden Abschnitt haben Sie mit einem Daten-Reader Daten aus einer Datenbank abgerufen. Dies war eine Datenzugriffsart mit Verbindung, bei dem Sie während des gesamten Prozesses mit der Datenbank verbunden sein mussten. Daher ist diese Art des Datenzugriffs am besten geeignet, wenn Sie eine Informationsmenge schnell abrufen möchten und nicht vorhaben, diese Informationen über eine längere Zeit (mehr als ein paar Sekunden) zu verwenden. Wenn Sie Daten abrufen und sie dann über eine längere Zeit hinweg ändern, ausgeben, filtern, verarbeiten oder anderweitig bearbeiten oder wenn sie einfach als eine Art von Cache beibehalten möchten, dann ist der Datenzugriff mit Verbindung weniger gut geeignet. Als Alternative dazu bietet .NET eine völlig verbindungslose Art des Datenzugriffs, nämlich über die Datenmenge (System.Data.DataSet). Datenmengen können Informationen enthalten, die normalerweise aus einer Datenbank stammen, aber auch aus beliebigen anderen Quellen kommen können. Sobald die Informationen in die Datenmengen geladen werden, sind sie von der ursprünglichen Datenquelle unabhängig. Diese Unabhängigkeit von der Datenquelle ist der Grund dafür, dass man diese Informationen als verbindungslos betrachtet und sie beliebig lange in einer Offline-Umgebung speichern
388
Mit Datenmengen arbeiten
kann. Wenn Sie die vorherige Version von ADO verwendet haben, dann klingt dies vielleicht ähnlich wie das verbindungslose Recordset-Objekt dieses Verfahrens. Es hat zwar einige Ähnlichkeiten mit diesem Objekt, ist aber zu wesentlich mehr Funktionalität fähig. Eine Datenmenge kann mehrere Tabellen speichern, Beziehungen zwischen diesen Tabellen verfolgen und sogar für jede einzelne Tabelle mehrere verschiedene Ansichten pflegen. Diese Objekte sind vollständige Datenbanken, die sich im Speicher befinden, während man sie zugleich einfach mit einer einzelnen Ergebnismenge verwenden kann. In den folgenden Abschnitten werden Sie lernen, wie Sie Daten in ein DataSet laden und dann durch diese Informationen navigieren. Nach der Besprechung dieser Grundlagen werden wir einige der komplexeren Elemente wie z.B. mehrere Tabellen, Ansichten und Beziehungen verwenden. Schließlich werden Sie noch erfahren, wie Sie nach dem Bearbeiten, Löschen und Hinzufügen von Zeilen alle Änderungen an die Datenbank zurückschicken.
Daten in ein DataSet laden Um Daten aus der Datenbank in ein DataSet zu laden, benötigen Sie einen anderen Objekttyp, den Datenadapter. Der Zweck der Datenadapterobjekte SQLDataAdapter und OleDBDataAdapter besteht darin, den Leim, die Verknüpfung zwischen der Datenbank und einem DataSet-Objekt zu liefern. Eine solche Verknüpfung wird in zwei Schritten hergestellt: Zuerst wird das DataSet gefüllt und anschließend werden die Änderungen am DataSet an die Datenquelle zurückgeschickt, um das Original zu aktualisieren. Über die Aktualisierungsphase werden Sie etwas später in der heutigen Lektion mehr erfahren. Bevor Sie aber irgendetwas aktualisieren können, müssen Sie die Daten laden. Zuerst müssen Sie wie in den vorherigen Beispielen ein Verbindungsobjekt erzeugen. Dann sollten Sie eine neue Instanz der richtigen Datenadapterklasse (OLE DB oder SQL) erzeugen. Um diese Instanz einzurichten, brauchen Sie nur die gewünschte SQL-Anweisung oder ein Befehlsobjekt, das auf die richtige SQL-Anweisung referiert, sowie das Verbindungsobjekt im Konstruktor des Datenadapters zu verwenden: Dim sSQL As String sSQL = "SELECT Artist.ArtistID, " & _ "Artist.ArtistName, Disc.CDTitle " & _ "FROM Artist INNER JOIN Disc ON " & _ "Artist.ArtistID = Disc.ArtistID;" Dim objConn As New OleDb.OleDbConnection(sConnection) Dim objDataAdapter As New OleDb.OleDbDataAdapter(sSQL, objConn)
Da Sie das Datenobjekt mit dem Ergebnis einer Anfrage füllen möchten, müssen Sie außerdem noch ein DataSet-Objekt erzeugen. Die folgende Codezeile erzeugt als Teil des Konstruktors eine neue Instanz und liefert einen Namen für das DataSet: Dim objDS As New DataSet("CDs")
389
Mit .NET auf Daten zugreifen
Um Daten in das DataSet zu laden, können Sie die Methode Fill von DataAdapter verwenden. Zuvor müssen Sie jedoch das Verbindungsobjekt erfolgreich öffnen: objConn.Open() objDataAdapter.Fill(objDS, "Disc")
Nachdem dieser Code ausgeführt wurde, enthält das DataSet eine Tabelle namens Disc, die mit dem Ergebnis der SQL-Anfrage gefüllt ist. Sobald diese Tabelle existiert, können Sie mithilfe der Sammlung Tables des DataSet-Objekts mit dem Tabelleninhalt arbeiten: Console.WriteLine("{ 0}
Rows", objDS.Tables("Disc").Rows.Count)
Weiter unten in der heutigen Lektion finden Sie weitere Informationen darüber, wie Sie auf Daten in einem DataSet zugreifen. Beachten Sie, dass Sie die Verbindung zur Datenbank auch schließen können, bevor Sie mit dem Inhalt des DataSet arbeiten, da dieses ein verbindungsloses Objekt ist. objDataAdapter.Fill(objDS, "Disc") objConn.Close() Console.WriteLine("{ 0} Rows", objDS.Tables("Disc").Rows.Count)
Das Listing 12.7 zeigt den vollständigen Code einschließlich der richtigen Fehlerbehandlung und lädt das Ergebnis einer SELECT-Anfrage mit einem OleDBDataAdapter in ein neues DataSet-Objekt: Listing 12.7: Ein DataSet mit dem Ergebnis einer Anfrage füllen 1 Module Module1 2 Private Const sConnection As String = "Provider=SQLOLEDB.1;" & _ 3 "Password=;" & _ 4 "User ID=sa;" & _ 5 "Initial Catalog=CD;" & _ 6 "Data Source=(local)" 7 8 Sub Main() 9 Dim sSQL As String 10: sSQL = "SELECT Artist.ArtistID, " & _ 11 "Artist.ArtistName, Disc.CDTitle " & _ 12 "FROM Artist INNER JOIN Disc ON " & _ 13 "Artist.ArtistID = Disc.ArtistID;" 14 15 Dim objConn As New _ 16 OleDb.OleDbConnection(sConnection) 17 Dim objDataAdapter As New _ 18 OleDb.OleDbDataAdapter(sSQL, objConn) 19 Dim objDS As New DataSet("CDs") 20 21 Try
390
Mit Datenmengen arbeiten
22 objConn.Open() 23 Catch myException As System.Exception 24 Console.WriteLine(myException.Message) 25 End Try 26 27 If objConn.State = ConnectionState.Open Then 28 Try 29 objDataAdapter.Fill(objDS, "Disc") 30 objConn.Close() 31 Console.WriteLine("{ 0} Rows", _ 32 objDS.Tables("Disc").Rows.Count) 33 Catch myException As System.Exception 34 Console.WriteLine(myException.Message) 35 End Try 36 Console.ReadLine() 37 End If 38 End Sub 39 End Module
Ebenso wie alle anderen bisherigen Beispiele in der heutigen Lektion können Sie auch diesen Code selbst ausführen, indem Sie ihn in das Modul einer neue Konsolenanwendung setzen.
Durch Daten navigieren Nachdem Sie einige Daten in das DataSet geladen haben, möchten Sie sich wahrscheinlich durch diese Informationen bewegen. Das Objekt, mit dem Sie arbeiten, ist nicht das DataSet selbst, da dies eventuell eine Reihe verschiedener Tabellen repräsentiert. Vielmehr arbeiten Sie mit dem DataTable-Objekt, das mit den geladenen Daten übereinstimmt, an denen Sie interessiert sind. Der folgende Code zeigt, wie Sie ein DataTable-Objekt aus dem DataSet holen können, in dem es enthalten ist: Dim objTable As DataTable objTable = objDS.Tables("Disc")
Da der Code, der die Daten in das DataSet geladen hat, für die neu geladene Tabelle den Namen Disc bereitgestellt hat, können Sie mit diesem Namen auf dieselbe Tabelle zugreifen. Das DataTable-Objekt stellt zwei Sammlungen zur Verfügung, die besonders nützlich sind: Die Sammlung Rows enthält alle Datensätze aus der Tabelle und die Sammlung Columns enthält eine Sammlung von DataColumn-Objekten, die die einzelnen Felder in der Tabelle beschreiben. Mit der Sammlung Rows können Sie auf verschiedene Arten alle Datensätze der Tabelle mit einer Schleife durchlaufen:
391
Mit .NET auf Daten zugreifen
Mit einer For Each-Schleife: Dim objRow As DataRow For Each objRow In objTable.Rows Console.WriteLine(objRow.Item("CDTitle")) Next
Oder mit einer regulären For-Schleife: Dim i As Integer Dim objRow As DataRow For i = 0 To objTable.Rows.Count – 1 objRow = objTable.Rows(i) Console.WriteLine(objRow.Item("CDTitle")) Next
Beide Verfahren liefern in diesem Fall dasselbe Ergebnis: Jeder einzelne Datensatz der Tabelle wird von einem DataRow-Objekt dargestellt. Dieses Objekt ermöglicht es, mit der Eigenschaft Item (in der Sie den Index oder den Namen des Feldes angeben können, das Sie abrufen möchten) auf seine Felder zuzugreifen. Außerdem stellt es mehrere nützliche Eigenschaften zur Verfügung, unter anderem die Eigenschaft RowState, die den aktuellen Bearbeitungszustand des Datensatzes zurückgibt. Die Sammlung Columns enthält Einzelheiten der Felder der DataTable, wobei jedes Feld durch ein DataColumn-Objekt dargestellt wird. Durch diese Sammlung und die darin enthaltenen Objekte stehen Ihnen alle Informationen über die Struktur Ihrer Tabelle zur Verfügung. Ebenso wie bei der Sammlung Rows können Sie auch hier die Felder der Tabelle entweder mit einer For Each- oder mit einer For-Schleife durchlaufen: Dim objColumn As DataColumn For Each objColumn In objTable.Columns Console.WriteLine("Column Name: { 0} Data Type: { 1} ", _ objColumn.ColumnName, _ objColumn.DataType.FullName) Next
Das Listing 12.8 zeigt den Prozedurcode am Beispiel von DisplayTable. Es veranschaulicht, wie Sie mit den Eigenschaften Rows und Columns einer DataTable eine generische Prozedur schreiben können, um alle Informationen in der Tabelle mit Spaltenköpfen auszugeben: Listing 12.8: Den Inhalt einer DataTable anzeigen 1 2 3 4 5
392
Sub DisplayTable(ByRef objTable As DataTable) Dim objRow As DataRow Dim objCol As DataColumn Dim i, j As Integer
Mit Datenmengen arbeiten
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
For j = 0 To objTable.Columns.Count – 1 objCol = objTable.Columns(j) Console.Write("{ 0} :{ 1} ", _ objCol.ColumnName, _ objCol.DataType.Name) Next Console.WriteLine() For i = 0 To objTable.Rows.Count – 1 objRow = objTable.Rows(i) For j = 0 To objTable.Columns.Count – 1 Console.Write("{ 0} ", objRow.Item(j)) Next Console.WriteLine() Next End Sub
Sie können diese Prozedur ausprobieren, indem Sie den Code zum Laden eines DataSet so verändern, dass er einen Aufruf dieser neuen Prozedur enthält. Das Listing 12.9 veranschaulicht dies: Listing 12.9: Die Funktion DisplayTable 1 Private Const sConnection As String = "Provider=SQLOLEDB.1;" & _ 2 "Password=;" & _ 3 "Persist Security Info=True;" & _ 4 "User ID=sa;" & _ 5 "Initial Catalog=CD;" & _ 6 "Data Source=(local)" 7 8 Sub Main() 9 Dim sSQL As String 10 sSQL = "SELECT Artist.ArtistID, " & _ 11 "Artist.ArtistName, Disc.CDTitle " & _ 12 "FROM Artist INNER JOIN Disc ON " & _ 13 "Artist.ArtistID = Disc.ArtistID;" 14 15 Dim objConn As New _ 16 OleDb.OleDbConnection(sConnection) 17 Dim objDataAdapter As New _ 18 OleDb.OleDbDataAdapter(sSQL, objConn) 19 Dim objDS As New _ 20 DataSet("CDs") 21 22 Try 23 objConn.Open() 24 Catch myException As System.Exception
393
Mit .NET auf Daten zugreifen
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 End
Console.WriteLine(myException.Message) End Try If objConn.State = ConnectionState.Open Then Try objDataAdapter.Fill(objDS, "Disc") objConn.Close() Dim objTable As DataTable objTable = objDS.Tables("Disc") DisplayTable(objTable) Catch myException As System.Exception Console.WriteLine(myException.Message) End Try Console.ReadLine() End If Sub
Das DataRow-Objekt ist wichtig. Es ist nicht nur das Mittel, mit dem Sie auf Feldwerte zugreifen (wie im Listing 12.8), sondern Sie verwenden es auch, wenn Sie Datensätze in einer DataTable bearbeiten, löschen oder hinzufügen. Im folgenden Abschnitt werden Sie erfahren, wie Sie den Inhalt eines DataSet mit DataTable- und DataRow-Objekten verändern.
Daten bearbeiten (Hinzufügen, Bearbeiten, Löschen) In diesem Abschnitt schreiben Sie Code, um die Datenbank mit dem DataSet-Objekt zu verändern. Die Datenbank selbst kennt diese DataSets jedoch noch nicht und letztlich wird sie durch SQL-Anweisungen verändert. Die Änderungen, die Sie am DataSet vornehmen, werden von DataAdapter in SQL-Anweisungen übersetzt, die in der Datenbank ausgeführt werden sollen. Sie könnten natürlich einfach alle einzelnen UPDATE-, INSERT- und DELETE-Anweisungen direkt ausführen, aber das DataSet-Modell hat den Vorteil, dass es verbindungslos (während es auf Eingaben des Anwenders wartet, nimmt es keine Serverressourcen in Anspruch) und einfach ist (es verwendet anstelle benutzerdefinierter SQLAnweisungen Objektmethoden wie z.B. Delete). Wir werden die drei Arten der Datenbearbeitung – Hinzufügen, Bearbeiten und Löschen – einzeln behandeln, damit Sie sehen, wie Sie Ihre Daten mit den Methoden der DataTableund DataRow-Objekte einfach verändern können.
394
Mit Datenmengen arbeiten
Datensätze hinzufügen Nachdem Sie eine Verbindung hergestellt, einen Datenadapter erzeugt und einige Daten in das DataSet geladen haben, können Sie direkt auf ein DataTable-Objekt zugreifen: objDataAdapter.Fill(objDS, "Disc") Dim objTable As DataTable objTable = objDS.Tables("Disc")
Sobald Sie das DataTable-Objekt haben, können Sie mit der Sammlung Rows auf seinen Inhalt zugreifen. Rows gibt ein DataRowCollection-Objekt zurück: Dim drRows As DataRowCollection drRows = objTable.Rows
Dieses Objekt stellt alle Datensätze in der Tabelle als eine Sammlung von DataRow-Objekten dar. Die Sammlung selbst stellt eine Methode namens Add bereit, mit der Sie neue Datensätze anlegen können. Diese Methode können Sie auf zweierlei Art aufrufen: mit einem Array von Feldwerten für den neuen Datensatz oder mit einem einzelnen DataRowObjekt, das die hinzuzufügenden Informationen darstellt. Für das zweite Verfahren, den Aufruf mit einem DataRow-Objekt, benötigen Sie eine Möglichkeit, ein neues Zeilenobjekt zu holen, das dasselbe Schema (dieselben Felder mit denselben Datentypen und -größen) wie die übrigen Zeilen in der Tabelle hat. Das DataTable-Objekt stellt diese Möglichkeit selbst durch die Methode NewRow zur Verfügung. In den Listings 12.10 und 12.11 sehen Sie Beispiele für beide Verfahren. Das Listing 12.10 veranschaulicht die Verwendung eines Arrays, das Sie zwingt, die Reihenfolge der Felder zu kennen, und das einen Code hervorbringt, der wesentlich weniger klar ist als bei der zweiten Methode. Das Listing 12.11 zeigt das zweite Verfahren, die Verwendung eines DataRow-Objekts. Listing 12.10: Ein Array verwenden (unvollständiges Codelisting) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Dim drRows As DataRowCollection Dim objNewRow As DataRow drRows = objTable.Rows 'In diesem Fall haben wir drei Spalten: 'ArtistID 'ArtistName 'CDTitle 'Die Verwendung der Array-Methode liefert: Dim arrFields(3) As Object arrFields(0) = 10 arrFields(1) = "Sting" arrFields(2) = "Dream of Blue Turtles" objNewRow = drRows.Add(arrFields)
395
Mit .NET auf Daten zugreifen
Listing 12.11: Ein DataRow-Objekt als Parameter der Methode Add bereitstellen 1 2 3 4 5 6 7 8 9 10 11 12 13 14
Dim drRows As DataRowCollection Dim objNewRow As DataRow drRows = objTable.Rows 'In diesem Fall haben wir drei Spalten: 'ArtistID 'ArtistName 'CDTitle objNewRow = objTable.NewRow() objNewRow("ArtistID") = 3 objNewRow("ArtistName") = "George Orwell" objNewRow("CDTitle") = "Party like it's 1984" drRows.Add(objNewRow)
In beiden Fällen werden die Daten der Tabelle hinzugefügt, was aber nicht bedeutet, dass sie auch in die Datenbank gelangen. Wenn die Serverdatenbank geändert wurde, nachdem die Daten in das DataSet geladen wurden, dann könnte dies dazu führen, dass die hinzugefügten Datensätze nicht gültig sind (vielleicht geben Sie einen Künstler an, der inzwischen aus der Künstlertabelle gelöscht wurde). Derartige Fehler sehen Sie jedoch in der Aktualisierungsphase. Da Sie mit einem DataSet verbindungslos, also offline arbeiten, können Sie Probleme, die mit dem Server zu tun haben, erst dann erkennen, wenn Sie den Server zu aktualisieren versuchen.
Datensätze bearbeiten Das Bearbeiten von Datensätzen verläuft ähnlich wie das Hinzufügen. Sie arbeiten mit DataRow-Objekten, die die Datensätze der Tabelle darstellen. Allerdings erstellen Sie nicht wie im vorherigen Abschnitt ein neues DataRow-Objekt, sondern verwenden ein vorhandenes. Ebenso wie bei den vorherigen Beispielen müssen Sie zuerst eine Verbindung, einen Befehl und einen Datenadapter erzeugen. Dann laden Sie Daten in ein DataSet, um ein DataTable-Objekt zu erzeugen. Anschließend können Sie auf dessen DataRowCollection zugreifen und sind nun in der Lage, die Datensätze zu bearbeiten: objDataAdapter.Fill(objDS, "Disc") Dim objTable As DataTable objTable = objDS.Tables("Disc") Dim drRows As DataRowCollection Dim objRow As DataRow drRows = objTable.Rows objRow = drRows(5)
396
Mit Datenmengen arbeiten
Jedes DataRow-Objekt hat mehrere Methoden, die direkt mit der Bearbeitung zu tun haben: BeginEdit, EndEdit und CancelEdit. BeginEdit versetzt das DataRow-Objekt in den Bearbeitungsmodus und EndEdit beendet diesen Modus. Der Bearbeitungsmodus ist ein besonderer Zustand, in dem die Zeile Informationen über die gerade laufende Bearbeitung speichert. Daher werden nicht für jede einzelne Feldänderung Change-Ereignisse ausgelöst. Im Abschnitt »Datenbindung« weiter unten in dieser Lektion werden Sie mehr über Change-Ereignisse erfahren. BeginEdit ist optional, gibt Ihre Absicht aber klar zu erkennen. Mit der Eigenschaft Item, die den Namen oder die Position des Feldes verwendet, können Sie auf die Werte zugreifen und sie verändern: objRow.BeginEdit() objRow("au_lname") = "Exciting" objRow.EndEdit()
Wenn nach der Verwendung von BeginEdit die Methode EndEdit aufgerufen wird, wird die Zeile an das DataTable-Objekt gesandt. Nun werden alle Fehler, die während der Bearbeitung aufgetreten sind, von ADO.NET ausgelöst. Wenn Sie BeginEdit und EndEdit nicht verwenden, treten die Fehler bei der Bearbeitung der einzelnen Felder auf. Später in der heutigen Lektion werden Sie erfahren, wie Sie ein DataSet, das veränderte Felder, neue Datensätze und sogar gelöschte Datensätze enthält, übernehmen und die ursprüngliche Quelldatenbank aktualisieren können.
Datensätze löschen Mit der Methode Delete eines DataRow-Objekts können Sie den betreffenden Datensatz aus einer Datentabelle löschen. Dieses Verfahren ist zwar unkompliziert, aber auf einen Aspekt müssen Sie dennoch achten: Die Methode Delete markiert die Zeile als gelöscht, löscht das DataRow-Objekt aber nicht aus DataRowCollection. Dies ist wichtig, da es bedeutet, dass Sie das Löschen des Objekts rückgängig machen und alle für die Aktualisierung der Quelldatenbank nötigen Informationen erhalten können, indem Sie diesen Datensatz in der ursprünglichen Tabelle löschen. Die Klasse DataRowCollection hat außerdem die Methoden Remove und RemoveAt, die das betreffende DataRow-Objekt direkt aus der Sammlung herausnehmen. Remove und RemoveAt können Sie problemlos verwenden, wenn Sie ein DataTable-Objekt ohne Back-End-Datenbank benutzen. Wenn Sie jedoch alle Änderungen am DataSet übernehmen und die Quelltabelle(n) aktualisieren möchten, müssen Sie die Methode Delete verwenden. Das Listing 12.12 zeigt ein Beispiel dafür, wie Sie eine Tabelle mit einer Schleife durchlaufen und dabei bestimmte Zeilen finden und löschen. All diese Löschaktionen wirken sich erst dann auf die Quelldaten (z.B. SQL Server) aus, wenn Sie diese auf dem Server aktualisieren.
397
Mit .NET auf Daten zugreifen
Listing 12.12: Die Methode Delete markiert eine Zeile als gelöscht. 1 Module Module1 2 Private Const _ 3 sConnection As String = _ 4 "Provider=SQLOLEDB.1;" & _ 5 "Password=;" & _ 6 "Persist Security Info=True;" & _ 7 "User ID=sa;" & _ 8 "Initial Catalog=CD;" & _ 9 "Data Source=(local)" 10 11 Sub Main() 12 Dim sSQL As String 13 sSQL = "SELECT ArtistID, DiscID, CDTitle From Disc" 14 15 Dim objConn _ 16 As New OleDb.OleDbConnection(sConnection) 17 Dim objDataAdapter _ 18 As New OleDb.OleDbDataAdapter(sSQL, objConn) 19 Dim objDS _ 20 As New DataSet("CDs") 21 22 Try 23 objConn.Open() 24 Catch myException As System.Exception 25 Console.WriteLine(myException.Message) 26 End Try 27 28 If objConn.State = ConnectionState.Open Then 29 Try 30 objDataAdapter.Fill(objDS, "Disc") 31 objConn.Close() 32 Dim objTable As DataTable 33 objTable = objDS.Tables("Disc") 34 Dim drRows As DataRowCollection 35 Dim objCurrentRow As DataRow 36 drRows = objTable.Rows 37 38 DisplayTable(objTable) 39 40 Console.Write("About to Delete, press return to start!") 41 Console.ReadLine() 42 43 For Each objCurrentRow In drRows 44 If CType(objCurrentRow("ArtistID"), Integer) = 3 Then
398
Mit Datenmengen arbeiten
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
objCurrentRow.Delete() End If Next Console.Write("After Deletion, press return to view") Console.ReadLine() DisplayTable(objTable) Catch myException As System.Exception Console.WriteLine(myException.Message) End Try Console.ReadLine() End If End Sub Sub DisplayTable(ByRef objTable As DataTable) Dim objRow As DataRow Dim objCol As DataColumn Dim i, j As Integer For j = 0 To objTable.Columns.Count – 1 objCol = objTable.Columns(j) Console.Write("{ 0} :{ 1} ", _ objCol.ColumnName, _ objCol.DataType.Name) Next Console.WriteLine() For i = 0 To objTable.Rows.Count – 1 objRow = objTable.Rows(i) Select Case objRow.RowState Case DataRowState.Deleted Console.Write("[Deleted] ") Case DataRowState.Modified Console.Write("[Modified] ") Case DataRowState.Added Console.Write("[New] ") Case DataRowState.Unchanged Console.Write("[Unchanged] ") End Select For j = 0 To objTable.Columns.Count – 1 If objRow.RowState DataRowState.Deleted Then Console.Write("{ 0} ", _ objRow.Item(j)) Else Console.Write("{ 0} ", _
399
Mit .NET auf Daten zugreifen
91 objRow.Item(j, DataRowVersion.Original)) 92 End If 93 94 Next 95 Console.WriteLine() 96 Next 97 End Sub 98 End Module
Beachten Sie, dass in der Zeile 90 innerhalb der Routine DisplayTable für den Zugriff auf einen Feldwert eine andere Syntax verwendet wird. Durch den zweiten Parameter, DataRowVersionOriginal, gibt dieser Code an, dass der Wert des Feldes nach der jüngsten Aktualisierung oder dann, wenn er geladen wird, anstelle des aktuellen Feldwerts (des Standards) angezeigt werden soll. In diesem Fall ist dies besonders wichtig, da eine gelöschte Zeile keine zulässigen aktuellen Feldwerte hat und ein Fehler generiert wird, falls sie auf diese Werte zuzugreifen versuchen.
Die Datenbank aktualisieren Nachdem Sie Datensätze hinzugefügt, bearbeitet oder gelöscht haben, müssen Sie die ursprüngliche Datenquelle (die Datenbank) aktualisieren, bevor die Änderungen wirksam werden. Das Verfahren, mit dem Sie ein geändertes DataSet übernehmen und die Quelldatenbank aktualisieren, ist unabhängig von den vorgenommenen Änderungen immer dasselbe. Sie benötigen dafür die Datenadapterklasse.
Befehle zum Aktualisieren, Einfügen und Löschen angeben Als Sie einen Datenadapter erstellten, um Daten in das DataSet zu laden, haben Sie ein SQL-Anweisungs- oder Befehlsobjekt bereitgestellt, mit dem der Adapter dann Daten aus der Datenbank abgerufen hat. Dieser Befehl ist einer von vier Befehlen, die der Datenadapter unterstützen kann. Jeder dieser Befehle behandelt bei der Übermittlung von Daten zwischen der Datenbank und dem DataSet eine andere Aufgabe. Nur der bereits bereitgestellte Befehl SelectCommand behandelt das Extrahieren von Daten aus der Datenbank. Die übrigen Befehle (UpdateCommand, DeleteCommand und InsertCommand) behandeln das Bearbeiten, Löschen und Hinzufügen zur Datenbank. Jeder Befehl muss einen richtigen Wert enthalten, der entweder eine SQL-Anweisung oder ein Aufruf einer gespeicherten Prozedur auf dem Server ist. Erst dann kann die entsprechende Aktion durchgeführt werden. Alle diese Eigenschaften repräsentieren ein Befehlsobjekt des passenden Typs (OleDBCommand oder SQLCommand), das Sie erzeugen und initialisieren, bevor Sie der Eigenschaft etwas zuweisen.
400
Mit Datenmengen arbeiten
Um diese Befehle selbst zu erzeugen, benötigen Sie einen Anfragetyp, den wir in dieser Lektion noch nicht besprochen haben: einen Befehl mit Parametern. Dieser Befehlstyp hinterlässt in seinem SQL-Code bestimmte Platzhalter, die Parameter. Diese werden mit speziellen Werten gefüllt, bevor der Befehl ausgeführt wird. Das Listing 12.13 zeigt, wie in SQL-Anweisungen Parameter durch Fragezeichen dargestellt werden. Listing 12.13: Datenadapter verlangen Platzhalterbefehle 1 2 3 4 5 6 7
Dim objDeleteCommand As New OleDb.OleDbCommand() Dim sDeleteSQL As String sDeleteSQL = "DELETE FROM Artist WHERE ArtistID = ?" objDeleteCommand.Connection = objConn objDeleteCommand.CommandText = sDeleteSQL
Für jeden Platzhalter müssen Sie Einzelheiten angeben, indem Sie der Parametersammlung des Befehls ein Parameterobjekt hinzufügen (OleDBParameter oder SQLParameter, je nachdem, womit Sie arbeiten). Mithilfe der Eigenschaft SourceColumn wird jedes Parameterobjekt mit einem speziellen Feld im DataSet verknüpft. Außerdem kann man ein Parameterobjekt mithilfe der Eigenschaft SourceVersion mit einer speziellen Version des Feldes verbinden. Das Listing 12.14 zeigt den Code, der erforderlich ist, damit Sie einen Parameter hinzufügen und einige der verfügbaren Eigenschaften setzen können. Listing 12.14: Parameter auf einem Befehlsobjekt erstellen 1 2 3 4 5 6 7
Dim objParam As OleDb.OleDbParameter objParam = objDeleteCommand.Parameters.Add( _ "@ArtistID", OleDb.OleDbType.Integer) objParam.SourceColumn = "AristID" objParam.SourceVersion = DataRowVersion.Original
In diesem Beispiel wurde das einzelne ? in der SQL-Anweisung mit dem neuen Parameterobjekt verbunden. Platzhalter werden nach ihrer Position mit Parametern verbunden: Der erste Platzhalter ist mit dem ersten Parameter verknüpft. Nachdem wir das Parameterobjekt erzeugt haben, geben wir in den Zeilen 5 und 6 die entsprechenden Felder und Feldversionen an. Da wir es hier ebenso wie im Listing 12.12 mit gelöschten Zeilen zu tun haben, ist es wichtig, die SourceVersion anzugeben. Die Standardversion ist der aktuelle Wert (DataRowVersion.Current), der für eine gelöschte Zeile nicht zur Verfügung steht. Der letzte Schritt, bevor der Adapter diesen Befehl verwendet, besteht darin, ihn der Eigenschaft DeleteCommand zuzuweisen: objDataAdapter.DeleteCommand = objDeleteCommand
401
Mit .NET auf Daten zugreifen
Das Listing 12.15 zeigt ein vollständiges und sehr großes Beispiel. Es stellt eine Datenbankverbindung her, erzeugt einen Datenbankadapter und richtet dann die einzelnen Befehle für UpdateCommand, DeleteCommand und InsertCommand ein. Um diese Befehle zu prüfen, erzeugt es schließlich noch eine Datenmenge, lädt sie mit Daten und verändert sie und aktualisiert dann die Datenquelle mit der Methode Update des Adapters: Listing 12.15: Änderungen wirken sich erst aus, wenn Update aufgerufen wird. 1 Module Module1 2 Private Const _ 3 sConnection As String = _ 4 "Provider=SQLOLEDB.1;" & _ 5 "Password=;" & _ 6 "Persist Security Info=True;" & _ 7 "User ID=sa;" & _ 8 "Initial Catalog=CD;" & _ 9 "Data Source=(local)" 10 11 Sub Main() 12 Dim sSQL As String 13 sSQL = "SELECT ArtistID, ArtistName From Artist" 14 Dim objConn _ 15 As New OleDb.OleDbConnection(sConnection) 16 Dim objDataAdapter _ 17 As New OleDb.OleDbDataAdapter(sSQL, objConn) 18 Dim objDS _ 19 As New DataSet("Artists") 20 Dim objDeleteCommand _ 21 As New OleDb.OleDbCommand() 22 Dim objUpdateCommand _ 23 As New OleDb.OleDbCommand() 24 Dim objInsertCommand _ 25 As New OleDb.OleDbCommand() 26 Dim sDeleteSQL As String 27 Dim sUpdateSQL As String 28 Dim sInsertSQL As String 29 30 sDeleteSQL = "DELETE FROM Artist WHERE ArtistID = ?" 31 32 objDeleteCommand.Connection = objConn 33 objDeleteCommand.CommandText = sDeleteSQL 34 35 Dim objParam As OleDb.OleDbParameter 36 objParam = objDeleteCommand.Parameters.Add( _ 37 "@ArtistID", OleDb.OleDbType.Integer) 38 objParam.SourceColumn = "ArtistID"
402
Mit Datenmengen arbeiten
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
objParam.SourceVersion = DataRowVersion.Original objDataAdapter.DeleteCommand = objDeleteCommand sUpdateSQL = "Update Artist SET ArtistName = ? " & _ "WHERE ArtistID = ?" objUpdateCommand.Connection = objConn objUpdateCommand.CommandText = sUpdateSQL objParam = objUpdateCommand.Parameters.Add( _ "@ArtistName", OleDb.OleDbType.Char) objParam.SourceColumn = "ArtistName" objParam.SourceVersion = DataRowVersion.Current objParam = objUpdateCommand.Parameters.Add( _ "@ArtistID", OleDb.OleDbType.Integer) objParam.SourceColumn = "ArtistID" objParam.SourceVersion = DataRowVersion.Original objDataAdapter.UpdateCommand = objUpdateCommand sInsertSQL = "INSERT INTO Artist " & _ "(ArtistID, ArtistName) " & _ "VALUES (?,?)" objInsertCommand.Connection = objConn objInsertCommand.CommandText = sInsertSQL objParam = objInsertCommand.Parameters.Add( _ "@ArtistID", OleDb.OleDbType.Integer) objParam.SourceColumn = "ArtistID" objParam.SourceVersion = DataRowVersion.Current objParam = objInsertCommand.Parameters.Add( _ "@ArtistName", OleDb.OleDbType.Char) objParam.SourceColumn = "ArtistName" objParam.SourceVersion = DataRowVersion.Current objDataAdapter.InsertCommand = objInsertCommand Try objConn.Open() Catch myException As System.Exception Console.WriteLine(myException.Message) End Try
403
Mit .NET auf Daten zugreifen
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
404
Try Dim sNewArtistSQL As String sNewArtistSQL = "INSERT INTO Artist " & _ "(ArtistID, ArtistName, " & _ "ArtistFirstName, ArtistLastName)" & _ "VALUES (11, 'Weird Al Yankovich'," & _ " 'Al', 'Yankovich')" Dim objNewArtistCmd _ As New OleDb.OleDbCommand(sNewArtistSQL, objConn) objNewArtistCmd.ExecuteNonQuery() Catch e As Exception 'Kann einen Fehler auslösen, weil 'ArtistID #11 bereits vorhanden ist. 'Das macht aber nichts. Console.WriteLine(e.Message) End Try If objConn.State = ConnectionState.Open Then Try objDataAdapter.MissingSchemaAction _ = MissingSchemaAction.AddWithKey objDataAdapter.Fill(objDS, "Artist") objConn.Close() Dim objTable As DataTable objTable = objDS.Tables("Artist") Dim drRows As DataRowCollection Dim objCurrentRow As DataRow drRows = objTable.Rows DisplayTable(objTable) Console.Write("About to Delete," & _ " press return to start!") Console.ReadLine() 'Lösche die Zeile mit Primärschlüssel = 11. drRows.Find(11).Delete() Console.Write("After Deletion," & _ " press return to view results") Console.ReadLine() DisplayTable(objTable)
Mit Datenmengen arbeiten
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
Console.Write("About to Insert," & _ " press return to start!") Console.ReadLine() Dim drRow As Data.DataRow drRow = objTable.NewRow drRow("ArtistID") = 40 drRow("ArtistName") = "Kent Sharkey" objTable.Rows.Add(drRow) Console.Write("After Insert," & _ " press return to view!") Console.ReadLine() DisplayTable(objTable)
Console.Write("About to Update," & _ " press return to start!") Console.ReadLine() 'Hole die Zeile mit dem Primärschlüssel 7 (Lisa Loeb). drRow = drRows.Find(7) drRow.BeginEdit() drRow("ArtistName") = "John Doe" drRow.EndEdit() Console.Write("After Update," & _ " press return to view results") Console.ReadLine() DisplayTable(objTable) objConn.Open() objDataAdapter.Update(objDS, "Artist") Catch myException As System.Exception Console.WriteLine(myException.Message) End Try Console.Write("Press Return to End.") Console.ReadLine() End If End Sub Sub DisplayTable(ByRef objTable As DataTable) Dim objRow As DataRow Dim objCol As DataColumn Dim i, j As Integer
405
Mit .NET auf Daten zugreifen
177 178 For j = 0 To objTable.Columns.Count – 1 179 objCol = objTable.Columns(j) 180 Console.Write("{ 0} :{ 1} ", _ 181 objCol.ColumnName, _ 182 objCol.DataType.Name) 183 Next 184 Console.WriteLine() 185 For i = 0 To objTable.Rows.Count – 1 186 objRow = objTable.Rows(i) 187 Select Case objRow.RowState 188 Case DataRowState.Deleted 189 Console.Write("[Deleted] ") 190 Case DataRowState.Modified 191 Console.Write("[Modified] ") 192 Case DataRowState.New 193 Console.Write("[New] ") 194 Case DataRowState.Unchanged 195 Console.Write("[Unchanged] ") 196 End Select 197 For j = 0 To objTable.Columns.Count – 1 198 If objRow.RowState DataRowState.Deleted Then 199 Console.Write("{ 0} ", _ 200 objRow.Item(j)) 201 Else 202 Console.Write("{ 0} ", _ 203 objRow.Item(j, DataRowVersion.Original)) 204 End If 205 Next 206 Console.WriteLine() 207 Next 208 End Sub 209 End Module
Der Code im Listing 12.15 steht zum Herunterladen auf der Website zu diesem Buch bereit, sodass Sie ihn nicht manuell einzugeben brauchen, falls Sie dies nicht wirklich möchten. Außerdem finden Sie ihn auf der Begleit-CD im Unterverzeichnis \Beispiele\Kap12. Der erste Codeabschnitt sieht ähnlich aus wie aller Code, den wir in der heutigen Lektion schreiben. Wir erstellen die Verbindungszeichenkette, die Sie durch die für Ihr System passenden Werte ersetzen sollten. Mit der Verbindungskette instanziieren wir ein neues OleDBConnection-Objekt (Zeile 14). Außerdem erzeugen wir einen OleDBDataAdapter und ein DataSet und initialisieren sie, damit Sie eine Verbindung zur Datenbank herstellen und das Ergebnis der SELECT-Anweisung wieder zu Ihrem Programm zurückbringen können. Als Teil des Initialisierungscodes werden
406
Mit Datenmengen arbeiten
zudem drei neue OleDBCommand-Objekte erzeugt, mit denen wir die UPDATE-, DELETE- und INSERT-Befehle für den OleDBDataAdapter darstellen. Diese Befehlsobjekte werden jedoch nicht hier erzeugt, sondern erst dann, wenn die einzelnen Befehle eingerichtet werden. Die Zeilen 30 bis 41 erzeugen das Befehlsobjekt Delete und richten es ein, wobei sie das einzelne Parameterobjekt für ArtistID (den Primärschlüssel) hinzufügen. Achten Sie darauf, wie die Zeile 39 SourceVersion als DataRowVersion.Original angibt, da die aktuellen Daten für eine gelöschte Zeile nicht gültig sind. Die Zeilen 43 bis 59 richten den Befehl Update ein und die Zeilen 61 bis 78 behandeln den Befehl Insert. Beachten Sie, dass wir für Update und Insert viele Parameter benötigen, da alle Felder der Tabelle in diese Befehle geschickt werden müssen. Die Zeilen 80 bis 84 sind wieder Code, der in allen heutigen Beispielen vorkommt. Sie richten die Datenbankverbindung ein. Anschließend wird mit der Methode ExecuteNonQuery() eines Befehlsobjekts eine einzige Datenzeile in die Datenbank eingefügt. Diese Zeile wird eingefügt, damit sie später wieder gelöscht werden kann, denn der Versuch, irgendeinen anderen Künstlerdatensatz zu löschen, würde scheitern, weil es Kinddatensätze gibt (CDs dieses Künstlers in der Tabelle Disc). Diese Zeile fügen wir direkt ein, verwenden die Datenmenge und den Datenadapter also nicht im verbindungslosen Modus. Die Zeile 106, in der die Datenbankverbindung bereits geöffnet und der einzelne neue Datensatz eingefügt ist, teilt dem Datenadapter mit, wie er die Einrichtung einer Tabelle behandeln soll, wenn es kein Schema gibt. (Das Schema sind die Informationen über das Layout und den Datentyp.) Die hier verwendete Methode AddWithKey teilt dem Datenadapter mit, dass er das Basisschema (die Feldinformationen) automatisch erstellen und auch die Primärschlüsselinformationen einfügen soll. Anschließend wird in der Zeile 108 das DataSet mit den Daten (mit der Methode Fill) und einem zusätzlichen Parameter geladen, der den Namen der neu erstellten Tabelle angibt (Artist). Um sicherzustellen, dass dies wirklich ein verbindungsloser Prozess ist, schließt die Zeile 109 die Verbindung zur Datenbank, bevor die eigentliche Bearbeitung der Daten stattfindet. An dieser Stelle löscht, verändert oder ergänzt der Code Datensätze im DataTable-Objekt, um alle drei Arten von Änderungen zu prüfen, die bei einer DataTable stattfinden können. Mit der Methode Find des DataRowCollection-Objekts finden wir die richtige Zeile, die gelöscht (Zeile 123) oder bearbeitet (Zeile 152) werden soll. Diese Methode gibt für einen zulässigen Primärschlüssel, den sie nachschlägt, ein DataRow-Objekt zurück. Dieser Prozess verlangt, dass das DataTable-Objekt Primärschlüsselinformationen enthält. (Eine Erklärung dafür, wie die Zeile 106 diese Informationen bereitstellt, finden Sie im vorherigen Abschnitt.)
407
Mit .NET auf Daten zugreifen
Nach vielen Bearbeitungen wendet die Zeile 164 mit der Methode Update von OleDBDataAdapter alle Datenänderungen auf DataSet an. Diese Methode nimmt als Parameter ein DataSet sowie eine Zeichenkette entgegen, die angibt, mit welcher Tabelle in DataSet die Datenbank aktualisiert werden soll. Wenn der Name der Tabelle nicht angegeben ist, versucht der Adapter, die Datenbank mit allen verfügbaren Tabellen zu aktualisieren. Dieser Code bewirkt, dass in der Datenbank eine Sequenz von SQL-Anweisungen ausgeführt wird, von denen jede mit einem der Befehle des Adapters übereinstimmt. Wenn Sie SQL Server verwenden und sehen möchten, was geschieht, können Sie während der Ausführung des Codes mit dem Profiler beobachten, wie die Befehle auf dem Server ausgeführt werden. In diesem Buch können wir nicht detailliert darauf eingehen, wie Sie den Profiler verwenden; die Ausgabe sollte ungefähr folgendermaßen aussehen: INSERT INTO Artist (ArtistID, ArtistName, ArtistFirstName, ArtistLastName) VALUES (11, 'Weird Al Yankovich', 'Al', 'Yankovich') go SELECT ArtistID, ArtistName From Artist go exec sp_executesql N'Update Artist SET ArtistName = @P1 WHERE ArtistID = @P2', N'@P1 char(8),@P2 int', 'John Doe', 7 go exec sp_executesql N'DELETE FROM Artist WHERE ArtistID = @P1', N'@P1 int', 11 go exec sp_executesql N'INSERT INTO Artist (ArtistID, ArtistName) VALUES (@P1,@P2)', N'@P1 int,@P2 char(12)', 40, 'Kent Sharkey' go
In diesem Beispiel wurde zwar nur ein einziger Datensatz eingefügt, verändert und gelöscht, aber die Methode Update würde die passenden Befehle einfach für jeden veränderten Datensatz jeweils einmal aufrufen.
Automatisch generierte Befehle verwenden Sie brauchen die Befehle nicht selbst anzugeben, sondern können es ADO.NET überlassen, die notwendigen Befehle für Sie zu erstellen. Das kann einfacher sein, obwohl der Prozess in mehreren Schritten abläuft. Die erste Anforderung für automatisch generierte Befehle besteht darin, dass das DataSet mindestens Primärschlüsselinformationen für die relevanten Tabellen enthalten muss. Diese Informationen können auf verschiedene Arten bereitgestellt werden, die alle die gewünschte Wirkung erzielen.
408
Mit Datenmengen arbeiten
Erstens können Sie sie manuell mit der Eigenschaft PrimaryKey von DataTable angeben. Diese Eigenschaft wird auf das passende Feld gesetzt, das Sie mit der Sammlung Columns des DataTable-Objekts erhalten: Dim PK(0) As DataColumn PK(0) = objTable.Columns("ArtistID") objTable.PrimaryKey = PK
Außerdem können Sie die Methode FillSchema verwenden und dadurch den Datenadapter veranlassen, die passenden Informationen einschließlich des Primärschlüssels und vieler anderer Details zu der Tabelle aus der Datenquelle zu holen. Diese Methode erzeugt das DataTable-Objekt, lädt es dann und füllt die Tabelle mit einer Liste von Spalten, den Primärschlüsselinformationen und allen anderen Beschränkungen, die sie in der Datenquelle gefunden hat: objDataAdapter.FillSchema(objDS, SchemaType.Mapped, "Artist")
Schließlich können Sie den Adapter noch veranlassen, die erforderlichen Schemainformationen während des Ladens der Daten zu erzeugen, indem Sie den Wert der Eigenschaft MissingSchemaAction setzen, bevor Sie Fill aufrufen. Diese Eigenschaft hat einige interessante Einstellungen; für unseren Zweck benötigen wir jedoch AddWithKey. Der Standardwert Add erzeugt die Spalteninformationen (Namen und Datentypen) automatisch, falls diese Details in der Tabelle noch nicht vorhanden sind. Der Wert AddWithKeys fügt dieselben Spalteninformationen ein und setzt außerdem für alle Tabellen, die er füllt, die Eigenschaft PrimaryKey. objDataAdapter.MissingSchemaAction _ = MissingSchemaAction.AddWithKey objDataAdapter.Fill(objDS, "Artist")
Unabhängig davon, welche Methode Sie verwenden, können die Befehle Update, Insert und Delete automatisch generiert werden, sobald das zu aktualisierende DataTable-Objekt die Primärschlüsselinformationen enthält. Zuerst muss eine Instanz der passenden CommandBuilder-Klasse (OleDBCommandBuilder oder SQLCommandBuilder) erzeugt und initialisiert werden. Wenn dann die Methode Update des Adapters aufgerufen wird, werden alle fehlenden Befehle bei Bedarf erzeugt. Das Listing 12.16 veranschaulicht diese Begriffe, indem es einige einfache Daten in ein DataSet-Objekt lädt, einige Änderungen vornimmt und dann Update aufruft. Listing 12.16: Die Methode Update führt auf der Quelldatenbank die entsprechenden Befehle aus. 1 Module Module1 2 Private Const _ 3 sConnection As String = _ 4 "Provider=SQLOLEDB.1;" & _ 5 "Password=;" & _ 6 "Persist Security Info=True;" & _
409
Mit .NET auf Daten zugreifen
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
410
"User ID=sa;" & _ "Initial Catalog=CD;" & _ "Data Source=(local)" Sub Main() Dim sSQL As String sSQL = "SELECT ArtistID, ArtistName From Artist" Dim objConn _ As New OleDb.OleDbConnection(sConnection) Dim objDataAdapter _ As New OleDb.OleDbDataAdapter(sSQL, objConn) Dim objCommandBuilder _ As New OleDb.OleDbCommandBuilder(objDataAdapter) Dim objDS _ As New DataSet("Artists") Try objConn.Open() Catch myException As System.Exception Console.WriteLine(myException.Message) End Try If objConn.State = ConnectionState.Open Then Try objDataAdapter.MissingSchemaAction _ = MissingSchemaAction.AddWithKey objDataAdapter.Fill(objDS, "Artist") objConn.Close() Dim objTable As DataTable objTable = objDS.Tables("Artist") Dim drRows As DataRowCollection Dim objCurrentRow As DataRow drRows = objTable.Rows DisplayTable(objTable) Console.Write("About to Edit," & _ " press return to start!") Console.ReadLine() 'Lösche die Zeile mit Primärschlüssel = 11. drRows.Find(7)("ArtistName") = "Kent Sharkey" Console.Write("After Edit," & _ " press return to view results")
Mit Datenmengen arbeiten
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
Console.ReadLine() DisplayTable(objTable) objConn.Open() objDataAdapter.Update(objDS, "Artist") Catch myException As System.Exception Console.WriteLine(myException.Message) End Try Console.Write("Press Return to End.") Console.ReadLine() End If End Sub Sub DisplayTable(ByRef objTable As DataTable) Dim objRow As DataRow Dim objCol As DataColumn Dim i, j As Integer For j = 0 To objTable.Columns.Count – 1 objCol = objTable.Columns(j) Console.Write("{ 0} :{ 1} ", _ objCol.ColumnName, _ objCol.DataType.Name) Next Console.WriteLine() For i = 0 To objTable.Rows.Count – 1 objRow = objTable.Rows(i) Select Case objRow.RowState Case DataRowState.Deleted Console.Write("[Deleted] ") Case DataRowState.Modified Console.Write("[Modified] ") Case DataRowState.Added Console.Write("[New] ") Case DataRowState.Unchanged Console.Write("[Unchanged] ") End Select For j = 0 To objTable.Columns.Count – 1 If objRow.RowState DataRowState.Deleted Then Console.Write("{ 0} ", _ objRow.Item(j)) Else Console.Write("{ 0} ", _ objRow.Item(j, DataRowVersion.Original)) End If
411
Mit .NET auf Daten zugreifen
99 Next 100 Console.WriteLine() 101 Next 102 End Sub 103 End Module
Wie alle Beispielen in der heutigen Lektion kann auch dieser Code mit einigen kleinen Änderungen für die Arbeit mit SQLClient-Klassen angepasst werden: In der Verbindungszeichenkette müssen Sie den Provider-Abschnitt löschen und alle als OleDB-Klassen deklarierten Objekte müssen in die SQLClient-Entsprechungen umgewandelt werden.
Mit mehreren Tabellen arbeiten Wenn Daten in ein DataSet geladen werden, werden sie in einzelne Tabellen platziert, sodass ein DataSet die Ergebnisse vieler Anfragen enthalten kann. Nachdem Sie ein DataSet mit mehr als einer Tabelle erstellt haben, können Sie die Beziehungen zwischen diesen Tabellen angeben, z.B. (entsprechend unseren bisherigen Beispielen) zwischen der Tabelle Artist und der Tabelle Disc. Alle diese Beziehungen, die es innerhalb eines DataSet gibt, werden durch Objekte in der Sammlung Relations dargestellt, auf die man als eine Eigenschaft des DataSet-Objekts zugreifen kann. Mit der Methode Add der Sammlung Relations können Sie eine Beziehung zwischen zwei Tabellen erstellen. Diese Methode hat viele Parameteroptionen, die auf verschiedene Arten aufrufen können. Aber wenn Sie eine einfache Beziehung erstellen möchten, die auf einem einzigen Feld (einer Spalte) in der Elterntabelle und einem einzigen Feld in der Kindtabelle beruht, dann funktioniert die folgende Syntax gut: objNewRelation = objDS.Relations.Add(RelationName,ParentColumn,ChildColumn)
Als ein Beispiel hierfür stellt Listing 12.17 eine Datenbankverbindung her und lädt die Tabellen Artist und Disc in ein neues DataSet. Nach dem Laden wird eine Beziehung zwischen diesen beiden Tabellen hergestellt: Listing 12.17: DataSet spiegelt die Quelldaten genau wider. 1 Module Module1 2 3 Private Const _ 4 sConnection As String = _ 5 "Provider=SQLOLEDB.1;" & _ 6 "Password=321dweksp302axn;" & _ 7 "Persist Security Info=True;" & _ 8 "User ID=sa;" & _ 9 "Initial Catalog=CD;" & _ 10 "Data Source=(local)" 11
412
Mit Datenmengen arbeiten
12 Sub Main() 13 Dim sSQLArtist As String 14 Dim sSQLDisc As String 15 16 sSQLArtist = "SELECT ArtistID, ArtistName From Artist" 17 sSQLDisc = "SELECT ArtistID, CDTitle From Disc" 18 19 Dim objConn _ 20 As New OleDb.OleDbConnection(sConnection) 21 Dim objDataAdapterArtist _ 22 As New OleDb.OleDbDataAdapter(sSQLArtist, objConn) 23 Dim objDataAdapterDisc _ 24 As New OleDb.OleDbDataAdapter(sSQLDisc, objConn) 25 Dim objDS _ 26 As New DataSet("CD") 27 28 objDataAdapterArtist.MissingSchemaAction = _ 29 MissingSchemaAction.AddWithKey 30 objDataAdapterDisc.MissingSchemaAction = _ 31 MissingSchemaAction.AddWithKey 32 33 objDataAdapterArtist.Fill(objDS, "Artist") 34 objDataAdapterDisc.Fill(objDS, "Disc") 35 36 objDS.Relations.Add("DiscArtist", _ 37 objDS.Tables("Artist").Columns("ArtistID"), _ 38 objDS.Tables("Disc").Columns("ArtistID")) 39 End Sub 40 End Module
Nachdem die Beziehungen hergestellt wurden, können Sie mit diesen Informationen einen strukturierten Zugriff von einer Elterndatenzeile auf alle verwandten Zeilen in der Kindtabelle ermöglichen. Der Code im Listing 12.18, den Sie direkt vor dem Ende der Prozedur im Listing 12.17 einfügen können, durchläuft die Tabelle Artist mit einer Schleife und zeigt die mit dem Künstler verbundenen CDs an. Listing 12.18: Mit der Methode GetCHildRows auf Kindzeilen irgendeiner Elternzeile zugreifen 1 2 3 4 5 6 7 8
Dim objDRParent As DataRow Dim objDRChild As DataRow Dim objChildRows() As DataRow For Each objDRParent In objDS.Tables("Artist").Rows Console.WriteLine("{ 0} { 1} ", _ objDRParent("ArtistID"), _ objDRParent("ArtistName"))
413
Mit .NET auf Daten zugreifen
9 10 11 12 13 14 15
objChildRows = objDRParent.GetChildRows("DiscArtist") For Each objDRChild In objChildRows Console.WriteLine(" { 0} ", _ objDRChild("CDTitle")) Next Next Console.ReadLine()
Der wesentliche Teil dieses Codebeispiels ist die Zeile 9, in der mit der Methode GetChildRows des DataRow-Objekts aus der Kindtabelle ein Array von DataRow-Objekten zurückgegeben wird. Dieses Array umfasst alle Zeilen aus der Kindtabelle, in denen die ArtistIDFelder der Eltern- und der Kindtabelle übereinstimmen.
Ansichten ADO.NET umfasst Ansichten von Daten sowie die Fähigkeit, eine Sortierreihenfolge, einen Zeilenfilter und einen auf RowState basierenden Filter (Modified, Deleted, Unchanged oder New) anzugeben. Auf diese Ansicht (DataView) können Sie entweder mit der Eigenschaft DefaultView der Tabelle oder über die Sammlung Relations zugreifen, die eine Eigenschaft von DataSet ist. Das Listing 12.19 enthält den Standardcode zum Öffnen einer Datenbank, Füllen der Tabelle mit Daten und Erzeugen eines DataView-Objekts mit der Tabelle als Konstruktor. Nachdem die Ansicht erzeugt wurde, kann man sie mit einer beliebigen Kombination von drei Eigenschaften konfigurieren: RowStateFilter, Sort und/oder RowFilter. Die Ansicht selbst bindet man an ein Windows-Forms-Steuerelement (mehr dazu im folgenden Abschnitt zur »Datenbindung«) oder man greift auf ihren Inhalt direkt als eine Sammlung von DataRowViews zu: Listing 12.19: Mehrere DataView-Objekte zeigen auf dieselbe DataTable. 1 Module Module1 2 Private Const _ 3 sConnection As String = _ 4 "Provider=SQLOLEDB.1;" & _ 5 "Password=;" & _ 6 "Persist Security Info=True;" & _ 7 "User ID=sa;" & _ 8 "Initial Catalog=CD;" & _ 9 "Data Source=(local)" 10 11 Sub Main() 12 Dim sSQLArtist As String 13 sSQLArtist = "SELECT ArtistID, ArtistName From Artist"
414
Mit Datenmengen arbeiten
14 Dim objConn _ 15 As New OleDb.OleDbConnection(sConnection) 16 Dim objDataAdapterArtist _ 17 As New OleDb.OleDbDataAdapter(sSQLArtist, objConn) 18 Dim objDS _ 19 As New DataSet("CD") 20 objDataAdapterArtist.MissingSchemaAction = _ 21 MissingSchemaAction.AddWithKey 22 objDataAdapterArtist.Fill(objDS, "Artist") 23 24 Dim objDR As DataRow 25 Console.WriteLine("Through Table") 26 For Each objDR In objDS.Tables("Artist").Rows 27 Console.WriteLine("{ 0} { 1} ", _ 28 objDR("ArtistID"), _ 29 objDR("ArtistName")) 30 Next 31 32 Dim objDV As DataView 33 objDV = New DataView(objDS.Tables("Artist")) 34 objDV.Sort = "ArtistName" 35 objDV.RowFilter = "ArtistID < 8" 36 37 Dim objDRV As DataRowView 38 Console.WriteLine("Through DataView") 39 For Each objDRV In objDV 40 Console.WriteLine("{ 0} { 1} ", _ 41 objDRV("ArtistID"), _ 42 objDRV("ArtistName")) 43 Next 44 Console.ReadLine() 45 End Sub 46 End Module
Bis zur Zeile 22 unterscheidet sich dieser Code nicht wesentlich von den anderen Beispielen, die Sie in der heutigen Lektion gesehen haben: Eine Datenbankverbindung wird hergestellt und Daten werden in ein DataSet geladen. Nachdem die Daten geladen wurden, gibt eine kurze Schleife (Zeilen 26 bis 30) die Daten aus, wobei sie DataView nicht zu durchlaufen scheint (tatsächlich durchläuft sie die Standarddatenansicht). Dann wird ein DataView-Objekt erzeugt und mit einer Sortierreihenfolge und einem Filter auf den Werten in den Zeilen eingerichtet (Zeilen 32 bis 35). Schließlich greifen wir mit diesem DataView-Objekt auf die Zeilen zu (Zeilen 39-43), wobei wir sie diesmal mit der Sortierreihenfolge und dem Filter von DataView ausgeben. Falls Sie alle bisherigen Beispiele ausgeführt haben, erhalten Sie durch diesen Code ungefähr die folgende Ausgabe:
415
Mit .NET auf Daten zugreifen
Through Table 1 Natalie Imbruglia 2 The Tragically Hip 3 Sting 4 Queen 5 Boney M. 6 Barenaked Ladies 7 Kent Sharkey 8 Janet Jackson 9 Matchbox Twenty 10 Madonna 40 Kent Sharkey Through DataView 6 Barenaked Ladies 5 Boney M. 7 Kent Sharkey 1 Natalie Imbruglia 4 Queen 3 Sting 2 The Tragically Hip
Selbst wenn Ihre Ausgabe nicht genauso aussieht wie dieser Text, werden Sie feststellen, dass in der Liste Through Table elf Zeilen ausgegeben werden, die nach der ArtistID geordnet sind. In der Liste Through DataView greifen wir über die neu erstellte Ansicht auf die Zeilen zu und die Ausgabe spiegelt sowohl den Filter als auch die Sortierreihenfolge wider.
12.4 Datenbindung Die Datenbindung bietet eine Möglichkeit, ein Benutzeroberflächenelement (z.B. ein Raster) mit einer Datenquelle wie z.B. einem DataTable- oder DataSet-Objekt zu verknüpfen. Sobald ein Benutzeroberflächenelement an eine Datenquelle gebunden ist, werden Änderungen dieser Datenquelle in der Benutzeroberfläche widergespiegelt und Änderungen, die über die Benutzeroberfläche an den Daten vorgenommen wurden, werden an die Datenquelle weitergeleitet. Für den Entwickler schließt die Datenbindung all die komplexen Fragen der Interaktion des Anwenders mit den Daten ab. Außerdem brauchen Sie durch die Datenbindung nicht mehr so viel Code zu schreiben, nur um diese Interaktion zu ermöglichen. Eine interessante und mächtige Eigenschaft der Datenbindung in .NET besteht darin, dass sie nicht auf die Arbeit mit DataSet-Objekten beschränkt ist. Sehr viele verschiedene Objekte können datengebunden werden: Array-, Collection-, DataView-, DataSet-, DataTable- und andere Objekte. In unseren Beispielen arbeiten wir nur mit Data-
416
Datenbindung
Set-, DataView- und DataTable-Objekten, aber es gibt die Möglichkeit, ganz verschiedene
Informationsarten zu binden. Wenn ein DataSet- oder DataTable-Objekt an ein Steuerelement einer Benutzeroberfläche gebunden ist, dann findet die eigentliche Bindung mit der Standardansicht (DataView- oder DataSetView-Objekt) des DataTable- oder DataSetObjekts statt. In .NET können Sie zwei Haupttypen von Benutzeroberflächen erstellen: den umfänglichen Windows-Client (System.Windows.Forms) und den Internetclient WebForms. Die Einzelheiten der Bindung für Windows Forms werden wir im folgenden Abschnitt zusammen mit einem Schritt-für-Schritt-Beispiel für die Bindung einer Datenmenge an ein Raster besprechen. Die Datenbindung in WebForms und der allgemeine Datenzugriff mit ASP.NET gehören nicht in die heutige Lektion.
Datenbindung mit Windows Forms Alle Windows-Forms-Steuerelemente, die die Datenbindung unterstützen, haben zwei Versionen: eine, die die einfache Bindung unterstützt, und eine, die die komplexe Bindung behandeln kann. Einfache Bindung bedeutet, dass eine Eigenschaft eines Steuerelements mit einem Feld einer Datenquelle verbunden wird. Bei der komplexen Bindung wird das Steuerelement mit einer vollständigen Datenquelle (z.B. einer DataTable) verbunden. Von den integrierten Objekten unterstützen nur die Steuerelemente DataGrid und ComboBox die komplexe Bindung.
Einfache Bindung Steuerelemente, die die einfache Bindung unterstützen, bieten eine Sammlung namens DataBindings, mit der man Eigenschaften mit Datenfeldern verknüpfen kann. Das Listing 12.20 gibt ein Beispiel für diesen Typ der Datenbindung als Teil einer Windows FormsAnwendung. Um diesen Code selbst auszuführen, erstellen Sie eine neue Visual Basic Windows-Anwendung und setzen ein einzelnes Textfeld und eine Schaltfläche in das Formular. Lassen Sie die Standardnamen der Steuerelemente (textbox1 und button1) unverändert und platzieren Sie diesen Code in das Ereignis Click für die Schaltfläche. Listing 12.20: Die Datenbindung kann man dynamisch im Code erstellen. 1 2 3 4 5
Private Sub button1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles button1.Click Dim sConnection As String = _ "Provider=SQLOLEDB.1;" & _
417
Mit .NET auf Daten zugreifen
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
"Password=;" & _ "Persist Security Info=True;" & _ "User ID=sa;" & _ "Initial Catalog=CD;" & _ "Data Source=(local)" Dim sSQL As String sSQL = "SELECT ArtistID, ArtistName From Artist" Dim objConn _ As New OleDb.OleDbConnection(sConnection) Dim objDataAdapter _ As New OleDb.OleDbDataAdapter(sSQL, objConn) Dim objDS _ As New DataSet("Artists") Dim objDV _ As DataView Try objConn.Open() Catch myException As System.Exception Windows.Forms.MessageBox.Show(myException.Message) End Try If objConn.State = ConnectionState.Open Then Try objDataAdapter.Fill(objDS, "Disc") objConn.Close() Dim objTable As DataTable objTable = objDS.Tables("Disc") objDV = objTable.DefaultView textBox1.DataBindings.Add("Text", _ objDV, _ "ArtistName") Catch myException As System.Exception Windows.Forms.MessageBox.Show(myException.Message) End Try End If End Sub
Zusätzlich zu dem Code, von dem Sie inzwischen wissen, dass er die Datenbankverbindung öffnet und ein DataSet-Objekt holt (Zeilen 1 bis 34), enthält das Listing am Ende der Prozedur eine Zeile (Zeile 37), die die eigentliche Datenbindung vornimmt. Die Methode Add der Sammlung DataBindings ermöglicht es Ihnen, eine Eigenschaft des Steuerelements und die Datenquelle (in diesem Fall die Standarddatenansicht der Tabelle) anzugeben. Anschließend wird der Wert des Feldes ArtistName angezeigt (siehe Abbildung 12.6).
418
Datenbindung
Abbildung 12.6: Die einfache Datenbindung kann mit vielen verschiedenen Steuerelementen gleichzeitig vorgenommen werden, wodurch Sie die Felder Ihrer Daten auf verschiedene Art anzeigen können.
Dies ist nicht sonderlich nützlich, da Sie nur den ersten Datensatz betrachten und keine Möglichkeit haben, sich rückwärts oder vorwärts durch die Daten zu bewegen. Um bei der einfachen Datenbindung solche Möglichkeiten zu bieten, müssen Sie auf die Sammlung BindingContext des Elternformulars zugreifen. Wenn Sie eine bestimmte Datenquelle angeben, an der Sie interessiert sind, können Sie ein BindingContext-Objekt für diese Datenquelle holen. Im folgenden Beispiel war die ursprüngliche Datenquelle keine Variable auf Formularebene, sodass der Code darauf zugreift, indem er die Sammlung DataBindings des Textfeldes durchläuft. Da es nur eine Datenbindung gibt, wird die einzige verfügbare Datenquelle zurückgegeben, wenn Sie den Index Null angeben. Mit diesem Kontextobjekt haben Sie Zugriff auf mehrere Eigenschaften und Methoden für die Erstellung einer Schnittstelle bei der Arbeit mit gebundenen Daten. Die Eigenschaft Position ist nützlich, da Sie es Ihnen ermöglicht, sich nach Belieben vorwärts und rückwärts durch die Daten zu bewegen. Wenn Sie dem Formular eine zweite Schaltfläche hinzufügen und den folgenden Code einfügen, können Sie sich vorwärts durch die Liste der Künstler bewegen. Beachten Sie, dass dieser Code alles andere als intelligent ist: Wenn Sie sich über das Ende der verfügbaren Daten hinausbewegen, bricht er zusammen. Private Sub button2_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles button2.Click Me.BindingContext(textBox1.DataBindings(0).DataSource).Position += 1 End Sub
Dieses Kontextobjekt stellt weitere Elemente wie z.B. AddNew und CancelCurrentEdit bereit, wodurch es zu einem Objekt wird, das Sie wahrscheinlich häufig verwenden werden.
419
Mit .NET auf Daten zugreifen
Komplexe Datenbindung Die komplexe Datenbindung bedeutet, dass man Steuerelemente nicht an ein einzelnes Feld, sondern an eine ganze Datenquelle bindet. Diese Art der Datenbindung wird von zwei Steuerelementen unterstützt, die mit Visual Studio .NET geliefert werden: DataGrid und ComboBox. Um die Datenbindung mit einem dieser Steuerelemente einzurichten, setzten Sie die Eigenschaft DataSource des Steuerelements auf das richtige Objekt (in diesem Fall DataView, es könnte aber auch Array, Collection, DataSet usw. sein). Wenn DataSource mehr als ein Objekt enthält, an das gebunden werden kann (z.B. ein DataSet mit mehr als einer Tabelle), dann geben Sie das richtige Element als einen Zeichenkettenwert in der Eigenschaft DataMember an. Wenn Sie also das DataGrid an ein DataSet binden, müssen sie den Namen der Tabelle als die Eigenschaft DataMember angeben. Der Code hierfür ist identisch mit dem in Listing 12.20, wobei Sie die Zeilen 36-38 allerdings durch die folgenden Zeilen ersetzen müssen: dataGrid1.DataSource = objDS dataGrid1.DataMember = "Disc"
Um die komplexe Bindung selbst auszuprobieren, erstellen Sie eine neue WindowsAnwendung und setzen eine Schaltfläche und ein DataGrid in das Formular (beide stehen in der Toolbox zur Verfügung). Lassen Sie die Standardnamen überall unverändert und platzieren Sie den Code aus dem Listing 12.20 einschließlich der beschriebenen Änderungen in das Ereignis Click Ihrer Schaltfläche. Dieser Code bewirkt, dass nach dem Anklicken der Schaltfläche das DataGrid an die Tabelle Artist im DataSet gebunden wird und diese anzeigt. Ihr Formular sieht vielleicht nicht genau wie das in der Abbildung 12.7 gezeigte aus, sollte aber zumindest Ähnlichkeit damit haben.
Abbildung 12.7: Ein DataGrid erstellt ohne viel Code eine Umgebung, die professionell aussieht.
420
Zusammenfassung
12.5 Zusammenfassung Für .NET wurde der Datenzugriff vollständig neu entworfen, aber er unterstützt noch immer das zugrunde liegende OLE DB-Verfahren. Mit OLE DB-Providern (Treibern) können Sie Code schreiben, der auf den meisten Datenbanken läuft. Dennoch benötigen Sie nur eine einzige Art, um alle diese Datenbanken zu programmieren. Dank der verbindungslosen Natur von DataSets können Sie vollständig interaktive Systeme erstellen, die keine wertvollen Datenbankverbindungen besetzen. Wenn Sie Benutzeroberflächen einrichten, bietet die Datenbindung eine schnelle und einfache Möglichkeit, die Daten in einem Windows-Formular anzuzeigen.
12.6 Fragen und Antworten F
In allen Beispielen der heutigen Lektion war die Verbindungszeichenkette hart in den Code geschrieben. Da sie auch meinen Benutzernamen und mein Passwort umfasst, scheint dies kein sicheres Verfahren zu sein. A
F
Ein ausgezeichnetes Argument. Beim Code in diesen Beispielen befand sich die Verbindungszeichenkette direkt in der .vb-Datei, die relativ unsicher ist. Wenn Sie nicht einfach nur Beispiele schreiben, wäre es besser, den Wert der Verbindungszeichenkette in die Registry oder in eine Einstellungsdatei auf der Festplatte zu speichern. Die Sicherheit ist nicht der einzige Grund hierfür. Wenn Sie die Verbindungszeichenkette nicht in den Code schreiben, können Sie Datenbanken und andere Verbindungsparameter ändern, ohne das System neu kompilieren zu müssen.
Was geschieht bei der Aktualisierung, falls der Datensatz, den Sie zu aktualisieren versuchen, geändert wurde, nachdem Sie die ursprünglichen Daten extrahiert haben? A
Bei Aktualisierungen der Datenbank müssen Sie Fragen der Nebenläufigkeit berücksichtigen, d.h., dass möglicherweise zwei oder mehr Personen dieselben Daten innerhalb desselben Zeitraumes ändern. Im Fall von DataSet-Objekten ist es wahrscheinlich, dass zwischen dem Laden der Daten und der Aktualisierung relativ viel Zeit vergeht, da sie dafür entworfen sind, offline verwendet zu werden. Während dieser Zeit zwischen dem Laden und dem Aktualisieren könnte ein anderer Anwender dieselben Daten verändert haben. Automatisch generierte Befehle enthalten auch Code, der diese Aspekte vor dem Aktualisieren prüft. Wenn Sie die Befehle manuell erstellen, müssen Sie selbst die ursprünglichen Daten mit den aktuellen in der Datenbank vergleichen.
421
Mit .NET auf Daten zugreifen
12.7 Workshop Der Workshop soll Ihnen dabei helfen, mögliche Fragen vorherzusehen und das Gelernte zu wiederholen. Außerdem soll er Sie dazu anregen, über die praktische Umsetzung Ihres Wissens nachzudenken. Die Antworten zum Quiz finden Sie im Anhang A.
Quiz 1. Welche Eigenschaft müssen Sie setzen (und auf welchen Wert), damit der Datenadapter Primärschlüsselinformationen erstellt, wenn er Daten in ein DataSet-Objekt lädt? 2. Welcher Unterschied besteht zwischen der Methode Delete von DataRow und der Methode Remove von DataRowCollection? 3. Wie sollten Sie die folgende SQL-Anweisung ausführen? DELETE FROM Artist Where ArtistID=3?
Übung Erstellen Sie eine Windows-Anwendung, die mit den Klassen, die Sie heute kennen gelernt haben, eine Benutzeroberfläche bereitstellt, mit der Sie Datensätze in der CDDatenbank betrachten, verändern, hinzufügen und löschen können.
422
Der Server-Explorer
Der Server-Explorer
Eines der Hauptziele der Integrierten Entwicklungsumgebung (Integrated Development Environment, IDE) besteht darin, Werkzeuge bereitzustellen, die den Entwicklern nicht zur Verfügung stehen, wenn sie die Befehlszeilen-Compiler mit einem Texteditor verwenden. Dabei kann es sich z.B. um verbesserte Bearbeitungs- und Debugging-Eigenschaften handeln, also um Werkzeuge, mit denen Programmierer schnelleren und besseren Code entwickeln können. Außerdem kann eine gute IDE Eigenschaften enthalten, die den Entwickler dadurch unterstützen, dass sie Code generieren, sodass der Entwickler diesen nicht selbst zu schreiben braucht. Dies trifft auf die Add-Ins und Wizards zu, die zur IDE von Visual Basic .NET gehören. Auch der Server-Explorer fällt in diese Kategorie. Der ServerExplorer ist ein Funktionsmerkmal von Visual Basic .NET, das eine einfache Steuerung der verschiedenen Dienste des Betriebssystems ermöglicht. Darüber hinaus können die Entwickler mithilfe des Server-Explorers schnell und problemlos Code für die Verwendung dieser Dienste schreiben.
Themen heute: 쐽
Was ist der Server-Explorer?
쐽
Dienste erkunden
쐽
Mit Diensten arbeiten
쐽
Programme schreiben, die Dienste verwenden
13.1 Was ist der Server-Explorer? Der Server-Explorer ist ein Werkzeug, das innerhalb der IDE von Visual Basic .NET zur Verfügung steht. Ebenso wie der Windows Explorer und der Internet Explorer ist er ein Werkzeug, mit dem Sie eine Gruppe von Eigenschaften durchsuchen können. Allerdings durchsuchen Sie mit dem Server-Explorer nicht Ordner auf einer Festplatte oder in einer Website, sondern Sie durchsuchen die verschiedenen Server in Ihrem Netzwerk und sehen die Dienste, die auf diesen Servern laufen. Bei einigen Diensten können Sie zudem den Dienst steuern oder Code schreiben, um diesen Dienst zu verwenden. Und dies alles machen Sie vom Server-Explorer aus. Abbildung 13.1 zeigt den Server-Explorer. Wenn er nicht sichtbar ist, dann wurde er wahrscheinlich geschlossen. Um ihn zu öffnen, wählen Sie im Menü ANSICHT den Eintrag SERVER-EXPLORER. Der Server-Explorer zeigt eine hierarchische Liste der Dienste, die auf dem gewählten Rechner zur Verfügung stehen. In der Standardeinstellung zeigt er nur die Dienste auf dem Entwicklungsrechner, aber bei Bedarf können Sie weitere hinzufügen. Es gibt zwei Dienstkategorien:
424
Was ist der Server-Explorer?
Abbildung 13.1: Der Server-Explorer 쐽
Datenverbindungen: Dieser Abschnitt hat Ähnlichkeit mit der Datenumgebung in Visual Basic 6. Er ermöglicht es Ihnen, Verbindungen zu Datenbanken herzustellen und Datenbanken zu betrachten, ohne dass Sie die Visual Basic .NET-Umgebung verlassen. Dies ist offensichtlich dann nützlich, wenn Sie viel Code für den Zugriff auf und die Aktualisierung von Datenbanken programmieren, was einer der Hauptverwendungszwecke von Visual Basic .NET ist.
쐽
Server: Dieser Abschnitt zeigt eine Liste der Server, mit denen Sie verbunden sind, sowie die Dienste, die auf diesen Servern laufen (oder zur Verfügung stehen). Zu den Informationen in diesem Abschnitt gehören auch die Möglichkeit, auf Ereignisprotokolle und die Leistungsüberwachung auf diesen Rechnern zuzugreifen, sowie einige andere Dienste wie z.B. Nachrichtenschlangen und SQL Server.
Wir werden den Server-Explorer heute für zwei seiner Hauptzwecke verwenden: Informationen betrachten und Programme schreiben.
Was ist ein Dienst? Ein Dienst ist ein Programm, das im Hintergrund läuft und als Ergänzung zum Betriebssystem eine bestimmte Leistung bereitstellt. Beispiele für solche Dienste sind Programme, die normalerweise als Server betrachtet werden: Datenbankserver wie z.B. Microsoft SQL Server, Webserver wie z.B. Microsoft IIS und Mailserver wie z.B. Microsoft Exchange. Alternativ kann ein Dienst auch einige zusätzliche Funktionen bieten, die andere Programme nutzen können. Hierzu
425
Der Server-Explorer
gehören z.B. die Leistungsüberwachung und das Protokollieren von Ereignissen. Windows 95, 98 und ME bieten weniger Dienste als Windows NT und 2000. Normalerweise haben Dienste ein Programm, mit dem Sie Informationen über den Dienst betrachten können. Mit dem Werkzeug Event Viewer können Sie die Ereignisprotokolle auf einem Rechner einsehen und mit dem SQL Server Enterprise Manager können Sie einen SQL Server betrachten und verwalten. Der Server-Explorer ergänzt diese Werkzeuge, indem er es Ihnen ermöglicht, mit den Diensten zu arbeiten, ohne die IDE zu verlassen.
13.2 Dienste erkunden Um den Server-Explorer zu betrachten, wählen Sie ihn aus dem Menü ANSICHT aus. In der Standardeinstellung erscheint er auf der linken Seite der IDE. Ebenso wie andere Ansichten kann er so eingerichtet werden, dass er dauerhaft geöffnet ist oder automatisch minimiert wird, sobald Sie ihn nicht mehr benötigen. Auf einem Rechner, der unter Windows läuft und auf dem Microsoft SQL Server installiert ist, sieht er wie in der Abbildung 13.2 aus. Auf anderen Rechnern, auf denen andere Dienste installiert sind, werden auch andere Dienste angezeigt.
Abbildung 13.2: Der Server-Explorer
426
Dienste erkunden
Datenverbindungen Im Abschnitt DATENVERBINDUNGEN des Server-Explorers können Sie von Visual Basic .NET aus Datenbanken betrachten und verwalten (siehe Abbildung 13.3). In diesem Abschnitt können Sie Verbindungen zu beliebigen Datenquellen herstellen.
Abbildung 13.3: Datenverbindungen mit dem ServerExplorer betrachten
Durch Wahl von VERBINDUNG HINZUFÜGEN aus dem Kontextmenü zu DATENVERBINöffnen Sie das Dialogfenster DATENLINKEIGENSCHAFTEN (siehe Abbildung 13.4). Mit diesem Dialogfenster können Sie eine neue Datenverbindung konfigurieren. Welche Optionen Ihnen bei diesem Dialogfenster zur Verfügung stehen, hängt davon ab, zu welchem Datentyp Sie eine Verbindung herstellen möchten. Die Einstellungen im entsprechenden Dialogfenster DATENLINKEIGENSCHAFTEN sind auf mehrere Registerkarten aufgeteilt. DUNGEN
Auf der Registerkarte PROVIDER wählen Sie den Provider aus, mit dem Sie eine Verbindung zur Datenbank herstellen möchten. Je nachdem, welche Provider Sie installiert haben, stehen hier eventuell sehr viele Provider zur Verfügung. Die Abbildung 13.4 zeigt die auf meinem Rechner verfügbaren Provider. Die folgenden Datenzugriffs-Provider werden häufig verwendet: 쐽
Microsoft Jet 4.0 OLE DB Provider: Damit können Sie auf Microsoft Access-Datenbanken (.mdb-Dateien) zugreifen.
쐽
Microsoft OLE DB Provider for ODBC Databases: Damit können Sie auf alle Datenbanken zugreifen, für die ein ODBC-Treiber installiert ist. Da viele ODBC-
427
Der Server-Explorer
Treiber zur Verfügung stehen, sollten Sie mit diesem Provider auf praktisch alle Datenbanken zugreifen können. Allerdings sollten Sie ihn nur im Notfall verwenden, da er mit OLE DB und ODBC auf Informationen zugreift. Dadurch liegen mehr Ebenen zwischen Ihrem Programm und der Datenbank, sodass dieser Provider im Allgemeinen der langsamste ist. 쐽
Microsoft OLE DB Provider for Oracle: Damit können Sie auf Oracle-Datenbanken zugreifen.
쐽
Microsoft OLE DB Provider for SQL Server: Damit können Sie auf Microsoft SQL Server-Datenbanken zugreifen.
Abbildung 13.4: Installierte Datenzugriffs-Provider
Je nachdem, welchen Provider Sie hier wählen, stehen Ihnen auf den anderen Registerkarten unterschiedliche Optionen zur Verfügung. Die Tabelle 13.1 stellt einige der Optionen auf der Registerkarte VERBINDUNG dar. Feld
Provider
Zweck
GEBEN SIE DEN DATEN-
Access
Dieses Feld benennt die Access-Datenbank (MDB-Datei), zu der eine Verbindung hergestellt werden soll.
Allgemein
Das Benutzerkonto für die Verbindung zur Datenbank.
BANKNAMEN EIN, ODER WÄHLEN
SIE EINEN AUS
BENUTZERNAME
Tabelle 13.1: Optionen der Registerkarte DATENLINKEIGENSCHAFTEN
428
Dienste erkunden
Feld
Provider
Zweck
KENNWORT
Allgemein
Das Passwort für die Verbindung zur Datenbank.
GEBEN SIE DEN SERVER-
SQL Server
Dieses Feld benennt den Server, zu dem eine Verbindung hergestellt werden soll. Mit (local) stellen Sie eine Verbindung zu Ihrem eigenen Rechner her, falls Sie SQL Server lokal ausführen.
WÄHLEN SIE DIE DATENBANK AUF DEM SERVER AUS
SQL Server
Da ein einzelner SQL Server mehrere Datenbanken enthalten kann, benennt dieses Feld die Datenbank auf dem Server, zu der eine Verbindung hergestellt werden soll.
VERBINDUNG TESTEN
Allgemein
Mit dieser Schaltfläche können Sie die Verbindungsparameter prüfen. Dadurch versucht die IDE, eine Verbindung zur Datenbank herzustellen. Eine erfolgreiche Verbindung gewährleistet, dass auf die Datenbank zugegriffen werden kann.
NAMEN EIN, ODER WÄHLEN
SIE EINEN AUS
Tabelle 13.1: Optionen der Registerkarte DATENLINKEIGENSCHAFTEN (Forts.)
Die Registerkarten WEITERE... und ALLE werden selten verwendet. Die Registerkarte WEITERE... enthält Einstellungen, die auf dem gewählten Provider basieren und die die meisten Anwender nicht vorzunehmen brauchen. Die Registerkarte ALLE ist einfach eine andere Möglichkeit, auf alle Eigenschaften zuzugreifen. Hier bearbeiten Sie nicht einzelne, providerspezifische Felder, sondern eine Liste aller verfügbaren Eigenschaften. Nachdem Sie alle Informationen eingegeben haben, die Sie für den Datenbankzugriff benötigen, klicken Sie die Schaltfläche VERBINDUNG TESTEN an, um die Einstellungen zu bestätigen. Wenn nun ein Dialogfenster angezeigt wird, das besagt, dass die Verbindung erfolgreich getestet wurde, können Sie OK anklicken, das Dialogfenster schließen und weiterarbeiten. Anderenfalls sollte Ihnen das Dialogfenster eine Möglichkeit vorschlagen, wie Sie den Fehler beheben können (normalerweise besteht die Fehlerursache darin, dass Sie eine Einstellung vergessen haben). Beheben Sie den Fehler und prüfen Sie die Verbindung noch einmal. Nachdem Sie eine Verbindung zur Datenbank hergestellt haben, sollte diese im Abschnitt DATENVERBINDUNGEN des Server-Explorers erscheinen. Je nach dem Typ der Datenbank hat der Eintrag mehrere Abschnitte. Die Abbildung 13.5 zeigt die verfügbaren Elemente für einige der möglichen Datenbanktypen. Die Tabelle 13.2 zeigt, welche Elementtypen Sie für die verschiedenen Datenbanken sehen sollten, zu denen häufig Verbindungen eingerichtet werden.
429
Der Server-Explorer
Abbildung 13.5: Datenverbindungen Objekt
Beschreibung
Tabellen
Enthält eine Liste aller Tabellen, die in der Datenbank gespeichert sind. Eine Tabelle fasst Informationen zu Gruppen zusammen. Sie kann z.B. Informationen über die Angestellten eines Unternehmens, über die Produkte, die dieses Unternehmen verkauft, oder über ähnliche Dinge enthalten. Im Allgemeinen ist dies der wichtigste Abschnitt der DATENVERBINDUNGEN.
Ansichten
Enthält eine Liste der Ansichten der Daten in der Datenbank. Eine Ansicht ist eine Möglichkeit, die Informationen zu betrachten. Sie kann z.B. alle Felder anzeigen, Informationen auf andere Art sortieren oder Informationen aus mehreren Tabellen zusammenstellen.
Gespeicherte Prozeduren
Enthält eine Liste der Programme, die in der Datenbank gespeichert sind. Dazu gehören Anfragen, die Informationen zurückgeben können, sowie Programme, die Informationen löschen, aktualisieren oder einfügen.
Funktionen
Funktionen gleichen den gespeicherten Prozeduren, gelten aber nur für einige Datenbanken. Normalerweise gibt man damit kleine Informationsmengen zurück. Im Moment gleichen sie den Funktionen, die wir in Visual Basic .NET verwenden.
Datenbankdiagramme
Diagramme gibt es normalerweise nur bei SQL Server. Man verwendet sie dazu, die Datenbank zu dokumentieren und eine visuelle Beschreibung der Tabellen in der Datenbank zu erstellen.
Tabelle 13.2: Datenbankobjekte
430
Mit Diensten arbeiten
Eine Verbindung zur Datenbank herstellen Wie wir bereits beschrieben haben, sind die Tabellen der wichtigste Abschnitt der DATENVERBINDUNGEN. Tabellen repräsentieren eine Gruppe von Informationen, die in der Datenbank gespeichert sind. Mit den Tabellen können Sie die Daten in der Datenbank einsehen, die Tabellen ändern usw. Wir wollen nun mithilfe der DATENVERBINDUNGEN eine Verbindung zur Datenbank herstellen und diese betrachten. Als Beispieldatenbank verwenden wir Northwind, die mit Visual Basic .NET geliefert wird. 1. Öffnen Sie das Dialogfenster DATENLINKEIGENSCHAFTEN. 2. Gehen Sie zur Registerkarte PROVIDER und wählen Sie den Eintrag MICROSOFT JET 4.0 OLE DB PROVIDER. Drücken Sie auf WEITER. 3. Klicken Sie auf die Schaltfläche ... neben dem Feld GEBEN SIE DEN DATENBANKNAMEN EIN, ODER WÄHLEN SIE EINEN AUS. Suchen Sie eine Access-Datenbank. Falls Sie keine finden sollten, können Sie die Datenbank nwind.mdb angeben. Klicken Sie OK an, um diese Datenbank zu akzeptieren. 4. Klicken Sie die Schaltfläche VERBINDUNG TESTEN an. Nun sollte sich ein Dialogfenster öffnen, das Ihnen mitteilt, dass die Verbindung erfolgreich hergestellt wurde. Ist dies nicht der Fall, dann prüfen Sie, ob sich die Datenbank an der angegebenen Stelle befindet. Drücken Sie OK, um diese Datenbankverbindung zu akzeptieren. 5. Zu dieser Verbindung gehört eine Reihe von Tabellen, Ansichten und gespeicherten Prozeduren. Öffnen Sie den Ordner Tabellen und klicken Sie doppelt auf den Ordner Customers. Nun öffnet sich ein neues Fenster, das die Liste der Angestellten der Northwind Company anzeigt. Außerdem sollten Sie auch andere Tabellen öffnen und die darin gespeicherten Daten betrachten können. Nehmen Sie jetzt keine Änderungen an der Datenbank vor. Wenn Sie diese Datenbank ändern, funktionieren andere Beispiele oder Anwendungen vielleicht nicht mehr. Sehen Sie sich die Informationen im Moment nur an. Wenn Sie versuchen möchten, an einer Datenbank etwas zu ändern, dann legen Sie zum Experimentieren eine eigene Datenbank an.
13.3 Mit Diensten arbeiten Mit dem Server-Explorer können Sie nicht nur Datenbanken einsehen, sondern er erleichtert auch den Zugriff auf Dienste sowie das Betrachten und Steuern von Diensten.
431
Der Server-Explorer
Was ist ein Dienst? Ein Dienst ist eine Anwendung, die im Hintergrund läuft und dem Betriebssystem bestimmte Fähigkeiten zur Verfügung stellt. Entwicklern, die bereits mit Windows NT oder Windows 2000 gearbeitet haben, wird dies bekannt vorkommen. Entwickler, die Windows 95, 98 oder ME benutzen, sind damit wahrscheinlich weniger vertraut, da es bei diesen Betriebssystemen normalerweise weniger Dienste gibt. Zu den Diensten, die unter Windows NT und 2000 laufen, gehören die Ereignisprotokollierung (ein zentrales Protokoll für das System, die Sicherheit und Anwendungen), der Systemmonitor (verfolgt mehrere wichtige Werte für das Betriebssystem und andere Anwendungen) und der Druckspooler (verwaltet Druckaufträge). Darüber hinaus können noch weitere Dienste installiert sein, z.B. IIS, SQL Server (Datenbank) und Message Queue Server (asynchrone oder losgelöste Kommunikation zwischen Anwendungen, ähnlich der E-MailKommunikation). In Windows 95, 98 und ME gehören die Dienste entweder zum Betriebssystem selbst oder sind Programme, die normalerweise in der Taskleiste erscheinen.
Dienste einsehen Ebenso wie die Datenverbindungen variieren auch die Dienste, die im Server-Explorer angezeigt werden. Dies hängt davon ab, was auf dem Rechner installiert ist und ausgeführt wird. Die Tabelle 13.3 beschreibt einige der Elemente, die im Server-Explorer angezeigt werden können. Diese Elemente stehen zum Zeitpunkt der Drucklegung dieses Buches bereits zur Verfügung; viele weitere werden später hinzukommen. Dienst
Beschreibung
Ereignisprotokolle
Ermöglicht den Zugriff auf die Anwendungs-, Sicherheits- und SystemProtokolldateien von Windows NT und 2000. Diese Protokolldateien enthalten Fehlermeldungen und andere Informationen aus den Anwendungen, die auf dem Rechner laufen. Dies bietet dieselbe Funktionalität wie die Anwendung Event Viewer. Man braucht dafür aber die Visual Basic .NET IDE nicht zu verlassen.
Geladene Module
Liefert eine Liste aller DLLs, die in den Speichern geladen sind, sowie der Programme, die sie verwenden. Je nachdem, was Sie gerade ausführen, kann diese Liste lang sein. Dieser Dienst ist nützlich, wenn Sie sehen möchten, ob eine bestimmte DLL in den Speicher geladen ist und ob sie von einer Anwendung verwendet wird.
Tabelle 13.3: Dienste im Server-Explorer
432
Mit Diensten arbeiten
Dienst
Beschreibung
Verwaltungsdienst
Ermöglicht den Zugriff auf die Windows Management Information (WMI) für den Server. WMI ist ein Mittel, um bei einem Rechner Informationen abzufragen. Diese Informationen reichen von einfachen Werten wie dem installierten Betriebssystem oder der CPU bis hin zu den Programmen, die ausgeführt werden. Sie umfassen sogar so präzise Dinge wie die Einstellungen der installierten Drucker. WMI ist ein mächtiges Werkzeug und wird am besten dafür verwendet, Informationen abzurufen, und nicht dafür, sie zu ändern.
Systemmonitore
Ermöglicht den Zugriff auf die Rechnerleistung. Während Sie Programme ausführen, registrieren Windows NT und 2000 unaufhörlich Informationen. Dazu gehören die Zeit, die die Ausführung einer Aufgabe benötigt, der verwendete Speicher und vieles mehr. Mit den Systemmonitoren können Sie die CPU-Auslastung, den Speicherplatz usw. messen.
Prozesse
Liefert eine Liste aller gerade laufenden Anwendungen. Sie sind vielleicht überrascht, wenn Sie sehen, wie viele Programme auf ihrem Rechner laufen.
Dienste
Zeigt eine Liste der Dienste, die auf dem Rechner laufen. Dies sind die Anwendungen, die im Hintergrund laufen. Eine angenehme Eigenschaft ist, dass Sie mit diesem Abschnitt die Dienste starten oder anhalten können (in Abhängigkeit von den Sicherheitseinstellungen).
SQL ServerDatenbanken
Bietet eine andere Möglichkeit (neben den DATENVERBINDUNGEN), auf SQL Server-Datenbanken zuzugreifen. Dadurch erhalten Sie ähnliche Informationen wie mit den Datenverbindungen (allerdings nur für SQL Server-Datenbanken).
Webdienste
Liefert eine Liste der Webdienste, die auf dem Rechner installiert sind. (Weitere Einzelheiten dazu finden Sie am Tag 21, Webdienste mit Visual Basic .NET erstellen.)
Message Queuing
Liefert eine Liste der verfügbaren Nachrichtenwarteschlangen. Diese verwendet man, wenn man nachrichtenbasierte Anwendungen erstellt (aber nicht bei E-Mail-Anwendungen).
Tabelle 13.3: Dienste im Server-Explorer (Forts.)
Eine Verbindung zu einem anderen Server herstellen Einige der Dienste, die in der Tabelle 13.3 genannt sind, können Sie erst sehen, wenn Sie eine Verbindung zu einem Server hergestellt haben. Hierfür wählen Sie das Element SERVER HINZUFÜGEN im Kontextmenü zu SERVER, geben im Dialogfenster einen gültigen Servernamen ein (siehe Abbildung 13.6) und drücken auf OK. Nun können Sie die Dienste sehen, die auf diesem und Ihrem eigenen Rechner geladen sind (in Abhängigkeit von den Sicherheitseinstellungen).
433
Der Server-Explorer
Sie können ein weiteres Benutzerkonto einrichten, das Sie verwenden, falls dieser Benutzer das Recht hat, auf den anderen Computer zuzugreifen. Mithilfe dieser Eigenschaft können Sie Verbindungen zu den Servern herstellen, mit denen Sie normalerweise arbeiten. Viele Entwickler benötigen z.B. während der Entwicklung eines Programms Zugriff auf einen Datenbankserver, einen Testserver und vielleicht auch einen Webserver. Mit dem Server-Explorer können Sie zu allen drei Servern Verbindungen herstellen, damit Sie die Dienste nach Bedarf überwachen und/oder verwenden können.
Abbildung 13.6: Einen weiteren Server hinzufügen
13.4 Programme schreiben, die Dienste verwenden Eine der interessantesten Eigenschaften des Server-Explorers besteht darin, dass Sie damit die Dienste nicht nur einsehen und grafisch mit ihnen arbeiten, sondern auch ganz einfach Anwendungen schreiben können, die mit den Diensten kommunizieren. So können Sie z.B. mit den Objekten, die durch den Server-Explorer bereitstehen, einer Anwendung den Zugriff auf Daten hinzufügen. Außerdem können Sie Ihrer Anwendung Objekte hinzufügen, die die Dienste steuern oder mit ihnen kommunizieren können. Damit können Sie z.B. die Systemmonitore lesen oder Informationen in den Ereignisprotokollen von Windows NT oder Windows 2000 lesen oder schreiben. Bisher war für derlei Dinge sehr viel Code erforderlich; nun genügen ein paar Zeilen, die die Eigenschaften verändern und die Methoden derjenigen Objekte ausführen, die diese Eigenschaften repräsentieren.
Mit dem Server-Explorer Datenzugriffscode schreiben Der Server-Explorer ist zwar nützlich, wenn Sie Dienste einsehen möchten, wird aber noch wesentlich nützlicher, wenn Sie auf diese Dienste zugreifen möchten. Mit dem Server-Explorer können Sie sehr viel Code schreiben lassen, was Sie davor bewahrt, ihn selbst schreiben zu müssen. Ein Bereich, in dem dies nützlich ist, ist das Schreiben von Anwendungen für den Datenzugriff. Normalerweise müssen Sie dafür (wie Sie am Tag 11, Einführung in Datenbanken, und Tag 12, Mit .NET auf Daten zugreifen, gesehen haben)
434
Programme schreiben, die Dienste verwenden
zweimal dieselbe Art von Code schreiben: einmal, um die Informationen abzurufen, und zum Anderen, um sie dem Anwender anzuzeigen. Warum sollten Sie Ihre Zeit darauf verwenden, wenn der Computer das für Sie erledigen kann? Erstellen wir also eine einfache Anwendung, um die Daten in einer Tabelle der Datenbank zu betrachten. Ich werde dafür die Datenbank Northwind verwenden, aber Sie können auch jede andere Datenbank benutzen, auf die Sie von Ihrem Rechner aus zugreifen können. Nachdem Sie im Server-Explorer eine funktionierende Verbindung zu einer Datenbank eingerichtet haben, erstellen Sie ein neues Windows-Anwendungsprojekt. Meines heißt DataView. Sie können diesen Namen (er ist nicht urheberrechtlich geschützt) oder einen beliebigen anderen verwenden. Nachdem Sie das Projekt erstellt haben, löschen Sie das ursprüngliche Formular, indem Sie es im Projektmappen-Explorer mit Rechtsklick auswählen und dann LÖSCHEN wählen. Akzeptieren Sie die Warnung. Nach einem Rechtsklick auf das Projekt wählen Sie HINZUFÜGEN und dann WINDOWS FORM HINZUFÜGEN. Nennen Sie das Formular Customers. Klicken Sie das Projekt noch einmal mit der rechten Maustaste an und wählen Sie EIGENSCHAFTEN. Setzen Sie das Startup-Objekt auf Customers und klicken Sie auf OK. Wie üblich müssen sie bei der Erstellung einer Visual Basic .NET-Anwendung zuerst dem Formular die gewünschten Steuerelemente hinzufügen. Wir verwenden in unserem Beispiel eine einfache Benutzeroberfläche, die nur aus einem Raster besteht. Wählen Sie in der Werkzeugsammlung das Steuerelement DataGrid und fügen Sie es dem Formular hinzu. Setzen Sie die Eigenschaften so, wie es die Tabelle 13.4 zeigt. Steuerelement
Eigenschaft
Wert
Form
Text
Customer listing
DataGrid
Name
dbgCustomers
Dock
Fill
CaptionText
Customers
Tabelle 13.4: Eigenschaften von DataGrid
Außerdem sollten Sie die Eigenschaft Automatisch formatieren hinzufügen (oder manuell Werte setzen), damit das Raster hübsch aussieht. Nun möchten wir den Code für den Datenzugriff hinzufügen. Dies machen wir jedoch nicht manuell, sondern wir verwenden den Server-Explorer. Wählen Sie in Ihrer Datenbank eine Tabelle (ich habe die Tabelle Customers (oder Kunden) in Northwind gewählt), ziehen Sie sie aus dem Server-Explorer und legen Sie sie auf dem Formular ab. Unter dem Formular erscheinen nun die neuen Objekte: OleDbConnection1 und OleDbDataAdapter1 (siehe Abbildung 13.7).
435
Der Server-Explorer
Abbildung 13.7: Hinzugefügte Verbindungsobjekte OleDbConnection1 ist erwartungsgemäß die Verbindung zur Datenbank. OleDbAdapter1 ist
die Komponente, die Informationen aus der Datenbank extrahiert (und speichert). Ändern Sie die Namen der Steuerelemente so, wie die Tabelle 13.5 es zeigt. Steuerelement
Eigenschaft
Wert
OleDbConnection
Name
conNW
OleDbDataAdapter
Name
cmdCustomers
Tabelle 13.5: Eigenschaften von Connection und Command
Nun folgt der amüsante Teil. Klicken Sie mit der rechten Maustaste auf den Datenadapter cmdCustomers und wählen Sie DATASET GENERIEREN. Setzen Sie den Namen auf CostumerSet und aktivieren Sie das Kontrollkästchen, mit dem Sie dem Designer eine Instanz hinzufügen (siehe Abbildung 13.8). Daraufhin wird dem Fenster ein neues CostumerSet1 hinzugefügt (siehe Abbildung 13.9). Setzen Sie den Namen auf dsCustomers. Bevor wir Code schreiben, sollten wir die Datenbank und DataSet mit dem DataGrid verbinden. Setzen Sie die DataSource des DataGrid auf dsCostumers und DataMember auf Costumers. Nun sollten im DataGrid die Spaltenköpfe erscheinen (siehe Abbildung 13.10). Nach dem amüsanten kommt der schwierige Teil: Wir müssen einigen Code schreiben. Finden Sie die Prozedur Form_Load und aktualisieren Sie sie so, dass DataSet gefüllt wird:
436
Programme schreiben, die Dienste verwenden
Abbildung 13.8: Das DataSet hinzufügen
Abbildung 13.9: Das DataSet wurde hinzugefügt.
Listing 13.1: Änderungen der Prozedur New 1 2 3 4
Private Sub Customers_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load cmdCustomers.Fill(dsCustomers) End Sub
437
Der Server-Explorer
Abbildung 13.10: DataSet und DataGrid verbinden
Die Prozedur Form_Load ist eine Standardprozedur in Windows-Anwendungen. Wir haben ihr die Zeile 3 hinzugefügt, die die Methode Fill des Datenadapters cmdCustomers aufruft. dsCustomers ist das DataSet, das wir durch die Auswahl von DATASET GENERIEREN erstellt haben. Anders ausgedrückt: Wir brauchen nur diese eine Zeile einzugeben; alles andere wird automatisch generiert. Erstellen Sie Ihr eigenes Programm und führen Sie es aus. Wenn alles funktioniert, sollte das Ergebnis wie in der Abbildung 13.11 aussehen.
Auf Leistungsmonitore und Ereignisprotokolle zugreifen Damit Sie den Nutzen der Werkzeuge im Server-Explorer erkennen, werden wir eine kleine Anwendung schreiben, die einige Leistungsbereiche des Betriebssystems überwacht. Außerdem werden wir im Anwendungsereignisprotokoll den Zeitpunkt (Datum und Uhrzeit) aufzeichnen, zu dem das Programm beginnt und endet. Das Endergebnis sollte ähnlich wie in Abbildung 13.12 aussehen. Erstellen Sie eine neue Windows-Anwendung. Nennen Sie das neue Projekt PerfLite, da es eine verkleinerte (in der Marketingsprache »Light« oder »Lite«) Version der in Windows NT und 2000 integrierten Anwendung Systemmonitor ist. Wenn das Projekt fertig ist, schließen Sie das Standardformular und geben ihm den neuen Namen frmMain.vb. Anschließend klicken Sie mit der rechten Maustaste auf die Datei und wählen CODE ANZEIGEN.
438
Programme schreiben, die Dienste verwenden
Abbildung 13.11: Die Anwendung DataView ausführen
Abbildung 13.12: PerfLite bei der Arbeit
Ändern Sie alle Referenzen von Form1 auf frmMain. Dabei kann Ihnen der Befehl ERSETZEN helfen (denken Sie daran, AUSGEBLENDETEN TEXT DURCHSUCHEN auszuwählen). Schließlich wählen Sie das Projekt im Projektmappen-Explorer mit Rechtsklick aus, wählen dann EIGENSCHAFTEN und setzen das Startobjekt im Formular auf frmMain (siehe Abbildung 13.13). Kompilieren Sie die Anwendung, um sicher zu sein, dass alle Änderungen vorgenommen wurden. Nachdem alles kompiliert wurde, sind wir bereit, Steuerelemente hinzuzufügen und deren Eigenschaften zu setzen. Setzen Sie die Eigenschaften des Formulars, wie die Tabelle 13.6 es zeigt.
439
Der Server-Explorer
Abbildung 13.13: Eigenschaften von PerfLite
Eigenschaft
Wert
Text
PerfLite
Size
480, 360
Tabelle 13.6: Eigenschaften des Formulars PerfLite
Wir werden einige Informationen zur Leistung anzeigen, indem wir sie auf eine Tafel schreiben. Außerdem müssen wir die Zeichengeschwindigkeit steuern können. Fügen Sie die in der Tabelle 13.7 genannten Steuerelemente hinzu und setzen Sie ihre Eigenschaften auf die angegebenen Werte. Steuerelement
Eigenschaft
Wert
Panel
Name
pnlSweep
Dock
Top
Height
240
Name
cmdSweep
Button
TrackBar
Text
&Start
Location
8, 248
Size
64, 24
Name
trkSpeed
Location
8, 280
Tabelle 13.7: Steuerelemente im Formular PerfLite
440
Programme schreiben, die Dienste verwenden
Steuerelement
Label
Label
Label
Timer
Eigenschaft
Wert
Size
104, 42
Minimum
1
Maximum
20
Value
10
Name
lblCPU
Location
240, 248
Autosize
True
Text
CPU:
Forecolor
Red (or something you like/can see)
Name
lblProc
Location
240, 272
Autosize
True
Text
Processes:
Forecolor
Green (again, or something appealing)
Name
lblMem
Location
240, 296
Autosize
True
Text
Memory:
Forecolor
Blue
Name
tmrClock
Interval
1000
Tabelle 13.7: Steuerelemente im Formular PerfLite (Forts.)
Um die Leistungsinformationen anzuzeigen, müssen wir der Anwendung noch die richtigen Leistungsmonitore hinzufügen: 1. Öffnen Sie den Server-Explorer und wählen Sie einen Server aus (eventuell Ihren eigenen Rechner). 2. Öffnen Sie den Abschnitt LEISTUNGSINDIKATOREN. 3. Suchen Sie das Element PROZESSOR und öffnen Sie es. 4. Öffnen Sie das Element PROZESSOR ZEIT (%) und wählen Sie das Element _TOTAL. 5. Ziehen Sie dieses Element über das Formular und legen Sie es dort ab. Nun sollte in einem neuen Abschnitt unterhalb des Formulars ein neues Element namens PerformanceCounter1 zu sehen sein (siehe Abbildung 13.14).
441
Der Server-Explorer
Abbildung 13.14: Der Leistungsmonitor wurde hinzugefügt.
Es gibt auch eine andere Möglichkeit, einer Anwendung ein Element aus dem ServerExplorer hinzuzufügen. Suchen Sie den Leistungsmonitor SYSTEM\PROCESSES. Beachten Sie, dass sich darunter keine Elemente befinden, wie es bei _TOTAL unter dem Element PROZESSOR ZEIT (%) der Fall war. Klicken Sie mit der rechten Maustaste auf den Prozessmonitor und wählen Sie den Befehl ZUM DESIGNER HINZUFÜGEN. Nun sollten Sie ein weiteres Leistungsmonitor-Objekt sehen. Abschließend wählen Sie den Leistungsmonitor SPEICHER\VERFÜGBARE MB und fügen ihn hinzu. Geben Sie den Leistungsmonitoren die neuen Namen prfCPU, prfProcs und prfMem. Zusätzlich zu den Leistungsmonitoren können wir noch Objekte hinzufügen, die den Zugriff auf das Ereignisprotokoll erheblich erleichtern. Öffnen Sie den Abschnitt EREIGNISPROTOKOLLE des Server-Explorers und wählen Sie ANWENDUNG. Entweder ziehen Sie nun das Element ANWENDUNG in das Formular oder Sie klicken mit der rechten Maustaste und wählen ZUM DESIGNER HINZUFÜGEN (was Ihnen lieber ist). Nun sollte neben den drei Leistungsmonitoren ein neues Element erscheinen. Setzen Sie die Eigenschaften dieses neuen Objekts so, wie es die Tabelle 13.8 zeigt. Das letzte Formular sollte wie in Abbildung 13.15 dargestellt aussehen. Eigenschaft
Wert
Name
logApp
Source
PerfLite
Tabelle 13.8: Eigenschaften des EventLog-Objekts
442
Programme schreiben, die Dienste verwenden
Abbildung 13.15: Nun können Sie PerfLite neuen Code hinzufügen.
Nachdem die Benutzeroberfläche fertig ist, müssen wir einigen Code hinzufügen, damit unser Programm auch etwas macht. Für diese Anwendung werden wir zu drei Steuerelementen Code und außerdem eine hilfreiche Prozedur hinzufügen. Am Ende sollte sich eine vertikale Linie über die Tafel bewegen, der drei farbige Linien folgen (siehe Abbildung 13.16).
Abbildung 13.16: Unser Beispiel in Aktion
Mit dem Ereignis Timer des Steuerelements Timer sorgen wir dafür, dass die vertikale Linie periodisch neu gezeichnet wird. Den entsprechenden Code finden Sie im Listing 13.2.
443
Der Server-Explorer
Listing 13.2: Der Code für das Steuerelement Timer 1 2 3 4 5 6 7 8 9 10 11 12
Private Sub tmrClock_Tick(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles tmrClock.Tick Dim sngCPU As Single = prfCPU.NextValue() / 100 Dim sngProcs As Single = prfProcs.NextValue() Dim sngMem As Single = prfMem.NextValue / 1024 'Zeichne die vertikale Tafel. DrawSweep(sngCPU, sngProcs, sngMem) 'Aktualisiere die Beschriftungen. lblCPU.Text = "CPU: " & sngCPU.ToString("p") lblProc.Text = "Processes: " & sngProcs lblMem.Text = "Memory: " & sngMem.ToString("f0") & "KB" End Sub
Der Code in dieser Prozedur dient vor allem zwei Zwecken: Die Werte in den Leistungsmonitoren werden in Single-Variablen umgewandelt und die Beschriftungen der Steuerelemente werden aktualisiert. Die Zeilen 3-5 rufen die Werte der einzelnen Leistungsmonitore ab. Beachten Sie, dass wir die Werte den Variablen dann zuweisen, wenn wir diese erstellen. Anschließend übergeben wir diese Werte an die Routine DrawSweep (Zeile 7, siehe auch Listing 13.3), damit sie angezeigt werden. Schließlich formatieren wir alle Werte und zeigen sie auf den einzelnen Beschriftungen an (Zeilen 9 bis 11). Die Aufrufe von ToString in den Zeilen 9 und 11 kommen Ihnen vielleicht merkwürdig vor. Mit der Methode ToString kann man bei der Umwandlung der Zahl in eine Zeichenkette ein optionales Format anwenden. Die Zeile 9 formatiert den Wert in Prozent, die Zeile 11 hingegen als Zahl mit einer festen Anzahl von Dezimalstellen, in diesem Fall mit null Dezimalstellen.
Listing 13.3: Die vertikale Linie zeichnen 13 14 15
444
Private Sub DrawSweep(ByVal CPU As Single, _ ByVal Processes As Single, _ ByVal Memory As Single)
Programme schreiben, die Dienste verwenden
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
Dim oGrafix As Graphics = pnlSweep.CreateGraphics() Dim sngHeight As Single = pnlSweep.Height 'für die Punkte Dim sngCPUY As Single Dim sngProcsY As Single Dim sngMemY As Single 'Lösche die vorherige vertikale Linie. oGrafix.DrawLine(penBack, m_sngX, 0, m_sngX, sngHeight) 'Zeichne die Datenpunkte. sngCPUY = sngHeight – (CPU * sngHeight) – 1 oGrafix.DrawLine(penCPU, _ m_sngX – increment, m_sngCPUY, m_sngX, sngCPUY) m_sngCPUY = sngCPUY sngProcsY = sngHeight – Processes oGrafix.DrawLine(penProcs, _ m_sngX – increment, m_sngProcsY, m_sngX, sngProcsY) m_sngProcsY = sngProcsY 'Die 10.000 steht für den Rechner, damit der Speicher einen 'schönen Wert bekommt. Vielleicht müssen Sie dies ändern, 'falls die Speicherlinie nicht richtig angezeigt wird. sngMemY = sngHeight – (Memory / 10000) oGrafix.DrawLine(penMem, _ m_sngX – INCREMENT, m_sngMemY, m_sngX, sngMemY) m_sngMemY = sngMemY 'Inkrementiere x. m_sngX += increment If m_sngX > pnlSweep.Width Then 'Setze an den Anfang zurück. m_sngX = 0 'Und lösche die Zeichenoberfläche. oGrafix.Clear(SystemColors.Control) End If 'Zeichne die Linie oGrafix.DrawLine(penFore, m_sngX, 0, m_sngX, sngHeight) End Sub
Wie erwartet ist diese Routine das Kernstück der Anwendung. Lassen Sie sich von der Länge und den Grafikaufrufen nicht abschrecken: Die Routine ist recht einfach. Zuerst rufen wir das Graphics-Objekt aus dem Steuerelement Panel auf (Zeile 16). In Visual Basic .NET werden alle Grafiken auf einem Graphics-Objekt erstellt. Alle Steuerelemente, auf denen man zeichnen kann (wie z.B. die Steuerelemente Image und Panel sowie Formulare), stellen mit der Methode CreateGraphics das Graphics-Objekt bereit. Das
445
Der Server-Explorer
Objekt wiederum stellt Objekte und Methoden bereit, mit denen man auf ihm zeichnen kann (und das Steuerelement Container). Bevor wir den Code besprechen, müssen wir wissen, was er bewirken soll. Das Endergebnis ist eine vertikale Linie, die sich horizontal über das Formular bewegt. Dabei folgen ihr drei farbige Linien, die die drei Leistungsmonitore repräsentieren. Wenn die vertikale Linie (»sweep«) zum Ende des Formulars gelangt, soll sie auf der linken Seite des Formulars neu beginnen und das Formular soll für die nächste Linie freigemacht werden. Das Ergebnis sollte ungefähr wie ein EKG-Monitor aussehen (allerdings ohne Piepsen). Wir benötigen also Code, der die vertikale Linie und die drei Leistungslinien zeichnet (und die vertikale Linie über das Formular bewegt). Um die vertikale Linie zu bewegen, müssen wir eine neue Linie in der Hintergrundfarbe zeichnen, die die alte Linie überdeckt, ein Stück weitergehen und eine neue sichtbare Linie zeichnen. Im obigen Code löschen wir in der Zeile 23 die alte Linie und in den Zeilen 25 bis 41 wird die neue Linie bewegt und gezeichnet. Dies dauert so lange, weil wir prüfen müssen, ob wir uns über den rechten Rand des Formulars hinausbewegen. Wenn dies der Fall ist (dies drückt die If-Anweisung aus), löschen wir das Formular, gehen an den linken Rand zurück und beginnen eine neue vertikale Linie. Die Linien der Leistungsmonitore werden ebenfalls in dieser Routine gezeichnet. Bei diesen Aufrufen müssen wir auch ermitteln, wo die Linie gezeichnet werden soll. Und wir müssen uns den alten Wert merken, damit wir die neue Linie mit der alten verbinden können. Wenn wir den ersten Leistungsmonitor als Beispiel nehmen (die Zeilen 25 bis 28), dann sind die folgenden drei Schritte zu erkennen: 1. Wir ermitteln den neuen y-Wert (die Höhe) für die neue Linie. Die Zeile 25 macht dies für den CPU-Monitor, indem sie ermittelt, welchen Prozentsatz der Gesamthöhe die CPU einnimmt (denken Sie daran, dass der CPU-Monitor in Prozent zählt). 2. Wir zeichnen die Linie und verbinden dabei die alte mit der neuen Linie. Die Zeilen 26 und 27 machen dies, indem sie von den alten x- und y-Werten zu den neuen eine Linie in der CPU-Farbe zeichnen. Den neuen x-Wert ermitteln wir, indem wir das Inkrement (15 Einheiten) zum alten Wert addieren. Den neuen y-Wert haben wir schon im ersten Schritt berechnet. 3. Wir speichern den neu berechneten y-Wert für den nächsten Schleifendurchlauf (Zeile 28). Mit diesem Wert zeichnen wir die nächste Linie. Die Routinen zum Zeichnen der beiden anderen Leistungsmonitorlinien (die Zeilen 29 bis 35 und 36 bis 39) sehen ähnlich aus; lediglich der neue y-Wert wird anders berechnet.
446
Programme schreiben, die Dienste verwenden
Listing 13.4: Die vertikale Linie aktivieren und deaktivieren 51 52 53 54 55 56 57 58 59 60 61
Private Sub cmdSweep_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles cmdSweep.Click 'Wechseln Sie den Schaltflächentext und den Zeitgeber (an oder aus) If cmdSweep.Text = "&Start" Then cmdSweep.Text = "&Stop" tmrClock.Enabled = True Else cmdSweep.Text = "&Start" tmrClock.Enabled = False End If End Sub
Diese Routine soll es dem Anwender ermöglichen, die vertikale Linie zu starten und anzuhalten. Sie ist also recht einfach. Basierend auf dem Text der Schaltfläche »kippen« wir die Schaltfläche und den Zeitgeber. Wenn der Zeitgeber aktiv ist, werden wir ihn anhalten, und wenn er nicht aktiv ist, aktivieren wir ihn. Wie könnten diesen Zustand auch mit einer Variable verfolgen, aber statt dessen verwenden wir die Beschriftung der Schaltfläche. Wenn die Eigenschaft Text der Schaltfläche den Wert &Start (den Anfangswert) hat, starten wir den Zeitgeber und setzen Text auf &Stop. (Achten Sie darauf, dass beide denselben Zugriffsschlüssel haben, nämlich den Buchstaben S.) Und wenn wir den Zeitgeber anhalten, setzen wir Text wieder auf &Start. Als Nächstes wollen wir es dem Anwender ermöglichen, die Geschwindigkeit der vertikalen Linie zu ändern. Hierfür verwenden wir den Code im Listing 13.5.
Listing 13.5: Die Geschwindigkeit einstellen 62 63 64 65 66
Private Sub trkSpeed_Scroll(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles trkSpeed.Scroll Dim iValue As Integer iValue = CInt(trkSpeed.Value) 'Setze das Zeitgeberintervall auf die gewählte Zeit.
447
Der Server-Explorer
67 68
tmrClock.Interval = iValue * 100 'ms End Sub
Der Code zum Einstellen der Geschwindigkeit ist recht einfach. Die Zeilen 64 und 65 stellen sicher, dass der verwendete Wert (die aktuelle Geschwindigkeit) eine ganze Zahl ist. Anschließend passen wir mithilfe der Geschwindigkeit das Intervall für den Zeitgeber (Zeile 67) an. Kleinere Intervalle bedeuten, dass die vertikale Linie sich schneller bewegt. Nun benötigen wir noch einige weitere Variablen. Diese speichern die aktuellen x- und yKoordinaten, die wir beim Zeichnen verwenden, und die »Stifte« (Pen), mit denen wir die Linien zeichnen. Fügen Sie dem Formular den Code im Listing 13.6 hinzu, und zwar direkt unterhalb des als Windows Form Designer generated code bezeichneten Abschnitts, aber vor allem anderen Code.
Listing 13.6: Variablen auf Formularebene 69 70 71 72 73 74 75 76 77 78 79 80 81 82
Dim m_sngX As Single Dim m_sngY As Single Dim m_sngCPUY As Single Dim m_sngProcsY As Single Dim m_sngMemY As Single Dim Dim Dim Dim Dim
penCPU As New System.Drawing.Pen(Color.Red) penProcs As New System.Drawing.Pen(Color.Green) penMem As New System.Drawing.Pen(Color.Blue) penFore As New System.Drawing.Pen(SystemColors.WindowText) penBack As New System.Drawing.Pen(SystemColors.Control)
Const INCREMENT As Single = 1
Mit den Variablen in den Zeilen 69 bis 74 speichern wir die aktuellen Stiftpositionen, wenn wir nicht gerade Linien zeichnen. Eine Variable speichert die aktuelle x-Position und die anderen die aktuellen y-Positionen für die einzelnen Leistungsmonitore. Mit den Stiften zeichnen wir die Leistungslinien und die vertikale Linie. Und mit der Konstanten in der Zeile 82 legen wir die Schritte fest, in denen sich die vertikale Linie über das Formular bewegt.
448
Zusammenfassung
Nachdem Sie den Code eingefügt haben, kompilieren Sie das Programm und führen es aus. Klicken Sie die auf Schaltfläche START und warten Sie ein paar Sekunden. Nun sollten Sie sehen, wie sich die vertikale Linie von links nach rechts über das Formular bewegt, wobei ihr drei Linien folgen: eine für die CPU (% Auslastung), eine für die Prozesse (laufende Programme) und eine für den Speicher (verwendetes Volumen). Der Text auf der Schaltfläche sollte nun STOP lauten. Ändern Sie den Geschwindigkeitsregler (setzen Sie ihn höher oder niedriger), um zu sehen, wie sich dies auf die vertikale Linie auswirkt. Klicken Sie auf die Schaltfläche STOP und beobachten Sie, wie sich der Text wieder zu START ändert. Versuchen Sie als Übung auch, der Anzeige weitere Monitore hinzuzufügen.
13.5 Zusammenfassung Die meisten modernen Betriebssysteme wie z.B. Windows enthalten mehrere Dienste. Diese Dienste erweitern das Betriebssystem um Elemente wie Datenbanken und Protokollierung. Mit dem Server-Explorer können Sie den Zustand dieser Dienste auf Ihrem eigenen Entwicklungscomputer oder auf einem anderen Rechner einsehen und ändern, ohne die Visual Basic .NET-IDE zu verlassen. Darüber hinaus ist es mit dem Server-Explorer bei vielen der verfügbaren Dienste einfach, Code für die Verwendung dieser Dienste zu schreiben. Morgen werden wir damit beginnen, die wahrhaft objektorientierte Natur von Visual Basic .NET zu erkunden. Dabei werden wir lernen, was es bedeutet, »wirklich« objektorientiert zu sein.
13.6 Fragen und Antworten F
Mein Server-Explorer enthält einige Dinge, die hier nicht erwähnt werden (oder ich habe einige nicht, die hier erwähnt werden). Was sind das für Dinge? A
F
Andere Unternehmen können Komponenten erstellen, die in den Server-Explorer eingefügt werden. Dadurch können Entwickler mit diesen Diensten ebenso direkt arbeiten wie mit den normalen Diensten. Und wenn der Server, zu dem Sie eine Verbindung herstellen, einen bestimmten Dienst nicht bereitstellt, dann erscheint dieser auch nicht in der Liste.
Wenn ich die Daten im Server-Explorer ändere, wirkt sich das dann auch auf die Datenbank aus? A
Ja, seien Sie also bitte vorsichtig.
449
Der Server-Explorer
13.7 Workshop Die Antworten und Lösungen finden Sie im Anhang A.
Quiz 1. Nennen Sie zwei Abschnitte im Server-Explorer, die Ihnen verraten, welche Programme jetzt gerade auf Ihrem Rechner laufen. 2. Was ist ein Dienst? 3. Was wird angezeigt, wenn der folgende Code einer Anwendung hinzugefügt und ausgeführt wird? Dim prfCPU As New PerformanceCounter("processor", __ "% Processor Time", "_total") Console.WriteLine(prfCPU.NextValue())
Übungen 1. Stellen Sie mithilfe der DATENVERBINDUNGEN eine Verbindung zu einer Datenbank her und erkunden Sie die Tabellen und andere Elemente, die Ihnen dann zur Verfügung stehen. (Denken Sie daran, dass Sie keine Änderungen vornehmen sollten, wenn dies nicht wirklich notwendig ist.) 2. Erkunden Sie den Server-Explorer, um herauszufinden, was auf Ihrem Rechner und den Servern, die Sie für die Entwicklung verwenden, zur Verfügung steht.
450
Einführung in die objektorientierte Programmierung
Einführung in die objektorientierte Programmierung
In der .NET-Edition wurde Visual Basic von Grund auf neu geschrieben. Einer der Hauptgründe hierfür war, dass man vollständig integrierte objektorientierte Merkmale hinzufügen wollte.
Themen heute 쐽
Überblick über die objektorientierte Programmierung (OOP)
쐽
Wichtige Konzepte der OOP
쐽
Wie bauen Sie die OOP in Ihre eigenen Systeme ein?
Zusätzlich zu diesen Themen werden Sie am Ende der heutigen Lektion Techniken kennen lernen, die Ihnen bei der Erstellung von Anwendungen mit diesen OOP-Verfahren helfen.
14.1 Überblick über die objektorientierte Programmierung (OOP) Die objektorientierte Programmierung (OOP) ist keine eigene Technologie oder Programmiersprache, sondern eine Art, Anwendungen zu entwerfen und zu erstellen. Noch abstrakter ausgedrückt ist sie eine bestimmte Art, sich das Entwerfen und Erstellen von Anwendungen vorzustellen. Die OOP hat eine akademische Geschichte und ich werde hier nicht versuchen, ein genaues Datum zu nennen, an dem sie erfunden wurde. Es mag genügen, dass sie in den 80er-Jahren bei der Softwareindustrie erstmals größeren Anklang fand. Heute geht man zwar fast schon automatisch davon aus, dass Sie die OOP verwenden, allerdings verwenden viele Personen diesen Begriff, ohne seine Bedeutung wirklich zu verstehen. Das Kernstück der OOP ist das Objekt, ein Programmierkonstrukt, das spezielle Informationen und eine Gruppe zusammengehöriger Verhaltensweisen kombiniert. In der OOP stellt man sich Szenarien nicht als das lineare Schritt-für-Schritt-Verfahren vor, das in einem Großteil der Computerprogramme verwendet wurde (und noch immer verwendet wird), sondern als eine Form von Objekten. Mit Objekten beschreibt man normalerweise Entitäten, die real (etwa ein Fahrzeug) oder abstrakt (etwa eine bei einer Fluggesellschaft gebuchte Flugreise) sein können. Diese Entitäten haben Attribute wie z.B. die Farbe des Fahrzeugs oder das Abflugdatum der Flugreise und diese Attribute beschreiben das Objekt. Außerdem gibt es bestimmte Aktionen, die auf den Entitäten durchgeführt werden können, z.B. »Verkaufen« bei dem Fahrzeug oder »Stornieren« bei der Flugreise. In der OOP werden diese Entitäten zu Objekten, die Attribute werden als Eigenschaften bezeichnet (die Sie setzen oder abrufen können) und die Aktionen heißen Methoden.
452
Überblick über die objektorientierte Programmierung (OOP)
Ein Objekt ist eine Darstellung einer realen oder abstrakten Entität sowie der Eigenschaften dieser Entität und der zugehörigen Aktionen, die durchgeführt werden können.
Lineare und objektorientierte Programmierung Der Unterschied zwischen der linearen oder prozeduralen Programmierung und der OOP erscheint vielleicht einfach, ist tatsächlich aber ein komplexes Thema, das häufig missverstanden wird. Ich werde Ihnen den Prozess, mit dem Sie eine Hypothekenrate berechnen, auf zweierleise Weise vorführen. Zuerst werde ich den Prozess in linearen Schritten (was man auch als prozedurale Programmierung bezeichnet) und dann vom objektorientierten Standpunkt aus beschreiben. Der lineare Prozess durchläuft die folgenden Schritte: 1. Hole die Darlehenssumme. 2. Hole den Zinssatz pro Jahr. 3. Hole die Laufzeit in Jahren. 4. Berechne den Zinssatz pro Monat. 5. Berechne die Anzahl der Raten (Jahre * 12). 6. Berechne den Ratenbetrag. Der objektorientierte Prozess durchläuft die folgenden Schritte: 1. Lege eine neue Hypothek an. 2. Setze die Hypothekeneigenschaften Darlehenssumme, Zinssatz und Laufzeit. 3. Rufe die Eigenschaft Rate ab. Auch im Code sehen diese beiden Verfahren unterschiedlich aus. Betrachten Sie die beiden Codelistings 14.1 und 14.2. Beide kann man unter einer Windows Form- oder einer Web Form-Anwendung ausführen, um die Hypothekenraten zu berechnen. Listing 14.1: Prozedurale Ansicht des Hypothekenrechners 1 2 3 4 5 6
'Linearer Stil Private Sub btnCalc_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnCalc.Click Dim iYears As Integer Dim iMonths As Integer Dim dblInterestRate As Double
453
Einführung in die objektorientierte Programmierung
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
Dim curPrincipal As Decimal Dim curPayment As Decimal iYears = CInt(txtPayments.Text) iMonths = iYears * 12 dblInterestRate = CType(txtInterest.Text, Double) curPrincipal = CType(txtPrincipal.Text, Decimal) 'Teile den Zinssatz durch 12, um die Monatrate zu 'erhalten. dblInterestRate /= 12 curPayment = curPrincipal * _ ((1 – (1 + dblInterestRate)) _ / (1 – ((1 + dblInterestRate) ^ iMonths)) _ + dblInterestRate) lblMonthlyPayment.Text = curPayment.ToString() End Sub
Listing 14.2: Objektorientierte Ansicht des Hypothekenrechners 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
'Objektstil Private Sub btnCalc_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnCalc.Click Dim iYears As Integer Dim dblInterestRate As Double Dim curPrincipal As Decimal Dim curPayment As Decimal iYears = CInt(txtPayments.Text) dblInterestRate = CType(txtInterest.Text, Double) curPrincipal = CType(txtPrincipal.Text, Decimal) Dim objMort As New Mortgage() objMort.AnnualInterestRate = dblInterestRate objMort.NumberOfYears = iYears objMort.Principal = curPrincipal lblMonthlyPayment.Text = objMort.PaymentAmount.ToString End Sub
Das Listing 14.2 erzeugt für seine Berechnungen zwar eine Instanz des Mortgage-Objekts, aber die zugrunde liegenden Berechnungen sind dieselben. Beachten Sie, dass Sie den Code im Listing 14.2 nicht ausführen können, da das Mortgage-Objekt noch nicht definiert wurde. Sie können zwar ein ähnliches Ergebnis erzielen, indem Sie mit prozeduralem
454
Überblick über die objektorientierte Programmierung (OOP)
Code eine Funktion wie z.B. CalculateMortgagePayment() schreiben, aber das wäre keine OOP. Nur wenn Sie das Mortgage-Objekt verwenden, das die Informationen über die Hypothek und den Code für die Verarbeitung dieser Informationen miteinander kombiniert, arbeiten Sie wirklich mit der OOP. Dieser Programmierstil ist Ihnen nicht neu, falls Sie die erste Hälfte des Buches durchgearbeitet haben, da in Visual Studio .NET fast alles auf diese Art erstellt wird. Betrachten Sie den Code, den wir für die Arbeit mit Steuerelementen in einem Windows-Formular verwenden: txtPayment.Text = lblResult.Text. Analog zum Mortgage-Objekt im obigen Beispiel sind auch diese beiden Steuerelemente Objekte, in diesem Fall mit der Eigenschaft Text. Ihr Code wird noch nicht objektorientiert, wenn Sie einfach nur die von Visual Basic .NET und vom .NET-Framework bereitgestellten Objekte verwenden, obwohl man Sie dies häufig glauben machen möchte. Sie können Objekte verwenden und dennoch im prozeduralen Schritt-für-Schritt-Stil programmieren.
Codeorganisation mit Objekten In der prozeduralen Programmierung ohne OOP-Verfahren ist es üblich, die Funktionsmerkmale in Subroutinen oder Funktionen zu zerlegen, die man dann von irgendeinem anderen Teil des Programms aus aufrufen kann. Außerdem fasst man diese Subroutinen (normalerweise mit anderen, gleichartigen Subroutinen) häufig zu Modulen, DLLs oder anderen Codestrukturen zusammen. Dann fügen Sie diese Gruppen von Subroutinen als einzelne Einheiten in Ihre Programme ein und rufen sie bei Bedarf ab. So können Sie z.B. alle mathematischen Funktionen in math.dll und alle Zeichenkettenfunktionen in string.dll zusammenfassen und damit ein organisiertes System anlegen. Diese Art der Codeerstellung ist noch immer eher prozedural als objektorientiert, was zwar ihren Wert für die Organisation von Code nicht schmälert, aber doch nach einer OOP mit Aufrufen wie z.B. Math.SquareRoot(x) aussieht. Wenn Sie mit .NET programmieren, müssen Sie die Prozeduren auf diese Art mit Objekten zu Gruppen zusammenfassen. Es ist zwar sinnvoll, 20 verschiedene Prozeduren als Methoden eines Objekts zusammenzufassen, aber Sie sollten dabei nicht vergessen, dass diese Art des Zusammenfassens nicht OOP ist, sondern einfach die Art, wie Code in .NET organisiert wird. Ihr Code ist nur dann wirklich objektorientiert, wenn Sie die Entitäten und Konzepte, die gemeinsam Ihre Anwendung bilden, mit Objekten darstellen. In .NET kennzeichnen Objekte, die weder eine abstrakte noch eine reale Entität repräsentieren, sondern einfach nur Code zu Gruppen zusammenfassen, sich selbst und alle ihre Methoden werden meist statisch oder gemeinsam genutzt. Über diese Objekttypen werden Sie in der morgigen Lektion mehr erfahren (Tag 15, Objekte in Visual Basic .NET erzeugen). Denken Sie aber daran, dass sie einfach eine bestimmte Art sind, diese prozeduralen Codebibliotheken zu erstellen.
455
Einführung in die objektorientierte Programmierung
14.2 Wichtige Konzepte der OOP Für die OOP wurden bestimmte Begriffe definiert und diese Begriffe werden in allen Programmiersprachen und Verfahren relativ einheitlich verwendet. In diesem Abschnitt werde ich die gebräuchlichsten Termini erklären und jeweils ein Beispiel für ihre Funktionsweise anführen.
Klassen, Objekte und Instanzen Die ersten Begriffe, die wir besprechen müssen, sind diejenigen, die Sie in allen Materialen zur OOP finden: Klasse, Objekt und Instanz. Diese Konzepte sind die Grundlage Ihrer Arbeit mit der OOP und Sie müssen Sie verstanden haben, bevor Sie weitermachen können. Eine Klasse ist eine Schablone für ein Objekt. Sie beschreibt die Grundstruktur des Objekts. Die Beziehung zwischen der Klasse, dem Objekt und der Instanz wird mit vielen verschiedenen Vergleichen umschrieben. Häufig werden diese Konzepte mit Häusern und dem Hausbau verglichen. Dabei ist die Klasse der Bauplan des Hauses und das Haus selbst ist ein Objekt. Anhand eines einzigen Bauplans können viele Häuser errichtet werden und anhand einer einzigen Klasse können viele Objekte erzeugt werden. Jedes Objekt, das aus einer Klasse erzeugt wird, bezeichnet man als Instanz dieser Klasse. Das Listing 14.3 veranschaulicht diese Begriffe im Bereich von Visual Basic. Listing 14.3: Mehrere Klassen in eine einzige Datei einfügen 1 2 3 4 5 6 7 8 9 10
Module AllAboutObjects Sub Main() Dim x As myFirstClass x = New myFirstClass() End Sub End Module Public Class myFirstClass ' End Class
Die Anweisung Public Class myFirstClass (Zeile 8) definiert eine neue Klasse, einen Bauplan für Objekte. Die Zeile 3 erstellt eine Variable des Typs MyFirstClass, also eine Variable, die eine Instanz von myFirstClass speichern kann. Die Zeile 4 erzeugt eine neue Instanz von myFirstClass (sie erzeugt ein Objekt) und weist es der Variablen x zu. Nun referenziert x auf eine Instanz der Klasse myFirstClass. Das Listing 14.4 zeigt ein weiteres Beispiel für die Verwendung von Objekten.
456
Wichtige Konzepte der OOP
Listing 14.4: Variablen und Instanzen sind nicht dasselbe. 1 Module AllAboutObjects 2 Sub Main() 3 Dim x As myFirstClass 4 Dim y As myFirstClass 5 x = New myFirstClass() 6 y = New myFirstClass() 7 End Sub 8 End Module
Im Listing 14.4 werden zwei Variablen des Typs myFirstClass deklariert (in den Zeilen 3 und 4). Anschließend werden zwei neue Instanzen von myFirstClass erzeugt (in den Zeilen 5 und 6). Jede Instanz wird durch eine Variable referenziert. x speichert eine Referenz auf ein anderes Objekt als y, aber beide Objekte sind Instanzen von myFirstClass. Mit dem Unterschied zwischen Instanzen und Variablen werden wir uns später anhand eines Beispiels aus Listing 14.5 ausführlicher befassen. Listing 14.5: Vergleich von Instanzen und Variablen 1 Module AllAboutObjects 2 Sub Main() 3 Dim x As myFirstClass 4 Dim y As myFirstClass 5 x = New myFirstClass() 6 y = x 7 End Sub 8 End Module
Das Listing 14.5 trägt noch zur Verwirrung bei. Hier wurden zwei Variablen deklariert, die beide auf eine Instanz der Klasse myFirstClass referenzieren können. Anschließend wird (mit dem Schlüsselwort New) eine neue Instanz dieser Klasse erzeugt und eine Referenz auf das neue Objekt in x gespeichert. Dann wird y der Variablen x zugewiesen, was bewirkt, dass y jetzt eine Referenz auf dieselbe Instanz von myFirstClass speichert wie x. Es gibt nur ein einziges Objekt und damit nur einen einzigen Speicherbereich, aber zwei Variablen, die darauf referenzieren (oder verweisen). Dies werden wir im folgenden Abschnitt veranschaulichen, wenn wir uns mit Eigenschaften befassen.
457
Einführung in die objektorientierte Programmierung
Eigenschaften Klassen können Eigenschaften definieren, die jede ihrer Instanzen haben sollte. Eine Eigenschaft ist ein Wert, der Teil eines Objekts ist und den Sie über das Objekt abrufen oder setzen können. Das Listing 14.6 fügt der Definition von myFirstClass eine Eigenschaft hinzu, indem es eine öffentliche Variable einfügt. An Tag 15 werden Sie weitere Möglichkeiten kennen lernen, eine Eigenschaft hinzuzufügen. Listing 14.6: Der Definition von myFirstClass eine Eigenschaft hinzufügen 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Module AllAboutObjects Sub Main() Dim x As myFirstClass Dim y As myFirstClass x = New myFirstClass() y = New myFirstClass() x.Name = "Fred" y.Name = "Joe" Console.WriteLine("x.Name = { 0} ", x.Name) Console.WriteLine("y.Name = { 0} ", y.Name) Console.ReadLine() End Sub End Module Public Class myFirstClass Public Name As String End Class
Wie zuvor deklarieren wir die beiden Variablen x und y, erzeugen zwei neue Instanzen von myFirstClass und fügen in die beiden Variablen Referenzen auf diese Instanzen ein. Anschließend speichern wir in den Zeilen 7 und 8 einen Wert in der Eigenschaft Name aller Instanzen von myFirstClass, wobei wir über die Variablen, die auf das Objekt referenzieren, auf diese Eigenschaft zugreifen. Die Console.WriteLine-Anweisungen in den Zeilen 9 und 10 liefern die folgende Ausgabe: x.Name = Fred y.Name = Joe
Da jeder Instanz einer Klasse ein eigener Speicherbereich zugeordnet ist, werden Eigenschaftswerte unabhängig von den Instanzen gespeichert. Wenn wir noch einmal den Vergleich mit dem Haus heranziehen, können wir sagen, dass zwar alle Instanzen eines Hauses aus demselben Bauplan erzeugt werden, jede Instanz aber in einer anderen Farbe gestrichen sein kann. Die Farbe des Hauses ist eine Eigenschaft und ihr Wert wird nicht vom Bauplan festgelegt, sondern ist ein Attribut der jeweiligen Instanz. Das Listing 14.7 veranschaulicht, wie Eigenschaften mit einer einzelnen Instanz einer Klasse verbunden sind.
458
Wichtige Konzepte der OOP
Listing 14.7: x und y verweisen auf dieselbe Objektinstanz 1 Module AllAboutObjects 2 Sub Main() 3 Dim x As myFirstClass 4 Dim y As myFirstClass 5 x = New myFirstClass() 6 y = x 7 x.Name = "Fred" 8 y.Name = "Joe" 9 Console.WriteLine("x.Name = { 0} ", x.Name) 10 Console.WriteLine("y.Name = { 0} ", y.Name) 11 Console.ReadLine() 12 End Sub 13 End Module
Der einzige Unterschied zwischen den Listings 14.7 und 14.6 besteht in der Zeile 6, in der y keine Referenz auf eine neue Instanz von myFirstClass, sondern eine Referenz auf x zugewiesen wird. Dieser Unterschied bedeutet, dass der Code in den Zeilen 7 und 8 mit der Eigenschaft Name derselben Instanz arbeitet und die Ausgabe dieser Routine folgendermaßen aussieht: x.Name = Joe y.Name = Joe
Beide WriteLine-Anweisungen liefern denselben Eigenschaftswert, da sowohl x als auch y auf dieselbe Objektinstanz verweisen. Auch dieses Konzept, dass nämlich mehrere Variablen auf dasselbe Objekt referenzieren, ist verwirrend. Weitere Einzelheiten dazu finden Sie im Abschnitt 6.1 der Visual Basic-Spezifikation. (Diese gehört zur .NET-Dokumentation; suchen Sie unter den Stichwörtern »Value Type« und »Reference Types«.) In .NET können Sie steuern, wie auf die Werte von Eigenschaften zugegriffen werden kann: nur als Lesezugriff, als Schreibzugriff oder als Lese- und Schreibzugriff. An Tag 15 werden Sie erfahren, wie Sie den Zugriff auf Eigenschaften Ihrer eigenen Klassen steuern.
Methoden Klassen können nicht nur Eigenschaften haben, sondern auch mit Verhaltensweisen oder Aktionen verbunden sein. Diese Aktionen werden als Methoden bezeichnet und ermöglichen es einer Klasse, zusätzlich zu den in den Eigenschaften gespeicherten Informationen noch eine bestimmte Logik zu speichern. Ein Haus ist zwar nicht das beste Beispiel, um Methoden zu veranschaulichen, aber wir wollen dennoch dabei bleiben. Die Klasse Haus, die für eine Immobilienanwendung erstellt wurde, kann Methoden haben, um das Haus im Internet einzutragen (myHouse.List()), um es zu verkaufen (myHouse.Sell()) oder um eine Broschüre über das Haus zu drucken (myHouse.PrintBrochure("Printer1")). Diese
459
Einführung in die objektorientierte Programmierung
Methoden unterscheiden sich nicht von anderen Prozeduren: Sie können Parameter entgegennehmen, wenn Sie sie aufrufen, und auch Ergebniswerte zurückgeben. Im Listing 14.8 fügen wir myFirstClass die Methode DoSomething hinzu, indem wir die Public-Prozedur Sub in die Klassendefinition einfügen. Listing 14.8: myFirstClass eine Methode hinzufügen 1 2 3 4 5 6 7 8 9 10 11 12 13 14
Module AllAboutObjects Sub Main() Dim x As myFirstClass x = New myFirstClass() x.DoSomething() End Sub End Module Public Class myFirstClass Public Name As String Public Sub DoSomething() 'Hier würde der Code folgen. End Sub End Class
Diese Methode (Zeile 5) können Sie dann aus jeder Instanz der Klasse aufrufen. Ebenso wie Eigenschaften werden Methoden jedoch zwar in der Klasse definiert, aber in der Instanz aufgerufen. Dies bedeutet, dass die Methode, wenn sie irgendwelche (privaten oder öffentlichen) Eigenschaften der Klasse verwendet, die Informationen nutzt, die in der betreffenden Instanz gespeichert sind.
Vererbung Die Vererbung ist ein Schlüsselkonzept der OOP, allerdings eines, das vor .NET in Visual Basic nicht leicht zu implementieren war. Die Vererbung bedeutet, dass Sie eine Klasse als Grundlage einer anderen Klasse verwenden können. Dies dient normalerweise dazu, die Eigenschaften, die in der ursprünglichen Klasse zur Verfügung stehen, zu ändern oder neue Eigenschaften hinzuzufügen. Da ich gerade vom Mittagessen komme, werde ich statt der Häuser Hamburger als Vergleich verwenden. Stellen Sie sich einen einfachen Burger vor: ein Hamburgerbrötchen mit einem Hacksteak.
460
Wichtige Konzepte der OOP
Wenn ein Restaurant nun Cheeseburger anbieten möchte, beginnt es nicht ganz von vorne. Vielmehr gibt es dem Koch die Anweisung: »Mache einen Hamburger, aber füge eine Scheibe Käse hinzu.« Der Koch hat eine neue Burgerklasse erstellt, Cheeseburger, die aus der Klasse Hamburger erbt. Wir können diesen Vergleich so lange fortführen, bis sogar ich der Burger überdrüssig werde: Das Restaurant kann einen Luxuxburger anbieten, der aus der Klasse Cheeseburger erbt und Salat und Tomaten hinzufügt. In allen diesen Fällen dient die Basisklasse Hamburger als Grundlage. Auf diese Art können Sie eine Objekthierarchie erstellen, in der die Klasse, die Sie gerade verwenden, am Ende mehrerer Vererbungsebenen stehen kann. Auf der Grundlage einer einzigen Klasse können Sie beliebig viele andere Klassen erstellen. Das Restaurant kann also vegetarische Burger oder Hähnchenburger anbieten, die beide von der Klasse Hamburger abgeleitet sind und den Typ Hacksteak überschreiben. Der Begriff Überschreiben bedeutet im Gegensatz zum einfachen Hinzufügen neuer Merkmale, dass eine Kindklasse eine eigene Implementierung eines Merkmals der Basisklasse bereitstellt. Am Tag 15 werden Sie erfahren, wie Sie in Visual Basic .NET mithilfe des Überschreibens, der Vererbung und anderer Möglichkeiten eigene Objekte erzeugen. Hier zeige ich im Listing 14.9 nur ein einfaches Beispiel für die Vererbung. Listing 14.9: Mit der Vererbung Objekthierarchien erstellen 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
Module AllAboutObjects Public Sub Main() Dim mySnippet As CodeSnippet mySnippet = New CodeSnippet() With mySnippet .Contents = "txtEntry.Text = lblInfo.Text" .Author = "Joe" .Language = "VB.NET" .Purpose = "Get Label Caption Into Text Box" End With End Sub End Module Public Class Snippet Public Contents As String Public Author As String End Class Public Class CodeSnippet
461
Einführung in die objektorientierte Programmierung
22 23 24 25 End
Inherits Snippet Public Language As String Public Purpose As String Class
Wie Sie sehen, fügt CodeSnippet, das aus Snippet abgeleitet ist, zwei neue Eigenschaften hinzu. Wenn Sie eine neue Instanz der Klasse CodeSnippet (Zeile 5) erzeugen, dann stehen dieser die Eigenschaften beider Klassen zur Verfügung. Häufig muss eine neue Klasse einige Funktionsmerkmale der Basisklasse überschreiben, da die Grundfunktionalität die anderen Änderungen, die Sie an der Klasse vorgenommen haben, nicht berücksichtigt. Sehen wir uns z.B. eine Methode namens GetSnippet() an, die zur Klasse Snippet gehört und so entworfen wurde, dass sie Snippet vollständig als String zurückgibt. Das Listing 14.10 veranschaulicht dies. Listing 14.10: Berücksichtigen Sie alle Merkmale Ihrer neuen Klasse. 1 Public Class Snippet 2 Public Contents As String 3 Public Author As String 4 Public Function GetSnippet() As String 5 Dim sTmp As String 6 sTmp = "Author: " & Author _ 7 & System.Environment.NewLine _ 8 & Contents 9 Return sTmp 10 End Function 11 End Class
Diese Funktion gibt nur die beiden Eigenschaften der Basisklasse aus. Wenn Sie sie in der neuen Klasse also nicht überschreiben, dann werden Ihre beiden neuen Eigenschaften auch nicht behandelt. Die Klasse CodeSnippet im Listing 14.11 löst dieses Problem, indem Sie die Funktion GetSnippet der Basisklasse überschreibt. Listing 14.11: Eine Funktion überschreiben, damit sie neue Eigenschaften Ihrer Klasse behandelt. 1 Module AllAboutObjects 2 Public Sub Main() 3 4 Dim mySnippet As CodeSnippet 5 mySnippet = New CodeSnippet() 6 7 With mySnippet 8 .Contents = "txtEntry.Text = lblInfo.Text" 9 .Author = "Joe" 10 .Language = "VB.NET" 11 .Purpose = "Get Label Caption Into Text Box"
462
Wichtige Konzepte der OOP
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
End With End Sub End Module Public Class Snippet Public Contents As String Public Author As String Public Overridable Function GetSnippet() As String Dim sTmp As String sTmp = "Author: " & Author _ & System.Environment.NewLine _ & Contents Return sTmp End Function End Class Public Class CodeSnippet Inherits Snippet Public Language As String Public Purpose As String Public Overrides Function GetSnippet() As String Dim sTmp As String sTmp = MyBase.GetSnippet() & _ System.Environment.NewLine & _ "Language: " & Language & _ System.Environment.NewLine & _ "Purpose: " & Purpose Return sTmp End Function End Class
Damit diese Funktion überschrieben werden kann, muss sie in der Basisklasse als Overridable gekennzeichnet sein (Zeile 19) und Sie müssen in der abgeleiteten Klasse (durch Overrides gekennzeichnet; Zeile 33) eine neue Implementierung bereitstellen. Um das Ergebnis aus der Funktion der Basisklasse als Teil der Implementierung der neuen Klasse zu erhalten, können Sie mit dem speziellen Schlüsselwort MyBase (Zeile 35) auf die Eigenschaften und Methoden der Basisklasse zugreifen. Ein weiteres besonderes Schlüsselwort ist Me, das wir in diesem Beispiel jedoch nicht verwendet haben. Da Me auf das aktuelle Objekt verweist, würde Me.Name in der Prozedur GetSnippet auf die Eigenschaft Name der aktuellen Instanz dieser Klasse referenzieren. Eine nützliche Eigenschaft der Vererbung besteht darin, dass Klassen, die aus einer speziellen Basisklasse abgeleitet sind, so verwendet werden können, als seien sie diese Basis-
463
Einführung in die objektorientierte Programmierung
klasse. Instanzen der neuen Klassen können in Variablen gesetzt werden, die den Datentyp der alten Klasse verwenden Zudem können sie als Parameter an Prozeduren übergeben werden, die den alten Datentyp erwarten. Das Listing 14.12 veranschaulicht dies. Listing 14.12: Eine geerbte Klasse können Sie überall verwenden, wo ihre Basisklasse unterstützt wird. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
Option Strict On Option Explicit On Module AllAboutObjects Public Sub Main() Dim mySnippet As CodeSnippet mySnippet = New CodeSnippet() With mySnippet .Contents = "txtEntry.Text = lblInfo.Text" .Author = "Joe" .Language = "VB.NET" .Purpose = "Get Label Caption Into Text Box" End With PrintSnippet(mySnippet) Console.ReadLine() End Sub Public Sub PrintSnippet(ByVal objSnippet As Snippet) Console.WriteLine(objSnippet.GetSnippet()) End Sub End Module
Wenn ein Objekt als sein Basistyp übergeben wird, wie es in der Zeile 15 im Listing 14.12 der Fall ist, dann stehen die Eigenschaften und Methoden, die die geerbte Klasse hinzugefügt hat, nicht zur Verfügung. So könnte objSnippet.Language nicht kompiliert werden, wenn Sie es in die Zeile 20 setzten, da es keine zulässige Eigenschaft der Klasse Snippet ist. Überschriebene Methoden stehen hingegen zur Verfügung, da sie in der Definition der Basisklasse vorhanden sind. Allerdings wird statt der Basisimplementierung die Implementierung in der abgeleiteten Klasse aufgerufen. Das Listing 14.12 würde somit die folgende Ausgabe liefern: Author: Joe txtEntry.Text = lblInfo.Text Language: VB.NET Purpose: Get Label Caption Into Text Box
464
Wichtige Konzepte der OOP
In der morgigen Lektion (Tag 15) werden Sie mehr über die Vererbung und die Möglichkeit erfahren, ein abgeleitetes Objekt so zu behandeln, als sei es seine eigene Basisklasse.
Konstruktoren Eine weitere objektorientierte Eigenschaft, die in dieser Version von Visual Basic neu ist, sind die Konstruktoren für Objekte. Ein Konstruktor ist eine Routine, die aufgerufen wird, wenn eine Instanz einer Klasse erzeugt wird. Die Klasse kann dabei angeben, welche Parameter zum Zeitpunkt der Erzeugung bereitgestellt werden können. Dieses Konzept bietet die Möglichkeit, ein Objekt zum Zeitpunkt der Erzeugung zu initialisieren. In Visual Basic .NET wird der Konstruktor durch die Prozedur New in der Klassendefinition dargestellt und kann (wie jede andere Prozedur auch) beliebig viele überladene Versionen dieser Prozedur haben. Er bietet also vielerlei Möglichkeiten, das Objekt zu erzeugen. Im Listing 14.13 fügen wir den Klassen Snippet und CodeSnippet verschiedene Konstruktoren hinzu, wodurch Sie diese Objekte auf verschiedene Art initialisieren können. Listing 14.13: Konstruktoren erzeugen und initialisieren Ihr Objekt. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
Option Strict On Option Explicit On Module AllAboutObjects Public Sub Main() Dim mySnippet As CodeSnippet mySnippet = _ New CodeSnippet("txtEntry.Text = lblInfo.Text", _ "Joe", "VB.NET", "Get Label Caption Into Text Box") PrintSnippet(mySnippet) Console.ReadLine() End Sub Public Sub PrintSnippet(ByVal objSnippet As Snippet) Console.WriteLine(objSnippet.GetSnippet()) End Sub End Module Public Class Snippet Public Contents As String Public Author As String Public Sub New()
465
Einführung in die objektorientierte Programmierung
24 End Sub 25 26 Public Sub New(ByVal Contents As String) 27 Me.Contents = Contents 28 End Sub 29 30 Public Sub New(ByVal Contents As String, 31 ByVal Author As String) 32 Me.Contents = Contents 33 Me.Author = Author 34 End Sub 35 36 Public Overridable Function GetSnippet() 37 Dim sTmp As String 38 sTmp = "Author: " & Author _ 39 & System.Environment.NewLine _ 40 & Contents 41 Return sTmp 42 End Function 43 End Class 44 45 Public Class CodeSnippet 46 Inherits Snippet 47 Public Language As String 48 Public Purpose As String 49 50 Public Sub New() 51 MyBase.New() 52 End Sub 53 54 Public Sub New(ByVal Contents As String) 55 MyBase.New(Contents) 56 End Sub 57 58 Public Sub New(ByVal Contents As String, 59 ByVal Author As String) 60 Me.New(Contents) 61 Me.Author = Author 62 End Sub 63 64 Public Sub New(ByVal Contents As String, 65 ByVal Author As String, _ 66 ByVal Language As String) 67 Me.New(Contents, Author) 68 Me.Language = Language 69 End Sub
466
_
As String
_
_
Eine Anwendung mit der OOP entwerfen
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 End
Public Sub New(ByVal Contents As String, _ ByVal Author As String, _ ByVal Language As String, _ ByVal Purpose As String) Me.New(Contents, Author, Language) Me.Purpose = Purpose End Sub Public Overrides Function GetSnippet() As String Dim sTmp As String sTmp = MyBase.GetSnippet() & _ System.Environment.NewLine & _ "Language: " & Language & _ System.Environment.NewLine & _ "Purpose: " & Purpose Return sTmp End Function Class
Die Zeilen 22-34 sind die Konstruktoren der Klasse Snippet. Sie veranschaulichen das Standardverfahren der Objektinitialisierung. Die Zeilen 50-77 sind die Konstruktoren der Klasse CodeSnippet. Sie enthalten einige Tricks bezüglich der Initialisierung der Klasse. Der erste Trick besteht darin, dass Sie den Konstruktor der Basisklasse mit dem Schlüsselwort MyBase (Zeilen 51 und 55) aufrufen können. Dieses stellt sicher, dass aller Code in der Basisklassenroutine und dann auch für die abgeleitete Klasse ausgeführt wird. Der zweite Trick, der in allen übrigen Konstruktoren verwendet wird, besteht darin, Ihre einfacheren Konstruktoren aus den komplexeren heraus aufzurufen. Dadurch können Sie doppelten Code leichter vermeiden; die generische Initialisierung findet im ersten Konstruktor statt und allen zusätzlichen Code, den Sie für die Behandlung der neuen Parameter benötigen, können Sie in den ersten Konstruktor platzieren, in dem der neue Parameter vorkommt.
14.3 Eine Anwendung mit der OOP entwerfen Die objektorientierte Programmierung ist kein technisches Konzept und hat nicht nur mit der Programmierung eines Systems zu tun. Vielmehr ist sie seit dem ersten Konzeptentwurf an der Erstellung eines Systems beteiligt. Sie sollten entscheiden, ob Sie ein System mit objektorientiertem Verfahren entwerfen. Die Implementierung hat mit dieser Entscheidung nichts zu tun. Vor .NET hatte Visual Basic keine Merkmale, die die Vererbung,
467
Einführung in die objektorientierte Programmierung
das Überladen von Prozeduren und andere OOP-Konzepte ermöglichten, aber die Programmierer konnten eine Anwendung dennoch mit diesen Konzepten entwerfen und diese dann in mehreren Schritten oder auf Umwegen implementieren, um das zu erreichen, was heute bereits in Visual Basic .NET integriert ist. Nachdem Sie die Sichtungsphase und die Analyse der Anforderungen für ein Projekt abgeschlossen haben, machen Sie mit dem konzeptuellen Entwurf weiter. Während der Entwurfsphase beginnen Sie, Objekte in Ihre Anwendung zu integrieren. Bei diesem Entwurf gehen Sie folgendermaßen vor: 1. Bestimmen Sie die Entitäten. 2. Ermitteln Sie die Eigenschaften und Methoden. 3. Erstellen Sie Objekthierarchien. 4. Modellieren Sie die Objekte. Alle diese Schritte werde ich behandeln, während ich ein Siebzehnundvier-Spiel beschreibe, das Objekte verwendet. Sie sollten dieses Kartenspiel verstehen, bevor Sie die folgenden Abschnitte lesen. Wenn Sie nichts über Siebzehnundvier wissen, schlagen Sie unter Encarta (http://encarta.msn.de) den Begriff »Siebzehnundvier« nach.
Objekte identifizieren Zuerst müssen Sie die Anforderungen des Systems ermitteln. Diese Aufgabe ist wesentlich umfangreicher, als es zunächst vielleicht scheint. In dieser Phase müssen Sie das geplante System, die Anwendung oder die Anwendungskomponente so detailliert umreißen, dass Sie alle wesentlichen Entitäten ermitteln können. Ein Trick, den ich am Anfang dieses Prozesses gerne einsetze, besteht darin, einfach alle Substantive aus meinen Systembeschreibungen herauszuziehen. Sehen Sie sich für unser Beispiel die folgende kurze und möglicherweise ungenaue Beschreibung des Spiels Siebzehnundvier an. (Auf der oben genannten Encarta-Site finden Sie bessere Informationen.) Siebzehnundvier ist ein Kartenspiel für zwei oder mehr Spieler, von denen einer der Geber ist. Das Ziel des Spieles besteht darin, eine Punktzahl zu erreichen, die möglichst nahe an, aber nicht über 21 liegt. Das Spiel wird jeweils zwischen dem einzelnen Spieler und dem Geber gespielt. Jedes Unentschieden (Geber und Spieler kommen gleich nah an die 21 heran) geht an den Geber. Vor jeder Runde wetten die Spieler, d.h., sie setzen Geld auf die Wahrscheinlichkeit, mit der sie die Hand des Gebers schlagen werden. Ein oder mehrere Kartenspiele werden gemischt und der Geber gibt jedem Spieler und sich selbst jeweils eine Karte. Nachdem die Spieler ihre Karte gesehen haben, platzieren sie ihre Wetten. Dann gibt der Geber jedem Spieler und sich selbst eine weitere Karte. Die Runde endet, wenn irgendjemand mit nur zwei Karten die Punktzahl 21 erreicht hat. Wenn niemand die-
468
Eine Anwendung mit der OOP entwerfen
ses Ergebnis erzielt, gibt der Geber dem ersten Spieler so lange Karten, bis dieser unter oder bei 21 Punkten aufhören möchte. Wenn der Spieler mit den neuen Karten die 21 überschreitet, hat er diese Runde verloren. Genauso wird der Reihe nach bei allen Spielern verfahren. Jeder Spieler erbittet so lange weitere Karten, bis er aufhören möchte oder die 21 überschreitet und damit ausscheidet. Wenn alle Spieler entweder entschieden haben, dass sie keine Karten mehr bekommen möchten, oder aus der Runde ausgeschieden sind, gibt der Geber sich selbst nach denselben Regeln Karten. Anschließend muss der Geber alle Spieler auszahlen, die eine höhere Punktzahl haben als er selbst. Die Spieler, deren Punktzahl unter der des Gebers liegt, müssen diesen auszahlen. Da der Geber bei einem Unentschieden immer gewinnt, muss jeder den Geber auszahlen, wenn dieser 21 erreicht. Dies ist natürlich nur eine kurze Beschreibung, die das Verdoppeln der Einsätze, das Teilen und andere Eigenschaften des Spiels nicht beschreibt. Aber für unsere Zwecke ist sie ausführlich genug. Betrachten Sie diese Beschreibung als Teil der Anforderungsangaben für Ihre Arbeit und suchen Sie nach Entitäten, die in Objekte umgewandelt werden könnten. Denken Sie an meinen Tipp, nach Substantiven zu suchen, und versuchen Sie eine eigene Liste von Objekten zu erfassen, bevor Sie sich meine Liste ansehen. Wenn wir die Beschreibung nur durchlesen und einfach alle Substantive herausholen, erhalten wir mehr Entitäten, als wir haben möchten. Aus dieser Liste können wir jedoch eine Auswahl treffen. Ich bin dabei zu den folgenden Vorschlägen für Objekte gelangt: 쐽
Spieler
쐽
Geber (vielleicht auch ein Spieler?)
쐽
Runde (ein Durchgang)
쐽
Spiel (das ganze Spiel)
쐽
Pack (die verwendeten Karten)
쐽
Blatt (die Karten des Spielers oder Gebers)
쐽
Karte (eine einzelne Spielkarte)
쐽
Setzen (die Wette eines Spielers)
Mit dieser Liste können wir nun die Eigenschaften und Methoden der Objekte festlegen, was oft dabei hilft, zu erkennen, ob und welche Objekte verwandt sind.
Eigenschaften und Methoden Ich beschränke mich zunächst auf einen Teil der gefundenen Entitäten: Spieler, Geber, Pack, Blatt und Karte. Jede dieser Entitäten hat mehrere Attribute, die zu Eigenschaften werden können, und mehrere wahrscheinliche Aktionen, die zu Methoden werden kön-
469
Einführung in die objektorientierte Programmierung
nen. Ein Spieler kann die Eigenschaften Name, Aktuelles Blatt, Position (da der Geber die Spieler in einer bestimmten Reihenfolge durchgeht, kann die Position wichtig sein), und Abrechnung haben. Die Methoden des Objekts Spieler könnten Passen, Ziehen, Setzen usw. sein. Das Objekt Geber hat ebenso wie das Objekt Spieler die Eigenschaften Name, Aktuelles Blatt und Abrechnung sowie die Methoden Passen und Ziehen. Bisher haben wir uns erst zwei Objekte angesehen und dennoch wird bereits ein Muster erkennbar. Wegen der Ähnlichkeiten zwischen Spieler und Geber wäre es vielleicht am besten, wenn sie nur ein Objekt wären oder wenn es eine Vererbungsbeziehung zwischen ihnen gäbe. Das Erkennen dieser Beziehungen ist ein wesentlicher Teil der Identifizierung von Objekten und kann sich erheblich auf die Anwendung auswirken. Wenn die Objekte Geber und Spieler aus demselben Basisobjekt geerbt sind oder wenn Geber aus Spieler geerbt ist, dann können Sie Code schreiben, der für beide Entitäten zuständig ist, indem er sie einfach als Instanzen derselben Basisklasse behandelt. Das Objekt Pack hätte eine Sammlung individueller Kartenobjekte und die Eigenschaft Anzahl (für die Anzahl der Karten, die sich derzeit im Pack befinden; sie nimmt ab, während Karten ausgegeben werden). Das Objekt Blatt, das für die Karten steht, die ein Spieler oder der Geber derzeit hat, hätte ebenfalls eine Sammlung individueller Kartenobjekte und die Eigenschaft Anzahl. Möglicherweise wäre beim Objekt Blatt auch die Eigenschaft Summe sinnvoll, um den Gesamtwert der Karten anzugeben. Aber wegen der Regel »Ass kann 1 oder 11 sein« ist diese Eigenschaft schwer zu implementieren und erfordert einen aufwändigeren Entwurf. Das Objekt Pack könnte für die Sammlung einzelner Karten die Methoden Hinzufügen/Entfernen sowie die Methode Mischen haben, um eine zufällige Ordnung der Sammlung zu erzielen. Hinzufügen/Entfernen wäre auch auf das Objekt Blatt anwendbar, Mischen wäre hier jedoch nicht sinnvoll. Die Objekte Pack und Blatt haben nicht nur Ähnlichkeit miteinander, sondern sind fast identisch. Anstelle der Vererbung wäre es hier auch möglich, beide zu genau derselben Klasse zu machen, allerdings mit einer jeweils anderen Kartenzahl. Alternativ könnten Blatt und Pack auch abgeleitete Klassen der generischeren Klasse Kartensammlung sein. Die Klasse Karte ist relativ einfach zu definieren: Farbe (Herz, Karo, Kreuz, Pik) und Wert (2-10, B, D, K, A). Karte braucht nichts aus einem anderen Objekt zu erben.
Objekte modellieren Während Sie aus der Analyse der Anforderungen Objekte ermitteln, sollten Sie Diagramme oder Modelle erstellen, die die Objekte und deren Beziehungen veranschaulichen. Die Visual Studio .NET Architect Edition bietet Modellierungswerkzeuge, die auch in einer eigenständigen Ausgabe von Microsoft Visio zur Verfügung stehen. Die Abbildung 14.1 zeigt, wie ein Diagramm mit einem Objektmodell aussieht.
470
Zusammenfassung
Abbildung 14.1: Das Modellieren mit der UML (Unified Modeling Language) dient zum einen Entwurfszwecken, da Sie damit den aktuellen Zustand des Objektmodells Ihrer Anwendung sehen können, und bietet zum anderen eine Dokumentation der Interna Ihres Systems.
Das Diagramm in der Abbildung 14.1 wurde mit einem Modellstil namens UML oder Universal Modeling Language erstellt. Diese Art von Diagramm können Sie mit einem von mehreren Werkzeugen erstellen, unter anderem mit Visio und Rational Rose. Das Modellieren einer Anwendung ist sinnvoll, da es ein visuelles Diagramm Ihres ersten Objektentwurfs bietet, und Sie anhand dieses Modells später Änderungen vornehmen können. In einer großen Teamumgebung kann es auch hilfreich sein, dass die meisten Modellierungswerkzeuge die Codegenerierung ermöglichen, da Sie so den Basiscode für die Objekte in Ihren Diagrammen erstellen können. In Zusammenhang mit der Codegenerierung unterstützen Visio und Rational Rose (und wahrscheinlich auch andere Werkzeuge) das Reverse Engineering vorhandenen Codes und können mit dieser Technologie aus existierendem Code ein Objektdiagramm ableiten. Das Reverse Engineering ist eine hervorragende Möglichkeit, ein System zu dokumentieren, wenn Sie die Modellierung nicht von Anfang an verwendet haben.
14.4 Zusammenfassung Objekte sind ein wesentlicher Teil von Visual Basic .NET, aber ein objektorientierter Ansatz unterscheidet sich von einem regulären prozeduralen Verfahren. Zuerst müssen Sie sich auf den Entwurf konzentrieren und versuchen, die Konzepte und Entitäten Ihres Systems als Objekte zu betrachten, bevor Sie sich um die Einzelheiten der Implementierung dieser Objektkonzepte kümmern. Am Tag 15 werden Sie mehr darüber erfahren, wie Sie mit den verschiedenen OOP-Merkmalen, die wir in der heutigen Lektion besprochen haben, eigene Objekte erzeugen.
471
Einführung in die objektorientierte Programmierung
14.5 Fragen und Antworten F
Ich habe gehört, dass C# und C++ bessere Möglichkeiten der objektorientierten Programmierung bieten als Visual Basic .NET. Stimmt das? A
F
Vor .NET traf dies auf C++ tatsächlich zu. C++ bietet OOP-Eigenschaften (wie z.B. die Vererbung), die Visual Basic nicht besaß. Nun haben aber alle .NETSprachen dieselben OOP-Funktionsmerkmale, da diese nicht mehr von den einzelnen Sprachen, sondern von der Common Language Specification (der alle .NET-Sprachen entsprechen müssen) bereitgestellt werden. Dies bedeutet, dass Visual Basic .NET in .NET für die OOP ebenso gut geeignet ist wie C++ oder C#.
Ist die OOP besser oder schneller als die »lineare« oder reguläre Art der Programmierung? A
Nicht unbedingt. Die OOP liefert in einer Anwendung keine anderen Ergebnisse. Sie geht lediglich beim Entwerfen und Erstellen der Anwendung anders vor. Meist geht man davon aus, dass die OOP Systeme hervorbringt, die leichter zu warten und zu erweitern sind, aber dies hängt natürlich immer von Ihrem Code ab.
14.6 Workshop Der Workshop soll Ihnen dabei helfen, mögliche Fragen vorherzusehen und das Gelernte zu wiederholen. Außerdem soll er Sie dazu anregen, über die praktische Umsetzung Ihres Wissens nachzudenken. Die Antworten und Lösungen finden Sie im Anhang A.
Quiz 1. Mit welchem Schlüsselwort können Sie auf die Basisklasse referenzieren, wenn Sie innerhalb einer abgeleiteten Klasse Code schreiben? 2. Wie bezeichnet man es, wenn Sie mehrere Versionen derselben Methode mit unterschiedlichen Gruppen von Parametern erstellen? 3. Welche Ausgabe liefert der folgende Code? 1 Module AllAboutObjects 2 Sub Main() 3 Dim x As myFirstClass 4 Dim y As myFirstClass 5 x = New myFirstClass() 6 y = x
472
Workshop
7 y.Name = "Fred" 8 x.Name = y.Name 9 Console.WriteLine("x.Name = { 0} ", x.Name) 10 Console.WriteLine("y.Name = { 0} ", y.Name) 11 Console.ReadLine() 12 End Sub 13 End Module 14 15 Public Class myFirstClass 16 Public Name As String 17 End Class
Übung Beschreiben Sie einen möglichen Objektentwurf, mit dem Sie Ereignisse in einem großen Stadion registrieren. Berücksichtigen Sie dabei verschiedene Arten von Ereignissen und erstellen Sie eine kleine Liste der Objekte, die Sie für diesen Zweck entwerfen könnten.
473
Rückblick In der zweiten Woche haben wir sehr viele Themen behandelt: Von der Einführung in eine Reihe von .NET-Framework-Klassen bis hin zu Einzelheiten komplizierter objektorientierter Merkmale von Visual Basic .NET. Insgesamt haben Sie so viel gelernt, dass Sie nun eine vollständige, funktionierende Anwendung mit einer Benutzeroberfläche, Datenbankanbindung und sogar einigen Verbindungen zu Serverfunktionen wie z.B. zu Ereignisprotokollen erstellen können. Tag 8 leitete die Woche mit einer Beschreibung der nützlichsten Elemente des .NET-Frameworks ein und zeigte Ihnen, wie Sie das Framework selbstständig erkunden können. Dank dieser Kenntnisse können Sie nun im Framework alle Funktionsmerkmale finden, die Sie benötigen. Diese Fähigkeit ist von unschätzbarem Wert, wenn Sie etwas machen müssen, das dieses Buch aus Zeitgründen nicht erklären kann. Heute ist es zwar modern und chic, Webanwendungen zu erstellen, aber Windows-Anwendungen sind noch immer gebräuchlich. Tag 9 zeigte Ihnen, wie Sie in Visual Basic .NET Windows-Anwendungen erstellen. Die Besprechung der Windows Forms hat Ihnen auch den Grund für das »Visual« in Visual Basic verdeutlicht: das Erstellen einer Drag&DropBenutzeroberfläche. Mit Steuerelementen, Eigenschaften und ein wenig Code können Sie nun eine eigene Windows-Anwendung erstellen. Natürlich haben Sie auch gelernt, wie Sie eine webbasierte Benutzeroberfläche einrichten. Darum ging es am Tag 10. Diese Lektion stellte Ihnen die Web Forms vor und zeigte Ihnen dabei, wie Sie eine Webanwendung schreiben, die Sie auch mit dem ereignisorientierten Modell programmieren könnten, das Windows-Anwendungen seit Jahren verwenden. An den Tagen 11 und 12 haben Sie Datenbanken kennen gelernt – mit einiger Theorie, aber vor allem mit vielen praktischen Hinweisen. Diese Lektionen zeigten Ihnen, wie Sie mithilfe der Klassen des System.Data-Frameworks eine Verbindung zu einer Datenbank herstellen, die benötigten Informationen abrufen und diese Daten dann nach Bedarf verändern können. Nebenbei haben Sie mehr über den Musikgeschmack eines der Autoren (wir verraten nicht, welches Autors) erfahren, als Sie eigentlich wollten.
475
Rückblick
Am Tag 13 haben Sie eines der wichtigen neuen Merkmale der Visual Studio IDE kennen gelernt, den Server Explorer. Sie haben erfahren, wie Sie mit dem Server Explorer von Visual Basic .NET aus Verbindungen zu Servern herstellen, Leistungsmonitore betrachten und herstellen und mit den Ereignisprotokollen von Windows XP/2000/NT arbeiten. Nun können die Anwendungen, die Sie erstellen, genauso agieren wie Programme auf Unternehmensebene: Mit den Werkzeugen, mit denen Systemadministratoren bereits vertraut sind, können auch Ihre Anwendungen jetzt Leistungs-, Prüf- und Fehlerinformationen liefern. Am letzten Tag der zweiten Woche, dem Tag 14, sind wir zu den Objekten in Visual Basic .NET zurückgekehrt, mit denen wir am Tag 7 bereits begonnen hatten. In dieser Aufbaulektion haben Sie die Struktur und die Merkmale von Objekten kennen gelernt und erfahren, wie Sie mit Objekten Anwendungen erstellen können. Über Objekte werden Sie noch mehr erfahren: Am Tag 15 lernen Sie, wie Sie in Visual Basic .NET Objekte und objektorientierte Systeme einrichten. Am Ende der zweiten Woche verstehen Sie nun die Grundlagen von Visual Basic .NET und können Systeme anlegen, die nicht mehr nur einfache Beispiele, sondern bereits echte Anwendungen sind. Das Bonusprojekt der zweiten Woche, eine Online-CD/DVDBibliothek, nutzt Ihr fortgeschrittenes Wissen und stellt es auf die Probe: Sie erstellen eine Anwendung mit einer komplexeren internen Struktur und einer Benutzeroberfläche für Windows und das Internet. Alle drei Bonusprojekte finden Sie im Internet unter http:// www.samspublishing.com und – soweit bei der Drucklegung dieses Buches schon verfügbar – auf der Begleit-CD in Verzeichnis \Bonus.
476
Tag 1
Willkommen bei Visual Basic .NET
29
T ag 2
Mit Visual Basic .NET arbeiten
59
T ag 3
Einführung in die Programmierung mit Visual Basic .NET
95
Tag 4
Den Programmfluss steuern
127
Tag 5
Anwendungsarchitektur in .NET
165
Tag 6
Was zu tun ist, wenn ein gutes Programm nicht funktioniert, und wie Sie das verhindern
187
Tag 7
Mit Objekten arbeiten
221
Tag 8
Einführung in das .NET-Framework
255
Tag 9
Mit Windows Forms eine Benutzeroberfläche erstellen
279
Tag 10
Mit Web Forms eine Benutzeroberfläche erstellen 319
Tag 11
Einführung in Datenbanken
345
Tag 12
Mit .NET auf Daten zugreifen
375
Tag 13
Der Server-Explorer
423
Tag 14
Einführung in die objektorientierte Programmierung
451
Tag 15
Objekte in Visual Basic .NET erzeugen
479
Tag 16
Windows Forms für Fortgeschrittene
513
Tag 17
Mit dem .NET Framework arbeiten
549
Tag 18
Der letzte Schliff
595
Tag 19
Die Anwendung bereitstellen
619
Tag 20
Einführung in XML
639
Tag 21
Mit Visual Basic .NET Webdienste erstellen
665
W O C H E
W O C H E
W O C H E
Überblick In der letzten Woche werden wir eine Vielfalt von Themen für fortgeschrittene Anwender behandeln, um Ihr Wissen über Visual Basic und das .NET-Framework abzurunden. Zuerst werden Sie am Tag 15 lernen, wie Sie eigene Komponenten und Bibliotheksklassen erstellen. Tag 16 bespricht ausführlich, wie Sie mit den Windows-Forms-Klassen komplexere Benutzeroberflächen für Windows-Anwendungen erstellen. Dabei behandeln wir Menüs, Multiple-Document-Interface-Anwendungen (MDI) und verschiedene komplexere Steuerelemente, die von den Windows Forms-Klassen bereitgestellt werden. Wie Sie in den ersten 14 Tagen gesehen haben, ist das .NET-Framework umfangreich und komplex, bietet aber alle Funktionsmerkmale, die Sie in Ihren Anwendungen benötigen. Aus diesem Grund erforscht Tag 17 weitere Bereiche des Frameworks, unter anderem die Arbeit mit Grafiken und den Umgang mit Dateien in .NET. Tag 18 und Tag 19 liefern Ihnen die Rahmeninformationen, die Sie benötigen, damit Sie Ihre Anwendung packen und auf dem Zielsystem installieren können, sei es nun ein einzelner Webserver oder einige tausend Desktops. In diesen Lektionen werden wir auch das .NET-Framework im Rahmen der Bereitstellung von Anwendungen sowie die Grundanforderungen an die Clients besprechen. Tag 20 führt Sie in XML ein, die Sprache, die allgemein für Daten verwendet wird, die durch .NET fließen. Diese Lektion behandelt sowohl XML selbst als auch diejenigen Teile von .NET (System.Xml), die es Ihrem Code erleichtern, XML-Informationen zu lesen, zu schreiben und zu verändern. In der letzten Lektion dieses Buches, Tag 21, behandeln wir die Web Services, also den Code, den Sie mit Industriestandardverfahren wie SOAP, XML und HTTP über das ganze Internet hinweg aufrufen können. Welchen Platz die Web Services in Ihren Systemen einnehmen, haben wir bereits am Tag 5 erläutert; das Material von Tag 21 zeigt nun, wie Sie mit Visual Basic .NET einen einfachen Web Service erstellen und prüfen. Schließlich folgt noch das dritte und letzte Bonusprojekt: Hangman, ein komplexes Programm, das die XML-Konzepte von Tag 20 verwendet, einige Arbeit mit Dateien und Grafiken (Tag 17) erfordert und die Erstellung einer komplizierteren Benutzeroberfläche umfasst (Tag 16). Dieses Programm sollte auch Spaß machen. Sie schreiben ein Spiel, das Sie Ihrer Familie, Ihren Freunden und Mitarbeitern vorführen können, damit diese sich auch einmal an einem Programm erfreuen können, das Sie geschrieben haben!
478
Objekte in Visual Basic .NET erzeugen
Objekte in Visual Basic .NET erzeugen
In früheren Lektionen haben Sie gelernt, wie Sie Objekte verwenden und entwerfen. Ab einem gewissen Punkt wollen oder müssen Sie jedoch damit beginnen, eigene Objekte zu erzeugen.
Themen heute 쐽
Objekte in Visual Basic .NET definieren
쐽
Eigene Objekte im Code verwenden
쐽
Die Klassen zu Assemblies zusammenfügen und diese Assemblies von einer anderen Anwendung aus verwenden
Um diese Konzepte zu veranschaulichen, werden wir in der heutigen Lektion mehrere Beispielklassen erstellen und eine Assembly generieren, die diese Klassen enthält.
15.1 Objekte erzeugen In Visual Basic .NET erzeugen Sie Objekte nicht direkt, sondern Sie erstellen Klassen, die dann zur Definition des Objekts werden. Anschließend erzeugen Sie aus der Klasse Objekte, die zu Instanzen der Klasse werden. Wenn Sie eigene Objekte definieren, müssen Sie daher zuerst einmal eine neue Visual Basic .NET-Klasse erstellen.
In Visual Basic .NET eine neue Klasse deklarieren Das Erstellen einer neuer Klasse ist ganz einfach: Geben Sie im Codebearbeitungsfenster (in einer Visual Basic .NET-Datei) Public Class Test ein und drücken Sie die Eingabetaste. Vorausgesetzt, Sie haben dies nicht in einer tatsächlichen Prozedur (Subroutine oder Funktion) ausprobiert, wissen Sie, dass der Vorgang erfolgreich ausgeführt wurde, wenn Visual Basic .NET automatisch die Zeile End Class einfügt. Diese Anweisungen deklarieren eine neue Klasse und geben den Anfang und das Ende der Klassendefinition an. Innerhalb der Klasse definieren Sie Eigenschaften, Methoden und Ereignisse. Zuerst müssen Sie jedoch die Klassendeklaration verstehen.
Gültigkeitsbereich Wenn Sie Public Class Test eingeben, erstellen Sie eine leere Klasse mit dem Gültigkeitsbereich Public. Dadurch kann jeder, der über eine Referenz auf dieses Projekt verfügt, eine Instanz von Test erzeugen. Er braucht dafür nur myTest = New Test() auszuführen. Da dies normalerweise Ihren Wünschen entspricht, erstellen Sie Klassen im Allgemeinen
480
Objekte erzeugen
mit dem Gültigkeitsbereich Public. Sie haben jedoch noch andere Möglichkeiten. So können Sie auch Private-Klassen erstellen. Hierfür verwenden Sie das Schlüsselwort Private und produzieren damit eine Klasse, die Sie nur aus Code heraus verwenden können, der sich auf derselben Gültigkeitsbereichsebene wie die Deklaration befindet. In der Regel bedeutet dies, dass er sich in derselben Datei befindet. Wenn Sie eine Klasse innerhalb der Deklaration einer anderen Klasse deklariert haben (wenn Sie die Klassen also geschachtelt haben), dann kann jeder Code innerhalb der enthaltenden Klasse auf das neue Objekt zugreifen. Private verwenden Sie dann, wenn Sie eine Klasse haben, die nicht außerhalb der aktuellen Codemenge erstellt werden soll. Schließlich gibt es noch den Gültigkeitsbereich Friend. Auf Klassen, Variablen und Prozeduren, die mit dem Gültigkeitsbereich Friend deklariert wurden, kann jeder Code innerhalb desselben Programms (Assembly, Webdienst, Windows-Anwendung usw.) zugreifen, während Code außerhalb dieses Programms nicht zugriffsberechtigt ist.
Vererbung Als Teil der Klassendeklaration können Sie eine einzelne Basisklasse angeben, aus der die neue Klasse erben soll, falls die Klasse aus irgendeiner anderen Klasse abgeleitet ist. Das Listing 15.1 zeigt ein Beispiel hierzu. Listing 15.1: Eine Klasse kann aus einer einzigen anderen Klasse erben. 1 2 3 4 5 6 7 8 9
Public Class Snippet 'Klassendefinition End Class Public Class CodeSnippet Inherits Snippet 'Klassendefinition End Class
Außerdem können Sie angeben, dass aus Ihrer Klasse nicht geerbt werden darf. Hierfür fügen Sie hinter dem Gültigkeitsbereich-Abschnitt der Klassendeklaration das Schlüsselwort NotInheritable ein. Wenn Sie dieses Schlüsselwort angeben, kann niemand eine neue Klasse erstellen, die aus Ihrer Klasse abgeleitet ist. Dieses Verfahren ist gleichbedeutend mit dem Begriff »versiegelt«, mit dem C# angibt, dass aus der neuen Klasse nicht geerbt werden kann, und wird in der Definition vieler .NET-Objekte wie z.B. System. String verwendet. Dem Erstellen versiegelter Klassen genau entgegengesetzt ist das Erstellen abstrakter Klassen, die als Basisklassen verwendet werden müssen und nicht direkt erstellt werden können. Wenn Sie Basisobjekte definieren, die nicht eigenständig, sondern nur als Teil ihrer Objekthierarchie verwendet werden sollen, dann ist das Schlüsselwort MustInherit genau
481
Objekte in Visual Basic .NET erzeugen
das, was Sie brauchen. Dieses Schlüsselwort wird häufig dann verwendet, wenn die Basisklasse allein nicht funktionsfähig ist und Sie erst eine oder mehrere Schlüsselmethoden in abgeleiteten Klassen implementieren müssen, bevor Sie irgendetwas Sinnvolles machen können. Das Listing 15.2 veranschaulicht die Verwendung des Schlüsselworts MustInherit. Es ist sinnvoll, darauf hinzuweisen, dass Sie das Schlüsselwort MustOverride als Teil von Methodendeklarationen verwenden können. Damit erzwingen Sie, dass diese Methoden in jede geerbte Version dieser Klasse aufgenommen werden. Wenn Sie MustOverride verwenden, brauchen Sie für die betreffende Methode oder Eigenschaft keine Implementierung bereitzustellen. Listing 15.2: Mit MustInherit und MustOverride können Sie angeben, dass eine Klasse nicht direkt verwendet werden darf. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Public MustInherit Class BaseShape Public MustOverride ReadOnly Property NumSides() As Integer Public MustOverride Sub Draw() End Class Public Class Square Inherits BaseShape Public Overrides Sub Draw() 'Zeichne ein Quadrat. End Sub Public Overrides ReadOnly Property NumSides() As Integer Get Return 4 End Get End Property End Class
Für sich allein genommen ist die Klasse Shape nutzlos: Sie richtet lediglich die allgemeine Schablone für alle Objekte ein, die diese Klasse als Basisklasse verwenden. Diese Basisklasse ist insofern nützlich, als sie es Ihnen ermöglicht, rund um die einzelne Basisklasse verschiedene Routinen zu schreiben und alle abgeleiteten Klassen automatisch zu unterstützen. Indem Sie diese Klasse als MustInherit kennzeichnen, stellen Sie sicher, dass kein Programmierer gegen Ihre Absichten verstoßen und direkt eine Instanz von Shape erzeugen kann. Es ist ohnehin unwahrscheinlich, dass jemand eine Instanz von Shape erzeugen möchte (und wenn jemand dies doch macht, stellt das auch kein Problem dar), aber machen Sie Ihre Wünsche auf jeden Fall deutlich und verhindern die Verwendung des falschen Typs. Indem Sie die Methode Draw als MustOverride kennzeichnen, geben Sie an, dass diese Methode in jeder neuen, aus dieser Klasse abgeleiteten Klasse bereitgestellt werden muss. Auch dies ist wieder eine
482
Objekte erzeugen
Möglichkeit, wie Sie mithilfe der Eigenschaften von Visual Basic .NET Ihre Absichten deutlich machen können, statt sich darauf zu verlassen, dass andere Programmierer immer das Richtige machen.
Eigenschaften hinzufügen Es gibt zwei Hauptmöglichkeiten, einer Klasse Eigenschaften hinzuzufügen: Sie können öffentliche Variablen erstellen (die direkt zu Eigenschaften werden) oder vollständige Eigenschaftsroutinen anlegen. Wenn Sie Eigenschaften mit öffentlichen Variablen erstellen, haben Sie keinerlei Kontrolle über die verwendeten Variablen und wenn Sie die Werte der Variablen ändern, können Sie keinen Code mehr ausführen. Das Listing 15.3 zeigt diese Art, Eigenschaften zu definieren. Dieses Verfahren ist dann angebracht, wenn Sie einfach nur mit den Klassen in Visual Basic .NET experimentieren. Listing 15.3: Öffentliche Variablen werden innerhalb einer Klasse deklariert zu Eigenschaften. 1 Public Class Person 2 Public Name As String 3 Public FirstName As String 4 Public SecondName As String 5 End Class
Ein komplexeres Verfahren, Eigenschaften zu erstellen, besteht darin, eine Eigenschaftsroutine anzulegen. Dabei haben Sie vollständige Kontrolle über die Eigenschaft. Zuerst ermitteln Sie den Namen und den Datentyp der Eigenschaft, die Sie hinzufügen möchten, und dann geben Sie innerhalb einer Klasse die folgende Zeile ein (wobei Sie den Eigenschaftsnamen und -datentyp durch die passenden Werte ersetzen): Public Property Name() As String
Wenn Sie am Ende der Zeile die Eingabetaste drücken, erstellt Visual Basic .NET das Grundgerüst einer vollständigen Eigenschaftsprozedur: Public Property Name() As String Get End Get Set(ByVal Value As String) End Set End Property
Diese Prozedur besteht aus zwei Teilen: Get und Set. Set behandelt die Zuweisung eines Eigenschaftswertes und Get das Holen eines Wertes (objTest.Name = "Fred"). Da Sie
483
Objekte in Visual Basic .NET erzeugen
bei dieser Art von Eigenschaftsroutine keine öffentliche Variable verwenden, müssen Sie wahrscheinlich eine private Variable deklarieren, die den Eigenschaftswert speichert. Wenn Sie Ihrer Beispielklasse die Variable m_sName hinzufügen, können Sie den Code schreiben, der in die Eigenschaftsroutine Get/Set platziert wird. Die private Klassenvariable hat das Präfix m_ (für »member variable«), das anzeigt, dass dies die interne Darstellung einer bereitgestellten Eigenschaft ist. In der einfachsten Form wird der Code innerhalb der Eigenschaftsroutine (siehe Listing 15.4) einfach dafür verwendet, Werte in die und aus der internen Variablen zu tauschen. Listing 15.4: Eigenschaftsprozeduren sind eine Alternative zur Verwendung öffentlicher Variablen. 1 Public Class Test 2 Private m_sName As String 3 4 Public Property Name() As String 5 Get 6 Return m_sName 7 End Get 8 Set(ByVal Value As String) 9 m_sName = Value 10 End Set 11 End Property 12 End Class
Die Eigenschaftsroutine im Listing 15.4 ist Public, was wahrscheinlich Ihren Wünschen entspricht. Sie könnten sie jedoch auch als Private kennzeichnen und sie damit auf die Verwendung innerhalb der Klasse beschränken. Die eigentlichen Vorteile von Eigenschaftsroutinen gegenüber öffentlichen Variablen bestehen darin, dass Sie damit Nur-Lese- und Nur-Schreibeigenschaften erstellen und bei jedem Versuch, in eine Eigenschaft zu schreiben, eine Datenüberprüfung durchführen können.
Nur-Lese und Nur-Schreibeigenschaften erstellen Wenn Sie eine Eigenschaftsroutine mit Nur-Lese- oder Nur-Schreibzugriff versehen möchten, geben Sie in der Eigenschaftsdeklaration die entsprechende Einstellung an. Wenn die Eigenschaft ohne zusätzliche Schlüsselwörter deklariert wird, wie es in Listing 15.4 der Fall ist, dann ist sie eine Eigenschaft mit Lese-/Schreibzugriff und Sie müssen sowohl den Get- als auch den Set-Teil der Routine bereitstellen. Wenn Sie in der Deklaration hingegen Public ReadOnly Property angeben, erstellen Sie eine Eigenschaft, die nur Werte zurückgeben kann. Und mit Public WriteOnly Property erstellen Sie eine Eigenschaft, die nicht gelesen werden kann. Nur-Leseeigenschaften sind zwar weniger gebräuchlich als Lese-/Schreibeigenschaften, können aber in vielen Fällen nützlich sein.
484
Objekte erzeugen
Das Listing 15.5 zeigt, wie Sie mithilfe von drei Eigenschaften, zwei Lese-/Schreib- und einer Nur-Leseeigenschaft, eine nützliche Klasse erstellen können. Listing 15.5: Es gibt Lese-/Schreib-, Nur-Lese- und Nur-Schreibeigenschaften. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
Option Strict On Option Explicit On Module AllAboutObjects Public Sub Main() Dim objSample As New Person() objSample.FirstName = "Fred" objSample.LastName = "Jones" Console.WriteLine(objSample.DisplayName) Console.ReadLine() End Sub End Module Public Class Person Private m_sName As String Private m_sFirstName As String Private m_sLastName As String Public ReadOnly Property DisplayName() As String Get Return String.Format("{ 0} { 1} ", m_sFirstName, m_sLastName) End Get End Property Public Property FirstName() As String Get Return m_sFirstName End Get Set(ByVal Value As String) m_sFirstName = Value End Set End Property Public Property LastName() As String Get Return m_sLastName End Get Set(ByVal Value As String) m_sLastName = Value End Set
485
Objekte in Visual Basic .NET erzeugen
42 End Property 43 End Class
Nur-Schreibeigenschaften sind etwas verwirrender. Ich kann mir nicht viele Gründe für eine Eigenschaft vorstellen, die der Anwender zwar setzen, aber nicht sehen kann. Wenn er sie setzen kann, dann sollte er den Wert bereits kennen; warum sollten Sie ihn also daran hindern, diesen Wert zu sehen? Ich kann mir nur eine einzige Situation vorstellen, in der eine Nur-Schreibeigenschaft sinnvoll sein könnte, nämlich bei Passwörtern. Stellen Sie sich eine Klasse vor, die dafür verwendet wird, eine Verbindung zu einer Back-EndDatenbank herzustellen. Eine der Eigenschaften dieser Klasse ist das Datenbankpasswort. In einigen Fällen möchte man nach dem Setzen der Eigenschaftswerte eine Instanz dieses Objekts an eine andere Routine übergeben, ohne dabei sichere Informationen sichtbar zu machen.
Daten mit Eigenschaftsroutinen validieren Eigenschaftsroutinen haben einen weiteren Vorteil gegenüber öffentlichen Variablen: Bei einer Eigenschaftsroutine führen Sie jedes Mal Code aus, wenn der Anwender den Wert dieser Eigenschaft abrufen oder setzen möchte. Dies eröffnet Ihnen mehrere Handlungsmöglichkeiten. Unter anderem können Sie die Daten auf ihrem Weg zu den Objekten überprüfen. Das Listing 15.6 prüft z.B. eine Kreditkartennummer auf ihrem Weg zum Objekt und weist sie zurück, falls sie nicht gültig ist. Listing 15.6: Eigenschaftsprozeduren ermöglichen die Datenvalidierung. 1 Option Explicit On 2 Option Strict On 3 4 Public Class CreditCardValidation 5 Public Enum CardTypes 6 ccvVisa 7 ccvMasterCard 8 ccvDiscover 9 ccvAMEX 10 ccvDinersClub 11 ccvEnRoute 12 ccvUndefined 13 End Enum 14 15 Private Const Numbers As String = "0123456789" 16 Private Const sInvalidCheckSumError As String _ 17 = "Credit Card Number contains an error " & _ 18 "in one or more digits (Checksum Error)" 19 Private Const sLengthError As String _ 20 = "Credit Card is not the correct length " & _
486
Objekte erzeugen
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
"for its type (Length Error)" Private Private Private Private
sErrorMsg As String mCardType As CardTypes sCardNumber As String bValid As Boolean
Public Property CardNumber() As String Get Return sCardNumber End Get Set(ByVal Value As String) bValid = ValidCreditCard(Value) If bValid Then sCardNumber = Value Else Throw New System.ArgumentException(sErrorMsg, _ "CardNumber") End If End Set End Property Private Function CTPrefix(ByVal sCard As String) As CardTypes If CType(Left(sCard, 2), Integer) > 50 AndAlso _ CType(Left(sCard, 2), Integer) < 56 Then Return CardTypes.ccvMasterCard ElseIf Left(sCard, 1) = "4" Then Return CardTypes.ccvVisa ElseIf Left(sCard, 4) = "6011" Then Return CardTypes.ccvDiscover ElseIf Left(sCard, 2) = "34" OrElse _ Left(sCard, 2) = "37" Then Return CardTypes.ccvAMEX ElseIf Left(sCard, 2) = "36" Then Return CardTypes.ccvDinersClub Else Return CardTypes.ccvUndefined End If End Function
Private Function Prefix(ByVal sTest As String, _ ByVal sArg As String) As Boolean If Left(sArg, Len(sTest)) = sTest Then Prefix = True Else
487
Objekte in Visual Basic .NET erzeugen
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
488
Prefix = False End If End Function Private Function ValidCreditCard(ByVal sNumber As String) _ As Boolean Dim sTemp As String Dim iTemp As Integer Dim sCreditCard As String Dim iCardLength As Integer Dim Checksum As Integer Dim i As Integer sTemp = sNumber sCreditCard = "" Checksum = 0 For i = 1 To Len(sTemp) If InStr(Numbers, Mid(sTemp, i, 1)) 0 Then sCreditCard = sCreditCard & Mid(sTemp, i, 1) End If Next mCardType = CTPrefix(sCreditCard) sCardNumber = sCreditCard iCardLength = Len(sCreditCard) Select Case mCardType Case CardTypes.ccvAMEX If iCardLength 15 Then ValidCreditCard = False sErrorMsg = sLengthError End If Case CardTypes.ccvVisa If (iCardLength 13) AndAlso (iCardLength 16) Then ValidCreditCard = False sErrorMsg = sLengthError End If Case CardTypes.ccvMasterCard, CardTypes.ccvDiscover If iCardLength 16 Then ValidCreditCard = False sErrorMsg = sLengthError End If End Select
Objekte erzeugen
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 End
sCreditCard = Right("0000000000000000" & sCreditCard, 16) For i = 1 To Len(sCreditCard) – 1 iTemp = CInt(Mid(sCreditCard, 16 – i, 1)) iTemp = iTemp * (1 + (i Mod 2)) If iTemp >= 10 Then iTemp = iTemp – 9 End If Checksum = Checksum + iTemp Next i Checksum = (10 – (Checksum Mod 10)) Mod 10 If Checksum = CInt(Right(sCreditCard, 1)) Then ValidCreditCard = True sErrorMsg = "" Else ValidCreditCard = False sErrorMsg = sInvalidCheckSumError End If End Function Public Function CardTypeName(ByVal sCardNumber As String) As String Dim sTmp As String If ValidCreditCard(sCardNumber) Then Select Case mCardType Case CardTypes.ccvAMEX sTmp = "American Express" Case CardTypes.ccvVisa sTmp = "Visa" Case CardTypes.ccvMasterCard sTmp = "MasterCard" Case CardTypes.ccvDinersClub sTmp = "Diners Club" Case CardTypes.ccvDiscover sTmp = "Discover Card" Case CardTypes.ccvEnRoute sTmp = "enRoute" Case CardTypes.ccvUndefined sTmp = "Unknown" End Select Else Throw New ArgumentException(sErrorMsg, "Card Number") End If Return sTmp End Function Class
489
Objekte in Visual Basic .NET erzeugen
Dieser Code prüft nur, ob die Kreditkartennummer gültig ist, und nicht, ob es sich tatsächlich um eine Kreditkartennummer handelt. Diese Nummer kann sich ebenso gut auf ein gekündigtes, abgelaufenes oder auf andere Art ungültiges Kreditkartenkonto beziehen. Der Set-Teil dieser Eigenschaftsroutine nimmt eine Zeichenkette entgegen und ermittelt dann mit einer Prüfsumme, ob dies eine gültige Kreditkartennummer ist. Dabei addiert er die Ziffern auf besondere Art, um sie mit der letzten Ziffer zu prüfen. Wenn die bereitgestellte Zeichenkette nicht zulässig ist, wird eine Ausnahme ausgelöst. Wenn die Eigenschaft CardNumber genauso gesetzt ist wie in diesem Beispiel, sollte das Programm diese Ausnahme abfangen. Im Allgemeinen sollten Sie öffentliche Variablen nicht als Eigenschaften verwenden. Dieses Verfahren funktioniert zwar, aber Sie haben weder Kontroll- noch Wahlmöglichkeiten. Am sichersten ist es, wenn Sie immer Eigenschaftsroutinen verwenden und sich die zusätzliche Tipparbeit durch Copy&Paste erleichtern.
Methoden erstellen Um einer Klasse Methoden hinzuzufügen, erstellen Sie Sub- und Function-Prozeduren, wobei der genaue Typ von der jeweiligen Situation abhängt. Ebenso wie Klassen (siehe den Abschnitt »Gültigkeitsbereich« weiter oben in diesem Kapitel) können Sie Methoden mit den Gültigkeitsbereichen Public, Private oder Friend erstellen. Auf Methoden, die mit dem Gültigkeitsbereich Public deklariert wurden, kann jeder mit einem Objekt dieses Typs zugreifen. Private-Methoden stehen nur innerhalb der Klasse selbst zur Verfügung und auf Friend-Methoden kann nur Code innerhalb derselben Assembly oder ausführbaren Datei zugreifen. Das Listing 15.7 zeigt dieselbe Klasse wie die bisherigen Beispiele (einige nicht dazugehörigen Codestücke wie z.B. die Property-Routinen wurden entfernt), enthält aber eine zusätzliche Public-Methode, die eine Verbindung zu Outlook herstellt und eine E-Mail-Adresse für diese Person zu finden versucht. Um diesen Code verwenden zu können, müssen Sie Ihrem Projekt eine Referenz auf Outlook hinzufügen (Outlook 2000 oder XP muss installiert sein und funktionieren.) Um Ihrem Projekt diese Referenz hinzuzufügen, gehen Sie folgendermaßen vor: 1. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf den Ordner References in Ihrem Projekt und wählen Sie im Kontextmenü VERWEIS HINZUFÜGEN aus. 2. Gehen Sie zur Registerkarte COM und suchen Sie in der Liste nach MICROSOFT OUTLOOK. 3. Klicken Sie die Schaltfläche AUSWÄHLEN an und schließen Sie das Dialogfenster mit der Schaltfläche OK. Nun werden Sie wahrscheinlich gefragt, ob Sie eine InteropAssembly erstellen möchten, wobei Sie JA anklicken sollten.
490
Objekte erzeugen
Listing 15.7: Diese Funktion ruft Informationen aus Outlook ab. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
Public Class Person 'Nicht dazugehöriger Code wurde aus dem Listing gelöscht. Public Function LookUpInOutlook() As String Dim objOutlook As New Outlook.Application() Dim objSession As Outlook.NameSpace Dim objPerson As Outlook.Recipient Dim sEmailAddress As String objOutlook.Session.Logon(NewSession:=False) objSession = CType(objOutlook.Session, Outlook.NameSpace) objPerson = objSession.CreateRecipient(Me.DisplayName) Try sEmailAddress = String.Format("{ 0} :{ 1} ", _ objPerson.AddressEntry.Type, _ objPerson.AddressEntry.Address) Return sEmailAddress Catch objException As Exception 'Adresse nicht gefunden. Return "" End Try End Function End Class Module AllAboutObjects Public Sub Main() Dim objSample As New Person() objSample.FirstName = "Joel" objSample.LastName = "Semeniuk" Console.WriteLine(objSample.DisplayName) Console.WriteLine(objSample.LookUpInOutlook()) Console.ReadLine() End Sub End Module
Wenn Sie diesen Code ausführen, gibt Outlook möglicherweise die Warnmeldung aus, dass ein Programm auf E-Mail-Adressen zuzugreifen versucht. Das trifft natürlich zu, da Sub genau dies macht. Wenn Sie sicher sind, dass Ihr Code die Ursache für die Warnung ist, dann können Sie OK anklicken, damit er eine E-Mail-Adresse holen kann. Sie müssen einen Namen bereitstellen, der in Ihrem Ordner Contacts oder in einem Adressverzeichnis vorhanden ist. Anderenfalls kann der Code niemanden finden und die Adresse wird nicht aufgelöst. Die Zeile 4 erzeugt eine neue Instanz von Outlook und die Zeile 9 holt die aktuelle MAPI-Sitzung.
491
Objekte in Visual Basic .NET erzeugen
Die Zeile 10 holt ein Outlook-Namespace-Objekt. Dies ist ein Schlüsselobjekt des Objektmodells von Outlook und zudem eines, das alles bereitstellt, worauf Sie zugreifen müssen. Die Verwendung der Methode CreateRecipient (Zeile 11) ist gleichbedeutend damit, den Namenswert in das Feld »An« einer E-Mail einzugeben und Outlook diesen Namen auflösen zu lassen. Wenn CreateRecipient scheitert (der Anwender nicht aufgelöst werden konnte), sollte der Try/Catch-Block rund um das Holen der Adresse alle sichtbaren Fehler verhindern. Sollen abgeleitete Klassen in der Lage sein, eine in ihrer Basisklasse definierte Methode zu überschreiben, dann muss die Basisklasse durch das Schlüsselwort Overridable gekennzeichnet sein und die Methode der abgeleiteten Klasse muss das Schlüsselwort Overrides enthalten. Im Listing 15.8 ist die Klasse Mortgage so definiert, dass sie die Hypothekenberechnungen behandelt (diese Klasse haben wir auch am Tag 14, Einführung in die objektorientierte Programmierung, verwendet). In Listing 15.9 leiten wir aus Mortgage die neue Klasse AccelMortgage ab. In diesen Listings verwenden wir die Schlüsselwörter Overridable und Overrides, damit sich die neue Klasse auch für andere als monatliche Ratenzahlungen eignet. Listing 15.8: Die Vererbung ist eine gute Möglichkeit, Funktionsmerkmale zu ergänzen. 1 Public Class Mortgage 2 'Sehr einfacher Hypothekenrechner. 3 'Setze AnnualInterestRate, NumberOfYears und Principal. 4 'Rufe die Eigenschaft PaymentAmount ab. 5 Private m_dblInterestRate As Double 6 Private m_iDuration_Years As Integer 7 Private m_curPrincipal As Decimal 8 9 Public Property AnnualInterestRate() As Double 10 Get 11 Return m_dblInterestRate 12 End Get 13 Set(ByVal Value As Double) 14 m_dblInterestRate = Value 15 End Set 16 End Property 17 18 Public Property NumberOfYears() As Integer 19 Get 20 Return m_iDuration_Years 21 End Get 22 Set(ByVal Value As Integer) 23 m_iDuration_Years = Value 24 End Set 25 End Property
492
Objekte erzeugen
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 End
Public Property Principal() As Decimal Get Return m_curPrincipal End Get Set(ByVal Value As Decimal) m_curPrincipal = Value End Set End Property Public Overridable Function PaymentAmount() As Decimal Dim iNumPaymentsPerYear As Integer = 12 Dim iPayments As Integer Dim dblFractionalInterestRate As Double Dim curPayment As Decimal iPayments = m_iDuration_Years * iNumPaymentsPerYear dblFractionalInterestRate = m_dblInterestRate / iNumPaymentsPerYear curPayment = m_curPrincipal * _ ((1 – (1 + dblFractionalInterestRate)) _ / (1 – ((1 + dblFractionalInterestRate) ^ iPayments)) _ + dblFractionalInterestRate) Return curPayment End Function Class
Die Einzelheiten des Listings 15.8 sind nicht sonderlich relevant. Wichtig ist vor allem, wie diese Klasse geschrieben werden muss, damit Sie aus ihr eine neue, beschleunigte Hypothekenklasse ableiten können. In Zeile 36 wird die Funktion PaymentAmount mit dem Schlüsselwort Overridable deklariert. Ohne dieses Schlüsselwort könnte AccelMortgage (siehe Listing 15.9) keine eigene Version der Funktion PaymentAmount bereitstellen. Listing 15.9: AccelMortgage enthält die zusätzliche Möglichkeit, einen Plan für eine schnellere Hypothekenrückzahlung zu berechnen. 1 Public Class AccelMortgage 2 Inherits Mortgage 3 Private m_iNumberOfPaymentsInYear As Integer = 12 4 5 Public Property PaymentsInYear() As Integer 6 Get 7 Return m_iNumberOfPaymentsInYear
493
Objekte in Visual Basic .NET erzeugen
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 End
End Get Set(ByVal Value As Integer) m_iNumberOfPaymentsInYear = Value End Set End Property Public Overrides Function PaymentAmount() As Decimal Dim iPayments As Integer Dim dblFractionalInterestRate As Double Dim curPayment As Decimal iPayments = Me.NumberOfYears * Me.PaymentsInYear dblFractionalInterestRate = _ Me.AnnualInterestRate / Me.PaymentsInYear curPayment = Me.Principal * _ ((1 – (1 + dblFractionalInterestRate)) _ / (1 – ((1 + dblFractionalInterestRate) ^ iPayments)) _ + dblFractionalInterestRate) Return curPayment End Function Class
Das Listing 15.9 veranschaulicht, wie eine Methode in einer abgeleiteten Klasse überschrieben wird. Die Zeile 14 macht dies, indem sie eine Methode deklariert, die in der Basisklasse vorhanden ist, und das Schlüsselwort Overrides angibt. Die Funktion PaymentAmout wurde so überschrieben, dass sie nicht nur monatliche Ratenzahlungen unterstützt. In dieser überarbeiteten Version der Funktion greifen wir auf erforderliche Eigenschaften der Klasse nicht direkt mit internen Variablen zu, sondern mit dem Schlüsselwort Me (Zeilen 19, 20 und 22). Der Grund hierfür ist, dass eine abgeleitete Klasse keinen Zugriff auf die privaten Variablen der Basisklasse hat. Das Listing 15.10 ist ein schneller und einfacher Client für diese beiden Klassen, der so entworfen ist, dass er eine reguläre Hypothek mit monatlichen Raten mit derselben Hypothek vergleicht, die jedoch die beschleunigte Rückzahlung in vierzehntägigen Raten verwendet. Listing 15.10: Das Clientprogramm ruft beide Hypothekenrechner auf. 1 Module Main 2 Sub Main() 3 Dim objMortgage As New Mortgage() 4 With objMortgage 5 .AnnualInterestRate = 0.07 6 .NumberOfYears = 25
494
Objekte erzeugen
7 .Principal = 140000 8 Console.WriteLine("{ 0:C} ", .PaymentAmount()) 9 End With 10 11 Dim objNewMortgage As New AccelMortgage() 12 With objNewMortgage 13 .AnnualInterestRate = 0.07 14 .NumberOfYears = 25 15 .PaymentsInYear = 26 16 .Principal = 140000 17 Console.WriteLine("{ 0:C} ", .PaymentAmount()) 18 End With 19 Console.ReadLine() 20 End Sub 21 End Module
Es ist nicht immer leicht zu entscheiden, was eine Eigenschaft und was eine Methode sein soll, und viele Entwickler treffen die falsche Entscheidung. Es gibt keine eindeutige Regel, die Ihnen sagt, was Sie machen müssen, aber ich kann Ihnen die Richtlinie verraten, die ich verwende, und erklären, welche Logik dahinter steht. Ich verwende beim Entwurf von Objekten eine einfache Prüfung: »Eigenschaften sollten nie dazu führen, dass etwas geschieht.« Ich schreibe Klassen gerne so, dass ich mit einer Eigenschaft immer dasselbe Ergebnis erziele, egal ob ich sie einmal oder dreimal setze. Diese einzelne Richtlinie vermeidet Situationen, in denen die Reihenfolge, in der Sie die Eigenschaften eines Objektes setzen, das Ergebnis beeinflussen kann. In der Klasse Mortgage könnten Sie den Code z.B. auch so schreiben, dass das Setzen der Eigenschaft Principal dazu führt, dass der Ratenbetrag neu berechnet wird. Wenn dies der Fall ist, dann müssen Sie sicherstellen, dass Sie die übrigen Eigenschaften (AnnualInterestRate, NumberOfYears) vor Principal gesetzt haben. Ich vermeide es, irgendeinen Code in eine Eigenschaftsroutine zu setzen, die nicht nur die entsprechende interne Variable, sondern auch noch andere interne Teile meiner Klasse verändert. PaymentAmount, die Funktion in der Klasse Mortgage, könnte ebenso gut eine Eigenschaft wie eine Methode sein, da sie während ihrer Berechnungen die Klasse nicht verändert. Ich habe sie in der heutigen Lektion zu einer Methode gemacht, in der Lektion 14 jedoch zu einer Eigenschaft. In vielen Fällen sind beide Möglichkeiten gleich gut.
Ereignisse hinzufügen Ereignisse sind in .NET ein wichtiger Bestandteil der Objekte, insbesondere im Bereich der GUI, aber auch im übrigen Framework. Ereignisse ermöglichen es einem Objekt, jedem der interessierten Anwender mitzuteilen, dass etwas geschehen ist. Hier interagieren das Objekt und der Code, der das Objekt verwendet, also wesentlich stärker, als wenn die Eigenschaften des Objekts einfach nur wiederholt abgerufen werden. Ereignisse passen
495
Objekte in Visual Basic .NET erzeugen
nicht in jede Situation; die Klassen Mortgage und AccelMortgage benötigen z.B. keine Ereignisse, da ihr Code eigenständig ist und nur auf Anfrage ausgeführt wird. Wenn ein Objekt jedoch die Anwender über irgendetwas informieren soll, dann sind Ereignisse hervorragend geeignet. Stellen Sie sich z.B. eine Klasse vor, die so entworfen ist, dass sie mit einem E-Mail-Programm kommuniziert und Sie benachrichtigt, wenn eine neue E-Mail eintrifft, die bestimmte Kriterien erfüllt. Sie könnten zwar eine Eigenschaft als Flag setzen, die signalisiert, wenn eine solche E-Mail eintrifft, aber dann müsste Ihr Programm diesen Wert ständig prüfen. Statt dessen können Sie ein Ereignis einrichten, das Ihre Klasse auslösen kann, und es durch einige Daten ergänzen, die das Ereignis beschreiben. Das Hauptprogramm ruft das Objekt nicht ab; vielmehr schreiben Sie einfach Code in einen Ereignisbehandler für dieses Ereignis Ihres Objekts. Um ein Ereignis anzulegen, fügen Sie der Klasse eine Deklaration hinzu. Das Listing 15.11 zeigt hierzu ein Beispiel: Listing 15.11: Eine Ereignisdeklaration hinzufügen 1 Public Class OutlookMessageWatcher 2 3 Public Event EmailArrived(ByVal From As String, _ 4 ByVal Subject As String, _ 5 ByVal Message As Outlook.MailItem) 6 7 End Class
Sie können für all diese Parameter Werte angeben und wenn das Ereignis dann ausgelöst wird, werden diese Werte an alle Routinen geschickt, die dieses Ereignis behandeln möchten. Das Ereignis wird aus der Klasse heraus ausgelöst, und zwar, wie das Listing 15.12 (die vollständige Klasse OutlookMessageWatcher) zeigt, mit der Anweisung RaiseEvent. Listing 15.12: Vollständiges Listing des Outlook-Nachrichtenagenten 1 Imports System 2 Imports System.Windows.Forms 3 4 Public Class OutlookMessageWatcher 5 Private WithEvents objInboxItems As Outlook.Items 6 Private objOutlook As Outlook.Application 7 Public Event EmailArrived(ByVal From As String, _ 8 ByVal Subject As String, _ 9 ByVal Message As Outlook.MailItem) 10 11 Public Sub New() 12 objOutlook = New Outlook.Application() 13 objOutlook.Session.Logon(NewSession:=False) 14 objInboxItems = objOutlook.Session.GetDefaultFolder _
496
Objekte erzeugen
15 (Outlook.OlDefaultFolders.olFolderInbox).Items 16 End Sub 17 18 Private Sub objInboxItems_ItemAdd(ByVal Item As Object) _ 19 Handles objInboxItems.ItemAdd 20 Dim objNewMail As Outlook.MailItem 21 Try 22 objNewMail = CType(Item, Outlook.MailItem) 23 RaiseEvent EmailArrived(objNewMail.SenderName, _ 24 objNewMail.Subject, _ 25 objNewMail) 26 Catch objException As Exception 27 MessageBox.Show(objException.Message) 28 End Try 29 End Sub 30 End Class
Nachdem Sie in Ihrer Klasse ein Ereignis eingerichtet haben, können Sie Code schreiben, der dieses Ereignis in anderen Klassen behandelt, indem Sie Ihr Objekt mit dem Schlüsselwort WithEvents deklarieren und dann mit Handles eine Ereignisroutine erstellen. Das Listing 15.13 zeigt, wie wir unsere neue Klasse als Teil eines Windows-Formulars verwenden können. Listing 15.13: Den Nachrichtenagenten ausprobieren 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
Public Class Form1 Inherits System.Windows.Forms.Form Private WithEvents objOMW As OutlookMessageWatcher 'Windows Form Designer generated code omitted Private Sub Form1_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load objOMW = New OutlookMessageWatcher() End Sub Private Sub objOMW_EmailArrived(ByVal From As String, _ ByVal Subject As String, _ ByVal Message As Outlook.MailItem) _ Handles objOMW.EmailArrived lbMailItems.Items.Add(Subject) End Sub End Class
Damit Sie die Ereignisse Ihres Objekts verwenden können, müssen zwei Anforderungen erfüllt sein: Sie müssen das Objekt mit WithEvents deklarieren und mit dem Schlüsselwort
497
Objekte in Visual Basic .NET erzeugen
Handles einen Ereignisbehandler erstellen. Wenn Sie in der Visual Studio-IDE arbeiten, kann diese die Ereignisprozedur einrichten, falls Sie das Objekt mit WithEvents deklarieren, und anschließend zuerst das Objekt aus der rechten Dropdown-Liste und dann das Ereignis aus der linken Dropdown-Liste auswählen.
Interfaces definieren und verwenden Eines der mächtigsten Merkmale der objektorientierten Programmierung (OOP) ist die Fähigkeit, ein Objekt so zu behandeln, als sei es eine Instanz einer anderen Klasse. Bei der Vererbung bedeutet dies, dass Sie eine abgeleitete Klasse so behandeln können, als sei sie eine ihrer Vorfahren. Dies ist nützlich und häufig auch ein Hauptmotiv für die Erstellung von Basisklassen. Es ist allerdings auf Klassen mit einer Vererbungsbeziehung beschränkt. Mit Interfaces können Sie ein ähnliches Ergebnis erzielen, nämlich Klassen, die so behandelt werden können, als seien sie andere Klassen. Ein Interface ist ein besonderer Klassentyp, der keinen Code enthält, sondern als eine Möglichkeit verwendet wird, um das Erscheinungsbild eines Objektes zu beschreiben. Andere Klassen können dann eines oder mehrere dieser Interfaces implementieren, wodurch man sie so behandeln kann, als seien sie ein Objekt eines dieser Typen. Interfaces werden im gesamten .NET-Framework verwendet und ermöglichen es beliebig vielen Klassen, anzugeben, dass sie alle eine bestimmte Gruppe von Funktionsmerkmalen bereitstellen. Eines der im .NET-Framework definierten Interfaces ist IComparable (die Namen von Interfaces beginnen häufig mit einem großen I). IComparable stellt eine einzige Methode bereit, CompareTo, mit der das aktuelle Objekt mit einer anderen Instanz derselben Klasse verglichen wird. Jede Klasse, die das Interface IComparable implementiert, gibt im Grunde genommen an, dass zwei Instanzen dieser Klasse verglichen werden können und dass eine Größer-als-, Kleiner-als- oder Ist-gleich-Beziehung ermittelt werden kann. Um ein Interface zu implementieren, muss eine Klasse normalerweise eine eigene Version einer oder mehrerer Methoden bereitstellen. Klassen, die IComparable implementieren möchten, brauchen nur eine eigene Version der Methode CompareTo zu schreiben. Das Listing 15.14 zeigt eine Klasse, die IComparable implementieren möchte, sowie die angepasste Version von CompareTo, die diese Klasse bereitstellt. Listing 15.14: Mit IComparable funktioniert Ihre Klasse. 1 Public Class Person 2 Implements IComparable 3 Private m_sName As String 4 Private m_sFirstName As String 5 Private m_sLastName As String
498
Objekte erzeugen
6 7 Public ReadOnly Property DisplayName() As String 8 Get 9 Return String.Format("{ 0} { 1} ", _ 10 m_sFirstName, m_sLastName) 11 End Get 12 End Property 13 14 Public Property FirstName() As String 15 Get 16 Return m_sFirstName 17 End Get 18 Set(ByVal Value As String) 19 m_sFirstName = Value 20 End Set 21 End Property 22 23 Public Property LastName() As String 24 Get 25 Return m_sLastName 26 End Get 27 Set(ByVal Value As String) 28 m_sLastName = Value 29 End Set 30 End Property 31 32 Public Function CompareTo(ByVal obj As Object) As Integer _ 33 Implements System.IComparable.CompareTo 34 'Vergleiche diese Instanz mit obj, gib eine Zahl kleiner 0 zurück, 35 'um anzuzeigen, dass obj < me, oder 0, um anzuzeigen, dass 36 'obj = me, oder eine Zahl größer 0, um anzuzeigen, dass obj > me. 37 Dim objOtherPerson As Person 38 objOtherPerson = CType(obj, Person) 39 40 If objOtherPerson.LastName < Me.LastName Then 41 Return -1 42 ElseIf objOtherPerson.LastName > Me.LastName Then 43 Return 1 44 Else 45 If objOtherPerson.FirstName < Me.FirstName Then 46 Return -1 47 ElseIf objOtherPerson.FirstName > Me.FirstName Then 48 Return 1 49 Else 50 Return 0 51 End If
499
Objekte in Visual Basic .NET erzeugen
52 End If 53 End Function 54 End Class
Da die Klasse Person das Interface IComparable implementiert, können wir sie nun so an Funktionen übergeben, als sei sie ein Objekt des Typs Icomparable. Wir können sie auch mit anderen Objekten verwenden, die verlangen, dass dieses Interface implementiert ist. In der .NET-Framework-Klasse SortedList findet sich ein Beispiel dafür, inwiefern dies nützlich sein kann. Diese Klasse ermöglicht es Ihnen, eine Liste von Objekten zu erstellen, die jeweils mit einem Schlüssel verbunden sind. Außerdem sorgt sie dafür, dass diese Liste immer nach den Schlüsselwerten sortiert ist. Der Haken ist dabei, dass die Objekte, die Sie als Schlüssel bereitstellen, IComparable unterstützen müssen, da SortedList Sortierungen nur in Abhängigkeit von der Methode CompareTo durchführen kann und diese Methode von IComparable bereitgestellt wird. Diese Anforderung stellt kein Problem dar, wenn Ihr Schlüssel ein einfacher Datentyp wie z.B. String, Integer oder Date ist, da diese Datentypen das Interface IComparable unterstützen. Eine eigene Klasse können Sie hingegen nur als Schlüssel verwenden, wenn diese Klasse IComparable implementiert. Das Listing 15.15 zeigt, wie Sie eine sortierte Liste erstellen können, bei der Sie Instanzen der Klasse Person als Schlüsselwerte verwenden. Listing 15.15: Eine Klasse, die IComparable implementiert, in einer sortierten Liste. 1 Option Strict On 2 Option Explicit On 3 4 Module AllAboutObjects 5 Public Sub Main() 6 Dim objPerson1 As New Person() 7 Dim objPerson2 As New Person() 8 Dim objPerson3 As New Person() 9 Dim Val1, Val2, Val3 As Integer 10 11 Val1 = 234 12 Val2 = 13 13 Val3 = 500 14 15 objPerson1.FirstName = "John" 16 objPerson1.LastName = "Adams" 17 objPerson2.FirstName = "Quincy" 18 objPerson2.LastName = "Wallace" 19 objPerson3.FirstName = "Arlene" 20 objPerson3.LastName = "Ratuski" 21 22 Dim slPeople As New SortedList()
500
Objekte erzeugen
23 24 slPeople.Add(objPerson1, Val1) 25 slPeople.Add(objPerson2, Val2) 26 slPeople.Add(objPerson3, Val3) 27 28 Dim objDE As DictionaryEntry 29 30 For Each objDE In slPeople 31 Console.WriteLine("{ 0} { 1} ", _ 32 CType(objDE.Key, Person).DisplayName, _ 33 CType(objDE.Value, Integer)) 34 Next 35 Console.ReadLine() 36 End Sub 37 End Module
Wenn Sie angeben möchten, dass eine Klasse eine Form allgemeiner Funktionalität (z.B. die Fähigkeit, verglichen zu werden) bereitstellt, sollten Sie statt der Vererbung Interfaces verwenden. Da die Klasse Person aus den vorherigen Beispielen einen Zweck hat, der mit der Unterstützung des Vergleichs überhaupt nichts zu tun hat, sollten Sie diese Unterstützung mit einem Interface angeben. Das .NET-Framework bietet viele verschiedene Interfaces. Sie können jedoch auch eigene Interfaces erstellen, wobei Sie ähnlich vorgehen wie bei der Definition einer neuen Klasse. Die Definition eines Interfaces beginnt mit Public Interface und endet mit End Interface. Zwischen Start und End enthält sie Deklarationen von Methoden, Eigenschaften und Ereignissen. Unabhängig davon, was Sie machen, enthält das Interface keinerlei Code, sodass die Methoden- und Eigenschaftsdeklarationen nur aus der ersten Zeile einer regulären Deklaration bestehen. Das Listing 15.16 definiert ein einfaches Interface namens IDebugInfo, um für das Debugging allgemeine Informationen über die Klasse bereitzustellen. Listing 15.16: Interfaces sehen wie leere Klassen aus. 1 2 3 4 5 6 7 8 9
Option Explicit On Option Strict On Public Interface IDebugInfo ReadOnly Property ClassName() As String ReadOnly Property Author() As String ReadOnly Property SourceFile() As String ReadOnly Property Description() As String End Interface
Interfaces gleichen Klassen zwar in mancherlei Hinsicht, aber wie Sie in Listing 15.16 sehen können, unterscheiden sich beide in anderen Punkten erheblich voneinander.
501
Objekte in Visual Basic .NET erzeugen
Innerhalb der Deklaration von Interface (Zeilen 4 bis 9) sind die Elemente ausschließlich mit ihren Deklarationen definiert; es wird keinerlei Implementierungscode bereitgestellt. Der Gültigkeitsbereich ist ebenfalls nicht in diesen Deklarationen genannt, aber Public ist der Standardwert, wenn Sie dieses Interface in einer Klasse implementieren. Unabhängig von dem Gültigkeitsbereich, den Sie in Ihrer Klasse wählen, stehen diese Elemente über das Interface IDebugInfo zur Verfügung. Bei diesem Interface sind diese Eigenschaften bei allen Instanzen einer Klasse dieselben und sollen diese bestimmte Klasse beschreiben. Die Eigenschaften in unserem Beispiel lassen Nur-Lesewerte zu. Allerdings können Interfaces alle erforderlichen Eigenschaftsformen umfassen. Das Listing 15.17 zeigt ein Beispiel für eine Protokollroutine, die Informationen aus einer beliebigen Klasse verfolgt, die IDebugInfo unterstützt. Listing 15.17: Diese Protokollprozedur nimmt irgendeine Klasse entgegen, die IDebugInfoimplementiert. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
Option Explicit On Option Strict On Imports System Imports System.IO Public Class LoggingRoutine Const DefaultLogFile As String = "C:\ LogFile.txt" Shared Sub LogMsgToFile(ByVal Message As String, _ ByVal FileName As String, _ ByVal Source As IDebugInfo) Dim objLogFile As StreamWriter If File.Exists(FileName) Then objLogFile = File.AppendText(FileName) ElseIf Directory.Exists _ (Path.GetDirectoryName(FileName)) Then objLogFile = File.CreateText(FileName) Else 'Fehler, verwende Standardprotokolldatei. objLogFile = File.AppendText(DefaultLogFile) End If
502
objLogFile.WriteLine("-----------------------------") objLogFile.WriteLine(Message) objLogFile.WriteLine("From { 0} ({ 1} ) by { 2} ", _ Source.ClassName, Source.SourceFile, Source.Author) objLogFile.WriteLine("Description: { 0} ", _ Source.Description) objLogFile.WriteLine("-----------------------------")
Objekte erzeugen
31 objLogFile.Flush() 32 objLogFile.Close() 33 End Sub 34 End Class
Diese Protokollklasse zeigt, wie die Verwendung eines allgemeinen Interfaces in mehreren Objekten Bestandteil eines Anwendungsentwurfs sein kann. Die gemeinsam genutzte Prozedur LogMsgToFile (Zeile 9) nimmt mehrere Parameter entgegen, zu denen auch ein Objekt des Typs IDebugInfo gehört. Sie ermöglicht es damit, dass man dieser Routine jede Instanz einer Klasse, die dieses Interface implementiert, übergeben kann. Die Routine selbst macht nichts Besonderes, ist aber nicht ganz uninteressant. Die Klassen Path, File und Directory stammen alle aus demselben Namensraum System.IO und stellen mehrere statische oder gemeinsam genutzte Funktionen bereit. Daher können Sie diese Objekte verwenden, ohne jemals eine Instanz davon zu erzeugen. Der Code in Listing 15.18 zeigt eine Beispielklasse, die IDebugInfo implementiert. Listing 15.18: Das Interface IDebugInfo verwenden 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
Option Explicit On Option Strict On Module Main Sub Main() Dim objPerson As New Person() objPerson.FirstName = "Duncan" objPerson.LastName = "Mackenzie" Console.WriteLine(objPerson.DisplayName()) End Sub End Module Public Class Person Implements IComparable Implements IDebugInfo Private m_sName As String Private m_sFirstName As String Private m_sLastName As String Public Sub New() LoggingRoutine.LogMsgToFile _ ("Started Up", "C:\ test.txt", Me) End Sub
503
Objekte in Visual Basic .NET erzeugen
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
504
Public ReadOnly Property DisplayName() As String Get LoggingRoutine.LogMsgToFile _ (String.Format("DisplayName called, { 0} { 1} output", _ m_sFirstName, m_sLastName), "C:\ test.txt", Me) Return String.Format("{ 0} End Get End Property
{ 1} ", m_sFirstName, m_sLastName)
Public Property FirstName() As String Get Return m_sFirstName End Get Set(ByVal Value As String) m_sFirstName = Value End Set End Property Public Property LastName() As String Get Return m_sLastName End Get Set(ByVal Value As String) m_sLastName = Value End Set End Property Public Function CompareTo(ByVal obj As Object) As Integer _ Implements System.IComparable.CompareTo 'Vergleiche diese Instanz mit obj, gib entweder eine Zahl kleiner '0 zurück, um anzuzeigen, dass obj < me, oder 0, um anzuzeigen, 'dass obj = me, oder größer 0, um anzuzeigen, dass obj > me. Dim objOtherPerson As Person objOtherPerson = CType(obj, Person) If objOtherPerson.LastName < Me.LastName Then Return -1 ElseIf objOtherPerson.LastName > Me.LastName Then Return 1 Else If objOtherPerson.FirstName < Me.FirstName Then Return -1 ElseIf objOtherPerson.FirstName > Me.FirstName Then Return 1 Else
Objekte erzeugen
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 End
Return 0 End If End If End Function Public ReadOnly Property Author() As String _ Implements IDebugInfo.Author Get Return "Duncan Mackenzie" End Get End Property Public ReadOnly Property ClassName() As String _ Implements IDebugInfo.ClassName Get Return "Person" End Get End Property Public ReadOnly Property Description() As String _ Implements IDebugInfo.Description Get Return "The Person is designed to represent" _ & " a customer or employee" End Get End Property Public ReadOnly Property SourceFile() As String _ Implements IDebugInfo.SourceFile Get Return "\ \ liquidsoap\ bigproject\ Source\ Person.vb" End Get End Property Class
Um diesen Code zu verwenden, platzieren Sie das Interface IDebugInfo (aus Listing 15.16), die Protokollroutine (aus Listing 15.17) und diesen Code (Listing 15.18) jeweils in eine eigene Datei in einem Projekt. Gehen Sie zu den Projekteigenschaften und stellen Sie sicher, dass die Routine Main aus Listing 15.18 als Startobjekt gewählt ist. Nun sollte alles funktionieren. Denken Sie daran, dass Sie diesen Code auch auf der Begleit-CD und im Internet (http:// www.samspublishing.com) finden und ihn nicht selbst einzugeben brauchen. Das Listing 15.18 enthält zwei wichtige Dinge: eine Beispielklasse (Person), die IDebugInfo implementiert (Zeilen 13 bis 105), und etwas Beispielcode (Zeilen 4 bis 11), der eine
505
Objekte in Visual Basic .NET erzeugen
Instanz der Klasse Person erzeugt und dann die Methode DisplayName aufruft. Gehen Sie wie bereits beschrieben vor: Setzen Sie den gesamten Code in ein Projekt (drei separate .vb-Dateien) und machen Sie Sub Main zum Startobjekt des Projekts. Dann sollte der Code problemlos auszuführen sein und eine Datei namens c:\test.txt mit den Trace-Informationen hervorbringen. Sowohl Interfaces als auch Inheritance ermöglichen es, dass viele verschiedene Objekte Attribute gemeinsam nutzen. Allerdings bestehen zwischen beiden zwei wesentliche Unterschiede, durch die sie sich für verschiedene Aufgaben eignen: Interfaces umfassen nur diese Attribute, aber keine Implementierungen. Die Definition von Interface enthält keinen Code und die Klassen, die dieses Interface implementieren, müssen alle Funktionsmerkmale selbst bereitstellen. Demgegenüber hat Inheritance den Vorteil, dass die implementierte Funktionalität von der Basisklasse und den abgeleiteten Klassen gemeinsam genutzt wird. Die Interfaces machen dies jedoch durch eine erheblich größere Flexibilität wett, die es den einzelnen Klassen ermöglicht, beliebig viele Interfaces zu implementieren. Letztlich haben beide Verfahren zwar einige Ähnlichkeiten, sind aber für ganz verschiedene Zwecke gedacht. Verwenden Sie Interfaces, wenn Sie anzeigen möchten, dass eine Klasse bestimmte (häufig mehrere) Fähigkeiten hat, die sich etwa als »meineKlasse unterstützt das Interface IDegubInfo« beschreiben lassen. Die Inheritance gibt hingegen eine »ist ein«-Beziehung an. Sätze wie z.B. »Kunde ist eine Person« und »Angestellter ist eine Person« könnten die Beziehung zwischen zwei abgeleiteten Klassen und ihrer Basisklasse beschreiben.
15.2 Die erzeugten Objekte verwenden Nachdem Sie eine Gruppe von Klassen und Interfaces definiert haben, möchten Sie diese wahrscheinlich in einer Anwendung einsetzen. In den bisherigen Beispielen kam nur ein einziges Projekt vor, sodass Sie Ihre Klassen verwenden konnten, indem Sie einfach neue Variablen deklarierten. Dies kann natürlich auch in realen Systemen vorkommen, aber es ist nicht sinnvoll, zu verlangen, dass die Klassendefinition Bestandteil aller Projekte ist. Haben Sie viele verschiedene Projekte, die dieselben Klassen- und Interface-Definitionen nutzen, dann funktioniert das auch nicht. Damit jedes Projekt, das Ihren Code verwenden muss, dies auch problemlos tun kann, müssen Sie ein Klassenbibliotheks-Projekt erstellen. Diese Art von Projekt soll nicht für sich ausgeführt werden, sondern speichert Code, den andere Projekte verwenden. 1. Wählen Sie im Menü DATEI zuerst NEU, dann PROJEKT. Nun wird das Dialogfenster NEUES PROJEKT angezeigt (siehe Abbildung 15.1).
506
Die erzeugten Objekte verwenden
Abbildung 15.1: Das Dialogfenster NEUES PROJEKT enthält Schablonen für die Erstellung von Klassenbibliotheken.
2. Wählen Sie die Projektschablone KLASSENBIBLIOTHEK und achten Sie auf die schöne Textbeschreibung (»Ein Projekt zum Erstellen von Klassen, die in anderen Anwendungen verwendet werden.«), die den Zweck dieser Schablone bestätigt. 3. Geben Sie dem neuen Projekt an Stelle des Standardnamens ClassLibrary1 einen anderen Namen und klicken Sie OK an, um das Projekt zu erstellen. Projekte dieses Typs bestehen aus beliebig vielen Codedateien mit Klassen, haben aber kein Startobjekt. Sie können Ihre Klassen nach Belieben verteilen, vielleicht sogar in jeder Datei nur eine einzige Klasse speichern, oder aber den ganzen Code in einer Datei kombinieren. Das Ergebnis ist immer dasselbe.
Namensräume Eine nützliche Art, Ihre Klassen zu organisieren, ist die Verwendung von Namensräumen. Eigentlich ist die Definition von Namensräumen zwar ein Verfahren der Codeorganisation, aber auch, wenn Sie Klassenbibliotheken erstellen, ist die Organisation wichtig. Das .NET-Framework verwendet Namensräume sehr häufig dafür, seine ordentlich organisierten Klassen und die Namensräume, in denen diese Klassen enthalten sind, anzulegen (System, System.Data, System.IO usw.). Sie können dasselbe machen, indem Sie Ihrem Code einfach Namensraumdefinitionen hinzufügen. Namensräume werden mit Namespace und End Namespace um den Code herum deklariert. Geschachtelte Namensräume wie z.B. Namensraum1.Namensraum2.Namensraum3 erhalten Sie, indem Sie den vollständigen Namen (Namensraum1.Namensraum2) angeben, da Sie Namespace-Deklarationen nicht schachteln können. Wenn der Namensraum um eine Klasse (siehe Listing 15.19) oder mehrere Klassen herum definiert ist, betrachtet man diese Klassen als Teil des Namensraums. In Ihrem Code können Sie darauf dann als Namensraum1.Klasse1 Bezug nehmen.
507
Objekte in Visual Basic .NET erzeugen
Listing 15.19: Namensräume ermöglichen die Organisation Ihrer Klassen. 1 2 3 4 5 6 7 8 9 10 11 12 13 14
Option Explicit On Option Strict On Namespace libPerson Public Class Class2 End Class End Namespace Module mod1 Sub main() Dim objClass2 As libPerson.Class2 End Sub End Module
Sie können denselben Namensraum in mehreren Codedateien und selbst in mehreren Codebibliotheken verwenden. Visual Studio .NET betrachtet jede einzelne Definition dieses Namensraums als Bestandteil des ganzen Namensraums. Die Listings 15.20 und 15.21 veranschaulichen dies, indem sie zwei separate Dateien darstellen. Listing 15.20: Derselbe Namensraum kann in mehreren Dateien verwendet werden. 1 2 3 4 5 6 7
Option Explicit On Option Strict On Namespace libPerson Public Class Class1 End Class End Namespace
Listing 15.21: Der Programmierer, der Ihren Code verwendet, sieht alles als Teil eines einzigen Namensraums. 1 2 3 4 5 6 7 8 9 10 11
Option Explicit On Option Strict On Namespace libPerson Public Class Class2 End Class End Namespace Module mod1 Sub main() Dim objClass2 As libPerson.Class2
508
Die erzeugten Objekte verwenden
12 Dim objClass1 As libPerson.Class1 13 End Sub 14 End Module
Am Ende des Listings 15.21 sehen Sie Code, der die beiden Klassen deklariert und verwendet (Zeilen 11 und 12). Wenn Sie eine Codebibliothek erstellen und sie in eine DLL kompilieren, können Sie all Ihren Code in Namespace-Deklarationen hüllen, damit der Anwender die Klassen leichter finden kann. Etwas weiter unten werden wir zeigen, dass Ihre Klassen bereits zu Gruppen zusammengefasst erscheinen, nachdem Sie sie in eine Bibliothek eingefügt und diese Bibliothek in einem anderen Projekt referenziert haben.
Eine Bibliotheks-DLL erstellen und verwenden Nachdem Sie Ihrem Bibliotheksprojekt alle Klassen und Interfaces hinzugefügt haben, brauchen Sie nur noch im Menü ERSTELLEN den Eintrag ERSTELLEN zu wählen oder (Strg)+(ª)+(B) zu drücken, um die endgültige DLL zu erzeugen. Normalerweise heißt diese Datei .dll und befindet sich im Verzeichnis bin unterhalb Ihres Projekts. Dieser Speicherort und Name sind zwar in Ordnung, aber um Ihre neue Bibliothek zu prüfen, müssen Sie diese Lösung schließen und ein neues Konsolenanwendungsprojekt öffnen. 1. Wählen Sie im Menü DATEI zuerst NEU, dann PROJEKT und erstellen Sie eine neue Visual Basic .NET-Konsolenanwendung. 2. Nachdem Sie das neue Projekt geöffnet haben, klicken Sie den Ordner VERWEISE in der Projektmappen-Explorer-Ansicht Ihres Projekts mit der rechten Maustaste an. 3. Wenn Sie in dem Menü, das nun erscheint, VERWEIS HINZUFÜGEN wählen, erscheint das Dialogfenster VERWEIS HINZUFÜGEN (siehe Abbildung 15.2). 4. Klicken Sie die Schaltfläche DURCHSUCHEN an und suchen Sie die neu angelegte Datei libPerson.dll. 5. Nachdem Sie die gewünschte DLL gefunden haben, klicken Sie OK an und kommen damit wieder zum Dialogfenster VERWEIS HINZUFÜGEN zurück. Klicken Sie erneut OK an, um zu Ihrem Code zurückzukehren. Nun steht Ihrem Projekt die neue Referenz zur Verfügung. Um diese Klassenbibliothek zu verwenden, behandeln Sie die zugehörigen Klassen einfach als Teil des .NET-Frameworks. Die Klassen selbst finden Sie über den Pfad ... Wenn Ihr Projekt libPerson heißt und Sie eine Referenz auf libPerson hinzugefügt haben, dann veranschaulicht das Listing 15.22, wie Sie von Ihrer Bibliothek aus Klasseninstanzen erzeugen können.
509
Objekte in Visual Basic .NET erzeugen
Abbildung 15.2: Mit dem Dialogfenster VERWEIS HINZUFÜGEN können Sie Komponentenreferenzen und Referenzen hinzufügen, die auf andere Projekte verweisen. Listing 15.22: Die Bibliothek verwenden 1 2 3 4 5 6 7 8
Option Explicit On Option Strict On Module Module1 Sub Main() Dim objClass As libPerson.libPerson.Class1 End Sub End Module
Beachten Sie, dass die Verwendung des Namensraumes libPerson zu einer sekundären Gruppierung geführt hat, die wahrscheinlich unnötig ist. Verwenden Sie die Namensräume in Ihren Bibliotheken dafür, anstelle der ganzen Bibliothek feinere Gruppierungen zu bilden.
15.3 Zusammenfassung Der Einsatz der OOP in Ihren Anwendungen ist keine Frage der Technik, sondern des Entwurfs. Wenn Sie den konzeptuellen Entwurf für Ihre Anwendung anlegen, müssen Sie in Objekten denken. Wenn Ihnen dies gelingt, dann bedeutet die Erstellung eines Systems mit Objekten kein vollkommenes Umdenken, sondern einfach eine Übersetzung aus dem konzeptuellen Entwurf in die Implementierung. Weitere Informationen über alle OOPKonzepte, die in Visual Basic .NET zur Verfügung stehen, finden Sie in der .NET-Dokumentation unter »Introduction to Objects in Visual Basic.«
510
Fragen und Antworten
15.4 Fragen und Antworten F
Im Zusammenhang mit der objektorientierten Programmierung habe ich den Begriff Polymorphismus gehört. Bedeutet dies, dass ich mehr als einen Ehepartner haben muss, um mit Visual Basic .NET arbeiten zu können? A
F
Keine Sorge, dieser Begriff hat nichts mit Ihren Familienverhältnissen zu tun. Der Polymorphismus beschreibt die Fähigkeit, viele verschiedene Objekte so zu behandeln, als hätten sie denselben Typ. Dadurch können Sie verschiedene Klassen wie z.B. Circle, Square und Triangle haben und diese alle als den Typ Shape behandeln. Dieses Konzept ist nützlich und steht mittels Vererbung und Interfaces zur Verfügung.
Muss ich mit Objekten programmieren, um Visual Basic .NET verwenden zu können? A
Kurz gesagt: Ja. Visual Basic .NET und das ganze .NET-Framework sind um Objekte herum aufgebaut und immer, wenn Sie eine der Klassen des .NET-Frameworks verwenden, arbeiten Sie mit Objekten. In Ihren eigenen Programme können Sie jedoch so viele oder so wenige Objekte verwenden wie Sie möchten. Jeder Code, den Sie schreiben, ist in Klassen (oder Modulen, die einfach eine etwas eigenartige Form von Klassen sind) enthalten, sodass Sie Objekte nicht völlig umgehen können. Aber es bleibt allein Ihre Entscheidung, ob Sie ein ganzes System mit Entitäten erstellen möchten, die als Objekte dargestellt sind, oder nicht.
15.5 Workshop Die Antworten und Lösungen finden Sie wie immer im Anhang A.
Quiz 1. Angenommen, Sie haben in Ihrem Unternehmen Entitäten, die unterschiedlichsten Zwecken dienen (Orders, Customers, OrderItems, Events), aber gemeinsame Merkmale oder Fähigkeiten haben. Mit welchem OOP-Konzept erstellen Sie diese Entitäten? 2. Wenn Sie eine Vererbungsbeziehung haben (wenn z.B. Employee und Customer aus Person abgeleitet sind), wie stellen Sie dann sicher, dass bestimmte Methoden und Eigenschaften der Basisklasse in den abgeleiteten Klassen implementiert werden?
511
Objekte in Visual Basic .NET erzeugen
3. Wie schreiben Sie bei einer Vererbungsbeziehung eine Basisklasse, die nicht erstellt werden kann, wenn Sie die Anwender zwingen wollen, nur Klassen zu erstellen, die aus dieser Basisklasse abgeleitet sind? 4. Wie geben Sie an, dass aus einer Klasse nichts geerbt werden kann? Wie bezeichnet man dies?
Übung Erzeugen Sie ein Objekt oder eine Objekthierarchie, das oder die einen Kunden repräsentiert. Versuchen Sie das Objekt mit einer oder mehreren Klassen zu erzeugen, wobei der Schwerpunkt darauf liegen soll, den Code in anderen Teilen Ihrer Anwendung wiederzuverwenden.
512
Windows Forms für Fortgeschrittene
Windows Forms für Fortgeschrittene
Am Tag 9, Mit Windows Forms eine Benutzeroberfläche erstellen, haben Sie gelernt, wie Sie mit Windows Forms Anwendungen erstellen. In diesem Bereich gibt es jedoch noch wesentlich mehr zu lernen. Heute werden wir uns andere Themen ansehen, die mit der Erstellung von Windows-Forms-Anwendungen zu tun haben. Dabei werden wir uns auf die folgenden Themen konzentrieren:
Themen heute 쐽
Menüs
쐽
Multiple-Document-Interface-Programme
쐽
Kompliziertere Windows-Forms-Steuerelemente
16.1 Menüs Sofern Sie bereits mit irgendeiner Windows-Version gearbeitet haben, sind Ihnen Menüs nicht neu. In fast jeder Anwendung gibt es Menüs, die den Zugriff auf die Funktionsmerkmale des Programms ermöglichen. Auch Sie müssen vielen Ihrer Programme Menüs hinzufügen.
Einem Formular ein Menü hinzufügen Ein Menü fügen Sie Ihrer Anwendung beinahe auf dieselbe Art hinzu wie ein Steuerelement, aber es gibt lediglich zwei kleine Unterschiede. Den ersten Unterschied sehen Sie, wenn Sie in der Werkzeugsammlung das Steuerelement MainMenu doppelt anklicken, um Ihrem Formular ein neues Menü hinzuzufügen. Sie sehen keine Instanz des Menüs auf dem Formular, sondern das Steuerelement erscheint in einem neuen Abschnitt der IDE (siehe Abbildung 16.1). Der zweite Unterschied zwischen dem Hinzufügen des Steuerelements MainMenu und dem Hinzufügen der meisten anderen Steuerelemente besteht darin, dass Sie das Steuerelement MainMenu normalerweise jedem Formular nur einmal hinzufügen. Sie können einem Formular zwar mehrere MainMenu-Steuerelemente hinzufügen, aber es kann immer nur eines aktiv sein. Um zwischen den MainMenuSteuerelementen zu wechseln, setzen Sie die Eigenschaft Menu des Formulars auf das gewünschte Menü. Me.Menu = mnuSecond
514
Menüs
Dies ist in Fällen angebracht, in denen Sie ein Formular für verschiedene Aufgaben verwenden, z.B. wenn Sie verschiedene Dateitypen in ein Steuerelement des Formulars laden. Dabei könnten Sie die Menüs zwar so ändern, dass sie die Operationen widerspiegeln, die auf der Datei durchgeführt werden können. Einfacher ist es jedoch, die Menüelemente je nach Bedarf zu verbergen oder anzuzeigen.
Abbildung 16.1: Das Formular mit dem Steuerelement MainMenu.
Das Steuerelement MainMenu erscheint zwar nicht im Formular, verändert das Formular aber. Nachdem Sie das Steuerelement MainMenu hinzugefügt haben, sollte auf dem Formular direkt unterhalb der Titelleiste eine neue Leiste erscheinen. Die Abbildung 16.1 veranschaulicht dies. Um dem Menü Elemente hinzuzufügen, erstellen Sie das Menü visuell. Wenn das Steuerelement MainMenu in der IDE ausgewählt ist, sollten Sie in der oberen linken Ecke des Formulars, also an der Stelle, an der sich normalerweise das Menü DATEI befindet, einen Textbereich mit dem Text Hier eingeben sehen. Setzen Sie den Cursor in diesen Bereich und geben Sie den Text ein, der in den Menüs erscheinen soll. Während Sie dies machen, erscheinen zwei weitere Textbereiche: einer neben dem neuen Element, das Sie gerade erstellt haben, und einer darunter (siehe Abbildung 16.2). Die Elemente, die Sie der Menüleiste hinzufügen, sind für Ihre Anwendung die Menüs der ersten Ebene, während die Elemente, die Sie unterhalb davon einfügen, die Elemente der einzelnen Menüs sind. Geben Sie im ersten Feld z.B. File und unterhalb davon New und Exit ein. Klicken Sie in den Bereich neben dem Menü File und geben Sie View ein, um ein neues Menü anzulegen. Unterhalb von View geben Sie die neuen Elemente Red, Green und Blue ein. Während
515
Windows Forms für Fortgeschrittene
Sie an einem Toplevel-Menü arbeiten, sind die anderen geschlossen. Sie können sie jedoch wieder öffnen, falls Sie weitere Elemente hinzufügen möchten.
Abbildung 16.2: Vor-Ort-Bearbeitung des Menüs
Ebenso wie bei anderen Steuerelementen sollten Sie auch bei den Menüelementen eine allgemeine Namenskonvention einhalten. In der heutigen Lektion schlagen wir Ihnen die folgende Namenskonvention vor: 쐽
Toplevel-Menüelemente sollten mit dem aus drei Buchstaben bestehenden Präfix mnu beginnen, dem der Text des Menüs folgt, also z.B. mnuFile, mnuEdit, mnuHelp.
쐽
Die Namen der Kindmenüelemente sollten mit dem Namen des jeweils übergeordneten Toplevel-Menüs beginnen, dem der Text des Menüelements folgt. Sie können den Text abkürzen oder nur einen Teil davon verwenden, sofern der Name dennoch eindeutig bleibt. Beispiele für solche Elementnamen sind mnuFileOpen, mnuHelpAbout oder mnuWindowHorizontal. Die Namen doppelter Menüelemente wie z.B. der Separatoren (die horizontalen Linien, die einzelne Abschnitte eines Menüs voneinander trennen) sollten am Ende eine Zahl enthalten, sofern Sie ihnen keine eindeutigen Namen geben können.
Tastatur und Menü Menüs ermöglichen zwar den Zugriff auf Funktionsmerkmale, aber sie können den Zugriff auch auf einige Anwender beschränken. Menüs sind an sich durchaus nützlich, können aber Anwender abschrecken, denen es an der nötigen Koordination oder Fähigkeit fehlt,
516
Menüs
eine Maus zu verwenden. Und fortgeschrittene Anwender möchten vielleicht nicht ständig zwischen der Tastatur und der Maus hin- und herwechseln. Diese beiden Personengruppen sollten Sie im Gedächtnis behalten, wenn Sie Menüs für Ihre Anwendungen entwerfen. Es gibt zwei Möglichkeiten, den Menüs eine Tastaturunterstützung hinzuzufügen. Erstens sollten Sie allen Menüelementen Zugriffstasten hinzufügen. Die Zugriffstasten sind die unterstrichenen Menüelemente, z.B. das (D) für das Menü DATEI. Die Zugriffstasten ermöglichen es denjenigen Anwendern, die die Maus nicht verwenden können oder keine Maus haben, Ihre Menüs zu benutzen. Im Allgemeinen ist der erste Buchstabe eines Menüs oder Menüelementnamens die Zugriffstaste. Allerdings müssen Sie möglicherweise einen anderen Buchstaben des Namens verwenden, damit nicht mehrere Menüelemente dieselbe Zugriffstaste haben. In vielen deutschen Microsoft-Produkten ist die Zugriffstaste für das Menü FENSTER die Taste (F), während das Menü FORMAT die Taste (T) verwendet. Klicken Sie das Menü FILE an, das Sie gerade erstellt haben. Setzen Sie die Eigenschaft Text auf &File. Unter dem F erscheint nun ein Unterstrich. Sie können diesen Unterstrich
auch hinzufügen, indem Sie ein Menüelement einmal anklicken, um es auszuwählen, und es dann erneut anklicken, damit Sie den Text bearbeiten können. Fügen Sie vor dem Buchstaben, den Sie als Zugriffstaste für dieses Menüelement verwenden möchten, das Zeichen & ein. Versehen Sie alle Menüs, die Sie bisher erstellt haben, mit den in Tabelle 16.1 gezeigten Zugriffstasten. Menüelement
Zugriffsschlüssel
FILE
(F)
NEW
(N)
EXIT
(X)
VIEW
(V)
RED
(R)
GREEN
(G)
BLUE
(B)
Tabelle 16.1: Zugriffstasten im Menü-Beispiel
Bei dieser Liste von Zugriffstasten wird Ihnen auffallen, dass fast alle die Anfangsbuchstaben des jeweiligen Menünamens sind. Die einzige Ausnahme ist das Element EXIT, für das die Taste (X) die übliche Zugriffstaste ist. Es gibt noch eine zweite Möglichkeit, die Menüs um eine Tastaturunterstützung zu erweitern, nämlich mithilfe von Tastenkombinationen. Diese werden zuweilen auch als Hotkeys bezeichnet. Tastenkombinationen sind normalerweise mit Funktionstasten (z.B. (F1) für
517
Windows Forms für Fortgeschrittene
die Hilfe) und Tasten verbunden, die gleichzeitig mit der Steuerungstaste ((Strg)) gedrückt werden. So ist (Strg)+(C) z.B. die übliche Tastenkombination für den Kopierbefehl. Mithilfe der Tastenkombinationen können die Anwender schnell auf häufig benötigte Befehle zugreifen. Sie sind zwar nicht erforderlich, erleichtern dem Anwender aber die Arbeit. Dies gilt insbesondere für die Tastenkombinationen, die ausschließlich die Tastatur oder fortgeschrittene Anwender betreffen. Normalerweise ist zwar nicht mit jedem Menüelement eine Tastenkombination verbunden, für häufig verwendete Menüelemente sollten Sie aber dennoch eine Tastenkombination anbieten. Außerdem sollten Sie sie zu denjenigen Menüs hinzufügen, für die es in den meisten Anwendungen Tastenkombinationen gibt. Beispiele hierfür sind die Befehle KOPIEREN, AUSSCHNEIDEN und EINFÜGEN. Mit der Eigenschaft Shortcut fügen Sie die Tastenkombinationen zu den Menüelementen hinzu. Wählen Sie in der Dropdown-Liste den Eintrag Shortcut und fügen Sie die in Tabelle 16.2 gezeigten Tastenkombinationen hinzu. Menüelement
Tastenkombination
NEW
StrgN
EXIT
StrgQ
RED
StrgR
GREEN
StrgG
BLUE
StrgB
Tabelle 16.2: Tastenkombinationen
Bei dieser Liste fällt Ihnen wahrscheinlich auf, dass nur eine Tastenkombination aus dem Rahmen fällt, nämlich die für EXIT. Wir haben (Strg)+(Q) anstelle von (Strg)+(X) gewählt, da (Strg)+(X) normalerweise mit dem Befehl AUSSCHNEIDEN verbunden ist. Die gewählten Tastenkombinationen erscheinen nicht zur Entwurfszeit in den Menüelementen (siehe Abbildung 16.3), sondern erst dann, wenn die Anwendung ausgeführt wird (siehe Abbildung 16.4).
Code hinzufügen Sowohl Toplevel-Menüs als auch Menüelemente unterstützen eine Reihe von Ereignissen. Normalerweise wählen Sie aber nur das Ereignis Click. Dieses Ereignis tritt dann auf, wenn der Anwender ein Menüelement auswählt. Fügen Sie diesem Ereignisbehandler Code hinzu, der die passende Aktion ausführt. Klicken Sie die Menüelemente doppelt an, um das Codefenster zu öffnen, und fügen Sie dem Ereignis Click Code hinzu. Fügen Sie dem Formular den Code im Listing 16.1 hinzu.
518
Menüs
Abbildung 16.3: Tastenkombinationen zur Entwurfszeit
Abbildung 16.4: Tastenkombinationen zur Laufzeit Listing 16.1: Code für das Menü-Beispiel 1 2 3 4 5 6 7 8 9 10
Private Sub mnuFileNew_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles mnuFileNew.Click Dim frmNew As New frmMenu() frmNew.Show() End Sub Private Sub mnuFileExit_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles mnuFileExit.Click Me.Close() End Sub Private Sub mnuViewRed_Click(ByVal sender As System.Object, _
519
Windows Forms für Fortgeschrittene
11 12 13 14 15 16 17 18 19 20 21
ByVal e As System.EventArgs) Handles mnuViewRed.Click Me.BackColor = Color.Red End Sub Private Sub mnuViewGreen_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles mnuViewGreen.Click Me.BackColor = Color.Green End Sub Private Sub mnuViewBlue_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles mnuViewBlue.Click Me.BackColor = Color.Blue End Sub
Der Ereignisbehandler mnuFileNew_Click bezieht sich auf das Menüelement NEW im Menü FILE. Der Code für dieses Ereignis (Zeilen 1 bis 5) erzeugt eine neue Instanz des aktuellen Formulars und zeigt sie an. Dadurch können Sie mehrere Exemplare des Formulars gleichzeitig betrachten. Der Code für das Ereignis mnuFileExit_Click (für das Element EXIT im Menü FILE; die Zeilen 6 bis 9) schließt das aktive Formular wieder. Die Anwendung endet, wenn das letzte Formular geschlossen ist. Die letzten drei Ereignisbehandler beziehen sich auf die Menüelemente RED, GREEN und BLUE. Sie setzen die Hintergrundfarbe des aktiven Formulars auf die gewünschte Farbe. Kompilieren Sie die Anwendung und führen Sie sie aus. Öffnen Sie mit dem Menüelement FILE, NEW (oder der Tastenkombination (Strg)+(N)) mehrere Ansichten des Menüs. Versuchen Sie mit den Menüelementen RED, GREEN und BLUE die Farbe des Formulars zu ändern. (Denken Sie daran, dass es auch für diese Elemente Tastenkombinationen gibt.) Die Abbildung 16.5 zeigt die Anwendung, während sie ausgeführt wird. Schließen Sie mit dem Menüelement EXIT alle Formulare, um die Anwendung zu beenden.
Verbesserungsvorschläge Wie Sie gesehen haben, bieten Menüs einen allgemeinen Einblick in die Funktionsmerkmale eines Programms. Allerdings sind Menüs nützlicher, wenn die Anwender die Menübefehle bei allen Programmen an derselben Stelle finden. Daher ist es sinnvoll, Ihre Menüs ähnlich wie in anderen Anwendungen einzurichten. Dies hilft den Anwendern dabei, sich in Ihrer Anwendung zurechtzufinden, da sie Befehle schneller finden, wenn diese sich dort befinden, wo die Anwender sie zu sehen erwarten. Dies bedeutet, dass Sie einige häufig verwendete Menüs haben sollten, die sich an derselben Stelle befinden wie in anderen Programmen. Die Abbildung 16.6 zeigt einige gebräuchliche Menüleisten aus Programmen wie z.B. Word, Excel, PowerPoint, dem Windows-Rechner und Visual Basic .NET.
520
Menüs
Abbildung 16.5: Das Menü-Beispiel ausführen
Versuchen Sie Anwendungen wie diese zu emulieren, falls dies bei Ihrer Anwendung angemessen ist. So sollte die Menüleiste die Menüs DATEI, BEARBEITEN, ANSICHT, EXTRAS und HILFE in genau dieser Reihenfolge enthalten.
Abbildung 16.6: Häufig verwendete Menüleisten
Wenn Ihre Anwendung natürlich keine Dateien verwendet oder mehrere Ansichten bietet, sind einige der Menüs nicht sonderlich sinnvoll. Die Anwendung RECHNER, die in Windows enthalten ist, hat z.B. kein DATEI-Menü. Und in vielen Anwendungen sind gebräuchliche Menüs wie z.B. BEARBEITEN, EXTRAS oder HILFE überflüssig. Wenn Sie bestimmte Menüs nicht benötigen, brauchen Sie sie auch nicht in Ihre Anwendungen ein-
521
Windows Forms für Fortgeschrittene
zufügen; aber wenn Sie die Menüs einfügen, dann sollten diese sich dort befinden, wo der Anwender sie erwartet. Ebenso wie die gebräuchlichen Toplevel-Menüs sollten Sie auch die anwendungsspezifischen Menüs so anlegen, dass sie denen in anderen Anwendungen gleichen, die der Anwender vielleicht kennt. Das Menü DATEI sollte z.B. immer die Elemente NEU, ÖFFNEN, SPEICHERN und BEENDEN enthalten. Das Menü BEARBEITEN sollte immer die Elemente AUSSCHNEIDEN, KOPIEREN und EINFÜGEN enthalten. Wenn Sie Ihre Menüs entwerfen, sollten Sie sich unbedingt bei anderen Anwendungen ansehen, wie häufig verwendete Menüs aufgebaut sind. Verwenden Sie gängige Menüstrukturen, um den Anwendern das Auffinden von Menübefehlen zu erleichtern. Halten Sie nicht krampfhaft an dieser Regel fest. Wenn ein Menü ungeeignet ist, sollten Sie nicht zögern, es wegzulassen. Wenn Ihre Anwendung z.B. keine Dateien verwendet, benötigen Sie auch kein DATEI-Menü. Allerdings sollten Sie das Element BEENDEN trotzdem immer als letztes Element des ersten Menüs einfügen.
16.2 Multiple Document Interface-Programme Häufig muss man ein Programm erstellen, das mit mehreren ähnlichen Elementen zu tun hat. Dies könnte z.B. ein Texteditor sein, bei dem mehrere Fenster gleichzeitig geöffnet sein müssen, oder ein Datenbankwerkzeug, mit dem der Anwender die Ergebnisse mehrerer Anfragen gleichzeitig betrachten kann. Derartige Programme können Sie als MDIAnwendungen (Multiple Document Interface) entwerfen. Dies ist zwar keine zwingende Anforderung, aber Sie erleichtern es damit den Anwendern, die Beziehung zwischen den geöffneten Fenstern zu erkennen. Die Abbildung 16.7 zeigt eine MDI-Anwendung.
Was ist das Multiple Document Interface? Am einfachsten lässt sich das MDI beschreiben, wenn wir es mit einer normalen, also einer SDI-Anwendung (Single Document Interface) vergleichen. In einer normalen Anwendung wie z.B. NotePad wird jedes geöffnete Dokument in einem eigenen Fenster gezeigt. So enthält ein Formular ein einziges Dokument. Bei einer MDI-Anwendung könnte man meinen, dass ein Formular mehrere Dokumente enthält. Im Prinzip stimmt das auch. Allerdings wäre eine bessere Definition, dass ein Fenster eine Reihe anderer Formulare
522
Multiple Document Interface-Programme
Abbildung 16.7: Multiple Document Interface-Anwendung
enthält, von denen jedes wiederum ein einzelnes Dokument enthält. Ältere Versionen von Microsoft Word arbeiteten mit dieser Strategie. Auch Visual Basic .NET selbst ist eine MDI-Anwendung: Mehrere Dokumente können gleichzeitig geöffnet sein und jedes wird in einem eigenen Fenster innerhalb des Elternfensters von Visual Basic .NET angezeigt.
Das Elternformular einfügen Der einzige Unterschied zwischen der Erstellung einer normalen Windows-Anwendung und einer MDI-Anwendung ist der MDI-Container. Dieser Container ist das Elternformular und damit das einzige Formular in Ihrer Anwendung, das seine Kinder enthält. Enthaltene Formulare können nicht aus dem MDI-Elternformular heraus an eine andere Stelle verschoben werden. Dies hat zwei Gründe: 쐽
Es verringert die Gefahr, dass unerfahrene Anwender ein oder mehrere Formulare verlegen. Anwender, die mit der Navigation in Windows nicht sehr vertraut sind, verlieren manchmal ein Formular, wenn sie ein anderes auswählen, und sind dann nicht im Stande, das erste Formular mithilfe der Taskleiste wiederzufinden.
쐽
Es unterstreicht die Vorstellung, dass die Kindformulare untereinander und mit dem Elternformular verwandt sind. Wenn die Formulare nicht unabhängig voneinander verschoben werden können, dann stellt man sie sich als voneinander abhängig vor.
Im Allgemeinen sind alle anderen Formulare in der Anwendung Kinder des Elternformulars. Allerdings kann es vorkommen, dass nicht alle Formulare Kindformulare sein sollen. Dies ist z.B. dann der Fall, wenn das Formular nicht logisch im Elternformular enthalten
523
Windows Forms für Fortgeschrittene
ist. So ist z.B. in Visual Basic .NET ein Codefenster logisch in Visual Basic .NET enthalten und sollten daher ein Kindformular sein. Ein geöffnetes DATEI-Dialogfenster ist hingegen nicht logisch in der Anwendung enthalten und ist daher auch kein Kindfenster. Ein neues MDI-Elternformular erstellen Sie, indem Sie die Eigenschaft IsMdiContainer auf True setzen. Damit fügen Sie dem Formular ein neues Steuerelement hinzu, nämlich MdiClient. Dieses Steuerelement füllt das gesamte Formular und ist der Container der Kindformulare. Sobald Sie ein MDI-Elternformular haben, können Sie Kindformulare dieses Formulars anlegen, indem Sie deren Eigenschaft MdiParent setzen, bevor Sie sie anzeigen. Setzen Sie diese Eigenschaft so, dass sie auf das Elternformular verweist. Das Listing 16.2 zeigt hierzu ein Beispiel. Listing 16.2: Ein Kindformular erstellen 1 2 3 4 5 6 7 8 9
Private Sub mnuFileOpen_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles mnuFileOpen.Click Dim oForm As New frmView() oForm.MdiParent = Me oForm.Show() End Sub
Um ein anderes Formular anzuzeigen, müssen Sie zuerst eine neue Instanz des gewünschten Formulars (Zeile 6) erzeugen. Dann setzen Sie die Eigenschaft MdiParent auf das Elternformular. Die Eigenschaft IsMdiContainer dieses Formulars muss auf True gesetzt sein. Zuletzt wird das Formular angezeigt (Zeile 8). Es darf nicht aus dem Rahmen des Elternformulars herausgenommen werden. Obwohl MDI-Anwendungen viele Vorteile bieten, sollten die meisten Ihrer Anwendungen das MDI nicht verwenden. Microsoft hat die Verwendung von MDI-Anwendungen stark eingeschränkt, weil viele unerfahrene Anwender sie verwirrend finden. Sowohl Microsoft Word als auch Excel waren früher MDIAnwendungen, sind heute jedoch SDI-Anwendungen, die pro Dokument ein Fenster erstellen. Daher sollten Sie das MDI wie jedes Verfahren nur dort anwenden, wo es angemessen ist, und auch nur dann, wenn die Anwender es verstehen können.
524
Multiple Document Interface-Programme
Das MDI und Menüs Wenn ein einzelnes Fenster mehrere unterschiedliche Kindfenster enthalten kann, benötigen MDI-Anwendungen natürlich spezielle Menüeinstellungen. Diese erleichtern die Organisation der Kindfenster. Im Allgemeinen haben MDI-Anwendungen das Menü FENSTER, das den Zugriff auf diese Befehle ermöglicht. Das Standardmenü FENSTER enthält vier Befehle: 쐽
UNTEREINANDER (TILE HORIZONTALLY) ordnet die Kindfenster so an, dass jedes ein horizontaler Streifen über den Bildschirm ist (siehe Abbildung 16.8). Dies ist dann nützlich, wenn Sie Informationen vergleichen müssen, bei denen die ganze Zeile sichtbar sein muss. Ein Beispiel hierfür sind zwei Textverarbeitungsprogramme.
Abbildung 16.8: Der Befehl UNTEREINANDER wurde auf Kindformulare angewandt. 쐽
NEBENEINANDER (TILE VERTICALLY) ordnet die Kindfenster so an, dass jedes ein vertikaler Streifen über den Bildschirm ist und die einzelnen Streifen Seite an Seite liegen. Dies ist dann nützlich, wenn Sie kleine Bereiche zweier oder mehrerer Dokumente vergleichen oder zwei Dokumente schnell auf Änderungen hin durchsuchen möchten.
쐽
ÜBERLAPPEND (CASCADE) ordnet die Kindfenster so an, dass sie alle dieselbe Größe haben. Jedes Fenster befindet sich etwas unterhalb und rechts vom dahinter liegenden Fenster. Dadurch entsteht eine Anordnung, bei der Sie die Titelleisten aller Kindfenster gleichzeitig sehen können (siehe Abbildung 16.9). Dies ist dann nützlich, wenn Sie wissen möchten, welche Fenster gerade geöffnet sind.
525
Windows Forms für Fortgeschrittene
Abbildung 16.9: Überlappend angeordnete Kindformulare 쐽
SYMBOLE ANORDNEN (ARRANGE ICONS) organisiert alle minimierten Fenster so, dass sie in Reihen am unteren Rand des Elternfensters angeordnet werden (siehe Abbildung 16.10). Dies ist dann nützlich, wenn Sie Kindfenster organisieren möchten, die Sie derzeit nicht verwenden.
Abbildung 16.10: Der Befehl SYMBOLE ANORDNEN wurde auf Kindformulare angewandt.
526
Multiple Document Interface-Programme
Zusätzlich zu den gebräuchlichen Elementen enthält das Menü FENSTER (WINDOW) normalerweise noch eine Liste aller Kindfenster (siehe Abbildung 16.11), die so genannte Fensterliste. Sie führt alle geöffneten Kindfenster auf und kennzeichnet das aktive Kindfenster durch ein Häkchen. Den Code für die Einrichtung dieser Liste brauchen Sie nicht selbst zu schreiben und zu pflegen, da es mit Visual Basic .NET ganz einfach ist, die Liste einzufügen. Setzen Sie einfach die Eigenschaft MdiList für das Toplevel-Menü, das die Fensterliste enthalten soll, auf True.
Abbildung 16.11: Die Fensterliste
Damit Sie sehen, wie eine einfache MDI-Anwendung erstellt wird, wollen wir eine einfache MDI-Anwendung für einen Bildbetrachter einrichten. Erstellen Sie eine neue Windows-Anwendung und nennen Sie sie MultiPicView. Setzen Sie den Namen des Formulars im Projektmappen-Explorer auf MdiParent. Öffnen Sie die Codeansicht für das Formular und ändern Sie den Klassennamen von Form1 auf MdiParent. Schließlich öffnen Sie noch die Projekteigenschaften und setzen das Startobjekt auf MdiParent. Setzen Sie die Eigenschaften des Formulars so, wie es die Tabelle 16.3 zeigt. Eigenschaft
Wert
Size
480, 360
Text
Picture View
IsMdiContainer
True
Tabelle 16.3: Eigenschaften des MDI-Elternformulars
Der Anwender bekommt damit ein Menü, mit dem er neue Dateien öffnen kann, um sie zu betrachten. Fügen Sie dem MDI-Elternformular das Steuerelement MainMenu und OpenFileDialog hinzu. Geben Sie dem MainMenu-Objekt den Namen mnuMain und richten Sie das Menü wie in Tabelle 16.4 gezeigt ein. Setzen Sie die Eigenschaften von OpenFileDialog wie in Tabelle 16.5. Menüelement
Eigenschaft
Wert
Toplevel
(Name)
mnuFile
Text
File
Tabelle 16.4: Menüstruktur für das MDI-Elternformular
527
Windows Forms für Fortgeschrittene
Menüelement
Eigenschaft
Wert
Unter FILE
(Name)
mnuFileOpen
Text
&Open
Shortcut
Ctrl+O
(Name)
mnuFileSep
Text
-
(Name)
mnuFileExit
Text
E&xit
Shortcut
Ctrl+Q
(Name)
mnuWindow
Text
&Window
MdiList
True
(Name)
mnuWindowHorizontal
Text
Tile &Horizontally
(Name)
mnuWindowVertical
Text
Tile &Vertically
(Name)
mnuWindowCascade
Text
&Cascade
(Name)
mnuWindowArrange
Text
&Arrange Icons
Unter OPEN Unterhalb des Trennzeichens
Toplevel-Menü
Unter WINDOW Unter TILE HORIZONTAL Unter TILE VERTICALLY Unter CASCADE
Tabelle 16.4: Menüstruktur für das MDI-Elternformular (Forts.) Eigenschaft
Wert
(Name)
DlgOpen
Filter
Graphics files|*.gif; *.bmp; *.jpg|All files|*.*
Tabelle 16.5: Eigenschaften von OpenFileDialog
Das Menü FILE dient dazu, neue Bilddateien zu öffnen und die Anwendung zu schließen. Fügen Sie dem Formular für die Menüelemente den Code im Listing 16.3 hinzu. Listing 16.3: Code für die Befehle im Menü File für MultiPicView 1 2 3 4
528
Private Sub mnuFileOpen_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles mnuFileOpen.Click
Multiple Document Interface-Programme
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
If dlgOpen.ShowDialog() = DialogResult.OK Then Dim oForm As New frmView() oForm.MdiParent = Me oForm.Picture = Image.FromFile(dlgOpen.FileName) oForm.Show() End If End Sub Private Sub mnuFileExit_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles mnuFileExit.Click Me.Close() End Sub
Der Code sollte Ähnlichkeit mit dem haben, den Sie bereits gesehen haben. Der Ereignisbehandler FILE, OPEN zeigt OpenFileDialog an (Zeile 6). Wenn der Anwender eine Datei auswählt, erzeugt der Code ein neues frm.View-Formular (dazu kommen wir gleich) und setzt dessen Eigenschaft MdiParent auf das aktuelle Formular (Zeile 8). Anschließend setzt er die Formulareigenschaft Picture auf die Bilddatei und zeigt das neue Formular an (Zeile 10). In Zeile 9 laden wir mit der Methode FromFile der Klasse Image das Bild, d.h., wir rufen das in einer Datei gespeicherte Bild ab. Der Ereignisbehandler FILE, EXIT, der das aktuelle Formular schließt, ist sogar noch einfacher. Er sorgt dafür, dass auch alle geöffneten Kindfenster geschlossen werden und die Anwendung beendet wird. Sie sollten nicht nur für das Menü FILE Code schreiben, sondern auch für die Befehle des Menüs WINDOWS. Das Listing 16.4 zeigt diesen Code. Listing 16.4: Der Code für die Befehle des Menüs Windows von MultiPicView 22 23 24 25 26 27 28 29 30
Private Sub mnuWindowHorizontal_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles mnuWindowHorizontal.Click Me.LayoutMdi(MdiLayout.TileHorizontal) End Sub Private Sub mnuWindowVertical_Click( _
529
Windows Forms für Fortgeschrittene
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles mnuWindowVertical.Click Me.LayoutMdi(MdiLayout.TileVertical) End Sub Private Sub mnuWindowCascade_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles mnuWindowCascade.Click Me.LayoutMdi(MdiLayout.Cascade) End Sub Private Sub mnuWindowArrange_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles mnuWindowArrange.Click Me.LayoutMdi(MdiLayout.ArrangeIcons) End Sub
Alle vier Elemente des Menüs WINDOW führen einfach die Methode LayoutMdi des MDI-Elternformulars aus, die die gewünschte Anordnung der Kindformulare setzt. Jetzt sind Sie soweit, dass Sie das Formular erstellen können, das das Bild anzeigt. Fügen Sie dem Projekt ein zweites Formular hinzu, indem Sie das Menüelement PROJEKT, WINDOWS FORM HINZUFÜGEN wählen. Wenn Sie aufgefordert werden, einen Namen einzugeben, nennen Sie das neue Formular PictureView. Setzen Sie die Eigenschaft Text des Formulars auf Picture View. Fügen Sie dem Formular das Steuerelement PictureBox hinzu und setzen Sie die Eigenschaften dieses Steuerelements so, wie es die Tabelle 16.6 zeigt. Eigenschaft
Wert
(Name)
picView
Dock
Fill
Tabelle 16.6: Eigenschaften des Steuerelements PictureBox
Gehen Sie in die Codeansicht und fügen Sie dem Formular die öffentliche Eigenschaft Picture hinzu, die die Eigenschaft Image von PictureBox repräsentiert. Dies sollte ähnlich aussehen wie der Code in Listing 16.5.
530
Multiple Document Interface-Programme
Listing 16.5: Die Eigenschaft Picture 1 2 3 4 5 6 7 8
Public Property Picture() As Image Get Return picView.Image End Get Set(ByVal Value As Image) picView.Image = Value End Set End Property
Ebenso, wie Sie Eigenschaften haben können, die private, in der Klasse Form gespeicherte Werte repräsentieren, können Sie auch Eigenschaften haben, die Steuerelemente auf dem Formular oder Eigenschaften dieser Steuerelemente repräsentieren. In diesem Fall greifen wir mit der Eigenschaft Picture auf die Eigenschaft Image des Steuerelements picView zu. Hierfür könnten wir zwar auch das Steuerelement picView selbst Public machen, aber das würde zu weitreichende Zugriffsmöglichkeiten auf das Steuerelement zulassen. Mit unserer Lösung kann der Code das Bild leicht lesen und verändern, aber der Code in einem anderen Formular kann keine anderen Informationen über PictureBox ändern. Führen Sie die Anwendung aus. Sie sollten nun in der Lage sein, mehrere Bilddateien zu öffnen, wie es auch Abbildung 16.12 zeigt.
Abbildung 16.12: MultiPicView in Aktion
531
Windows Forms für Fortgeschrittene
16.3 Kompliziertere Windows Forms-Steuerelemente Häufig ist es zwar möglich, eine Anwendung nur mit den gängigsten Steuerelementen wie z.B. Label, TextBox und PictureBox zu erstellen, aber wahrscheinlich werden Sie gelegentlich auch andere Steuerelemente benötigen. In Visual Basic .NET stehen viele Steuerelemente zur Verfügung, die wir in diesem Buch nicht besprochen haben, und viele weitere können Sie kaufen. Es wäre schwierig, wenn nicht gar unmöglich, in einem einzigen Buch alle verfügbaren Steuerelemente zu behandeln. Erst recht können wir dies natürlich nicht in einem einzigen Abschnitt einer Lektion machen. Daher werde ich Ihnen nur einige der wichtigeren Steuerelemente zeigen und ihre Verwendung erläutern.
TreeView Das Steuerelement TreeView sollte allen bekannt sein, die schon länger mit Windows arbeiten. Dieses Steuerelement erscheint auf der linken Seite des Explorer-Fensters sowie auch an allen anderen Stellen, an denen etwas in hierarchischer Form angezeigt wird. Mit TreeView können Sie dem Anwender eine Liste von Elementen und deren Beziehungen zeigen. Das Bild, das für die einzelnen Elemente in der Liste angezeigt wird, kann geändert oder ganz weggelassen werden. Auch die Zeilen, die die einzelnen Elemente miteinander verbinden, können angepasst oder gelöscht werden. TreeView ist überall dort sinnvoll, wo Sie viele verwandte Elemente und ihre Beziehungen anzeigen müssen, z.B. damit der Anwender Verzeichnisse oder E-Mail-Ordner durchsuchen oder die Gliederung für einen Artikel anlegen kann. Die Abbildung 16.13 veranschaulicht die Verwendung des Steuerelements TreeView; sie zeigt eine Liste von Ordnern auf einer Festplatte.
Abbildung 16.13: Das Steuerelement TreeView bei der Arbeit.
532
Kompliziertere Windows Forms-Steuerelemente
Das Steuerelement TreeView hat viele Eigenschaften, Methoden und Ereignisse; Tabelle 16.7 beschreibt die gebräuchlichsten. Element
Beschreibung
Eigenschaften Checkboxes
Fügt neben jedem Element in TreeView ein Kontrollkästchen ein. Dies vereinfacht die Erstellung von Listen mit Mehrfachauswahl.
FullRowSelect
Falls diese Eigenschaft True ist, wird die ganze Zeile gewählt, wenn ein Element ausgewählt wird. Alternativ wird nur der Text für das gewählte Element ausgewählt.
Nodes
Dies ist die wichtigste Eigenschaft von TreeView. Sie enthält die Liste aller Toplevel-Elemente in TreeView.
PathSeparator
Wird verwendet, wenn der Pfad zu einem Knoten in TreeView abgerufen wird. Dieses Zeichen wird zwischen alle Knoten eingefügt. Das Standardzeichen ist der Backslash (\).
SelectedNode
Der derzeit ausgewählte Knoten in TreeView.
ShowLines
Ermittelt, ob zwischen den Knoten in TreeView Linien gezeichnet werden sollen.
ShowPlusMinus
Ermittelt, ob neben den Knoten in TreeView die Zeichen + und – angezeigt werden.
ShowRootLines
Ermittelt, ob die Toplevel-Knoten in TreeView durch Linien verbunden werden.
Sorted
Ermittelt, ob zu TreeView hinzugefügte Elemente sortiert werden sollen.
Methoden CollapseAll
Schließt alle Baumknoten.
ExpandAll
Klappt alle Baumknoten auf.
Ereignisse BeforeCollapse
Tritt auf, bevor ein Teilbaum geschlossen wird. Es kann dafür verwendet werden, das Ereignis abzubrechen oder andere Steuerelemente zu aktualisieren.
BeforeExpand
Tritt auf, bevor ein Teilbaum geöffnet wird. Dieser Ereignisbehandler ist hervorragend geeignet, die Elemente im Teilbaum zu aktualisieren, bevor sie dem Anwender angezeigt werden.
BeforeSelect
Tritt auf, bevor ein Knoten ausgewählt wird.
AfterCollapse
Tritt auf, nachdem ein Teilbaum geschlossen wurde. Diese Stelle eignet sich gut, um vom Teilbaum verwendete Ressourcen freizugeben, also z.B. eine Datenbankverbindung zu schließen.
Tabelle 16.7: Elemente von TreeView
533
Windows Forms für Fortgeschrittene
Element
Beschreibung
AfterExpand
Tritt auf, nachdem ein Teilbaum geöffnet wurde. Damit kann man andere Steuerelemente mit den neu angezeigten Baumknoten aktualisieren.
AfterSelect
Tritt auf, nachdem ein Baumknoten ausgewählt wurde. Damit kann man andere Steuerelemente auf der Grundlage des gewählten Knotens aktualisieren.
Tabelle 16.7: Elemente von TreeView
In der Tabelle 16.7 haben wir bereits erwähnt, dass Nodes die wichtigste Eigenschaft von TreeView ist. Jedes Element in dieser Sammlung ist ein TreeNode-Objekt und dieses Objekt steht bei der meisten Arbeit mit TreeView im Mittelpunkt. Die Tabelle 16.8 beschreibt die wichtigsten Eigenschaften und Methoden der Klasse TreeNode. Element
Beschreibung
Eigenschaften Checked
Wird zusammen mit der Eigenschaft Checkboxes von TreeView verwendet. Diese Eigenschaft ist True, wenn TreeNode markiert ist.
FullPath
Gibt eine Zeichenkette zurück, die alle Knoten bis hinunter zum gewählten Knoten enthält, wobei alle Knoten durch die Eigenschaft PathSeparator von TreeView voneinander getrennt sind. Normalerweise erzeugt diese Eigenschaft eine pfadähnliche Zeichenkette.
Nodes
Die Sammlung von TreeNodes dieses TreeNode.
Text
Der Text dieses TreeNode.
Methoden Collapse
Schließt den Baum, beginnt dabei beim aktuellen Knoten.
Expand
Öffnet den Baum; beginnt dabei beim ausgewählten Knoten.
Toggle
Schließt einen geöffneten oder öffnet einen geschlossenen Baum; beginnt dabei beim ausgewählten Knoten.
Tabelle 16.8: Eigenschaften und Methoden von TreeNode
Beim Steuerelement TreeView sind nicht nur TreeView selbst und die einzelnen TreeNodeObjekte wichtig, sondern auch die Objekte von TreeNodeCollection, d.h. der Sammlung, die von den Nodes-Eigenschaften repräsentiert wird. Eine dieser Sammlungen befindet sich auf der obersten Ebene der TreeView und wird von der Eigenschaft Nodes von TreeView widergespiegelt. Außerdem hat jeder TreeNode in dieser Sammlung eine eigene NodesSammlung, die alle Kindknoten dieses TreeNode darstellt. Die Tabelle 16.9 beschreibt einige der Eigenschaften und Methoden dieser wichtigen Sammlung.
534
Kompliziertere Windows Forms-Steuerelemente
Element
Beschreibung
Eigenschaften Count
Ermittelt, wie viele Elemente sich in der Sammlung befinden.
Item
Gibt einen der TreeNodes aus der Sammlung zurück und kann geschachtelt sein. So wäre z.B. tvwFolders.Nodes.Item(3).Nodes.Item(1)der zweite Knoten unterhalb des vierten Knotens in TreeView. (Denken Sie daran, dass alle Sammlungen bei 0 beginnen.)
Methoden Add
Fügt am Ende der aktuellen Sammlung einen neuen Knoten ein. Dies kann entweder ein tatsächliches TreeNode-Objekt oder einfach der Text sein, der für den neuen Knoten angezeigt werden soll.
Clear
Löscht alle Knoten aus der ausgewählten Sammlung.
IndexOf
Gibt die Positionsnummer des ausgewählten Knotens in der Sammlung zurück.
Insert
Fügt an einer definierten Position einen neuen TreeNode in die Liste der Knoten ein.
Remove
Löscht einen angefragten Knoten aus der Sammlung.
RemoveAt
Löscht einen Knoten nach dem Index aus der Sammlung.
Tabelle 16.9: Methoden und Eigenschaften von TreeNodeCollection
ListView Das Steuerelement ListView sollte den meisten Windows-Anwendern als die linke Seite des Explorer-Fensters bekannt sein. Es hat insofern Ähnlichkeit mit dem Steuerelement ListBox, als es mehrere Elemente speichern kann. Es bietet jedoch mehr Funktionalität, da die Elemente auf verschiedene Art angezeigt werden können. Die Elemente in der Liste können als Liste, mit großen oder kleinen Symbolen dargestellt werden. Außerdem kann eine Liste mit zusätzlichen Einzelheiten zu den Elementen angezeigt werden. Schließlich können Sie ListView auch anstelle des Steuerelements Grid verwenden, da es ebenfalls Rasterlinien anzeigen kann. Die Tabelle 16.10 zeigt einige der wichtigsten Eigenschaften, Methoden und Ereignisse des Steuerelements ListView. Element
Beschreibung
Eigenschaften Checkboxes
Ermittelt, ob bei den einzelnen Elementen in der Liste Kontrollkästchen angezeigt werden. Dies ist eine hervorragende Art, eine Liste mit Mehrfachauswahl zu erstellen.
Tabelle 16.10: Wichtige Eigenschaften, Methoden und Ereignisse des Steuerelements ListView
535
Windows Forms für Fortgeschrittene
Element
Beschreibung
CheckIndices
Gibt die Indexwerte aller ausgewählten Elemente in ListView zurück. Dadurch wissen Sie, welche Elemente ausgewählt sind, wenn Sie ListView für Listen mit Mehrfachauswahl verwenden.
Columns
In der Detailansicht von ListView kann die Liste in Spalten angeordnet werden.
FullRowSelect
Ermittelt, ob die ganze Zeile oder nur der Text des Elements markiert wird, wenn ein Element ausgewählt wird.
GridLines
Ermittelt, ob in der Detailansicht von ListView Gitterlinien angezeigt werden. Dadurch können Sie dafür sorgen, dass ListView wie ein einfaches Grid-Steuerelement aussieht.
Items
Repräsentiert die Sammlung aller Elemente in ListView.
View
Ermittelt, wie ListView angezeigt wird: große Symbole, kleine Symbole, Liste oder Details.
Methoden Clear
Löscht alle Elemente aus ListView.
Ereignisse ColumnClick
Tritt auf, wenn der Anwender auf einen Spaltenkopf klickt. Damit kann man die Sortierreihenfolge der Spalte ändern.
SelectedIndexChanged
Tritt auf, wenn der Anwender ein neues Element in ListView auswählt. Damit können Sie andere Steuerelemente auf der Grundlage dieser Auswahl aktualisieren.
Tabelle 16.10: Wichtige Eigenschaften, Methoden und Ereignisse des Steuerelements ListView (Forts.)
Ebenso wie die Sammlung Nodes die wichtigste Eigenschaft des Steuerelements TreeView ist, ist die Sammlung Items die wichtigste Eigenschaft des Steuerelements ListView. Alle einzelnen Elemente in der Sammlung Items sind ListViewItems. Diese Objekte haben wiederum Methoden und Eigenschaften, die das darstellen, was bei den einzelnen Elementen in ListView möglich ist. Die Tabelle 16.11 beschreibt die wichtigsten Eigenschaften. Element
Beschreibung
Eigenschaften Checked
Wird zusammen mit der Eigenschaft Checkboxes von ListView verwendet. Diese Eigenschaft ist True, wenn das Kontrollkästchen des jeweiligen Elements markiert ist.
Index
Gibt die Position des Elements in ListView zurück.
Tabelle 16.11: Eigenschaften von ListViewItem
536
Kompliziertere Windows Forms-Steuerelemente
Element
Beschreibung
Selected
Ermittelt, ob das ListViewItem ausgewählt ist.
SubItems
Wird zusammen mit der Detailansicht und Columns verwendet. Diese Eigenschaft enthält die Informationen, die in den zusätzlichen Spalten angezeigt werden sollen.
Tabelle 16.11: Eigenschaften von ListViewItem (Forts.)
Ebenso wie TreeView TreeNodeCollection-Objekte hat, die die Sammlungen von TreeNodeObjekten widerspiegeln, hat ListView ListViewItemCollection-Objekte. Diese Sammlung ist allerdings weniger wichtig als die entsprechende Eigenschaft von TreeView, da ListView nicht dieselbe Hierarchie hat wie die Knoten. Statt dessen gibt es nur eine wesentliche ListViewItemCollection. Die Tabelle 16.12 beschreibt einige der wichtigsten Eigenschaften und Methoden dieser Sammlung. Element
Beschreibung
Eigenschaften Count
Die Anzahl der Elemente in ListViewCollection.
Item
Gibt das ListViewItem an der angefragten Position zurück.
Methoden Add
Fügt der Sammlung am Ende ein neues ListViewItem hinzu. Das neue Element kann entweder ein ListViewItem-Objekt oder der Text sein, der für das Element angezeigt werden soll.
Clear
Löscht alle Elemente aus der Sammlung.
IndexOf
Gibt den Index oder die Position des angefragten ListViewItem zurück.
Insert
Fügt der Sammlung an der angefragten Position ein neues ListViewItem hinzu.
Remove
Löscht ein ListViewItem aus der Sammlung.
RemoveAt
Löscht ein Element anhand seiner Position aus der Sammlung.
Tabelle 16.12: Eigenschaften und Methoden von ListViewItemCollection
Splitter Die Steuerelemente TreeView und ListView kannten Sie wahrscheinlich bereits; das Steuerelement Splitter haben Sie dagegen wahrscheinlich noch nicht gesehen, wohl aber verwendet. Das Steuerelement Splitter dient dazu, die Größe von Kindsteuerelementen in einem Fenster zur Laufzeit zu ändern. Wenn Sie z.B. im Explorer die Maus über die graue Linie bewegen, die TreeView und ListView trennt, können Sie dieses Trennzeichen
537
Windows Forms für Fortgeschrittene
horizontal verschieben und damit die relative Größe der beiden Steuerelemente verwenden (siehe Abbildung 16.14). Dies ist das Steuerelement Splitter, ein einfaches Steuerelement, dessen einziger Zweck darin besteht, diese Art der dynamischen Größenänderung zu ermöglichen. Es wird zusammen mit zwei verankerten Steuerelementen verwendet. Eines dieser Steuerelemente ist an einer Seite des Elternformulars angedockt, während die Eigenschaft Dock des zweiten Steuerelements auf Fill gesetzt ist.
Abbildung 16.14: Das Steuerelement Splitter verwenden
Die Eigenschaften des Steuerelements Splitter werden Sie nur selten benötigen. Allerdings gibt es einige Eigenschaften, die Sie eventuell setzen müssen, um das Verhalten des Steuerelements an Ihre Bedürfnisse anzupassen. Die Tabelle 16.13 beschreibt diese Eigenschaften. Element
Beschreibung
BackColor
Definiert bei Splitter ebenso wie bei anderen Steuerelementen die Hintergrundfarbe. Diese Eigenschaft ist nützlich, da das Steuerelement Splitter es dem Anwender damit erleichtert, das Vorhandensein des Fensterteilers zu erkennen.
Dock
Diese Eigenschaft setzt die Richtung von Splitter. Wenn Splitter auf den rechten oder linken Rand gesetzt ist, ändert es die Höhe der beiden Steuerelemente. Wenn Splitter am oberen oder unteren Rand des Formulars angedockt ist, ändert es die Breite der beiden Steuerelemente.
MinSize
Setzt eine Minimalgröße für das Steuerelement, das nicht auf Fill gesetzt ist. Dies ist nützlich, falls Sie verhindern möchten, dass die Informationen auf einer Seite von Splitter nicht mehr zu erkennen sind.
Tabelle 16.13: Wichtige Eigenschaften des Steuerelements Splitter
538
Kompliziertere Windows Forms-Steuerelemente
Damit Sie sehen, wie Sie mit diesen drei Steuerelementen rasch eine Anwendung erstellen können, wollen wir einen Explorer-Klon anlegen, mit dem der Anwender durch die Laufwerke navigieren und die dort gespeicherten Dateien sehen kann. Erstellen Sie eine neue Windows-Anwendung. Ich habe meine Pioneer genannt, aber Sie können Ihrer Anwendung natürlich einen Namen Ihrer Wahl geben. Setzen Sie den Dateinamen des erstellten Formulars auf PioneerForm und die erstellte Klasse auf frmPioneer. Setzen Sie das Startobjekt des Projekts im Dialogfenster PROJEKT EIGENSCHAFTEN auf frmPioneer. Kompilieren Sie die Anwendung einmal, um sicherzugehen, dass sie keine Fehler enthält. Die Benutzeroberfläche ist spartanisch: Sie besteht lediglich aus den Steuerelementen TreeView, ListView und Splitter (siehe Abbildung 16.15).
Abbildung 16.15: Benutzeroberfläche für Pioneer
Ändern Sie die Größe des Formulars, sodass es größer als in der Standardeinstellung ist. Setzen Sie diese und die übrigen Eigenschaften so, wie Tabelle 16.14 es zeigt. Eigenschaft
Wert
Name
FrmPioneer
Text
Pioneer
Size
480, 330
Tabelle 16.14: Eigenschaften des Pioneer-Formulars
539
Windows Forms für Fortgeschrittene
Fügen Sie dem Formular das Steuerelement TreeView hinzu. Setzen Sie die Eigenschaften auf die in Tabelle 16.15 genannten Werte. Eigenschaft
Wert
Name
TvwFolders
Dock
Left
Width
121
Tabelle 16.15: Eigenschaften des Steuerelements TreeView
Fügen Sie dem Formular das Steuerelement Splitter hinzu und setzen Sie den Namen auf splView. Bei den übrigen Eigenschaften sind die Standardwerte in Ordnung. Fügen Sie dem Formular das Steuerelement ListView hinzu und setzen Sie seine Eigenschaften auf die in Tabelle 16.16 genannten Werte. Eigenschaft
Wert
Name
LvwFiles
Dock
Fill
View
List
Tabelle 16.16: Eigenschaften des Steuerelements ListView
Wenn das Formular zum ersten Mal geladen wird, sollte das Steuerelement TreeView die verfügbaren Laufwerke anzeigen (siehe Listing 16.6). Wenn Sie die einzelnen Laufwerke auswählen, sollten die Verzeichnisse auf diesem Laufwerk in TreeView und die Dateien in den einzelnen Verzeichnissen in ListView angezeigt werden. Anfangs ist jedoch nur TreeView mit den verfügbaren Laufwerken besiedelt. Da dieser Code das Directory-Objekt verwendet, sollten Sie am Anfang der Codeansicht Imports System.IO einfügen. Dadurch können Sie das Directory-Objekt verwenden, ohne es vollständig qualifizieren zu müssen (d.h. ohne System.IO.Directory zu schreiben). Listing 16.6: Eine Liste der Laufwerke zu TreeView hinzufügen 1 2 3 4 5 6 7 8 9 10
540
Private Sub PioneerForm_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles MyBase.Load 'Füge der Baumansicht Laufwerke hinzu. Dim sDrives() As String = Directory.GetLogicalDrives() Dim sDrive As String Dim tvwNode As TreeNode For Each sDrive In sDrives
Kompliziertere Windows Forms-Steuerelemente
11 12 13 14 15 16
tvwNode = tvwFolders.Nodes.Add(sDrive) 'Füge eine Knotenattrappe ein. tvwNode.Nodes.Add("dummy") Next End Sub
Die Methode GetLogicalDrives der Klasse Directory ist eine gemeinsam genutzte Methode, die ein Array mit den Namen aller auf dem Rechner verfügbaren Laufwerke zurückgibt. Dazu gehören Diskettenlaufwerke, CD-ROMLaufwerke, zugeordnete Netzwerklaufwerke und andere Geräte, die wie Festplattenlaufwerke aussehen. Die Zeile 6 füllt dieses Array aus und der For Each...Next-Block durchläuft das Array mit einer Schleife. Dies fügen wir der TreeView als einen TreeNode hinzu (Zeile 11) und in Zeile 13 fügen wir in diesen Knoten einen Kindknoten mit dem Text dummy (also eine Knotenattrappe) ein. Dieser Kindknoten dient zwei Zwecken: Er kennzeichnet diejenigen Knoten, die wir noch nicht geöffnet haben, und stellt sicher, dass alle Knoten mit einem + versehen werden. Wenn sich in TreeView keine Kindknoten befinden, fügt das Steuerelement auch kein Pluszeichen hinzu. Dann ist nicht offensichtlich, dass es Kinder geben könnte. Indem wir eine einzelne Knotenattrappe hinzufügen, die wir später wieder entfernen werden, sorgen wir dafür, dass jeder Knoten so aussieht, als hätte er Kinder. TreeView hat zwei Funktionen: Während es die einzelnen Verzeichnisse der Reihe nach auswählt, fügt es die Dateien in dem jeweiligen Verzeichnis der ListView hinzu. Und wenn die einzelnen Knoten in der TreeView aufgeklappt werden, werden die Kindverzeichnisse des ausgewählten Verzeichnisses der TreeView hinzugefügt. Das Listing 16.7 zeigt diese beiden Ereignisbehandler. Listing 16.7: Die Ereignisbehandler von TreeView 17 18 19 20 21 22 23 24 25 26 27 28 29
Private Sub tvwFolders_BeforeExpand(ByVal sender As Object, _ ByVal e As System.Windows.Forms.TreeViewCancelEventArgs) _ Handles tvwFolders.BeforeExpand 'Prüfe, ob wir die Kinder bereits kennen. '(Wenn es noch eine Attrappe gibt, kennen wir sie nicht.) Dim oNode As TreeNode = CType(e.Node, TreeNode) If oNode.Nodes(0).Text = "dummy" Then 'Lösche die Attrappe. oNode.Nodes(0).Remove() 'Füge die echten Kinder hinzu. GetChildren(oNode) End If
541
Windows Forms für Fortgeschrittene
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
542
End Sub Private Sub tvwFolders_AfterSelect(ByVal sender As Object, _ ByVal e As System.Windows.Forms.TreeViewEventArgs) _ Handles tvwFolders.AfterSelect Dim sFiles() As String Try sFiles = Directory.GetFiles(tvwFolders.SelectedNode.FullPath) Catch ex As Exception 'Ignoriere die Ausnahme einfach. 'Die wahrscheinlichste Ursache für die Ausnahme ist 'Das Gerät 'ist nicht bereit', wenn Sie auf ein Diskettenlaufwerk ' zugreifen, in dem sich keine Diskette befindet. End Try If Not IsNothing(sFiles) then Dim sFile As String Dim oItem As ListViewItem lvwFiles.Items.Clear() For Each sFile In sFiles oItem = lvwFiles.Items.Add(StripPath(sFile)) Next End If End Sub Private Dim Dim Dim
Sub GetChildren(ByVal node As TreeNode) sDirs() As String = Directory.GetDirectories(node.FullPath) sDir As String oNode As TreeNode
For Each sDir In sDirs oNode = node.Nodes.Add(StripPath(sDir)) 'Füge eine Knotenattrappe als Kind hinzu. oNode.Nodes.Add("dummy") Next End Sub Private Function StripPath(ByVal path As String) As String 'Lösche den vorangestellten Pfad aus dem Dateinamen. Dim iPos As Integer 'Finde das letzte Backslash-Zeichen. iPos = path.LastIndexOf("\ ") 'Alles danach ist der tatsächliche Dateiname.
Kompliziertere Windows Forms-Steuerelemente
76 Return path.Substring(iPos + 1) 77 End Function 78 End Class
Beim Steuerelement TreeView gibt es für jede Operation, die die Knoten im Baum beeinflusst, die Ereignisse Before und After. Die Before-Ereignisse wie z.B. der Ereignisbehandler BeforeExpand (Zeilen 17 bis 30) bieten die Möglichkeit, entweder das Ereignis abzubrechen oder Änderungen vorzunehmen, bevor die Auswirkungen des Ereignisses angezeigt werden. Mit dem Ereignis BeforeExpand können Sie den Inhalt der Kinder des Knoten ändern, bevor die Liste aufgeklappt wird. Denken Sie daran, dass Sie eine Knotenattrappe eingefügt haben, die sicherstellt, dass alle Knoten mit einem Pluszeichen versehen werden. Dieses Pluszeichen zeigt an, dass der Knoten erweitert werden kann. Der Ereignisbehandler BeforeExpand ist die geeignete Stelle, um diese Knotenattrappe wieder zu entfernen und die eigentlichen Knoten einzufügen. Sobald es keine Knotenattrappe mehr gibt, brauchen Sie auch keine Änderungen mehr vorzunehmen, da TreeView sich an die hinzugefügten Knoten erinnert. Zuerst weist die Routine den betroffenen Knoten einer temporären Variablen zu. Dies ist nicht unbedingt erforderlich, da wir den Code auch mit e.Node anstelle von oNode hätten schreiben können. Allerdings ist es etwas effizienter, eine temporäre Variable wie diese oder einen With...End With-Block zu verwenden. Anschließend prüft der Code, ob die Knotenattrappe vorhanden ist (Zeile 24). Falls es eine Knotenattrappe gibt, wird sie gelöscht und die tatsächlichen Kinder werden mit der Subroutine GetChildren hinzugefügt. (Diese Subroutine beschreiben wir etwas weiter unten.) Wenn es keine Knotenattrappe gibt, macht der Code auch nichts, da wir TreeView bereits mit den richtigen Kindknoten besiedelt haben. Der Ereignisbehandler tvwFolders_AfterSelect ist der Ort, an dem ListView mit den Dateien im gewählten TreeNode besiedelt wird. Zuerst werden mit der gemeinsam genutzten Methode GetFiles der Klasse Directory die Namen aller Dateien abgerufen (Zeilen 21 und 22). Diese Methode gibt den vollständigen Pfad zu allen Dateien in einem angefragten Verzeichnis zurück. Der zweite Parameter des Ereignisbehandlers AfterSelect lautet e und hat zwei interessante Eigenschaften: 왘
Action: Warum wurde dieser Knoten ausgewählt? Geschah dies während
des Aufklappens oder während des Zuklappens oder hat der Anwender den Knoten ausgewählt? 왘
Node: Der ausgewählte Knoten. Dies ist eine einfachere Möglichkeit, den ausgewählten Knoten abzurufen, als tvwFolders.SelectedItem.
543
Windows Forms für Fortgeschrittene
Jeder Knoten in TreeView kann seinen Pfad in TreeView abrufen. Dieser Pfad besteht aus allen Eltern, die mit dem Wurzelknoten beginnen und jeweils durch das PathSeparator-Zeichen des Steuerelements TreeView voneinander getrennt sind. Da das Standardzeichen für den Separator der Backslash ist, ist dies eine einfache Möglichkeit, einen Pfad abzurufen, der Ähnlichkeit mit dem Pfad auf dem Laufwerk hat. Wenn der Knoten System32 z.B. ein Kindknoten des Knotens Winnt ist, der wiederum ein Kindknoten von C:\ ist, dann ist C:\Winnt\System32 die Eigenschaft FullPath des Knoten System32. Dieser Pfad wird an die Methode GetFiles übergeben, die eine gemeinsam genutzte Methode der Klasse Directory ist, die ein Zeichenketten-Array mit allen Dateien im Verzeichnis zurückgibt. Nachdem der gesamte Inhalt von ListView gelöscht wurde (Zeile 25), werden mit einer For Each...Next-Schleife (Zeilen 27 bis 29) alle Dateinamen in ListView eingefügt. Beachten Sie, dass der Inhalt aller Verzeichnisse jedes Mal abgerufen wird, wenn TreeNode ausgewählt wird. Es wird nicht versucht, diese Liste zu speichern. Mit der Prozedur GetChildren wird ein TreeNode mit seinen Kindknoten besiedelt (Zeilen 58 bis 68). Der hinzuzufügende Knoten wird an die Routine übergeben, damit er ihr dabei hilft, die Unterverzeichnisse zu ermitteln und den TreeNode zu identifizieren, zu dem der Knoten hinzugefügt werden soll. Beachten Sie, dass der vorhandene Pfad des ausgewählten Knotens nicht übergeben wird, da dieser mit der Methode FullPath des Knotens (Zeile 59) so ermittelt werden kann, wie wir es beim Ereignisbehandler AfterSelect bereits gemacht haben. Mit der gemeinsam genutzten Methode Directory.GetDirectories rufen wir ein Zeichenketten-Array ab, das die Unterverzeichnisse des ausgewählten Verzeichnisses enthält. Der übrige Code gleicht dem, mit dem wir TreeView die Laufwerke hinzugefügt haben. Der Pfad wird aus dem abgerufenen Verzeichnisnamen gelöscht (Zeile 64) und eine Knotenattrappe wird hinzugefügt, damit der Knoten so aussieht, als könne er aufgeklappt werden (Zeile 66). Die Funktion StripPath wird von anderen Prozeduren dafür verwendet, den Pfad aus dem Dateinamen zu löschen, sodass nur der Name übrig bleibt. Diese Routine (Zeilen 70 bis 78) verwendet die Methode LastIndexOf des Zeichenkettenobjekts, um die letzte Position des abgefragten Zeichens zu holen, in diesem Fall also des Backslash-Zeichens. In diesem Pfad sollte alles, was vor dem letzten Backslash steht, der Pfad sein, während alles, was hinter dem letzten Backslash folgt, der tatsächliche Dateiname ist. Die Zeile 76 extrahiert schließlich noch den Text, der mit dem Zeichen unmittelbar hinter dem letzten Backslash beginnt, und gibt ihn zurück. Kompilieren Sie die Anwendung und führen Sie sie aus. Das Ergebnis sollte ungefähr wie Abbildung 16.16 aussehen, wobei die Einzelheiten davon abhängen, welche Laufwerke und Verzeichnisse Sie haben. Sie sollten in der Lage sein, durch die Verzeichnisse zu navi-
544
Zusammenfassung
gieren, wobei die Dateien in den jeweiligen Verzeichnissen angezeigt werden sollten. Nun haben Sie eine gute Grundlage, auf der Sie einen eigenen Datei-Explorer erstellen können. Fügen Sie Pioneer noch weitere Merkmale hinzu, z.B. die Fähigkeit, Verzeichnisse hinzuzufügen, die Ansicht der Dateien zu verändern, Bilder einzufügen usw.
Abbildung 16.16: Pioneer bei der Arbeit
16.4 Zusammenfassung Offensichtlich gibt es noch wesentlich mehr Themen, die mit Windows Forms zu tun haben. Und in der Werkzeugsammlung stehen viele zusätzliche Steuerelemente zur Verfügung, die wir hier nicht beschrieben haben. Experimentieren Sie mit diesen Steuerelementen, um zu sehen, wie sie Ihnen bei Ihren Anwendungen helfen können. Wenn Sie in einer anderen Anwendung ein Merkmal sehen, das Ihnen gefällt, dann suchen Sie das entsprechende Steuerelement oder die betreffende Eigenschaftseinstellung, mit der Sie dieses Funktionsmerkmal in Ihren eigenen Anwendungen reproduzieren können. Es sollte Ihnen aufgefallen sein, dass wir in allen unseren Anwendungen Objekte verwenden. Allerdings sollten Sie auch lernen, wie Sie eigene Objekte erzeugen. Morgen werden wir mit unserer Untersuchung des .NET-Frameworks fortfahren und uns einige andere häufig verwendete Klassen ansehen.
545
Windows Forms für Fortgeschrittene
16.5 Fragen und Antworten F
Wie verbinden Sie Strg+B so mit einem Menüelement, dass beim Drücken dieser Tastenkombination der Code für das Menüelement ausgeführt wird? A
F
Was geschieht, wenn Sie die Eigenschaft MdiParent eines Formulars nicht setzen, bevor Sie das Formular anzeigen? A
F
Um einem Menüelement eine Tastenkombination hinzuzufügen, verwenden Sie die Eigenschaft ShortcutKey. Wählen Sie das Element CtrlB aus der DropdownListe für die Eigenschaft ShortcutKey.
Wenn Sie die Eigenschaft MdiParent eines Formulars, dessen Eigenschaft IsMdiContainer auf True gesetzt ist, nicht setzen, dann ist das neue Formular nicht im MdiParent-Formular enthalten und kann nicht aus dem Rahmen des Elternformulars herausgenommen werden.
Welches Ergebnis liefert der folgende Code, wenn tvwFolders ein TreeView-Steuerelement auf einem Formular ist? Dim oItem as TreeNode oItem = tvwFolders.Nodes.Add("One") With oItem.Nodes.Add("Two") .Nodes.Add("Three") .Nodes.Add("Four") End With
A
TreeView werden vier Elemente hinzugefügt. Der Wurzelknoten hat den Text One. Er hat einen Kindknoten Two, der zwei Kindknoten hat, nämlich Three und Four. Der With...End With-Block ist eine Alternative dazu, einen Knoten in einer TreeNode-Variablen zu speichern, wenn Sie Kindknoten hinzufügen.
16.6 Workshop Die Antworten zum Quiz finden Sie im Anhang A.
Quiz Wie ordnen Sie die Kindknoten einer MDI-Anwendungen am besten an, damit Sie die Titelleisten aller Kinder sehen?
546
Workshop
Übungen 1. Finden Sie Beispiele für MDI- und SDI-Anwendungen, die Sie häufig verwenden. Erleichtern oder erschweren es diese Anwendungen, mehrere Dokumente gleichzeitig im Auge zu behalten? 2. Aktualisieren Sie die Anwendung Pioneer, die Sie in der heutigen Lektion erstellt haben, so, dass sie die Dateien in der Detailansicht des Steuerelements ListView anzeigt. Fügen Sie Spalten hinzu, um die Attribute ReadOnly, Hidden und System für alle Dateien anzuzeigen. Zeigen Sie außerdem in einer weiteren Spalte an, wann die Datei zuletzt geändert wurde.
547
Mit dem .NET Framework arbeiten
Mit dem .NET Framework arbeiten
Am Tag 8, Einführung in das .NET-Framework, haben wir erstmals das .NET-Framework und einige der Klassen besprochen, die es zur Verfügung stellt. Heute werden wir einige der gängigen Klassengruppen besprechen, mit denen Sie regelmäßig arbeiten werden.
Themen heute 쐽
Dateien und für Dateien relevante Klassen
쐽
Mit den Grafikklassen zeichnen
17.1 Ströme und Dateien Viele Visual Basic .NET-Anwendungen, die Sie schreiben, haben auf irgendeine Art mit Dateien zu tun. Entweder legen Sie Dateien an oder Sie müssen Konfigurationsdateien oder Dateien auf den Laufwerken des Anwenders lesen. Daher ist es wichtig, dass Sie lernen, wie Sie mit Dateien arbeiten. Dieses Wissen werden Sie als Visual Basic .NET-Entwickler benötigen. Wenn Sie mit den Klassen des .NET-Frameworks zu arbeiten beginnen und besonders, wenn Sie Anwendungen für die Arbeit mit Dateien schreiben, werden Sie häufig auf Namen treffen, die das Wort »Stream« enthalten, z.B. FileStream, TextStream, StreamWriter und NetworkStream. Erwartungsgemäß sind diese Dinge miteinander verwandt. Die Entwickler von Visual Basic .NET und des .NET-Frameworks haben versucht, die Verwendung des Frameworks zu erleichtern, indem Sie alles möglichst konsistent gemacht haben. So haben sie unter anderem an vielen Stellen dieselben Objekttypen verwendet. Eine Gruppe solcher Objekte besteht aus dem Objekt Stream sowie seinen Kindern und den Klassen, die man für die Arbeit mit Strömen verwendet. Zu diesen anderen Klassen gehören zahlreiche Reader- und Writer-Klassen, Encoders und viele andere. Wir werden uns hier zwar auf das Wesentliche beschränken, aber wenn Sie etwas mehr Erfahrung gewonnen haben, werden Sie feststellen, dass auch alle anderen Klassen in dieser Familie ähnlich aussehen.
Was ist ein Strom? Wie viele andere Computerbegriffe ist auch der Begriff Strom ein Versuch, etwas so zu benennen, dass es Assoziationen mit einem Gegenstück in der realen Welt auslöst. In der realen Welt ist ein Strom eine Menge fließenden Wassers. Ein Computerstrom ist etwas Ähnliches: Er ist ein Informationsfluss, der sequenziell verläuft. Allerdings werden Sie wahrscheinlich nicht an Wasser denken, wenn Sie die Dateien auf Ihrer Festplatten
550
Ströme und Dateien
betrachten – sofern Sie nicht gerade eine Datei über die Flüsse dieser Welt haben. Aber stellen Sie sich einmal vor, sie seien ein Teil der Festplatte, die Dateien liest. Alles, was Sie sehen, ist eine Folge von Informationen, die sequenziell an Ihnen vorbeifließt – eben ein Strom. Wenn Sie sich die Dateien abstrakt als einen Strom vorstellen, wird es einfacher, mit Dateien zu arbeiten. Nun können Sie alle Dateien auf dieselbe Art behandeln, ob sie nun binär sind oder Text enthalten. Sie öffnen die Datei, bewegen sich in ihr und schreiben in sie; und da alle Dateien Ströme sind, verwenden Sie dabei für alle dieselben Methoden. Diese Sichtweise ist auch bei anderen Dingen sinnvoll, die wie Ströme aussehen. Dazu gehören Nachrichten in einem Netzwerk, der Speicher Ihres Rechners, verschlüsselte Daten und viele andere Dinge, die wir in diesem Buch jedoch nicht behandeln werden. Wenn Sie erst einmal wissen, wie Sie einfache Textdateien lesen und schreiben, wissen Sie auch, wie Sie mit allen anderen Arten von Strömen arbeiten.
Dateien und Verzeichnisse Bevor wir Ströme erstellen, müssen wir uns jedoch zuerst einmal damit befassen, wie wir Verzeichnisse und Dateien finden, erstellen und löschen. Dies müssen Sie wissen, um Dateien finden und Verzeichnisse anlegen zu können, in denen Sie Ihre Anwendungen speichern. Wie Sie am Tag 8 gesehen haben, besteht das .NET-Framework aus einer Reihe von Namensräumen. Jeder dieser Namensräume enthält mehrere Klassen, die irgendwie miteinander verwandt sind. Denken Sie daran, dass wir am Tag 8 mit Sammlungen (z.B. ArrayList und Queue) gearbeitet haben und diese im Namensraum System.Collections zusammengefasst waren. In ähnlicher Weise sind auch alle Datei- und Verzeichnisklassen (und die Stromklassen) im Namensraum System.IO zusammengefasst. Der Namensraum System.IO ist nicht mit dem hochvulkanischen Jupitermond gleichen Namens verwandt (http://photojournal.jpl.nasa.gov/cgi-bin/PIAGenCatalogPage.pl?PIA00292), sondern hat mit der Eingabe und Ausgabe zu tun. Daher ist er auch einer der im .NET-Framework am häufigsten verwendeten Namensräume. Ein Namensraum, der mit dem Jupitermond zu tun hat, mag zwar nützlich sein, wird aber sicher nicht häufig verwendet. Jedes Mal, wenn Sie eine Datei oder einen Strom öffnen, daraus lesen, etwas hineinschreiben oder etwas anderes damit machen, benötigen Sie diesen Namensraum. Glücklicherweise fügt Visual Basic .NET ihn immer ein, wenn Sie eine neue Windows-Anwendung erstellen, sodass er immer zur Verfügung stehen sollte. Wenn Sie den Namensraum System.IO in der Onlinehilfe nachschlagen, werden Sie zwei Klassen sehen: File und Directory. Diese beiden Klassen ordnen die Dateien und Verzeichnisse zu. Sie haben Eigenschaften und Methoden, die das repräsentieren, was Sie von Dateien und Verzeichnissen erwarten. Die Klasse Directory hat z.B. Methoden, um Unterverzeichnisse abzurufen, die Liste der Dateien im Verzeichnis zu holen usw. Und
551
Mit dem .NET Framework arbeiten
die Klasse File hat Methoden, um die Datei zu kopieren, zu öffnen und Informationen über die Datei abzurufen. Die Tabelle 17.1 führt die wichtigsten Methoden des DirectoryObjekts auf und die Tabelle 17.2 nennt die wichtigsten Methoden der Klasse File. Methode
Beschreibung
CreateDirectory
Erstellt ein oder mehrere Verzeichnisse. Eine der nützlichsten Verwendungsmöglichkeiten dieser Klasse ist die Erstellung eines vollständigen Verzeichnisbaumes.
Delete
Löscht ein Verzeichnis.
Exists
Gibt True zurück, falls das Verzeichnis existiert.
GetCurrentDirectory
Gibt den vollständigen Pfad zum aktuellen Verzeichnis zurück.
GetDirectories
Gibt ein Array zurück, das die Kindverzeichnisse des gewünschten Verzeichnisses enthält.
GetFiles
Gibt ein Array zurück, das die Dateien im angefragten Verzeichnis enthält.
Tabelle 17.1: Methoden der Klasse Directory Methode
Beschreibung
Copy
Kopiert eine Datei.
Create
Legt eine neue Datei an.
CreateText
Eine besondere Version von Create, die eine Textdatei anlegt.
Delete
Löscht eine Datei.
Exists
Gibt True zurück, falls die Datei existiert.
Open
Öffnet eine Datei zum Lesen, zum Schreiben oder zum Lesen und Schreiben.
OpenRead
Sonderform von Open, die die Datei immer zum Lesen öffnet.
OpenText
Sonderform von Open, die Textdateien nur zum Lesen öffnet. Dies ist eine nützliche Kurzform, wenn Sie eine Anwendung schreiben, die Konfigurationsinformationen oder eine Protokolldatei lesen muss.
OpenWrite
Sonderform von Open, die die Datei immer zum Schreiben öffnet.
Tabelle 17.2: Methoden der Klasse File
Dateien anlegen Wie Sie in Tabelle 17.1 und Tabelle 17.2 gesehen haben, können viele Methoden der Klassen File und Directory dafür verwendet werden, Dateien und Verzeichnisse zu durchsuchen und zu erstellen. Dies ist ein gutes Beispiel für die angewandte Objektorientierung:
552
Ströme und Dateien
Klassen, die reale Objekte darstellen, verhalten sich wie diese Objekte. So sollte ein Verzeichnis z.B. wissen, welche Kindverzeichnisse es hat. Und Sie sollten eine Datei bitten können, sich selbst zu öffnen. Viele der Methoden der Klassen File und Directory sind gemeinsam genutzte (Shared) Methoden. Dies bedeutet, dass Sie kein Objekt zu erzeugen brauchen, um diese Methoden nutzen zu können. Vielmehr greifen Sie einfach mit der Klasse auf die Methode zu. Die Methode Exists der Klasse File ist z.B. eine gemeinsam genutzte Methode. Um zu ermitteln, ob eine Datei existiert, brauchen Sie keine Instanz von File zu erzeugen, sondern verwenden einfach die Klasse File selbst. Wenn Sie ermitteln möchten, ob eine Datei existiert, schreiben Sie also nicht: Dim oFile As New File() bExists = oFile.Exists("somefile.txt")
Vielmehr verwenden Sie den folgenden Code: bExists = File.Exists("somefile.txt")
Aus einer Textdatei lesen Nachdem Sie eine Datei angelegt haben, müssen Sie aus ihr lesen können. Hier kommen die Stream-Klassen sowie die verschiedenen Reader- und Writer-Klassen ins Spiel. Nachdem Sie eine Datei geöffnet haben, wird Ihnen immer eine dieser Klassen übergeben, und zwar normalerweise irgendein Stream. Anschließend lesen Sie die Informationen in der Datei, wofür Sie entweder Stream verwenden oder StreamReader auf Stream anwenden. Da FileStream der am häufigsten verwendete Stream ist, werden wir uns vorwiegend damit beschäftigen. FileStream ist ein Stream, den Sie erhalten, indem Sie eine Datei lesen. Dies kann eine Textdatei oder eine Binärdatei sein. FileStream hat eine Reihe von Methoden, mit denen
Sie aus der Datei lesen können, und Eigenschaften, die mit der Datei zu tun haben. Die Tabelle 17.3 beschreibt die wichtigsten Methoden und Eigenschaften. Das Problem besteht bei den Read-Methoden der Klasse FileStream darin, dass sie meist nicht besonders komfortabel sind, da sie alle mit Bytes zu tun haben. Anstelle dieser Methoden können Sie auch einen StreamReader auf FileStream anwenden, um die Informationen auf bequemere Art zu lesen. Die Tabelle 17.4 zeigt die wichtigsten Methoden von StreamReader.
553
Mit dem .NET Framework arbeiten
Name
Beschreibung
CanRead (Eigenschaft)
Ist True, falls Sie aus der Datei lesen können. Diese Eigenschaft ist gut für Prüfungen geeignet, um eine Ausnahme zu vermeiden, die auftreten kann, falls die Datei gesperrt oder nur zum Schreiben geöffnet ist.
CanSeek (Eigenschaft)
Ist True, falls Sie die Datei durchsuchen können (d.h., falls Sie sich in ihr vorwärts und rückwärts bewegen können). Diese Eigenschaft ist gut für Prüfungen geeignet, um eine Ausnahme zu vermeiden, die ausgelöst wird, wenn Sie eine Datei lesen, in der Sie sich nicht vor- und zurückbewegen können. Bei Dateien ist dies nur selten der Fall, bei einigen anderen Stromtypen kommt es hingegen regelmäßig vor.
CanWrite (Eigenschaft)
Ist True, falls Sie in die Datei schreiben können. Diese Eigenschaft ist gut für Prüfungen geeignet, um eine Ausnahme zu vermeiden, die auftreten kann, falls die Datei gesperrt oder schreibgeschützt geöffnet ist.
Length (Eigenschaft)
Anzahl der Bytes in der Datei.
Position (Eigenschaft)
Die aktuelle Position in der Datei.
Close (Methode)
Schließt FileStream. Schließen Sie FileStream (und jeden anderen Strom) immer, wenn Sie ihn nicht mehr benötigen.
Read (Methode)
Liest mehrere Bytes aus FileStream. Diese kommen als Array zu Ihnen zurück.
Seek (Methode)
Bewegt sich in der Datei vor und zurück.
Write (Methode)
Schreibt mehrere Bytes in FileStream.
Tabelle 17.3: Methoden und Eigenschaften von FileStream Name
Beschreibung
Close
Schließt StreamReader. Schließen Sie StreamReader-Objekte immer, wenn Sie sie nicht mehr benötigen.
Read
Liest das nächste Zeichen aus Stream. Dies ist nützlich, wenn Sie die Informationen zeichenweise lesen.
ReadBlock
Liest einen Block von Zeichen aus Stream. Dies kann eine schnelle Art sein, Informationen aus einem Stream zu lesen.
ReadLine
Liest die nächste Zeile aus Stream. Dies ist eine komfortable Art, mit Dateien umzugehen, die zeilenweise angeordnete Informationen enthalten.
ReadToEnd
Liest alle Zeichen sofort aus Stream. Dies ist die schnellste Möglichkeit, alle Informationen aus Stream zu holen und in eine Variable zu setzen.
Tabelle 17.4: Die wichtigsten Methoden von StreamReader
Das Listing 17.1 zeigt eine typische Art, eine Textdatei zu öffnen und ihren Inhalt in eine Zeichenkettenvariable zu lesen.
554
Ströme und Dateien
Listing 17.1: Aus einer Datei lesen 1 2 3 4 5 6 7 8 9
Dim oFile As FileStream Dim oReader As StreamReader Dim sContents As String oFile = New FileStream("MyFile.txt", FileMode.OpenOrCreate, FileAccess.Read) oReader = New StreamReader(oFile) sContents = oReader.ReadToEnd() oReader.Close() oReader = Nothing oFile = Nothing
In eine Textdatei schreiben Ebenso wie beim Lesen können Sie auch beim Schreiben das Stream-Objekt dafür verwenden, in sich selbst zu schreiben. Einfacher ist es jedoch, hierfür ein StreamWriter-Objekt zu verwenden. Ebenso wie StreamReader wenden Sie auch StreamWriter auf einen vorhandenen Stream an und fügen mithilfe der Methoden Write und WriteLine Informationen hinzu. Die Tabelle 17.5 zeigt die wichtigsten Methoden von StreamWriter. Name
Beschreibung
Close
Schließt das StreamWriter-Objekt. Schließen Sie die erzeugten StreamWriter-Objekte immer. Andernfalls verlieren Sie möglicherweise Änderungen, die Sie an der Datei vorgenommen haben.
Write
Schreibt in Stream.
WriteLine
Schreibt in Stream und beendet dabei die Informationen mit einem Zeilenumbruch.
Tabelle 17.5: Die wichtigsten Methoden der Klasse StreamWriter
Das Listing 17.2 zeigt ein typisches Beispiel für das Schreiben in eine Textdatei.
555
Mit dem .NET Framework arbeiten
Listing 17.2: In eine Datei schreiben 1 Dim oFile As FileStream 2 Dim oWriter As StreamWriter 3 oFile = New FileStream("MyFile.txt", _ 4 FileMode.OpenOrCreate, FileAccess.Write) 5 oWriter = New StreamWriter(oFile) 6 'Schreibt die ganze Zahl 123 in die Datei. 7 oWriter.Write(123) 8 'Schreibt die Zeichenkette "Customer" in die Datei. 9 oWriter.Write("Customer") 10 ' Schreibt die Zeichenkette "John Bull" in die Datei, beim nächsten Mal wird in eine neue Zeile geschrieben. 11 oWriter.WriteLine("John Bull") 12 oWriter.Close() 13 oWriter = Nothing 14 oFile = Nothing
Die Methoden von StreamWriter sollten Ihnen bekannt vorkommen: Es sind dieselben Methoden wie bei der Klasse Console. Die Klasse Console ist ein StreamWriter, der dafür entworfen wurde, in die Konsole zu schreiben. Nun wollen wir die Lese- und Schreibvorgänge zusammenfügen und eine einfache Anwendung erstellen, die die drei besprochenen wichtigen Klassen (FileStream, StreamReader und StreamWriter) verwendet: Als Beispielanwendung werden wir einen einfachen Ersatz für Notepad erstellen, mit dem Sie Textdateien lesen und schreiben können. Später können Sie diese Anwendung so erweitern, dass Sie damit auch andere Informationsarten schreiben können. Erstellen Sie eine neue Windows-Anwendung und nennen Sie sie Note. Wie üblich besteht der erste Schritt nach dem Anlegen des Projekts darin, sicherzustellen, dass das Hauptformular nicht Form1 heißt. Öffnen Sie das Formular in der Codeansicht, suchen Sie Form1 und nennen Sie es frmMain. Geben Sie der Datei den Namen frmMain.vb. Schließlich öffnen Sie noch das Dialogfenster für die Projekteigenschaften und setzen das Startobjekt auf frmMain. Kompilieren Sie die Anwendung einmal, um sicherzustellen, dass sie keine Fehler enthält. Fügen Sie dem Formular das Steuerelement TextBox hinzu und setzen Sie seine Eigenschaften so, wie die Tabelle 17.6 es zeigt.
556
Ströme und Dateien
Eigenschaft
Wert
(Name)
TxtText
Multiline
True
Text
(leer lassen)
Scrollbars
Vertical
Dock
Fill
Tabelle 17.6: Eigenschaften von TextBox
Damit Ihr Notepad wie der von Windows aussieht, benötigen Sie ein Menü. Klicken Sie das Element MainMenu in der Werkzeugsammlung doppelt an, um ein Menü hinzuzufügen. Wie üblich ändern Sie den Namen auf mnuMain. Fügen Sie die in Tabelle 17.7 gezeigten Elemente hinzu. Element
Eigenschaft
Wert
Toplevel-Element
Caption
&File
Name
mnuFile
Caption
&New
Name
mnuFileNew
Shortcut
CtrlN
Caption
&Open...
Name
mnuFileOpen
Shortcut
CtrlO
Caption
&Save
Name
mnuFileSave
Shortcut
CtrlS
Caption
Save &As...
Name
mnuFileSaveAs
Caption
-
Name
mnuFileSep
Caption
E&xit
Name
mnuFileExit
Shortcut
CtrlQ
Caption
&Edit
Unter &File
Unter &New
Unter &Open...
Unter &Save Unter Save &As... Unter -
Toplevel-Element Tabelle 17.7: Die Menüleiste
557
Mit dem .NET Framework arbeiten
Element Unter &Edit
Unter Cu&t
Unter &Copy
Toplevel-Element Unter &Help
Eigenschaft
Wert
Name
mnuEdit
Caption
Cu&t
Name
mnuEditCut
Shortcut
CtrlX
Caption
&Copy
Name
mnuEditCopy
Shortcut
CtrlC
Caption
&Paste
Name
mnuEditPaste
Shortcut
CtrlV
Caption
&Help
Name
mnuHelp
Caption
&About
Name
mnuHelpAbout
Tabelle 17.7: Die Menüleiste (Forts.)
Das Ergebnis sollte wie die Menüs in Abbildung 17.1 aussehen.
Abbildung 17.1: Das Note-Beispiel ausführen
Um das Formular zu vervollständigen, fügen Sie ihm noch FileOpenDialog und FileSaveDialog hinzu und setzen deren Eigenschaften wie in Tabelle 17.8 gezeigt.
558
Ströme und Dateien
Objekt
Eigenschaft
Wert
FileOpenDialog
Name
dlgOpen
Filter
Text Files|*.txt|All Files|*.*
Name
DlgSave
Filter
Text Files|*.txt|All Files|*.*
FileName
note1
FileSaveDialog
Tabelle 17.8: Weitere Steuerelement auf dem Formular
Nun ist die Benutzeroberfläche eingerichtet und wir müssen Code hinzufügen. Zuerst fügen wir dem Projekt und dem Formular den Namensraum System.IO hinzu. Öffnen Sie den Ordner VERWEISE im Projektmappen-Explorer. In der Auflistung sollten Sie zwar den Namensraum System, nicht aber System.IO finden. Die Elemente unter VERWEISE repräsentieren die tatsächlichen Bibliotheken (DLLs), mit denen Sie Code finden, wenn Sie die Anwendung kompilieren. Alle diese DLLs können mehrere Namensräume enthalten. Einer davon ist System.IO. Um sich das Schreiben von Code zu erleichtern, können Sie die verwendeten Namensräume importieren. Dadurch können Sie mit Tastenkombinationen auf die Klassen referenzieren. Im folgenden Hinweis finden Sie weitere Informationen hierzu. Importieren Sie den Namensraum System.IO, um sich einige Tipparbeit zu ersparen. Fügen Sie in der obersten Zeile vor der Zeile Public Class frmMain die Zeile Imports.System.IO ein. Viele Programmierer, die noch keine Erfahrung mit Visual Basic .NET haben, finden es verwirrend, wann Verweise verwendet werden und wann die Anweisung Imports. Verweise werden mit dem Projektmappen-Explorer oder dem Menü PROJEKT hinzugefügt. Sie stellen die weiteren Komponenten dar, die Ihre Anwendung verwendet. Wenn Sie Code verwenden, der sich in einer anderen DLL (entweder Code in einem der zum .NET-Framework gehörigen Namensräume, die nicht automatisch hinzugefügt werden, oder Code, der von einem anderen Entwickler oder Unternehmen geschrieben wurde) befindet, müssen Sie ihn den Verweisen für Ihr Projekt hinzufügen. Die Anweisung Imports können Sie sich als ein Werkzeug vorstellen, mit dem Sie den Code verkürzen. Mit dieser Anweisung können Sie es vermeiden, zu lange Klassennamen schreiben zu müssen. Wenn Sie einem Formular z.B. eine TextBox hinzufügen, schreibt Visual Basic .NET Folgendes: Private WithEvents txtText As System.Windows.Forms.TextBox
Der vollständige Klassenname ist also System.Windows.Forms.TextBox. Wenn Sie am Anfang der Datei jedoch Imports System.Windows.Forms einfügen, können Sie die obige Codezeile verkürzen: Private WithEvents txtText As TextBox
559
Mit dem .NET Framework arbeiten
Wenn Visual Basic .NET die TextBox sieht, sucht es mithilfe der Namensräume, die die Imports-Anweisung hinzugefügt hat, nach der Klasse TextBox. Wenn Sie also einen Namensraum verwenden, muss dieser im Abschnitt VERWEISE enthalten sein. Wenn Sie hingegen Ihren Code verkürzen möchten, verwenden Sie die Anweisung Imports. Als Nächstes fügen Sie dem Formular zwei neue Eigenschaften hinzu. Zunächst benötigen Sie eine Eigenschaft, um den Dateinamen (einschließlich des Pfads) und mögliche Änderungen zu beobachten. Hierfür fügen Sie den Code im Listing 17.3 direkt nach der Zeile Inherits System.Windows.Forms.Form in das Programm ein.
Listing 17.3: Die Eigenschaften der Anwendung Note 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
560
Private m_sFileName As String Private m_bDirty As Boolean Public Property FileName() As String Get Return m_sFileName End Get Set(ByVal Value As String) m_sFileName = Value Me.Text = "Note – " & m_sFileName End Set End Property Public Property Dirty() As Boolean Get Return m_bDirty End Get Set(ByVal Value As Boolean) m_bDirty = Value Me.Text = "Note – " & m_sFileName & "*" End Set End Property Public Sub NewFile() Me.txtText.Text = "" Me.FileName = "" Me.Dirty = False End Sub
Ströme und Dateien
Der Code ist eine relativ einfache Gruppe von Eigenschaftsanweisungen. Die ersten beiden Zeilen deklarieren private Felder, die die Werte der Eigenschaften speichern. Die Zeilen 3 bis 11 repräsentieren die Eigenschaft FileName. Diese Eigenschaft speichert den Dateinamen für den späteren Zugriff im Feld m_sFileName. Wenn diese Eigenschaft gesetzt ist, wird FileName auch in den Titel des Formulars geschrieben (Zeile 9). Die Eigenschaft Dirty (Zeilen 12 bis 20) speichert ihre Informationen im Feld m_bDirty und fügt am Ende der Titelleiste ein Sternchen ein. Nun fügen Sie den Kasten mit Informationen über den Autor ein. Immerhin ist dies der wichtigste Teil vieler Anwendungen: Er teilt den Anwendern mit, wer für die Anwendung verantwortlich ist. Der Einfachheit halber zeigen Sie die Informationen mit einer MessageBox an. Klicken Sie das Menüelement HELP, ABOUT doppelt an, um den Codee-Eitor zu starten. Fügen Sie den in Listing 17.4 gezeigten Code hinzu.
Listing 17.4: Das Menü Help, About 1 Private Sub mnuHelpAbout_Click(ByVal sender As System.Object, _ 2 ByVal e As System.EventArgs) Handles mnuHelpAbout.Click Dim sMessage As String 3 sMessage = "Note – a simple text editor" & ControlChars.CrLf & _ 4 "Original in Teach Yourself Visual Basic .NET in 21 Days." & _ 5 controlchars.CrLf & _ 6 "copyright 2001. Full rights to extend provided by the author." 7 MessageBox.Show(sMessage, _ 8 "Note", _ 9 MessageBoxButtons.OK, MessageBoxIcon.Information) 10 End Sub
Der Informationskasten für Note ist eine einfache MessageBox. Im Code erstellen wir Zeichenkettenvariablen und füllen sie (Zeilen 3 bis 6). Beachten Sie, dass wir der Nachricht neue Zeilen hinzufügen, indem wir in der Mitte der Zeichenkette ControlChars.CrLf einbetten. Dieses Symbol repräsentiert für die Zeichenkette eine Zeilenumbruchs-/Zeilenvorschubskombination und erzeugt eine neue Zeile. Zuletzt zeigen wir die MessageBox einschließlich der Schaltfläche OK und des Informationssymbols an (Zeile 7).
561
Mit dem .NET Framework arbeiten
Warum dieses komplizierte »Zeilenvorschub, Zeilenumbruch«? Warum nehmen wir nicht einfach ein Zeilenschaltzeichen oder etwas Einfacheres? Wie üblich liegt die Ursache in den frühen Computerzeiten (ungefähr vor 30 Jahren). Bei den ersten Rechnern arbeitete man nicht mit Monitoren und Mäusen, sondern mit Fernschreibern, die fast wie die Schreibmaschinen funktionierten, denen sie ähnlich sahen. Wie jeder, der schon einmal mit einer manuellen Schreibmaschine gearbeitet hat, weiß, drückt man dabei den Wagenrücklaufhebel, sobald man das Ende einer Zeile erreicht und das Klingeln ertönt. Der Wagenrücklaufhebel macht zwei Dinge: Er geht an den Anfang der Zeile zurück und zur nächsten Zeile hinunter. Zeilenvorschub heißt: »Schiebe den Wagen (das Ding, das Papier und Rolle enthält) zum Anfang der Zeile zurück«. Zeilenumbruch heißt: »Gehe zur nächsten Zeile«. Zeilenvorschub, Zeilenumbruch bedeutet daher »Gehe zum Anfang der nächsten Zeile«. Zeilenvorschub, Zeilenumbruch ist selbst für Informatiker ein etwas langer Begriff, weshalb er meist als CRLF (für Carriagereturn, Line-feed) abgekürzt wird. Visual Basic .NET stellt dies als ControlChars.CrLf zur Verfügung. Nun ist es an der Zeit, unser Programm mit wirklicher Funktionalität auszustatten. Wir beginnen mit dem Menü FILE, das im Listing 17.5 gezeigt wird.
Listing 17.5: Befehle im Menü File 1 Private Sub mnuFileNew_Click(ByVal sender As System.Object, _ 2 ByVal e As System.EventArgs) Handles mnuFileNew.Click 3 If Me.Dirty = True Then 4 If MessageBox.Show( _ 5 "You have made changes to the file that will be lost. " & _ 6 "Do you want to Continue?", _ 7 "New File", _ 8 MessageBoxButtons.YesNo, MessageBoxIcon.Question) = _ 9 DialogResult().Yes Then 10 NewFile() 11 End If 12 Else 13 NewFile() 14 End If 15 End Sub
562
Ströme und Dateien
16 Private Sub mnuFileOpen_Click(ByVal sender As System.Object, _ 17 ByVal e As System.EventArgs) _ 18 Handles mnuFileOpen.Click 19 Dim oFile As FileStream 20 Dim oReader As StreamReader 21 If Me.dlgOpen.ShowDialog = DialogResult().OK Then 22 'OK, we can try to open and read the file 23 Try 24 Me.FileName = Me.dlgOpen.FileName 25 oFile = File.OpenRead(Me.FileName) 26 oReader = New StreamReader(oFile) 27 Me.txtText.Text = oReader.ReadToEnd 28 Catch ex As Exception 29 'just display the error for now 30 MessageBox.Show(ex.Message, _ 31 "Error Opening File", _ 32 MessageBoxButtons.OK, _ 33 MessageBoxIcon.Error) 34 Finally 35 'remember to always close your readers and files 36 oReader.Close() 37 oFile.Close() 38 End Try 39 End If 40 End Sub 41 Private Sub mnuFileSave_Click(ByVal sender As System.Object, _ 42 ByVal e As System.EventArgs) _ 43 Handles mnuFileSave.Click 44 'we should only try to save this file if it has a name 45 If Me.FileName "Untitled" Then 46 'OK, let's try saving the file 47 Dim oFile As FileStream 48 Dim oWriter As StreamWriter 49 Try 50 oFile = File.OpenWrite(Me.FileName) 51 'Wandle den Inhalt der TextBox in ein Array von Bytes um 52 oWriter = New StreamWriter(oFile) 53 'und schreibe es in die Datei. 54 oWriter.Write(Me.txtText.Text) 55 Und nun sind wir sauber. 56 Me.Dirty = False 57 Catch ex As Exception 58 'Zeige im Moment nur bei einer Ausnahme einen Fehler an. 59 MessageBox.Show(ex.Message, _ 60 "Error Saving File", _ 61 MessageBoxButtons.OK, _
563
Mit dem .NET Framework arbeiten
62 MessageBoxIcon.Error) 63 Finally 64 'Alle Writer und Streams müssen geschlossen werden. 65 oWriter.Close() 66 oFile.Close() 67 End Try 68 Else 69 'Anderenfalls hole einen Namen. 70 mnuFileSaveAs_Click(sender, e) 71 End If 72 End Sub 73 Private Sub mnuFileSaveAs_Click(ByVal sender As System.Object, _ 74 ByVal e As System.EventArgs) _ 75 Handles mnuFileSaveAs.Click 76 If Me.dlgSave.ShowDialog = DialogResult().OK Then 77 'OK wurde angeklickt, wir können den Dateinamen setzen und speichern. 78 Me.FileName = Me.dlgSave.FileName 79 'Speichere die Datei mit dem im Element File, Save vorhandenen Code. 80 mnuFileSave_Click(sender, e) 81 End If 82 End Sub 83 Private Sub mnuFileExit_Click(ByVal sender As System.Object, _ 84 ByVal e As System.EventArgs) _ 85 Handles mnuFileExit.Click 86 Me.Close() 87 End Sub
Da dieser Code sehr lang aussieht, wollen wir ihn Schritt für Schritt durchgehen und uns ansehen, was er macht. Fangen wir mit dem Befehl FILE, NEW in den Zeilen 1 bis 15 an. Wir haben die Eigenschaft Dirty hinzugefügt, damit wir ermitteln können, ob der Text geändert wurde. Wenn er geändert wurde, müssen wir sicherstellen, dass der Anwender diese Änderungen nicht aus Versehen zerstört. Daher geben wir eine Warnmeldung aus, wenn der Anwender eine neue Datei anfragt, nachdem er an der ersten Datei Änderungen vorgenommen hat. Dabei handelt es sich um guten Programmierstil: Vielleicht hat der Anwender nur aus Versehen eine Taste gedrückt und wir schützen ihn nun vor den Konsequenzen. Wenn die Datei unsauber (geändert) ist, fragen wir, ob es in Ordnung ist, wenn diese Änderungen verloren gehen (Zeilen 3 bis 9) und wir eine neue Datei erstellen. Ist es in Ordnung (Zeile 10) oder wurden keine Änderungen vorgenommen (Zeile 13), dann erstellen wir eine NewFile. Die Prozedur NewFile richten wir weiter unten ein. Da es viele mögliche Stellen gibt, an denen wir eine neue Datei anlegen könnten, ist es sinnvoll, diesen Code in eine eigene Prozedur zu setzen.
564
Ströme und Dateien
Nun kommen wir zum Menüelement FILE, OPEN (Zeilen 16 bis 40). Hier beginnen wir damit, die Klassen für die Arbeit mit Dateien zu verwenden. Wir werden ein FileStream-Objekt (Zeile 19) und ein StreamReader-Objekt (Zeile 20) verwenden. Denken Sie daran, dass ein FileStream-Objekt einfach eine Sonderform eines Stream-Objekts ist. Nachdem wir diese beiden Variablen deklariert haben, ermöglichen wir es dem Anwender, eine Datei auszuwählen, die geöffnet werden soll (Zeile 21). Hierfür haben wir dem Formular das Steuerelement FileOpenDialog hinzugefügt. Wenn der Anwender eine Datei ausgewählt hat, können wir sie öffnen. Achten Sie darauf, dass wir die Datei in einem Try...End Try-Block zu öffnen versuchen (Zeilen 23-38). Das Öffnen von Dateien ist fehleranfällig: Vielleicht gibt es die Datei nicht oder sie ist nicht verfügbar; sie kann auch durch einen anderen Anwender gesperrt sein usw. In all diesen Fällen wird eine Ausnahme ausgelöst. Daher sollten Sie Dateien immer in einem Try...End Try-Block öffnen. Fangen Sie Ausnahmen unbedingt mit einem Try...End Try-Block ab, wenn Sie Dateien öffnen, löschen oder speichern. Verwenden Sie immer den Finally-Teil des Blocks, um die Datei und alle Ströme, Reader und Writer zu schließen, die Sie für den Dateizugriff verwendet haben. Innerhalb des Try...End Try-Blocks rufen wir zuerst (Zeile 24) den Namen der zu öffnenden Datei aus dem Dialogfenster ab (einschließlich des Pfads). Dieser Name wird in der Titelleiste des Note-Fensters angezeigt. Anschließend (Zeile 25) öffnen wir die Datei mit der Shared-Methode OpenRead. Denken Sie daran, dass Shared-Methoden keine Instanz einer Datei, sondern die Datei selbst benötigen. Da dies ein Bereich ist, in dem eine Ausnahme auftreten könnte, ist es sinnvoll, dass er sich im Try...End Try-Block befindet. Die Variable oFile in der Zeile 25 hat den Typ FileStream, repräsentiert also eine Folge von Bytes in der Datei (d.h. einen Strom). Um diesen Strom zu lesen, übergeben wir dieses FileStreamObjekt an den Konstruktor für ein StreamReader-Objekt (Zeile 26). Mit diesem StreamReader (oReader) können wir den gesamten Inhalt von FileStream lesen (Zeile 27) und ihn an die Haupt-TextBox auf dem Formular übergeben. Falls während des Öffnens oder Lesens der Datei eine Ausnahme auftritt (am wahrscheinlichsten ist dies beim Öffnen), fangen wir diese in der Zeile 28 ab und zeigen Sie dem Anwender in den Zeilen 30 bis 33 an. Ein idealer Ausnahmebehandler würde versuchen, dem Anwender beim Beheben der Ausnahme zu helfen. Wenn der Anwender z.B. die Datei aus Sicherheitsgründen nicht öffnen darf (eine SecurtyException wird ausgelöst), dann können Sie ihm mitteilen, dass er den Eigentümer des Dokuments um eine Berechtigung bitten muss, bevor er die Datei zu öffnen versucht. In den Zeilen 36 und 37 schließen wir die StreamReader- und FileStream-Objekte wieder. Schließen Sie diese Objekte immer, damit Sie sicher wissen, dass sie nicht geöffnet bleiben und möglicherweise ein anderes Programm daran hindern, die Datei zu betrachten und zu ändern.
565
Mit dem .NET Framework arbeiten
Am Ende des Menüelements FILE, OPEN sollten wir eine Datei zum Bearbeiten ausgewählt haben und deren Inhalt sollte sich in der TextBox befinden. Nun müssen wir uns um die beiden Arten, die Datei zu sichern, kümmern: das Menüelement FILE, SAVE und das Menüelement FILE, SAVE AS. FILE, SAVE ist das kompliziertere Verfahren. Sie werden feststellen, dass das Menüelement FILE, SAVE AS einfach dieses Menüelement verwendet, um seine Aktion durchzuführen. Den Code für das Menüelement FILE, SAVE finden Sie in den Zeilen 41 bis 72. Zuerst prüfen wir, ob der Anwender die Datei bereits benannt hat (Zeile 45). Ist dies nicht der Fall, dann rufen wir die Routine mnuFileSaveAs_Click auf (d.h. das Menüelement FILE, SAVE AS), um einen Namen zu bekommen (Zeile 70). Sofern die Datei bereits einen Namen hat, öffnen wir sie in der Zeile 50 zum Schreiben. Wenn sie geöffnet ist, erzeugen wir einen StreamWriter, um in die Datei zu schreiben (Zeile 52), und schreiben den Inhalt der TextBox in die neu erstellte Datei (Zeile 54). Da es in der Datei nun keine ungespeichertern Änderungen mehr gibt, setzen wir das Flag Dirty auf False. Falls beim Schreiben in die Datei eine Ausnahme auftritt, fangen wir sie in der Zeile 57 ab. Ebenso wie bei den Ausnahmen, die beim Lesen der Datei auftreten können, zeigen wir auch hier einfach die Ausnahme an (Zeilen 58 bis 62). Auch hier ist es wieder wichtig, das StreamWriter- und das FileStream-Objekt zu schließen (Zeilen 65 und 66). Beim Schreiben in die Datei ist dies sogar noch wichtiger als beim Lesen der Datei, da die Änderungen möglicherweise erst dann geschrieben werden, wenn der Strom und die Datei geschlossen werden. Am Ende des Menüelements FILE, SAVE sollte der Inhalt der TextBox in die angefragte Datei geschrieben werden. Sie können dies überprüfen, indem Sie die Datei anschließend im Notepad oder einem anderen Editor öffnen. Der Code für das Menüelement FILE, SAVE AS ist wesentlich einfacher als der für FILE, SAVE, da er auf der in der anderen Routine bereits getanen Arbeit aufbaut. Dieser Code generiert einfach einen neuen Dateinamen für die Datei. Mit FileSaveDialog (Zeile 76), das wir dem Formular hinzugefügt haben, ermöglichen wir es dem Anwender, für die Datei einen Speicherort und einen Namen anzugeben. Wenn der Anwender OK anklickt, um das Dialogfenster zu schließen, setzen wir die Eigenschaft FileName auf den neuen Namen (Zeile 78) und rufen die Routine mnuFileSave_Click auf, um die Datei zu speichern (Zeile 80). Wir hätten auch den gesamten Inhalt des SAVE-Codes hier platzieren können. Allerdings hätten wir dafür sehr viel Code mehrmals schreiben müssen. Schließlich haben wir noch den Code für den Befehl FILE, EXIT. Hier schließen wir das Formular einfach und beenden damit die Anwendung (Zeile 86).
566
Ströme und Dateien
Ein Großteil des Codes, der mit Stream zu tun hat, befindet sich (wie erwartet) im Menü FILE, wo er das Öffnen und Speichern von Dateien ermöglicht. Das Listing 17.6 zeigt den Code für das Menü EDIT, mit dem Sie in einer Anwendung wie z.B. Notepad oder Word ausschneiden, kopieren und einfügen können.
Listing 17.6: Befehle im Menü Edit 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Private Sub mnuEditCut_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles mnuEditCut.Click Clipboard.SetDataObject(Me.txtText.SelectedText) Me.txtText.SelectedText = "" End Sub Private Sub mnuEditCopy_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles mnuEditCopy.Click Clipboard.SetDataObject(Me.txtText.SelectedText) End Sub Private Sub mnuEditPaste_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles mnuEditPaste.Click Me.txtText.SelectedText = _ CType(Clipboard.GetDataObject.GetData(DataFormats.Text), String) End Sub
Glücklicherweise ist der Code für die EDIT-Befehle wesentlich kürzer als der für die FILE-Befehle. Außerdem sehen wir hier ein weiteres mächtiges Objekt des .NET-Frameworks: das Clipboard-Objekt. Wir fangen mit dem Code für EDIT, CUT an und kopieren den in der TextBox ausgewählten Text in die Zwischenablage (Zeile 3). Die Klasse Clipboard ist eine Abstraktion der Windows-Zwischenablage, die von allen Anwendungen gemeinsam genutzt wird. Diese Klasse hat Methoden, mit denen Sie der Zwischenablage Text, Bilder und andere Informationen programmatisch zuweisen und diese Informationen aus der Zwischenablage abrufen können. Die Tabelle 17.9 beschreibt diese beiden Methoden. Nachdem wir die Informationen in die Zwischenablage kopiert haben, löschen wir den gewählten Text in der TextBox (Zeile 4). Dadurch erzeugen wir das Verhalten, das wir beim Ausschneiden von Text erwarten: Der Text wird der Zwischenablage hinzugefügt und aus dem Formular gelöscht.
567
Mit dem .NET Framework arbeiten
Methode
Beschreibung
SetDataObject
Weist der Zwischenablage Informationen zu. Da diese Methode ein Objekt entgegennimmt, können Sie alles in die Zwischenablage schreiben. Beachten Sie, dass es zwei Versionen dieser Methode gibt, dass es sich also um eine überladene Methode handelt. Die zweite Version enthält einen booleschen Wert, der ermittelt, ob der Inhalt erhalten bleiben soll, nachdem die Anwendung beendet wurde. Das normale Verhalten besteht darin, die Informationen in der Zwischenablage auch nach dem Programmende beizubehalten.
GetDataObject
Mit dieser Methode rufen Sie entweder Informationen aus der Zwischenablage oder Informationen über den Inhalt der Zwischenablage ab. Sie gibt das Interface IDataObject zurück, mit dem dann die eigentlichen Informationen abgerufen werden. Die Tabelle 17.10 beschreibt die wichtigsten Methoden dieses Interfaces.
Tabelle 17.9: Methoden der Klasse Clipboard Methode
Beschreibung
GetData
Ruft die in der Zwischenablage gespeicherten Informationen ab. Alternative Formen ermöglichen die Umwandlung der Informationen in ein bestimmtes Format.
GetFormats
Mit dieser Methode wird ermittelt, welche Formate in der Zwischenablage gespeichert sind. Wenn z.B. ein Bild in die Zwischenablage kopiert wurde, gibt diese Methode alle Formate zurück, die beim Einfügen des Bildes möglich sind.
GetDataPresent
Ermittelt, ob in der Zwischenablage irgendwelche Daten eines bestimmten Formats gespeichert sind. Damit können Sie feststellen, ob Sie es dem Anwender erlauben möchten, Informationen in Ihre Anwendung einzufügen. Wenn in der Zwischenablage z.B. Bildinformationen gespeichert sind, erlauben wir es dem Anwender nicht, diese Informationen in unsere Note-Anwendung einzufügen.
Tabelle 17.10: Methoden von IDataObject
Der Code für EDIT, COPY hat Ähnlichkeit mit dem für den Befehl EDIT, CUT (Zeile 9). Beide Befehle bewirken, dass Informationen in die Zwischenablage kopiert werden, aber bei COPY löschen wir den ausgewählten Text nicht. Der Code für den Befehl EDIT, PASTE sieht komplex aus, ist jedoch ganz einfach, wenn Sie die Zeilen 14 und 15 in einzelne Schritte zerlegen. Beim Abrufen von Informationen aus der Zwischenablage besteht der erste Schritt darin, mit GetDataObject das in der Zwischenablage gespeicherte IDataObject abzurufen. Anschließend rufen wir die Informationen mit der Methode GetData von IDataObject ab und verlangen dabei, dass das Format der Informationen das einfache Textformat ist. Trotz dieses Formats sind die
568
Ströme und Dateien
Informationen ein Objekt. Daher müssen wir sie in eine Zeichenkette umwandeln, bevor wir sie der TextBox zuweisen können. Hier haben wir die Funktion CType verwendet, aber auch die Funktion CStr wäre möglich gewesen. Schließlich nehmen wir den Text und ersetzen damit den SelectedText in der TextBox. Hier verwenden wir nicht Text, sondern SelectedText, da der Inhalt von TextBox beim Ersetzen von Text nur aus den Informationen aus der Zwischenablage besteht. Dies ist nicht das Verhalten, das man bei der Zwischenablage normalerweise erwartet. Indem wir SelectedText verwenden, ersetzen wir nur den hervorgehobenen Text. Wenn nichts hervorgehoben ist, wird der Text aus der Zwischenablage an der Position des Textcursors eingefügt. Das letzte Menü, das wir hier verwenden, ist das Menü HELP. Das Listing 17.7 zeigt den Code für die Prozedur HELP, ABOUT.
Listing 17.7: Der Befehl Help, About 1 Private Sub mnuHelpAbout_Click(ByVal sender As System.Object, _ 2 ByVal e As System.EventArgs) Handles mnuHelpAbout.Click 3 Dim sMessage As String 4 sMessage = "Note -- a simple text editor" & _ 5 ControlChars.CrLf & _ 6 "Original in Teach Yourself Visual Basic .NET in 21 Days." & _ 7 ControlChars.CrLf & _ 8 "copyright 2001. Full rights to extend provided by the author." 9 MessageBox.Show(sMessage, _ 10 "Note", _ 11 MessageBoxButtons.OK, _ 12 MessageBoxIcon.Information) 13 End Sub
Für unser Beispiel ist der Befehl HELP, ABOUT zwar nicht erforderlich, aber der Vollständigkeit halber fügen wir ihn trotzdem ein. Dabei handelt es sich um eine einfache Routine, die eine Zeichenkette erstellt (Zeilen 4 bis 8) und sie dem Anwender anzeigt (Zeilen 9 bis 12). In einer ausgefeilteren Anwendung würden Sie dem Anwender ein richtiges Formular anzeigen. Das Listing 17.8 zeigt schließlich noch drei Routinen, die Sie der Anwendung hinzufügen können, um ihrer Funktionalität den letzten Schliff zu geben.
569
Mit dem .NET Framework arbeiten
Listing 17.8: Zusätzlicher Code für die Note-Anwendung 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
Public Sub NewFile() Me.txtText.Text = "" Me.FileName = "Untitled" Me.Dirty = False End Sub Private Sub txtText_TextChanged(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles txtText.TextChanged Me.Dirty = True End Sub Private Sub frmNote_Closing(ByVal sender As System.Object, _ ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing If Me.Dirty = True Then If MessageBox.Show( _ "You have made changes to the file that will be lost. Continue?", _ "New File", _ MessageBoxButtons.YesNo, _ MessageBoxIcon.Question, _ MessageBoxDefaultButton.Button2) = DialogResult().No Then e.Cancel = True End If End If End Sub
Wie Sie bereits wissen, wird die Prozedur NewFile von der Prozedur mnuFileNew_Click aufgerufen, wenn wir eine neue Datei anlegen. Diese Prozedur weist die Standardwerte für die neue Datei zu: Sie löscht die Informationen in der TextBox (Zeile 2), setzt die Eigenschaft FileName auf "Untitled" (Zeile 3) und die Eigenschaft Dirty auf False (Zeile 4). Wir möchten, dass die Anwendung und der Anwender wissen, ob am Dokument Änderungen vorgenommen wurden. Hierfür ist die Eigenschaft Dirty zuständig. Mithilfe des Ereignisses TextChanged können wir den Wert dieser Eigenschaft ändern. Dieses Ereignis wird ausgelöst, wenn der Anwender irgendwelche Änderungen vornimmt, indem er den Text bearbeitet, Informationen aus der Zwischenablage kopiert oder etwas eingibt. Das Ereignis setzt dann das Flag Dirty auf True.
570
Mit den Grafikklassen zeichnen
Zuletzt möchten wir den Anwender wieder vor den Folgen eines versehentlichen Tastendruckes schützen. Das Ereignis Closing tritt auf, wenn das Formular geschlossen wird. An dieser Stelle sollten wir ermitteln, ob der Anwender die Informationen speichern möchte. Da wir den Anwender nur dann fragen sollten, ob er die Änderungen speichern möchte, wenn er tatsächlich welche vorgenommen hat, müssen wir zuerst herausfinden, ob das Dokument seit dem letzten Speichern geändert wurde (Zeile 14). Ist dies nicht der Fall, so brauchen wir nichts zu machen und können das Formular schließen. Wurden jedoch Änderungen vorgenommen, so müssen wir den Anwender fragen, ob er diese vor dem Beenden des Programms speichern möchte. Die Zeilen 15 bis 20 erstellen eine MessageBox mit den Schaltflächen YES und NO und zeigen sie an. Wenn der Anwender die Schaltfläche NO wählt, möchte er die Datei nicht schließen. Dann können wir dieses Ereignis (und damit das Schließen des Formulars) abbrechen, indem wir die Eigenschaft Cancel des EventArgs-Objekts (das an alle Ereignisse übergeben wird) auf True setzen (Zeile 21). Und das war's: Nur ungefähr 100 Codezeilen und schon haben Sie einen funktionierenden Texteditor, den Sie als Grundlage für eine vollständigere Implementierung verwenden können. In dieser Version können Sie Dateien öffnen und speichern und die Zwischenablage verwenden. Sie haben mithilfe von FileStream-, StreamReader- und StreamWriterObjekten die Textdateien verändert, mit denen die Anwendung arbeitet. Fast auf dieselbe Art können Sie auch eine Anwendung erstellen, die Binärdateien, wie sie z.B. die Grafikklassen erstellen, öffnet und speichert.
17.2 Mit den Grafikklassen zeichnen Visual Basic .NET (eigentlich das .NET-Framework) besitzt eine Vielfalt von Zeichenwerkzeugen, mit denen Sie auf praktisch allen Formularoberflächen Linien, Kästen, Kreise und andere Formen zeichnen können. Auf diese Werkzeuge greifen Sie mit den Klassen im Namensraum System.Drawing und in verwandten Namensräumen zu. Die Werkzeuge bieten zahlreiche Fähigkeiten zum Zeichnen von Bildern und für die Arbeit mit verschiedenen Bilddateien. System.Drawing enthält viele der wichtigsten Klassen, die Sie für jede Art des Zeichnens benötigen, z.B. Color, Brush und Image. Etwas später werden wir uns mit diesen wichtigen Klassen befassen. Der zweitwichtigste Namensraum, der mit Grafiken zu tun hat, ist System.Drawing.Drawing2D. Diese Klasse enthält die Klassen, die mit dem eigentlichen Zeichnen von Linien und Formen zu tun haben. Nicht nur in der heutigen Lektion werden Sie viele dieser Klassen verwenden, sondern Sie benötigen sie auch immer dann, wenn Sie einem Formular oder einem Bild einfache Formen hinzufügen.
571
Mit dem .NET Framework arbeiten
Die Grafikklassen Öffnen Sie eine Windows-Anwendung und starten Sie den Objektbrowser (ANSICHT, ANDERE FENSTER, OBJEKTBROWSER). Finden Sie den Namensraum System.Drawing und öffnen Sie ihn (siehe Abbildung 17.2).
Abbildung 17.2: Der Namensraum System.Drawing
Zunächst scheint es eine überwältigende Anzahl an Klassen, Strukturen und Aufzählungen zu geben. Viele dieser Klassen werden jedoch nur selten verwendet. Eigentlich müssen Sie nur einige wenige Elemente in diesem Namensraum wirklich kennen und die wichtigsten davon sind in der Tabelle 17.11 beschrieben. Element
Typ
Beschreibung
Bitmap
Klasse
Bitmap repräsentiert ein Bild wie z.B. ein GIF, BMP oder JPG. Diese Klasse wird beim Laden eines Bildes und bei der Arbeit mit Bildern häufig verwendet.
Brush
Klasse
Wird verwendet, wenn ein Bereich mit einer Farbe ausgefüllt wird. Es gibt verschiedene Arten von Pinselklassen, u.a. zum soliden Füllen mit einer Farbe oder zum Füllen mit einer Struktur. Eine Struktur basiert normalerweise auf einer Bitmap.
Color
Struktur
Die Struktur Color enthält Informationen über alle gebräuchlichen, benannten Farben sowie die Möglichkeit, neue Farben zu erstellen.
Tabelle 17.11: Die wichtigsten Klassen und Strukturen im Namensraum System.Drawing
572
Mit den Grafikklassen zeichnen
Element
Typ
Beschreibung
Font
Klasse
Die Klasse Font verwenden Sie, wenn Sie auf einem Bild Text zeichnen möchten.
Graphics
Klasse
Die wichtigste der System.Drawing-Klassen. Sie enthält alle Methoden zum Zeichnen und ist die Oberfläche, auf der gezeichnet wird.
Pen
Klasse
Wird zum Zeichnen farbiger Linien verwendet.
Point
Struktur
Stellt eine bestimmte Stelle auf einer Zeichenoberfläche dar und enthält die x- und y-Koordinaten dieses Punktes.
Rectangle
Struktur
Stellt einen rechteckigen Bereich an einer gegebenen Stelle dar. Damit wird ein Bereich definiert, der an einem bestimmten Punkt beginnt und eine gegebene Breite und Höhe hat.
Size
Struktur
Repräsentiert einen rechteckigen Bereich. Dieser Bereich hat eine Höhe und eine Breite.
Tabelle 17.11: Die wichtigsten Klassen und Strukturen im Namensraum System.Drawing (Forts.)
Die Klassen in der Tabelle 17.11 werden Sie häufig verwenden. Wenn Sie z.B. auf dem Bildschirm ein Rechteck zeichnen, dann zeichnen Sie es mit einem bestimmten Pen und einer bestimmten Color auf einem Graphics-Objekt innerhalb eines Rectangle. Das Rectangle besteht wiederum aus einem Point und einer Size. Schließlich füllen Sie das Rechteck noch mit dem gewählten Brush. Um die Verwendung der Grafikklassen zu veranschaulichen, erstellen wir ein einfaches Zeichenprogramm. Die Abbildung 17.3 zeigt diese Anwendung während der Ausführung. Sie können die Anwendung später so erweitern, dass Sie komplexere Bilder zeichnen können.
Abbildung 17.3: Das Programm Scribble bei der Arbeit
573
Mit dem .NET Framework arbeiten
Zuerst erstellen wir ein neues Windows-Anwendungsprojekt. Nennen Sie das Projekt Scribble. Wie Abbildung 17.3 zeigt, stehen dem Anwender drei Zeichenwerkzeuge zur Verfügung: ein Stift, Formen und Text. Jedes dieser drei Werkzeuge hat eigene Optionen, die sich darauf auswirken, wie dieses Werkzeug zeichnet. Außerdem kann jedes Werkzeug in einer ausgewählten Farbe zeichnen. Der große weiße Zeichenbereich ist das Steuerelement PictureBox. Ändern Sie den Namen des Formulars wie üblich. Schließen Sie den Formular-Designer. Setzen Sie den Namen des Formulars im Projektmappen-Explorer auf frmScribble. Gehen Sie in die Codeansicht und ändern Sie alle Referenzen auf Form1 im Formular auf frmScribble. Klicken Sie das Projekt im Projektmappen-Explorer mit der rechten Maustaste an und wählen Sie EIGENSCHAFTEN. Auf der Registerkarte ALLGEMEINE EIGENSCHAFTEN setzen Sie das Startobjekt auf frmScribble. Kompilieren Sie die Anwendung und führen Sie sie aus, um sicherzustellen, dass alle Änderungen korrekt vorgenommen wurden. Nun fügen wir dem Formular Steuerelemente hinzu. Öffnen Sie das Formular in der Entwurfsansicht und fügen Sie die in der Tabelle 17.12 gezeigten Steuerelemente hinzu. Verwenden Sie die Abbildung 17.3 als Hilfestellung, aber machen Sie sich keine Sorgen, wenn sich nicht alle Steuerelemente an genau derselben Stelle befinden. Steuerelement
Eigenschaft
Wert
Form
Height
332
Width
500
Text
Scribble
BorderStyle
FixedSingle
Menu
Name
mnuMain
File Menu
Name
mnuFile
File, New
Name
mnuFileNew
Shortcut
CtrlN
Text
&New
Name
mnuFileOpen
Shortcut
CtrlO
Text
&Open…
Name
mnuFileSave
Shortcut
CtrlS
Text
&Save
File, Open
File, Save
Tabelle 17.12: Steuerelemente der Anwendung Scribble
574
Mit den Grafikklassen zeichnen
Steuerelement
Eigenschaft
Wert
File, Exit
Name
mnuFileExit
Shortcut
CtrlQ
Text
E&xit
Name
dlgOpen
DefaultExt
Bmp
Filter
Bitmap files|*.bmp|PNG files|*.png
Name
dlgSave
DefaultExt
Bmp
Filename
Scribble1
Filter
Bitmap files|*.bmp|PNG files|*.png
Dock
DockStyle.Left
Width
64
Name
optPen
Appearance
Button
Checked
True
Location
8,8
Size
48,36
Text
Pen
TextAlign
MiddleCenter
Name
optShape
Appearance
Button
Location
8, 48
Size
48,36
Text
Shape
TextAlign
MiddleCenter
Name
optText
Appearance
Button
Location
8,88
Size
48,36
Text
Text
OpenFileDialog
SaveFileDialog
RadioButton
RadioButton
RadioButton
RadioButton
Tabelle 17.12: Steuerelemente der Anwendung Scribble (Forts.)
575
Mit dem .NET Framework arbeiten
Steuerelement
PictureBox
Panel
Label
ComboBox
Panel
Label
NumericUpDown
Panel
Eigenschaft
Wert
TextAlign
MiddleCenter
Name
picDraw
BackColor
White
BorderStyle
Fixed3D
Dock
Fill
Name
pnlOptions
Dock
Bottom
Height
72
Name
lblColor
Location
8, 8
Size
48, 16
Text
Color:
Name
cboColors
DropDownStyle
DropDownList
Location
8, 24
Width
136
Name
pnlPenOptions
Location
160, 8
Size
274, 56
Name
lblPenWidth
Location
8, 8
Size
80, 16
Text
Pen Width:
Name
updPenWidth
Location
96, 8
Maximum
10
Minimum
1
Size
48, 20
Value
1
Name
pnlShapeOptions
Location
160, 8
Tabelle 17.12: Steuerelemente der Anwendung Scribble (Forts.)
576
Mit den Grafikklassen zeichnen
Steuerelement
Label
DropDown
Label
NumericUpDown
Label
NumericUpDown
Panel
Eigenschaft
Wert
Size
274, 56
Name
lblShapeType
Location
8, 8
Size
48, 16
Text
Type:
Name
cboShapeType
DropDownStyle
DropDownList
Location
64, 8
Width
121
Name
lblShapeHeight
Location
8, 32
Size
48, 16
Text
Height:
Name
updShapeHeight
Location
64, 32
Maximum
1000
Minimum
1
Size
48, 20
Value
20
Name
lblShapeWidth
Location
128, 32
Size
48, 16
Text
Width:
Name
updShapeWidth
Location
184, 32
Maximum
1000
Minimum
1
Size
48, 20
Value
20
Name
pnlTextOptions
Tabelle 17.12: Steuerelemente der Anwendung Scribble (Forts.)
577
Mit dem .NET Framework arbeiten
Steuerelement
Label
TextBox
Label
DropDown
Label
NumericUpDown
Eigenschaft
Wert
Location
160, 8
Size
274, 56
Name
lblText
Location
8, 8
Size
40, 16
Text
Text:
Name
txtText
Location
48, 8
Size
208, 20
Text
Scribble!
Name
lblTextFont
Location
8, 32
Size
40, 16
Text
Font:
Name
cboTextFont
DropDownStyle
DropDownList
Location
48, 32
Width
104
Name
lblFontSize
Location
160, 32
Size
40, 16
Text
Size:
Name
updFontSize
Location
208, 32
Maximum
72
Minimum
6
Size
48, 20
Value
12
Tabelle 17.12: Steuerelemente der Anwendung Scribble (Forts.)
578
Mit den Grafikklassen zeichnen
Denken Sie daran, dass Sie das Steuerelement RadioButton zur Werkzeugtafel hinzufügen, indem Sie es in der Werkzeugsammlung anklicken und auf die Tafel ziehen. Dadurch stellen Sie sicher, dass das Steuerelement wirklich in der Tafel enthalten ist. Dies sollten Sie auch bei den Steuerelementen auf der Optionstafel machen. Achten Sie auch darauf, dass die drei Optionstafeln (pnlPenOptions, pnlShapeOptions und pnlTextOptions) dieselbe Größe haben und sich an derselben Stelle befinden. Zur Laufzeit ist jeweils nur eines davon sichtbar. Allerdings kann es schwierig sein, dies zur Entwurfszeit einzurichten. Am einfachsten ist es, diese Tafeln der Reihe nach zu bearbeiten. Wenn Sie mit einer fertig sind, setzen Sie die Eigenschaft Location so, dass sich die Tafel außerhalb des Bildschirms befindet, indem Sie z.B. die Eigenschaft Left auf -10000 setzen. Wenn alle Tafeln fertig sind, setzen Sie Location für alle drei Tafeln wieder auf den ursprünglichen Wert.
Worauf kann ich zeichnen? Sie können auf beinahe allen Windows-Forms-Steuerelementen und auf vielen WebForms-Steuerelementen zeichnen. Dies bedeutet, dass Sie die vorhandenen Steuerelemente leicht verändern und fast allen Bestandteilen Ihrer Programme einfach eine Grafik oder ein Symbol hinzufügen können. Dies ist sowohl nützlich als auch flexibel, da Sie Ihre Formulare und Steuerelemente damit leicht anpassen können, indem Sie ihnen eine eindeutige Grafik hinzufügen und sie individuell gestalten. Der erste Schritt beim Zeichnen besteht immer darin, ein Graphics-Objekt zu holen. Dieses Objekt können Sie entweder mit der Methode CreateGraphics oder als Teil einiger Ereignisse wie z.B. des Ereignisses Paint holen. Die Methode CreateGraphics wird von allen Steuerelementen einschließlich der Formulare gemeinsam genutzt. Sobald Windows feststellt, dass es alle oder einen Teil der Formulare oder Steuerelemente neu zeichnen muss, löst es für das Element, das neu gezeichnet werden soll, das Ereignis Paint aus. Dies geschieht dann, wenn ein neues Fenster erstellt wird oder wenn ein anderes Formular vor Ihr Fenster geschoben wird. Wenn Sie z.B. eine MessageBox angezeigt haben und diese geschlossen wird, dann muss der Teil des Formulars, der von der MessageBox verdeckt wurde, neu gezeichnet werden. Dafür wird das Ereignis Paint für alle verdeckten Steuerelemente und für das Formular aufgerufen. Für ein Formular sieht das Ereignis Paint folgendermaßen aus: Private Sub frmScribble_Paint(ByVal sender As Object, _ ByVal e As System.Windows.Forms.PaintEventArgs) _ Handles MyBase.Paint End Sub
579
Mit dem .NET Framework arbeiten
In diesem Beispiel heißt das Formular frmScribble. Wie bei allen Ereignissen werden uns auch hier zwei Objekte übergeben: der Absender und das EventArgs-Objekt. Beim Ereignis Paint erhalten wir nicht das EventArgs-Basisobjekt, sondern eines seiner Kinder, nämlich PaintEventArgs. Dieses Objekt hat zwei schreibgeschützte Eigenschaften: 쐽
ClipRectangle: Das Rectangle muss neu gezeichnet werden. Dabei können Sie den
Zeichencode so optimieren, dass nicht das ganze Bild, sondern nur der geänderte Bereich aktualisiert wird. Wir werden diesen Parameter in unseren Anwendungen nicht verwenden. 쐽
Graphics: Die Graphics-Oberfläche, auf der gezeichnet wird. Diese Eigenschaft verwenden wir, um die benötigten Formen zu zeichnen.
Alternativ dazu können Sie mit der Methode CreateGraphics eines Steuerelements auch ein eigenes Graphics-Objekt erzeugen: Dim oGraphics As System.Drawing.Graphics oGraphics = picDraw.CreateGraphics()
Unabhängig davon, mit welcher Methode Sie ein Graphics-Objekt erzeugen, können Sie dieses immer dafür verwenden, verschiedene Formen zu zeichnen. In der Anwendung Scribble speichern wir das Graphics-Objekt so, dass es verschiedene Zeichenwerkzeuge verwenden können. Außerdem müssen wir die aktiven und verfügbaren Werkzeuge im Auge behalten. Fügen Sie dem Projekt Scribble den Code im Listing 17.9 hinzu.
Listing 17.9: Die Eigenschaften und der allgemeine Code für die Anwendung Scribble 1 2 3 4 5 6 7 8 9 10 11 12 13 14
580
Public Enum DrawingTools Pen Shape Text End Enum Private m_sFileName As String Private m_bDrawing As Boolean Private m_eCurrentTool As DrawingTools Private Private Private Private
oGraphics As System.Drawing.Graphics oTool As Object sngX As Single sngY As Single
Mit den Grafikklassen zeichnen
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
Public Property FileName() As String Get Return m_sFileName End Get Set(ByVal Value As String) m_sFileName = Value End Set End Property Public Property Drawing() As Boolean Get Return m_bDrawing End Get Set(ByVal Value As Boolean) m_bDrawing = Value End Set End Property Public Property CurrentTool() As DrawingTools Get Return m_eCurrentTool End Get Set(ByVal Value As DrawingTools) m_eCurrentTool = Value 'Lösche das vorhandene Werkzeug. If Not oTool Is Nothing Then CType(oTool, IDisposable).Dispose() End If End Set End Property Private Sub frmScribble_Load(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles MyBase.Load 'Richte die ComboBox Color ein. FillLists() 'Erstelle das Graphics-Objekt, auf dem wir zeichnen werden. Me.picDraw.Image = New Bitmap(picDraw.Width, _ picDraw.Height, _ System.Drawing.Imaging.PixelFormat.Format24bppRgb) Me.oGraphics = Graphics.FromImage(Me.picDraw.Image) 'Setze die Hintergrundfarbe auf Weiß. Me.oGraphics.Clear(Color.White) 'Setze das erste Werkzeug auf Pen. optPen_Click(Nothing, Nothing)
581
Mit dem .NET Framework arbeiten
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
582
End Sub Private Sub optPen_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles optPen.Click 'Setze das Werkzeug auf einen Stift. Me.CurrentTool = DrawingTools.Pen 'Verberge die Tafeln aller anderen Werkzeuge. pnlPenOptions.Visible = True pnlShapeOptions.Visible = False pnlTextOptions.Visible = False End Sub Private Sub optShape_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles optShape.Click 'Setze das Werkzeug auf eine Form. Me.CurrentTool = DrawingTools.Shape 'Verberge die Tafeln aller anderen Werkzeuge. pnlPenOptions.Visible = False pnlShapeOptions.Visible = True pnlTextOptions.Visible = False End Sub Private Sub optText_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles optText.Click 'Setze das Werkzeug auf Text. Me.CurrentTool = DrawingTools.Text 'Verberge die Tafeln aller anderen Werkzeuge. pnlPenOptions.Visible = False pnlShapeOptions.Visible = False pnlTextOptions.Visible = True End Sub Private Sub FillLists() With cboColors.Items .Add("Black") .Add("Red") .Add("Green") .Add("Blue") End With cboColors.SelectedIndex = 0 With cboShapeType.Items .Add("Rectangle")
Mit den Grafikklassen zeichnen
107 108 109 110 111 112 113 114 115 116 117
.Add("Ellipse") End With cboShapeType.SelectedIndex = 0 With cboTextFont.Items .Add("Arial") .Add("Times New Roman") .Add("Courier New") End With cboTextFont.SelectedIndex = 0 End Sub
Zuerst deklarieren wir eine neue Aufzählung (Zeilen 1 bis 5). Mit dieser Aufzählung verfolgen wir die verfügbaren Werkzeuge und stellen sicher, dass die (weiter unten deklarierte) Eigenschaft CurrentTool nur auf eine der zulässigen Einstellungen gesetzt werden kann. Wenn Sie später andere Zeichenwerkzeuge hinzufügen möchten, sollten Sie diese der Aufzählung hinzufügen. Die Zeilen 7 bis 9 sind die Elementvariablen für die drei Eigenschaften, die vom Formular bereitgestellt werden. Die drei Eigenschaften (Zeilen 16 bis 23, 25 bis 32 und 34 bis 45) sind im Grunde genommen einfache Eigenschaften, allerdings mit einer Ausnahme. Im Set-Teil der Eigenschaft CurrentTool fallen die Zeilen 41 bis 43 auf. Zeichenwerkzeuge wie z.B. Stifte, Pinsel und Schriftarten haben in Windows eine Sonderstellung. Nur eine beschränkte Anzahl dieser Komponenten steht zur Verfügung. Daher sollten Sie immer daran denken, die Methode Dispose auf diesen Komponenten aufzurufen, damit sie für den nächsten Anwender frei werden. Normalerweise können Sie Dispose auf diesen Objekten direkt aufrufen. Der Code in der Zeile 42 wird benötigt, da oTool in der Zeile 12 als Objekt deklariert wurde. Dies ist ein Beispiel dafür, dass das Schreiben generischen Codes manchmal dazu führt, dass Sie später mehr Arbeit haben. Das Ereignis Form Load (Zeilen 47 bis 61) ist der Ort, an dem wir das Formular einrichten, indem wir DropDownLists laden und das Bild zum Zeichnen vorbereiten. Die Zeile 51 ruft die Routine FillLists auf, die wir später schreiben werden. Anschließend erstellen wir in den Zeilen 53 bis 55 ein neues BitmapObjekt und weisen es der PictureBox zu. Auf diesem Bitmap-Objekt werden wir zeichnen. Die verwendeten Parameter definieren die Größe des neuen Bildes und den Bitmap-Typ. Hier erstellen wir eine 24-Bit-Grafik, was bedeutet, dass wir aus 16 777 216 Farben auswählen können. Die Zeile 56 extrahiert das Graphics-Objekt aus der Bitmap und weist es dem Formular zu. Das Graphics-Objekt werden wir weiter unten beim Zeichnen verwenden. Um eine schöne und saubere Zeichenoberfläche anzulegen, setzen
583
Mit dem .NET Framework arbeiten
wir das Graphics-Objekt mit Clear auf White. Damit wir auf jeden Fall ein Werkzeug ausgewählt haben, wählen wir das Werkzeug Pen. Die nächsten drei Routinen führen dieselbe Aufgabe aus: Sie werden dann verwendet, wenn der Anwender ein neues Werkzeug auswählt. Die Routinen setzen CurrentTool auf das gewünschte Werkzeug und stellen sicher, dass die richtigen Optionen für das aktuelle Werkzeug sichtbar und die übrigen Optionen verborgen sind. Die Prozedur FillLists (Zeilen 96 bis 105) füllen die DropDownList-Steuerelemente mit verschiedenen Farben, Formen und Schriftarten und wählen jeweils das erste Element aus. Sie können hier weitere Farben, Formen und Schriftarten einfügen.
Formen zeichnen Sobald Sie ein Graphics-Objekt haben, müssen Sie wissen, wie Sie es verwenden. Das Graphics-Objekt hat mehrere Methoden, mit denen wir verschiedene Linien, Formen und Text auf eine Oberfläche zeichnen können. Dabei gibt es zwei Kategorien von Methoden: 쐽
Zeichenmethoden: Diese Methoden dienen dem Zeichnen mit einem Stift. Normalerweise wird hier einfach eine leere Form erstellt
쐽
Füllmethoden: Diese Methoden dienen dem Zeichnen mit einem Pinsel. Sie erstellen eine Form, die mit einer bestimmten Farbe oder Struktur gefüllt wird.
Die Tabelle 17.13 beschreibt einige der häufig verwendeten Methoden der Klasse Graphics. Methode
Beschreibung
Clear
Löscht den gesamten Inhalt des Graphics-Objekts und ersetzt ihn durch die angefragte Farbe.
DrawEllipse
Zeichnet auf dem Graphics-Objekt mit dem gewählten Stift eine Ellipse oder einen Kreis. Wenn Höhe und Breite gleich sind, erhalten Sie einen Kreis, ansonsten ein Oval.
DrawLine
Zeichnet auf dem Graphics-Objekt mit dem gewählten Stift eine Linie.
DrawRectangle
Zeichnet auf dem Graphics-Objekt mit dem gewählten Stift ein Rechteck oder Quadrat. Wenn Höhe und Breite gleich sind, erhalten Sie ein Quadrat, ansonsten ein Rechteck.
DrawString
Zeichnet auf dem Graphics-Objekt Text.
Tabelle 17.13: Methoden der Klasse System.Drawing.Graphics
584
Mit den Grafikklassen zeichnen
Methode
Beschreibung
FillEllipse
Füllt mit dem gewählten Pinsel einen ovalen oder runden Bereich auf dem Graphics-Objekt.
FillRectangle
Füllt mit dem gewählten Pinsel einen rechteckigen oder quadratischen Bereich auf dem Graphics-Objekt.
Tabelle 17.13: Methoden der Klasse System.Drawing.Graphics (Forts.)
Nun können wir der Anwendung Scribble den Code hinzufügen, der für das eigentliche Zeichnen zuständig ist. Im Shape- oder Text-Modus zeichnen wir ein Rechteck oder eine Ellipse und fügen dieser Form an der Stelle, die der Anwender anklickt, Text hinzu. Linien werden etwas anders gezeichnet. Eine Linie können wir nicht zu dem Zeitpunkt zeichnen, wenn der Anwender die Maustaste drückt, sondern wir müssen warten, bis er die Maus bewegt. Wenn die Maustaste gedrückt wird, kennzeichnen wir sie als gedrückt. Und sobald der Anwender die Maus mit gedrückter Maustaste bewegt, zeichnen wir. Das Listing 17.10 zeigt den Code für die Ereignisbehandler von PictureBox.
Listing 17.10: Ereignisbehandler von PictureBox 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
Private Sub picDraw_MouseMove(ByVal sender As Object, _ ByVal e As System.Windows.Forms.MouseEventArgs) _ Handles picDraw.MouseMove If Me.Drawing Then 'Ist nur true, wenn das aktuelle Werkzeug ein Stift ist. oGraphics.DrawLine(oTool, sngX, sngY, e.X, e.Y) sngX = e.X sngY = e.Y 'Erzwinge erneutes Zeichnen. Me.picDraw.Refresh() End If End Sub Private Sub picDraw_MouseUp(ByVal sender As Object, _ ByVal e As System.Windows.Forms.MouseEventArgs) _ Handles picDraw.MouseUp 'Nun können wir aufhören, zu zeichnen. Me.Drawing = False End Sub Private Sub picDraw_MouseDown(ByVal sender As Object, _
585
Mit dem .NET Framework arbeiten
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
586
ByVal e As System.Windows.Forms.MouseEventArgs) _ Handles picDraw.MouseDown 'Fange an, zu zeichnen. 'Shape und Text sind Schablonen, Pen arbeitet bei MouseMove. 'Wir müssen das Werkzeug erstellen und entweder 'zeichnen oder uns zum Zeichnen bereit machen. Select Case Me.CurrentTool Case DrawingTools.Shape Select Case Me.cboShapeType.Text Case "Rectangle" oGraphics.FillRectangle( _ New SolidBrush( _ Color.FromName(Me.cboColors.Text)), _ e.X, _ e.Y, _ Me.updShapeWidth.Value, _ Me.updShapeHeight.Value) Case "Ellipse" oGraphics.FillEllipse( _ New SolidBrush( _ Color.FromName(Me.cboColors.Text)), _ e.X, _ e.Y, _ Me.updShapeWidth.Value, _ Me.updShapeHeight.Value) Case Else End Select 'Erzwinge erneutes Zeichnen. Me.picDraw.Refresh() Case DrawingTools.Text 'Erstelle eine Schriftart. oTool = New System.Drawing.Font( _ Me.cboTextFont.Text, Me.updFontSize.Value) 'Zeichne den Text bei der aktuellen Mausposition. oGraphics.DrawString(Me.txtText.Text, _ oTool, _ New SolidBrush( _ Color.FromName(Me.cboColors.Text)), _ e.X, e.Y) 'Erzwinge erneutes Zeichnen. Me.picDraw.Refresh() Case DrawingTools.Pen 'Erstelle den Stift (um bei MouseMove zu zeichnen). oTool = New System.Drawing.Pen( _ Color.FromName(Me.cboColors.Text), _ Me.updPenWidth.Value)
Mit den Grafikklassen zeichnen
68 69 70 71 72
sngX = e.X sngY = e.Y Me.Drawing = True End Select End Sub
Linien werden während des Ereignisses MouseMove der PictureBox gezeichnet. Wir möchten die Linie nur dann zeichnen, wenn wir uns im Zeichenmodus befinden. Daher prüfen wir zuerst die Eigenschaft Drawing (Zeile 4). Diese Eigenschaft wird später im Ereignis MouseDown auf True gesetzt. Im Zeichenmodus zeichnen wir zuerst mit dem aktuellen Stift eine Linie (Zeile 6). Die Variablen sngX und sngY sind private Variablen auf Formularebene, mit denen wir die letzte Mausposition speichern. Jedes Mal, wenn wir eine Linie zeichnen, zeichnen wir sie von dieser gespeicherten Position bis zur aktuellen Mausposition. Die aktuelle Mausposition wird in Form von Parametern der Klasse MouseEventArgs an den Ereignisbehandler übergeben. In den Zeilen 7 und 8 kopieren wir die aktuelle Position in den temporären Speicher, damit sie uns beim nächsten Aufruf des Ereignisses zur Verfügung steht. In Zeile 10 erzwingen wir das Neuzeichnen von PictureBox. Dadurch wird sichergestellt, dass dem Anwender alle Änderungen, die wir vorgenommen haben, angezeigt werden. Der Ereignisbehandler MouseUp (Zeilen 14 bis 19) ist einfach. Er dient lediglich dazu, den Zeichenmodus zu beenden, falls wir uns bis dahin in diesem Modus befanden. Bei späteren MouseMove-Ereignissen, bei denen Drawing nicht auf True gesetzt ist, wird nichts gezeichnet. Der Großteil der Verarbeitung findet im Ereignisbehandler Mousedown statt. Je nachdem, welche Werkzeuge und Einstellungen gerade ausgewählt sind, reagiert die Anwendung unterschiedlich. Zunächst müssen wir das aktuelle Werkzeug ermitteln (Zeile 28). Falls das Werkzeug Shape ist, müssen wir den gewählten Formtyp herausfinden. Der Code zum Zeichnen sieht immer ähnlich aus. Mit der Methode Fill zeichnen wir bei der aktuellen Mausposition ein Rechteck oder eine Ellipse mit der von den NumericUpDown-Steuerelementen jeweils ausgewählten Größe. Die aktuelle Mausposition wird vom Parameter MouseEventArgs des Ereignisbehandlers MouseDown bereitgestellt. Wenn das aktuelle Werkzeug Text ist, müssen wir zuerst eine neue Schriftart erstellen (Zeilen 53 bis 54). Diese Schriftart verwenden wir, wenn wir auf der PictureBox den grafischen Text zeichnen (Zeilen 56 bis 60). Der Text wird mit der neu erstellten Schriftart und einem neu erstellten Pinsel gezeichnet.
587
Mit dem .NET Framework arbeiten
Wenn wir die Linien mit dem Werkzeug Pen zeichnen, müssen wir zuerst das neue Pen-Objekt erzeugen (Zeilen 65 bis 67). Dieses wird die gewählte Farbe und Stiftbreite haben. Dann speichern wir die aktuelle Mausposition für die spätere Verwendung im Ereignisbehandler MouseMove (Zeilen 68 bis 69) und aktivieren den Zeichenmodus (Zeile 70).
Bilder speichern Nachdem Sie das Bild erstellt haben, speichern Sie es mit der Methode Save des BitmapObjekts als Datei. Diese Methode ermöglicht es, das Bild in einem bestimmten Format und mit einer bestimmten Größe zu speichern. Das Bitmap-Objekt unterstützt viele verschiedene Formate: 쐽
Bitmap: Ein in Windows häufig verwendetes Bildformat, das normalerweise recht groß ist. Es kann beliebig viele Farben unterstützen. Die Dateierweiterung lautet .BMP.
쐽
GIF: Dies ist die Abkürzung für Graphics Interchange Format. Es ist ein Bildformat, das im Internet häufig verwendet wird. Normalerweise sind GIFs klein, da sie komprimiert sind. Allerdings können sie nur 256 Farben pro Bild haben. Die Dateierweiterung lautet .GIF.
쐽
JPEG: Dies ist die Abkürzung für Joint Photographic Experts Group. Auch dieses Bildformat wird im Internet häufig verwendet. Fotografien und Bilder, bei denen viele Farben angezeigt werden müssen, werden normalerweise als JPEG gespeichert. Dieses Format kann stark komprimiert werden, wobei die Dateien durch eine starke Kompression jedoch Informationen oder Qualität verlieren können. Die Erweiterung für diese Dateien lautet .JPG.
쐽
PNG: Dies ist die Kurzform für Portable Network Graphics, ein relativ ungebräuchliches Dateiformat. Es wurde als Ersatz für GIF- und TIFF-Dateien entworfen. PNGDateien sind relativ klein, normalerweise allerdings größer als GIF- oder JGEGDateien. Das Format wird selten unterstützt, obwohl der Internet Explorer und einige andere Programme es anzeigen können. Die Erweiterung dieser Dateien lautet .PNG.
쐽
TIFF: Dies ist die Abkürzung für Tagged Image File Format. Früher wurde dieses Format häufig verwendet, während man es heute nur noch selten findet. Je nachdem, welche der vielen TIFF-Varianten zum Speichern benutzt wird, können TIFF-Dateien groß oder klein sein. Die Dateierweiterung lautet .TIF. Wenn Sie ein Bild auf einer Webseite anzeigen möchten, speichern Sie es normalerweise im GIF- (wenn es weniger als 256 Farben hat) oder im JPEG-Format. Wenn Sie das Bild in einer Desktop-Anwendung verwenden möchten,
588
Mit den Grafikklassen zeichnen
sollten Sie die verschiedenen Dateiformate ausprobieren, um herauszufinden, welche die beste Kombination aus Dateigröße und Qualität bietet. JPEG ist in der Regel gut geeignet, wenn das Bild viele Farben verwendet (z.B. eine Fotografie), PNG und BMP eignen sich hingegen für einfachere Bilder besser. Die Tabelle 17.14 zeigt, welche Größe ein einfaches Bitmap-Bild in den verschiedene Formaten hat. Format
Dateigröße/KB
BMP
340,134
GIF
10,088
JPEG
20,971
PNG
10,262
TIFF
15,492
Tabelle 17.14: Vergleich von Grafikformaten
Nun können wir unsere Anwendung fertig stellen, indem wir den FILE-Befehlen den Code im Listing 17.11 hinzufügen.
Listing 17.11: Menübefehle 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Private Sub mnuFileNew_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles mnuFileNew.Click Me.oGraphics.Clear(Color.White) 'Erzwinge erneutes Zeichnen. Me.picDraw.Refresh() End Sub Private Sub mnuFileOpen_Click() If Me.dlgOpen.ShowDialog = DialogResult.OK Then Me.picDraw.Image.FromFile(Me.dlgOpen.FileName) End If End Sub Private Sub mnuFileSave_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles mnuFileSave.Click Dim sFileName As String Dim oFormat As System.Drawing.Imaging.ImageFormat
589
Mit dem .NET Framework arbeiten
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
'Hole den Dateinamen, unter dem gespeichert werden soll. If dlgSave.ShowDialog = DialogResult.OK Then sFileName = dlgSave.FileName Select Case dlgSave.FilterIndex Case 0 'save as bitmap oFormat = System.Drawing.Imaging.ImageFormat.Bmp Case 1 'save as PNG oFormat = System.Drawing.Imaging.ImageFormat.Png Case Else 'should never happen End Select 'Mögliche Ausnahme beim Speichern. Try Me.picDraw.Image.Save(sFileName, oFormat) Catch ex As Exception 'Zeige sie im Moment einfach nur an. MessageBox.Show(ex.Message, "Error saving file", _ MessageBoxButtons.OK, MessageBoxIcon.Error) End Try End If End Sub Private Sub mnuFileExit_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles mnuFileExit.Click Me.Close() End Sub
Die Zeilen 1 bis 6 zeigen den Code für den Befehl FILE, NEW. Er ist einfach, da er lediglich den Inhalt des oGraphics-Objekts löscht (Zeile 3) und durch einen neuen weißen Hintergrund ersetzt. Wie zuvor zwingen wir die PictureBox, sich neu zu zeichnen (Zeile 5), um das geänderte Bild anzuzeigen. Das Öffnen der Datei ist fast so einfach wie das Löschen. In der Zeile 9 zeigen wir das Dialogfenster FILE OPEN an. Wenn der Anwender eine Datei auswählt, laden wir sie in Image der PictureBox. Die PictureBox ermittelt den Typ der Datei und zeigt diese richtig an. Der Code für das Speichern des Bildes ist etwas länger, aber ebenfalls einfach. Zuerst zeigen wir das Standarddialogfeld FILE SAVE an (Zeile 20). Nachdem der Anwender den Speicherort und den Dateityp gewählt hat, können wir weitermachen. Um die späteren Zeilen zu verkürzen, speichern wie den gewählten Dateinamen in einer Variablen. Bei unserem Dialogfenster kann der Anwender zwischen zwei Dateiformaten wählen: Bitmap und PNG. Mit diesem Gerüst
590
Zusammenfassung
setzen wir eine weitere Variable für das Imageformat, mit dem wir das Bild speichern werden. Schließlich können wir das Bild speichern. Für den Fall, dass eine Ausnahme auftritt, weil z.B. der Speicherplatz nicht ausreicht, stellen wir das Bild in einen Try...End Try-Block. In der Zeile 32 speichern wir die Datei, wobei wir die Fähigkeit von Image-Objekten nutzen, sich selbst zu speichern. Zuletzt schließt der Befehl FILE, EXIT das Formular und beendet damit die Anwendung. Nicht nur das Laden eines neuen Image-Objekts ist relativ einfach, sondern auch das Speichern eines in einem Image-Objekt gespeicherten Bildes. Tatsächlich ist dies eines der besten Beispiele für die Vorteile, die die Verwendung von Objekten mit sich bringt, da wir hier eine an sich komplexe Aufgabe dadurch erledigen, dass wir den erforderlichen Code in die Graphics-, Image- und Bitmap-Objekte speichern.
17.3 Zusammenfassung Es nimmt einige Zeit in Anspruch, alle Klassen, Strukturen, Eigenschaften, Methoden und Ereignisse kennen zu lernen, die gemeinsam das .NET-Framework bilden. Allerdings wird es sich immer wieder als hilfreich erweisen, wenn Sie einige grundlegende Klassen kennen, da die in diesen Klassen gezeigten Konzepte im ganzen Framework verwendet werden. Wenn Sie einen Strom kennen, wissen Sie bei allen Strömen, was Sie zu tun haben. Und sobald Sie wissen, wie Sie mit den Graphics-Klassen zeichnen können, können Sie überall zeichnen. In der morgigen Lektion werden wir uns damit befassen, Ihren Anwendungen den »letzten Schliff« zu geben.
17.4 Fragen und Antworten F
Was ist Unicode? A
Ein Problem besteht in unserer Welt darin, dass es nie die eine Sprache gegeben hat, auf die sich alle Menschen einigen konnten. Viele Sprachen verwenden denselben oder ähnliche Zeichensätze wie Nordamerika und Westeuropa. Diese Zeichen sind im ASCII-Zeichensatz (American Standard Code for Information Interchange) definiert, der 255 Zeichen (ein Byte) verwendet, um alle in diesen Sprachen verwendeten Zeichen darzustellen. Das Zeichen »A« wird z.B. in allen Zeichensätzen, die die ASCII-Codierung verwenden, durch das Zeichen 65 dargestellt.
591
Mit dem .NET Framework arbeiten
Andere Sprachen wie z.B. Tamil oder Japanisch erfordern hingegen mehr Zeichen. Dafür entwickelte die International Standards Organization (ISO) den Zeichensatz Unicode, der jedes Zeichen, das in irgendeiner Sprache der Erde vorkommt, in zwei Bytes codiert. (Auch für einige außerirdische Sprachen gibt es Zuordnungen, z.B. für Klingonisch: http://anubis.dkuug.dk/jtc1/sc2/wg2/docs/n1643/n1643.htm.) Das Unicode-Zeichen 20AC steht z.B. immer für den Euro. Die ersten 256 Zeichen der Unicode-Codierung sind dieselben wie die in der ASCII-Codierung. Die Antwort auf unsere Frage lautet also: Unicode ist ein Zwei-Byte-Wert, der ein beliebiges Zeichen in einem beliebigen Zeichensatz identifizieren kann. Warum ist das wichtig? Manchmal sieht man eine Sammlung von unverständlichen Zeichen. Häufig liegt dies daran, dass es sich um Unicode-Zeichen handelt. Daher sollten Sie sich informieren, wie Sie diese Zeichen in ASCII-Code umwandeln oder die nativen Zeichen anzeigen können. F
Ich möchte eine komplexe Form zeichnen. Gibt es in System.Drawing auch Befehle zum Zeichnen von Formen wie z.B. Achtecken? A
Im Graphics-Objekt gibt es eine Reihe von Methoden, mit denen Sie komplexere Formen erstellen können, u.a. DrawArc, DrawCurve, DrawLines, DrawPath und DrawPolygon. Um ein Achteck zu zeichnen, verwenden Sie die Methode DrawPolygon, übergeben Ihr den Pen, mit dem Sie zeichnen möchten, sowie ein Array von Points. Das Point-Array beschreibt die Ecken des Achtecks.
17.5 Workshop Der Workshop soll Ihnen dabei helfen, mögliche Fragen vorherzusehen und das Gelernte zu wiederholen. Außerdem soll er Sie dazu anregen, über die praktische Umsetzung Ihres Wissens nachzudenken. Die Antworten und Lösungen finden Sie im Anhang A.
Quiz 1. Welches der folgenden Codefragmente würden Sie verwenden, um zu ermitteln, ob eine Datei existiert? A. Dim oFile As New File Dim bExists As Boolean = oFile.Exists("some file")
B. Dim bExists As Boolean = Directory.Exists("some file") C. Dim bExists As Boolean = File.Exists("some file")
592
Workshop
D. Dim oDir As New Directory Dim bExists As Boolean = oDir.Exists("some file")
2. Welche Beziehung besteht zwischen den Klassen Stream, StreamReader und StreamWriter? 3. Welche Bildformate können Sie erstellen (oder in einem Image-Steuerelement anzeigen)? 4. Wodurch unterscheiden sich Point, Size und Rectangle?
Übungen 1. Bei unserer Anwendung Note haben wir das Problem, dass Sie die Befehle AUSSCHNEIDEN, KOPIEREN und EINFÜGEN auch verwenden können, wenn kein Text ausgewählt oder in der Zwischenablage gespeichert ist. Versuchen Sie eine einfache Möglichkeit zu finden, diese Menüelemente zu aktivieren oder zu deaktivieren, bevor Sie dem Anwender das Menü anzeigen. (Tipp: Fügen Sie dem Menüelement mnuEdit ein Ereignis hinzu.) 2. Ändern Sie das Format, in dem Sie Bilder gespeichert haben, und vergleichen Sie die Dateigrößen. Welches ist das kleinste Format für einfache Schwarzweiß-Zeichnungen? Und welches für Fotos?
593
Der letzte Schliff
Der letzte Schliff
Dieses Buch soll Ihnen vor allem beibringen, wie Sie mit .NET programmieren, aber die Erstellung einer vollständigen Anwendung hat viele Aspekte. Die heutige Lektion befasst sich mit einigen Einzelheiten, die Sie wissen müssen, um ein echtes, vor allem ein großes und komplexes System erstellen zu können:
Themen heute: 쐽
Eine Anwendung dokumentieren
쐽
Die Quellcodekontrolle verwenden
Diese beiden Themen sind zwar optional und Sie benötigen Sie nicht, um ein funktionierendes System zu erstellen, aber sie sind Bestandteil fast jedes großen Entwicklungsprojektes.
18.1 Eine Anwendung dokumentieren Wenn Sie ein System erstellen, schreiben Sie manchmal den gesamten Code und stellen den Anwendern Ihr System zur Verfügung und dann sieht niemand jemals wieder diesen Code. Dies ist allerdings die Ausnahme und trifft normalerweise nur bei kleinen Systemen zu. Der Regelfall ist, dass Ihr Code immer wieder betrachtet wird, während das ursprüngliche System um Funktionsmerkmale erweitert wird und Programmfehler behoben werden. Er könnte sogar die Grundlage für völlig neue Versionen desselben Systems sein. Damit Sie und andere Programmierer möglichst problemlos Änderungen vornehmen und den Code wiederverwenden können, sollten Sie Ihr System dokumentieren. Diese Art der Dokumentierung ist nicht dieselbe wie die Dokumentation des Systems für die Anwender. Mit der zweiten Art werde wir uns weiter unten in der heutigen Lektion befassen. Hier geht es darum, denjenigen, die auf Ihren Code zugreifen, Informationen über die Funktionsweise Ihres Systems zu liefern. Hinsichtlich des Detailreichtums weichen solche Dokumentationen stark voneinander ab, da es nicht die eine richtige Art gibt, Code zu dokumentieren. Um eine gute Dokumentation zu schreiben, können Sie sich z.B. zuerst einmal überlegen, warum Sie diese Informationen bereitstellen möchten. Die Dokumentation soll gewährleisten, dass kein Wissen über ein System verloren geht, weder die Geschäftsanforderungen noch die Überlegungen, die hinter den technischen Entscheidungen stehen. Indem Sie möglichst viele Informationen über Ihren Code zur Verfügung stellen, vereinfachen Sie die Pflege des Systems. Wir können die Dokumentation in die folgenden zentralen Richtlinien zerlegen, die hier nicht unbedingt in der Reihenfolge ihrer Wichtigkeit genannt sind. Die erste dieser Regeln hat eigentlich nichts mit der Dokumentation zu tun:
596
Eine Anwendung dokumentieren
쐽
Erstellen Sie die einfachste Lösung, die Ihr Problem löst.
쐽
Gehen Sie bei der Dokumentation des Systems von möglichst wenigen Vorbedingungen aus.
쐽
Nutzen Sie Ihre Zeit sinnvoll. Verzichten Sie darauf, Offensichtliches zu dokumentieren, und konzentrieren Sie sich darauf, die komplexen Teile des Systems zu erklären.
쐽
Ohne Klärung des Gesamtzweckes Ihres Systems ist eine ausführliche Erläuterung des Codes sinnlos. Dokumentieren Sie das System selbst, alle Systemkomponenten und den Code.
Wenn Sie alle diese Richtlinien einhalten, erstellen Sie ein gut dokumentiertes System mit der bestmöglichen Wartbarkeit. In den folgenden Abschnitten werde ich auf diese Richtlinien eingehen und sie bei Bedarf durch Codebeispiele veranschaulichen.
Erstellen Sie die einfachste Lösung Wenn die Dokumentation den Code möglichst leicht verständlich machen soll, wäre es dann nicht am besten, diesen gleich von Anfang an so einfach wie möglich zu machen? Ihr Ziel sollte es sein, Code zu schreiben, der ohne weitere Erklärung zu verstehen ist, und nur dort, wo Ihnen dies nicht gelingt, sollten Kommentare die Lücke schließen. Es gibt zwei Hauptmöglichkeiten, dieses Ziel zu erreichen: Schreiben Sie sauberen Code und schreiben Sie einfache Lösungen. Mit dem ersten Konzept, dem sauberen Code, wollen wir uns zuerst befassen; aber wichtiger ist zweifellos die Erstellung einfacher Lösungen.
Schreiben Sie sauberen Code Sehen Sie sich zunächst die Listings 18.1 und 18.2 an, die beide dieselbe Aufgabe haben. Listing 18.1: Code kann unnötig komplex sein. 1 2 3 4 5 6 7 8 9 10 11 12 13
Option Explicit On Option Strict On Imports System Imports System.IO Module Module1 Sub Main() Dim Path1 Dim Path2 Dim File1 Dim File2
As As As As
String = "C:\ file.txt" String = "C:\ file_new.txt" New StreamReader(Path1) New StreamWriter(Path2)
597
Der letzte Schliff
14 Dim x As String 15 x = File1.ReadLine() 16 Do Until x Is Nothing 17 File2.WriteLine(x) 18 x = File1.ReadLine() 19 Loop 20 File2.Close() 21 File1.Close() 22 End Sub 23 24 End Module Listing 18.2: Wenn Sie Ihren Code leichter verständlich machen, müssen Sie ihn weniger ausführlich dokumentieren. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
Option Explicit On Option Strict On Imports System Imports System.IO Module WorkWithFiles Sub CopyFiles() Dim sInputFilePath As String = "C:\ test.txt" Dim sOutputFilePath As String = "C:\ output.txt" Dim fileInput As New StreamReader(sInputFilePath) Dim fileOutput As New StreamWriter(sOutputFilePath) Dim sLine As String sLine = fileInput.ReadLine() Do Until sLine Is Nothing fileOutput.WriteLine(sLine) sLine = fileInput.ReadLine() Loop fileOutput.Close() fileInput.Close() End Sub Sub Main() CopyFiles() End Sub End Module
598
Eine Anwendung dokumentieren
Die Listings 18.1 und 18.2 lesen eine Datei und geben sie an eine neue Datei aus. Der Code in Listing 18.2 ist wesentlich klarer als derselbe Code in Listing 18.1. Der erste Grund hierfür sind die Namen von Module und Sub: Die Standardwerte liefern keine Informationen, während der Prozedurname CopyFiles ein relativ direkter Hinweis auf den Zweck der Routine ist. Der zweite Unterschied besteht in der räumlichen Gestaltung. In Listing 18.2 haben wir durch Leerzeilen verdeutlicht, welche Codeabschnitte zusammengehören: der Initialisierungscode in den Zeilen 10 bis 14, der Code zum Lesen der Datei (Zeilen 16 bis 20) und der Bereinigungscode (Zeilen 22 und 23). Die Leerzeilen haben keinerlei Auswirkungen auf das Ergebnis des Systems: Beide Beispiele werden gleich schnell und mit demselben Ergebnis ausgeführt. Aber die Zusammenfassung des Codes zu einzelnen Gruppen verbessert die Lesbarkeit. Der dritte Hauptunterschied zwischen den beiden Codebeispielen ist die Benennung der Variablen. Mit diesem Thema haben wir uns in früheren Lektionen bereits kurz befasst. Abgesehen von Schlüsselwörtern und anderen für .NET reservierten Werten können Sie Variablen beliebige Namen geben. Stellen Sie sich den Variablennamen als einen integrierten Dokumentationsmechanismus vor. Das Listing 18.1 ist ein Extrembeispiel für schlechte Variablennamen, wie es Ihnen nur selten begegnen wird. Aber es macht die Folgen schlechter Variablennamen sehr deutlich. Wenn wir die beiden Dateien hingegen als fileInput (statt File1) und fileOutput (statt File2) bezeichnen, brauchen wir den Zweck der Variablen eigentlich nicht weiter zu kommentieren. Auch bei den anderen Variablennamen sind wir ähnlich vorgegangen und wenn wir alle diese Variablen gemeinsam verwenden (z.B. in der Zeile 12 im Listing 18.2), dann wird der Zweck des Codes schon durch die verwendeten Namen fast vollständig erklärt. Bei der Wahl der Variablennamen haben wir außerdem eine nützliche Konvention angewandt und den Datentyp durch ein Präfix aus einem oder mehreren Zeichen angezeigt. Im Listing 18.2 wird Zeichenketten ein s vorangestellt und bei den StreamReader- und StreamWriter-Objekten zeigen wir durch das Präfix file an, dass sie dem Lesen aus und Schreiben in Dateien dienen. Diese Art, Variablen zu benennen, beruht auf der Ungarischen Notation, bei der vorangestellte Zeichen den Datentyp und den Gültigkeitsbereich (global oder lokal, öffentlich oder privat) von Variablen beschreiben. Wie gesagt beruht dieses Verfahren auf der Ungarischen Notation, es ist jedoch nicht die Ungarische Notation, da ich normalerweise anstelle der von ihrem Erfinder (Charles Simonyj von Microsoft) als Ungarische Notation bezeichneten Regeln einen wesentlich lockereren Benennungsstandard verwende. Die Benennung von Variablen werde ich weiter unten in der heutigen Lektion im Abschnitt »Tipps und Konventionen« besprechen.
599
Der letzte Schliff
Abgesehen von den im obigen Beispiel veranschaulichten Verfahren kann sich auch die Struktur des Codes auf dessen Lesbarkeit auswirken. Ich erstelle meine Systeme gerne mit vielen Prozeduren, selbst wenn der Code nicht unbedingt wiederverwendet werden wird. Diese Art der Strukturierung hat den Vorteil, dass Sie Prozeduren auf höherer Abstraktionsebene verwenden, die leichter zu lesen und zu verstehen sind, und zugleich die komplexeren (und verwirrenderen) Details vermeiden. Das Listing 18.3 zeigt hierzu ein Beispiel. Listing 18.3: Prozeduren sind eine weitere Möglichkeit, Ihren Code leichter verständlich zu machen. 1 Module StartUp 2 Public Sub Main() 3 Dim sPassword, sData, sFilePath As String 4 sFilePath = "C:\ Input.txt" 5 InitializeSystemVariables() 6 7 sPassword = GetPassword() 8 sData = ReadData(sFilePath) 9 sData = EncryptData(sPassword, sData) 10 WriteData(sFilePath, sData) 11 End Sub 12 End Module
Das Listing 18.3 macht selbst nichts; der Kern des Systems ist in den Prozeduren enthalten, auf die verwiesen wird. Schon wenn Sie diese Routine lesen, bekommen Sie eine gute Vorstellung davon, was das System macht, und bei Bedarf können Sie sich die einzelnen Routinen genauer ansehen. Wenn Ihr Code richtig strukturiert ist, können Routinen auf höherer Ebene als allgemein verständliche Ansicht der Routinen auf niedrigerer Abstraktionsebene dienen, die sie aufrufen. Beachtenswert ist dabei allerdings, dass Sie den Code in Listing 18.3 zwar lesen und auch verstehen können, was geschieht. Aber um zu erkennen, warum es geschieht, benötigen Sie eine ordentliche Dokumentation.
Vereinfachen Sie Ihr System Die Erstellung sauberen Codes ist ein Aspekt der ersten Richtlinie, aber nicht der wichtigste. Wesentlich wichtiger ist, dass die tatsächliche Lösung, also die Art, wie Sie den Zweck des Systems erzielen möchten, möglichst einfach ist.
Vermeiden Sie Vorbedingungen Wenn Sie Ihren Code dokumentieren, haben Sie dabei wahrscheinlich (bewusst oder unbewusst) ein bestimmtes Publikum im Sinn. Vielleicht erwarten Sie, dass nur Sie selbst diese Kommentare lesen werden oder dass andere Entwickler aus Ihrem derzeitigen Team
600
Eine Anwendung dokumentieren
den Code verstehen müssen, damit sie ihn pflegen können. Die Dokumentation für eine bestimmte Zielgruppe zu schreiben ist insofern problematisch, als man für verschiedene Personen meist verschiedene Dokumentationen schreibt. Gehen Sie nicht davon aus, dass die Kommentare nur von Programmierern mit Erfahrung in C++ oder sogar Visual Basic .NET gelesen werden. Gehen Sie auch nicht davon aus, dass die Kommentare von Personen gelesen werden, die den Gesamtzweck des Systems kennen oder die geschäftliche Seite des Systementwurfs kennen. Solche Annahmen können zwar zutreffen, da Sie vielleicht ein Team haben, das ausschließlich aus Experten besteht, aber die Dinge ändern sich. Am Ende wird Ihr Code vielleicht von einem Beratungsunternehmen übernommen, das ihn wiederum für ein anderes Unternehmen aktualisieren soll, das Ihre derzeitige Firma gekauft hat. Oder ein Teil Ihres Codes wird in ein anderes Projekt eingefügt. Selbst so kleine Vorbedingungen wie z.B., dass der Leser die Datenbankterminologie kennen muss, sollten Sie am Anfang Ihrer Dokumente klar benennen. Natürlich hat alles seine Grenzen. Wenn Sie nicht zumindest Grundkenntnisse in Visual Basic voraussetzen können, dann müssen Sie wirklich jeden einzelnen Aspekt Ihres Codes erklären.
Kommentieren Sie nur das Verwirrende, nicht das Offensichtliche Diese Regel steht in enger Beziehung zur ersten, da umso weniger Kommentare nötig sind, je einfacher und sauberer Ihr Code ist. Betrachten Sie noch einmal die Listings 18.1 und 18.2 und überlegen Sie, wie Sie sie kommentieren könnten, damit sie leicht zu pflegen sind. Letztlich müssen Sie beim ersten Beispiel wesentlich mehr Zeit auf das Kommentieren verwenden, um die Bedeutung von Variablen wie z.B. Path1 und Path2 zu erklären. Das zweite Beispiel dokumentiert sich selbst dagegen etwas besser. Ein Variablenname wie z.B. sInputFilepath teilt Ihnen alles mit, was Sie wissen müssen; Sie brauchen keine Kommentare hinzuzufügen. Wenn Sie neben den bedeutungsvollen Variablennamen auch noch die Ungarische Notation, mit der wir uns weiter unten in dieser Lektion beschäftigen werden, verwenden, erhalten Sie sogar noch mehr Informationen. Dadurch wissen Sie nun, dass dies eine Zeichenkettenvariable ist, und brauchen auch diese Information nicht mehr in einem Kommentar anzugeben.
Dokumentieren Sie das System, nicht nur den Code Der letzte Punkt bei unseren allgemeinen Dokumentationsprinzipien ist die Notwendigkeit, nie den Zweck der Dokumentation aus den Augen zu verlieren: Sie wollen anderen Personen das Verstehen Ihres Systems erleichtern. Wenn Sie jede Variable und jede Schleife ausführlich dokumentieren, hilft mir dies nicht dabei, Ihr System zu verstehen,
601
Der letzte Schliff
wenn ich nicht weiß, was dieses System eigentlich machen soll. Stellen Sie Dokumentationen zu verschiedenen Ebenen des Systems bereit, von der ursprünglichen Idee und dem Gültigkeitsbereich, auf denen das System beruht, bis hin zu den Details der einzelnen Codezeilen. Zu jedem System sollten die folgenden Dokumentationsteile zur Verfügung stehen: 쐽
Idee und Umfang
쐽
Anforderungen und Verwendung
쐽
Ausführlicher Entwurf
쐽
Eine beliebige Form von Protokollierung des Systems und der Änderungsanfragen
Diese Dokumentationstypen weichen bei unterschiedlichen Entwicklungsverfahren und verschiedenen Personen stark voneinander ab. Uns interessiert hier allerdings nur der grundlegende Zweck der Dokumentation, auf den ich in den folgenden Abschnitten eingehen werde. Wenn Sie diese Einzelheiten lesen, werden Sie sich wahrscheinlich fragen, ob Sie dies alles wirklich schreiben müssen, nachdem Sie das System doch eigentlich schon fertig gestellt haben. Eine gute Frage. Wenn Ihr System bereits fertig gestellt ist und Sie noch keinerlei Dokumentation geschrieben haben, dann werden Sie wahrscheinlich auch keine mehr schreiben. Aber das ist die falsche Reihenfolge. Eigentlich sollten Sie die Dokumentation schreiben, bevor Sie das System erstellen. Den Großteil der Dokumentation sollten Sie schreiben, bevor Sie Code schreiben, der über einen Prototyp hinausgeht, und einen kleineren Teil während der Codierungsphase. Am Tag 1, Willkommen zu Visual Basic .NET, habe ich den Lebenszyklus der Softwareentwicklung besprochen, den die folgende Abbildung 18.1 noch einmal zeigt:
Idee Umfang
Anforderungen & Entwurf
Bereitstellung &Pflege
Entwicklung Release
Abbildung 18.1: Der Lebenszyklus der Softwareentwicklung ist ein endloser Prozess, der häufig als ein in sich geschlossener Kreis dargestellt wird, der einen fortwährenden Kreislauf symbolisieren soll.
Idee und Umfang Die Dokumentation zu Idee und Umfang hält den vereinbarten Zweck des Systems fest und sollte am Anfang der Entwicklung ausgearbeitet werden. Derjenige, der das System
602
Eine Anwendung dokumentieren
warten soll, sollte zuerst den Zweck des Systems von Grund auf verstehen. Ein einfaches Beispiel für Idee und Umfang könnte z.B. folgendermaßen lauten: »Das System zur Protokollierung des Qualifikationsniveaus von Angestellten pflegt eine Liste von Angestellten und verfolgt deren Qualifikation in verschiedenen, für ihr Aufgabengebiet relevanten Kenntnisbereichen. Dies dient der Bildung von Arbeitsgruppen und der Projektplanung.« Achten Sie darauf, dass diese Anweisung Einiges offen lässt. Welche Kenntnisbereiche? Wer wird das System nutzen? Woher bekommt es die Liste der Angestellten? Diese und andere Fragen gehören nicht zur Idee. Sie müssen zwar beantwortet werden, aber nicht in dieser Dokumentation.
Anforderungen und Verwendung Die Anforderungen und die Verwendung kann man entweder zusammen oder getrennt dokumentieren. Unabhängig davon, ob Ihnen vielleicht nur die Anforderungen oder nur die Verwendungsmöglichkeiten vorliegen, wird immer derselbe Zweck erfüllt. Die Anforderungen eines Systems dokumentieren die einzelnen Aufgaben, die die Anwender mithilfe des Systems durchführen werden, und die Verwendungsmöglichkeiten beschreiben repräsentative Interaktionen zwischen den Anwendern und dem System. Beide Bereiche listen Aufgaben auf, die das System leisten können muss, und sollten Ihnen eine Vorstellung davon vermitteln, wie diese Aufgaben funktionieren. Die Dokumentationen skizzieren die Anwendung vom Standpunkt des Anwenders aus, aber Sie werden weiterhin nach Informationen darüber suchen müssen, wie das System diese Aufgaben tatsächlich erfüllt.
Ausführlicher Entwurf In diese Kategorie gehören viele Dokumente, nämlich alles, was mehr Einzelheiten bietet als die Dokumentation der Anforderungen. Die Dokumente in dieser Kategorie sollen ausführlich beschreiben, wie das System funktioniert, d.h., welche Schritte es intern durchläuft, um die in der vorherigen Dokumentation genannten Anforderungen zu erfüllen. Dieses Material enthält Flussdiagramme, Zustandsdiagramme und Listen von Komponenten, Prozeduren und anderen Codeelementen.
Änderungsprotokoll Es wäre ein Wunder, wenn Anwendungen zum Zeitpunkt ihrer Auslieferung genau mit ihrer Spezifikation übereinstimmten. Wenn es in der Anwendungsentwicklung eine Konstante gibt, so ist es die, dass fortwährend Änderungen vorgenommen werden. Ein Großteil der Arbeit bei der Systemerstellung besteht darin, diese Änderungen zu verfolgen und zu kontrollieren, damit das System nicht ganz aus der Zeitplanung fällt. Diesen Vorgang
603
Der letzte Schliff
bezeichnet man häufig als Änderungskontrolle. Dabei werden alle erbetenen Änderungen protokolliert, wobei durch eine bestimmte Form von Zustimmungsprozess ermittelt wird, welche Änderungen tatsächlich in das System aufgenommen werden. Anschließend werden diese Entscheidungen und die Auswirkungen der jeweiligen Änderungen auf die Projektplanung dokumentiert. Die Dokumentation der Änderungen, die an Ihrem System vorgenommen werden, ist eine unschätzbare Ressource für Entwickler, die an neuen Merkmalen und der Pflege vorhandener Systemeigenschaften arbeiten, sowie für Systemarchitekten und Manager, die neue Editionen Ihrer Software planen.
18.2 Tipps und Konventionen Es gibt einige Tipps, die Sie bei der Dokumentierung Ihres Codes berücksichtigen sollten. Erstens sollten Sie eine Gruppe gemeinsamer (zumindest in Ihrem Team eingehaltener) Standards für die Benennung von Variablen, Steuerelementen und Objekten festlegen. Zweitens sollten Sie in Ihren Code Header-Blöcke einfügen, d.h. Kommentare am Anfang von Modulen, Klassen und Prozeduren, die ein gemeinsames Format verwenden und an einer einzigen Stelle Informationen über diesen Code liefern. In den folgenden Abschnitten werde ich diese beiden Strategien erläutern und einige Beispiele dafür anführen, wie Sie sie in Ihrem Team durchführen können.
Benennung von Variablen, Steuerelementen und Objekten Der wichtigste Aspekt von Namensstandards besteht darin, dass es sich wirklich um einen Standard handeln muss: Jedes Mitglied Ihres Teams muss ausnahmslos dieselben Namenskonventionen einhalten. Die Namensgebung sollte nur ein Teil eines umfangreicheren Dokuments sein, das die Einzelheiten von Kommentierung, Ausnahmebehandlung, Quellcodekontrolle usw. enthält, d.h. alles das, was ein Entwickler in Ihrem Team wissen muss. Wenn Sie dafür sorgen, dass alle Teammitglieder Ihren Namensstandard einhalten, ist die Einrichtung des Standards nur noch eine Kleinigkeit. Die Ungarische Notation ist eine Form der Namensgebung, die wir weiter oben in der heutigen Lektion bereits kurz angesprochen haben. Mithilfe von Präfixen liefert sie Informationen über eine Variable, ein Steuerelement oder ein Objekt. Diese Präfixe bestehen aus einem oder mehreren Buchstaben und sollen mindestens den Datentyp und manchmal auch den Gültigkeitsbereich des Objekts erkennbar machen. Der KnowledgeBase-Artikel Q110264 (http://support.microsoft.com/support/kb/articles/Q110/2/64.asp) beschreibt Beispielnamenskonventionen, die für die Verwendung mit älteren Versionen von Visual Basic erstellt wurden. Ein Großteil dieses Artikels ist noch immer gültig, wurde für .NET aber nicht aktualisiert. Für meinen eigenen Code verwende ich einige relativ einfa-
604
Tipps und Konventionen
che Namenskonventionen, die zumindest teilweise auf diesem Artikel beruhen. Bevor ich die Konventionen ausführlicher behandle, möchte ich zuerst einige allgemeine Richtlinien besprechen: 쐽
Ich gebe mit dieser Notation nur den Datentyp an. Die einzige Information zum Gültigkeitsbereich, die ich hinzufüge, ist das m_, das eine (private) Elementvariable einer Klasse kennzeichnet. Auf die Angabe des Gültigkeitsbereichs verzichte ich der Einfachheit halber, da ich selten andere als Prozedur- oder Klassenvariablen verwende. Dadurch benötige ich auf Prozedurvariablen keinerlei Indikator für den Gültigkeitsbereich.
쐽
Alle Namenskonventionen gelten für interne Namen wie z.B. private Variablen, Steuerelemente auf einem Formular usw. Wenn Sie Klassen, öffentliche Eigenschaften und öffentliche Methoden erstellen, definieren Sie ein externes Interface und die entsprechenden Namen sollten für Menschen einfach zu lesen sein. So kann ich eine interne Zählervariable iCount nennen (um anzugeben, dass es sich um eine ganze Zahl handelt), aber eine ähnliche Eigenschaft in einer öffentlichen Klasse sollte einfach Count heißen.
쐽
Letztlich sollen die Konventionen den Code leichter lesbar machen. Verwenden Sie also nicht nur die Ungarische Notation, sondern sorgen Sie auch dafür, dass der Variablenname selbst etwas aussagt.
Bei allen folgenden Namensbeispielen verwende ich die Kamelschreibweise, bei der das Präfix kleingeschrieben ist und der Variablenname mit einem Großbuchstaben beginnt, wodurch besser lesbare Namen entstehen.
Datentypen Normalerweise verwende ich für die Datentypnotation möglichst wenige Zeichen. Im Gegensatz zu dem weiter oben genannten Microsoft-Artikel versuche ich daher nicht, für alle Datentypen dieselbe Anzahl von Zeichen zu verwenden. Denken Sie daran, dass ich in der Tabelle 18.1 meine eigene Notation angebe; in Klammern habe ich jedoch allgemein gebräuchliche Alternativen genannt, soweit mir dies angemessen erschien. Da ich nicht alle .NET-Datentypen einschließlich aller Klassen im .NET-Framework abdecken kann, versuche ich genug Beispiele anzuführen, um Ihnen eine Vorstellung davon zu vermitteln, wie ich meine Variablen benenne. Datentyp
Ungarische Notation
String
S [str]
Integer, Int32
I [int]
Tabelle 18.1: Häufig verwendete Präfixe für Datentypen
605
Der letzte Schliff
Datentyp
Ungarische Notation
Double
Dbl
Single
sng [f(für float, einen C++-Typ)]
Long, Int64
L [lng]
Boolean
B [bool]
Date/Time
dt
System.Data.DataSet
ds
System.Data.DataColumn
dc
Object
obj
Tabelle 18.1: Häufig verwendete Präfixe für Datentypen (Forts.)
Für alle Elemente, die eher ungebräuchlich sind oder die ich nur selten verwende, benutze ich einfach einen beschreibenden Namen mit dem Präfix obj, z.B. objWebRequest.
Steuerelemente Die Beispiele in der Tabelle 18.2 gelten für Websteuerelemente, die mit WebForms verwendet werden, sowie für Windows.Forms-Steuerelemente. Steuerelement
Ungarische Notation
Button
btn [cmd(stammt aus der Zeit vor .NET VB)]
Label
lbl
Text box
txt
List box
lbw
Combo box
cbo
Radio buttons
opt
Check boxes
chk
Data grid
dg
Menu items
mnu
Tabelle 18.2: Häufig verwendete Präfixe für Steuerelemente
Kommentarblöcke Am Anfang jeder Prozedur und jeder Klasse können Sie einen mehrzeiligen Kommentar einfügen, der diese Codemenge beschreibt. Diese Codeblöcke sind häufig der Ort, an dem alle Informationen bereitgestellt werden, die nicht nur für eine einzelne Zeile oder Struk-
606
Tipps und Konventionen
tur, sondern für die ganze Codegruppe gelten. Normalerweise füge ich die folgenden Informationen in Kommentar-Header von Klassen (und Modulen) ein: 쐽
Name (der Klasse)
쐽
Abhängigkeiten (Referenzen, die diese Klasse benötigt; andere Klassen; Systemanforderungen wie z.B. SQL Server)
쐽
Zweck
쐽
Autor (normalerweise der ursprüngliche Autor)
쐽
Zuletzt bearbeitet von
쐽
Zuletzt bearbeitet am
쐽
Kommentare (Hinweise zu Problemen; gefundene und behobene Programmfehler; alles, was sich nicht auf eine bestimmte Zeile innerhalb der Klasse bezieht, aber dennoch beachtenswert ist)
Das Listing 18.4 zeigt ein Beispiel für einen Klassenkommentarblock Listing 18.4: Konsistente Kommentarblöcke sind die Mühe wert. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
Imports System Imports System.Data Imports System.Data.SqlClient '********************************************************** 'Klassenname: GetAuthors 'Abhängigkeiten: System.Data, SQL Server muss verfügbar sein 'Zweck: Auf SQL Server zugreifen und Daten aus der ' Tabelle Authors in der Beispieldatenbank ' Pubs abrufen 'Autor: Duncan Mackenzie 'Zuletzt bearbeitet von: Duncan Mackenzie 'Zuletzt bearbeitet am: 08/09/2001 'Kommentare: So geändert, dass der SQL Server-Name nicht in ' den Code geschrieben, sondern übergeben wird. '********************************************************** Public Class GetAuthors End Class
In Kommentarblöcke zu Prozeduren füge ich etwas andere Informationen ein: 쐽
Name (der Prozedur)
쐽
Abhängigkeiten (Referenzen, die von dieser Subroutine oder Funktion benötigt werden; andere Klassen; Systemanforderungen wie z.B. SQL Server)
607
Der letzte Schliff
쐽
Zweck
쐽
Autor (normalerweise der ursprüngliche Autor)
쐽
Parameter (wenn Sie nur einen Kommentarblock und viele überladene Versionen haben, nehmen Sie alle möglichen Parameter auf; wenn Sie vor jede überladene Version einen Kommentarblock setzen, nehmen Sie nur die aktuelle Version auf)
쐽
Zuletzt geändert von
쐽
Zuletzt geändert am
쐽
Kommentare (Hinweise auf Probleme; gefundene und behobene Programmfehler; alles, was sich nicht auf eine bestimmte Zeile in der Prozedur bezieht, aber dennoch beachtenswert ist)
Es ist nicht schwer, ein Add-In für .NET zu schreiben, das zumindest eine Schablone eines Kommentarblocks liefert und damit einige Tipparbeit erspart. Wenn Sie also Zeit haben, erstellen Sie als Wochenendprojekt ein solches Add-In.
18.3 Die Quellcodekontrolle verwenden Der Quellcode, den Sie erstellen, ist von unschätzbarem Wert. Er repräsentiert das Ergebnis Ihrer Arbeit und die Grundlage für zukünftige Arbeiten. Sowohl während der Entwicklung als auch danach müssen Sie eine Möglichkeit finden, Ihren Code zu speichern. Die Hauptanforderung für das Speichern des Codes besteht darin, dass der Code sicher ist und Änderungen protokolliert werden, sodass Sie zwei verschiedene Versionen vergleichen können. In Visual Studio .NET können Sie auf ein System zur Quellcodekontrolle zugreifen und die von Microsoft bereitgestellte Software entspricht im Wesentlichen dem mit Visual Studio 6.0 gelieferten System Visual SourceSafe (VSS) 6.0c. Um die Unterstützung für VSS in .NET zu aktivieren, installieren Sie einfach den Server und den Client (oder nur den Client, falls an anderer Stelle bereits ein Server vorhanden ist) auf Ihrem Rechner. Der Client aktiviert dann die richtigen Menübefehle in Visual Studio .NET. In diesem Abschnitt werden wir uns die Grundlagen der Quellcodekontrolle Schritt für Schritt ansehen und dabei besprechen, wie Sie Code in das Quellcodesystem einchecken, wie Sie ihn aus diesem System auschecken und wie Sie frühere Änderungen rückgängig machen.
608
Die Quellcodekontrolle verwenden
Code auschecken Nachdem Sie die Quellcodekontrolle in Visual Studio .NET aktiviert haben, indem Sie den Client-Teil der VSS installiert haben, stehen Ihnen einige neue Menüoptionen zur Verfügung. Im Menü DATEI bietet das Untermenü QUELLCODEVERWALTUNG mehrere Wahlmöglichkeiten, die davon abhängen, ob gerade ein Projekt geöffnet ist. Wenn nichts geöffnet ist, haben Sie die Möglichkeit, ein Projekt direkt aus dem Quellcode-Kontrollsystem zu öffnen. Wenn Sie VSS zum ersten Mal verwenden, ist es jedoch sinnvoller, ein Projekt zu öffnen und es dem Quellcodesystem hinzuzufügen. Dabei spielt es eigentlich keine Rolle, welches Projekt Sie öffnen. Für unser Beispiel ist es vielleicht am besten, wenn Sie eine neue, leere Konsolenanwendung erstellen. Nennen Sie die Anwendung SCCSample und fügen Sie den Code im Listing 18.5 in das Modul der Anwendung ein. Dieses Listing veranschaulicht nicht nur den Umgang mit der Codekontrolle, sondern zeigt auch, wie Sie auf Webinhalte zugreifen. Listing 18.5: Eine Webseite abrufen 1 Imports System.Net 2 Imports System.IO 3 Module SCC_Sample 4 5 Sub Main() 6 Dim sOutput As String 7 Dim sURL As String 8 sURL = "http://msdn.microsoft.com" 9 Try 10 Dim objNewRequest _ 11 As WebRequest = HttpWebRequest.Create(sURL) 12 Dim objResponse _ 13 As WebResponse = objNewRequest.GetResponse 14 Dim objStream _ 15 As New StreamReader(objResponse.GetResponseStream()) 16 sOutput = objStream.ReadToEnd() 17 Catch eUFE As UriFormatException 18 sOutput = "Error in URL Format: [" & sURL & "]" & _ 19 System.Environment.NewLine() & eUFE.Message 20 Catch eWEB As WebException 21 sOutput = "Error With Web Request: " _ 22 & System.Environment.NewLine() & eWEB.Message 23 Catch e As Exception 24 sOutput = e.ToString 25 Finally 26 Console.Write(sOutput) 27 End Try 28 Console.ReadLine()
609
Der letzte Schliff
29 End Sub 30 End Module
Nun speichern Sie alle Dateien, indem Sie ALLE SPEICHERN im Menü DATEI wählen. Nachdem Sie Ihr Projekt nun gestartet haben und es einige Daten enthält, fügen Sie es im nächsten Schritt dem Quellcode-Kontrollsystem hinzu. Wählen Sie unter DATEI, QUELLCODEVERWALTUNG, PROJEKTMAPPE ZUR QUELLCODEVERWALTUNG HINZUFÜGEN den Punkt MICROSOFT VISUAL SOURCESAFE. Für Visual SourceSafe sind Benutzer-IDs und Passwörter erforderlich, die Sie konfigurieren können, indem Sie das Programm VISUAL SOURCESAFE ADMIN ausführen, das Sie im Ordner VISUAL SOURCESAFE im Menü PROGRAMME finden. Wenn Sie aufgefordert werden, eine Benutzer-ID oder ein Passwort einzugeben, verwenden Sie im Moment einfach ADMIN und ein leeres Passwort (dies ist die Standardkonfiguration). Nun erscheint ein Dialogfenster, in dem Sie einen Pfad innerhalb des Quellcodesystems wählen können, um Ihre Lösung und die darin enthaltenen Projekte zu speichern (siehe Abbildung 18.2).
Abbildung 18.2: Wenn Sie Ihren Code einchecken, können Sie wählen, wo er im Quellcode-Container gespeichert wird.
Ihr Dialogfenster zeigt wahrscheinlich nur die Wurzel des Quellcode-Kontrollbaums ($/). Mein Quellcode-Kontrollsystem enthält hingegen bereits einige Projekte. Sie können Unterverzeichnisse anlegen, indem Sie den gewünschten Namen eingeben und auf ERSTELLEN klicken. Stellen Sie sicher, dass die Wurzel im Dialogfenster ausgewählt ist, geben Sie dann Beispiele ein und klicken Sie auf ERSTELLEN. Nun ist wunschgemäß das neue Verzeichnis ausgewählt; geben Sie Kap18 ein und klicken Sie ERSTELLEN an. Schließlich geben Sie noch den Namen für Ihre Lösung ein (SCCSample ist sinnvoll; siehe Abbildung 18.3) und klicken OK an. Nach der Mitteilung, dass dieses Projekt noch nicht existiert, werden Sie gefragt, ob es automatisch erzeugt werden soll; klicken Sie OK an.
610
Die Quellcodekontrolle verwenden
Abbildung 18.3: Zum Speichern Ihrer Projekte können Sie auf der Grundlage beliebiger Kategorien eine vollständige Hierarchie erstellen.
Nun fügt Visual Studio .NET dieses Projekt der Quellcodekontrolle hinzu und checkt es zugleich ein, sodass Sie es wieder auschecken müssen, bevor Sie es bearbeiten können. Beachten Sie, dass Ihre Dateien jetzt schreibgeschützt ist. Dies ist sowohl in der Titelleiste von Visual Studio .NET als auch im Projektmappen-Explorer angegeben (siehe Abbildung 18.4).
Abbildung 18.4: Ein Ziel der Quellcodekontrolle besteht darin, dass nur immer ein einziger Entwickler den Code bearbeiten kann. Daher kann nur Code, der ausgecheckt ist, verändert werden.
Nun können Sie Ihren Code auschecken, da Sie endlich alles zum ersten Mal eingecheckt haben. Wählen Sie das Modul im Projektmappen-Explorer, das ich in meinem Projekt SCCSample.vb genannt habe, und klicken Sie es mit der rechten Maustaste an. Im Kontextmenü, das nun erscheint, haben Sie eine neue Option: AUSCHECKEN (siehe Abbildung
611
Der letzte Schliff
18.5). Mit dieser Option können Sie diese eine Datei auschecken und bearbeiten. Eine Datei ist so lange schreibgeschützt, bis sie ausgecheckt wird, und wenn sie ausgecheckt ist, kann sie nur auf dem Rechner bearbeitet werden, von dem aus sie abgerufen wurde.
Abbildung 18.5: Sie können jede Einzeldatei auschecken, mit der Sie arbeiten möchten.
Wenn Sie das Projekt oder die Lösung mit der rechten Maustaste anklicken, steht Ihnen eine weitere Option zur Verfügung, die noch nützlicher sein könnte, nämlich JETZT AUSCHECKEN (RECURSIV). Diese Option (siehe Abbildung 18.6) checkt die Lösung oder das Projekt sowie alle darin enthaltenen Dateien aus. Diese Option, alles auf einmal auszuchecken, sehen Sie nur, wenn Sie die Option BEFEHL »AUSCHECKEN, OHNE DIALOGFENSTER AKTIVIEREN« IM MENÜ ANZEIGEN ausgewählt haben. Diese Option finden Sie im Ordner QUELLCODEVERWALTUNG des Optionsdialogfensters, das geöffnet wird, wenn Sie das Menüelement EXTRAS, OPTIONEN auswählen. Nachdem eine Datei ausgecheckt wurde, weist ein neues Symbol neben dem Namen dieser Datei im Projektmappen-Explorer auf diesen Zustand hin (siehe Abbildung 18.7). Außerdem ist dieser Zustand daran erkennbar, dass die Datei nicht mehr schreibgeschützt ist. Nun können Sie beliebig viele Änderungen an diesen Dateien vornehmen. Allerdings werden die Änderungen erst dann im System von SourceSafe widergespiegelt, wenn Sie die Dateien wieder einchecken.
612
Die Quellcodekontrolle verwenden
Abbildung 18.6: Häufig ist es einfacher, mit dem rekursiven Auschecken alle Projekte gleichzeitig auszuchecken, als sie einzeln auszuchecken.
Abbildung 18.7: Sobald Sie für Ihr Projekt die Quellcodekontrolle verwenden, zeigt der ProjektmappenExplorer an, ob eine Datei ein- oder ausgecheckt ist.
Code einchecken Während eine Datei ausgecheckt ist, können andere Programmierer, die das Quellcodesystem verwenden, nur auf eine schreibgeschützte Kopie der Datei zugreifen. Auf die Kopie, die Sie geöffnet haben, können nur Sie zugreifen. Wenn Sie eine Datei nicht verwenden,
613
Der letzte Schliff
sollten Sie sie nicht ausgecheckt lassen, sondern Ihren Code wieder einchecken, sobald Sie alle gewünschten Änderungen vorgenommen (und geprüft) haben. Die einzige Ausnahme ist, wenn Sie andere Programmierer bewusst davon abhalten möchten, die ausgecheckte Dateien zu bearbeiten. Für Ihren Beispielcode, der gegenwärtig ausgecheckt sein sollte, nehmen Sie am Modul einige Änderungen vor. Sie könnten z.B. in der Zeile 28 Console.ReadLine löschen und in der Zeile 8 die URL auf http://www.microsoft.com setzen. Speichern Sie die Dateien nun. Anschließend können Sie sie wieder in SourceSafe einchecken. Es gibt verschiedene Möglichkeiten, Dateien einzuchecken. Die einfachste besteht darin, einfach die Lösung oder das Projekt (dieselbe Ebene wie beim Auschecken) mit der rechten Maustaste anzuklicken und EINCHECKEN auszuwählen. Dadurch wird ein Dialogfenster (Abbildung 18.8) geöffnet, bei dem Sie angeben, welche Dateien in der Lösung eingecheckt werden sollen, und einen Kommentar hinzufügen, der erklärt, was warum geändert wurde.
Abbildung 18.8: Wenn Sie Dateien wieder einchecken, können Sie Kommentare hinzufügen, die die vorgenommenen Änderungen dokumentieren.
Änderungen einsehen und rückgängig machen (Rollback) Nachdem Sie Ihre Dateien wieder eingecheckt haben, können Sie mithilfe der Quellcodekontrolle mit einigen besonders schönen Funktionsmerkmalen experimentieren. Das erste dieser Merkmale ist das Betrachten des Änderungsprotokolls der Datei, das jedes Aus- und Einchecken zeigt, bei dem Änderungen vorgenommen wurden. Markieren Sie das Modul im Projektmappen-Explorer und wählen Sie dann das Menüelement DATEI, QUELLCODEVERWALTUNG, VERLAUF. Sie können das Änderungsprotokoll konfigurieren; wenn Sie einfach OK anklicken, wird eine Liste mit allen verfügbaren Versionen dieser Datei angezeigt (siehe Abbildung 18.9).
614
Die Quellcodekontrolle verwenden
Abbildung 18.9: Jedes Mal, wenn Sie geänderten Quellcode wieder in das Quellcode-Kontrollsystem einchecken, wird die neue Version für zukünftige Referenzen gepflegt.
Wählen Sie die erste Version, also die ursprüngliche Version, die Sie in SourceSafe eingecheckt haben, und klicken Sie dann die Schaltfläche UNTERSCHIEDE an. Nun wird ein Dialogfenster angezeigt, in dem Sie sicherstellen können, dass VISUELL ausgewählt ist. Klicken Sie dann auf OK. Als Nächstes wird ein Fenster angezeigt (Abbildung 18.1), das die Datei in ihrem aktuellen Zustand und in der gewählten Version anzeigt und die Unterschiede zwischen beiden Versionen hervorhebt. Das visuelle Format veranschaulicht, was geändert wurde, aber wenn Sie eine große Quelldatei haben, nimmt diese Anzeige der Änderungen sehr viel Raum ein. Die beiden anderen Formate (UNIX und SOURCESAFE) bieten kurze Zusammenfassungen der Änderungen zwischen zwei Versionen, was vielleicht eher Ihren Wünschen entspricht.
Abbildung 18.10: SourceSafe kann eine visuelle Schnittstelle bieten, die die Änderungen zwischen zwei Versionen detailliert anzeigt. Dadurch wird es einfacher, eine Problemquelle in einer bestimmten Version zu finden.
615
Der letzte Schliff
Falls Sie sehr viele Änderungen rückgängig machen möchten und nichts dagegen haben, alle Änderungen zu verlieren, die zwischen zwei Versionen vorgenommen wurden, können Sie im VERLAUF-Fenster zu einer früheren Version einer Datei zurückkehren. Im Gegensatz zum Betrachten der Unterschiede und zum manuellen Rückgängigmachen einzelner Änderungen ist die Wiederaufnahme einer früheren Version eine radikale Operation. Manchmal ist sie jedoch die einzige Möglichkeit, zu einem bekannten Zustand der Arbeit zurückzukehren. Wählen Sie die erste Version der Datei in der Verlaufsliste und klicken Sie dann die Schaltfläche VERSION ZURÜCKSETZEN an. Sie erhalten den Warnhinweis, dass diese Aktion nicht rückgängig gemacht werden kann. Machen Sie trotzdem weiter. Nun gibt es nur noch eine Version der Datei. Alle lokalen Kopien werden auf diese Version aktualisiert und wenn nun jemand diese Datei aus SourceSafe holt, bekommt er genau diese Version. Sie haben alle Änderungen, die Sie nach der ausgewählten Version vorgenommen haben, komplett gelöscht.
Sicherheitsüberlegungen bei der Verwendung von Visual SourceSafe Im Dialogfenster VERLAUF wird angezeigt, wer die Datei eingecheckt hat. In unserem Fall ist das immer »Admin«. Es ist wichtig, dass sich jeder unter seinem eigenen Namen anmeldet, damit Sie sehen können, wer welche Änderungen am Quellcode vorgenommen hat, und damit die Passwörter so eingerichtet werden, dass niemand unter dem Namen eines anderen Mitarbeiters Änderungen vornehmen kann. Visual SourceSafe scheint nicht in das Sicherheitssystem von Windows 2000/NT integrierbar zu sein, aber Sie können dieses Sicherheitssystem nachahmen, indem Sie Benutzer anlegen, deren Namen mit den IDs im Entwicklernetzwerk übereinstimmen, und verlangen, dass bei der Verbindung mit VSS diese IDS verwendet werden. Mit dem Visual-SourceSafe-Interface können Sie auf der Grundlage der Login-IDs der Anwender das Sicherheitssystem auf der Projektebene einrichten und damit den Zugriff auf ein Projekt nach Bedarf einschränken.
18.4 Zusammenfassung Die Dokumentation wird häufig als ein beim Programmieren notwendiges Übel betrachtet. Viele Programmierer fügen sie am Ende eines Projekts schnell und oberflächlich ein, damit sie wenigstens ein paar Kommentare liefern und die Arbeit als abgeschlossen bezeichnen können. Damit die Dokumentation aber nützlich und auch wesentlich einfacher zu erstellen ist, müssen Sie sie während der Arbeit am Projekt erstellen. Wenn Sie den Richtlinien in der heutigen Lektion folgen, wird Ihr Code möglichst leicht zu verstehen sein und die Kommentare füllen alle möglicherweise noch vorhandenen Verständnislücken.
616
Fragen und Antworten
18.5 Fragen und Antworten F
Sind die Kommentare nicht einfach eine Art, unklaren Code wettzumachen? Wenn der Code richtig geschrieben ist, sollte er doch keines Kommentars bedürfen. A
F
Diese Behauptung habe ich schon gelegentlich gehört und teilweise ist sie auch richtig. Je klarer Ihr Code ist, desto weniger Kommentare sind nötig. Wenn Sie diesen Gedanken konsequent zu Ende denken, kommen Sie zu der Schlussfolgerung, dass wirklich klarer Code keines Kommentars bedarf. Dem stimme ich zwar zu, aber ich glaube dennoch, dass jedes Programm, das aus mehr als zehn Codezeilen besteht, niemals völlig klar sein kann, da der Code von völlig verschiedenen Personen gelesen wird. Machen Sie Ihren Code so klar wie möglich und reduzieren Sie dadurch die Anzahl der Kommentare. Aber ganz können Sie nie auf Kommentare verzichten.
Ich war der Meinung, dass .NET Kommentare im XML-Stil enthält, aber das kam in dieser Lektion nicht vor. A
Leider sind die XML-Kommentare zwar ein Bestandteil von C#, nicht aber von Visual Basic .NET (oder anderer .NET-Sprachen). Wenn Sie an diesem Thema interessiert sind, können Sie den C#-Referenzmaterialien, die mit dem .NET-Framework geliefert werden, weitere Einzelheiten zur XML-Kommentierung entnehmen.
18.6 Workshop Die Antworten und Lösungen finden Sie im Anhang A.
Quiz 1. Wenn Sie von den in dieser Lektion besprochenen Namensstandards ausgehen, was wissen Sie dann über eine Variable namens sConnectionInfo? 2. Visual SourceSafe bietet Quellcodekontrolldienste für Visual Studio .NET. Nennen Sie wenigstens zwei Gründe, warum Sie ein Quellcodekontrollsystem verwenden sollten. 3. Wie nennt man die Schaltfläche, mit der Sie im Quellcodesystem eine frühere Version Ihres Codes wiederaufnehmen?
617
Die Anwendung bereitstellen
Die Anwendung bereitstellen
Nachdem Sie eine Anwendung erstellt haben, müssen Sie das System für die Anwender bereitstellen. Der Weg eines Systems von der Produktion zur Verwendung wird als Bereitstellung bezeichnet und häufig als eine der schwierigsten Phasen eines Entwicklungsprojektes angesehen. In der heutigen Lektion werden wir die Grundschritte bei der Bereitstellung einer Anwendung mit Visual Studio .NET besprechen:
Themen heute 쐽
Einführung in die Bereitstellung einer Anwendung
쐽
Ein Windows-Setup-Programm erstellen
쐽
Distributionsoptionen für ein Installationsprogramm
.NET hat zwar viel dafür getan, die Bereitstellung einer Anwendung weniger schwierig zu gestalten, aber sie ist noch immer ein notwendiger Prozess, der häufig komplizierter ist, als es zunächst scheint.
19.1 Einführung in die Bereitstellung einer Anwendung Die Bereitstellung einer Anwendung bedeutet einfach, dass Sie Ihre Anwendung den Personen zur Verfügung stellen, die sie verwenden werden. Für verschiedene Anwendungstypen umfasst die Bereitstellung verschiedene Elemente. Bei einer Webanwendung kann die Bereitstellung bedeuten, dass Sie Ihre Seiten und Ihren Code auf einem Webserver ablegen. Bei einer Windows-Anwendung bedeutet sie vielleicht, dass Sie Ihre .exe-Dateien an alle Anwender ausliefern. Häufig haben Sie es mit verschiedenen Dateien zu tun, die jeweils unterschiedliche Anforderungen haben, was den ganzen Prozess zusätzlich kompliziert. Eine Abhängigkeit besteht dann, wenn eine Datei wie z.B. Ihre .exe-Datei eine andere Datei wie z.B. eine Bibliothek (.dll) erfordert, damit sie richtig ausgeführt werden kann. So kann Ihre Anwendung z.B. von einer oder mehreren Dateien abhängig sein und auch diese Dateien können wieder Abhängigkeiten haben usw. Möglicherweise müssen Sie ein große Menge anderer Dateien installieren, um Ihre einzelne .exe-Datei so bereitzustellen, dass sie ordentlich funktioniert. Das .NET-Framework ist die Abhängigkeit, der Sie am häufigsten begegnen werden, da das .NET-Framework von allen .NET-Anwendungen verlangt wird. Das .NET-Framework wurde im Rahmen der Installation von Visual Studio .NET installiert und besteht aus vielen Dateien, die alle Klassenbibliotheken, die Common Language Runtime (CLR) und andere wichtige Komponenten enthalten. Damit Sie nicht all diese Dateien und ihre Abhängigkeiten einzeln einfügen müssen, wurde ein Merge-Modul bereitgestellt.
620
Einführung in die Bereitstellung einer Anwendung
Ein Merge-Modul ist eine einzelne Datei, die eigentlich ein ganzes Installationsprogramm ist, das Teil Ihrer Bereitstellung sein kann. Merge-Module (die auf der Festplatte als .msm-Dateien gespeichert werden) sind eine hervorragende Möglichkeit, alle für eine Bibliothek oder Anwendung erforderlichen Einstellungen, Abhängigkeiten und Dateien zu packen. Wenn Sie jemals eine Bibliothek oder ein anderes Projekt erstellen, das häufig in andere Setups eingefügt wird, dann können Sie eigene Merge-Module einrichten. Aber Visual Studio .NET enthält bereits viele MSMs, die Sie verwenden können. Diese MergeModule sind unter \Programme\Gemeinsame Dateien\Merge Modules gespeichert und enthalten die Teile des .NET-Frameworks, die erforderlich sind, damit Sie Ihre Anwendung auf einem anderen Rechner bereitstellen können. Vor der Einführung von .NET mussten bei der Installation häufig COM-Komponenten registriert werden. Für diese Komponenten mussten also in der Systemregistrierung Einträge angelegt werden, damit sie von Anwendungen, die sie verwenden wollten, gefunden und erstellt werden konnten. In .NET muss die Registrierungsdatenbank für die Installation einer Anwendung nicht mehr geändert werden, sondern alle Einstellungen und Metadaten zu einer Anwendung oder Komponente (z.B. einer Klassenbibliothek) werden entweder in der Datei selbst oder als zusätzliche Dateien in demselben Verzeichnis gespeichert. Da keine Registrierung mehr erforderlich ist, sind so genannte »X-Copy-Installationen« möglich, d.h., man kann Anwendungen komplett installieren, indem man die zugehörigen Dateien einfach von einem Rechner auf einen anderen kopiert. Wenn Sie dies bedenken, leuchtet Ihnen vielleicht nicht ein, warum Sie ein echtes Installationsprogramm erstellen sollen. Aber häufig muss man bei der Installation einer Anwendung mehr machen, als sie einfach nur auf dem Zielrechner auszuführen. Man muss Desktopsymbole und Einträge im Startmenü erstellen, Abhängigkeiten wie z.B. das .NETFramework installieren, Deinstallationsoptionen bereitstellen und manchmal auch Einträge für Anwendungseinstellungen in die Registrierungsdatenbank einfügen. Alle diese Aktionen haben Sie wahrscheinlich schon bei zahlreichen Anwendungsinstallationen gesehen. Sie stehen dann zur Verfügung, wenn Sie ein vollständiges Setup-Programm erstellen. In Visual Studio .NET legen Sie für die Bereitstellung einer Anwendung Windows-Installer-Dateien an. Der Windows Installer ist ein relativ neues Installationsverfahren, das vom Betriebssystem besser unterstützt und von mehr Produkten verwendet wird, z.B. von Visual Studio .NET, Microsoft Office und vielen anderen. Wenn Sie diese Technologie verwenden, können Ihre Setup-Programme viele ausgezeichnete Funktionsmerkmale nutzen, die alle als Teil der Plattform-SDK beschrieben sind. Da die URLS der zur Plattform-SDK gehörigen MSDN-Seiten leider häufig geändert werden, werde ich Ihnen keinen direkten Link nennen, sondern Sie statt dessen Schritt für Schritt durch den Bibliotheksbaum führen. Gehen Sie zur URL http://msdn.microsoft.com/ library und suchen Sie dort auf der linken Seite den Eintrag Setup and System Administra-
621
Die Anwendung bereitstellen
tion. Öffnen Sie diesen Knoten und gehen Sie dann weiter über Setup, Windows Installer und SDK Documentation bis zu Windows Installer (lassen Sie sich nicht davon irritieren, dass dieser Eintrag im Pfad zweimal erscheint). Hier stehen hervorragende und ausführliche Informationen über den Windows Installer zur Verfügung, aber am besten ist es vielleicht, wenn Sie alles selbst ausprobieren.
Ein einfaches Setup-Programm erstellen Statt weiter über die Bereitstellung einer Anwendung zu sprechen, werde ich Sie nun Schritt für Schritt durch die Erstellung eines einfachen Setup-Programms für eine einfache Anwendung führen. Damit dieses Beispiel perfekt ist, benötigen Sie einen weiteren Rechner (nicht den, auf dem Sie .NET installiert haben), und zwar möglichst einen, auf dem .NET nicht installiert ist. Dieser zweite Rechner (falls Sie einen haben) sollte über ein Netzwerk mit dem ersten verbunden sein. Ist dies nicht der Fall, benötigen Sie eine andere Möglichkeit, große Dateien zu verschieben, also z.B. einen CD-Brenner. Wenn Sie nur einen Rechner haben, können Sie alle Schritte nachvollziehen, aber die Demonstration wird die Ausführung eines echten Bereitstellungsprogramms weniger genau simulieren. Das Ziel dieses Beispiels besteht darin, eine einfache .NET-Anwendung zu erstellen, deren Inhalt völlig irrelevant ist. Da wir bereits darauf hingewiesen haben, dass wir in diesem Buch keine »Hello World!«-Beispielanwendungen erstellen möchten, müssen wir hier nun eine echte Anwendung einrichten, auf der unser Beispiel dann aufbauen kann. Um das Beispiel einfach zu halten, erstellen wir ein einzelnes Formular, das eine Verbindung zu einer SQL Server-Datenbank herstellt und eine Liste von Autoren (aus der Beispieldatenbank Pubs) anzeigt. In der Realität ist es natürlich nicht sinnvoll, eine Anwendung zu schreiben, bei der alles in einem einzigen Formular enthalten ist, aber auf diese Art bleibt unser Beispiel einfach. Etwas später in der heutigen Lektion werden wir dann ein realistischeres Beispiel erstellen. Auf den Code werde ich nur kurz eingehen. Ausführlichere Informationen über die Arbeit mit Datenbanken und den System.Data-Klassen finden Sie am Tag 12, Mit .NET auf Daten zugreifen. Um das Beispiel zu erstellen, richten Sie eine neue Windows-Anwendung in einer neuen Lösung ein. Ich werde Sie Schritt für Schritt durch alle relevanten Abschnitte führen und Sie können diese entweder gleich nachmachen oder den vollständigen Code von der Website zu diesem Buch herunterladen.
Das neue Projekt und die neue Lösung erstellen Wenn Sie gerade eine Lösung geöffnet haben, wählen Sie DATEI, PROJEKTMAPPE das Menüin dem Sie den Ordner VISUAL BASIC PROJEKTE und die Projektschablone WINDOWS-ANWENDUNG SCHLIESSEN, um sie zu schließen. Wenn nichts mehr geöffnet ist, wählen Sie element DATEI, NEU, PROJEKT. Das Dialogfenster NEUES PROJEKT öffnen Sie,
622
Einführung in die Bereitstellung einer Anwendung
wählen. Stellen Sie sicher, dass die vollständige Version dieses Dialogfensters angezeigt wird, indem Sie die Schaltfläche VERGRÖSSERN anklicken. Geben Sie dem Projekt einen passenden Namen wie z.B. AuthorList und markieren Sie das Kontrollkästchen PROJEKTMAPPENVERZEICHNIS ERSTELLEN, damit Sie für Ihre Lösung einen Namen angeben können. Geben Sie der Lösung einen Namen wie z.B. Pubs Demo, der anzeigt, dass die Lösung mehr als nur dieses eine Projekt enthalten könnte. Klicken Sie schließlich auf OK, um die neue Lösung und das neue Projekt zu erstellen. Legen Sie für jede Lösung, die mehr als ein Projekt enthält, ein Verzeichnis an. Dadurch werden Ihre Dateien automatisch organisiert, da alle Projekte, die zu einer bestimmten Lösung gehören, in ein gemeinsames Verzeichnis gelegt werden. Nun werden ein neues Projekt und eine neue Lösung mit den von Ihnen gewählten Namen erstellt.
Das neue Formular einrichten Im Windows-Anwendungsprojekt wird standardmäßig ein neues Formular namens Form1 erstellt. Geben Sie diesem Formular den aussagekräftigeren Namen frmDisplayAuthors und fügen Sie dem ansonsten leeren Formular das Steuerelement Listenfeld (ListBox) hinzu. Setzen Sie den Namen des neuen Listenfeldes auf lbAuthors, die Eigenschaft Dock auf Fill und die Eigenschaft IntegralHeight auf False. Diese Einstellungen liefern ein Datenraster, das das ganze Formular abdeckt, selbst wenn der Anwender die Größe des Formulars ändert. Die Eigenschaft IntegralHeight gibt es nur bei Listenfeldern. Sie ermittelt, ob die Höhe des Steuerelementes immer ein Vielfaches der Höhe eines einzelnen Listenelementes sein soll, damit kein Listenelement nur teilweise sichtbar ist. Wenn Sie IntegralHeight nicht auf False setzen, dann füllt das Listenfeld die Höhe des Formulars nicht immer vollständig, was weniger ansprechend aussieht.
Code hinzufügen Der Zweck dieser Anwendung besteht darin, eine Liste von Autoren anzuzeigen. Der Code muss also die Datenbankverbindung einrichten, diese Liste holen und sie im Steuerelement Listenfeld anzeigen. All diese Schritte haben wir am Tag 12 bereits besprochen, sodass ich hier nur den vollständigen Code zeigen werde (Listing 19.1), der aus dem Konstruktor des Formulars aufgerufen wird. Fügen Sie den Code ab der Zeile 8 in die Subroutine New Ihres Formulars ein und platzieren Sie die Routine LoadAuthors an irgendeine andere Stelle innerhalb des Formulars, jedoch nicht in eine andere Prozedur. Listing 19.1: Der Subroutine New für das Formular Code hinzufügen 1 2 3 4
Public Sub New() MyBase.New() 'Dieser Aufruf wird vom Windows-Form-Designer verlangt.
623
Die Anwendung bereitstellen
5 6 7 8: 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
InitializeComponent() 'Füge allen Initialisierungscode hinter dem Aufruf InitializeComponent() ein. LoadAuthors() End Sub Private Sub LoadAuthors() Dim sConnectionString As String sConnectionString = "Data Source=(local);" & _ "Initial Catalog=Pubs;" & _ "User ID=sa;password=daneel" Dim connPubs As New SqlConnection(sConnectionString) Dim cmdAuthors As New SqlCommand( _ "Select au_fname + ' ' + au_lname as Name from Authors", _ connPubs) Dim daAuthors As New SqlDataAdapter(cmdAuthors) Dim dsAuthors As New DataSet("Authors") connPubs.Open() daAuthors.Fill(dsAuthors, "Authors") lbAuthors.DataSource = dsAuthors.Tables(0).DefaultView lbAuthors.DisplayMember = "Name" End Sub
Hier müssen wir kurz ein Thema besprechen, das eigentlich nicht hierher gehört, das Ihnen in den letzten Phasen der Anwendungsentwicklung aber häufig begegnen wird. Der Code in Listing 19.1 enthält einen Aspekt, der bei der Bereitstellung Probleme verursachen wird. Er gibt den SQL-Server-Namen (Data Source in der Verbindungszeichenkette in Listing 19.1) mit (local) an. Diese Angabe teilt .NET mit, dass Sie die Verbindung zum SQL-Server auf dem lokalen Rechner herstellen möchten, was wahrscheinlich nur während der Entwicklung zutrifft. Wenn der Code hingegen auf mehreren Clientrechnern ausgeführt wird, sollen alle diese Rechner auf denselben zentralen SQL-Server zugreifen. Es gibt zwei Möglichkeiten, dieses Problem zu beheben: Der schnelle Weg besteht darin, den gewünschten SQL-Server-Namen fest in den Code zu schreiben. Bei der etwas komplizierteren Möglichkeit können Sie den Servernamen hingegen ändern, ohne den Code neu kompilieren zu müssen. Natürlich möchte ich Ihnen beide Verfahren vorführen, wobei ich mit der einfacheren Methode beginne, bei der Sie die Verbindungszeichenkette so ändern, dass sie den tatsächlichen Rechnernamen des SQL-Servers enthält: sConnectionString = "Data Source=Olivaw;" & _ "Initial Catalog=Pubs;" & _ "User ID=sa;password=daneel"
624
Einführung in die Bereitstellung einer Anwendung
Sonst brauchen Sie nichts zu ändern. Unabhängig davon, auf welchem Rechner dieser Code ausgeführt wird, wird er den Server immer durch den Namen Olivaw zu finden versuchen. Allerdings könnte es sein, dass sich dieser Servername im Laufe der Zeit ändert oder dass Sie für die Anwendung einen anderen Server verwenden möchten. Der Server, den Sie für die Bereitstellung der Anwendung verwenden, und der in der Produktionsphase verwendete Server sind normalerweise zwei verschiedene Rechner. Es ist sinnvoll, wenn Sie den Server, zu dem die Anwendung eine Verbindung herstellt, ändern können, ohne den Code neu kompilieren zu müssen. Vor .NET hätten Sie dies mithilfe der Windows-Registrierungsdatenbank ermöglicht. Diese Option steht Ihnen noch immer zur Verfügung, aber eines der wichtigen neuen Konzepte von VS .NET besteht darin, dass es möglich sein soll, eine Anwendung zu installieren, indem man das Verzeichnis dieser Anwendung einfach auf einen anderen Rechner (auf dem das .NET-Framework installiert ist) kopiert. Diese Art der Installation funktioniert nicht unbedingt richtig, wenn bestimmte Informationen von der Registrierungsdatenbank bereitgestellt werden müssen. Um die Installation und Neuinstallation von Anwendungen zu erleichtern, führt .NET ein neues System zum Speichern von Anwendungseinstellungen ein, das mehr Ähnlichkeit mit den alten .ini-Dateien als mit der Registrierungsdatenbank hat. Dieses neue System arbeitet mit Konfigurationsdateien, d.h. mit XML-Dokumenten, die mit einem ganzen Rechner oder mit einzelnen Anwendungen verbunden werden können. Nachdem wir dieses einfache Setup-Programm fertig gestellt haben, werden wir uns mit den Konfigurationsdateien befassen.
Das Projekt testen Kehren wir nun zu unserer eigentlichen Aufgabe zurück, dem Erstellen einer Setup-Datei. Bearbeiten Sie die Verbindungszeichenkette so, wie wir es gezeigt haben, damit sie die Verbindung zu einem SQL-Server korrekt herstellen und die Beispieldatenbank Pubs finden kann. Anschließend führen Sie das Projekt aus, um sicherzustellen, dass es funktioniert und Sie im Steuerelement ListBox eine Liste von Autorennamen sehen. Wenn alles funktioniert und Sie in der Verbindungszeichenkette einen anderen Servernamen als (local) angegeben haben, ist die Anwendung fertig gestellt und Sie können ein Setup-Programm für sie einrichten.
Das Setup-Projekt hinzufügen In VS .NET fügen Sie Bereitstellungsprojekte der Lösung hinzu, die ein oder mehrere weitere Projekte enthält, die Sie bereitstellen möchten. In unserem Fall haben wir ein einzelnes Projekt, das eine .exe-Datei produziert, und das Endergebnis muss die Installation dieser Datei auf dem Zielrechner sein. Um ein Setup-Programm für diese Anwendung zu
625
Die Anwendung bereitstellen
erstellen, fügen Sie der aktuellen Lösung ein neues Projekt hinzu (mit dem Menüelement DATEI, PROJEKT HINZUFÜGEN, NEUES PROJEKT) und wählen die Vorlage SETUP-ASSISTENT (siehe Abbildung 19.1), da dieser ein guter Ausgangspunkt ist.
Abbildung 19.1: Verwenden Sie entweder SETUPASSISTENT oder SETUP-PROJEKT und fügen Sie alle benötigten Dateien einzeln ein.
Wenn Sie dieses Projekt hinzufügen, startet es den Assistenten automatisch (siehe Abbildung 19.2).
Abbildung 19.2: Der Setup-Assistent führt Sie Schritt für Schritt durch die Erstellung eines einfachen Installationsprojekts für Ihre Lösung.
Dieser Assistent versteht, dass er zu einer Lösung gehört, und seine Optionen basieren auf den anderen Projekten in dieser Lösung. Die Standardeinstellung für den Installationstyp (siehe Abbildung 19.3) erstellt ein Installationsprogramm für eine Windows-Anwendung. Diese Einstellung verwenden wir für unser Beispiel. Auf dem folgenden Bildschirm (siehe
626
Einführung in die Bereitstellung einer Anwendung
Abbildung 19.4) können Sie wählen, was Ihr Setup-Programm bereitstellen soll, wobei die angebotenen Wahlmöglichkeiten die verschiedenen Teile widerspiegeln, die in anderen Projekten in dieser Lösung zur Verfügung stehen. In unserem Beispiel möchten wir die Ausgabe unserer einfachen Windows-Anwendung ermöglichen, die in unserem Fall eine .exe-Datei ist. Wir könnten auch den Installer verwenden, um unseren Quellcode bereitzustellen, Dateien von Fehlern zu bereinigen usw. Wählen Sie für unser Beispiel PRIMÄRE AUSGABE AUS AUTHORLIST.
Abbildung 19.3: Wenn Sie auf einem Windows-Rechner eine grafische Installation durchführen möchten, wählen Sie das Standard-Setup für eine WindowsAnwendung.
Abbildung 19.4: Die Ausgabe eines Projekts ist das Ergebnis seiner Kompilierung und kann eine .exe-Datei oder eine .dll sein.
Sie können nicht nur die Ausgabe eines oder mehrerer Projekte wählen (wenn die Lösung abgesehen vom Setup-Projekt noch mehr als ein Projekt enthält, dann werden alle diese Projekte in die Ausgabe-Liste aufgenommen), sondern auch beliebige zusätzliche Dateien
627
Die Anwendung bereitstellen
(siehe Abbildung 19.5). In unserem Beispiel habe ich nur eine Readme-Datei eingefügt, aber mit diesem Verfahren können Sie beinahe jeden Dateityp mit XML-Inhalt oder sogar eine Access-Datenbank (.mdb-Datei) bereitstellen.
Abbildung 19.5: Im Abschnitt ADDITIONAL FILES des Setup-Projekts können Sie z.B. Readme-Dateien, .config-Dateien und Dokumentationen hinzufügen.
Schließlich wird noch ein Zusammenfassungbildschirm angezeigt (siehe Abbildung 19.6) und wenn Sie auf FERTIG STELLEN klicken, ist der Assistent abgeschlossen. An dieser Stelle muss das Setup-Projekt die Abhängigkeiten der von Ihnen gewählten Ausgabe ermitteln, indem es die entsprechenden Projektdateien untersucht. Wenn dieser Vorgang abgeschlossen ist, sehen Sie im Projektmappen-Explorer das Setup-Projekt und im Unterordner ABHÄNGIGKEITEN ENTDECKT die Abhängigkeiten, die das Setup-Projekt gefunden hat. Mit dem Eigenschaftsfenster für das Projekt und dessen Dateien können Sie die Art ändern, wie das Setup-Programm erstellt wird. Im Moment lassen wir jedoch alles so, wie es ist.
Das Setup-Programm erstellen Ebenso wie bei allen anderen Projekten gilt auch bei unserem Setup-Projekt, dass Sie das Projekt zuerst kompilieren müssen, um die Ausgabe zu erzeugen. Drücken Sie (Strg)+(ª)+(B) oder wählen Sie das Menüelement ERSTELLEN, PROJEKTMAPPE ERSTELLEN, um alle Projekte in der Lösung einschließlich des neuen Setup-Projekts zu kompilieren. Das Kompilieren dauert einige Zeit, was vor allem am Setup-Projekt liegt, das alle erforderlichen übertragbaren .NET-Dateien zu einer einzigen .msi-Datei komprimiert. Auch mehrere andere Dateien werden kompiliert und in das Ausgabeverzeichnis des Setup-Projekts eingefügt, aber diese anderen Dateien sind eigentlich nur unterstützende Dateien für den Windows Installer.
628
Einführung in die Bereitstellung einer Anwendung
Abbildung 19.6: Das Zusammenfassungsfenster liefert Basisinformationen darüber, was das Setup-Projekt erstellt. Die eigentliche Arbeit beginnt jedoch erst, wenn Sie FERTIG STELLEN anklicken.
Visual Studio .NET verwendet die Version 2.0 des Windows Installers, die vielleicht bereits auf dem Zielrechner installiert ist. Falls diese Version noch nicht installiert ist, kann der Anwender sie mithilfe der Unterstützungsdateien installieren. Wenn Sie sicher sind, dass alle Anwender die richtige Version des Windows Installers haben, weil sie z.B. alle dieselbe Betriebssystem-Version verwenden oder bereits ein anderes .NET-Programm installiert haben, dann können Sie sich auch darauf beschränken, nur die .msi-Datei in die Bereitstellung aufzunehmen. Die Einstellungen, die das Einfügen dieser Installationsdateien steuern, finden Sie, indem Sie das Setup-Projekt markieren und dann im Menü PROJEKT den Eintrag EIGENSCHAFTEN wählen (siehe Abbildung 19.7).
Abbildung 19.7: Wenn Sie nicht sicher sind, welche Version (wenn überhaupt eine) des Windows Installers auf dem Zielrechner zur Verfügung steht, dann fügen Sie unbedingt die Dateien hinzu, die für eine Aktualisierung auf die richtige Version oder für deren Installation notwendig sind.
629
Die Anwendung bereitstellen
Der Begriff Zielrechner bezieht sich normalerweise auf den Rechner, der die Anwendung ausführen wird und auf dem die Anwendungsdateien installiert werden müssen. Wenn wir von Windows-Anwendungen sprechen, ist das Ziel eigentlich klar: die Rechner aller Anwender. Denken Sie aber daran, dass der Zielrechner bei Webanwendungen (z.B. Web Forms) der Webserver ist, nicht der Rechner des Endanwenders. Der Ordner ABHÄNGIGKEITEN Ihres Setup-Projekts enthält zahlreiche Dateien, die alle erforderlich sind, damit die Anwendung ausgeführt werden kann. Nun wissen Sie vielleicht, dass eine bestimmte Datei oder mehrere Dateien nicht installiert zu werden brauchen, aber das Setup-Programm nimmt sie dennoch auf. Wenn Sie den Ausschluss einer Datei erzwingen möchten, klicken Sie diese Datei mit der rechten Maustaste an und wählen Sie im Kontextmenü den Eintrag AUSSCHLIESSEN. Alternativ können Sie auch die Eigenschaften einer Datei einsehen und die Eigenschaft Exclude auf True setzen. Beides hat dieselbe Wirkung. Häufig schließt man Dateien dann aus, wenn man weiß, dass das .NET-Framework bereits installiert ist, und die Größe der Installation entsprechend verringern möchte. Damit haben wir nun ein vollständiges kleines Setup-Programm erstellt. Um es zu prüfen, stellen wir die .msi-Datei (die im Moment auf etwa 25 MB komprimiert ist) auf dem Zielrechner bereit. Kopieren Sie die Datei (die sich im Verzeichnis Debug unterhalb des Ordners mit dem Setup-Projekt befindet) auf den Zielrechner oder auf eine CD. Auf dem Zielrechner klicken Sie die .msi-Datei mit der rechten Maustaste an und wählen INSTALL. Wenn auf dem Zielrechner nicht die jüngste Version des Windows Installer installiert ist, erhalten Sie jetzt eine Fehlermeldung. Ansonsten sollte die Installation normal ablaufen und das .NET-Framework sowie Ihre .exe-Dateien auf dem Zielrechner ablegen. Möglicherweise ist es etwas schwierig, herauszufinden, was installiert wurde. Das Installationsprogramm platziert die Dateien auf dem Zielrechner unter dem Verzeichnis \Programme\\\, was standardmäßig in \Programme\ Microsoft\Setup1 umbenannt wird. Bevor Sie ein Setup-Programm für Ihre eigenen Zweck erstellen, sollten Sie sicherstellen, dass Sie die Eigenschaften Autor und Setup-Titel ändern, auf die Sie im Eigenschaftsfenster des Setup-Projekts zugreifen können.
Konfigurationsdateien Konfigurationsdateien sind XML-Dokumente, die auf der Grundlage von Name und Speicherort mit einer .NET-Anwendung verbunden sind. Diese Konfigurationsdateien folgen einem einfachen Namensformat: An den vollständigen Namen der Anwendung (z.B. AuthorList.exe) wird die Erweiterung .config (z.B. AuthorList.exe.config) angehängt. Die Konfigurationsdateien befinden sich in demselben Verzeichnis wie die Anwendungen.
630
Einführung in die Bereitstellung einer Anwendung
Der Inhalt des Dokuments ist Standard-XML-Code, aber ihr Format (der XML-Begriff ist Schema) wird von .NET vorgeschrieben. Wenn Sie in der Dokumentation zum .NET-Framework nach »Configuration file schema« suchen, erhalten Sie mehrere hilfreiche Ergebnisse, die ausführlich beschreiben, wie Sie eigene Konfigurationsdateien erstellen. Im Grunde genommen besteht die Datei aus einem oder mehreren Abschnitten, von denen jeder einen eigenen Namen hat und zusammengehörige Einstellungen enthält. Jeder Konfigurationsabschnitt muss als eine Zeile innerhalb der Tags und beschrieben sein. Allerdings gibt es auch auf der Ebene des Rechners Konfigurationsdateien und die auf dieser Ebene beschriebenen Abschnitte können Sie dann in anwendungsspezifischen .config-Dateien verwenden (ohne sie neu zu beschreiben). Einer der Hauptabschnitte, AppSettings, wird in der Konfigurationsdatei des Rechners bereits beschrieben, sodass Sie ihn einfach für Ihre Anwendungskonfiguration übernehmen können. Für die Autorenlisten-Anwendung habe ich eine Konfigurationsdatei erstellt, die als Beispiel für die Verwendung dieses Dateityps dienen kann. Den Code in dieser Datei sehen Sie unten. Die Datei enthält im Abschnitt AppSettings eine einzelne Zeile mit dem Namen Server und dem Wert Olivaw, die den Namen meines SQL-Servers repräsentiert.
In meinem Code muss ich Änderungen vornehmen, damit der fest darin enthaltene Servername in der Verbindungszeichenkette statt dessen aus der Konfigurationsdatei geladen wird. Das Listing 19.2 zeigt die geänderte Version von LoadAuthors, die den Namen des SQL-Servers mithilfe der Konfigurationsdatei ermittelt. Listing 19.2: Mit einer Konfigurationsdatei können Sie den Code flexibler machen. 1 2 3 4 5 6 7 8 9 10 11 12 13 14
Private Sub LoadAuthors() Dim sConnectionString As String sConnectionString = "Data Source=" & GetServerName() & ";" & _ "Initial Catalog=Pubs;" & _ "User ID=sa;password=daneel" Dim connPubs As New SqlConnection(sConnectionString) Dim cmdAuthors As New SqlCommand( _ "Select au_fname + ' ' + au_lname as Name from Authors", _ connPubs) Dim daAuthors As New SqlDataAdapter(cmdAuthors) Dim dsAuthors As New DataSet("Authors") connPubs.Open() daAuthors.Fill(dsAuthors, "Authors")
631
Die Anwendung bereitstellen
15 16 17 18 19 20 21 22 23 24 25
lbAuthors.DataSource = dsAuthors.Tables(0).DefaultView lbAuthors.DisplayMember = "Name" End Sub Private Function GetServerName() As String Dim sServerName As String sServerName = _ Configuration.ConfigurationSettings.AppSettings.Get("Server") Return sServerName End Function
Die eigentliche Arbeit mit der Konfigurationsdatei geschieht in der Zeile 22. Dadurch, dass meine Konfigurationsdatei nur integrierte Abschnitte verwendet, wird die Arbeit wesentlich erleichtert. Wenn ich meine eigenen Abschnitte einfügen möchte, muss ich den neuen Abschnitt als eine Zeile innerhalb des configSections-Blocks der Anwendungs- oder Rechnerkonfigurationsdateien beschreiben und dann die zusätzlichen Einstellungen als Zeilen innerhalb dieses neuen Abschnitts einfügen. In diesem Fall gibt die Zeile 22 alle Daten, die sich im Wertattribut der Zeile befinden, als Zeichenkette zurück und diese Zeichenkette kann ich dann bei der Herstellung der Verbindung verwenden (Zeile 3).
19.2 Bereitstellung mehrerer Projekte Die weiter oben in der heutigen Lektion erstellte Beispielanwendung war vollständig in einem einzigen Projekt enthalten. Häufiger kommen jedoch die Systeme vor, bei denen ein Teil der Funktionsmerkmale der Anwendung in eine oder mehrere Komponenten platziert wurde. Wenn Sie in Visual Studio .NET ein derartiges System erstellen, sind normalerweise alle zugehörigen Projekte (zumindest diejenigen, die Sie oder Ihr Team kontrollieren) Teil einer einzigen Lösung. In einer solchen Situation bietet Ihnen der Setup-Wizard andere Optionen, da er nicht wirklich erkennt, welches Projekt die »HauptAnwendung« ist. Um diesen Punkt zu veranschaulichen, zerlege ich die Beispielanwendung in zwei Projekte, indem ich den Datenzugriffscode aus dem ersten Projekt herausnehme und in eine separate Codebibliothek platziere. Das Listing 19.2 zeigt den aktuellen Code, der in die Subroutine LoadAuthors gekapselt ist. Fügen Sie der aktuellen Lösung ein neues Projekt, eine Visual Basic-Klassenbibliothek, hinzu und nennen Sie es PubsDataAccess. Dieses neue Projekt enthält eine einzige Klassendatei, die anfangs praktisch leer ist und Class1.vb heißt. Nennen Sie die Datei GetAuthors.vb und die Klasse GetAuthors und ersetzen Sie ihren Inhalt durch den Code im Listing 19.3.
632
Bereitstellung mehrerer Projekte
Listing 19.3: Mit einer separaten Klasse isolieren Sie den Datenzugriffscode. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
Option Explicit On Option Strict On Imports System Imports System.Data Imports System.Data.SqlClient Public Class GetAuthors Public Function AuthorList(ByVal sServerName As String) As DataSet Dim sConnectionString As String sConnectionString = "Data Source=" & sServerName & ";" & _ "Initial Catalog=Pubs;" & _ "User ID=sa;password=daneel" Dim connPubs As New SqlConnection(sConnectionString) Dim cmdAuthors As New SqlCommand( _ "Select au_fname + ' ' + au_lname as Name from Authors", _ connPubs) Dim daAuthors As New SqlDataAdapter(cmdAuthors) Dim dsAuthors As New DataSet("Authors") connPubs.Open() daAuthors.Fill(dsAuthors, "Authors") Return dsAuthors End Function End Class
Beachten Sie, dass die einzige Methode dieser Klasse, AuthorList (Zeile 10), einen Servernamen als Parameter entgegennimmt, wodurch wir diese Information aus der Hauptanwendung übergeben können. Die ursprüngliche Anwendung müssen wir nun so schreiben, dass sie die neue Bibliothek verwendet, wofür ebenfalls eine Referenz auf das Projekt notwendig ist. Um diese Referenz hinzuzufügen, klicken Sie den Ordner Verweise (des ursprünglichen Projekts) mit der rechten Maustaste an und wählen VERWEIS HINZUFÜGEN. Nun erscheint das Dialogfenster VERWEIS HINZUFÜGEN, bei dem Sie auf der Registerkarte PROJEKTE das neue Klassenbibliotheksprojekt finden (siehe Abbildung 19.8). Achten Sie darauf, dass das Projekt PubsDataAccess markiert ist und klicken Sie dann auf OK, um eine Referenz auf das Hauptprojekt einzufügen.
633
Die Anwendung bereitstellen
Abbildung 19.8: Auf der Registerkarte PROJEKTE können Sie andere Projekte, die sich in Ihrer Lösung oder anderswo befinden, direkt referenzieren, selbst wenn ihre Ausgabe noch nicht kompiliert wurde.
Nachdem Sie diese Referenz ergänzt haben, können Sie den Code für das Formular so schreiben, dass er die Bibliothek verwendet. Den Ergebniscode sehen Sie im Listing 19.4. Listing 19.4: Fügen Sie Code hinzu, um Werte in Ihre Anwendung zu laden. 1 Dim sServerName As String 2 Private Sub LoadAuthors() 3 Dim dsAuthors As DataSet 4 Dim objDA As New PubsDataAccess.GetAuthors() 5 6 dsAuthors = objDA.AuthorList(sServerName) 7 8 lbAuthors.DataSource = dsAuthors.Tables(0).DefaultView 9 lbAuthors.DisplayMember = "Name" 10: End Sub 11 12 Private Sub frmDisplayAuthors_Load _ 13 (ByVal sender As System.Object, _ 14 ByVal e As System.EventArgs) _ 15 Handles MyBase.Load 15 sServerName = _ 16 Configuration.ConfigurationSettings.AppSettings.Get("Server") 17 End Sub
Damit wir ein weiteres Setup-Projekt hinzufügen können, klicken Sie das vorhandene mit der rechten Maustaste an und wählen ENTFERNEN, um es aus der aktuellen Lösung zu entfernen. Klicken Sie die Lösung mit der rechten Maustaste an und wählen Sie im Kontext-
634
Bereitstellung mehrerer Projekte
menü HINZUFÜGEN, NEUES PROJEKT. Dadurch öffnen Sie das Dialogfenster NEUES PROJEKT. Wählen Sie den SETUP-ASSISTENT für einen Projekttyp, geben Sie einen anderen als den Standardnamen ein, um Konflikte mit dem bereits erstellten Projekt zu vermeiden, und klicken Sie dann OK an. Nun erscheint derselbe Setup-Wizard wie beim letzten Mal. Allerdings zeigt der Schritt EINZUBEZIEHENDE PROJEKTAUSGABEN AUSWÄHLEN nun andere Informationen als bei der vorherigen Ausführung. Diesmal werden die Ausgabedateien sowohl der Hauptanwendung als auch der neuen Bibliothek angezeigt (siehe Abbildung 19.9).
Abbildung 19.9: Wenn Sie ein Setup in einer Lösung mit mehr als einem Projekt (zusätzlich zum Setup selbst) kompilieren, stehen Ihrem Setup-Programm alle Ausgaben aller Projekte zur Verfügung.
Trotz der Unterschiede und all der Optionen, die nun im Schritt EINZUBEZIEHENDE PROJEKTAUSGABEN AUSWÄHLEN zu sehen sind, sollten Sie nur die Primärausgabe der Hauptanwendung auswählen. In diesem Fall ist dies die AuthorList, also das Projekt, das das einzelne Windows-Formular enthält. Sie brauchen also nur die primäre Ausgabe dieses einen Projekts zu wählen. Nachdem Sie die gewünschte Ausgaben gewählt haben, beenden Sie den Assistenten. Der Setup-Assistent ermittelt nun die Abhängigkeiten der gewählten Ausgabe(n). In unserem Fall findet er eine zusätzliche Abhängigkeit von der DLL PubsDataAccess. Wegen dieser Abhängigkeit wird die erforderliche DLL installiert, auch wenn Sie sie nicht als eine der gewünschten Ausgaben genannt haben. Wenn Sie auch die Ausgabe dieser DLL gewählt hätten, würde die Installation zwar nicht scheitern, aber während der Kompilierung der Setup-Datei würden Sie den folgenden Warnhinweis erhalten: WARNUNG: ZWEI ODER MEHR OBJEKTE VERWENDEN DASSELBE ZIEL ... Für den Endanwender sieht die Installation dieses Projekts nicht anders aus als die des vorherigen, obwohl eine zusätzliche Bibliothek installiert wird, und alles wird immer noch auf demselben Zielrechner installiert.
635
Die Anwendung bereitstellen
19.3 Zusammenfassung Die Bereitstellung von Systemen ist einer der letzten Schritte bei der Erstellung einer Anwendung und sie ist häufig wesentlich komplizierter als die Entwickler erwarten. .NET hat die Bereitstellung erheblich vereinfacht, aber nur wenn Sie Ihre Anwendung und das Setup-Programm kompilieren und die Bereitstellung auf möglichst vielen Rechnern prüfen, können Sie annähernd sicher sein, dass die Bereitstellung in der Realität funktionieren wird.
19.4 Fragen und Antworten F
Der Windows Installer scheint mehr Optionen zu haben, als ich in den Setup- und Bereitstellungsprojekten finden kann. Wie kann ich ein Setup-Programm erstellen, das dieselben Merkmale wie kommerzielle Installationen hat? A
F
Ich war der Meinung, mit .NET bräuchte ich keine Installationsprogramme mehr zu schreiben, sondern könnte einfach XCOPY verwenden. A
636
Die von VS .NET bereitgestellten Setup-Merkmale sind zwar hervorragend, decken aber nur die Grundanforderungen an ein Installationsprogramm ab. Die Setup-Projekte in VS .NET haben zwar mehr Eigenschaften, als ich in der heutigen Lektion gezeigt habe, aber um alle Eigenschaften im Windows Installer verfügbar zu machen, müssen Sie mit einem Installationsprogramm eines Fremdherstellers arbeiten. Beispiele hierfür sind Install Shield (http://www.installshield.com) und Wise Installer (http://www.wisesolutions.com).
Das stimmt, Sie brauchen für Ihre Anwendungen keine Setup-Programme bereitzustellen, sondern können die benötigten Funktionsmerkmale zur Verfügung stellen, indem Sie einfach das Anwendungsverzeichnis kopieren. Aber die Installation des .NET-Frameworks ist etwas komplexer und muss auf vielen Rechnern vorhanden sein, bevor Ihre Anwendung ausgeführt werden kann. Außerdem sollte für Ihre Zwecke zwar auch eine .bat-Datei genügen, die den Befehl XCOPY ausführt, aber ein grafisches Installationsprogramm mit Dialogfenstern und anderen Optionen sieht doch wesentlich professioneller aus.
Workshop
19.5 Workshop Der Workshop soll Ihnen dabei helfen, mögliche Fragen vorherzusehen und das Gelernte zu wiederholen. Außerdem soll er Sie dazu anregen, über die praktische Umsetzung Ihres Wissens nachzudenken. Die Antworten und Lösungen finden Sie im Anhang A.
Quiz 1. Wenn Sie nicht sicher sind, ob das .NET-Framework auf allen Zielrechnern zur Verfügung steht, müssen Sie dann zwei Setup-Programme erstellen (eines mit und eines ohne)? 2. Wenn Sie eine Anwendung erstellt haben, die aus einem einzigen Hauptprojekt und mehreren vom Hauptprojekt verwendeten Bibliotheksprojekten besteht, müssen Sie dann mehrere Setup-Programme erstellen? 3. Wie richten Sie ein Installationsprogramm ein, damit es mehrere Formen einer einzigen Installation gibt, z.B. typische Installation, vollständige Installation und minimale Installation? 4. Wie nehmen Sie in Ihre Installation eine Konfigurationsdatei (.config) oder andere zusätzliche Dateien auf?
Übung Erstellen Sie eine Windows-Forms-Anwendung, die mit einer .config-Datei ermittelt, welcher Titel (die Eigenschaft Text von Form) in die Windows-Titelleiste eingefügt werden soll. Erstellen Sie dann für diese Anwendung eine .config-Datei und ein Setup-Programm. Stellen Sie das Projekt bereit. Versuchen Sie, den Inhalt der .config-Datei zu ändern und die Anwendung erneut auszuführen.
637
Einführung in XML
Einführung in XML
Sofern Sie in den vergangenen Jahren nicht jeden Kontakt zu Menschen vermieden haben, die in irgendeiner Form mit einem Computer in Kontakt gekommen sind, haben Sie das Akronym »XML« wahrscheinlich schon einmal gehört. XML (eXtensible Markup Language) ist heute überall; es erscheint sogar in Nachrichtenmagazinen für die breite Öffentlichkeit. In der heutigen Lektion werde ich versuchen, hinter die Fassade zu blicken und zu untersuchen, was XML für Ihre Programme tun kann.
Themen heute 쐽
Was ist XML?
쐽
Mit XML arbeiten
20.1 Was ist XML? Die Extensible Markup Language (XML; natürlich wäre die richtige Abkürzung EML, aber XML klingt doch viel besser) ist eine Art, einem Text Informationen hinzuzufügen. XML ist derzeit ein beliebter Softwarebegriff und ganze Unternehmen und Produktserien wurden gegründet oder angepasst, nur damit man den Produktbroschüren das Wort XML hinzufügen konnte. Rund um XML gibt es viele Mythen und falsche Vorstellungen davon, was es machen kann und machen soll. Bevor wir uns mit diesen Missverständnissen befassen, wollen wir und ansehen, was XML eigentlich macht. XML stellt eine Möglichkeit dar, einem Text Informationen hinzuzufügen. Welche Informationen? Informationen über den Text. Dies sieht vielleicht so aus, als wollte ich etwas durch sich selbst erklären, aber ich meine das ganz ernst. Es gibt dafür auch einen anderen Namen: Metadaten. Metadaten sind Informationen über Informationen. Die formale Definition lautet folgendermaßen: »Data about data. Metadata describes how and when and by whom a particular set of data was collected, and how the data is formatted. Metadata is essential for understanding information stored in data warehouses (from Internet.com's Webopedia).«1 Betrachten wir als Beispiel das vorliegende Buch vom Standpunkt der Metadaten aus. Ein wichtiger Teil des Buches sind offensichtlich die darin enthaltenen Informationen. Dies sind die Daten. Darüber hinaus gibt es aber noch weitere Informationen über das Buch: die Anzahl der Lektionen, die Anordnung der Abschnittsüberschriften usw. Selbst die For1
Daten über Daten. Metadaten beschreiben, wie und wann und von wem eine bestimmte Datenmenge gesammelt wurde und wie diese Daten formatiert sind. Metadaten sind eine wesentliche Voraussetzung, damit man die in Daten-Warehouses gespeicherten Informationen verstehen kann. (Aus der Webopedia von Internet.com.)
640
Was ist XML?
matierung bietet zusätzliche Informationen über den Text. Wenn ich Text fett schreibe, können Sie davon ausgehen, dass er wichtiger ist als der umgebende Text. Dies sind Informationen über den Text in diesem Buch, die so genannten Metadaten. Die Metadaten sind ein mächtiges Konzept: Sie geben den Informationen einen Rahmen und einen Zweck. Es gibt viele Arten, Metadaten zu Daten hinzuzufügen. In einer Datenbank sind die Metadaten z.B. die Namen und Größen der Spalten in den Datenbanktabellen. Sie teilen Ihnen mit, welche Datentypen sich in dieser Spalte befinden sollten, wie groß die Spalte ist und vielleicht liefert der Name auch schon einen Hinweis auf den Inhalt. HTML bietet ebenfalls Metadaten, und zwar über die relative Wichtigkeit eines Textes: Befindet er sich in einem Überschriften-Tag? Und auf welcher Überschriftenebene? Allerdings ist HTML kein gutes Beispiel für Metadaten, da es der Formatierung dient, nicht der Bereitstellung von Informationen. Eines haben wir von HTML jedoch gelernt, nämlich wie mächtig die Möglichkeit ist, einem Text mit einfachen Tags eine Formatierung, also Metadaten, hinzuzufügen. Tags sind die Elemente in spitzen Klammern (z.B. ), die Sie in HTML-Code sehen. Wenn Sie noch nie HTML-Code gesehen haben oder einfach nur wissen möchten, wie er aussieht, dann betrachten Sie den Quelltext irgendeiner Webseite. Dafür wählen Sie im Internet Explorer das Menüelement ANSICHT, QUELLTEXT ANZEIGEN. Im Quelltext sollten Sie eine Menge Tags sehen. Mithilfe der Tags kennzeichnet HTML, welcher Text fett oder kursiv geschrieben werden soll, wo eine Tabelle oder ein Bild platziert werden soll usw. Außerdem kennzeichnet es damit das Ende aller dieser Abschnitte. Wenn Sie also den Quellcode der Webseite in der Abbildung 20.1 betrachten, sollten Sie Folgendes sehen: <strong>April 22, 1970
Das Tag <strong> markiert alles zwischen diesem Tag und dem End-Tag als wichtig und die meisten Browser zeigen dies dann in Fettschrift an. Das Tag <strong> hat dem Text Informationen hinzugefügt, ist also ein Metadatum. XML hat Ähnlichkeit mit HTML; beide sind miteinander verwandt. In den Sechzigerjahren erfand eine Gruppe von Wissenschaftlern, die es als irritierend empfand, auf wie viele verschiedene Arten man einen wichtigen Punkt kennzeichnen konnte, die so genannte Standard Generalized Markup Language (SGML). SGML führte viele innovative Konzepte ein: 쐽
Der Anfang eines Abschnitts soll mit einem Markierungszeichen angegeben werden.
쐽
Das Markierungszeichen soll sich selbst als solches ausweisen, wofür Zeichen verwendet werden sollen, die in normalem Text nur selten vorkommen. Die SGML-Autoren haben sich dabei für die spitzen Klammern entschieden ().
쐽
Das Markierungszeichen soll einige – wenn auch noch so kurze – Informationen über die enthaltenen Informationen liefern.
641
Einführung in XML
쐽
Das Ende der Informationen soll mit einem weiteren Markierungszeichen gekennzeichnet werden.
쐽
Dieses zweite Markierungszeichen soll sich selbst als mit dem ersten Markierungszeichen verwandt kennzeichnen, indem es dieselben Informationen liefert und ein Zeichen ergänzt, das in der ursprünglichen Information wahrscheinlich nicht vorkommt.
Abbildung 20.1: Ein wichtiges Datum
Das Ergebnis könnte ungefähr folgendermaßen aussehen: <strong>April 22, 1970
Wie Sie vermutlich bereits geahnt haben, ist HTML von SGML abgeleitet. Es ist eine von vielen auf einen bestimmten Zweck ausgerichteten Verwendungsmöglichkeiten von SGML. SGML dient nicht dazu, konkrete Dokumente auszuzeichnen, sondern es identifiziert die Tag-Typen, die Sie einem Dokument hinzufügen können, also die Metadaten zu einem Dokument. Anschließend erstellt man eine Definition eines Dokuments (d.h. eine Liste der Tags, die verwendet werden können) und legt mithilfe dieser Definition Dokumente an. HTML identifiziert eine Reihe von Tags, die für die Formatierung verwendet werden können, und mit diesen Tags wurden all die schönen Seiten erstellt, die es im World Wide Web (WWW) gibt. XML ist mit HTML verwandt. Beide erben viele der SGML-Konzepte. Allerdings hat XML mehr Ähnlichkeit mit SGML als mit HTML. XML definiert nicht einen bestimmten Dokumenttyp (z.B. eine Webseite), sondern eine Möglichkeit, die einzelnen Teile eines Dokuments zu identifizieren (ebenso wie SGML). Es verwendet dieselben Grundkonzepte wie SGML. Es kennzeichnet den Anfang und das Ende eines Informationsabschnitts mit
642
Was ist XML?
Tags. Diese Tags sind Wörter in spitzen Klammern, die Informationen über die enthaltenen Informationen liefern. Anders ausgedrückt: Es sieht genauso aus wie die beiden oben gezeigten HTML- und SGML-Codestücke. Warum haben die Autoren von XML etwas erfunden, das so viel Ähnlichkeit mit zwei vorhandenen Standards hat? Der Grund hierfür ist, dass SGML komplex ist und HTML eigentlich nur dafür dient, einem Dokument Formatierungsinformationen hinzuzufügen. XML soll ein vereinfachtes SGML sein, mit dem man beliebig viele branchen- oder technologiespezifische Tag-Gruppen erstellen kann. Es gibt viele solcher Tag-Gruppen oder Sprachen, unter anderem solche, die mathematische Gleichungen (MathML) oder Rezepte beschreiben, und sogar eine HTML-Version, die mit XML definiert wurde (XHTML). All diese Sprachen definieren die möglichen Metadaten, die auf Informationen angewendet werden können. Allerdings folgen alle den XML-Regeln. Wie lauten die XML-Regeln also? Offiziell sind sie folgendermaßen definiert: 쐽
XML soll im ganzen Internet einfach zu verwenden sein.
쐽
XML soll eine Vielfalt von Anwendungen unterstützen.
쐽
XML soll mit SGML kompatibel sein.
쐽
Es soll leicht sein, Programme zu schreiben, die XML-Dokumente verarbeiten.
쐽
Die Anzahl der optionalen Funktionsmerkmale von XML soll auf ein absolutes Minimum beschränkt sein, im Idealfall soll es überhaupt keine geben.
쐽
XML-Dokumente sollen für Menschen lesbar und einigermaßen klar sein.
쐽
Der XML-Entwurf soll schnell fertig gestellt sein.
쐽
Der Entwurf von XML soll formal und umfassend sein.
쐽
XML-Dokumente sollen leicht zu erstellen sein.
쐽
Die Kürze ist bei der XML-Auszeichnung von geringer Bedeutung.
Das ist also XML. Sehen wir uns nun die falschen Vorstellungen an, die ich weiter oben erwähnt habe. Davon gibt es eine Menge. Für viele dieser Missverständnisse sind die Marketingstrategien von Unternehmen, die XML verwenden, verantwortlich; viele andere lassen sich auf Autoren zurückführen, die eigenartige Vorstellungen von XML verbreiten (natürlich gehöre ich nicht zu diesen Autoren). Den folgenden Missverständnissen begegne ich relativ häufig: 쐽
Man muss zwischen Visual Basic .NET und XML wählen. XML ist zwar eine Sprache, aber keine Programmiersprache wie Visual Basic .NET. Viele Anwender finden den Begriff »Sprache« verwirrend und stellen Fragen wie z.B.: »Soll ich Visual Basic .NET oder XML lernen?« XML ist keine Programmiersprache, sondern eine Möglichkeit, Informationen zu beschreiben.
643
Einführung in XML
쐽
XML ist kompliziert und schwer zu lesen. Viele Anwender sind von den Tags und End-Tags verwirrt. Warum soll man einem Dokument also diesen »Kram« hinzufügen, wenn X (irgendein anderes Verfahren) dasselbe macht? Die Antwort ist einfach: XML ist eine einfache Möglichkeit, die Teile eines Dokuments zu identifizieren. Häufig wird z.B. angeführt, die so genannten Comma-separated Values (CSV) seien besser als XML: Sie seien weniger kompliziert, leicht zu erstellen und die Dateien seien kleiner (siehe den nächsten Punkt). Aber betrachten Sie die folgenden beiden Listings: Welches verrät Ihnen mehr über die angezeigten Informationen? John Bull Mary Tell 1, John, Bull 2, Mary, Tell
Ich persönlich bin der Meinung, dass das erste Listing (XML) wesentlich besser lesbar ist. 쐽
XML ist zu langsam (oder zu groß) für eine ordentliche Arbeit. Dieses Argument wird häufig von altmodischen Programmierern angeführt, die meinen, dass man Speicher- und Prozessorzeit und -leistung verschwendet, wenn man ein Zeichen zu viel schreibt. Ich stolpere zwar auch manchmal in diese Falle, allerdings nicht bei XML. Häufig hört man das folgende Argument: »All diese Tags und End-Tags benötigen sehr viel Platz und Zeit. Wir sollten statt dessen X (irgendetwas, das dem Sprecher besser gefällt) verwenden.« Die meisten von Ihnen können sich wahrscheinlich daran erinnern, wozu diese Denkweise geführt hat: zum Y2K-Bug. Es gibt auch überoptimierten Code (zumindest meiner Meinung nach). Manchmal ist ein weniger effizientes Verfahren, das den Zweck des Codes leichter durchschaubar macht, wesentlich besser (wenigstens glaube ich das). XML ist eines dieser weniger effizienten, aber leichter zu verstehenden Verfahren.
쐽
Die Lösung heißt XML, aber wie lautet die Frage? XML löst weder das weltweite Hungerproblem noch verhindert es Kriege. Es bereitet noch nicht einmal Ihren Frühstückskaffee zu. Es möchte Ihnen einfach nur Informationen über einen Textblock liefern. Viele Programmierer möchten XML als allumfassende Lösung verwenden oder versuchen es dort anzuwenden, wo es weder passen kann noch soll. Wenn Sie den Eindruck haben, dass XML mehr Probleme verursacht als löst oder sogar überhaupt keine Probleme löst, dann verwenden Sie es nicht, sondern suchen Sie nach einer anderen und besseren Lösung.
644
Was ist XML?
Elemente Wenn Sie ein XML-Dokument erstellen, können Sie viele verschiedene Elemente verwenden. Allerdings enthält fast jedes XML-Dokument zwei wichtige Teile: Elemente und Attribute. Ein Element ist der grundlegende Baustein jeder XML-Datei. Es besteht aus einem Startund einem End-Tag sowie dem Inhalt zwischen diesen beiden Tags. Der folgende XMLCode enthält z.B. drei Elemente: John Bull
Das erste Element ist employee. Dieses Element beginnt mit dem employee-Tag (), endet mit dem employee-End-Tag () und enthält die beiden Tags und . Das Element first name beginnt mit dem Tag , endet mit dem End-Tag und enthält den Text John. Elemente definieren die wichtigen Dinge in der XML-Datei. Der obige XML-Code beschreibt offenbar einen Angestellten eines Unternehmens und dieser Angestellte hat einen Vornamen und einen Nachnamen. Elemente haben Ähnlichkeit mit den Objekten und Eigenschaften, die wir am Tag 7, Mit Objekten arbeiten, behandelt haben. So könnten wir hier sagen, dass unser Code ein employee-Objekt mit zwei Eigenschaften beschreibt.
Attribute Der Großteil jeder XML-Datei besteht zwar aus Elementen, sie kann jedoch auch Attribute enthalten. Attribute liefern zusätzliche Informationen über ein Element. Wenn Sie bereits mit HTML gearbeitet haben, wissen Sie wahrscheinlich bereits, was ein Attribut ist. Betrachten Sie z.B. das folgende HTML-Fragment: Visual Basic Home Page href ist ein Attribut, das einen Speicherort für das Element a (Anker) definiert. Es definiert, wohin das Ankerelement den Browser leiten soll, wenn Sie das Attribut anklicken. Für diejenigen Leser, die mit HTML-Code nicht vertraut sind, möchte ich noch hinzufügen, dass diese Codezeile einen Hyperlink (einen dieser unterstrichenen Links) in eine Webseite einfügt.
Andere Attribute liefern Informationen über andere Elemente. Dies bringt uns zu einem der wichtigsten Unterschiede zwischen Attributen und Elementen: Attribute können nicht für sich allein stehen und müssen immer auf Elemente angewandt werden. Ein Attribut
645
Einführung in XML
erscheint immer innerhalb des öffnenden Tags eines Elements. Und es hat immer die folgende Form: Name="Wert"
Ein Attribut besteht also aus zwei Teilen: einem Namen und einem Wert. Der Name muss innerhalb jedes Elements eindeutig sein, obwohl zwei Elemente dasselbe Attribut haben können. Außerdem kann ein Element viele Attribute haben. Die XML-Gemeinde zerfällt in zwei Lager: Auf der einen Seite stehen diejenigen, die fordern, man solle überall Elemente verwenden. Alle Informationen sollen Elemente sein, die wiederum in anderen Elementen enthalten sind. Begründet wird dies damit, dass man zu etwas, das im Moment selbst wie eine Eigenschaft erscheint, später vielleicht auch wieder eine Eigenschaft hinzufügen möchte. Ein Beispiel hierfür ist die Eigenschaft Adresse, für die man später vielleicht die zusätzlichen Eigenschaften Straße, Stadt und Postleitzahl definieren muss. Die Befürworter des »Alles muss ein Element sein«-Konzepts würden den Angestellten John Bull aus unserem vorherigen Beispiel mit dem folgenden XML-Code definieren: 1 John Bull
Auf der anderen Seite stehen diejenigen, die verlangen, dass eine Information, die etwas über ein anderes Element aussagt (z.B. eine Eigenschaft eines Objekts), ein Attribut sein sollte. Dadurch werden XML-Dateien kleiner und in mancher Hinsicht leichter lesbar. Die Befürworter dieser Theorie würden John Bull folgendermaßen definieren:
Natürlich können Sie die Alternative wählen, die Ihnen besser zusagt, oder auch eine Mischung aus beidem verwenden. Bei der Entscheidung, ob etwas ein Element oder ein Attribut sein soll, sollten Sie jedoch die folgenden Punkte berücksichtigen:
646
왘
Ein Attribut kann keine Kinder haben. Wenn Sie also etwas zu einem Attribut machen, dann ist dies wie eine Einbahnstraße: Sie können später keine weiteren Informationen hinzufügen, ohne die Struktur der Datei zu verändern. Bei Elementen ist es hingegen durchaus möglich, später bei Bedarf neue Kindelemente oder Attribute zu ergänzen.
왘
Ein Dokument, das vorwiegend Attribute verwendet, ist in der Regel kleiner als eines, das vor allem Elemente verwendet. Je mehr Attribute und je weni-
Was ist XML?
ger Elemente Sie in Ihren XML-Code benutzen, desto kleiner wird der Code. Dies liegt in erster Linie daran, dass Attribute kein schließendes Tag haben.
Schemata Obwohl die Schemata eigentlich nicht wirklich zu XML gehören, spielen sie bei der Verwendung von XML eine wichtige Rolle. Sie werden sogar noch wichtiger sein, wenn sie ein offizieller Standard werden. Zum Zeitpunkt der Drucklegung dieses Buches sind die XML-Schemata erst ein empfohlener Standard. Im Grunde genommen sind sie eine XMLAnwendung: Genau wie die Dateien, die wir weiter oben erstellt haben, werden sie in XML geschrieben. Ebenso wie jeder andere XML-Code sind sie Informationen über Informationen (Metadaten). XML-Schemata definieren eine richtige Art, eine XML-Datei zu strukturieren. Dazu gehören die Datentypen der einzelnen Elemente und Attribute, die Angabe, was ein Kindelement eines anderen Elements sein kann, und sogar, welche Elemente für eine bestimmte Datei zulässig sind. Vielleicht haben Sie sich schon gefragt, warum Schemata erforderlich sind. Habe ich nicht gesagt, dass Sie eine XML-Datei erstellen können, die eine beliebige Kombination von Elementen und Attributen enthält? Das stimmt zwar, aber es gibt einen Unterschied zwischen der Erstellung einer XML-Datei, die einige Daten enthält, und XML-Code, der einen bestimmten Datentyp definiert. Wenn Sie mit einer Datei arbeiten, die Daten über Angestellte enthält, dann erwarten Sie bestimmte Elemente: den Namen, die Personalnummer, Kontaktinformationen usw. Allerdings wissen Sie nicht, wo diese Informationen in der Datei zu finden sind oder wie die zugehörigen Tags heißen. Ohne ein Schema müssen Sie sich die Datei ansehen und speziellen Code schreiben, um die einzelnen Dateien zu lesen. Mit einem Schema kennen Sie die Struktur der Daten im voraus. Nicht jedes Feld befindet sich bei allen Daten unbedingt an derselben Stellen, aber Sie wissen auf jeden Fall, wie die Felder heißen, und haben genug Informationen, um die Felder entweder mit dem DOM oder mit einem XMLTextReader abzurufen. Mit diesen Objekten werden wir uns etwas weiter unten befassen. Die Abbildung 20.2 zeigt ein einfaches XML-Schema. Wenn Sie Material über XML lesen, werden Sie wahrscheinlich auf den Begriff Document Type Definition (DTD) stoßen. DTDs sind ein früherer Versuch, eine ordentliche Struktur für XML-Dateien zu definieren. Sie gehen auf SGML zurück. DTDs scheinen dieselben Aufgaben wie XML-Schemata durchzuführen; allerdings gibt es einige wesentliche Unterschiede: 왘
XML-Schemata sind mit XML geschrieben, DTDs nicht. DTDs definieren die richtige Struktur einer XML-Datei mit einem ähnlichen, aber eben keinem XML-Format. Dies ist wichtig, da Sie für die Arbeit mit DTDs andere
647
Einführung in XML
Werkzeuge benötigen als für die Arbeit mit dem XML-Code, den Sie dadurch erhalten. Da die XML-Schemata in XML geschrieben sind, können Sie sie auch mit denselben Werkzeugen bearbeiten. 왘
DTDs definieren die Informationstypen nicht. Sie beschreiben die Struktur der Information (d.h. welche Elemente ineinander enthalten sein können), aber nicht, ob ein gegebenes Element eine ganze Zahl, eine Zeichenkette oder ein anderer Informationstyp sein soll. XML-Schemata bieten die Möglichkeit, diese Informationen zu ergänzen.
Wie wir in diesem Buch bereits mehrmals beschrieben haben, besteht einer der Hauptzwecke von Visual Basic .NET darin, die Arbeit mit komplexen Verfahren zu erleichtern. Daher ist es nicht überraschend, dass Visual Basic .NET einen Editor hat, mit dem Sie XML-Schemata betrachten und bearbeiten können. Die Erstellung von Schemata hat auch die angenehme Nebenwirkung, dass sie die Arbeit mit XML-Dokumenten erleichtert. Die Abbildung 20.2 zeigt ein einfaches XML-Schema. Dafür haben wir einen grafischen Editor verwendet, mit dem Sie die verschiedenen Typen und Strukturen erstellen können, aus denen XML-Schemata bestehen. Nachdem Sie ein Schema erstellt haben, können Sie es an eine XML-Datei anhängen und Visual Basic .Net stellt für die im Schema enthaltenen Elemente und Attribute IntelliSense bereit (siehe Abbildung 20.3).
Abbildung 20.2: Ein XML-Schema mit Visual Basic .NET erstellen
648
Mit XML arbeiten
Abbildung 20.3: IntelliSense mit XML
20.2 Mit XML arbeiten Nachdem Sie nun ein allgemeines Verständnis davon haben, was XML ist, wollen wir besprechen, wie Sie es in Ihren Anwendungen einsetzen. XML ist flexibel und kann auf vielerlei Art eingesetzt werden. Die folgende Liste nennt die wichtigsten Verwendungsmöglichkeiten: 쐽
Konfigurationsinformationen: Häufig verwendet man XML dafür, Konfigurationsinformationen zu speichern. Auch Visual Basic .NET benutzt es dafür. Zu den Konfigurationsinformationen gehören z.B. die Sicherheitseinstellungen für Webanwendungen, die Speicherorte erforderlicher Module usw. Wenn Sie Ihre Konfigurationsdateien mit XML formatieren, können Sie diese Werte besser beschreiben. Sie können entweder eine vorhandene Konfigurationsdatei verwenden oder eine neue erstellen. Die Erstellung einer neuen Konfigurationsdatei ist in der Regel die sicherere Alternative, da Sie damit auf keinen Fall vorhandene Konfigurationen beeinflussen.
쐽
Datenübergabe: XML ist ein Format, das sich hervorragend dafür eignet, Informationen zwischen verschiedenen Rechnern zu übergeben. Unabhängig von seinem Betriebssystem oder der verwendeten Programmiersprache kann jeder Rechner, der XML-Code erhält, die darin enthaltenen Informationen lesen. In der morgigen Lektion werden wir ein Beispiel dafür sehen, wenn wir uns mit SOAP (Simple Object Access Protocol) befassen. Allerdings können Sie zum Versenden von XML-Code genauso gut ein anderes Format verwenden.
649
Einführung in XML
쐽
Datenspeicherung: XML ist ein ausgezeichneter Ersatz für kleine Datenbanken. Mit XML können Sie ein einfaches Dateiformat erstellen, das sich selbst beschreibt, Beziehungen zulässt und mit Programmen oder von Hand bearbeitet werden kann.
Heute werden wir uns zwei Arten ansehen, wie Visual Basic .NET die Arbeit mit XMLDateien erleichtert. Wir werden uns vor allem auf das Lesen von XML-Code beschränken, aber auch kurz darauf eingehen, wie Sie XML-Code schreiben können. Visual Basic .NET bietet für die Arbeit mit XML zwei Verfahren: 쐽
Das Document Object Model
쐽
Reader und Writer
Das Document Object Model Das Document Object Model (DOM) ist eine etwas unverständliche Umschreibung für »das Standardverfahren zum Lesen und Schreiben von XML.« Das DOM ist die vom World Wide Web Consortium (www.w3.org) empfohlene Standardprogrammierschnittstelle für die Arbeit mit XML. Dies ist dieselbe Organisation, die auch XML und alle verwandten Technologien standardisiert hat. Auf den meisten Betriebssystemen steht eine DOM-Version zur Verfügung, die Sie mit den meisten Programmiersprachen einschließlich Visual Basic .NET verwenden können. Es ist ein API (Application Programming Interface) für die Abfrage von XML-Dateien. Hinter dem DOM steht das Konzept, dass jede XML-Datei als ein Baum von Knoten beschrieben werden kann. Die Abbildung 20.4 zeigt, wie dies für das folgende XML-Beispiel konzeptuell aussieht. foo 100 bar 1
650
Mit XML arbeiten
customer Element
id Attribute
order Element
date Attribute
items Element
item Element
item Element
itemid Element
quantity Element
itemid Element
quantity Element
foo Text
100 Text
bar Text
1 Text
Abbildung 20.4: Die logische Struktur einer XML-Datei
Es gibt zwei Möglichkeiten, die Knoten in einem DOM zu interpretieren. Einerseits sind alle DOM-Knoten Knoten. Andererseits handelt es sich um besondere Arten von Knoten. Der Knoten order ist z.B. ein Elementknoten, der Knoten date hingegen ein Attributknoten. Jeder Knotentyp unterstützt alle Fähigkeiten eines generischen Knotens sowie spezielle Methoden und Eigenschaften, die nur für diesen Knotentyp gelten. Die Tabelle 20.1 beschreibt die gebräuchlichsten Knotenarten. Knotentyp
Beschreibung
XmlElement
Repräsentiert ein Element in einer XML-Datei.
XmlAttribute
Repräsentiert ein Attribut in einer XML-Datei.
XmlDocument
Repräsentiert das Dokument als Ganzes.
XmlComment
Repräsentiert einen Kommentar in einer XML-Datei. XML-Kommentare beginnen mit
Tabelle 20.1: Knoten, die in XML-Dateien häufig verwendet werden
Wenn die verschiedenen Knotentypen aus einem einzigen gemeinsamen Knoten erben, hat das den Vorteil, dass alle Knoten bestimmte Eigenschaften und Methoden gemeinsam haben. Dies erleichtert es Ihnen, den Umgang mit dem DOM zu erlernen. Die Tabelle 20.2 beschreibt die gebräuchlichsten gemeinsam genutzten Methoden und Eigenschaften.
651
Einführung in XML
Element
Beschreibung
Eigenschaften Attributes
Sammlung aller Attribute dieses Knotens. Damit können Sie durch die Liste der Attribute auf den einzelnen Ebenen der Hierarchie navigieren.
ChildNodes
Sammlung aller Kindknoten dieses Knotens. Dies ist eine der Hauptarten, durch das DOM zu navigieren, bei der man die Kinder mit einer For...NextSchleife durchläuft.
FirstChild
Gibt den ersten Kindknoten des aktuellen Knotens zurück. Dies ist eine der Hauptarten, durch das DOM zu navigieren. Dabei sucht man den ersten Kindknoten und durchläuft dann eine Schleife, solange NextSibling (siehe weiter unten in dieser Tabelle) einen Wert zurückgibt.
InnerText
Der in den einzelnen Knoten enthaltene Text. Dazu gehören alle Kindknoten, die im Knoten enthalten sind.
Name
Der Text in den spitzen Klammern für den aktuellen Knoten.
NextSibling
Gibt den nächsten verfügbaren Knoten zurück, der sich auf derselben Ebene wie dieser Knoten befindet. Wenn in der Abbildung 20.4 z.B. der aktuelle Knoten in der Variablen oNode gespeichert ist und zum item-Knoten zeigt und Sie oNode=oNode.NextSibling aufrufen, dann zeigt oNode auf den nächsten item-Knoten.
NodeType
Gibt den Typ des aktuellen Knotens zurück; z.B. XmlElement, XmlAttribute usw.
Methoden AppendChild
Fügt in den aktuellen Knoten einen neuen Kindknoten ein. Auf diese Art erweitern Sie eine aktuelle Knotenmenge.
Tabelle 20.2: Häufig verwendete Eigenschaften und Methoden aller XML-Knoten
Wenn Sie für die Arbeit mit XML-Dateien das XmlDocument verwenden, gehen Sie ähnlich vor wie bei dem Knotenbaum in der Abbildung 20.4. Am oberen Ende der Hierarchie befindet sich ein einzelner Wurzelknoten: customer. Dieser und alle anderen Knoten im DOM haben eine Sammlung von Kindknoten. Jeder dieser Knoten hat wiederum Kindknoten usw. Die Objekte haben Methoden, die Sie bei der Navigation in dieser Hierarchie unterstützen. Die Tabelle 20.3 beschreibt einige dieser Methoden und Eigenschaften. Außerdem unterstützt das XmlDocument dieselben Eigenschaften und Methoden wie die Klasse XmlNode. Element
Beschreibung
Eigenschaften DocumentElement
Der Wurzelknoten für dieses Dokument.
Tabelle 20.3: Methoden und Eigenschaften von XMLDocument
652
Mit XML arbeiten
Element
Beschreibung
Methoden CreateNode
Damit erstellen Sie neue Knoten, die Sie in das Dokument einfügen möchten. Mit dieser generischen Methode können Sie jeden Typ von XmlNode erstellen.
CreateElement
Gleicht CreateNode, dient aber der Erstellung von Elementen.
CreateAttribute
Gleicht CreateNode, dient aber der Erstellung von Attributen.
Load
Lädt den Inhalt einer XML-Datei in das Dokument.
LoadXml
Lädt den Inhalt einer Zeichenkette, die XML-Code enthält, in das Dokument.
Save
Speichert den Inhalt des XML-Dokuments in einer Datei.
Tabelle 20.3: Methoden und Eigenschaften von XMLDocument (Forts.)
Wenn Sie mit dem XmlDocument und dem DOM XML-Code lesen, verwenden Sie normalerweise ChildNodes oder FirstChild/NextSibling, um den XML-Code durchzugehen und die relevanten Knoten zu finden. Das Schreiben des DOM kann einigermaßen arbeitsintensiv sein, da nur das XmlDocument die verschiedenen Elemente, Attribute usw. erstellen kann. Nachdem Sie einen bestimmten Knoten angelegt haben, können Sie ihn mit der Methode AppendChild der gewünschten Sammlung hinzufügen. Das DOM ist eine Standardverfahren, XML-Dateien zu lesen und zu schreiben. Normalerweise ist es dann nützlich, wenn Sie mit relativ kleinen Dateien arbeiten.
Reader und Writer Das DOM ist zwar eine mächtige Möglichkeit, XML-Code zu lesen und zu schreiben, aber es unterliegt einigen Beschränkungen. Am wichtigsten ist, dass beim Laden eines XML-Dokuments in das DOM immer das gesamte Dokument in den Speicher geladen werden muss. Zu diesem Zeitpunkt erstellt das DOM den ganzen Baum, der das Dokument beschreibt, einschließlich aller Knoten, Knotenlisten usw. Wenn die XML-Datei groß ist, kann dieser Vorgang natürlich eine Menge Speicherplatz in Anspruch nehmen. Außerdem dauert dieser Prozess sehr lange, was besonders dann unangenehm ist, wenn Sie nur ein oder zwei Elemente aus dem Dokument benötigen. Visual Basic .NET enthält noch eine weitere Strategie für die Arbeit mit XML, nämlich die Reader und Writer, genauer gesagt XmlTextReader und XmlTextWriter. Der XmlTextReader bietet einen schnellen Nur-Vorwärts-Zugriff auf einen XML-Block, der XmlTextWriter ist hingegen ein kleines und schnelles Verfahren zum Schreiben von XML. In mancherlei Hinsicht sind der Reader und der Writer zwar nicht so intuitiv wie das DOM, aber sie haben auch zwei Vorteile gegenüber dem DOM:
653
Einführung in XML
쐽
Sie brauchen nicht die ganze Datei zu laden, bevor Sie das Dokument zu verarbeiten beginnen. Die Datei braucht noch nicht einmal zur Verfügung zu stehen. Dies ist z.B. der Fall, wenn Sie eine XML-Datei aus dem Internet herunterladen. Da der XmlTextReader und der XmlTextWriter nicht die gesamte Datei in den Speicher zu laden brauchen, können sie die Speicheranforderunden der Anwendung erheblich reduzieren.
쐽
XmlTextReader und XmlTextWriter sind schnell. Sehr schnell. Dies liegt daran, dass sie
nicht alle die Speicherstrukturen wie z.B. Knotenlisten aufzubauen brauchen, die das DOM verwendet. Die Arbeit mit XmlTextReader und XmlTextWriter hat jedoch auch einen Nachteil. Da Sie nicht das ganze Dokument in den Speicher laden, ist es manchmal schwierig, einzelne Teile des Dokuments zueinander in Beziehung zu setzen. Wenn Sie mehrere Teile des Dokuments verfolgen müssen und dennoch den XmlTextReader und den XmlTextWriter verwenden möchten, müssen Sie selbst Code schreiben, mit dem Sie dies machen können. Mit dem XmlTextReader lesen Sie die Datei ein, indem Sie sie mit der Methode Read durchlaufen und bei jedem Knoten im XML-Dokument anhalten. Dann entscheiden, ob Sie an diesem speziellen Knoten interessiert sind, und falls dies zutrifft, verwenden Sie den Rest der Eigenschaften und Methoden für den aktuellen Knoten. Die Tabelle 20.4 zeigt einige der gebräuchlichsten Methoden und Eigenschaften von XmlTextReader. Element
Beschreibung
Eigenschaften Name
Der Name des aktuellen Knotens.
NodeType
Der Typ des aktuellen Knotens, d.h. Element, Attribut usw.
Methoden Read
Liest den nächsten Knoten im XML-Dokument. Jedes Mal, wenn diese Methode aufgerufen wird, geht sie zum nächsten Knoten weiter.
Tabelle 20.4: Häufig verwendete Eigenschaften und Methoden von XmlTextReader
Mit dem XmlTextWriter erstellen Sie das XML-Dokument, indem Sie die einzelnen Startund End-Tags der Elemente hinzufügen. Es kann eine natürliche Art der Dokumenterstellung sein, auf diese Weise Methoden aufzurufen, die die einzelnen Dokumentteile anlegen. Die Tabelle 20.5 beschreibt die gebräuchlichsten Eigenschaften und Methoden von XmlTextWriter.
654
Mit XML arbeiten
Element
Beschreibung
Eigenschaften Formatting
Damit geben Sie an, dass das XML-Ergebnisdokument formatiert werden soll. Wenn das Ergebnis von Menschen gelesen werden soll, sollte diese Eigenschaft auf Formatting.Indented gesetzt werden, da diese Einstellung ein besser lesbares Dokument erzeugt. Wenn diese Eigenschaft nicht oder auf Formatting.None gesetzt ist, erscheint der gesamte XML-Code auf einer einzigen Zeile.
Methoden Close
Schließt den Writer. Ebenso wie andere Writer sollten Sie auch diesen immer schließen, wenn Sie ihn nicht mehr benötigen.
WriteElementString
Erstellt ein neues Element im Dokument. Dieses neue Element enthält die Start- und End-Tags sowie jeglichen Text im Element. Diese Methode sollten Sie verwenden, wenn Sie ein eigenständiges Element erstellen, das keine Kindknoten hat.
WriteStartElement
Erstellt ein neues Element im Dokument. Diese Methode erzeugt nur das StartTag für das Element. Verwenden Sie diese Methode, wenn Sie Elemente erstellen, die Kindknoten haben.
WriteEndElement
Schreibt das End-Tag für das aktuelle Element.
Tabelle 20.5: Häufig verwendete Eigenschaften und Methoden von XmlTextWriter
XML-Code lesen Sie können zwar die Methoden IndexOf und SubString der Klasse String verwenden, um XML-Code zu lesen und seinen Inhalt zu ermitteln, aber dies ist nicht die effizienteste Art, XML-Code zu lesen. Verwenden Sie statt dessen besser entweder das DOM oder XmlTextReader. Diese beiden Objektfamilien können XML-Dateien leichter interpretieren. Das DOM ist besser geeignet, wenn Sie eine vollständige XML-Datei in den Speicher lesen und damit arbeiten müssen. Wie wir bereits beschrieben haben, können Sie sich mit dem DOM vorwärts und rückwärts durch die Datei bewegen, Abschnitte zueinander in Beziehung setzen und die Datei während des Lesens ändern. Der XmlTextReader ist hingegen dann am besten geeignet, wenn Sie die Datei nur einmal durchlesen müssen. Die Funktionsweise dieser beiden Objektgruppen wird am ehesten deutlich, wenn Sie sie in einem Beispiel bei der Arbeit sehen. Viele Anwendungen müssen vom Benutzer angepasst werden, wie Sie es z.B. bei den Einstellungen im Dialogfenster EXTRAS, OPTIONEN machen. Diese Informationen können leicht in einer XML-Datei gespeichert werden, sodass die Anwender die Einstellungen bequem lesen und verändern können. Wenn Sie den Code für dieses Funktionsmerkmal in eine Komponente setzen, können Sie es leichter wiederverwenden. Settings ist eine Klasse, mit der Sie Konfigurationseinstellungen in einer Datei speichern und später wieder lesen können. Der Inhalt der Datei sieht ähnlich
655
Einführung in XML
aus wie in Abbildung 20.5. Wenn Sie eine derartige Klasse erstellen, können Sie es dem Anwender leichter ermöglichen, die Anwendung an seine Bedürfnisse anzupassen. Das Listing 20.1 zeigt die grundlegenden Eigenschaften und Methoden dieser Klasse. Später werden wir die Möglichkeit ergänzen, um die Einstellungen in dieser Klasse zu lesen und zu schreiben.
Abbildung 20.5: Konfigurationsdatei Listing 20.1: Die Klasse AppSettings 1 Imports System.Xml 2 3 Public Class Settings 4 Private m_sFileName As String 5 Private m_oItems As Hashtable 6 7 Public Sub New() 8 Me.FileName = AppDomain.CurrentDomain.BaseDirectory & _ 9 AppDomain.CurrentDomain.FriendlyName & ".cfg" 10 End Sub 11 12 Public Sub New(ByVal fileName As String) 13 Me.FileName = fileName 14 End Sub 15 16 Public Property FileName() As String 17 Get 18 Return m_sFileName 19 End Get 20 Set(ByVal Value As String) 21 m_sFileName = Value 22 LoadDOM() 23 End Set
656
Mit XML arbeiten
24 25 26 27 28 29 30 31 32 33 34 35 End
End Property Public Property Items() As Hashtable Get Return m_oItems End Get Set(ByVal Value As Hashtable) m_oItems = Value End Set End Property Class
Die Klasse hat zwei Konstruktoren (Zeilen 7 bis 10 und 12 bis 14). Der Grund hierfür ist die größere Flexibilität, die damit erzielt wird. Mit oSettings = New Settings() kann der Programmierer eine neue Instanz dieser Klasse erzeugen. In diesem Fall ist die verwendete Datei der Standard und der Standardname besteht aus dem Anwendungsnamen und der Erweiterung .cfg. Alternativ dazu kann der Programmierer das Settings-Objekt auch erzeugen, indem er der Datei, die die Konfigurationsinformationen enthält, einen Namen wie z.B. oSettings = New Settings("SomeFile.xml")gibt. Bei beiden Verfahren wird die Eigenschaft FileName gesetzt. Nachdem dies geschehen ist (Zeilen 20 bis 23), wird die interne Variable m_sFileName gesetzt und die Methode LoadDOM (die wir gleich schreiben werden) aufgerufen. Diese Methode lädt die Einstellungen, die in der angegebenen Datei definiert wurden, in die interne HashTable. Eine HashTable ist eine der in System.Collections definierten Sammlungen. Ähnlich der Eigenschaft Tables von DataSet speichert sie eine Sammlung von Elementen, die nach dem Namen gespeichert werden. Dadurch können Sie mit Code, der oSettings.Items("SomeSetting") gleicht, Elemente aus der Eigenschaft Items abrufen. Bevor Sie diese Klasse verwenden können, sollten Sie ihr natürlich die Fähigkeit hinzufügen, die Einstellungen zu laden. Das Listing 20.2 zeigt, wie dies mit dem XmlTextReader funktioniert, und das Listing 20.3 zeigt denselben Vorgang unter Verwendung des DOM. Listing 20.2: Die Einstellungen mit dem XmlTextReader laden 36 37 38 39 40 41 42
Public Sub LoadReader() 'Lädt den Inhalt der Einstellungen-Datei in ' die HashTable m_oItems = New Hashtable() Dim oReader As XmlTextReader Dim sLastElement As String
657
Einführung in XML
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
Try oReader = New XmlTextReader(FileName) While (oReader.Read) Select Case oReader.NodeType Case XmlNodeType.Element If oReader.Name "appSettings" Then m_oItems.Add(oReader.Name, Nothing) sLastElement = oReader.Name End If Case XmlNodeType.Text m_oItems.Item(sLastElement) = oReader.Value Case Else End Select End While Catch ex As Exception Finally oReader.Close() oReader = Nothing End Try End Sub
Die Methode LoadReader initialisiert zuerst die HashTable (Zeile 39) und deklariert das XmlTextReader-Objekt sowie eine Zeichenkettenvariable, die wir während der Verarbeitung verwenden werden. Da diese Verarbeitung auf einer Datei durchgeführt wird, die möglicherweise nicht existiert, schützen wir das Öffnen und Schließen der Datei, indem wir es in einen Try...End Try-Block hüllen (Zeilen 43 bis 61). Wie zuvor gewährleistet die Finally-Klausel dieses Blocks (die in Zeile 58 beginnt), dass der Reader selbst dann geschlossen und gelöscht wird, wenn ein Fehler auftritt. Der größte Bestandteil dieser Methode ist die While...End While-Schleife in den Zeilen 45 bis 56. Diese liest alle Knoten aus der Datei. Beim Lesen der einzelnen Knoten ermittelt der Code zuerst den Typ des Knotens (Zeile 46). Bei diesem Code interessieren uns nur Element- und Textknoten, da dies die einzigen Knotentypen sind, die in der Datei vorkommen. Wenn der aktuelle Knoten ein Element, aber nicht der Knoten appSettings (der Wurzelknoten) ist, dann fügt der Code ein neues Element in die HashTable ein (Zeile 49) und speichert den Namen dieses neu hinzugefügten Elements in die Zeichenkettenvariable. Der nächste Read-Vorgang sollte dann den Textknoten zurückgeben, der mit diesem Element verbunden ist. Mit diesem Textknoten fügen wir dem zuletzt in die HashTable eingefügten Element einen Wert hinzu. Am Ende der Routine sollte die HashTable alle in der Datei gespeicherten Einstellungen enthalten.
658
Mit XML arbeiten
Listing 20.3: Die Einstellungen mit dem DOM laden 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
Public Sub LoadDOM() m_oItems = New Hashtable() Dim oDoc As New XmlDocument() Dim oNode As XmlElement Try oDoc.Load(FileName) oNode = oDoc.DocumentElement.FirstChild Do While Not IsNothing(oNode) m_oItems.Add(oNode.Name, oNode.InnerText) oNode = oNode.NextSibling Loop Catch ex As Exception Finally oDoc = Nothing End Try End Sub
Dieser Code beginnt ähnlich wie der vorhergehende: Er initialisiert die HashTable. Hier erzeugt der Code allerdings ein XmlDocument und ein XmlElement, um die Knoten zu speichern, wenn die Datei gelesen wird. Für den Fall, dass beim Lesen der Datei ein Fehler auftritt, verwenden wir auch hier wieder einen Try...End Try-Block. Zuerst wird die Datei geladen (Zeile 69). Denken Sie daran, dass in dieser Phase die gesamte Datei in den Speicher geladen und das DOM erstellt wird. In Zeile 71 werden zwei Navigationsvorgänge ausgeführt. Zuerst wird DocumentElement (der Knoten appSettings) und dann das FirstChild dieses Knotens ausgewählt. Nachdem dieser Code ausgeführt wurde, sollte oNode zum ersten Knoten innerhalb des Knotens appSettings zeigen. Der Code durchläuft dann die Geschwister dieses Knotens mit einer Schleife, wobei er Elemente in die HashTable einfügt. Wenn es keine Geschwister gibt, gibt die Methode NextSibling nichts zurück und die Schleife wird beendet. Wie zuvor ist XmlDocument auch hier auf Nothing gesetzt (Zeile 79), wenn es abgeschlossen ist. Diese Klasse können Sie nun anderen Projekten hinzufügen oder in eine Ihrer eigenen Klassenbibliotheken einfügen und zu einer DLL kompilieren. Wenn Sie fertig sind, können Sie mit dieser Klasse Ihre Anwendung konfigurieren, wie wir es in Listing 20.4 zeigen.
659
Einführung in XML
Listing 20.4: Die Klasse Settings verwenden 1: 2: 3: 4: 5: 6: 7: 8: 9: 10 11 12 13
Private Sub frmTest_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles MyBase.Load ''Kommentare: blah oSettings = New Settings() With Me .Height = oSettings.Items("height") .Width = oSettings.Items("width") .Left = oSettings.Items("left") .Top = oSettings.Items("top") .Text = oSettings.Items("caption") End With End Sub
Um die Einstellungen zu lesen und sie der aktuellen Anwendung zuzuweisen, initialisieren Sie zuerst das Settings-Objekt (Zeile 5). Denken Sie daran, dass es die Einstellungen an dieser Stelle lädt. Dann lesen Sie alle Einstellungen und weisen sie nach Bedarf der gewünschten Formulareigenschaft, einem Steuerelement des Formular oder Ähnlichem zu (Zeile 6 bis 12).
XML-Code schreiben Ebenso wie Sie beim Lesen von XML-Code Zeichenketten verwenden können, können Sie XML-Code auch mit Zeichenketten schreiben. Sie können XML-Code also schreiben, indem Sie einer Zeichenkettenvariable die passenden Start- und End-Tags von Elementen hinzufügen. Allerdings ist es wahrscheinlicher, dass Sie eine korrekte XML-Datei schreiben, wenn Sie die Klassen DOM oder XmlTextWriter verwenden. Das Listing 20.5 zeigt den Code, der erforderlich ist, damit Sie die Konfigurationsinformationen im Settings-Objekt mit XmlTextWriter speichern können. Das Listing 20.6 zeigt denselben Code für die XmlDocument-Objekte. Fügen Sie den Code dem Settings-Objekt hinzu, das wir weiter oben erstellt haben. Listing 20.5: Konfigurationseinstellungen mit dem XmlTextWriter schreiben 82 83 84 85 86 87
660
Public Sub SaveWriter() Dim oWriter As XmlTextWriter Dim oItem As DictionaryEntry oWriter = New XmlTextWriter(m_sFileName, Nothing) oWriter.Formatting = Formatting.Indented
Mit XML arbeiten
88 89 90 91 92 93 94 95 96 97 98
oWriter.WriteStartElement("appSettings") For Each oItem In m_oItems oWriter.WriteElementString(oItem.Key, oItem.Value) Next oWriter.WriteEndElement() oWriter.Flush() oWriter.Close() End Sub
Die Methode SaveWriter instanziiert zuerst die benötigten Variablen. Eine Variable, die Ihnen vielleicht eigenartig vorkommt, ist das in Zeile 84 deklarierte oItem-Objekt. Die Elemente in einer HashTable sind DictionaryEntryElemente. Daher benötigen wir eines dieser Objekte, damit wir die HashTable mithilfe einer For Each...Next-Schleife verarbeiten können. Der Writer-Code öffnet zuerst die Datei (Zeile 86) und gibt an, dass die eingerückte Formatierung verwendet werden soll. Denken Sie daran, dass dies dem menschlichen Leser dient und vom Code nicht verlangt wird. Anschließend wird das Startelement des Wurzelknotens geschrieben (Zeile 88) und in der For Each...Next-Schleife werden alle Elemente in die XML-Datei geschrieben (Zeilen 90 bis 92). Die Methode WriteElementString erstellt auf der Grundlage des Elementnamens in der HashTable die Start- und End-Tags. Außerdem erzeugt es basierend auf dem Wert des Elements in der HashTable den Text zwischen den Tags. Schließlich wird das End-Tag für den Wurzelknoten erzeugt (Zeile 93), die Datei wird geschlossen und die Informationen werden in die Datei geschrieben. Listing 20.6: Die Konfigurationseinstellungen mit dem DOM schreiben 99 100 101 102 103 104 105 106 107 108 109 110
Public Sub SaveDOM() Dim oDoc As New XmlDocument() Dim oRoot As XmlElement Dim oItem As DictionaryEntry Dim oNode As XmlElement oRoot = oDoc.CreateElement("appSettings") oDoc.AppendChild(oRoot) For Each oItem In m_oItems oNode = oDoc.CreateElement(oItem.Key) oNode.InnerText = oItem.Value
661
Einführung in XML
111 112 113 114 115
oRoot.AppendChild(oNode) Next oDoc.Save(m_sFileName) End Sub
Der Code zum Schreiben mit dem DOM erstellt zuerst den Wurzelknoten appSettings und hängt ihn an das Dokument an. Alle anderen Knoten werden an den Wurzelknoten angehängt. Dies geschieht ebenso wie bei der Methode SaveWriter in einer For Each...Next-Schleife. Jeder Knoten wird erstellt (Zeile 109), der Text wird zugewiesen (110) und zum Knoten appSettings hinzugefügt. Nachdem Sie den Code für das Sichern der Einstellungen hinzugefügt haben, können Sie das Programm, das ihn verwendet, so verändern, dass die Einstellungen für die nächste Ausführung der Anwendung gespeichert werden. Dies geschieht häufig beim Ereignis Closing des Formulars. Das Listing 20.7 zeigt den entsprechenden Code. Listing 20.7: Code für das Speichern der Einstellungen 14 15 16 17 18 19 20 21 22 23 24 25
Private Sub frmTest_Closing(ByVal sender As Object, _ ByVal e As System.ComponentModel.CancelEventArgs) _ Handles MyBase.Closing With Me oSettings.Items("height") = .Height oSettings.Items("width") = .Width oSettings.Items("left") = .Left oSettings.Items("top") = .Top oSettings.Items("caption") = .Text End With oSettings.SaveWriter() End Sub
Bevor Sie die Einstellungen in die Datei speichern können, sollten Sie sie aus den tatsächlichen Werten kopieren. Normalerweise machen Sie dies, wenn Sie das Dialogfenster EXTRAS, OPTIONEN schließen oder wenn die Anwendung endet. Wenn alle Konfigurationseinstellungen kopiert sind, können Sie die Einstellungen mit der Methode SaveWriter oder der Methode SaveDOM in die Datei speichern. Versuchen Sie das Projekt auszuführen, nachdem Sie diesen Code hinzugefügt haben (siehe Abbildung 20.6). Ändern Sie die Größe des Formulars und verschieben Sie es an eine andere Bildschirmposition. Schließen Sie das Formular, beenden Sie die Anwendung und führen Sie es erneut aus. Sie sollten das Formular nun an der Bildschirmposition und in der Größe sehen, auf die Sie es gesetzt haben. Häufig wissen die Anwender es zu schätzen, wenn sich Ihre Anwendung auf diese Art an die Formularposition erinnert.
662
Zusammenfassung
Abbildung 20.6: Die Klasse Settings prüfen
20.3 Zusammenfassung XML ist ein vielschichtiges Verfahren – es ist zwar einfach, hat aber tief greifende Auswirkungen. Sie können es mit einem Programm oder in einem einfachen Texteditor erstellen. Sie können ein Programm schreiben, das XML verarbeitet, dennoch ist XML aber auch für Menschen lesbar. Wie auch immer Sie XML erstellen oder verarbeiten, es ist auf jeden Fall eine höchst effektive Art, Informationen zu formatieren. Ich glaube, dass XML nicht nur hochgejubelt wird, sondern tatsächlich eine der wichtigsten Neuerungen im Computerbereich ist, wobei es nur von ASCII übertroffen wird. Daher sind Sie es sich selbst schuldig, zumindest ein wenig darüber zu lernen, was XML für Sie und Ihre Anwendungen tun kann. Morgen werden wir unsere Besprechung von Visual Basic .NET mit einem Blick auf eine neue Art der Anwendungserstellung abschließen: die Webdienste. Dabei wird Ihnen das Grundverständnis von XML zu Gute kommen, da auch die Webdienste XML in großem Umfang nutzen.
20.4 Fragen und Antworten F
Ich habe von einer anderen Art gehört, XML zu verarbeiten. Was ist SAX? A
SAX (Simple API for XML) ist eine weitere Möglichkeit, XML zu lesen. Sie wurde von Java-Entwicklern und neueren Versionen von MSXML (Microsofts XMLParster für COM) populär gemacht. SAX wurde von David Megginson und der Mailingliste XML-DEV als komfortable Möglichkeit entwickelt, XML zu lesen. Es hat insofern Ähnlichkeit mit dem XmlTextReader, als es nicht das ganze Dokument in den Speicher lädt. Es funktioniert jedoch in vielerlei Hinsicht völlig anders als der XmlTextReader. Der XmlTexteader ist ein »Zug-Modell«: Er zieht Teile aus dem XML-Code heraus. SAX ist hingegen ein »Ereignis-Modell«. Wenn ein SAX-
663
Einführung in XML
Prozessor eine XML-Datei liest, löst er Ereignisse wie z.B. BeginElement, EndElement, BeginDocument aus. Um den XML-Code verarbeiten zu können, müssen Sie die benötigten Ereignisbehandler schreiben. F
Ich habe von einigen Akronymen gehört, die mit XML zu tun haben, z.B. XSLT, XPath, SOAP. Wo finde ich Informationen über diese Dinge? A
F
Es gibt viele Standards und Vorschläge für Standards, die mit XML zu tun haben; viel zu viele, um sie hier alle zu behandeln. Wenn Sie mehr über diese anderen Standards erfahren möchten, sollten Sie sich ein gutes XML-Buch besorgen (davon gibt es viele) oder eine gute Website der XML-Gemeinde suchen, die die entsprechenden Informationen bietet (http://msdn.microsoft.com/xml ist ein guter Ausgangspunkt).
Ich habe gehört, dass man bei einigen Sprachen wie z.B. Java und C# einer Quelldatei Kommentare hinzufügen kann, während man sie schreibt, und diese Kommentare in eine Dokumentation umgewandelt werden. Kann ich das auch in Visual Basic .NET machen? A
Leider unterstützt Visual Basic .NET dies derzeit nicht.
20.5 Workshop Die Antworten und Lösungen finden Sie im Anhang A.
Quiz 1. Wie können Sie mit dem XmlTextWriter schnell ein neues Element einschließlich der Start- und End-Tags in eine XML-Datei einfügen? 2. Wann verwenden Sie anstelle der Klasse XmlTextReader das DOM, um eine XMLDatei zu lesen? 3. Was sind Metadaten?
Übung Der Code in der heutigen Lektion, der die Klasse Settings prüft, ist derzeit so geschrieben, dass er die Einstellungen mit der Methode LoadDOM lädt und mit der Methode SaveWriter speichert. Schreiben Sie ein neues Testformular, das mehrere Einstellungen speichert und bei dem der Anwender wählen kann, welche der beiden Methoden er zum Speichern und Abrufen verwenden möchte.
664
Mit Visual Basic .NET Webdienste erstellen
Mit Visual Basic .NET Webdienste erstellen
Sofern Sie in den letzten Jahren nicht in einem Schneckenhaus gelebt haben, haben Sie schon einmal vom Internet gehört – dieser erstaunlichen Angelegenheit, dem weltweiten Netzwerk. Für Entwickler ist das Internet auf jeden Fall ein wertvolles Werkzeug. Es bietet ein im Grunde kostenloses Netzwerk, über das Sie Informationen aus Ihren Programmen an andere übergeben können, und zwar unabhängig davon, wo sich diese befinden. Für Visual Basic .NET-Enwickler ist es also offenbar sehr wichtig, Programme zu erstellen, die das Internet verwenden.
Themen heute 쐽
Was ist ein Webdienst?
쐽
SOAP (und was es mit den Webdiensten zu tun hat)
쐽
Wie man in Visual Basic .NET einen Webdienst erstellt
쐽
Wie man in Visual Basic .NET einen Webdienst-Client erstellt
21.1 Was ist ein Webdienst? Jeder, der schon einmal das World Wide Web benutzt hat, weiß, dass dort sehr viele Dienste zur Verfügung stehen. Man verwendet sie fast überall: Suchmaschinendienste, Warenkorbdienste, Dienste, die Informationen abrufen usw. Warum nehmen wir also in ein Buch über Visual Basic eine Lektion über die Erstellung von Webdiensten auf? Die heutige Lektion erklärt den Unterschied zwischen einem Dienst im Internet und einem Webdienst. Um eine klare Unterscheidung zu ermöglichen, werden wir die regulären Dienste immer als Dienste im Internet und die in Visual Basic .NET erstellten Dienste als Webdienste bezeichnen. Mit einem »normalen« Dienst im Internet interagieren Sie, indem Sie zu einer bestimmten Internetseite gehen, dort ein Formular ausfüllen und diese Informationen an eine andere Webseite senden. Das ist ein ganz einfacher Vorgang und die leichte Handhabung hat definitiv zu der Größe und Beliebtheit des Internets beigetragen. Allerdings gibt es keine einfache Möglichkeit, die Informationen, die Sie von einem solchen Dienst im Internet erhalten, in einem Programm zu verwenden. Sie können z.B. mit einem Dienst im Internet den aktuellen Wert einer Aktie nachschlagen. Normalerweise geben Sie das Symbol der Aktie ein, klicken eine Schaltfläche an und erhalten eine Webseite, die den aktuellen Aktienkurs anzeigt. Häufig erhalten Sie zusätzlich noch andere Informationen wie z.B. die Eröffnungs- und Schlussnotierung, die Abweichung von der letzten Schlussno-
666
Was ist ein Webdienst?
tierung und vielleicht ein Diagramm der aktuellen Börsenkurse. Das ist alles sehr praktisch, wenn Sie im Internet surfen, aber wenn Sie einfach nur den aktuellen Aktienkurs holen und in einem anderen Programm verwenden möchten, dann stehen Ihnen die beiden folgenden Möglichkeiten zur Verfügung: 쐽
Lassen Sie den Anwender nach der Information suchen, das Formular ausfüllen, auf das Ergebnis warten und dieses dann in Ihr Programm eingeben. Es ist offensichtlich, welches Problem bei diesem Verfahren besteht: Die meisten Anwender wollen dies sicher nicht oft machen. Nehmen wir an, Sie wenden diese Strategie bei einem Wert an, der sich häufig ändert, also z.B. bei Aktienkursen. Und nun stellen Sie sich vor, Sie würden vom Anwender verlangen, dass er Informationen in eine Webseite eingibt, eine Schaltfläche anklickt und diesen einen Wert mehr als ein- oder zweimal pro Stunde in Ihr Programm eingibt.
쐽
Lassen Sie Ihr Programm die Webseite (im Hintergrund) durchsuchen, die Webseite parsen (lesen) und die gewünschten Informationen extrahieren. Wenn die Autoren der Webseite aber deren Layout ändern, dann müssen Sie Ihr Programm so ändern, dass es das neue Layout parst. Und wenn Sie für Ihre Informationen eine andere Website verwenden möchten, dann müssen Sie wahrscheinlich auch den Code neu schreiben, der die Webseite parst.
Was ist nun also mit den Webdiensten? Die Webdienste sind Programme, die einen Dienst im Internet bereitstellen, aber so entworfen sind, dass nicht Menschen mit ihnen arbeiten, sondern andere Programme mit ihnen kommunizieren. Nun können Sie also einen Webdienst erstellen, der es anderen Programmierern ermöglicht, Aktienkurse abzurufen, Suchvorgänge durchzuführen usw. Eine angenehme Eigenschaft der Webdienste besteht darin, dass man sie auf jedem Betriebssystem und in jeder beliebigen Programmiersprache schreiben kann. Sie verwenden ein Nachrichtenformat, das leicht zu lesen und zu schreiben ist. Mit Visual Basic .NET ist das Erstellen von Webdiensten nicht schwerer als das Erstellen jeder anderen Anwendung. Visual Basic .NET selbst verbirgt die Einzelheiten des Schreibens und Lesens einer Nachricht. Auch die Verwendung der Webdienste ist mit Visual Basic .NET genauso einfach wie das Aufrufen jedes anderen Objekts, obwohl sie sich vielleicht an einer ganz anderen Stelle im Internet befinden und sogar in einer anderen Programmiersprache geschrieben sind oder unter einem anderen Betriebssystem ausgeführt werden. Mit den Webdiensten können nicht nur zwei Programme über das Internet miteinander kommunizieren, sondern es ist auch möglich, dass ein Programm Informationen aus mehreren Webdiensten in einer einzigen Anwendung kombiniert und damit den Wert der Daten erhöht. Eine Börsenanwendung könnte z.B. nicht nur die (eventuell aus mehreren Börsen-Websites extrahierten) aktuellen Notierungen verschiedener Aktien anzeigen, sondern auch die neuesten Empfehlungen von Analysten nennen, die auf den angezeigten Aktien, ähnlichen Unternehmenstypen, Nachrichten über das Unternehmen usw. basieren, wobei alle diese Informationen von verschiedenen Webdiensten abgerufen werden.
667
Mit Visual Basic .NET Webdienste erstellen
Für die Kommunikation untereinander und mit Clients verwenden die Webdienste Standardinternetprotokolle. Sie können das HTTP-Protokoll verwenden, das auch die Browser benutzen. In diesem Fall verwenden Sie ebenso wie ein Webbrowser die Befehle GET und POST. Alternativ kann ein Webbrowser für die Kommunikation mit einem Client oder einem anderen Dienst auch das SOAP verwenden. SOAP ermöglicht eine tief greifendere Kommunikation, da bei SOAP im Gegensatz zu HTTP auch Objekte zwischen Anwendungen übergeben werden können.
21.2 Das Simple Object Access Protocol SOAP ist ein relativ neues Protokoll. Es definiert eine Möglichkeit, eine XML-Nachricht so zu definieren, dass sie als Nachricht verwendet werden kann. Die Webdienste verwenden SOAP normalerweise dafür, die Anfrage und die Antwort zwischen zwei Programmen zu übermitteln. SOAP verwendet normale Internetprotokolle wie z.B. HTTP, das von Webbrowsern benutzte Protokoll, oder SMTP, das die Internet-Mail ermöglicht. Allerdings ist mit SOAP eine reichere Kommunikation möglich als mit einfachen Nachrichten. SOAP wurde ursprünglich von mehreren Unternehmen, zu denen auch Microsoft gehörte, entwickelt, um eine reichhaltige Objekt-zu-Objekt-Kommunikation zwischen Programmen zu ermöglichen. Mit SOAP wollte man ein Verfahren für die Arbeit mit Objekten im Internet definieren, das an Stelle von Binärformaten und proprietären Protokollen nur Standardprotokolle und -formate verwendet. Nachdem SOAP eine Zeit lang im Umlauf war, definierten die Entwickler seine Fähigkeiten neu. Nun ist SOAP ein allgemeines Nachrichtenformat, das von jedem Protokoll verwendet werden kann und über die Erstellung von Webdiensten hinaus noch vielen anderen Bedürfnissen gerecht wird. Allerdings wollen wir uns in der heutigen Lektion SOAP nur im Kontext der Erstellung und Verwendung von Webdiensten ansehen. Weitere Informationen über SOAP finden Sie in der Spezifikation, die im Internet unter http://www.w3.org/TR/SOAP zur Verfügung steht.
Das Protokoll Die Einzelheiten des Protokolls SOAP sind zwar im .NET-Framework verborgen, aber vielleicht möchten Sie dennoch herausfinden, was im Hintergrund geschieht. Sehen wir uns also an, wie eine SOAP-Nachricht aussieht und wie man mit einer SOAP-Nachricht ein Objekt über das Netzwerk senden kann. Eine SOAP-Nachricht ist eine XML-Nachricht, die ein bestimmtes Format, ein so genanntes Schema verwendet. Das Schema definiert die Gesamtstruktur der Nachricht (weitere Informationen zu Schemata finden Sie am Tag 20, Einführung in XML). Dieses Format macht es möglich, in die Nachricht ein großes Informationsvolumen einzufügen, unter
668
Das Simple Object Access Protocol
anderem auch Objekte und zusätzliche Informationen darüber, wie beide Seiten die Nachricht interpretieren sollen. Die Nachricht kann z.B. Informationen über die Sicherheit enthalten und den Anwender identifizieren, der die Anfrage absetzt. Das Listing 21.1 zeigt eine einfache SOAP-Anfrage.
Listing 21.1: Eine einfache SOAP-Anfrage 1 2 3 4 5 6 7 8 9 10 11 12
int int
Diese Nachricht besteht aus drei Hauptabschnitten. Der erste (Zeilen 2 bis 5) ist das Tag Envelope. Dies ist das Wurzelelement für die SOAP-Nachricht. Die Namensräume definieren die Struktur der Nachricht, den Codierungstyp und die Verwendung von XML-Schemata in der Nachricht. Der zweite Abschnitt besteht nur aus der Zeile 6, die die Deklaration des Nachrichtenrumpfes (Body) enthält, der wiederum die eigentliche Anfrage enthält. Diese Anfrage ist der dritte Abschnitt und wird in den Zeilen 7 bis 10 definiert. In diesem Fall handelt es sich um Add-Anfrage mit den beiden Werten x und y.
Die Web Service Description Language (WSDL) Nachdem Sie eine Kommunikationsmöglichkeit gefunden haben, benötigen Sie noch eine Möglichkeit, die Nachricht zu beschreiben. Im sprachlichen Bereich bezeichnet man dies als Grammatik. Die Grammatik definiert, wie eine Nachricht aufgebaut wird und wie wichtige Teile der Nachricht identifiziert werden.
669
Mit Visual Basic .NET Webdienste erstellen
SOAP definiert zwar das Nachrichtenformat, aber Sie benötigen auch eine Möglichkeit, die Grammatik von SOAP-Nachrichten zu beschreiben. Dadurch können Clients und Server dann zulässige SOAP-Nachrichten automatisch generieren und erkennen. Die Web Service Description Language (WSDL) ist eine Möglichkeit, Webdienste zu beschreiben. Sie definiert die Nachrichten (Methoden), die von einem Webdienst unterstützt werden, sowie die Komponenten (Parameter) der einzelnen Nachricht. In vielerlei Hinsicht ist die WSDL der »Vertrag«, der Client und Server bindet. Die WSDL baut auf einem anderen Standard auf, den XML-Schemata, die die Struktur einer XML-Nachricht definieren (siehe Tag 20). Mithilfe von Schemata definiert die WSDL die beiden Nachrichten, die einen typischen Webdienst-Dialog bilden: Anfrage und Antwort. Die Schemata für die Anfrage und die Antwort sind nur ein Teil der WSDLDatei. Ein weiterer Teil der Datei beschreibt die Tatsache, dass die beiden Strukturen zusammengehören. Und schließlich identifiziert die WSDL-Datei noch das Ziel, an das der Client die SOAP-Nachricht schicken soll, damit sie verarbeitet werden kann. Das Listing 21.2 zeigt ein Beispiel für einen WSDL-Vertrag. Wenn Sie mit frühen Betaversionen von Visual Basic .NET gearbeitet haben, z.B. mit der Beta 1 oder der auf der Professional Developers' Conference (PDC) veröffentlichten Version, dann haben Sie wahrscheinlich schon von der SDL (Service Description Language) gehört. Die SDL ist ein älteres Format, das von der WSDL abgelöst wurde. Listing 21.2: Ein WSDL-Vertrag 1: 2: 11: 12: 15: 16: 17: 18:
670
Das Simple Object Access Protocol
20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65:
671
Mit Visual Basic .NET Webdienste erstellen
66: 67: 68: 70: 71: 72:
Discovery Sobald Sie eine Nachricht haben und diese Nachricht beschreiben können, benötigen Sie als Nächstes eine Möglichkeit, den Webdienst zu finden. In gewisser Weise hat dies Ähnlichkeit damit, auf einer Visitenkarte eine Telefon- oder Faxnummer zu nennen. Wenn Sie diese Nummern angeben, geben Sie damit bekannt, dass Sie über den entsprechenden Dienst verfügen, um mithilfe dieser Geräte zu kommunizieren. Bei SOAP und den Webdiensten heißt die Entsprechung zu einer solchen Visitenkarte Discovery, kurz DISCO. DISCO ist ein XML-Format (was auch sonst?), das die Dienste beschreibt, die von einem Server unterstützt werden. Andere Rechner können mithilfe dieser DISCO-Datei herausfinden, über welche Fähigkeiten ein bestimmter Rechner verfügt. Die DISCO-Datei befindet sich normalerweise im Wurzelverzeichnis einer Website, sodass die Clients sie leicht finden können. Visual Basic .NET unterstützt zwei Arten von DISCO-Dateien. Die erste ist eine manuell editierte Datei, wie Sie im Listing 21.3 gezeigt wird. Diese Datei führt alle unterstützten Dienste auf. Diese Dateien können Sie leicht selbst erstellen, indem Sie einem Projekt eine neue DISCO-Datei hinzufügen und die gewünschten Dienste wie in Listing gezeigt manuell einfügen.
Listing 21.3: Eine einfache DISCO-Datei 1 2 3 4 5 6 7 8 9
672
12
Jede DISCO-Datei besteht aus mehreren Verträgen. Jeder Vertrag bezeichnet eine WSDL-Datei, die einen Webdienst beschreibt. Jeder genannte Webdienst hat drei wichtige Attribute: 왘
ref: Der Speicherort der WSDL-Datei.
왘
docRef: Der Speicherort des Webdienstes.
왘
xmlns: Der Namensraum, der das Format der WSDL-Datei definiert. Damit kann eine einzige DISCO-Datei mit verschiedenen Dienstbeschreibungssprachen verwendet werden.
Die zweite Art von DISCO-Datei, die Visual Basic .NET unterstützt, ist eine dynamische Discovery-Datei. Diese Art von Discovery-Datei wird erstellt, wenn Sie ein Webdienst-Projekt starten. Damit man sie von einer manuell erstellen DISCO-Datei unterscheiden kann, hat sie die Erweiterung .vsdisco. Diese Datei durchsucht alle Verzeichnisse eines Webservers, um alle bereitgestellten Webdienste zu finden. Anschließend gibt sie diese Liste (in einfachem DISCOFormat) an den Client zurück. Diese Art von DISCO-Datei ist komfortabel, da Sie nichts manuell zu ändern brauchen, um einen neuen Webdienst hinzuzufügen. Vielmehr findet die dynamische DISCO-Datei den neuen Webdienst automatisch. Wenn Sie genauer kontrollieren möchten, welche Webdienste Sie bereitstellen, sollten Sie diese Art von DISCO-Datei nicht verwenden. Das Listing 21.4 zeigt ein Beispiel für eine dynamische DISCO-Datei.
Listing 21.4: Eine dynamische DISCO-Datei 1 2 3 4 5 6 7 8 9
673
Mit Visual Basic .NET Webdienste erstellen
Diese Form der DISCO-Datei ist wesentlich einfacher zu warten als die einfache Discovery-Datei. Sie identifiziert nicht alle unterstützten Webdienste (oder eigentlich die WSDL-Dateien), sondern führt alle Speicherorte auf, an denen nicht nach WSDL-Dateien gesucht werden soll. Jede Zeile bezeichnet ein Verzeichnis, in dem nicht gesucht werden soll. Die dynamische DISCO-Datei ist etwas langsamer als die manuelle, da sie verlangt, dass die Discovery-Engine alle Unterverzeichnisse außer den in der -Anweisung genannten durchsucht. Nachdem Sie nun alles Nötige wissen (oder zumindest die Protokolle kennen, die Sie verwenden werden), können Sie mit Visual Basic .NET einen Webdienst erstellen.
21.3 Einen einfachen Webdienst erstellen Eine Webdienst-Anwendung erstellen Sie, indem Sie einer vorhandenen Lösung ein Webdienst-Projekt hinzufügen oder eine neue Lösung anlegen, die ein Webdienst-Projekt enthält (siehe Abbildung 21.1).
Abbildung 21.1: Ein neues Webdienst-Projekt erstellen
Damit Sie sehen, was Sie machen müssen, um einen Webdienst zu erstellen und zu verwenden, werden wir einen einfachen Webdienst namens MathService erstellen. Dies ist ein einfaches Beispiel für einen Webdienst, das die Vorgehensweise des Dienstes dennoch gut zeigt. Alle zusätzlichen Funktionsmerkmale unterscheiden sich nicht wesentlich von denen, die wir hier zeigen. Ein Webdienst wird immer auf dieselbe Art erstellt.
674
Einen einfachen Webdienst erstellen
Das Projekt einrichten Wählen Sie in der Entwicklungsumgebung NEUES PROJEKT, ASP.NET WEBANWENDUNG. Nennen Sie den neuen Webdienst MathService. Sobald Sie das Projekt erstellt haben, enthält es eine Reihe von Dateien: 쐽
Web.Config: Dies ist die Datei, die der ASP.NET-Prozessor für seine Konfiguration verwendet. Mit dieser Datei können Sie die Einstellungen für das virtuelle Verzeichnis ändern, das Sie für diesen Webdienst verwenden. Sie können z.B. Tracing aktivieren, was eine bessere Nutzungsüberwachung ermöglicht. Im Beispiel werden wird diese Datei nicht ändern.
쐽
Global.asax. Dies ist die Datei, mit der der ASP.NET-Prozessor Ereignisbehandler
speichert. Mit dieser Datei können Sie Code schreiben, der dann auftritt, wenn in einer Webanwendung wichtige Ereignisse eintreten. Im Beispiel werden wird diese Datei nicht ändern. 쐽
MathService.vsdisco: Dies ist die Discovery-Datei für den Webdienst. Sie ermöglicht es dem Client, die Webdienste zu finden, die in diesem virtuellen Verzeichnis bereitgestellt werden. Die in diesem Projekt erstellte DISCO-Datei ist eine dynamische Discovery-Datei, sodass Sie keine Änderungen vorzunehmen brauchen, um neue Dienste bereitzustellen.
쐽
Service1.asmx: Diese Datei benötigen Sie, um die Funktionsmerkmale des Webdienstes einzurichten.
Das Listing 21.5 zeigt den Code in der Datei Service1.asmx.
Listing 21.5: Eine einfache Dienstdatei 1 2 3 4 5 6 7 8 9 10 11
Imports System.Web.Services Public Class Service1 Inherits System.Web.Services.WebService #Region " Web Services Designer Generated Code " Public Sub New() MyBase.New() 'Dieser Auruf wird vom Webdienst-Designer verlangt.
675
Mit Visual Basic .NET Webdienste erstellen
12 13 14
InitializeComponent() 'Fügen Sie eigenen Initialisierungscode nach dem Aufruf von InitializeComponent() ein.
15 16 End Sub 17 18 'Wird vom Webdienst-Designer verlangt. 19 Private components As System.ComponentModel.Container 20 21 'HINWEIS: Die folgende Prozedur wird vom Webdienst-Designer verlangt. 22 'Sie kann mit dem Webdienst-Designer geändert werden. 23 'Ändern Sie sie nicht mit dem Code-Editor. 24 Private Sub InitializeComponent() 25 components = New System.ComponentModel.Container() 26 End Sub 27 28 Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) 29 'CODEGEN: Die folgende Prozedur wird vom Webdienst-Designer verlangt. 30 'Ändern Sie sie nicht mit dem Code-Editor. 31 End Sub 32 33 #End Region 34 35 ' WEB SERVICE EXAMPLE 36 ' Der HelloWorld()-Beispieldienst gibt die Zeichenkette Hello World zurück. 37 ' Zum Kompilieren löschen Sie bei den folgenden Zeilen die Kommentarzeichen, speichern und kompilieren das Projekt. 38 ' Damit Sie den Webdienst prüfen können, muss die .asmx-Datei 39 ' die Startseite sein. Drücken Sie dann F5. 40 ' 41 ' Public Function HelloWorld() As String 42 'HelloWorld = "Hello World" 43 ' End Function 44 45 End Class
Das Webdienstmodul beginnt ebenso wie die meisten Visual Basic .NETDateien mit den eingefügten Namensräumen. In unserem Beispiel ist der wichtigste eingefügte Namensraums System.Web.Services. Dieser enthält die Kernroutinen, die die Funktionsmerkmale eines Webdienstes hinzufügen. Achten Sie darauf, dass die Klasse in der Zeile 4 aus System.Web.Services.WebService erbt. Dadurch erhält Ihr Webdienst mehrere Eigenschaften. Die Tabelle 21.1 beschreibt diese Eigenschaften. Die meisten dieser Eigenschaften
676
Einen einfachen Webdienst erstellen
ermöglichen es dem Webdienst, auf die ASP.NET-Interna, also die Eigenschaften Request, Response, Session und Application, zuzugreifen.
Eigenschaft
Beschreibung
Application
Ermöglicht den Zugriff auf die im Application-Objekt in ASP.NET gespeicherten Variablen. Dies ist eine Kurzform der Eigenschaft Context.Application.
Context
Ermöglicht den Zugriff auf alle in ASP.NET bereitgestellten Daten. Dazu gehören das Request- und das Response-Objekt, das Cache-Objekt und andere Eigenschaften der Anfrage.
Server
Ermöglicht den Zugriff auf das Server-Objekt. Dieses Objekt dient dazu, den Webserver abzufragen, auf dem der Webdienst bereitgestellt wird, und Nachrichten zu codieren (z.B. eine Zeichenkette in eine zulässige Anfragezeichenkette umzuwandeln und dabei die passenden +-, %20- und anderen Zeichen einzufügen). Dies ist eine Kurzform der Eigenschaft Context.Server.
Session
Ermöglicht den Zugriff auf die im Session-Objekt in ASP.NET gespeicherten Variablen. Dies ist eine Kurzform der Eigenschaft Context.Session.
User
Ermöglicht den Zugriff auf Informationen über den Anwender, der die Anfrage absetzt.
Tabelle 21.1: Eigenschaften von WebSercice
Der Code in den Zeilen 6 bis 33 in Listing 21.5 ist in den Bereich #Region gehüllt. In der Entwicklungsumgebung ist dieser Code geschlossen (siehe Abbildung 21.2). Dies ist ein nützliches und in Visual Basic .NET neues Verfahren, mit dem Sie die Komplexität Ihres Codes verringern können, indem Sie die weniger wichtigen Einzelheiten verbergen. In diesem Fall verbergen wir den Code, der von der IDE verwendet wird. Dies ist der Code, der es Ihnen ermöglicht, Elemente über die sichtbare Oberfläche zu ziehen und abzulegen. Da Sie diesen Code normalerweise manuell ändern sollten, ist es sinnvoll, ihn im Editor zu verbergen. Der Code in den Zeilen 41 bis 43 ist ein Beispiel dafür, wie Sie dem Webdienst eine neue Methode hinzufügen. Beachten Sie, dass diese Zeilen standardmäßig auskommentiert sind. Wenn Sie diese Methode bereitstellen möchten, entfernen Sie einfach die Kommentarzeichen.
677
Mit Visual Basic .NET Webdienste erstellen
Abbildung 21.2: Der Basis-Webdienst
Den Code hinzufügen Nachdem Sie nun einen einfachen Webdienst haben, müssen Sie den Code hinzufügen, der die Aufgaben des Webdienstes ausführt. Hierfür fügen Sie der Klasse Web Service öffentliche Methoden hinzu. Sie können auch öffentliche Eigenschaften hinzufügen, was jedoch weniger aussagen würde (wie Sie etwas weiter unten sehen werden). Die öffentlichen Methoden in einem Webdienst sind dieselben wie in jeder anderen Klasse. Die einzige Ausnahme stellt das Attribut dar.
Die Syntax von Webdienst-Methoden Die Syntax für Webdienst-Methoden sieht folgendermaßen aus: Public Function MethodName(ByVal Parameter As Type) As ReturnType 'Code für die Methode End Function
Dabei ist MethodName der Name der Methode, Parameter ist die Liste der Parameter für die Methode, Type ist der Typ eines Parameters und ReturnType ist der von der Webmethode zurückgegebene Typ. Dass es sich hier nicht um eine normale Methode handelt, wird dadurch angezeigt, dass in die normale Deklaration eingefügt ist. Dies ist ein Beispiel für ein Attribut, also ein .NET-Element, das einer Klasse, Methode oder Eigenschaft neues Verhalten hin-
678
Einen einfachen Webdienst erstellen
zufügt. In der Beispielsyntax gibt das Attribut an, dass der Webdienst für die Kommunikation mit einer Methode SOAP verwendet. Alle mit diesem Attribut gekennzeichneten Methoden stehen als Teil des Webdienstes öffentlich zur Verfügung. Im folgenden Beispiel habe ich dem Webdienst eine einfache Methode hinzugefügt, indem ich dem Webdienst-Projekt nach der Beispielmethode den Code im Listing 21.6 eingefügt habe. Alternativ dazu können Sie auch den Kommentar löschen und durch diese Methode ersetzen. Listing 21.6: Der Code für die Webdienst-Beispielmethode 1 Public Function Add( _ 2 ByVal X As Integer, _ 3 ByVal Y As Integer) As Integer 4 Return X + Y 5 End Function
Diese Methode fügt einfach zwei Zahlen hinzu und gibt deren Summe zurück. Dieses Beispiel mag allzu einfach erscheinen, aber es macht alles, was Webdienst-Methoden normalerweise machen: 쐽
Es enthält in der Deklaration das Attribut .
쐽
Es gibt einen Wert zurück.
Sie können weitere oder komplexere Methoden einfügen. Allerdings ist das nicht notwendig, da wir in der heutigen Lektion noch einen realistischeren Webdienst schreiben werden.
Den Webdienst kompilieren Einen Webdienst kompilieren Sie genauso wie alle anderen Projekte. Wählen Sie ERSTELLEN im Menü ERSTELLEN. Dadurch wird eine DLL erzeugt (und in das Verzeichnis bin unter dem Wurzelverzeichnis des Web Service platziert), die den Code für den Webdienst enthält. Nachdem Sie den Dienst kompiliert haben, suchen Sie die erzeugte ASMX-Datei. Klicken Sie sie im Projektmappen-Explorer mit der rechten Maustaste an und wählen Sie IN BROWSER ANZEIGEN, um die Datei bei der Arbeit zu sehen. Eine .asmx-Datei enthält die Funktionalität eines Webdienstes, so wie eine .exe-Datei ein Programm und eine .aspx-Datei ein Webformular ist. Die Abbildung 21.3 und die Abbildung 21.4 zeigen Ansichten des Beispielprojektes. Die zugehörige URL lautet: http://localhost/MathService/service1.asmx
Zuerst sehen Sie einen Bildschirm, der eine Beschreibung des Webdienstes sowie die Namen aller Methoden anzeigt, in die das Attribut WebMethod eingefügt wurde (siehe Abbildung 21.3).
679
Mit Visual Basic .NET Webdienste erstellen
Abbildung 21.3: Testseite für den Webdienst
Wenn Sie eine der Methoden (z.B. die gerade erstellte Methode Add) markieren, erscheint eine weitere Testseite mit einem Formular, in dem Sie die Testwerte für den Webdienst eingeben können (siehe Abbildung 21.4). Außerdem enthält sie SOAP-Beispielnachrichten für die Anfrage und die Antwort für diese Methode.
Abbildung 21.4: Werte in die Testseite eingeben
680
Einen Webclient erstellen
Diese Testseite wird automatisch generiert, wenn Sie eine ASMX-Datei zum Betrachten öffnen. Sie liefert Informationen über die vom Webdienst bereitgestellten Methoden. Außerdem erzeugt sie für jede Methode ein Testformular, das es Ihnen ermöglicht, einige Werte einzugeben und die Funktion auszuführen. Geben Sie in die Textfelder im Abschnitt für die Methode Add einige Werte ein und klicken Sie die Schaltfläche AUFRUFEN an. Nun öffnet sich ein weiteres Fenster, das die Summe anzeigt (siehe Abbildung 21.5).
Abbildung 21.5: Dem Webdienst Werte hinzufügen
21.4 Einen Webclient erstellen Die Testseite, die wir für die ASMX-Seite erzeugt haben, ist zwar für Testwecke geeignet, aber Sie sollten nicht von den Anwendern erwarten, dass sie diese Seite für die Arbeit mit Ihrem Webdienst verwenden. Statt dessen sollten Sie einen Client (ein Windows- oder Webformular) erstellen, das mit dem Webdienst kommuniziert. Die Kommunikation zwischen diesem Client und dem Webdienst wird mithilfe einer Zwischenklasse, des so genannten Proxy durchgeführt. Allgemein ausgedrückt ist ein Proxy ein autorisierter Stellvertreter oder Ersatz. Er ist ein Vermittler. Bei Webdiensten ist ein Proxy eine kleine clientseitige Komponente, die für das Clientprogramm wie der Webdienst aussieht. Wenn das Clientprogramm einen Aufruf an den Proxy absetzt, generiert dieser die passende SOAP-Anfrage für den Webdienst und gibt sie weiter. Wenn der Web-
681
Mit Visual Basic .NET Webdienste erstellen
dienst mit einer SOAP-Antwort reagiert, wandelt der Proxy diese wieder in den erwarteten Ergebniswert für die Funktion um und gibt diesen an den Client zurück. Die Abbildung 21.6 veranschaulicht diesen Prozess.
Client Server
Test Math Service 123 456
Add
579
1. Client ruft Methode auf Proxy auf
5. Proxy wandelt die SOAP-Antwort für den Client um 2. Proxy generiert SOAP-Anfrage Proxy
3. Webdienst führt Anfrage durch Web Service
4. Webdienst erzeugt SOAP-Antwort
Abbildung 21.6: Der Informationsfluss zwischen dem Client, dem Proxy und dem Webdienst
Das Projekt erstellen Es ist durchaus möglich, einen Client mit einem Windows- oder Webformular (oder sogar einer anderen Bibliothek oder einem Webdienst) zu schreiben. In diesem Abschnitt werden wir besprechen, wie Sie einen Webdienst-Client anlegen, indem Sie ein WindowsForms-Formular erstellen. Dabei zeigen wir Ihnen ein Beispiel für eine mögliche Verwendung von Webdiensten, nämlich die Erweiterung vorhandener (oder zukünftiger) Desktop-Clients wie z.B. Microsoft Office. Die Erstellung eines Clients mit Webformularen funktioniert ähnlich. Legen Sie ein neues Windows-Anwendungsprojekt an und nennen Sie es MathClient. Am besten erstellen Sie dieses Projekt in derselben Lösung wie den MathService, damit Sie beide zusammenhalten. Dies ist allerdings nicht unbedingt erforderlich. Wählen Sie DATEI, NEU, PROJEKT. Im Dialogfenster NEUES PROJEKT wählen Sie WINDOWS-ANWENDUNG und im Namensfeld geben Sie MathClient ein. Stellen Sie sicher, dass ZUR PROJEKTMAPPE HINZUFÜGEN markiert ist, und klicken Sie OK an. Visual Basic .NET erstellt nun ein Projekt mit einem einzigen Formular. Schließen Sie den Formulardesigner und
682
Einen Webclient erstellen
benennen Sie die Datei Form1.vb in frmTest.vb um. Öffnen Sie das Formular in der Codeansicht und ändern Sie alle Referenzen von Form1 auf frmTest. Klicken Sie das Projekt MathClient mit der rechten Maustaste an und wählen Sie EIGENSCHAFTEN. Auf der Registerkarte ALLGEMEIN setzen Sie das Startobjekt auf frmTest und klicken OK an. Schließlich klicken Sie das Projekt MathClient erneut mit der rechten Maustaste an und wählen ALS STAROBJEKT FESTLEGEN. Dadurch stellen Sie sicher, dass zuerst dieses Projekt ausgeführt wird, wenn Sie die Lösung starten.
Den Code hinzufügen Wenn Sie in Visual Basic .NET ein anderes Objekt aufrufen, müssen Sie normalerweise eine Referenz auf dieses Objekt laden. Dadurch erfährt die IDE, welche Eigenschaften und Methoden in dem Objekt vorhanden sind. Mit einem Webdienst können Sie dies jedoch nicht machen, da der Webdienst nicht über alle bereitgestellten Informationen über ein lokalen Objekt verfügt. Statt dessen müssen Sie eine Webreferenz laden, also einen Zeiger zu einer WSDL-Datei. Wie Sie bereits wissen, beschreibt diese Datei alle Methoden eines Webdienstes einschließlich ihrer Parameter. Das Hinzufügen einer Webreferenz ist einfach. Klicken Sie das Projekt MathClient mit der rechten Maustaste an und wählen Sie WEBVERWEIS HINZUFÜGEN. Nun sollte das in Abbildung 21.7 gezeigte Dialogfenster erscheinen.
Abbildung 21.7: Eine Webreferenz hinzufügen
683
Mit Visual Basic .NET Webdienste erstellen
Das Dialogfenster fragt nach der URL zu einer DISCO-Datei oder zum Speicherort der WSDL-Datei für den Webdienst. Normalerweise stellt der Autor des Dienstes diese Informationen bereit. Bei MathService lautet die URL http://localhost/MathService/ MathService.vsdisco. Geben Sie die URL ein und klicken Sie den Suchpfeil an. Im Idealfall wird nun nach einer kurzen Pause der Webdienst gefunden. Die Abbildung 21.8 veranschaulicht dies.
Abbildung 21.8: Die Webreferenz finden
Nachdem die Webreferenz gefunden wurde, klicken Sie die Schaltfläche VERWEIS HINZUFÜGEN an. Dadurch wird ein neuer Abschnitt in das Projekt eingefügt (WEBVERWEISE), der unter dem Abschnitt LOCALHOST drei Dateien enthalten sollte: Service1.wsdl, MathService.vsdisco und Reference.Map. Mithilfe dieser Dateien fügt der Compiler dem Clientprogramm zur Laufzeit einen Proxy hinzu, der weiß, wie er mit dem Webdienst kommunizieren muss. Da unser Clientprogramm nun weiß, wie es mit dem Webdienst kommunizieren soll, können Sie den Code hinzufügen, der den Dienst aufruft. Hierfür fügen Sie zwei Textfelder, eine Beschriftung und eine Schaltfläche in das Formular ein. Setzen Sie die Eigenschaften so, wie Tabelle 21.2 es zeigt. Nun müssen Sie noch den Code in Listing 21.7 einfügen, der den Webdienst aufruft. Klicken Sie die Schaltfläche ADD an, um diesen Code hinzuzufügen.
684
Einen Webclient erstellen
Steuerelement
Eigenschaft
Wert
Form
Text
Test Math Service
Size
248, 160
BorderStyle
FixedSingle
Name
txtX
Text
123
Location
16, 24
Size
100, 20
Name
txtY
Text
456
Location
16, 56
Size
100, 20
Name
cmdAdd
Text
&Add
Location
128, 56
Size
75, 23
Name
lblResult
Text
0
Location
16, 88
Size
100, 23
TextBox
TextBox
Button
Label
Tabelle 21.2: Eigenschaften des Formulars MathClient
Listing 21.7: Den Webdienst aufrufen 1
Private Sub cmdAdd_Click( _
2 3 4 5 6 7
ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles cmdAdd.Click Dim oMath As New localhost.Service1() lblResult.Text = oMath.Add(CInt(txtX.Text), CInt(txtY.Text)) End Sub
685
Mit Visual Basic .NET Webdienste erstellen
Hier sollten Ihnen sofort auffallen, dass der Code große Ähnlichkeit mit dem Code für einen normalen Objektzugriff hat. Der einzige Unterschied findet sich in den Zeilen 5 und 6. Im Gegensatz zu dem Code für den Zugriff auf ein lokales Objekt basiert der vollständige Name des Objekts hier auf dem Server, der den Webdienst bereitstellt, in unserem Fall also localhost.Service1. Beachten Sie auch, dass der Name des Webdienstes sich ändert, wenn sich der Host für diesen Dienst ändert. Das Programm sollte nun in der Lage sein, den Webdienst aufzurufen, und zwar unabhängig davon, ob sich dieser wie in unserem Beispiel auf demselben Rechner oder irgendwo im Internet befindet. Die Abbildung 21.9 zeigt das Ergebnis.
Abbildung 21.9: Den Webdienst mit dem Client aufrufen
Dieser neu hinzugefügte Dienst erscheint (und ist) zwar sehr einfach, aber wie bereits gesagt haben wir bei seiner Erstellung und Verwendung alles gemacht, was bei einem echten Webdienst notwendig ist: 쐽
ein Webdienst-Projekt erstellen (oder einem vorhandenen Webprojekt einen Webdienst hinzufügen),
쐽
mit dem Attribut eine oder mehrere öffentliche Methoden hinzufügen,
쐽
einer Clientanwendung eine Webreferenz hinzufügen, die zu einer WSDL für den Webdienst zeigt,
쐽
eine neue Variable erstellen, die zu dem Webdienst zeigt, und eine oder mehrere seiner Methoden ausführen.
21.5 Ein komplexerer Webdienst Um Ihnen zu zeigen, dass Sie tatsächlich bereits alles gesehen haben, was es über die Erstellung von Webdiensten zu wissen gibt, werden wir nun einen Webdienst erstellen, der mehr macht, als nur zwei Zahlen zu addieren. Wir werden einen Webdienst einrichten, der von einem Unternehmen, in unserem Fall von Northwind, bereitgestellt werden kann. Mit diesem Webdienst können Sie die folgenden Aufgaben durchführen:
686
Ein komplexerer Webdienst
쐽
eine Liste von Produktkategorien abrufen,
쐽
eine Liste der Produkte in einer Kategorie abrufen,
쐽
ein Produkt bestellen.
Ein Unterschied zwischen diesem Webdienst und dem vorherigen Beispiel besteht darin, dass das neue Beispiel zeigt, dass Sie Objekte abrufen können. Jedes integrierte oder vom Anwender erzeugte Objekt kann an einen Webdienst gesandt oder von diesem zurückgegeben werden. Das Objekt wird in XML-Code umgewandelt, damit es über das Netzwerk verschickt werden kann, und für die Verwendung durch den Client wird es wieder in ein Objekt umgewandelt.
Den Dienst erstellen Ebenso wie bei unserem einfachen Beispiel erstellen Sie auch hier zuerst den Webdienst. In Visual Basic .NET erstellen Sie ein neues Webdienst-Projekt. Nennen Sie es Northwind. Die Abbildung 21.10 zeigt das Dialogfenster NEUES PROJEKT.
Abbildung 21.10: Den Webdienst Northwind erstellen
Schließen Sie den Designer und benennen Sie die Datei Service1.asmx in Products.asmx um. Öffnen Sie diese Datei in der Codeansicht und ändern Sie die Zeile Public Class Service1
in: Public Class ProductManager
Nun fügen wir die drei Methoden hinzu. Die erste Methode gibt ein Array mit allen Kategorien zurück. Die zweite Methode gibt ein DataSet zurück, das alle Produktinformationen
687
Mit Visual Basic .NET Webdienste erstellen
für eine gegeben Kategorie enthält. Die dritte Methode fügt in die Tabelle Categories neue Informationen ein. Das Listing 21.8 zeigt diese drei Methoden. Weitere Informationen über den Code für den Datenzugriff in diesen Methoden finden Sie am Tag 12, Mit .NET auf Daten zugreifen. Fügen Sie diesen Code hinter dem Bereich, der als Web Services Designer Generated Code gekennzeichnet ist, aber vor der Zeile End Class ein. Außerdem müssen Sie zwei Imports-Anweisungen einfügen: eine, um den Namensraum System.Data zu importieren, und eine, um den Namensraum System.Data.SqlClient zu importieren. Fügen Sie diese beiden Anweisungen direkt unter der Zeile Imports System.Web.Service ein.
Listing 21.8: Der Code für den Webdienst Products 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
688
Private Const DSN As String = _ "server=localhost;database=northwind;user id=sa;password=;" Public Function GetCategories() As String() Dim oCon As New SqlConnection(DSN) Dim sSQL As String sSQL = "SELECT CategoryName FROM Categories" Dim oCmd As New SqlDataAdapter(sSQL, oCon) Dim oDS As New DataSet() Dim sReturn() As String Dim I As Integer oCmd.Fill(oDS, "Categories") ReDim sReturn(oDS.Tables(0).Rows.Count) For I = 0 To oDS.Tables(0).Rows.Count – 1 sReturn(I) = CStr(oDS.Tables(0).Rows(I).Item(0)) Next Return sReturn End Function _ Public Function GetProducts( _ ByVal categoryName As String) As DataSet Dim oCon As New SqlConnection(DSN) Dim sSQL As String sSQL = "SELECT ProductID, ProductName, UnitPrice " & _ "QuantityPerUnit, Discontinued, CategoryName " & _ "FROM Categories INNER JOIN Products " & _ "ON Categories.CategoryID = Products.CategoryID " & _
Ein komplexerer Webdienst
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
"WHERE (Discontinued=0) AND " & _ "(CategoryName LIKE '" & categoryName & "%')" Dim oCmd As New SqlDataAdapter(sSQL, oCon) Dim ods As New DataSet() oCmd.Fill(ods, "products") Return ods End Function _ Public Function InsertCategory( _ ByVal categoryName As String, _ ByVal description As String) As Boolean Dim oCon As New SqlConnection(DSN) Dim sSQL As String sSQL = "INSERT INTO Categories" & _ "(CategoryName, Description) " & _ "VALUES('" & categoryName & "'" & _ ", '" & description & "')" Dim oCmd As New SqlCommand(sSQL, oCon) Try oCon.Open() oCmd.ExecuteNonQuery() Return True Catch Return False End Try End Function
Die erste der drei Methoden, GetCategories, gibt ein Array von Zeichenketten zurück, das die Namen aller Produktkategorien enthält. Sie könnte ebenso gut ein DataSet zurückgeben (das wäre sogar einfacher gewesen), aber wir wollten Ihnen hier zeigen, dass auch Arrays zurückgegeben werden können. Die Verbindungszeichenkette, die wir für den Zugriff auf die Datenbank verwenden werden, erstellen wir in den Zeilen 1 und 2 als eine Konstante. Sie müssen diese Verbindungszeichenkette ändern, falls Ihre Datenbank an einer anderen Stelle gespeichert ist oder falls Sie sich für die Anmeldung bei der Datenbank authentifizieren müssen. Die passenden Datenzugriffsobjekte erstellen wir in den Zeilen 5 bis 11. Hier verwenden wir die Methoden für den Zugriff auf eine SQL-Datenbank. Daher müssen wir den Namensraum System.Data.SqlClient in die Datei einfügen. Wenn Sie auf eine Nicht-SQLDatenbank zugreifen, müssen Sie darüber hinaus die ADO-Verbindungsobjekte verwenden, die im Namensraum System.Data.OleDb gespeichert sind.
689
Mit Visual Basic .NET Webdienste erstellen
Beachten Sie, dass wir nur eine einzige Spalte aus der Tabelle Categories abrufen (Zeile 7). Die Zeile 13 ruft die Daten ab und füllt das DataSet. Die Zeilen 14 bis 17 verwenden die Informationen aus dem DataSet, um das Array zu besiedeln. Schließlich wird das Array in Zeile 18 an die aufrufende Funktion zurückgegeben. Die zweite Methode, GetProducts, ist einfacher als die erste. In dieser Methode geben Sie ein DataSet zurück, das mit allen lieferbaren Produkten in einer angefragten Kategorie gefüllt wird. Der komplexeste Teil dieser Methode ist der Code in den Zeilen 26 bis 31, in denen die SQL-Anweisung zum Abrufen der Liste erstellt wird. Im Idealfall wird dies in der Datenbank als eine Ansicht oder gespeicherte Prozedur gespeichert. Die endgültige Zeichenkette sieht ungefähr folgendermaßen aus: SELECT ProductID, ProductName, UnitPrice, QuantityPerUnit, Discontinued, CategoryName FROM Categories INNER JOIN Products ON Categories.CategoryID = Products.CategoryID WHERE (Discontinued = 0) AND (CategoryName LIKE 'Bev%'
Die SQL-Anweisung, die wir dadurch erhalten, gibt alle angefragten Felder zurück, in denen das Produkt lieferbar (Discontinued=0) ist und der Name der Kategorie mit Bev beginnt. Die letzte Methode, InsertCategory, veranschaulicht, dass Sie auch verlangen können, dass der Webdienst etwas anderes macht, als Informationen abzurufen. Hier fügen wir eine neue Kategorie in die Datenbank ein. Dabei fangen Sie ebenso an wie bei den beiden vorhergehenden Methoden, d.h., Sie stellen einen Verbindung zur Datenbank her und schreiben eine SQL-Anweisung. In diesem Fall sieht die Anweisung in den Zeilen 45 bis 48 etwa folgendermaßen aus: INSERT INTO Categories(CategoryName, Description) VALUES('Stuff', 'Other stuff we sell')
In dieser Methode erstellen Sie jedoch keinen SqlDataAdapter, sondern einen SqlCommand. Wie Sie am Tag 12 gelernt haben, ist der SqlCommand besser für das Einfügen, Löschen und Ändern von Daten als für das Abrufen von Daten geeignet. Auch der Try...Catch-Block in den Zeilen 50 bis 56 verdient einige Aufmerksamkeit. Hier versuchen wir die SQL-Anweisung auszuführen. Wenn der Versuch gelingt, gibt die Methode True zurück. Wenn aus irgendeinem Grund eine Ausnahme auftritt, wird False zurückgegeben. In dieser Methode wird normalerweise keine Ausnahme auftreten, außer wenn die Datenbank nicht läuft oder Sie nicht über ausreichend Festplattenspeicher verfügen.
690
Ein komplexerer Webdienst
Den Webdienst prüfen Ebenso wie den einfachen Webdienst können Sie auch diesen Webdienst mit den integrierten Funktionsmerkmalen prüfen. Kompilieren Sie das Projekt, öffnen Sie einen Webbrowser und navigieren Sie zu der Adresse http://localhost/northwind/products.asmx. Sie sollten nun die Testseite sehen, die in Abbildung 21.11 dargestellt ist.
Abbildung 21.11: Testseite für den Webdienst Northwind
Prüfen Sie alle drei Methoden, bevor Sie weitermachen. Die Abbildungen 21.12, 21.13 und 21.14 zeigen das Ergebnis der einzelnen Methoden. Die zweite Methode haben wir mit der Zeichenkette Bev geprüft und die dritte mit den Zeichenketten Stuff und Just some other stuff we sell.
Den Client erstellen Nun können wir für unseren Webdienst einen Client erstellen. Diesmal werden wir jedoch einen Client einrichten, der auf Web Forms basiert. Dies könnte z.B. eine Website sein, deren Inhalt von einem Webdienst bereitgestellt wird, die aber ein eigenes Layout verwendet. Es könnte aber auch der Web-Forms-Client für eine Intranetanwendung sein. Um das Clientprojekt zu erstellen, fügen Sie zunächst ein neues Webanwendungsprojekt hinzu. Nennen Sie es NWClient (für Northwind-Client). Klicken Sie den Projektnamen mit der rechten Maustaste an und wählen Sie ALS STARTOBJEKT FESTLEGEN. Schließen Sie den Designer und nennen Sie die Datei WebForm1.aspx in Products.aspx um.
691
Mit Visual Basic .NET Webdienste erstellen
Abbildung 21.12: GetCategories
prüfen
Abbildung 21.13: GetProducts prüfen
Öffnen Sie den Designer erneut, um eine recht einfache Benutzeroberfläche zu erstellen. Es ist vor allem deshalb einfach, weil ich mich mit Grafiken nicht sonderlich gut auskenne, aber auch, weil wir das Beispiel nicht durch viel zusätzlichen Code komplizieren möchten.
692
Ein komplexerer Webdienst
Abbildung 21.14: InsertCategory
prüfen
Die Tabelle 21.3 beschreibt die Eigenschaften der Steuerelemente auf der Seite. Steuerelement
Eigenschaft
Wert
Document
title
Northwind Products
Panel
(ID)
pnlNewCategory
Height
181px
Width
368px
(ID)
lblNewCategory
Text
New Category
(ID)
lblName
Text
Name:
(ID)
txtName
Height
24px
Width
213px
(ID)
lblDescription
Text
Description:
(ID)
txtDescription
Height
41px
Label
Label
TextBox
Label
TextBox
Tabelle 21.3: Eigenschaften des Webformulars für NWClient
693
Mit Visual Basic .NET Webdienste erstellen
Steuerelement
Button
Label
Label
DropDownList
DataGrid
Eigenschaft
Wert
Width
222px
TextMode
MultiLine
(ID)
cmdAdd
Text
Add
(ID)
lblResult
Height
19px
Width
329px
BackColor
Silver
(ID)
lblCategories
Text
Categories
(ID)
cboCategory
Height
22px
Width
202px
AutoPostBack
True
(ID)
dbgProducts
Height
265px
Width
438px
Tabelle 21.3: Eigenschaften des Webformulars für NWClient (Forts.)
Auf dem Formular fügen Sie die Steuerelemente Combobox (DropDownList) und DataGrid ein. Mit diesen Steuerelementen zeigen wir die Ergebnisse der Aufrufe von GetCategories und GetProducts an. Außerdem haben Sie eine Tafel mit zwei Textfeldern, einer Schaltfläche und einer Beschriftung. Diese verwenden wir für die Methode InsertCategory. Eine beachtenswerte Eigenschaft der DropDownList ist AutoPostBack. Wie Sie am Tag 10, Mit Web Forms eine Benutzeroberfläche erstellen, gelernt haben, bewirkt AutoPostBack, dass das Formular direkt an den Server zurückgeschickt wird und nicht erst dann, wenn die Schaltfläche Submit gedrückt wird. In unserem Fall wird das Formular abgeschickt, sobald der Anwender den Inhalt der DropDownList ändert. Nach der DropDownList wird das DataGrid definiert. Mit der Eigenschaft AutoFormat des DataGrid-Designers können Sie das DataGrid schöner gestalten, als es in der Standardeinstellung aussieht. Die Abbildung 21.15 zeigt, wie die Benutzeroberfläche in Visual Basic .NET aussieht.
694
Ein komplexerer Webdienst
Abbildung 21.15: Der NorthwindClient
Den Code hinzufügen Nun haben Sie es fast geschafft. Sie brauchen nur noch einige Routinen einzufügen. Wenn dem Anwender die Seite angezeigt wird, soll das Kombinationsfeld Categories ausgefüllt und das Raster mit dem Ergebnis dieser Kategorie gefüllt sein. Das Raster soll aktualisiert werden, sobald der Anwender eine neue Kategorie auswählt. Und wenn der Anwender die Schaltfläche ADD anklickt, soll eine neue Kategorie hinzugefügt werden. Bevor Sie Code hinzufügen können, müssen Sie eine Webreferenz auf den Webdienst einfügen. Klicken Sie das Projekt NWClient mit der rechten Maustaste an und wählen Sie WEBVERWEIS HINZUFÜGEN. Die DISCO-Datei, die für den Webdienst erzeugt wird, hat die Adresse http://localhost/Northwind/Northwind.vsdisco. Die Abbildung 21.16 zeigt, welches Ergebnis eine Abfrage dieser Datei liefert. Anstatt zuerst das Kombinationsfeld zu besiedeln, beginnen wir mit dem Raster. Sie wissen, dass Sie das Raster immer dann aktualisieren möchten, wenn sich der Inhalt des Kombinationsfeldes ändert. Das passende Ereignis, dem wir hierfür Code hinzufügen müssen, ist SelectedIndexChanged. Klicken Sie das Kombinationsfeld doppelt an und aktualisieren Sie den Ereignisbehandler so, wie in Listing 21.9 gezeigt.
695
Mit Visual Basic .NET Webdienste erstellen
Abbildung 21.16: Den Dienst Northwind erkunden
Listing 21.9: Das Raster aktualisieren 1 2 3 4 5 6 7 8 9 10
Private Sub cboCategory_SelectedIndexChanged( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles cboCategory.SelectedIndexChanged Dim oProducts As New localhost.ProductManager() Dim oDS As DataSet oDS = oProducts.GetProducts(cboCategory.SelectedItem.Text) dbgProducts.DataSource = oDS.Tables(0).DefaultView dbgProducts.DataBind() End Sub
Diese Methode instanziiert zuerst einen neuen ProductManager (den Webdienst). Anschließend ruft sie mit der Methode GetProducts die Liste der Produkte (als ein DataSet) vom Webdienst ab (Zeile 7). In den Zeilen 8 und 9 bindet sie das DataSet an DataGrid und löst dessen Aktualisierung aus. Nachdem Sie dafür gesorgt haben, dass sich das Raster selbst aktualisiert, müssen Sie sich um das Kombinationsfeld kümmern. Es soll mit der Kategorienliste gefüllt werden, wenn der Anwender die Seite erstmals lädt. Das passende Ereignis hierfür ist WebForm1_Load. Das Listing 21.10 zeigt diese Methode.
696
Ein komplexerer Webdienst
Listing 21.10: Die Kategorien laden 11 12 13 14 15 16 17 18 19 20 21 22
Private Sub Page_Load( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load If Not IsPostBack Then Dim oProducts As New localhost.ProductManager() Dim sCategories() As String sCategories = oProducts.GetCategories cboCategory.DataSource = sCategories cboCategory.DataBind() cboCategory_SelectedIndexChanged(Me, Nothing) End If End Sub
Wie Sie am Tag 10, Mit Web Forms eine Benutzeroberfläche erstellen, gelernt haben, wird mit der Eigenschaft IsPostBack ermittelt, ob der Anwender die Webseite zum ersten Mal betrachtet oder zu ihr zurückkehrt. Da Sie Aufgaben wie das Füllen von Steuerelementen normalerweise nur einmal durchführen, fügen Sie Ihren Code in den Abschnitt If Not IsPostBack ein. Im Ereignis Page_Load erzeugen Sie zuerst eine neue Instanz des Webdienstes (Zeile 15). Dann rufen Sie die Methode GetCategories auf (Zeile 17), die ein Zeichenketten-Array zurückgibt. Die Zeilen 18 und 19 des Ereignisses Page_Load enthalten einen interessanten Aspekt. Sie können Steuerelemente nicht nur an DataSets binden, sondern auch an Arrays (und Collections). Demnach können Sie das Array einer Kategorie direkt an ein Kombinationsfeld binden. Der Code in der Zeile 20 bedarf einer Erklärung. Wenn das Kombinationsfeld gefüllt wird, wird der gewählte Index nicht als geändert erachtet. Daher rufen Sie den Ereignisbehandler für das Kombinationsfeld explizit auf, um sicherzustellen, dass das Raster gefüllt wird, wenn der Anwender die Seite zum ersten Mal besucht. Die Abbildung 21.17 zeigt das Ergebnis, wenn die Webseite geladen wird, und Abbildung 21.18 zeigt, was geschieht, wenn die Kategorie geändert wird.
697
Mit Visual Basic .NET Webdienste erstellen
Abbildung 21.17: Den NorthwindClient betrachten
Abbildung 21.18: Die gewählte Kategorie ändern
Zuletzt fügen Sie noch den Code ein, der dafür sorgt, dass die Schaltfläche ADD der Datenbank eine neue Kategorie hinzufügt. Klicken Sie die Schaltfläche ADD doppelt an, um das Codefenster zu öffnen. Fügen Sie der Prozedur den Code in Listing 21.11 hinzu.
698
Zusammenfassung
Listing 21.11: Die Schaltfläche Add 23 24 25 26 27 28 29 30 31 32 33
Private Sub cmdAdd_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles cmdAdd.Click Dim oProducts As New localhost.ProductManager() If oProducts.InsertCategory(txtName.Text, txtDescription.Text) Then lblResult.Text = "New category added: " & txtName.Text Else lblResult.Text = "Failed to add new category" End If End Sub
Dieser Code sollte eigentlich nichts allzu Komplexes enthalten. Sie erzeugen eine neue Instanz des Webdienstes und rufen die Methode InsertCategory auf, der Sie den Inhalt der beiden Felder übergeben. Sobald die Methode True oder False zurückgibt, ändern Sie den Inhalt von Label entweder auf eine Erfolgsoder eine Misserfolgsmeldung. Die Abbildung 21.19 zeigt, welches Ergebnis wir erhalten, wenn wir diese Methode ausführen.
21.6 Zusammenfassung Die Webdienste sind eine neue Art, in einem Netzwerk oder im Internet auf Programme und Komponenten zuzugreifen. Sie sind sowohl bei Webanwendungen als auch bei traditionellen Desktopanwendungen nützlich. Sie erleichtern die Entwicklung und plattformübergreifende Verwendung von Webclients mit Objekten. Ein wesentlicher Vorteil der Webdienste besteht darin, dass sie in jeder beliebigen Programmiersprache geschrieben sein können und die Zusammenarbeit mit jedem beliebigen Betriebssystem ermöglichen. Sie können sicher sein, dass die Webdienste umso bedeutender werden, je mehr Webdienste es gibt.
699
Mit Visual Basic .NET Webdienste erstellen
Abbildung 21.19: Die Schaltfläche ADD anklicken
21.7 Fragen und Antworten F
Kann ich einen Webdienst so sichern, dass ich steuern kann, wer auf ihn zugreifen kann? A
F
Sind die Webdienste nur bei einer Lösung nützlich, die ausschließlich MicrosoftProdukte verwendet? A
F
Nein, die Webdienste können mit jedem Betriebssystem kommunizieren. Sofern der Client die SOAP-Anfrage interpretieren (und eine SOAP-Antwort erstellen) kann, können Sie ihn für die Zusammenarbeit mit einem Client oder Server verwenden, der in Visual Basic .NET geschrieben ist.
Was geschieht, wenn ich auf einen Webdienst zuzugreifen versuche, der gerade nicht zur Verfügung steht? A
700
Ja, die Webdienste unterstützen HTTPS (und SSL) für die Zugriffskontrolle uneingeschränkt.
Leider können Sie in einer derartigen Situation nicht viel machen. Normalerweise müssen Sie Ihrem Client Code hinzufügen, der die richtige Reaktion auf diese Situation ermöglicht. Vielleicht scheitert der Client elegant oder er speichert die letzte Reaktion im Zwischenspeicher und verwendet sie dann erneut, wenn dies angemessen ist.
Workshop
21.8 Workshop Der Workshop soll Ihnen dabei helfen, mögliche Fragen vorherzusehen und das Gelernte zu wiederholen. Außerdem soll er Sie dazu anregen, über die praktische Umsetzung Ihres Wissens nachzudenken. Die Antworten und Lösungen finden Sie im Anhang A, Antworten.
Quiz 1. Wozu dient die DISCO-Datei? 2. Wozu dient ein Proxy? 3. Warum ist SOAP für Webdienste wichtig?
Übung Versuchen Sie einem der beiden Webdienste, die wir erstellt haben, weitere Methoden hinzuzufügen. Fügen Sie z.B. dem MathService die Methoden Subtract, Multiply und Divide hinzu. Aktualisieren Sie den Client, damit er mit den neuen Methoden arbeitet.
701
Rückblick Die Woche 3 behandelte einen großen Themenbereich und nannte zahlreiche Elemente, mit denen Sie sich in Zukunft noch eingehender befassen müssen. Zunächst haben Sie gelernt, wie Sie in .NET eigene Objekte erzeugen, indem Sie eigenständige Codeblöcke einrichten, die Sie dann bei Bedarf in anderen Projekten wiederverwenden können. Am Tag 16 haben Sie einige der komplexeren Verfahren und Techniken der Erstellung von Webanwendungen mit .NET kennen gelernt, unter anderem den Fensterteiler (splitter) und Menüs. Am Tag 17 haben wir zwei Bereiche des .NET-Frameworks behandelt, die häufig benötigt werden: die Grafikbibliothek, mit der Sie Bilder und das Erscheinungsbild einer Anwendung verändern können, und die .NET-Elemente, die mit der Dateibehandlung zu tun haben und mit denen Sie Dateien anlegen, öffnen und bearbeiten können. Am Tag 18 haben Sie gelernt, wie Sie erreichen, dass Ihre Anwendungen möglichst leicht zu warten und zu unterstützen sind. In diesen Themenbereich gehören unter anderem die Dokumentierung des Codes und die Quellcodekontrolle, die Ihre Arbeit schützt. Am Tag 19 haben wir erklärt, wie Sie Ihre Anwendung für die Verteilung vorbereiten, und dabei einige Aspekte besprochen, die mit der Weiterverteilung der erforderlichen .NET-Dateien zu tun haben, ohne die Ihr Programm nicht richtig funktioniert. Wie Sie Installationsprogramme erstellen, welche Optionen es bei der Remote-Installation gibt und wie Sie die Dokumentation für den Anwender bereitstellen, haben Sie am Tag 19 erfahren. Am Tag 20 und am Tag 21 haben wir uns mit den beiden Verfahren befasst, die die Grundlage des .NET-Konzeptes bilden: XML und Webdiensten. Sie haben gelernt, welche Klassen .NET für das Lesen, Schreiben, Transformieren und Erstellen von XML-Dokumenten bereitstellt und wie Sie XML in Ihren Anwendungen einsetzen können. Am Tag 21 haben wir Ihnen die Webdienste vorgestellt, eine der Hauptfunktionen von .NET. Dabei haben wir gezeigt, wie Sie mithilfe der Webdienste und der Standards HTTP und XML dem Internet die Funktionalität Ihres Systems zur Verfügung stellen können. Außerdem haben wir uns damit befasst, wie Sie diese Dienste erstellen und in Ihren eigenen Systemen verwenden, obwohl wir diesen Themenbereich im Rahmen unseres Buches nicht vollständig abdecken können. Das Bonusprojekt der Woche 3 steht unter der Adresse http://www.samspublishing.com im Internet zur Verfügung. Es zeigt, wie Sie mit den Konzepten, die wir in früheren Kapiteln besprochen haben, ein vollständiges Programm erstellen können. Zu diesen Konzepten gehören Grafiken, die Verwendung von Dateien, XML und sogar einige komplexere Aspekte der Windows Forms. Diese Techniken können Sie auf fast alle Systemarten
703
Rückblick
anwenden; aber um das Erlernen dieser Verfahren etwas weniger trocken zu gestalten, erstellen Sie bei diesem Bonusprojekt ein Spiel. Wenn Sie diese Woche nun abschließen, haben Sie 21 Tage mit Visual Basic .NET verbracht und verstehen die Grundprinzipen dieser Sprache, das zugrunde liegende Framework und wie Sie beides dafür verwenden können, eigene Systeme zu erstellen. Als Softwareentwickler lernt man zwar nie aus, aber Sie haben nun bereits eine recht umfangreiche Einführung in eine bemerkenswerte neue Programmiersprache durchgearbeitet. Nun brauchen Sie sich eine Zeitlang nicht mehr mit einführenden Materialien zu befassen und können sich darauf konzentrieren, verschiedene (Beispiel- oder echte) Anwendungen zu erstellen, um alles zu vertiefen, was Sie in den vergangenen drei Wochen gelernt haben. Schreiben Sie unbedingt so viel .NET-Code wie möglich: Das ist die beste Art, im Umgang mit dieser neuen Programmiersprache und neuen Technologie sicherer zu werden und sie besser zu verstehen.
704
Quiz-Anworten und Beispiellösungen für die Übungen
Quiz-Anworten und Beispiellösungen für die Übungen
A.1
Tag 1
Quiz 1. Microsoft produzierte zuerst einen Compiler für die Original-BASIC-Sprache und nannte dieses Produkt Microsoft BASIC. Die nächste Version dieses Compilers und der Vorläufer von Visual Basic war Quick BASIC (oder QBASIC). 2. Alle .NET-Sprachen haben diese Funktionen gemeinsam, weil sie durch die Common-Language-Runtime-Umgebung zur Verfügung gestellt werden. Durch diese Laufzeitumgebung kann jede dieser Programmiersprachen mit .NET arbeiten. 3. Dieser Prozess wird Kompilieren genannt. 4. Wenn Sie nicht explizit mit dem Switch /out: eine Ausgabedatei angeben, verwendet der Compiler den Namen der Quelldatei als Namen der ausführbaren Datei. In diesem Fall wäre die Ausgabe MySourceCode.exe.
Übungen 1. Um eine bestimmte ausführbare Datei zu erzeugen, benötigen Sie nur den Switch /out: für den Befehlszeilen-Compiler. Dafür gehen Sie zur Konsole (Befehlseingabeaufforderung) und navigieren (mit dem Befehl cd) zu dem Ordner, der Ihre Arbeitsdateien der früheren Beispiele enthält, insbesondere step3.vb. Lassen Sie nun den Compiler laufen, indem Sie vbc step3.vb /t:exe /out:WhatOS.exe eingeben und (¢) drücken. Auf diese Weise wird step3 kompiliert, aber das kompilierte Ergebnis wird in einer Datei mit dem Namen WhatOS.exe gespeichert. 2. Es gibt viele verschiedene Eigenschaften, die über Environment des .NET-Frameworks zur Verfügung stehen, und somit gibt es auch viele unterschiedliche Antworten auf diese Frage. Im Endeffekt sollten aber alle Antworten der folgenden Lösung ähneln, die die Version des .NET-Frameworks ausgibt: Public Class FrameworkVersion Shared Sub Main() System.Console.WriteLine(System.Environment.Version.ToString()) End Sub End Class
Dieser Code erzeugt die folgende Ausgabe (diese Ausgabe wird bei der Beta 2-Version von .NET erzeugt; wenn Sie die Release-Version verwenden, werden Sie also ein anderes Ergebnis erhalten):
706
Tag 2
C:\ TYVB\ C1>vbc FrameworkVersion.vb /t:exe Microsoft (R) Visual Basic.NET Compiler version 7.00.9254 for Microsoft (R) .NET CLR version 1.00.2914.16 Copyright (C) Microsoft Corp 2001. All rights reserved.
C:\ TYVB\ C1>FrameworkVersion 1.0.2914.16
Alternativ hätten Sie sich dazu entscheiden können, den Pfad oder einen von vielen anderen Werten, die alle dem gleichen elementaren Muster folgen in das System-Verzeichnis auszugeben. Ein Beispiel für die Verwendung des Wertes SystemDirectory sehen Sie hier: Public Class SysDir Shared Sub Main() System.Console.WriteLine(System.Environment.SystemDirectory) End Sub End Class C:\ TYVB\ C1>vbc SysDir.vb /t:exe Microsoft (R) Visual Basic.NET Compiler version 7.00.9254 for Microsoft (R) .NET CLR version 1.00.2914.16 Copyright (C) Microsoft Corp 2001. All rights reserved.
C:\ TYVB\ C1>SysDir C:\ WINNT\ System32
A.2
Tag 2
Quiz 1. Sie würden den Solution Explorer verwenden, der alle offenen Projekte anzeigt und alle Dateien in jedem Projekt. Dieses Fenster kann sichtbar gemacht werden, indem Sie die Tastenkombination (Strg)+(R) drücken. 2. Standardmäßig werden Projekte in einem neuen Ordner mit dem Namen des Projekts unter Eigene Dokumente\Visual Studio Projects gespeichert. Wenn Sie ein neues Projekt erzeugen, wird dieser Pfad im Dialogfenster NEUES PROJEKT angezeigt und Sie können ihn beliebig ändern.
707
Quiz-Anworten und Beispiellösungen für die Übungen
3. Wählen Sie im Dialogfenster PROJEKT EIGENSCHAFTEN ein Symbol aus. Sie gelangen zu diesem Dialogfenster, indem Sie das Projekt im Solution Explorer mit rechts anklicken und aus dem Kontextmenü EIGENSCHAFTEN auswählen. Sie können auch unter ALLGEMEINE EIGENSCHAFTEN\ ERSTELLEN eine .ico(icon)-Datei auswählen. 4. Wenn Sie im BEFEHLSFENSTER >cmd eingeben und auf (¢) drücken, wechseln Sie in dem Command-Modus. Wenn Sie immed eingeben und wiederum (¢) drücken, wechseln Sie zurück in den Immediate-Modus.
Übung Wenn Sie einen MessageBox-Show-Aufruf in das Ereignis TextChanged eines Textfeldes setzen, wird immer dann eine Nachricht angezeigt, wenn der Inhalt dieses Textfeldes verändert wird. Dies ist zwar eine nützliche Stelle für Code, der die Texteingabe des Anwender validiert, das Anzeigen einer Nachricht in diesem Ereignis kann aber nervig werden.
A.3
Tag 3
Quiz 1. Sie sollten unter den verfügbaren Arten diejenige aussuchen, die in Bezug auf Größe und Art der Variable am besten geeignet ist. 2. A und C sind richtig. Wenn Sie eine Funktion aufrufen, müssen Sie die Parameter in Klammern angeben. Außerdem muss im Allgemeinen etwas mit dem Ergebnis gemacht werden, wenn die Funktion einen Wert zurückgibt. Weisen Sie ihn entweder einer Variablen zu oder zeigen Sie ihn an. 3. Wenn eine Variable mit dem Schlüsselwort Private deklariert wird, ist sie im gesamten Modul oder in der Klasse verfügbar, in der sie deklariert wurde.
Übung Eine mögliche Lösung für diese Übung wäre die folgende: 1 2 3 4
708
Module Payment Private dblAnnualInterest As Double = 0 Private iYears As Integer = 0
Tag 3
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
Private dblLoanAmount As Double = 0 Private dblMonthlyDeposit As Double = 0 Sub Main() Dim dblResult As Double 'Hole Eingabewerte. GetInputValues() 'Rechne. dblResult = CalculatePayment(dblLoanAmount, _ dblAnnualInterest, _ iYears) 'Gib die Ergebnisse aus. DisplayResults(dblResult) End Sub Private Function CalculatePayment(Byval LoanAmount As Double, _ ByVal AnnualInterest As Double, _ ByVal Years As Integer) As Double 'Teile durch 1200, um daraus Prozent pro Monat zu machen. Dim dblMonthlyInterest As Double = CDec(AnnualInterest / 1200) Dim iMonths As Integer = Years * 12 Dim dblTemp As Double Dim dblReturn As Double
'Wir benötigen diesen Wert an verschiedenen Stellen. dblTemp = CDec(((1 + dblMonthlyInterest) ^ iMonths)) dblReturn = LoanAmount * _ (dblMonthlyInterest * dblTemp / (dblTemp – 1)) Return dblReturn End Function Private Sub GetInputValues() Console.WriteLine() dblLoanAmount = CDec(GetValue("Loan Amount: ")) dblAnnualInterest = _ CDbl(GetValue("Annual Interest (e.g. for 5%, enter 5): ")) iYears = CInt(GetValue("Years of loan: ")) Console.WriteLine() End Sub Private Function GetValue(ByVal Prompt As String) As String
709
Quiz-Anworten und Beispiellösungen für die Übungen
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
Console.Write(Prompt) Return Console.ReadLine End Function Private Sub DisplayResults(ByVal Result As Double) Console.WriteLine() Console.WriteLine("If you borrow { 0:c} , ", dblLoanAmount) Console.WriteLine("at { 0} % interest.", dblAnnualInterest) Console.WriteLine("for { 0} years", iYears) Console.WriteLine() Console.WriteLine("Your monthly payment would be: { 0:c} ", Result) End Sub End Module
A.4
Tag 4
Quiz 1. Für diese Art der Lösung ist die For-Schleife am besten geeignet, obwohl auch jede andere Schleife verwendet werden kann. 2. Jede Schleife kann zwar jede andere Schleife simulieren, aber die Do-Schleife ist die flexibelste, da sie Unterstützung für die While- und Until-Klauseln und für Start- und Endpositionen für Bedingungsausdrücke bietet. 3. Der innere Block ist der Code, der bei jeder Iteration der Schleife ausgeführt wird. Somit verbessert eine Änderung der Leistung oder der Anzahl von Iterationen in einer Schleife die Leistung in der Schleife. 4. Wenn Sie davon ausgehen, dass CalculatedTotalNetWorth() ein wenig komplexer ist als der Code, der unter dtCurrentDate.Hour() läuft, dann würden Sie am besten die beiden Seiten des Ausdrucks vertauschen. Das würde sicherstellen, dass die größere und aufwändigere Routine gar nicht erst ausgeführt wird, wenn der erste Ausdruck dtCurrentDate.Hour() > 12 False ist.
710
Tag 4
Übung Es gibt viele verschiedene Methoden, wie Sie das Programm NumberGuesser schreiben könnten, aber im Allgemeinen sollte das Ergebnis ungefähr so aussehen: Public Class GamePlayer Shared Sub Main() Dim iUpperBound As Integer Dim iLowerBound As Integer Dim iCurrentGuess As Integer Dim sInput As String Dim sGuessStatus As String System.Console.Write("Please enter the lower bound: ") sInput = System.Console.ReadLine() iLowerBound = CInt(sInput) System.Console.Write("Please enter the upper bound: ") sInput = System.Console.ReadLine() iUpperBound = CInt(sInput) 'Bitte den Anwender, eine Zahl auszuwählen. System.Console.WriteLine("Pick a number between " & iLowerBound & _ " and " & iUpperBound & ", and remember it!") System.Console.WriteLine("Hit Return To Continue") System.Console.ReadLine() iCurrentGuess = 0 sGuessStatus = "" Do Until sGuessStatus = "=" iCurrentGuess = GetMyGuess(iLowerBound, iUpperBound) System.Console.WriteLine("My Guess Is: " & iCurrentGuess) System.Console.Write("How did I do? ") sGuessStatus = System.Console.ReadLine() sGuessStatus = sGuessStatus.ToUpper() Select Case sGuessStatus Case "H" iUpperBound = iCurrentGuess – 1 Case "L" iLowerBound = iCurrentGuess + 1 End Select
711
Quiz-Anworten und Beispiellösungen für die Übungen
Loop End Sub Shared Function GetMyGuess(iUpper As Integer, iLower As Integer) ➥As Integer If iUpper = iLower Then GetMyGuess = iLower Else GetMyGuess = iLower + ((iUpper – iLower – 1)\ 2) End If End Function End Class
A.5
Tag 5
Quiz 1. Es gibt verschiedene Vorteile, aber der wichtigste ist, dass keine Distribution nötig ist, ehe die Anwendung verwendet werden kann oder um die Anwendung zu aktualisieren. Ein weiterer wichtiger Vorteil ist, dass Nicht-Windows-Plattformen auf Anwendungen mit einer Webschnittstelle zugreifen können. 2. Hierbei handelt es sich um ein einschichtiges System. Die unterschiedlichen Schichten der Anwendung basieren auf den Orten, an denen der Code und/oder die Verarbeitung erfolgt. In diesem Beispiel wird vom Server keine Verarbeitung übernommen, er speichert nur die Datei, die von der Client-Anwendung verwendet wird. Die gesamte Logik, Verarbeitung und Code-Ausführung spielt sich nur auf dem Client-System ab. 3. Die wichtigste Voraussetzung ist das .NET-Framework, das auf jedem System installiert sein muss, das. NET-Code ausführen wird. Dazu gehört auch der Server, der eine ASP.NET-Website ausführt.
712
Tag 6
A.6
Tag 6
Quiz Das LOKAL-Fenster, das ÜBERWACHEN-Fenster, das Pop-up-Fenster QUICKINFO, das IMMEDIATE-Fenster und das SCHNELLÜBERWACHUNG-Fenster.
Übung 1. Listing 6.11 zeigt eine mögliche Lösung.
Listing 6.11: Die fertige Multiplikationstabelle 1 Imports System 2 Imports Microsoft.VisualBasic.ControlChars 3 4 Module modTable 5 6 Sub Main() 7 Dim iLow, iHigh As Integer 8 Dim sInput As String 9 10 'Lasse mehrer Durchläufe der Tabellengenerierung zu. 11 Do 12 'Frage nach Werten. 13 Console.Write("Low value (maximum of 20, 0 to end): ") 14 sInput = Console.ReadLine() 15 iLow = CInt(sInput) 16 17 If iLow 0 Then 18 Console.Write("High value (maximum of 20): ") 19 sInput = Console.ReadLine() 20 iHigh = CInt(sInput) 21 22 OutputTable(iLow, iHigh) 23 End If 24 Loop Until iLow = 0 25 Console.Write("Press ENTER to continue")
713
Quiz-Anworten und Beispiellösungen für die Übungen
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 End
A.7
Console.ReadLine() End Sub Private Sub OutputTable(ByVal MinValue As Integer, _ ByVal MaxValue As Integer) Dim iCount, iCount2 As Integer Console.WriteLine() Console.WriteLine("Multiplication Table ({ 0} MinValue, MaxValue)
to { 1} )", _
'Schreibe Kopfzeile. For iCount = MinValue To MaxValue Console.Write(Tab & CStr(iCount)) Next Console.WriteLine() 'Schreibe jede Tabellenzeile. For iCount = MinValue To MaxValue For iCount2 = MinValue To MaxValue Console.Write(Tab & CStr(iCount * iCount2)) Next Console.WriteLine() Next End Sub Module
Tag 7
Quiz 1. Wenn eine Methode innerhalb einer Klasse als MustOverride gekennzeichnet ist, dann muss die Klasse als MustInherit gekennzeichnet sein. Das liegt daran, dass die erste Anforderung, dass alle Client-Klassen ihren eigenen Code für diese Methode implementieren müssen, das Schlüsselwort MustInherit erzwingt, da die Basisklasse die Methode nicht überschreiben darf. 2. Das Schlüsselwort MyBase gestattet es Ihnen, Elemente der Eltern- oder Basisklasse einer Klasse aufzurufen. Sie können MyBase.New() verwenden, um den Standard-Konstruktor Ihrer Basisklasse aufzurufen.
714
Tag 7
3. Zwei der überladenen Konstruktoren sind für den Visual Basic-Compiler letztendlich gleich und daher nicht zulässig: Public Sub New(ByVal sColor As String) m_dtManufactured = System.DateTime.Now() m_sColor = sColor End Sub Public Sub New(ByVal sName As String) End Sub
In diesem Fall hält der Visual Basic-Compiler die beiden Prozeduren für dieselben, da sie den gleichen Namen und die gleichen Parameter haben, und lässt daher nicht zu, dass beide nebeneinander existieren. Die Tatsache, dass ein Argument sColor und ein anderes sName heißt, ist in diesem Fall nicht relevant.
Übung Es gibt viele verschiedene Methoden, wie Sie dieses Beispiel erzeugen können. Hier sehen Sie eine mögliche Implementierung (siehe Abbildung 7.4): Namespace Biology Public Class Animal Public Overridable Property Name() As String Get End Get Set(ByVal Value As String) End Set End Property Public Overridable Property NumberOfLimbs() As Integer Get End Get Set(ByVal Value As Integer) End Set End Property Public Overridable Property Color() As String Get End Get Set(ByVal Value As String) End Set End Property Public Overridable Property Weight() As Single Get End Get Set(ByVal Value As Single)
715
Quiz-Anworten und Beispiellösungen für die Übungen
End Set End Property Public Overridable Property AirBreather() As Boolean Get End Get Set(ByVal Value As Boolean) End Set End Property End Class Public Class Reptile Inherits Animal End Class Public Class Mammal Inherits Animal Public Overrides Property AirBreather() As Boolean Get Return True End Get Set(ByVal Value As Boolean) Throw New System.Exception("All mammals breathe air") End Set End Property End Class Public Class Human Inherits Mammal Public Overrides Property NumberOfLimbs() As Integer Get Return 4 End Get Set(ByVal Value As Integer) End Set End Property End Class End Namespace
Machen Sie sich keine Sorgen, wenn Ihr Code nicht ganz diesem Beispiel entspricht, solange er läuft und Sie in der Lage waren, einige der Objektfunktionen zu verwenden, die in der Lektion am Tag 7 beschrieben wurden.
716
Tag 8
Abbildung 7.4: Der Object Browser zeigt die Klassen, die dieses Beispiel bilden, und ihre Beziehungen zueinander an.
A.8
Tag 8
Quiz 1. Sie könnten die Ausgabe oder Fehlerströme für Protokollzwecke in eine Datei umleiten. 2. Die Sammlung SortedList sortiert die Elemente in einer Liste automatisch, wenn Sie sie hinzufügen, sodass Sie nicht daran denken müssen, immer die Methode Sort aufzurufen. Die Sammlung ArrayList verfügt nicht über diese Funktion. 3. Es würde eine Zahl zwischen 2 und 12 auf der Konsole angezeigt werden.
Übung Um das beschriebene Konsolenprogramm zu erstellen, würden Sie die Methode GetCommandArgs der Klasse Environment und die Methode Sort der Klasse ArrayList verwenden. Sie würden die Namensräume System und System.Collections benötigen. Folgen Sie diesen Schritten: 1. Erstellen Sie eine neue Konsolenanwendung. Meine heißt SortedArgs.
717
Quiz-Anworten und Beispiellösungen für die Übungen
2. Ändern Sie den Namen des Moduls von Module1 in modMain. Speichern Sie die resultierende Datei als Main.vb. 3. Klicken Sie den Projektnamen im Solution Explorer mit der rechten Maustaste an und wählen Sie EIGENSCHAFTEN aus. Ändern Sie das Startobjekt in modMain. Wählen Sie OK aus, um das Dialogfenster zu schließen. 4. Die Methode GetCommandArgs der Klasse Environment gibt ein Zeichenketten-Array der Argumente zurück, die in der Befehlszeile aufgelistet waren (einschließlich des Namens der Anwendung). Sie können damit eine neue ArrayList erzeugen und die eingebauten Funktionen verwenden, um Ihre Liste zu sortieren. 5. Da sich der Namen der Anwendung in Ihrer ArrayList befindet, sollten Sie ihn entfernen, bevor Sie die Liste sortieren. 6. Nachdem die Liste sortiert ist, können Sie die Methode Console.WriteLine verwenden, um die Ergebnisliste anzuzeigen. Sie werden eine For...Next-Schleife verwenden, um alle Elemente in der Liste zu betrachten. Den vollständigen Quellcode für das Beispiel sehen Sie in Listing 8.4.
Listing 8.4: Die Befehlszeilenargumente sortieren 1 Module modMain 2 Sub Main() 3 Dim oArgs As New ArrayList(Environment.GetCommandLineArgs()) 4 Dim I As Integer 5 With oArgs 6 .RemoveAt(0) 7 .Sort() 8 For I = 0 To .Count – 1 9 Console.WriteLine(.Item(I)) 10 Next 11 End With 12 End Sub 13 End Module
Listing 8.4 ist kurz, aber es zeigt eine Vielfalt der Techniken zum Bearbeiten von Sammlungen und zum Abrufen von Befehlszeilenargumenten.
718
Tag 9
Die Zeile 3 erzeugt eine neue ArrayList, indem sie das Zeichenketten-Array kopiert, das von der Methode GetCommandLineArgs zurückgegeben wird. Diese Technik steht für viele der Sammlung zur Verfügung, die in System.Collections definiert sind. Sie ermöglicht es Ihnen, ein bestehendes Array zu nehmen, es in die Sammlung umzuwandeln (ArrayList, Queue oder Stack) und so zusätzlich das Verhalten der Sammlung zu bekommen. In diesem Fall ermöglicht die Technik es Ihnen, das unerwünschte Element (Zeile 6) zu entfernen und die verbleibenden Elemente zu sortieren (Zeile 7). Der Code in den Zeilen 8 bis 10 zeigt schließlich das Ergebnis-Array an. Das Ausführen der Anwendung sollte die folgende Ausgabe erzeugen: 1 2 3 4 5 6 7 8 9 10 11 12
A.9
SortedArgs Asimov Heinlein Bradley Niven _ Pournelle Clarke Herbert Card LeGuin Haldeman Asimov Bradley Card Clarke Haldeman Heinlein Herbert LeGuin Niven Pournelle
Tag 9
Quiz 1. Wenn ein Fenster modal angezeigt wird, kann der Anwender erst mit einem anderen Teil der Anwendung interagieren, wenn er das Fenster geschlossen hat. In dem Code, der das modale Formular anzeigt, wird die Ausführung angehalten, bis das Formular verborgen oder geschlossen wird. Wenn ein nicht-modales Formular angezeigt wird, kann der Anwender nicht davon abgehalten werden, mit anderen Teilen der Anwendung weiterzuarbeiten, und der Code, der das Formular anzeigt, läuft weiter, ohne darauf zu warten, dass das Formular geschlossen wird. 2. Sie setzen die Eigenschaft CausesValidation auf True, um anzuzeigen, dass Sie sicherstellen möchten, dass die Dateneingabefelder gültige Daten enthalten, wenn der Anwender sich zu diesem Steuerelement begibt. Wenn Sie diese Eigenschaft bei einer
719
Quiz-Anworten und Beispiellösungen für die Übungen
CANCEL-Schaltfläche setzen, muss der Anwender gültige Informationen auch dann eingeben, wenn er den Prozess nur abbrechen möchte! 3. Die Handles-Anweisung, gefolgt von einem oder mehreren besonderen Ereignisnamen, wird verwendet, um anzuzeigen, dass eine Prozedur ein Ereignisbehandler ist. Handles btnOpen.Click am Ende einer Prozedur würde anzeigen, dass die Prozedur immer dann aufgerufen wird, wenn das Ereignis Click von btnOpen eintritt (wenn der Anwender btnOpen anklickt).
Übung Um die drei Ereignisse in einer einzigen Routine zu behandeln, müssen Sie feststellen, welches Ereignis eingetreten ist, als die Routine aufgerufen wurde. Indem Sie den SenderParameter in ein System.Windows.Forms.Control-Objekt umwandeln, können Sie auf seine Eigenschaft Name zugreifen, die Ihnen den Namen des Steuerelements liefert, das die Quelle des Ereignisses ist. Die überarbeitete Ereignisprozedur, die die anderen drei ersetzt, sehen Sie in Listing 9.21. Listing 9.21: Ereignisprozeduren 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
Private Sub DoEverything(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles btnCopy.Click, btnMove.Click, btnDelete.Click Dim sEventSource As String _ = CType(sender, Control).Name Dim sSource As String Dim sDestination As String sSource = txtSource.Text() sDestination = txtDestination.Text() Select Case sEventSource Case "btnCopy" File.Copy(sSource, sDestination) Case "btnMove" File.Move(sSource, sDestination) Case "btnDelete" File.Delete(sSource) Case Else 'Tue nichts. End Select End Sub
720
Tag 10
A.10 Tag 10 Quiz 1. Mit Label-Steuerelementen haben Sie die Möglichkeit, den Text später dynamisch zu ändern. 2. Die Steuerelemente LinkButton und HyperLink. 3. Sie verwenden entweder das Steuerelement Image oder ImageButton (wenn die Grafik anklickbar sein soll) oder wählen einfach aus dem Menü INSERT und IMAGE aus.
Übung Wegen der Bedingungen des Formulars sollten Sie die Validator-Steuerelemente verwenden. Sie fügen sie zu jedem Steuerelement hinzu. Bei den meisten wird es sich um RequiredFieldsValidatoren handeln, aber Sie benötigen auch einen CompareValidator, um sicherzustellen, dass die beiden Passwörter übereinstimmen. Außerdem werden Sie sehen, wie eine Passwortfeld erzeugt wird. Erstellen Sie eine neues Projekt (oder fügen Sie ein neues Formular zu einem bestehenden Projekt hinzu). Fügen Sie eine neue Tabelle (TABELLE/EINFÜGEN/TABELLE) mit drei Spalten und sechs Zeilen zu dem Formular hinzu. In Abbildung 10.9 sehen Sie Einzelheiten. Fügen Sie drei Beschriftungen zu dem neuen Formular hinzu, eine in jeder der ersten drei Zeilen der ersten Spalte. Fügen Sie entsprechend vier TextBox-Steuerelemente zu den ersten vier Zeilen der zweiten Spalte hinzu. Das Endergebnis sollte so ähnlich wie in Abbildung 10.10 aussehen. Um die zweite Spalte zu vervollständigen, fügen Sie ein Button- und ein weiteres LabelSteuerelement hinzu. Schließlich fügen Sie zu der dritten Spalte (von oben nach unten) drei RequiredFieldValidatoren und einen CompareValidator hinzu. Setzen Sie die Eigenschaften der Steuerelemente so, wie in Tabelle 10.6 gezeigt. Nun sind Sie bereit, den Code hinzuzufügen. Sie müssen wieder keinen Code für die Validierung hinzufügen, da das die Steuerelemente übernehmen. Daher ist nur wenig zusätzlicher Code nötig. Sie müssen nur Code für den Fall hinzufügen, dass der Anwender die Schaltfläche REGISTER anklickt. Dieser wird nur dann ausgeführt, wenn alle Fehler geklärt wurden. Der Code zeigt einfach eine Nachricht im Summary-Label an. Klicken Sie die Schaltfläche REGISTER doppelt an und fügen Sie den Code in Listing 10.5 hinzu.
721
Quiz-Anworten und Beispiellösungen für die Übungen
Abbildung 10.9: Das Dialogfenster TABELLE EINFÜGEN
Abbildung 10.10: Das Webformular in Aktion Steuerelement
Eigenschaft
Wert
Label
(ID)
lblName
Text
Name:
Font Bold
True
Tabelle 10.6: Eigenschaften des Registrierungsformulars
722
Tag 10
Steuerelement
Eigenschaft
Wert
Label
(ID)
lblAlias
Text
Alias:
Font Bold
True
(ID)
lblPassword
Text
Password:
Font Bold
True
TextBox
(ID)
txtName
TextBox
(ID)
txtAlias
MaxLength
10
(ID)
txtPassword1
TextMode
Password
(ID)
txtPassword2
TextMode
Password
(ID)
cmdRegister
Text
Register
(ID)
lblSummary
BackColor
#E0E0E0
BorderColor
Silver
BorderStyle
Groove
BorderWidth
1px
Text
(ID)
reqName
ControlToValidate
txtName
ErrorMessage
Name is a required field.
Label
TextBox
TextBox
Button
Label
Validator
Validator
Validator
CompareValidator
(ID)
reqAlias
ControlToValidate
txtAlias
ErrorMessage
Alias is a required field.
(ID)
reqPassword
ControlToValidate
txtPassword1
ErrorMessage
You must enter a password.
(ID)
cmpPassword
Tabelle 10.6: Eigenschaften des Registrierungsformulars (Forts.)
723
Quiz-Anworten und Beispiellösungen für die Übungen
Steuerelement
Eigenschaft
Wert
ControlToCompare
txtPassword1
ControlToValidate
txtPassword2
ErrorMessage
Two passwords must match.
Tabelle 10.6: Eigenschaften des Registrierungsformulars (Forts.)
Listing 10.5: Code für die Schaltfläche REGISTER 1 Public Sub cmdRegister_Click(ByVal sender As Object, ByVal e As System.EventArgs) 2 lblSummary.Text = "Welcome, " & txtAlias.Text 3 End Sub
Erstellen Sie das Projekt und führen Sie es aus. Wenn der Browser zur Verfügung steht, geben Sie unterschiedliche Werte für die Felder ein. Löschen Sie die Eingabe, die Sie in den Feldern Name und Alias gemacht haben. Es sollte einen Fehlermeldung für diese Steuerelemente erscheinen. Geben Sie zwei unterschiedliche Passwörter ein, um den CompareValidator in Aktion zu sehen (siehe Abbildung 10.11). Geben Sie schließlich Werte für Name, Alias und übereinstimmende Passwörter ein, um das abschließende Ergebnis zu sehen (siehe Abbildung 10.12).
Abbildung 10.11: Das Registrierungsformular mit Fehlern
724
Tag 11
Abbildung 10.12: Das vollständige Registrierungsformular
A.11 Tag 11 Quiz 1. Das Feld (oder die Felder) mit denen ein Datenbankdatensatz eindeutig identifiziert wird, wird Primärschlüssel genannt. 2. Diese SQL-Anweisung führte einen LEFT OUTER JOIN zwischen diesen beiden Tabellen aus, der angibt, dass alle Datensätze von der Make-Tabelle und die Datensätze der Modell-Tabelle, die mit der Join-Bedingung übereinstimmen, in der Ausgabe enthalten sein sollen. Das folgende Ergebnis wird dabei erzeugt: Marke
Modell
Ford
Mustang
Ford
Explorer
Audi
BMW
Pontiac
Grand Am
Pontiac
Grand Prix
725
Quiz-Anworten und Beispiellösungen für die Übungen
Marke
Modell
Pontiac
Aztek
Toyota
Rav 4
Toyota
Camry
3. Diese SQL-Anweisung gibt einen INNER JOIN an, die gängigste Art eines Joins. Die Anweisung erzeugt Ergebnisse, in denen nur die Datensätze aus beiden Tabellen enthalten sind, in denen die Join-Bedingung erfüllt wurde, wie Sie hier sehen: Marke
Modell
Ford
Mustang
Ford
Explorer
Pontiac
Grand Am
Pontiac
Grand Prix
Pontiac
Aztek
Toyota
Rav 4
Toyota
Camry
Beachten Sie, dass Audi und BMW, für die es keine Datensätze in der Modell-Tabelle gab, nicht in der Ausgabe der zweiten SQL-Anweisung enthalten sind.
Übungen 1. Es gibt viele verschiedene Methoden, wie Sie Ihre CD-Datenbank so erweitern können, dass sie mehrere Anwender handhabt. Hier sehen Sie die einfachste: Fügen Sie als Erstes eine User-Tabelle hinzu, die ein Feld UserID als Primärschlüssel enthält. Welche anderen Felder in dieser Tabelle enthalten sind, ist größtenteils irrelevant, aber voraussichtlich werden Informationen wie der Name des Anwenders, seine E-Mail-Adresse und vielleicht ein Passwort benötigt, damit Sie die Sicherheit verstärken können und Anwender davon abhalten, die CD-Sammlung von anderen zu bearbeiten. Gehen Sie als Nächstes davon aus, dass Sie nur einen Eintrag in der Artist-Tabelle für Sting haben möchten und nur einen Eintrag in Disc für diese bestimmte CD, wenn zwei Anwender beide das Album »Fields Of Gold« von Sting haben. Mit diesen Voraussetzungen haben Sie am Ende eine Viele-zu-viele-Beziehung zwischen User und Disc, die in einer UserDisc-Beziehungstabelle ausgedrückt werden kann. Die veränderte Datenbank sehen Sie in Abbildung 11.7.
726
Tag 11
User
Artist PK
KeyManager PK
PK
ArtistID ArtistName ArtistFirstName ArtistLastName
TableName HighestID
User/Disc PK PK
Disc PK
DiscID UserID
DiscID ArtistID CDTitle CDReleaseYear
Abbildung 11.7: Um aus Ihrer CDDatenbank ein Multiuser-System zu machen, müssen Sie nur zwei Tabellen hinzufügen.
Category PK
UserID Name Email Phone
CategoryID CategoryName
DiscCategory PK PK
DiscID CategoryID
2. So hässlich die Daten in Orders.txt auch aussehen mögen, es ist recht üblich, dass Sie Daten in diesem Format erhalten, besonders wenn sie von einer Tabellenkalkulation kommen. Um diese in eine effiziente Datenbank zu setzen, müssen Sie die CustomerInformationen aus den Order-Informationen herausziehen und auch die individuellen Bestellartikel (was ist, wenn ein Kunde vier Artikel in der gleichen Bestellung bestellen möchte?) in ihre eigene Tabelle setzen. Unterwegs können Sie direkt einige berechnete Felder (Order Total, ItemX Total Cost) entfernen, die statt dessen als Teil einer Anfrage abgerufen werden können. Dieses Datenbanklayout sehen Sie in Abbildung 11.8. Customer PK
CustomerID CustomerName
Order PK
OrderID CustomerID OrderDate ShipDate
OrderItem PK
OrderID OrderItemID Item SKU Item Cost Item Quantity
Abbildung 11.8: Die restrukturierte Order-Datenbank
727
Quiz-Anworten und Beispiellösungen für die Übungen
Wenn Sie möchten, können Sie diese Datenbank noch erweitern und eine ItemsTabelle erzeugen, um einen Datensatz für jede SKU zusammen mit einem Preis und der Artikelbeschreibung zu speichern, und diese Felder aus der Tabelle OrderItem entfernen. Beachten Sie, dass es in einer Bestelleingabedatenbank manchmal nötig ist, ein wenig Duplizität zu haben, z.B. die Artikelkosten zusammen mit den Bestellartikeln zu speichern, damit der Artikelpreis mit der Zeit geändert werden kann, ohne die Informationen in älteren Bestellungen ändern zu müssen.
A.12 Tag 12 Quiz 1. Die Eigenschaft ist MissingSchemaAction und die Einstellung, um sicherzustellen, dass Primärschlüsselinformationen erzeugt werden, ist AddWithKey. 2. Delete kennzeichnet die Zeile als gelöscht, entfernt sie aber nicht aus der Tabelle. Sie bleibt dort, damit die Routine Update weiß, dass sie diese Zeile aus der Datenbank entfernen muss. Remove entfernt einfach dieses DataRow-Objekt aus der Rows-Sammlung, es wird aber nicht aus der Datenbank entfernt, wenn Update aufgerufen wird. 3. Dies ist ein Befehl, der keine Datensätze zurückgibt, sodass Sie ihn mit der Methode ExecuteNoQuery des Befehlsobjekts aufrufen sollten. Ihr Code sollte so aussehen: Sub Main() Dim sSQL As String sSQL = "DELETE FROM Artist Where ArtistID=3" Dim objConn As New _ System.Data.OleDb.OleDbConnection(sConnection) Dim objCmd As New _ System.Data.OleDb.OleDbCommand(sSQL, objConn) objConn.Open() objCmd.ExecuteNonQuery() End Sub
Übung Sie können eine Benutzeroberfläche für Ihre CD-Bibliothekdatenbank auf viele verschiedene Arten erstellen, aber die wichtigen Bestandteile sind in jedem Fall die gleichen. Den gesamten Code für eine Beispielbenutzeroberfläche finden Sie auf der CD zu diesem Buch.
728
Tag 13
A.13 Tag 13 Quiz 1. Geladene Module, Prozesse (und Sie finden sie auch unter Management-Informationen) 2. Ein Dienst ist ein Programm, das auf Ihrem Computer im Hintergrund läuft und einige Funktionen bietet. Zum Beispiel bietet SQL Server Datenbank-Funktionen. 3. Der aktuelle Wert für die CPU-Belegung (% Prozessorzeit) wird in der Konsole ausgegeben.
A.14 Tag 14 Quiz 1. MyBase bietet die gewünschte Funktion und kann verwendet werden, um auf Eigenschaften oder Methoden der Basisklasse zu verweisen. 2. Das Erzeugen von mehreren Versionen der gleichen Prozedur, aber mit unterschiedlichen Parametern, wird Überladen genannt. 3. Die Ausgabe ist: x.Name = Fred y.Name = Fred
Sowohl x als auch y verweisen auf die gleiche Instanz von myFirstClass und x.Name = y.Name ändert nichts, da es äquivalent zu x.Name = x.Name ist.
Übung Eine mögliche Entwurfsentscheidung wäre es, die verschiedenen Ereignisarten mit einer Objekthierarchie zu behandeln, statt nur eine Kategorie oder Typeigenschaft für eine einzelne Event-Klasse zu verwenden. Sie könnten eine elementare Event-Klasse erzeugen, mit einigen gängigen Eigenschaften wie dem Ereignisnamen und vielleicht den Kontaktinformationen für dieses Ereignis. Abgeleitet von dieser Basisklasse könnten Sie Klassen wie SportingEvent (erbt von Event), ConcertEvent, ComedyEvent, PublicSpeakingEvent usw.
729
Quiz-Anworten und Beispiellösungen für die Übungen
erstellen. Diese Hierarchie könnte noch weiter geführt werden, zu Klassen wie BaseballGame (erbt von SportingEvent), PoliticalEvent (erbt von PublicSpeakingEvent) usw. Die einzelnen Eigenschaften der verschiedenen abgeleiteten Klassen können beliebig ausführlich beschrieben werden, aber eine Wiederholung sollte vermieden werden. Wenn zwei Klassen ähnliche Eigenschaften haben, sollten Sie in Erwägung ziehen, die allgemeinen Eigenschaften in eine Basisklasse zu verschieben.
A.15 Tag 15 Quiz 1. Schnittstellen bieten diese Art der Funktionalität und erlauben es Ihnen, anzugeben, dass viele unterschiedliche Klassen gemeinsame Funktionen oder Fähigkeiten haben. Schnittstellen sind für diese Aufgabe besonders gut geeignet, da eine einzelne Klasse viele unterschiedliche Schnittstellen implementieren kann. 2. MustOverride kann verwendet werden, um anzuzeigen, dass eine bestimmte Methode oder Eigenschaft in einer abgeleiteten Klassen überschrieben werden muss. 3. MustInherit macht aus einer Basisklasse eine »abstrakte« Klasse, also eine, die nicht selbst instanziiert werden kann, sondern Unterklassen haben muss, die von ihr abgeleitet sind. 4. Klassen, die als NotInheritable gekennzeichnet sind, sind auch als »Sealed«-Klassen bekannt. Sie können nicht als Basisklasse verwendet werden (denn andere Klassen können nicht von ihnen erben).
Übung Es gibt viele verschiede Möglichkeiten, wie Sie eine Objekthierarchie erstellen könne, die Customer umfasst. Die Abbildung 15.4 stellt ein Beispiel dar. In diesem Fall wurde die Klasse Customer von einer allgemeineren Person-Klasse abgeleitet, und der Aspekt Contact Information wurde direkt aus der ursprünglichen Basisklasse herausgelöst. Einen alternativen Entwurf sehen Sie in Abbildung, 15.5 die zeigt, wie eine einfachere Version der gleichen Informationen strukturiert werden kann.
730
Tag 15
Person Class First Name Last Name Contact Info ID Date of Birth Social Security Number
Contact Info Class enthält eine Instanz von
Customer Class Inherits Person erbt
Street Address City State Postal code Country Phone Fax Email Address
First Name Last Name Contact Info ID Date of Birth Social Security Number Government Customer Class
Customer ID Customer Since (date) Customer Category
Inherits Customer
erbt
First Name Last Name Contact Info ID Date of Birth Social Security Number Customer ID Customer Since (date) Customer Category Government Agency Government Order ID
Abbildung 15.4: Das Entwerfen von Klassen für Wiederverwendbarkeit kann manchmal zu einer komplexeren Architektur führen.
Customer Class Customer ID Customer Since (date) Customer Category First Name Last Name Date of Birth Social Security Number Street Address City State Postal Code Country Phone Fax Email Address
erbt
Government Customer Class Inherits Customer Customer ID Customer Since (date) Customer Category First Name Last Name Date of Birth Social Security Number Street Address City State Postal Code Country Phone Fax Email Address Government Agency Government Order ID
Abbildung 15.5: Die gleichen Informationen können auf mehrer Arten gebildet werden.
731
Quiz-Anworten und Beispiellösungen für die Übungen
A.16 Tag 16 Quiz Verwenden Sie die Methode LayoutMdi des MDI-Elternfomulars, um das Layout auf MdiLayout.Cascade zu setzen. Damit werden die Kindfenster so angeordnet, dass alle Titelleisten sichtbar sind.
Übungen 1. Es ist im Grunde eine Sache der persönlichen Vorlieben. Im Allgemeinen ziehe ich SDI vor, da ich dann die einzelnen Fenster in der Taskleiste sehen kann. Bei einigen Anwendungen, die möglicherweise mehrere Dateien öffnen, wie z.B. grafische Anwendungen, ziehen ich MDI vor, um alle Grafiken an einer Stelle zu haben. 2. Die Methoden GetAttributes und GetLastWriteTime der Klasse File sind nützlich, wenn Sie diese Informationen von einer Datei abrufen. Beide sind Shared-Methoden der Klasse File und daher müssen Sie keine Instanz einer Datei erzeugen, bevor Sie sie verwenden. Um diese Funktionalität hinzuzufügen, können Sie entweder die ursprüngliche Pioneer-Anwendung verändern oder alle Dateien aus dem Pioneer-Verzeichnis in eine neues Verzeichnis kopieren und diese bearbeiten. Ändern Sie die Eigenschaft View von ListView in Detail. Damit aktivieren Sie die Funktion, mit der sie mehrere Spalten zur ListView hinzufügen können. Fügen Sie mit dem Eigenschaftseditor von Columns drei Spalten zur ListView hinzu. Die Tabelle 16.17 beschreibt die Eigenschaften für diese Spalten. Spalte
Eigenschaft
Wert
erste
Name
hdrName
Text
File Name
Width
180
Name
hdrAttributes
Text
Attributes
TextAlign
Center
Name
hdrModified
zweite
dritte
Tabelle 16.17: Die Eigenschaften von Columns
732
Tag 16
Spalte
Eigenschaft
Wert
Text
Last Modified
TextAlign
Right
Width
80
Tabelle 16.17: Die Eigenschaften von Columns (Forts.)
Der einzige Code, der notwendig ist, dient dazu, die Informationen zu den Spalten für die Spalten Attributes und Last Modified hinzuzufügen. Verändern Sie den Ereignisbehandler tvwFolders_AfterSelect so, dass er diese Spalten erzeugt (siehe Listing 16.8). Listing 16.8: Zusätzliche Spalten erzeugen 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
Private Sub tvwFolders_AfterSelect(ByVal sender As Object, _ ByVal e As System.Windows.Forms.TreeViewEventArgs) _ Handles tvwFolders.AfterSelect Dim sFiles() As String = _ Directory.GetFiles(tvwFolders.SelectedNode.FullPath) Dim sFile As String Dim oItem As ListViewItem lvwFiles.Items.Clear() For Each sFile In sFiles oItem = lvwFiles.Items.Add(StripPath(sFile)) If lvwFiles.Items.Count > 0 Then oItem.SubItems.Add(GetAttributeString(sFile)) oItem.SubItems.Add(File.GetLastWriteTime(sFile)) End If Next End Sub
Der zusätzliche Code ist die Deklaration von ListViewItem in der Zeile 8 und der Code in den Zeilen 13 bis 17. Wenn Sie ein neues ListViewItem zu einer ListView hinzufügen, wird das hinzugefügte Element von der Methode Add zurückgegeben. Wenn Sie einfach ein Element zu der Liste hinzufügen, wie wir es bisher getan haben, ist es nicht wichtig, es in einer neuen Variablen zu speichern. Da Sie zu diesem neuen Element aber Unterelemente hinzufügen werden, sollten Sie es in einer Variablen speichern (siehe Zeile 13). Nachdem Sie das Element hinzugefügt haben, verwenden Sie die Eigenschaft SubItems von jedem ListViewItem, um neue Werte für die weiter oben hinzugefügten Spalten hinzuzufügen. Der Code ruft zuerst die Funktion GetAttribute-
733
Quiz-Anworten und Beispiellösungen für die Übungen
String auf, die Sie gleich erstellen werden, um die Zeichenkette hinzuzufügen, die die Attribute für jede Datei darstellt. Dann fügt die Zeile 16 das Datum und die Uhrzeit, als zum letzten Mal in die Datei geschrieben wurde, in die Spalte Last Modified ein.
Zum Schluss sollten Sie eine Routine erstellen, die die Attribute anzeigt, die der Datei zugewiesen sind. Fügen Sie dafür den Code in Listing 16.9 hinzu. Listing 16.9: Die Funktion GetAttributeString 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
Private Function GetAttributeString(ByVal fileName As String) _ As String Dim sReturn As String Dim oAttr As FileAttributes = File.GetAttributes(fileName) If (oAttr And FileAttributes.ReadOnly) = _ FileAttributes.ReadOnly Then sReturn = "R" Else sReturn = "-" End If If (oAttr And FileAttributes.Hidden) = _ FileAttributes.Hidden Then sReturn += "H" Else sReturn += "-" End If If (oAttr And FileAttributes.System) = _ FileAttributes.System Then sReturn += "S" Else sReturn += "-" End If Return sReturn End Function
Die Funktion GetAttributeString ruft die Attribute für jede Datei ab und wandelt sie für die Betrachtung in eine Zeichenkette um. Der Rückgabewert der Methode File.GetAttributes ist ein FileAttributes-Wert, der die Summe aller Attribute enthält, die der Datei zugewiesen sind (Zeile 23). Die If-Anweisung
734
Tag 16
in den Zeilen 25 und 26 testet diesen Wert, um zu sehen, ob er das Attribut ReadOnly enthält. Dieser Test verwendet den logischen Operator And, um den vollen Wert, der alle Attribute enthält, mit dem Attribut FileAttributes.ReadOnly zu vergleichen. Wenn der Wert FileAttributes.ReadOnly ein Teil des Ganzen ist, gibt dieser Test den Wert FileAttributes.ReadOnly zurück. Das heißt, dass der Rückgabewert der Wert ist, den beide gemeinsam haben, wenn Sie zwei Zahlen mit dem And-Operator verbinden, oder 0, wenn sie nichts gemeinsam haben. Zum Beispiel gibt 33 And 1 1 zurück, aber 33 And 2 gibt 0 zurück. Wenn für die Datei das Attribut ReadOnly gesetzt ist, setzt der Code ein R in die Zeichenkette, andernfalls setzt er einen Gedankenstrich ein. Die Tests auf die Attribute Hidden und System sind identisch, mit Ausnahme des Tests auf das gewünschte Attribut. Schließlich wird in der Zeile 46 die gesamte Zeichenkette zurückgegeben, um sie zur Spalte ListView hinzuzufügen.
Der Rückgabewert von GetAttributes ist eine Bitmaske aller Attribute einer Datei. Eine Bitmaske ist der Begriff, der für einen Wert verwendet wird, bei dem jedes Bit im Wert als Flag verwendet wird, um Eigenschaften zu kennzeichnen. Wenn Sie sich z.B. einige der tatsächlichen Werte für jede der FileAttributes-Aufzählungen ansehen (siehe Tabelle 16.18), sehen Sie, dass es zwischen den Werten Lücken gibt. Element
Wert
ReadOnly
1
Hidden
2
System
4
Directory
16
Archive
32
Device
64
Normal
128
Temporary
256
Sparse File
512
Reparse Point
1024
Compressed
2048
Offline
4096
Not Content Indexed
8192
Encrypted
16384
Tabelle 16.18: Mögliche FileAttribute-Werte
735
Quiz-Anworten und Beispiellösungen für die Übungen
Wie Sie sehen können, ist jeder Wert immer das Doppelte des vorherigen. Das bedeutet, dass Sie mehrere Flags in die gleichen Integer- (oder Long-)Variable packen können, ohne dass sie sich gegenseitig beeinflussen. Jede verwendet die nächste Potenz von zwei, um den gewünschten Wert darzustellen. Wenn eine Datei z.B. schreibgeschützt ist und das Archiv auf True gesetzt ist, wäre der Rückgabewert 33: Power of 2: Bit: Value:
7 6 5 4 3 2 1 0 0 0 1 0 0 0 0 1 32 1
FileAttributes.Archive hat den Wert 32, während der Wert für FileAttributes.ReadOnly 1 ist. Daher würde eine Datei, bei der diese beiden Attribute gesetzt sind, von GetAttributes 33 zurückgeben.
Bitmasken werden häufig als Strategie verwendet, wenn Sie viele Flags in einem relativ kleinen Speicher bereitstellen müssen. An die Bitarithmetik mit And, Or und Xor muss man sich erst gewöhnen, aber es ist einfach, wenn sie die Aufgabe in Zweierpotenzen aufteilen. Führen Sie das veränderte Programm aus. Sie sollten sich den Status der Dateiattribute Hidden, ReadOnly und System ansehen, zusammen mit den Dateinamen und den Daten, wann sie zuletzt verändert wurden (siehe Abbildung 16.18).
Abbildung 16.18: Die veränderte Pioneer-Anwendung ausführen
736
Tag 17
A.17 Tag 17 Quiz 1. Die richtige Antwort ist C. Die Methode Exists ist eine Shared-Methode des Objekts File. Sie müssen keine Instanz der Klasse File erzeugen, ehe Sie die Methode verwenden. 2. Stream ist eine abstrakte Klasse, die einen Informationsfluss darstellt. StreamReader ist ein einfaches Mittel, um etwas zum Strom hinzuzufügen, während StreamWriter verwendet wird, um in den Strom zu schreiben. 3. Sie können viele Dateiformate im Steuerelement Image erzeugen und verwenden, darunter auch BMP-, GIF-, JPG-, PNG- und TIF-Dateien. 4. Point beschreibt einen Ort im Raum. Size definiert Höhe und Breite. Rectangle besteht aus Size an einem bestimmten Point. Das heißt, dass es eine Höhe und Breite hat und sich an einem bestimmten Ort befindet.
Übungen 1. Das Ereignis Click für das Toplevel-Bearbeitungsmenü ist ein guter Ort, um das Menü vorzubereiten, ehe Sie es dem Anwender anzeigen, wie in Listing 17.12 gezeigt. So können Sie das Menü individuell zuschneiden und die Eigenschaft Enabled der Befehle AUSSCHNEIDEN, KOPIEREN und EINFÜGEN nach Bedarf setzen. Wenn sich z.B. nichts in der Zwischenablage befindet, sollte der Befehl EINFÜGEN nicht aktiviert sein.
Listing 17.12: Das Bearbeitungsmenü dynamisch aktualisieren 1 2 3 4 5 6 7 8
Private Sub mnuEdit_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles mnuEdit.Click 'Gibt es ausgewählten Text? If Me.txtText.SelectedText.Length > 0 Then mnuEditCut.Enabled = True mnuEditCopy.Enabled = True
737
Quiz-Anworten und Beispiellösungen für die Übungen
9 10 11 12 13 14 15 16 17 18 19 20 21
Else mnuEditCut.Enabled = False mnuEditCopy.Enabled = False End If 'Gibt es etwas in der Zwischenablage, das wir hier einfügen können? If Clipboard.GetDataObject.GetDataPresent(DataFormats.Text) Then mnuEditPaste.Enabled = True Else mnuEditPaste.Enabled = False End If End Sub
Der Code im Ereignis mnuEdit_Click wird ausgeführt, ehe das Menü angezeigt wird. Diese Routine hat zwei Teile. Der erste (Zeilen 5 bis 12) bestimmt, ob die Menüpunkte AUSSCHNEIDEN und KOPIEREN aktiviert sein sollen. Wenn sich in der txtText-TextBox ausgewählter Text befindet, ist der Test in Zeile 6 True. Damit werden die Menüpunkte AUSSCHNEIDEN und KOPIEREN aktiviert. Der Text in Zeile 15 ist komplexer. Denken Sie daran, dass die Klasse Clipboard zwei Methoden hat: GetDataObject und SetDataObject. Hier rufen wir GetDataObject auf, die das Objekt zurückgibt, das die Daten der Zwischenablage enthält. Das Objekt hat eine Methode namens GetDataPresent, mit der das DataObject abgefragt wird, um zu bestimmen, ob es irgendetwas in einem bekannten Format enthält. Ist dies der Fall, dann fragen wir es ab, um zu sehen, ob es Text enthält. Wenn es Text gibt, können wir den Menüpunkt EINFÜGEN aktivieren, da dies ein annehmbares Format für die TextBox ist. Wenn sich statt einfachem Text so etwas wie ein Word-Dokument in der Zwischenablage befinden würde, wäre auch das annehmbar, da das DataObject zwischen den beiden Arten konvertieren kann. Wenn aber eine Grafik in der Zwischenablage wäre, würde die Methode GetDataPresent False zurückgeben, da es keine Möglichkeit gibt, eine Grafik in Text umzuwandeln. 2. Das ist von Fall zu Fall unterschiedlich. Im Allgemeinen sind aber GIF und PNG die kleinsten Dateiformate für einfache Grafiken mit wenigen Farben, während JPEG beim Speichern von Fotos das kleinste ist.
738
Tag 18
A.18 Tag 18 Quiz 1. Aus dem Namen lässt sich nur schließen, dass die Variable vermutlich etwas mit dem Herstellen einer Datenbankverbindung zu tun hat und dass es sich um eine Zeichenkette handelt (durch das vorangestellte s). 2. Um Ihren Quellcode zu schützen, um die Arbeit von mehreren Programmierern am gleichen Projekt zu koordinieren und um Veränderungen zu verfolgen, die im Laufe der Zeit am Code vorgenommen wurden. 3. Diese Schaltfläche heißt ROLLBACK. Mit ihr können Sie zu jedem früheren Zeitpunkt zurückkehren, zu dem Sie Code eingegeben haben.
A.19 Tag 19 Quiz 1. Sie müssen nur eine einzige Installation erstellen, die das .NET-Framework enthält. Diese wird dann zur Laufzeit feststellen, ob das Framework auf dem Zielrechner installiert werden muss. 2. Wenn alle Komponenten der Anwendung auf dem gleichen Zielrechner installiert werden sollen, reicht ein Setup vollkommen aus. Wenn die Bibliothekskomponenten auf einem Server installiert werden sollen, während das Hauptprojekt auf dem Rechner des Anwenders installiert ist, dann sollten Sie separate Installationsversionen für die beiden Projektgruppen erstellen. 3. Fortgeschrittene Installationsoptionen und -funktionen stehen über die VS.NETSetup-Werkzeuge nicht zur Verfügung. Sie müssen Installations-Software von Drittanbietern verwenden, wie z.B. Install Shield und Wise Installer. 4. Im Setup Wizard haben Sie die Möglichkeit, zusätzliche Dateien zu Ihrem Setup hinzuzufügen. Diese Dateien werden standardmäßig im Anwendungsverzeichnis installiert. Sie können auch nach Abschluss des Wizard Dateien hinzufügen, indem Sie sie einfach zum Projekt hinzufügen (klicken Sie das Setup-Projekt mit der rechten Maustaste an und wählen Sie ADD und FILE aus dem Kontextmenü aus).
739
Quiz-Anworten und Beispiellösungen für die Übungen
Übung Um diese Übung abzuschließen, können Sie mit mir die erforderlichen Schritte durchgehen: 1. Erzeugen Sie eine neue Visual Basic .NET-Windows-Anwendung. 2. Erzeugen Sie eine .config-Datei und setzen Sie sie in das bin-Verzeichnis Ihres neuen Projekts. Die Konfigurationsdatei muss nicht viel enthalten, nur eine einzige Einstellung, die wir für den Windows-Titel verwenden werden:
3. Fügen Sie den folgenden Code zum Konstruktor Ihres Formulars hinzu (die Subroutine New()), um die Titelwerte zu laden und sie der Eigenschaft Text des Formulars zuzuweisen. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Public Sub New() MyBase.New() 'Dieser Aufruf wird vom Windows Form Designer gefordert. InitializeComponent() 'Fügen Sie die Initialisierung nach dem Aufruf von InitializeComponent() ein. Me.Text = GetTitle() End Sub Private Function GetTitle() As String Dim sTitle As String sTitle = _ Configuration.ConfigurationSettings.AppSettings.Get("Title") Return sTitle End Function
4. Führen Sie das Projekt aus, um es zu testen. 5. Fügen Sie nun zu Ihrer Lösung ein Setup-Wizard-Projekt hinzu und führen Sie die entsprechenden Schritte aus. Akzeptieren Sie dabei alle Voreinstellungen, bis Sie zum Schritt EINZUBEZIEHENDE DATEIEN AUSWÄHLEN gelangen. Fügen Sie die .configDatei als einzubeziehende Datei hinzu und sie wird mit dem gleichen Verzeichnis zu Ihrer Installation hinzugefügt und bereitgestellt, wie die .exe-Datei der Anwendung.
740
Tag 20
A.20 Tag 20 Quiz 1. Verwenden Sie die Methode WriteElementString von XmlTextWriter, um ein vollständiges Element zu erzeugen: oWriter.WriteElementString("name", "value")
2. Sie sollten die DOM verwenden, wenn Sie mit unterschiedlichen Teilen eines XMLDokuments arbeiten müssen oder wenn Sie sich in der Datei vor- und zurückbewegen müssen. Außerdem sollten Sie die DOM verwenden, wenn Sie die Datei sowohl lesen als auch in sie schreiben. 3. Metadaten sind Daten über Daten, z.B. die Größe von Informationen, die Namen von Feldern usw.
Übung Die Abbildung 20.9 und das Listing 20.8 zeigen eine mögliche Lösung.
Abbildung 20.9: Die Test-Anwendung mit Speicher
741
Quiz-Anworten und Beispiellösungen für die Übungen
Listing 20.8: Die Test-Anwendung 1 Public Class frmTest 2 Inherits System.Windows.Forms.Form 3 4 Private oSettings As New Settings() 5 6 Private Sub frmTest_Closing( _ 7 ByVal sender As Object, _ 8 ByVal e As System.ComponentModel.CancelEventArgs) _ 9 Handles MyBase.Closing 10 11 'Kopiere Konfiguration in das Objekt Settings. 12 With oSettings 13 .Items("left") = Me.Left 14 .Items("top") = Me.Top 15 .Items("height") = Me.Height 16 .Items("width") = Me.Width 17 .Items("color") = cboColor.SelectedIndex 18 If optDOM.Checked Then 19 .Items("strategy") = "DOM" 20 .SaveDOM() 21 Else 22 .Items("strategy") = "ReaderWriter" 23 .SaveWriter() 24 End If 25 End With 26 End Sub 27 28 Private Sub cboColor_SelectedIndexChanged( _ 29 ByVal sender As Object, _ 30 ByVal e As System.EventArgs) _ 31 Handles cboColor.SelectedIndexChanged 32 Me.BackColor = Color.FromName(cboColor.Text) 33 End Sub 34 35 Private Sub frmTest_Load( _ 36 ByVal sender As Object, _ 37 ByVal e As System.EventArgs) _ 38 Handles MyBase.Load 39 With oSettings 40 If .Items("strategy") = "DOM" Then 41 optDOM.Checked = True 42 .LoadDOM() 43 Else 44 optReaderWriter.Checked = True
742
Tag 20
45 46 47 48 49 50 51 52 End 53 End Sub 54 End Class
.LoadReader() End If Me.Left = .Items("left") Me.Top = .Items("top") Me.Height = .Items("height") Me.Width = .Items("width") cboColor.SelectedIndex = .Items("color") With
Dieser Code ähnelt der ursprünglichen Testanwendung. Im Ereingis Form Closing (Zeilen 6 bis 26) werden die Konfigurationsinformationen, darunter auch die Position des Formulars und die aktuellen Werte für die Optionsfelder und DropDownList, in der Variablen Settings gespeichert und dann mit der
gewünschten Technik geschrieben. Im Ereignis Form Load (Zeilen 35 bis 53) geschieht fast das genaue Gegenteil, da hier zuerst der gewünschte Reader bestimmt wird. Das ist möglich, da die Erzeugung des Objekts Settings eigentlich die Einstellungen mit Werten füllt, sodass die Einstellungen neu geladen werden, wenn der Ladevorgang in der Zeile 42 oder 45 eintritt. Schließlich werden die Einstellungen zurück in die gewünschten Variablen kopiert. Der letzte Ereignisbehandler in der gleichen Anwendung bestätigt, dass eine Auswahl in der DropDownList getätigt wurde, indem er die Hintergrundfarbe in die ausgewählte Farbe umändert. Führen Sie die Anwendung aus und ändern Sie die Größe oder Position des Formulars, die ausgewählte Farbei in der DropDownList oder die Auswahl der Strategie. Schließen Sie das Formular und führen Sie es erneut aus. Ihre Änderungen sollten durchgeführt worden sein.
743
Quiz-Anworten und Beispiellösungen für die Übungen
A.21 Tag 21 Quiz 1. Die DISCO-Datei wird vom Client verwendet, um die Webdienste zu entdecken, die von einem Server angeboten werden. 2. Der Proxy ist die clientseitige Ansicht des Webdienstes. Er ist verantwortlich für das Erstellen der SOAP-Nachricht und für das Umwandeln der SOAP-Antwort in die entsprechenden Datenarten. 3. SOAP ist das Nachrichtenformat, das von Webdiensten verwendet wird. Es bietet die Möglichkeit, plattform- und programmiersprachenunabhängige Nachrichten zu erstellen.
744
Auf der Buch-CD
Auf der Buch-CD
Auf der beiliegenden CD finden Sie die folgenden Dateien, die Sie dabei unterstützen sollen, VB.NET möglichst schnell und ohne viel downzuloaden zu erlernen. Die CD ist im mehrere Ordner untergliedert: \Buchdaten\Beispiele\kap[xx] Den gesamten Code aller im Buch beschriebenen Beispiele finden Sie im Order \BEISPIELE. Die Dateien zu den einzelnen Kapiteln finden Sie in den Unterordnern \KAP01, \KAP02 bis \KAP21. So können Sie ohne viel zu tippen mit den Beispielen experimentieren. Selber tippen erhöht jedoch den Lernerfolg! Wenn die im Anhang A beschriebenen Lösungen zu den Workshops neuen Code liefern, finden Sie diesen in den jeweiligen Kapitelordnern. Die zusätzliche Datei CODE.ZIP enthält die Codedateien nochmals in komprimierter Form. Im Ordner \BONUS sind die bei der Drucklegung des Buches schon verfügbaren Bonusprojekte zusammengestellt. Das sind die Bonusprojekte 1 und der erste Teil von Bonusprojekt 2. Die restlichen Bonusprojekte können Sie nach der Veröffentlichung von der Webseite http://www.samspublishing.com herunterladen. \Buchdaten\Software\ Im Ordner \SOFTWARE finden Sie das .NET FRAMEWORK SDK sowie ein weiteres Werkzeug, das Sie beim Programmieren mit Visual Basic .NET wahrscheinlich gut brauchen können – VISUAL BASIC .NET PROJECTS 2.0 der Firma KIDWARE. Dabei handelt es sich um eine aus sechs Programmen bestehende Anwendung, die die wesentlichsten Bestandteile von Visual Basic .NET gut demonstriert. \Buchdaten\HTML Der Ordner \HTML enthält dieses Buch im HTML-Format. So können Sie bequem am Rechner suchen, nachschlagen und lernen.
746
Stichwortverzeichnis , Operator 137 =, Operator 138 .asax, Dateierweiterung 675 .asmx, Dateierweiterung 675, 679 .bmp, Dateierweiterung 588 .cfg, Dateierweiterung 657 .config, Dateierweiterung 630 .gif, Dateierweiterung 588 .jpg, Dateierweiterung 588 .mdb, Dateierweiterung 428 .msi, Dateierweiterung 628 .msm, Dateierweiterung 621 .png, Dateierweiterung 588 .tif, Dateierweiterung 588 .udl, Dateierweiterung 380, 382 .vsdisco, Dateierweiterung 673 .NET Enterprise Servers 40 .NET-Dienste 43 .NET-Framework 40, 41, 256 –, Foundation Classes 42 –, Laufzeitumgebung 41 .NET-Geräte 44 .NET-Server 40
A Abdocken 64 Active Server Pages 174, 176, 321 ActiveX 178 ActiveX Data Objects (ADO) siehe ADO ADO 376 –, .NET 377 –, Architektur 377 –, Befehle generieren 408 –, Datenbankverbindung 379 Aggregat 365 Änderungskontrolle 604
Andocken 64 ANSI 101 Anweisung –, Bedingung 129, 133 –, eingebettete 134 –, Steueranweisung 128 Anwendung –, Abhängigkeit 620 –, Anforderungen 37 –, Basisstruktur anlegen 50 –, Befehlszeilen-Compiler 51 –, bereitstellen 620 –, Break-Modus 205 –, Entwurf 37 –, Entwurf mit der OOP 468 –, Installation 625 –, MDI 522 –, mehrteilige Anwendung bereitstellen 632 –, Menü hinzufügen 514 –, Merge-Modul 621 –, mit Notepad schreiben 51 –, Modi 205 –, ohne Registry-Eintrag 625 –, Quellcodekontrolle 608 –, verteilte 40 –, Webanwendung siehe Webprogramm –, Windows Installer bereitstellen 621 Anwendungsarchitektur 166 –, Änderung der Geschäftslogik 180 –, Beispiele 181 –, Clientplattform 179 –, Datenzugriff beschreiben 171 –, Ebenen 169 –, Entwicklungsteam 181 –, Kommunikationsprotokoll 171 –, Netzwerk 179 –, Rolle des Architekten 168 –, Schichten 169, 173 –, Schlüsselbereiche 169 –, Sicherheit 171, 180
747
Stichwortverzeichnis
–, Skalierbarkeit 171 –, Technologien beschreiben 170 –, Verfügbarkeit 172 –, zukunftsorientierte Planung 181 Anwendungsentwicklung 168 –, Implementierungsphase 168 Anwendungsentwurf 468 –, Anforderungen ermitteln 468 –, Beziehungen identifizieren 470 –, Objekte ermitteln 469 –, Objekte modellieren 470 API 31 App Center 41 Application Programming Interface (API) Application, ASP-Eigenschaft 677 Architektur –, Client/Server 173 Array 97, 103 –, dynamisches 105 ArrayList, Sammlung 267 –, Methoden 268 As, Schlüsselwort 103 ASCII 101 ASP.NET 175, 176, 321, 675, 677 Assembly 480 Attribut 223, 678 Aufgabenliste 81 Ausdruck 129 –, Boolescher 130 Ausführen bis Rücksprung, Befehl 209 Ausnahme 188 –, abfangen 190 –, auslösen 199 –, Division durch Null 191 –, Meldung 193 –, strukurierte 188 –, Throw, Anweisung 199 –, Typen 194 –, Vergleich mit Fehler 189 Ausnahmebehandlung 188 –, Catch, Schlüsselwort 190 –, in Anwendung einfügen 195 –, SEH (Structured Exception Handling) –, Try-Block 190 –, Ursache lokalisiseren 194
748
B
650
188
BASIC 37 Befehlseingabeaufforderung 52 Befehlskonsole 72 Befehlszeilen-Compiler 51 Befehlszeilen-Switches 55 Beispiel –, Anwender begrüßen 131 –, Array durchsuchen 151 –, auf Fluchtwert prüfen 151 –, Berechnung einer Fakultät 160 –, Bildbetrachter 527 –, CD-Datenbank 348 –, Datei lesen 555 –, Datei-Menü 562 –, Datenbank aktualisieren 402 –, Datenbank erstellen 367 –, Datenbankanfrage 385 –, Datenbankverbindung öffnen 384 –, Datenbindung 417 –, Dateneingabe validieren 295 –, Datensätze löschen 398 –, Debugging 203 –, Debugging-Interface implementieren 503 –, einfache Autorendatenbank 622 –, einfache Windows-Anwendung 86 –, einfacher Webdienst 674 –, Fehlerbereinigung im Hypthekenrechner 212 –, Finally-Abschnitt 198 –, Funktion überschreiben 462 –, Future Value 118 –, geerbte Klasse verwenden 464 –, Hypothekenrate berechnen 453 –, Hypothekentabelle berechnen 202, 213 –, integrierte Dialogfenster 308 –, Interface implementieren 498 –, Investitionsrechnung 118 –, komplexere Autorendatenbank 632 –, komplexerer Webdienst 686 –, Konfigurationseinstellungen setzen 656 –, Konstruktoren 465 –, Kontrollkästchen 293 –, Kreditkartennummer prüfen 486 –, Leistungsmonitor 438 –, Login-Dialogfenster erstellen 311
Stichwortverzeichnis
–, MathService 674 –, Menü anlegen 519 –, Methode hinzufügen 460 –, Mustervergleich 138 –, Namensraum deklarieren 244 –, Notepad-Klon 556 –, Objektinitialisierung 465 –, Optionsschalter 292 –, Outlook-Informationen abrufen 491 –, Outlook-Nachrichtenagent 496 –, PerfLite 438 –, Produktkatalog im Internet 686 –, schnellere Hypothekenrückzahlung 493 –, Setup-Programm erstellen 622, 625 –, Siebzehnundvier 468 –, sortierte Liste 500 –, Textdatei schreiben 556 –, Try-Block 196 –, Umleitung 262 –, Verbindungszeichenkette 383 –, Vererbung 461, 492 –, Web Forms-Anwendung 327 –, Webdienst einrichten 675 –, Webdienst Products 688 –, Website abrufen 609 –, Windows Explorer-Klon 539 –, Zahlenratespiel 157 –, Zeichenprogramm 573 Benutzeroberfläche –, DataGrid 420 –, Element an Daten binden 416 Beschriftung, Steuerelement 289 Betriebssystem 31 –, Versionsnummer abrufen 54 Bibliothek 39 Bildfeld, Steuerelement 289 Bitmap, Klasse 572 BizTalk Server 41 Boolean, Variablentyp 102 Boolescher Ausdruck 130, 137 –, Kombinationsmöglichkeiten 139 –, Kurzschluss 140 Break-Modus 207 Browser 320, 323 Brush, Klasse 572 Bug
–, fehlerhafte Eingabe 201 –, logischer Fehler 200 –, Tippfehler 200 Button, Steuerelement 289, 325 Byte, Integer-Typ 98
C C# 57 –, versiegelte Klasse 481 Calendar, Steuerelement 334 Case Else, Klausel 142 Catch, Schlüsselwort 190 CausesValidation, Eigenschaft 294 Char, Zeichenkettenvariable 101 CheckBox, Steuerelement 289, 326 Click, Ereignis 285, 287, 313 Client –, für Webdienst erstellen 682, 691 –, Thick Client 175, 177 –, Thin Client 175, 176 –, Typen 175 Clipboard, Klasse 567 –, Methoden 568 CLR 256 –, Common Language Runtime 40 Cluster Server 41 Code –, Änderungen verfolgen 615 –, Einzelheiten auskommentieren 677 –, Fehlerbehandlung 230 –, Haltepunkt 206 –, klar strukturieren 599, 600 –, nativer 32 –, Pseudocode 128 –, Quellcodekontrolle siehe Quellcodekontrolle –, sauber schreiben 597 –, Schritt für Schritt prüfen 207 –, Shortcuts 81 –, Step, Befehl 208 Collections, Klasse 257 Color, Dialogsteuerelement 310 Color, Struktur 273, 572 COM 175, 376, 621 Comma-separated values (CSV) 644 Common Language Runtime (CLR) 40, 256, 620
749
Stichwortverzeichnis
Common Language Specification 42, 472 CompareTo, Methode 498 compile, Anweisung 54 Compiler 33, 42, 49, 55 –, Befehlszeilen-Compiler 51 –, Konstante ohne Wert 106 –, und IDE 49 Computername 274 Computerprogramm 30 Console, Klasse 54, 257, 556 –, Methoden 257 –, Umleitung 261 Console.Read, Routine 120 Console.WriteLine, Routine 121 Console.WriteLine, Subroutine 114 ContextMenu, Steuerelement 289 ControlChars.CrLf 561 Copy, Methode 285 CreateGraphics, Methode 445 CRLF 562
D DataGrid, Steuerelement –, DataSource 436 –, Eigenschaften 435 DataSet 389 Date –, kommagetrennte 348 Date, Variablentyp 102 Datei –, Änderungsprotokoll 614 –, ausführbare 53, 88 –, frühere Version wiederaufnehmen –, Kopfzeile 347 –, lesen 155 –, öffnen 565 –, Quellcodekontrolle 612 –, Rollback 615 –, schreibgeschützte Kopie 612 –, speichern 566 –, Textdatei lesen 553 –, Zwischenablage 567 Datenadapter 389 Datenbank 376 –, Access 368, 381, 427 –, Add, Methode 395
750
615
–, Aktualisierungsinkonsistenz 355 –, Anfrageergebnis sortieren 350 –, Ansicht 414, 430 –, auf Tabellen zugreifen 391 –, Befehle generieren 408 –, Beziehungen 360 –, Columns, Sammlung 391, 392 –, DataSet 389 –, DataTable 391 –, DataView 414 –, Daten laden 389 –, Datenadapter 389 –, Datenbindung 416 –, Dateninkonsistenz 356 –, Daten-Reader 388 –, Datensatz 348 –, Datensatz abrufen 349 –, Datensatz aktualisieren 352 –, Datensatz bearbeiten 396 –, Datensatz eintragen 352 –, Datensatz hinzufügen 395 –, Datensatz löschen 353, 397 –, Delete, Methode 397 –, DeleteCommand 402 –, Diagramm 430 –, Fehlerbehandlung bei der Verbindung –, FillSchema, Methode 409 –, Fremdschlüssel 357 –, Funktion 430 –, gespeicherte Prozedur 366, 430 –, GUID 366 –, Inner Join 361 –, InsertCommand 402 –, Join 360 –, mehrwertige Felder 359 –, Metadaten 641 –, mit mehreren Tabellen 412 –, mit Server Explorer verwalten 429 –, ODBC 427 –, Oracle 428 –, Outer Join 362 –, Parameter hinzufügen 401 –, Platzhalter 401 –, Primärschlüssel 357 –, Primärschlüssel erstellen 363 –, Profiler 408
384
Stichwortverzeichnis
–, Provider 377 –, Referenzintegrität 363 –, Remove, Methode 397 –, Rows, Sammlung 391 –, Schema 395, 407 –, Schlüssel automatisch generieren 364 –, Schlüssel berechnen 365 –, SQL Server 381, 382, 428 –, SQL-Anfrage 386 –, SQL-Befehlsobjekt 400 –, SQL-Befehlsobjekte 385 –, SQL-Server 624 –, Tabelle 348, 430 –, Tabellen verbinden 357 –, Treiber 377 –, UpdateCommand 402 –, Verbindung einrichten 379 –, Verbindung öffnen 384 –, Verbindungsinformationen bearbeiten 380 –, verbindungsloser Zugriff 388 –, Verbindungsobjekt 379 –, Verbindungszeichenkette 379, 382, 624 –, Versionsinkonsistenz 356 –, Zeile 348 –, Zugriff mit Verbindung 388 –, Zugriff mit Webdienst 689 Datenbindung 416 –, einfache 417 –, komplexe 420 Datenlink, Dateityp 380 –, Datei anlegen 380 –, im Server Explorer 427 Datenlinkeigenschaften 380 Datensatz 348 –, Feld 348 –, Spalte 348 Datenzugriffsbibliothek 376 DCOM 175 Debugging 202 –, Aufrufliste 216 –, Direkt-Fenster 216 –, Disassembly, Werkzeug 216 –, Locals-Fenster 211 –, Schnellüberwachung-Fenster 216 –, Step, Befehl 208
–, Threads, Werkzeug 216 –, Variablen beobachten 211, 212 –, Watch-Fenster 211 Debug-Modus 193 DELETE, Klausel 353 DESC, Schlüsselwort 350 Dezimalvariable 99 Dialogfeld 281 Dialogfenster –, anzeigen 314 –, Eingabe validieren 313 DialogResult, Eigenschaft 312 Dienst –, Datenverbindung 424 –, Definition 425, 432 –, Dienstliste 433 –, Ereignisprotokoll 432 –, Event Viewer 426 –, geladene Module 432 –, im Internet 666 –, Kategorien 424 –, Message Queuing 433 –, Prozesse 433 –, Server 424 –, Server Explorer 426 –, SQL Server-Datenbanken 433 –, Systemmonitore 433 –, Verwaltungsdaten 433 –, Webdienst siehe Webdienst 433, 667 Dim, Schlüsselwort 103 Directory, Klasse –, Methoden 552 Dirty, Eigenschaft 561, 564, 570 DISCO-Datei –, dynamische 673 –, manuell bearbeiten 672 –, Vertrag 673 Discovery (DISCO) 672 DisplayTable, Funktion 393 Dispose, Methode 583 Distributed Network Architecture (DNA) 174 DLL 559 DNA 174, 186 DNS, Klasse 275 Document Object Model siehe DOM
751
Stichwortverzeichnis
Document Type Definition (DTD) 647 Dokumentation –, Änderungsprotokoll 604 –, auf allen Ebenen 602 –, Bestandteile 602 –, Kommentar-Header 607 –, Namensstandards 604 –, Richtlinien 596 –, summary-Tags 664 –, Vorbedingungen vermeiden 601 –, Zielgruppe 601 –, zu Anforderungen und Verwendung 603 –, zu Idee und Umfang 602 –, zum Detailentwurf 603 DOM 650 –, Knoten 651 –, Nachteile 653 –, Vergleich mit XmlTextReader 655 Domain Naming Service 275 Do-Schleife 149 DOS-Fenster 72 Double, Dezimalvariablentyp 100 DropDownList, Steuerelement 326, 694
E
Eigenschaft 224, 452 –, Daten validieren 486 –, Definition 226, 458 –, deklarieren 225 –, Eigenschaftsroutine 483 –, Lese-/Schreibzugriff 484 –, Nur-Lese- 226 –, Nur-Lesezugriff 484 –, Nur-Schreib- 226 –, Nur-Schreibzugriff 484 –, öffentliche Variable als 483 –, überschreiben 462 –, validieren 229 –, Vergleich mit Methode 495 –, zu Klasse hinzufügen 483 Eingabe –, validieren 294, 313, 337 Einzelschrittt, Befehl 208 Else, Klausel 134 ElseIf, Anweisung 134
752
Environment, Klasse 257, 264 –, Methoden 264 Ereignis 285, 495 –, Behandler 285, 496 –, Behandler erstellen 285, 497 –, Deklaration 496 –, einrichten 496 ErrorProvider, Steuerelement 305 Ex, Variable 196 Exchange Server 41 exe, Option 53, 56 ExecuteReader, Methode 386 Exit, Anweisung 150 Extensible Markup Language siehe XML
F Farben 272 Fehler 189 –, fehlerhafte Eingabe 201 –, logischer 200 –, Tippfehler 200 Fenster 280 –, Dialogfeld 281 –, modal/nichtmodal 281 Fensterteiler 537 FIFO 269 File, Klasse 263, 286 –, Methoden 552 filename, Option 56 FileStream, Klasse –, Methoden 554 FILO 269 Finally, Klausel 658 Finally, Schlüsselwort 198 Font, Dialogsteuerelement 310 Font, Klasse 573 For Each-Schleife 392 Form_Load, Prozedur 438 Formular –, Dialogfenster-Steuerelemente 306 –, Steuerelement hinzufügen 283 –, Variablen einfügen 448 For-Schleife 143, 392 Funktion 114 –, erstellen 115
Stichwortverzeichnis
–, integrierte 113 –, Umwandlungsfunktionen 109 –, Zeichenkettenbearbeitung 111
G Geschäftslogik 173 GetType, Methode 240 Globally Unique Identifier 366 Grafik 445 –, Bild speichern 588 –, Farben bestimmen 272 –, Formate 588 –, Formen zeichnen 584 –, Graphics-Objekt 445, 579 –, Linie zeichnen 585 –, Paint, Ereignis 579 –, PictureBox 585 –, Systemfarben 274 –, vertikale Linie verschieben 446 grafische Benutzeroberfläche 32, 35, 38, 280, 283, 380, 416, 692 –, DataGrid, Steuerelement 435 –, Dialogfenster 306 –, TextBox, Steuerelement 556 –, und Konsolen-Interface 73 –, unsichtbares Steuerelement 302 –, Verhalten der IDE-Optionen 70 Graphical User Interface (GUI) 32 Graphics, Klasse 573 –, Methoden 584 GroupBox, Steuerelement 290 GUI 32 GUID 366 Gültigkeitsbereich 116 –, Bedeutung 117 –, Friend 231, 481 –, Private 116, 117, 481 –, Public 116, 117, 480 –, von Klassen 480 –, von Methoden 490
H
Haltepunkt 73, 206, 209 Handles, Schlüsselwort 287, 288, 497 HashTable, Sammlung 657, 661
help, Option 56 Host Integration Server 41 Hotkey 517 HTML 320, 641 –, DHTML 321 –, Dynamic HTML-Programmiermodell –, dynamisches 178 –, Tags 641 –, und SGML 642 –, Verwendungszweck 642 HTTP 321 Hyperlink, Steuerelement 326 HyperText Transfer Protocol 321
322
I IComparable, Interface 498 IdataObject, Interface 568 IDE 33, 49, 60, 259, 285, 424 –, ?, Anweisung 75 –, Aufgabenliste 81 –, ausführbare Datei 91 –, ausführbare Datei erzeugen 88 –, Ausgabeanweisung 75 –, Auto-Hide 67 –, Benutzeraufgabe 82 –, Break-Modus 73, 75 –, Code zum Projekt hinzufügen 91 –, Code-Shortcuts 81 –, Command-Fenster 72 –, Datei speichern 84 –, Dateisymbol 89 –, dynamische Hilfe 76 –, Fenster als Registerkarten 67 –, Fenster andocken 64 –, Fenster skalieren 69 –, Fenster verbergen 66 –, Fensterfunktionen 64 –, Hauptarbeitsbereich 62 –, Immediate-Fenster 72 –, Interaktion mit Befehlskonsole 72 –, Klassenansicht 79 –, Objektbrowser 80 –, Profileinstellungen 61 –, Projekt ausführen 88 –, Projekt erstellen 82, 86
753
Stichwortverzeichnis
–, Projekt kompilieren 88 –, Projekt öffnen 83 –, Projekteinstellungen 89 –, Projekttypen 82 –, Properties 77 –, Server Explorer 76, 426 –, Solution Explorer 78 –, Startseite 62 –, Tool-Fenster 64 –, und grafische Benutzeroberfläche 70 –, unverankertes Fenster 64 –, View Code, Menüelement 74 –, Werkzeuge verwenden 69 –, Werkzeugsammlung 69 If, Anweisung 216 –, einfache 129 –, eingebettete 134, 141 –, einzeilige 136, 162 –, komplexe 132 IIS 321 Image, Steuerelement 327 ImageButton, Steuerelement 326 Imports, Anweisung 54 –, Vergleich mit References 559 Inherits, Schlüsselwort 238 INSERT, Anweisung 352, 358, 386 Install Shield 636 Installation von Visual Basic .NET 45 Instanz –, Definition 456 –, und Eigenschaft 458 –, und Variable 456 Integer-Variable 98 Integrierte Entwicklungsumgebung siehe IDE Interface –, Definition 498 –, erstellen 501 –, oder Vererbung 501, 506 –, Unterschied zur Klassendeklaration 501 Internet Information Server (IIS) 321 interpretierte Sprache 34 IsPostBack,Eigenschaft 697 Items, Sammlung 331
754
J JavaScript 321 JavaServer Pages 321 Join 361 Just-in-Time 193
K Klasse 222 –, abstrakte 481 –, Attribut 223 –, Basis- 461, 481 –, Definition 456 –, deklarieren 480 –, dokumentieren 607 –, Eigenschaft 224 –, Eigenschaften hinzufügen 483 –, erweitern 239 –, Framework-Klassen 154 –, Friend 481 –, geschachtelt 481 –, Gültigkeitsbereich 480 –, in mehreren Projekten verwenden –, Interface 498 –, Konstruktor 465 –, Methode 223, 459 –, New, Konstruktor 465 –, Private 481 –, Public 480 –, Sammlungsklasse 267 –, überschreiben 461 –, Vererbung 235, 460, 481 –, Verhalten implementieren 229 –, versiegelte 481 –, zu Projekt hinzufügen 224 Klassenbibliothek 506 –, .dll erzeugen 509 Kommentarblock 607 Kompilieren 33 kompilierte Sprache 34 Komponente 39 Konfigurationsdatei 625 –, AppSettings 631 –, Namenskonvention 630 Konsolenanwendung 316 Konsolen-Interface 73
506
Stichwortverzeichnis
Konstante 106 –, deklarieren 106 Konstruktor 242, 248 –, Definition 465 –, New 465 Kontextmenü, Steuerelement 289 Kontrollkästchen 289, 290, 292 –, in Formular einfügen 293 –, Standardwert 293
L
Label, Steuerelement 289, 325, 328 lblResult, Steuerelement 333 Like, Operator 138 Lineare Programmierung und OOP 453 LinkButton, Steuerelement 326 LinkLabel, Steuerelement 289 ListBox, Steuerelement 326, 623 –, IntegralHeight, Eigenschaft 623 ListView –, ListViewItem 536 ListView, Steuerelement 535 –, Eigenschaften 535 –, Items, Sammlung 536 –, ListViewItemCollection 537 Long, Integer-Typ 99 Loop, Anweisung 150
M
MainMenu, Steuerelement 289, 514, 557 Maschinensprache 32 Math, Klasse 257, 266 –, Methoden 266 MDI 522 MDI-Anwendung 522 –, Elternformular 523 –, MdiClient, Steuerelement 524 –, MDI-Container 523 –, MdiParent, Eigenschaft 546 –, Menü einfügen 525 –, Nachteile 524 Me, Schlüsselwort 463, 494 Menü –, Ausschneiden 567 –, Bearbeiten 567 –, Code hinzufügen 518
–, Datei 528 –, Datei öffnen 565 –, Datei speichern 566 –, Einfügen 568 –, einrichten 514 –, Fenster 525, 529 –, Fensterliste 527 –, FileOpenDialog 558 –, FileSaveDialog 558 –, häufig verwendete Menüs 520 –, Hilfe 561, 569 –, Hotkey 517 –, in MDI-Anwendung 525 –, Konventionen 520 –, Kopieren 568 –, MainMenu, Steuerelement 514 –, Menüelemente anlegen 515 –, mnuFileNew_Click, Ereignisbehandler 520 –, Namenskonventionen 516 –, Tastaturunterstützung 517 –, Tastenkombinationen 517, 546 –, Zugriffstasten 517 Menüleiste, Steuerelement 289 Merge-Modul 621 MessageBox, Klasse 92, 286, 297, 311, 316 –, Parameter von Show 298 –, Show, Methode 298 Metadaten 640 Methode 223, 231, 452 –, Definition 459 –, erstellen 490 –, gemeinsam genutzte 553 –, Shared 553 –, überladen 233, 242 –, überschreiben 492 –, Vergleich mit Eigenschaft 495 Microsoft Access 368 Microsoft Intermediate Language (MSIL) 256 Microsoft Visio 470 Microsoft, Namensraum 272 Microsoft.VisualBasic, Namensraum 276 modMain.vb, Modul 207 Modus 205 MouseEventArgs, Klasse 587 MSDE 367, 368
755
Stichwortverzeichnis
MSDN-Seiten 621 MSIL 256 MSXML 663 Multiple Document Interface siehe MDI MustInherit, Schlüsselwort 240, 481 MustOverride, Schlüsselwort 240, 482 MyBase, Schlüsselwort 243, 463, 467
N Namenskonvention 604 –, Datentypen 605 –, Kamelschreibweise 605 –, Microsoft 604 –, Steuerelemente 284, 606 –, Ungarische Notation 604 Namensraum 243, 272, 507 –, deklarieren 507 –, für Ein-/Ausgabe 551 –, für Grafik 571 New, Konstruktor 465 New, Schlüsselwort 242 Next, Anweisung 143 Notepad 51 NotifyIcon, Steuerelement 305 NotInheritable, Schlüsselwort 481
O
Objekt 154, 222, 452 –, Definition 453 –, Eigenschaft 452, 458 –, erzeugen 480 –, Methode 452 –, mit Objekt vergleichen 498 –, Modellierung 470 –, Objektreferenz 223 –, Steuerelement 455 –, und Instanz 456 –, und Klasse 456 –, zerstören 247 Objektbrowser 80 Objektorientierte Programmierung siehe OOP ODBC 377 OLE DB 376, 428 OleDBCommand 385 On Error, Anweisung 190, 217
756
OOP 247, 498, 552 –, Anwendung entwerfen 468 –, Instanz 456 –, Klasse 456 –, Konstruktor 465 –, Methode 459 –, Überschreiben 461 –, und .NET-Framework 455 –, und lineare Programmierung 453 –, Vererbung 460 Open, Dialogsteuerelement 307 Operator –, Auflistung 108 –, logischer 139 –, Vergleichsoperatoren 137 Option Explicit, Moduloption 200 Option Strict, Moduloption 202 Optionsschalter 289, 290 –, Checked, Eigenschaft 291 –, in Formular einfügen 290 –, mehrere Schalter gruppieren 290 ORDER BY, Klausel 350 out, Option 56 Overloads, Schlüsselwort 242 Overridable, Schlüsselwort 239, 463, 492 Overrides, Schlüsselwort 239, 463, 492, 494
P Page_Load, Ereignis 697 Panel, Steuerelement 327 Pen, Klasse 573 Perl 321 Personal Web Server 321 PictureBox, Steuerelement 289 Plattform-SDK 621 Platzhalter 258 Point, Struktur 573 Profileinstellungen 61 Profiler 408 Programm –, Schritt für Schritt prüfen 209 –, Webprogramm siehe Webprogramm Programmierung –, lineare 453 –, objektorientierte siehe OOP
Stichwortverzeichnis
–, prozedurale 453, 455 –, typlose 124 Projekt –, andere Projekte referenzieren 634 –, Bereitstellungsprojekt 625 –, Code hinzufügen 91 –, erstellen 82, 88 –, Klasse hinzufügen 224 –, Klassenbibliothek 506, 632 –, kompilieren 88 –, Konsolenanwendung 191, 509 –, Modul hinzufügen 227 –, Setup-Wizard 635 –, speichern 611 –, Webanwendung 691 –, Webdienst 673, 674 –, Windows-Anwendung 74, 83, 282, 435, 539, 556, 574, 622, 682 –, Windows-Formular 530 Provider 377, 427 Proxy 681 Prozedur –, dokumentieren 607 –, gespeicherte 366 –, Gültigkeitsbereich 117 –, klare Namen 599 Prozedurale Programmierung 453 Prozedurschrittt, Befehl 208 Pseudocode 128
Q Quellcode 60 Quellcodekontrolle –, Änderungen kommentieren 614 –, Änderungen verfolgen 615 –, Änderungsprotokoll 614 –, Code auschecken 609, 611 –, Code einchecken 610, 613 –, konfigurieren 610 –, Lösung auschecken 612 –, Projekt hinzufügen 609 –, rekursives Auschecken 612 –, Solution Explorer 611 Queue, Klasse –, Methoden 269 Queue, Sammlung 268
Quick BASIC 37 QuickInfo, Werkzeug
211
R
RadioButton, Steuerelement 289, 327 RadioButtonList, Steuerelement 331 RaiseEvent, Anweisung 496 Random, Klasse 257, 265 –, Methoden 265 Rational Rose 471 Reader, Datenbank 386 ReadLine, Methode 257 ReadOnly, Schlüsselwort 226 Rectangle, Struktur 573 ReDim, Schlüsselwort 105 Referenz, auf Outlook 490 Registrierungsdatenbank 621, 625 Rekursion 159 Request, ASP-Eigenschaft 677 Response, ASP-Eigenschaft 677 ReturnType, Datentyp 115 Routine 114 –, Funktion 114 –, Subroutine 114
S Sammlungsklassen 267 Save, Dialogsteuerelement 308 Schaltfläche 289 Schema 631 Schleife 143 –, Bedeutung für die Leistung 153 –, Bedingungsanweisung 150 –, Beendigungsbedingung 150 –, Do-Schleife 149, 151 –, Endlosschleife 152 –, Exit, Anweisung 150, 151 –, For-Schleife 143 –, Inkrementierung bestimmen 146 –, Loop, Anweisung 150 –, Step, Option 146 –, Until, Klausel 149 –, While-Schleife 147 –, Zählervariable 144 Schlüssel 357 Schlüsselmanager 366
757
Stichwortverzeichnis
SDL (Service Description Language) 670 SEH (Structured Exception Handling) 188 Select Case, Anweisung 142 SELECT, Anfrage 387 SELECT, Anweisung 349, 361 Select, Anweisung 143 Server Explorer 76, 424 –, Anwendung schreiben 435 –, Datenbankelemente 429 –, Datenbanken verwalten 427 –, Datenbankverbindung herstellen 431 –, Datenverbindungen 427 –, Dienstkategorien 424 –, Verbindung zu Server herstellen 433 –, Verbindungsobjekte hinzufügen 436 –, Verbindungsoptionen 428 Service Description Language (SDL) 670 Session, ASP-Eigenschaft 677 Settings, Klasse 655 Setup-Programm 622 –, Abhängigkeiten 628, 635 –, Ausgabe wählen 635 –, Dateien ausschließen 630 –, Projekt kompilieren 628 –, Setup-Wizard 626 –, Windows Installer 629, 636 SGML 641 –, Verwendungszweck 642 Shared, Schlüsselwort 246 Short, Integer-Typ 99 Shortcut, Eigenschaft 518 ShowDialog, Methode 315 Simple API for XML (SAX) 663 Simple Object Access Protocol siehe SOAP Single Document Interface (SDI) 522 Single, Dezimalvariablentyp 100 Size, Struktur 573 SOAP 175, 186, 668, 679 –, Anfrage 669 –, Nachrichtenformat 668 –, Schema 668 –, WSDL 670 Software Development Life Cycle (SLDC) 36 Softwareentwicklung 36, 167 –, Lebenszyklus 167 Solution Explorer 78, 290, 611
758
SortedList, Klasse 500 SortedList, Sammlung 270 –, Methoden 270 Speicherbereinigung 228 Splitter, Steuerelement 537 –, Eigenschaften 538 SQL 349 –, Anfragen 385 –, Arbeit mit Datenbanken siehe Datenbank –, Informationsquellen 354 –, Query Analyzer 354 SQL Server 41, 354, 368 SQLCommand 385 SQL-Server, zentraler 624 Stack, Sammlung 268, 269 –, Methoden 270 Standard Generalized Markup Language (SGML) 641 Steueranweisung 128 Steuerelement 280 –, als Eigenschaft 531 –, an Sammlung binden 331 –, Beispiele 440 –, Dialogfenster 306 –, in Datenbank speichern 331 –, in Formular einfügen 86, 283 –, in Windows-Formular 455 –, Namenskonventionen 606 –, unsichtbares 302 –, Web Forms 323, 325 Stop, Schlüsselwort 206 StreamReader, Klasse 155 –, Methoden 554 StreamWriter, Klasse –, Methoden 555 String, Klasse 111 String, Zeichenkettenvariable 101 Strom –, Definition 550 –, FileStream, Klasse 553 –, in Textdatei schreiben 555 –, StreamReader, Klasse 553 –, StreamWriter, Klasse 555 –, Textdatei lesen 553 Structured Query Language 349 Struktur 97
Stichwortverzeichnis
Sub Main, Prozedur 260 Sub, Schlüsselwort 115 Subroutine 114, 455 –, erstellen 115 Switch 53 System –, Drei-Schicht-System 173 –, Ein-Schicht-System 173 –, n-Schicht-System 173 –, Thick Client-System 177 –, Thin Client-System 176 –, verteiltes 40, 170 –, Zwei-Schicht-System 173 System, Klasse 54 System, Namensraum 259, 272 System.Collections, Namensraum 267 System.Data, Namensraum 369, 377, 688 System.Data.OleDB, Namensraum 377 System.Data.SQLClient, Namensraum 377 System.Drawing, Namensraum 273, 571 –, Klassen 572 System.Environment, Namensraum 274 System.IO, Namensraum 155, 263, 285, 503, 551 –, Directory, Klasse 551 –, File, Klasse 552 System.Object, Namensraum 241 System.Random, Namensraum 157 System.String, Namensraum 481 System.Web.Services, Namensraum 676 System.Windows.Forms, Namensraum 275 Systemarchitektur, Systemanforderungen 178 SystemColors, Klasse 274 SystemInformation, Klasse 275 Systeminformationen 274 Systemzeit 130
T target, Option 56 Tastaturunterstützung bei Menüs 517 Tastenkombination 517 TextBox, Steuerelement 87, 289, 325, 556 –, Eigenschaften 557 Textdatei schreiben 555 Textfeld, Steuerelement 289 TextReader, Klasse 261
TextWriter, Klasse 261 Thread 216 Timer, Steuerelement 303, 443 Token 260 Toolbox-Fenster 283 ToString, Methode 240, 444 TreeView, Steuerelement 532, 546 –, Elemente 533 –, Ereignisbehandler 541 –, GetChildren, Prozedur 544 –, Knotenattrappe 541 –, TreeNode, Klasse 534 –, TreeNodeCollection 535 Treiber 377 Try...Catch 230 Try-Block 190 –, Finally 198 –, geschachtelter 197 Type, Validator-Eigenschaft 340 typlose Programmierung 124 Typumwandlung 110 –, Auflistung der Funktionen 110
U Überladen 233 Überschreiben 461 Umgebung 264 UML (Unified Modeling Language) Umleitung 261 Ungarische Notation 599, 604 Unicode 101 UPDATE, Anweisung 352
471
V Validating, Eigenschaft 313 Validating, Ereignis 295 Validator, Steuerelement 337 –, Eigenschaften 338 –, Nutzen 340 Validierung 294 Variable 96 –, Array 97, 103 –, benutzerdefinierter Typ 97 –, Boolean 102 –, Date 102 –, deklarieren 103
759
Stichwortverzeichnis
–, Dezimalvariablen 99 –, einfache 97 –, Gültigkeitsbereich 116, 224 –, Inhalt betrachten 211 –, Integer 98 –, klare Namen 599 –, Namenskonventionen 107, 599 –, Präfix von Klassenvariablen 484 –, Präfixe 107 –, Private 116 –, Public 116 –, Struktur 97 –, Typen 97 –, und Instanz 456 –, Wert zuweisen 106 –, Zählervariable 144 –, Zeichenketten 100 VBA (Visual Basic for Applications) 162, 376 Verbindungszeichenkette 369 verbose, Option 56 Vererbung 235 –, Definition 460 –, Eigenschaften überschreiben 462 –, oder Interface 501, 506 Vergleich –, bitweiser 139 –, Boolesche Kombinationen 139 –, Operatoren 137 Verkettung 258 Versionskontrolle siehe Quellcodekontrolle Visual Basic 35, 57, 105, 188, 226, 272, 277, 475 –, Datenumgebung 425 –, Datenzugriff 376 –, Geschichte 37 –, OOP-Konzepte 467 Visual Basic for Applications (VBA) 162, 376 Visual SourceSafe (VSS) siehe Quellcodekontrolle visuelle Sprache 35
W Web Forms –, Eigenschaften 330 –, Webprogramm siehe Webprogramm Web Programming-Modell 320, 321
760
Web Service Description Language siehe WSDL Webdienst 43, 667 –, Attribut 678 –, .asmx-Datei 679 –, auf Rechner finden 672 –, aufrufen 684 –, Beschreibungssprache 670 –, Client erstellen 682, 691 –, DISCO-Datei 672 –, erstellen 674, 687 –, kompilieren 679 –, Methodensyntax 678 –, Nachricht definieren 668 –, Proxy 681 –, prüfen 679, 691 –, SOAP 668, 679 –, Testseite 680 –, Webreferenz hinzufügen 683 –, WSDL 670, 683 –, Zugriffskontrolle 700 Webprogramm –, Besonderheiten 322 –, Browser 323 –, Client und Server 323 –, Dynamic HTML-Programmiermodell 322 –, Eingabe validieren 337 –, Steuerelemente 325, 329 –, Vorteile 324 Webreferenz 683 WebService, Klasse 677 Werkzeugsammlung 69, 283, 327 –, Elemente verwenden 69 –, Textelemente 71 WHERE, Klausel 351, 352, 353 While-Schleife 147 Windows Forms 175, 177, 280 Windows Installer 621 Windows Management Information (WMI) 433 Windows-Anwendung 280 winexe, Befehl 53 winexe, Option 56 Wise Installer 636 WithEvents, Schlüsselwort 497 Write, Methode 555 WriteLine, Anweisung 459
Stichwortverzeichnis
WriteLine, Methode 257 WriteOnly, Schlüsselwort 227 WSDL, Vertrag 670
X XCOPY 636 X-Copy-Installation 621 XML 175, 372, 478, 640 –, Attribut 645 –, Beziehung zu HTML, SGML 641 –, Code lesen 655 –, Code schreiben 660 –, Datenspeicherung 650 –, Datenübergabe 649 –, DISCO 672 –, DOM 650 –, DTD 647 –, Editor für Schemata 648 –, Eigenschaften von XmlDocument 652 –, Eigenschaften von XmlTextReader 654 –, Eigenschaften von XmlTextWriter 655 –, Element 645 –, Knoten 651 –, Knoteneigenschaften 652 –, Konfigurationsdatei 630, 649 –, Konfigurationseinstellungen laden 657 –, Konfigurationseinstellungen speichern 660 –, Regeln 643 –, Schema 647 –, SOAP 668 –, Tags 642
–, Vergleich DOM und Reader/Writer –, Vergleich Element/Attribut 645 –, Vergleich mit SAX 663 –, Vergleich Schema/DTD 647 –, Verwendungsmöglichkeiten 649 –, Verwendungszweck 642 –, WSDL 670 –, XmlDocument 652 –, XmlTextReader 653, 654 –, XmlTextWriter 653, 654
653
Z Zahl –, Boolean 102 –, Double 100 –, Gleitkommazahl 100 –, in Zeichenkette umwandeln 93 –, Integer 98 –, Operatoren 108 –, Random, Klasse 265 –, Single 100 Zeichenkette –, Bearbeitungsfunktionen 111 –, Char, Variable 100 –, in Zahl umwandeln 93, 444 –, Operatoren 108 –, String Char, Variable 100 –, verketten 258 Zeilenvorschub und Zeilenumbruch Zugriffstaste 517 Zuweisung 105
562
761