Sie sind auf Seite 1von 935

Chris Payne

bersetzung: Michael Matzer

.NET Windows Forms


I I I

ent Oberflchen objektorientiert programmieren berfl hen ob rf ADO.NET, ActiveX, Multithreading, Web Services nbi lio ken enl enn Klassenbibliothek des .NET Framework kennenlernen

Markt + Technik Verlag

Bibliografische Information Der Deutschen Bibliothek Die Deutsche Bibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet ber <http://dnb.ddb.de> abrufbar. Die Informationen in diesem Produkt werden ohne Rcksicht auf einen eventuellen Patentschutz verffentlicht. Warennamen werden ohne Gewhrleistung der freien Verwendbarkeit benutzt. Bei der Zusammenstellung von Texten und Abbildungen wurde mit grter Sorgfalt vorgegangen. Trotzdem knnen Fehler nicht vollstndig ausgeschlossen werden. Verlag, Herausgeber und Autoren knnen fr fehlerhafte Angaben und deren Folgen weder eine juristische Verantwortung noch irgendeine Haftung bernehmen. Fr Verbesserungsvorschlge und Hinweise auf Fehler sind Verlag und Herausgeber dankbar. Authorisierte bersetzung der amerikanischen Originalausgabe: Teach Yourself .NET Windows Forms 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 zulssig. Fast alle Hardware- und Software-Bezeichnungen, die in diesem Buch erwhnt werden, sind gleichzeitig auch eingetragene Marken oder sollten als solche betrachtet werden. Authorized translation from the English language edition, entiteled SAMS TEACH YOURSELF .NET Windows Forms in 21 DAYS by Chris Payne, 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, electronis or mechanical, including photocopying, recording or by any Information storage retrieval system, without permisson from Pearson Education, Inc. German language edition published by PEARSON EDUCATION DEUTSCHLAND, Copyright 2002 Umwelthinweis: Dieses Buch wurde auf chlorfrei gebleichtem Papier gedruckt.

10 9 8 7 6 5 4 3 2 1 05 04 03

ISBN 3-8272-6455-3

2003 by Markt+Technik Verlag, ein Imprint der Pearson Education Deutschland GmbH, Martin-Kollar-Strae 1012, D81829 Mnchen/Germany Alle Rechte vorbehalten bersetzung: Michael Matzer, Waldenbruch Lektorat: Rainer fuchs, rfuchs@pearson.de Fachlektorat: Frank Langenau, Chemnitz Herstellung: Philipp Burkart, pburkart@pearson.de Satz: reemers publishing services gmbh, Krefeld, (www.reemers.de) Einbandgestaltung: Grafikdesign Heinz H. Rauner, Gmund Druck und Verarbeitung: Bercker, Kevelaer Printed in Germany

Inhaltsverzeichnis
Einleitung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Wer dieses Buch lesen sollte . . . . . . . . . . . . . . . . . . . . . . . . . . . . Vorkenntnisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Software fr die Beispiele. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Wie dieses Buch aufgebaut ist . . . . . . . . . . . . . . . . . . . . . . . . . . . Programmcode auf Begleit-CD und US-Website . . . . . . . . . . . . Konventionen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Und zu guter Letzt... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 17 18 18 19 20 21 21 23 25 26 27 28 29 30 32 33 34 35 36 37 38 39 41 42 42 42 43 45 46 47

Woche 1 Vorschau . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Tag 1 Mit Windows Forms beginnen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1 Einfhrung in die Windows-Programmierung . . . . . . . . . . . . . . Programmiersprachen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Win32-Programmierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2 Was ist .NET?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .NET-Kompilierung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Common Language Runtime (CLR). . . . . . . . . . . . . . . . . . Das .NET Framework installieren. . . . . . . . . . . . . . . . . . . . . . . . Entwicklungsumgebungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3 Was ist .NET Windows Forms? . . . . . . . . . . . . . . . . . . . . . . . . . . Integration mit .NET . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Windows Forms-Steuerelemente. . . . . . . . . . . . . . . . . . . . . . . . . 1.4 Die objektorientierte Vorgehensweise . . . . . . . . . . . . . . . . . . . . . 1.5 So erstellen Sie Ihre erste Anwendung . . . . . . . . . . . . . . . . . . . . 1.6 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.7 Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.8 Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . bung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Windows Forms-Anwendungen erstellen . . . . . . . . . . . . . . . . . . . . . . . 2.1 Ein weiterer Blick auf Ihre Anwendung . . . . . . . . . . . . . . . . . . . Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Tag 2

Inhaltsverzeichnis

2.2 2.3 2.4 2.5 2.6

Namensrume und Assemblies . . . . . . . . . . . . . . . . . . . . . . . . . . Die Main-Methode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Konstruktoren und Destruktoren. . . . . . . . . . . . . . . . . . . . . . . . . Ein abschlieender Blick auf den Programmcode . . . . . . . . . . . Das Kompilieren von Windows Forms . . . . . . . . . . . . . . . . . . . . Eine vollstndige Anwendung . . . . . . . . . . . . . . . . . . . . . . . . . . Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . bung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das objektorientierte Windows-Formular . . . . . . . . . . . . . . . . . . Mit dem Objekt Object arbeiten . . . . . . . . . . . . . . . . . . . . . . . . . Formular-Eigenschaften . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Formular-Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ereignisbehandlung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Nachrichtenschleife . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Form-Ereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . bungen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Einfhrung in Windows Forms-Steuerelemente . . . . . . . . . . . . Mens hinzufgen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Mens anpassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Kontextmens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Symbolleisten, Statusleisten und Bildlaufleisten hinzufgen. . . Symbolleisten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Statusleisten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Bildlaufleisten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . bung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

49 54 58 62 63 67 74 74 75 75 75 77 78 79 82 92 94 94 97 106 107 108 108 109 111 112 114 120 122 125 126 133 136 146 147 148 148 148

Tag 3

Mit Windows Forms arbeiten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.1

3.2

3.3 3.4 3.5

Tag 4

Mens und Symbolleisten. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.1 4.2

4.3

4.4 4.5 4.6

Inhaltsverzeichnis

Tag 5

Ereignisse in Windows Forms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.1 Was sind Ereignishandler? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ereignisse behandeln . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Mehrfache Ereignisse behandeln . . . . . . . . . . . . . . . . . . . . . . . . 5.2 Delegaten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.3 Ereignisse und Objekte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ereignishandler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Benutzerdefinierte Ereignisse erstellen . . . . . . . . . . . . . . . . . . . . Steuerelemente sezieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Benutzerdefinierte Delegaten erstellen . . . . . . . . . . . . . . . . . . . . 5.4 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.5 Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.6 Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . bung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Windows Forms mit Steuerelementen erweitern . . . . . . . . . . . . . . . . . 6.1 Das Steuerelement Button. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.2 Die Steuerelemente CheckBox und RadioButton . . . . . . . . . . . 6.3 Das Steuerelement ComboBox . . . . . . . . . . . . . . . . . . . . . . . . . . Das Kombinationsfeld instantiieren . . . . . . . . . . . . . . . . . . . . . . Die Anzeige des Steuerelements steuern . . . . . . . . . . . . . . . . . . Die ComboBox verwenden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.4 Das Steuerelement DateTimePicker. . . . . . . . . . . . . . . . . . . . . . 6.5 Das Steuerelement ImageList . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.6 Das Steuerelement ListBox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.7 Das Steuerelement PictureBox . . . . . . . . . . . . . . . . . . . . . . . . . . 6.8 Das Steuerelement TabControl . . . . . . . . . . . . . . . . . . . . . . . . . . 6.9 Die Steuerelemente TextBox und RichTextBox . . . . . . . . . . . . . Rich Text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.10 Das Steuerelement Timer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.11 Das Steuerelement TreeView . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.12 Zustzliche ntzliche Steuerelemente . . . . . . . . . . . . . . . . . . . . 6.13 Das Layout steuern. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.14 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.15 Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.16 Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . bung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

151 152 153 156 158 162 162 163 168 169 173 173 174 174 175 177 179 180 183 184 186 187 189 193 195 199 200 206 208 215 218 221 222 228 229 230 230 230

Tag 6

Inhaltsverzeichnis

Tag 7

Mit Dialogfeldern arbeiten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.1 Informationen holen und anzeigen . . . . . . . . . . . . . . . . . . . . . . . 7.2 Unterschiedliche Arten von Dialogen. . . . . . . . . . . . . . . . . . . . . Modal vs. nichtmodal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Standarddialogfelder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.3 Dialogfelder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eigene Dialogfelder erstellen . . . . . . . . . . . . . . . . . . . . . . . . . . . Informationen abrufen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.4 Standarddialogfelder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein- und Ausgabe von Dateien . . . . . . . . . . . . . . . . . . . . . . . . . . Farben und Schriftarten whlen . . . . . . . . . . . . . . . . . . . . . . . . . Drucken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.5 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.6 Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.7 Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . bung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

231 232 234 234 235 235 238 244 248 248 250 253 257 258 259 259 260 261 261 261 262 262 274 279 281 282 283 284 285 287 290 290 293 296 306 307

Woche 1 Rckblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Projekt 1: Eine Textverarbeitung erstellen. . . . . . . . . . . . . . . . . . ber das Textverarbeitungsprojekt. . . . . . . . . . . . . . . . . . . . . . . . Was Sie bentigen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Benutzeroberflche aufbauen. . . . . . . . . . . . . . . . . . . . . . . . Einige ntzliche Dinge mit Pfiff hinzufgen . . . . . . . . . . . . . . .

Woche 2 Vorschau . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Tag 8 Datenbindung in Windows Forms . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8.1 8.2 Einfhrung in die Datenbindung . . . . . . . . . . . . . . . . . . . . . . . . Daten aus jeder beliebigen Quelle . . . . . . . . . . . . . . . . . . . . . . . Einfache Datenbindung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine Schaltflche mit Daten verbinden . . . . . . . . . . . . . . . . . . . Das Bearbeiten gebundener Daten . . . . . . . . . . . . . . . . . . . . . . . Formulare mit Daten verbinden . . . . . . . . . . . . . . . . . . . . . . . . . Das Steuerelement DataGrid . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein kurzer Blick auf ADO.NET . . . . . . . . . . . . . . . . . . . . . . . . . Die Interaktion mit einem DataGrid . . . . . . . . . . . . . . . . . . . . . Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

8.3

8.4 8.5

Inhaltsverzeichnis

8.6

Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . bung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Was ist ADO.NET? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ADO.NET gegenber ADO . . . . . . . . . . . . . . . . . . . . . . . . . . . . ADO.NET und XML. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine Datenbank anlegen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine Access-Datenbank . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eine Datenbank in SQL Server 2000 anlegen . . . . . . . . . . . . . . SQL fr die Datenbankabfrage . . . . . . . . . . . . . . . . . . . . . . . . . . Das SELECT-Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das INSERT-Statement. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das UPDATE-Statement. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das DELETE-Statement. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Einfhrung in DataSet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das Objektmodell von ADO.NET . . . . . . . . . . . . . . . . . . . . . . . Verbindung zur Datenbank aufnehmen und Daten abrufen. . . Daten bearbeiten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Andere ADO.NET-Objekte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Parametrisierte Abfragen und gespeicherte Prozeduren . . . . . . . Das Objekt DataView . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . XML im Zusammenhang mit ADO.NET . . . . . . . . . . . . . . . . . Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . bung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

307 307 308 309 310 311 313 314 314 317 319 319 322 322 323 323 325 326 329 334 338 341 342 345 346 347 347 348 349 350 352 353 361 362 362 365 367 372

Tag 9

ADO.NET einsetzen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9.1

9.2

9.3

9.4 9.5

9.6

9.7 9.8 9.9 9.10

Tag 10

MDI-Anwendungen erstellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.1 Einfhrung in die Mehrfachdokumentschnittstelle . . . . . . . . . . MDI-Mens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.2 Eine MDI-Anwendung erstellen . . . . . . . . . . . . . . . . . . . . . . . . . Verschiedene Arten von untergeordneten Fenstern erstellen. . . 10.3 Die Besonderheiten von MDI-Formularen steuern . . . . . . . . . . Mens. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Untergeordnete Fenster . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Auf untergeordnete Steuerelemente zugreifen . . . . . . . . . . . . . . 10.4 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Inhaltsverzeichnis

10.5 10.6

Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . bung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Einfhrung in Streams. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Dateien und Verzeichnisse. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Dateien lesen und schreiben . . . . . . . . . . . . . . . . . . . . . . . . . . . . Binrdaten lesen und schreiben . . . . . . . . . . . . . . . . . . . . . . . . . Serialisierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das Steuerelement RichTextBox . . . . . . . . . . . . . . . . . . . . . . . . Das Objekt FileSystemWatcher. . . . . . . . . . . . . . . . . . . . . . . . . . Asynchrone Ein-/Ausgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Drucken unter .NET . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . bung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Internetprotokolle. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Austauschbare Protokolle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Clients und Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anforderungen mit WebRequest senden . . . . . . . . . . . . . . . . . . Anfragen mit WebResponse verarbeiten . . . . . . . . . . . . . . . . . . . Protokollspezifische Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . Arbeiten mit dem Internet Explorer . . . . . . . . . . . . . . . . . . . . . . Windows Forms-Steuerelemente in den Internet Explorer einbetten . . . . . . . . . . . . . . . . . . . . . . . . . Internetsicherheit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Authentifizierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Berechtigungsklassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . bung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

373 374 374 375 377 378 380 388 393 396 400 402 405 406 411 412 413 413 413 415 416 418 419 419 427 428 430 430 433 433 434 435 436 436 436 437

Tag 11

Arbeiten mit der Windows-Ein-/Ausgabe . . . . . . . . . . . . . . . . . . . . . . . 11.1 11.2

11.3 11.4 11.5 11.6 11.7 11.8

Tag 12

Formulare Internet-fhig machen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12.1 12.2

12.3

12.4

12.5 12.6 12.7

10

Inhaltsverzeichnis

Tag 13

Grafische Anwendungen mit GDI+ . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.1 Einfhrung in die Grafik und Bildbearbeitung unter Windows Pinsel und Stifte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Rechtecke und Bereiche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.2 Das Ereignis Paint . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.3 Text zeichnen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.4 Grafiken anzeigen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.5 Figuren zeichnen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Figuren ausfllen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Figuren dauerhaft machen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Figuren umwandeln. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.6 Windows Forms beschneiden . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.7 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.8 Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.9 Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . bung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ActiveX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14.1 Einfhrung in ActiveX. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14.2 ActiveX-Steuerelemente. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das ActiveX-Steuerelement Webbrowser . . . . . . . . . . . . . . . . . . Die Typbibliotheken von Microsoft Office . . . . . . . . . . . . . . . . . 14.3 .NET-ActiveX-Steuerelemente erstellen . . . . . . . . . . . . . . . . . . . 14.4 Richtlinien fr das Arbeiten mit ActiveX. . . . . . . . . . . . . . . . . . . 14.5 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14.6 Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14.7 Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . bung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

439 440 442 442 443 446 450 454 460 468 470 471 473 474 475 475 475 477 478 480 482 485 499 500 502 502 503 503 504 505 505 505 507 511

Tag 14

Woche 2 Rckblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Projekt 2: Die Textverarbeitung erweitern. . . . . . . . . . . . . . . . . . MDI hinzufgen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . E/A hinzufgen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Druckfunktionen hinzufgen . . . . . . . . . . . . . . . . . . . . . . . . . . .

11

Inhaltsverzeichnis

Woche 3 Vorschau . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Tag 15 Webdienste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15.1 Was sind Webdienste? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das Simple Object Access Protocol (SOAP). . . . . . . . . . . . . . . . Andere Protokolle fr Webdienste. . . . . . . . . . . . . . . . . . . . . . . . Mit ASP.NET Webdienste erstellen. . . . . . . . . . . . . . . . . . . . . . . Webmethoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Dienstbeschreibung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Webdienste unter ASP.NET . . . . . . . . . . . . . . . . . . . . . . . . . . . . Webdienste in Anwendungen . . . . . . . . . . . . . . . . . . . . . . . . . . . Webdienste suchen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Empfehlungen fr den Einsatz von Webdiensten. . . . . . . . . . . . Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . bung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Was sind Windows-Dienste? . . . . . . . . . . . . . . . . . . . . . . . . . . . . Einen Windows-Dienst erzeugen . . . . . . . . . . . . . . . . . . . . . . . . Dienste zum Zusammenspiel mit Windows bringen . . . . . . . . . Windows-Dienste starten und beenden. . . . . . . . . . . . . . . . . . . . Ihre Dienste berwachen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Schnittstellen fr Windows-Dienste . . . . . . . . . . . . . . . . . . . . . . In Protokolldateien schreiben . . . . . . . . . . . . . . . . . . . . . . . . . . . Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . bung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Noch einmal: Mens und die Steuerelemente Listbox, ComboBox und TabControl . . . . . . . . . . . . . . . . . . . . . DrawMode festlegen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Steuerelemente ListBox und ComboBox erweitern . . . . . . Das Steuerelement TabControl erweitern . . . . . . . . . . . . . . . . .

513 515 516 519 520 520 523 524 526 526 534 535 536 537 538 538 538 539 540 542 549 552 553 555 560 562 563 564 564 564 565 566 568 570 578

15.2

15.3 15.4 15.5 15.6 15.7 15.8

Tag 16

Windows-Dienste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16.1 16.2 16.3

16.4 16.5 16.6 16.7 16.8

Tag 17

Fortgeschrittene Techniken fr Steuerelemente in Windows Forms . 17.1 17.2

12

Inhaltsverzeichnis

17.3 17.4 17.5 17.6

Mens: Eine Frage des Besitzes . . . . . . . . . . . . . . . . . . . . . . . . . Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . bung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

581 586 587 588 588 588 589 590 591 592 594 599 605 609 610 611 613 617 618 618 619 619 621 622 626 627 628 635 645 652 653 654 654 654 655 656 659

Tag 18

Benutzerdefinierte Steuerelemente in Windows Forms . . . . . . . . . . . 18.1 Wozu benutzerdefinierte Steuerelemente erzeugen? . . . . . . . . . 18.2 Benutzerdefinierte Steuerelemente . . . . . . . . . . . . . . . . . . . . . . . Rekapitulation der Architektur fr Steuerelemente . . . . . . . . . . Eigenschaften, Methoden und Ereignisse. . . . . . . . . . . . . . . . . . Ihr Steuerelement zeichnen . . . . . . . . . . . . . . . . . . . . . . . . . . . . Alles zusammensetzen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18.3 Benutzerdefinierte Steuerelemente in Windows Forms . . . . . . . 18.4 Benutzerdefinierte Steuerelemente aus vorhandenen erzeugen Vorhandene Steuerelemente erweitern. . . . . . . . . . . . . . . . . . . . Benutzersteuerelemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18.5 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18.6 Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18.7 Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . bung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Multithreading-Anwendungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19.1 Einfhrung in Multithreading. . . . . . . . . . . . . . . . . . . . . . . . . . . Probleme mit Threading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Threading-Objekte verwenden . . . . . . . . . . . . . . . . . . . . . . . . . . 19.2 Eine Multithreading-Anwendung erstellen . . . . . . . . . . . . . . . . . Threads miteinander synchronisieren . . . . . . . . . . . . . . . . . . . . . Threadpooling einsetzen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19.3 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19.4 Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19.5 Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . bung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Windows Forms konfigurieren und bereitstellen. . . . . . . . . . . . . . . . . 20.1 Einfhrung in die .NET-Konfiguration. . . . . . . . . . . . . . . . . . . . 20.2 So konfigurieren Sie Ihre Anwendungen . . . . . . . . . . . . . . . . . .

Tag 19

Tag 20

13

Inhaltsverzeichnis

20.3 20.4 20.5 20.6 20.7 20.8

Konfigurationsabschnitte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Der Abschnitt <configSections> . . . . . . . . . . . . . . . . . . . . . . . . . Der Abschnitt <appSettings>. . . . . . . . . . . . . . . . . . . . . . . . . . . . Der Abschnitt <startup> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Der Abschnitt <system.windows.forms> . . . . . . . . . . . . . . . . . . . Konfigurationswerte abrufen . . . . . . . . . . . . . . . . . . . . . . . . . . . . Lokalisieren Sie Ihre Anwendung . . . . . . . . . . . . . . . . . . . . . . . . Eingabehilfen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Der einfache Teil der Arbeit: Bereitstellung . . . . . . . . . . . . . . . . Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . bungen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

660 662 664 665 666 667 669 676 677 678 679 680 680 680 681 682 689 690 694 696 702 703 704 704 704 705 705 705 722 723 724 809

Tag 21

Debugging und Profiling. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21.1 Debuggen in Windows Forms . . . . . . . . . . . . . . . . . . . . . . . . . . . 21.2 JIT-Debugging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das Werkzeug DbgClr. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das Werkzeug CorDbg . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21.3 Profiling Ihrer Anwendung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21.4 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21.5 Fragen und Antworten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21.6 Workshop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Quiz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . bung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Woche 3 Rckblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Projekt 3: Das Textprogramm vervollstndigen . . . . . . . . . . . . . . Eine Klasse fr eine neue Seite erzeugen . . . . . . . . . . . . . . . . . . Der weitere Weg . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Anhang A. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A.1 Antworten auf die Quizfragen und bungen . . . . . . . . . . . . . . . Stichwortverzeichnis. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

14

Inhaltsverzeichnis

Inhaltsverzeichnis der Begleit-CD


Bonuskapitel 1: Steuerelemente in Windows Forms. . . . . . . . . . . . . . 1.1 Die Klasse Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2 Die Steuerelemente Button, CheckBox und RadioButton. . . . . Das Steuerelement Button. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das Steuerelement CheckBox. . . . . . . . . . . . . . . . . . . . . . . . . . . Das Steuerelement RadioButton . . . . . . . . . . . . . . . . . . . . . . . . . 1.3 Die Steuerelemente ComboBox, ListBox und CheckedListBox Das Steuerelement ComboBox . . . . . . . . . . . . . . . . . . . . . . . . . . Das Steuerelement ListBox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Das Steuerelement CheckedListBox. . . . . . . . . . . . . . . . . . . . . . 1.4 Standarddialogfeld-Steuerelemente. . . . . . . . . . . . . . . . . . . . . . . Das Steuerelement ColorDialog . . . . . . . . . . . . . . . . . . . . . . . . . Dateidialogfelder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.5 Mens. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ContextMenu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . MainMenu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . MenuItem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.6 Das Steuerelement DataGrid . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.7 DateTimePicker . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.8 Bildlauffhige Steuerelemente . . . . . . . . . . . . . . . . . . . . . . . . . . DomainUpDown . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Form . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . NumericUpDown . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Panel. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . PrintPreviewDialog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . PropertyGrid. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . TabPage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.9 ErrorProvider . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.10 GroupBox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.11 Bildlaufleisten. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . HScrollBar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . VScrollBar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.12 ImageList . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.13 Label-Steuerelemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.14 ListView . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.15 PictureBox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 2 11 11 12 13 13 14 16 18 19 20 20 25 25 26 26 28 32 33 35 36 39 39 40 40 42 42 43 44 45 45 45 46 47 50

15

Inhaltsverzeichnis

1.16 1.17 1.18 1.19

1.20 1.21 1.22 1.23 1.24 1.25 1.26

PrintDocument . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . PrintPreviewControl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ProgressBar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Textfeld-Steuerelemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . RichTextBox. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . TextBox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Steuerelemente der Statusleiste. . . . . . . . . . . . . . . . . . . . . . . . . . TabControl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Timer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Steuerelemente der Symbolleiste . . . . . . . . . . . . . . . . . . . . . . . . ToolTip . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . TrackBar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . TreeView-Steuerelemente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

50 51 52 53 55 57 58 59 61 61 63 64 65 71 72 72 73 75 77 79 81 85 86 87 88 88 90 92 93 94 96

Bonuskapitel 2: Steuerelemente in ADO.NET: Eigenschaften und Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.1 Die DataSet- und verwandte Klassen . . . . . . . . . . . . . . . . . . . . . Constraint und ConstraintCollection . . . . . . . . . . . . . . . . . . . . . DataColumn und DataColumnCollection. . . . . . . . . . . . . . . . . 2.2 DataRelation und DataRelationCollection . . . . . . . . . . . . . . . . 2.3 DataRow und DataRowCollection . . . . . . . . . . . . . . . . . . . . . . . DataSet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . DataTable und DataTableCollection . . . . . . . . . . . . . . . . . . . . . DataView . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.4 Verwaltete Provider . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . OleDbCommand. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . OleDBCommandBuilder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . OleDbConnection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . OleDbDataAdapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . OleDbdataReader . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . OleDbError und OleDbErrorCollection . . . . . . . . . . . . . . . . . . OleDbParameter und OleDbParameterCollection . . . . . . . . . . OleDbTransaction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

16

Einleitung
Willkommen bei .NET Windows Forms in 21 Tagen! Dieses Buch wird Sie Schritt fr Schritt von den Grundlagen in Microsoft .NET Windows Forms zu recht fortgeschrittenen Themen fhren. Whrend der nchsten 21 Tage erwartet Sie ein sehr informatives und aufregendes Abenteuer. Die Windows Forms-Technologie ist Bestandteil der .NET-Initiative Microsofts. Sie erlaubt Ihnen auf sehr einfache Weise leistungsfhige Windows-Anwendungen zu erstellen, wobei Sie fast jede beliebige Programmiersprache verwenden knnen, die Sie kennen. Vom einfachen Taschenrechner bis hin zu Datenbank-gesttzten multithreaded Webdiensten knnen Sie praktisch alles erstellen (wenn Sie von Multithreading oder Datenbankzugriffen keine Ahnung haben, dann lesen Sie am besten weiter). Einfach ausgedrckt: Wenn Sie knftig Windows-Anwendungen entwickeln wollen, werden Sie Windows Forms benutzen. Whrend Sie sich durch die Lektionen dieses Buches arbeiten, werden Sie entdecken, auf welch einfache Art und Weise sich diese Aufgaben bewltigen lassen. Sie werden nicht nur Beispiele sehen und lernen, wie Sie diese und andere Anwendungen erzeugen, sondern werden auch ein Verstndnis dafr entwickeln, das Sie in die Lage versetzt, bei jedem knftigen Windows-Programmierprojekt, das Sie vielleicht anpacken wollen, zuversichtlich an dieses Unterfangen heranzugehen.

Wer dieses Buch lesen sollte


Es ist der Zweck dieses Buches, Einsteiger in Sachen Windows Forms mit dieser leistungsfhigen Technologie vertraut zu machen. Einsteiger ist jedoch ein recht allgemeiner Begriff. Ganz konkret sollten Sie dieses Buch verwenden, wenn Sie lernen wollen, wie Sie Windows-Anwendungen in .NET erstellen knnen, ganz gleich, ob Sie bereits Erfahrung mit ihrer Erstellung in einer anderen Umgebung haben oder nicht. Da aber Microsofts Zukunft auf .NET fut, wird jeder Windows-Anwendungsentwickler frher oder spter auf Windows Forms umsteigen mssen. Selbst wenn Sie Windows Forms bereits kennen, wird Ihnen dieses Buch ein unschtzbares Hilfsmittel sein, das dazu beitrgt, vergessenes Grundlagenwissen aufzufrischen und Sie auf fortgeschrittenere Projekte vorzubereiten. Wer bereits mit anderen Methoden der Anwendungsentwicklung fr Windows vertraut ist, wird Windows Forms als Erleichterung empfinden, da es bislang lstige Arbeitsschritte wie

Einleitung

etwa Speicherverwaltung zu einem Kinderspiel macht. Sie werden sehen, dass viele der Kenntnisse, die Sie bereits erworben haben, sich auch weiterhin in diesem neuen Rahmenwerk anwenden lassen. Und schlielich: Wenn Sie einfach nur neugierig auf das .NET Framework sind, dient Ihnen dieses Buch als ausgezeichnete Einfhrung und fhrt Sie durch viele der zentralen Themen, die sich auf die Anwendungsentwicklung beziehen.

Vorkenntnisse
Die einzigen absolut erforderlichen Voraussetzungen sind Grundkenntnisse im Umgang mit dem Betriebssystem Windows, von der Bedienung der Eingabeaufforderung bis zum Arbeiten mit Anwendungsfenstern. Die Kenntnis einer .NET-Programmiersprache wie C# oder VB .NET wird sich als uerst hilfreich erweisen, ist aber keine Voraussetzung. Dieses Buch fhrt Sie rasch in die Grundlagen dieser beiden Programmiersprachen ein und stellt jedes neue Thema vor, bevor diese Sprachen verwendet werden. Wenn Sie dem Code und Text folgen knnen und sowohl C# als auch VB .NET sind einfach zu lesen dann werden Sie keine Probleme haben. Kenntnisse des .NET Frameworks sind nicht erforderlich. Dieses Buch befasst sich grndlich mit dem Framework und wird ntigenfalls auf weitere Ressourcen verweisen.

Software fr die Beispiele


Die einzige Software, die Sie neben Windows NT 4.0 mit Service Pack (SP) 6, Windows 2000 oder Windows XP bentigen, ist das .NET Framework und der Windows-Editor (NotePad). Das .NET Framework ist notwendig, um Windows Forms-Anwendungen erstellen zu knnen, und lsst sich kostenlos von Microsofts Website http://www.microsoft.com/net herunterladen. Auerdem finden Sie das .NET Framework SDK auf der Begleit-CD. Um Anwendungen zu schreiben, brauchen Sie lediglich ein Textprogramm, das einfache Textdateien (zum Beispiel mit der Endung .txt) erstellen kann. Der Windows-Editor eignet sich fr diesen Zweck hervorragend. Wenn Ihnen ein anderes Textprogramm wie etwa Word, WordPad oder gar FrontPage eher behagt, dann knnen Sie selbstverstndlich auch dieses benutzen. Visual Studio .NET wird hier nicht bentigt. In der Tat wird es im ganzen Buch recht selten verwendet. Whrend es zutrifft, dass Visual Studio .NET eine Menge ausgezeichneter Funktionen fr die Anwendungsentwicklung bereitstellt, sind wir doch der Meinung, dass

18

Wie dieses Buch aufgebaut ist

es am besten ist, die Aufgaben zu Fu zu bewltigen, bevor man sie von einer solchen Entwicklungsumgebung erledigen lsst. Auf lange Sicht werden Sie dafr dankbar sein. Um schlielich die Datenbank-bezogenen Beispiele verwenden zu knnen, bentigen Sie irgendeine Form von Datenbanksystem, das zu OLE DB konform ist, beispielsweise Microsoft Access oder SQL Server. Ohne diese Werkzeuge werden Sie die diesbezglichen Beispiele nicht nutzen knnen.

Wie dieses Buch aufgebaut ist


Das Buch ist als Serie von Lernabschnitten, als Tutorial, aufgebaut, so dass jeder Tag auf dem vorhergehenden aufbaut. Es ist sinnvoll, Schritt fr Schritt vorzugehen, wenn auch nicht zwingend. Nach der Errterung des Themas folgen jeweils ein paar Beispiele, die von Analysen begleitet werden. Jedes Kapitel endet mit Antworten auf hufig gestellte Fragen, einem kurzen Quiz und einer bung. Diese Bestandteile sind sorgfltig ausgewhlt, so dass sie Ihr Wissen erweitern und Ihre Erfahrung mit der Technologie vertiefen knnen. Das Buch ist in drei logische Abschnitte aufgeteilt, von denen sich jeder ber eine Woche erstreckt, sowie in einen Block aus drei Anhngen:

Die erste Woche werden Sie damit verbringen, die Grundlagen von .NET und Windows Forms zu erlernen. Sie werden verstehen, was zur Erstellung einer Windows Forms-Anwendung gehrt, werden herausfinden, wie man dabei vorgeht und mit den Steuerelementen umgehen lernen, die Ihre Anwendung mit Funktionen versehen. Die feste Grundlage, die Sie in dieser Woche aufbauen, hilft Ihnen in den nchsten zwei Wochen. Whrend der gesamten zweiten Woche befassen Sie sich mit Technologien, die den Funktionsumfang Ihrer Windows Forms-Anwendung erweitern, angefangen mit ActiveX bis hin zu ADO.NET. Sie sind nicht unbedingt ein Teil von Windows Forms, aber Sie werden sie hchstwahrscheinlich bei der Anwendungsentwicklung bentigen. In der dritten Woche reichen die Windows Forms-Themen schon ein gutes Stck tiefer. Sie lernen, wie Sie Ihre Anwendungen vollstndig anpassen, angefangen vom Aufbau leistungsfhiger (und vermarktbarer) Steuerelemente bis hin zur Konfiguration von Installationsoptionen und Sprachen. Sie werden sich einige Techniken aneignen, mit denen Sie die Leistung Ihrer Anwendungen betrchtlich erhhen knnen: von Multithreading bis hin zu Profiling.

19

Einleitung

Am Ende jeder Woche werden Sie ein wenig Zeit auf das Rekapitulieren der Lektionen verwenden und daraufhin das Wissen dieser Woche auf ein echtes Projekt anwenden. Sie werden ein Textverarbeitungsprogramm hnlich wie Microsoft Word von Anfang an aufbauen, indem Sie es durch neu erlernte Funktionen und Technologien erweitern. Die Rekapitulation jeder Woche beruht auf den entsprechenden Lektionen. Diese Projekte bieten Ihnen die Gelegenheit, Ihre Kenntnisse, die Sie whrend dieser Woche erworben haben, anzuwenden und zu festigen.

Schlielich erhalten Sie mit den Anhngen eine vollstndige Referenz fr die in dem vorliegenden Buch behandelten Themen. Um Antworten auf die Quizfragen zu erhalten, knnen Sie hier nachschlagen. Damit knnen Sie auch Ihr Wissen ber die im Buch vorgestellten Steuerelemente und Objekte auffrischen.

Programmcode auf Begleit-CD und US-Website


Auf der Begleit-CD finden Sie den vollstndigen Code fr alle Beispiele des Buches in der amerikanischen Originalversion. Die im Buch wiedergegebenen Listings unterscheiden sich hiervon nur darin, dass Beschriftungen von sichtbaren Elementen der Benutzeroberflche sowie die Kommentare ins Deutsche bersetzt wurden. Bei eventuellen Problemen mit der Lokalisierung Ihres eigenen Codes knnen Sie so durch Vergleich mit dem Originalcode die Fehlerursachen schnell einkreisen. Den Code fr dieses Buch knnen Sie auch von der Sams Publishing-Website herunterladen: http://www.samspublishing.com/. Geben Sie die ISBN in das Suchfeld ein (0672323206 ohne Bindestriche!) und klicken Sie auf SEARCH. Sobald der Buchtitel angezeigt wird, klicken Sie darauf und gehen auf eine Seite, wo Sie den Code herunterladen knnen. Auf dieser Website finden Sie gegebenenfalls auch aktualisierte bzw. fehlerbereinigte Versionen der Codebeispiele. Weitere Ressourcen finden Sie auf meiner Homepage http://www.clpayne.com. Hier finden Sie Links zu entsprechenden Informationen wie etwa Artikeln und Quellcode.

20

Konventionen

Konventionen
Im Buch werden die folgenden typographischen Konventionen verwendet:

Codezeilen, Befehle, Anweisungen, Variablen und jeglicher Text, den Sie entweder eingeben oder der auf dem Bildschirm erscheint, werden in Schreibmaschinenschrift angezeigt. Benutzereingaben werden hufig in Fettschrift dargestellt. Platzhalter in Syntaxbeschreibungen werden kursiv gedruckt. Ersetzen Sie den Platzhalter durch den tatschlichen Dateinamen, Parameter oder das Element, das Sie verwenden mchten. Manchmal sind Codezeilen zu lang, als dass sie auf eine einzelne Zeile auf der Seite passen. An der fehlenden Zeilennummer knnen Sie erkennen, wenn eine Codezeile noch zur darber liegenden Zeile gehrt. Die Analyseabschnitte nach Codelistings besprechen den Code im Detail. Hier finden Sie heraus, was ein bestimmtes Stck Code bewirkt und wie es funktioniert, sowie hufig eine Betrachtung Zeile fr Zeile.

Ferner enthlt dieses Buch Hinweise, Tipps und Warnungen, die Ihnen dabei helfen sollen, wichtige oder hilfreiche Informationen schneller auszumachen. Einige stellen ntzliche Hilfestellungen zum effizienteren Arbeiten dar. Auerdem finden Sie ntzliche Gegenberstellungen der Art Tun Sie dies/Tun Sie dies nicht. Hier finden Sie Vorschlge ber den richtigen Weg, eine Aufgabe zu lsen (und Warnungen vor dem falschen).

Und zu guter Letzt...


Ich hoffe, Sie haben viel Spa beim Erlernen dieser interessanten Technologie. Sie wird es Ihnen ermglichen, mit Windows viele verblffende Dinge, die auf dem heutigen Markt sehr gefragt sind, anzustellen. Als Leser dieses Buches sind Sie fr Autor, bersetzer, Fachlektor und Verlag der wichtigste Kritiker und Kommentator. Bitte teilen Sie uns mit, was Sie an diesem Buch positiv finden, was wir besser machen knnten oder was Ihnen sonst am Herzen liegt. Wenden Sie sich bitte an den Lektor Rainer Fuchs, rainer.fuchs@mut.de, der Ihnen weiterhilft oder Ihre Anfrage weiterleitet.

21

Tag 1 Tag 2 Tag 3 Tag 4 Tag 5 Tag 6 Tag 7

Mit Windows Forms beginnen Windows Forms-Anwendungen erstellen Mit Windows Forms arbeiten Mens und Symbolleisten Ereignisse in Windows Forms Windows Forms mit Steuerelementen erweitern Mit Dialogfeldern arbeiten

25 45 77 111 151 177 231

W O C H E

Tag 8 Tag 9 Tag 10 Tag 11 Tag 12 Tag 13 Tag 14

Datenbindung in Windows Forms ADO.NET einsetzen MDI-Anwendungen erstellen Arbeiten mit der Windows-Ein-/Ausgabe Formulare Internet-fhig machen Grafische Anwendungen mit GDI+ ActiveX

281 309 349 377 415 439 477

W O C H E

Tag 15 Tag 16 Tag 17 Tag 18 Tag 19 Tag 20 Tag 21

Webdienste Windows-Dienste Fortgeschrittene Techniken fr Steuerelemente in Windows Forms Benutzerdefinierte Steuerelemente in Windows Forms Multithreading-Anwendungen Windows Forms konfigurieren und bereitstellen Debugging und Profiling

515 539 565 589 621 655 681

W O C H E

Woche 1: Die Grundlagen


Auf Ihrer Reise durch Windows Forms erlernen Sie in der ersten Woche die Grundlagen. Die ersten sieben Lektionen vermitteln Ihnen ein festes Fundament, so dass Sie in den Wochen 2 und 3 fortgeschrittenere Themen angehen knnen. Tag 1 mit dem Titel Mit Windows Forms beginnen stellt Ihnen das .NET Framework, Windows Forms und die Windows-Programmierung vor. Sie werden in dieser Lektion auch Ihre erste Anwendung sehen. An Tag 2 mit dem Titel Windows Forms-Anwendungen erstellen werden Sie anfangen, Ihre erste eigene Windows-Anwendung zu erzeugen und mehr darber erfahren, wie Sie diese Anwendung kompilieren und ausfhren. Tag 3 mit dem Titel Mit Windows Forms arbeiten ist eine sehr wichtige Lektion. Sie erfahren alles ber das Objekt Form, den Hauptbestandteil jeder Windows Forms-Anwendung. Sie werden erstmals auch einen Blick darauf werfen, wie Sie Ereignisse behandeln, d.h. wie Sie Ihre Anwendung auf Benutzereingaben reagieren lassen. An Tag 4 mit dem Titel Mens und Symbolleisten werden Sie von vielen der Steuerelemente im Windows Forms-Framework erfahren, die Ihnen das Leben als Entwickler sehr erleichtern. Tag 5, Ereignisse in Windows Forms, lehrt Sie, wie Sie auf verschiedene Geschehnisse in Ihrer Anwendung, wie etwa Mausklicks auf Schaltflchen oder Mens, reagieren knnen. An Tag 6, der den Titel Windows Forms mit Steuerelementen erweitern trgt, werden Sie Ihr Repertoire an Windows Forms-Steuerelementen betrchtlich aufbessern: Sie lernen alle wichtigen Steuerelemente kennen, die an Tag 4 nicht behandelt wurden. Tag 7, Mit Dialogfeldern arbeiten, bringt Ihnen schlielich alles ber eine besondere Art von Windows Forms bei. Es gibt eine ganze Reihe von Dialogfeldtypen, die in der Windows-Programmierung sehr ntzlich sind. Am Ende der Woche werden Sie das Gelernte rekapitulieren und es dazu verwenden, eine funktionierende Anwendung zu erzeugen. Lassen Sie uns ohne weiteren Verzug anfangen!

24

Mit Windows Forms beginnen

Mit Windows Forms beginnen

Willkommen zu Tag 1, Ihrem ersten Schritt in die Welt von Windows Forms! Windows Forms als Bestandteil von Microsoft .NET ist eine neue Technologie, die es Ihnen erlaubt, Windows-Anwendungen auf schnelle und einfache Weise zu erstellen. Heute fangen Sie damit an, worum es bei Windows Forms und .NET geht, und Sie werfen einen Blick auf echten Code. Heute

entdecken Sie, was Windows-Programmierung fr Entwickler bedeutet, erfahren Sie, was .NET und Windows Forms sind, finden Sie etwas ber die Vorteile heraus, die Ihnen .NET und Windows Forms bieten, erkunden Sie, wie die Common Language Runtime und die Microsoft Intermediate Language funktionieren, lernen Sie, wie Sie das .NET Framework installieren, untersuchen Sie eine einfache Windows Forms-Anwendung.

1.1

Einfhrung in die Windows-Programmierung

Das Wichtigste zuerst: Was ist ein Windows-Programm? Vielleicht sollten wir noch einen weiteren Schritt zurck tun und fragen: Was ist Microsoft Windows? Da Sie nun mal auf dem Weg sind, ein Entwickler zu werden, mssen Sie Windows mit anderen Augen betrachten. Wenn Sie bereits ein Programmierer sind, dann haben Sie bereits gelernt, ein Betriebssystem zu steuern. Aus der Sichtweise eines Normalbenutzers ist Microsoft Windows ein Betriebssystem, das den Computer zum Laufen bringt. Es gestattet ihm Dokumente zu erstellen und zu speichern, Berechnungen auszufhren, im Internet zu surfen, Dateien auszudrucken und so weiter. Wenn Sie dieses Buch lesen, dann behagt Ihnen diese Beschreibung hchstwahrscheinlich nicht, denn Sie wollen mehr wissen: wie alles unter der Motorhaube funktioniert und wie Sie Windows dazu bringen knnen, bestimmte Dinge fr Sie zu erledigen. Unter seinem nett anzusehenden ueren ist Microsoft Windows genau wie jedes beliebige andere Betriebssystem eine Umgebung fr bersetzer und zuweilen auch selbst ein bersetzer. Windows und seine Anwendungen nehmen vom Benutzer eine Eingabe entgegen und bersetzen sie in etwas, das ein Computerprozessor verstehen kann nmlich Einsen und Nullen und umgekehrt. Abbildung 1.1 gibt dieses mehrschichtige Paradigma wieder. Windows selbst wei nicht, wie es jede Eingabe bersetzen soll, und das ist der Punkt, an dem Windows-Anwendungen in Aktion treten.

26

Einfhrung in die Windows-Programmierung

Benutzereingabe

Microsoft Windows

CPU

0101101 1001..

Abbildung 1.1: Windows fungiert als bersetzer zwischen dem Benutzer und dem Hauptprozessor.

Ein Windows-Programm wird von einem Entwickler geschrieben und sagt Windows, was es tun soll, um auf eine Eingabe zu reagieren. Diese Eingabe knnte in einem Tastendruck bestehen, einem Mausklick oder auch einfach in einem Programmstart. Durch Verwendung einer Programmiersprache einer Sammlung von Schlsselwrtern, Anweisungen, Begriffen und so weiter, die fr den Computer von besonderer Bedeutung sind schreibt ein Programmierer Befehle fr das Betriebssystem, so dass es wei, wie es Eingaben in Maschinensprache zu bersetzen hat. Was ist also ein Windows-Programm? Es ist ein Befehlssatz, der Windows mitteilt, was es tun und wie es auf eine Benutzereingabe reagieren soll. Als Entwickler werden Sie solche Instruktionen schreiben vertrauen Sie mir, es ist einfacher, als Sie glauben. Wir werden diesen Vorgang nher unter die Lupe nehmen, wenn Sie Ihre ersten Anwendungen heute und morgen auf die Beine stellen.

Programmiersprachen
Wie bereits erwhnt, ist eine Programmiersprache das, womit ein Programmierer das Betriebssystem instruiert. Sie enthlt bestimmte Schlsselbegriffe und eine Syntax. Auch wenn diese Elemente fr den Laien wenig Sinn ergeben mgen, entsprechen sie einer wohldefinierten Logik. Selbst ohne Erfahrung auf diesem Gebiet haben Sie wahrscheinlich schon einmal von einigen Programmiersprachen gehrt: C, C++, C# (sih scharp ausgesprochen), Visual Basic, Pascal, Java und so weiter. Warum so viele? Jede Sprache geht das Thema ein wenig anders an und stellt dem Entwickler andere Wahlmglichkeiten bereit; ihre Anwendungen hneln gesprochenen Sprachen sehr. Beispielsweise kennt ein Eskimo (Inuit) zehn Ausdrucksweisen fr den Begriff Schnee, wohingegen die englische Sprache nur wenige besitzt. In hnlicher Weise verfgt C++ ber verschiedene Mglichkeiten, Funktionszeiger auszudrcken, whrend Visual Basic nur wenige oder keine besitzt. Das bedeutet nun nicht, dass eine der beiden Sprachen mangelhaft ist, sondern dass die zwei sich aus unterschiedlichen Wurzeln entwickelt haben. Darber hinaus verwenden die meisten Sprachen (mit den Ausnahmen, die Sie im Abschnitt Was ist .NET? kennen lernen werden) unterschiedliche Paradigmen, um Windows-Anwendungen zu erstellen. C- und C++-Programme verwenden hufig die

27

Mit Windows Forms beginnen

Microsoft Foundation Classes (MFC), wohingegen Visual Basic seinen eigenen Formulardesigner besitzt. Das fhrte dazu, dass das Erlernen einer neuen Sprache hufig auch bedeutete, eine neue Methode zur Erstellung von Windows-Anwendungen zu erlernen (was inzwischen in .NET nicht mehr der Fall ist; mehr darber spter). Programmiersprachen werden in der Regel nicht vom Betriebssystem verstanden. Vielmehr bersetzt ein Zwischenprogramm namens Compiler die Programmiersprache in Maschinensprache, also in etwas, was das Betriebssystem versteht. Man knnte theoretisch ebenfalls in Maschinensprache schreiben, doch die meisten Menschen verstehen Binrbefehle nicht. Beispielsweise drfte die Zeichenfolge 010101111 nach Unsinn aussehen, ergibt aber fr den Computer tatschlich einen Sinn. Die Sprachen, die Sie in diesem Buch benutzen werden, sind C# und Visual Basic .NET (VB .NET) die erste, weil sie speziell fr den Einsatz in der .NET-Umgebung entworfen wurde, und die zweite, weil die meisten Anfnger diese leicht erlernbare Sprache verwenden. Es ist meistens einfach, die eine in die jeweils andere zu bersetzen. Beispielsweise zeigt das folgende Stck Code zwei Befehlszeilen: eine in C# und die andere in Visual Basic .NET; beide tun das Gleiche.
C# System.Console.WriteLine("Hello World!"); VB .NET System.Console.WriteLine("Hello World!")

In diesem Fall besteht der einzige Unterschied im Semikolon am Schluss der ersten Befehlszeile. Im ganzen Buch werden Ihnen Codebeispiele in beiden Sprachen angeboten, so dass Sie das bevorzugte Beispiel whlen knnen. Ich halte es fr eine gute Idee, wenn Sie entweder C# oder VB .NET vor dem Lesen dieses Buches kennen. Sie bekommen zwar einiges dieser Sprachen durch das Lesen mit, aber Ihnen wrden viele Elemente entgehen, die Sie sonst beim separaten Erlernen dieser Programmiersprachen erhalten wrden.

Win32-Programmierung
Man hrt hufig den Begriff Win32, mit dem Entwickler um sich werfen. Win32Anwendungen sind einfach Anwendungen, die fr eine 32-Bit-Version von Windows (also Windows 95, 98, ME, 2000 und XP) erstellt wurden. ltere Versionen von Windows verwendeten 16 Bit und werden daher als Win16 bezeichnet. Der bergang von 16 zu 32 Bit stellt Ihnen viel mehr Verarbeitungsleistung zur Verfgung und gestattet Ihnen mehr Kontrolle ber den Computer. Win16-Programme laufen auf einem Win32-Betriebssystem, das gilt aber nicht umgekehrt wegen beschrnkter Verarbeitungsleistung und fehlender Abwrtskompatibilitt.

28

Was ist .NET?

Sehr einfach ausgedrckt sind also 32 Bit besser als 16 Bit, weil Sie damit mehr in der gleichen Zeit verarbeiten knnen. Fr einen Computerprozessor heit 32 Bit, dass er auf 32 Bit lange Daten im Speicher zugreifen und sie verarbeiten kann, was wesentlich mehr Platz bedeutet als 16 Bit. In der heutigen EDV-Welt sind die meisten Anwendungen, die Sie sehen, Win32 genau wie alle Anwendungen, die Sie in diesem Buch erstellen werden. Win16-Programme bezeichnet man als Legacy- oder Vermchtnis-Programme, weil sie noch aus der Frhzeit der modernen Computerei stammen (also bis ca. 1995). Viele Entwickler halten nichts von Legacy-Anwendungen, weil diese Programme meist recht alt sind und ihnen die modernen Verarbeitungstechniken fehlen.

1.2

Was ist .NET?

.NET ist Microsofts Initiative, eine neue Generation von Windows-Anwendungen aus der Taufe zu heben. .NET vereinfacht viele der hinderlichen Aspekte in der Windows-Programmierung zu einer einfachen Architektur und integriert dabei das Internet eng in alle Anwendungen. Whrend .NET ein Konzept ist, bringt das .NET Framework es zum Laufen. Sie werden das Framework bei der Arbeit mit Windows Forms ausgiebig nutzen. Einfach ausgedrckt ist das .NET Framework eine Architektur, auf deren Grundlage sowohl Internet- als auch Desktop-basierte Anwendungen erstellt und ausgefhrt werden knnen. Fr die Arbeitsweise von Windows stellt es eine vllig neue Welt dar, indem es ein standardisiertes System bereitstellt, das alle Anwendungen nutzen knnen, anstelle der vielen unterschiedlichen Frameworks, die heute verfgbar sind. .NET stellt fr diese Anwendungen auch eine neue Umgebung bereit, in der sie ablaufen knnen. Das ist effizienter, sicherer und leistungsfhiger. Kurz und gut: Mit Hilfe von .NET knnen Sie Anwendungen leichter als je zuvor erstellen. Eines der Hauptziele von .NET besteht darin, Anwendungen oder Anwendungskomponenten ber das Internet als Dienste (Services) bereitzustellen. Ein Dienst ist eine Anwendung, die ber das Web zur Verfgung steht und sich direkt oder von anderen Anwendungen nutzen lsst. Diese Dienste wren fr jeden verfgbar, der sie braucht; man whlt sich einfach ins Web ein und stellt die Verbindung zum Dienst her. Stellen Sie sich vor, Sie htten nicht solche Software wie Microsoft Word auf Ihrem Computer zu installieren. Statt dessen gehen Sie online und nutzen es von der Microsoft-Website aus, genauso, als befnde es sich auf Ihrem Computer. Abbildung 1.2 illustriert dieses Konzept.

29

Mit Windows Forms beginnen

Ihr Computer

Befehle senden

Dienst

Internet

gelieferte Daten senden

Abbildung 1.2: Ein Webdienst ist eine Anwendung oder Komponente, die ber das Internet zum Gebrauch bereitsteht.

Dieses Paradigma erspart sowohl Endanwendern als auch Entwicklern Kopfschmerzen. Die Endanwender mssen sich keine Sorgen machen, ob ihre Installation klappen wird oder ob sie ihre Anwendung pflegen, also aktualisieren mssen. Der Entwickler muss sich nicht sorgen wegen Installationsproblemen oder der Pflege mehrerer Programmversionen auf mglicherweise Tausenden von Anwendercomputern. Er erstellt das Programm einfach und stellt es ber das Internet zur Verfgung, so dass Anwender vorbeischauen knnen und es einsetzen, wann sie es bentigen. Wenn der Entwickler eine neue Version erstellt, stellen die Benutzer eine Verbindung nur deshalb her, um die neuen Funktionen zu nutzen. In den nchsten Abschnitten erfahren Sie mehr ber die Komponenten von .NET und wie sie zusammenwirken, um Ihre Arbeit als Entwickler zu vereinfachen. Es mag zwar zunchst etwas trocken zu lesen sein, doch Sie werden diese Grundlage brauchen, bevor Sie anfangen, eigene Anwendungen zu erstellen.

.NET-Kompilierung
Eine nicht auf .NET basierende Anwendung funktioniert folgendermaen: Sie wird von einem Entwickler in einer Programmiersprache geschrieben und dann zu einer ausfhrbaren Anwendung kompiliert (das ist ein sehr wichtiges Konzept). Das Betriebssystem stellt die Umgebung fr die Ausfhrung der Anwendung bereit und bietet ihr je nach Bedarf Zugriff auf seine Ressourcen. In einem Multitasking-Betriebssystem wie Windows lassen sich mehrere Anwendungen gleichzeitig ausfhren, denn jede Anwendung hat ihre eigenen Betriebssystemressourcen (Speicher usw.). Normalerweise respektiert jede Anwendung die Ressourcen der anderen, so dass keine Konflikte auftreten. Jede Anwendung hat sich auch selbst zu pflegen; das heit sie wird ausgefhrt und rumt sozusagen hinter sich wieder auf, wobei sie Ressourcen wieder freigibt, sobald sie nicht mehr gebraucht werden. Das ist im Allgemeinen eine gute Arbeitsweise. Anwendungen sind blicherweise mit sich selbst beschftigt und kmmern sich nicht darum, was andere Anwendungen so treiben. So knnte Anwendung A beispielsweise abstrzen, whrend Anwendung B weiterluft, weil die beiden sich keine Ressourcen teilen. Das Betriebssystem braucht nur minimale Ressourcen zu investieren, um die Anwendung aufrechtzuerhalten (von der Prozessorleistung einmal abgesehen). Dieses Modell hat jedoch auch ein paar Nachteile. Das Betriebssystem muss die meisten Anwendungen bei sich registrieren, damit es Einstellungen pflegen, Ressourcengrenzen

30

Was ist .NET?

bestimmen und es den Programmen erlauben kann, mit sich und anderen Anwendungen zu interagieren, wenn es ntig ist. Das kann zu einem recht komplizierten Registrierungsvorgang fhren, was wiederum umfangreiche Installationsprozeduren erfordert. Da jede Anwendung ihre eigenen Ressourcen zu verwalten hat, ist es zudem dem Entwickler berlassen, diese Ressourcen sauber zu handhaben. Man muss mitunter lstigen Code schreiben, nur um dafr zu sorgen, dass Ressourcen sauber geholt und wieder freigegeben werden, so dass es zu keinen Konflikten kommt. Die Common Language Runtime (CLR: etwa Laufzeitversion in einer gemeinsamen Sprache) ist die .NET-Form eines Managers oder Verwalters. Die CLR verwaltet alle Aspekte derjenigen Anwendungen, die in .NET laufen. Aufgrund dessen werden die Dinge in .NET ein wenig anders gehandhabt als bislang. Wenn Sie eine .NET-Anwendung erstellen, wird sie nicht zu einem direkt ausfhrbaren Programm kompiliert. Vielmehr wird Ihr Quellcode in etwas bersetzt, was als Microsoft Intermediate Language (MSIL) bezeichnet wird und im Grunde nur eine andere Methode ist, Ihren Code darzustellen. Wenn Sie spter Ihr Programm ausfhren, wird es von einem Just-In-Time (JIT) -Compiler in Maschinencode bersetzt, welcher wiederum von der CLR ausgefhrt wird. Dieser zweistufige Vorgang ist zwar langsamer als der einstufige vor der Einfhrung von .NET, aber nicht so viel, wie Sie jetzt annehmen wrden. Der JIT kann die MSIL sehr leicht verstehen. Daher lsst sie sich effizienter kompilieren als eine traditionelle Sprache. Und zum anderen bietet dieser Vorgang einen groen Vorteil gegenber der ersten Methode: nmlich plattformbergreifende Kompatibilitt. Maschinensprachen hngen von der jeweils verwendeten Maschine ab. Computer, die mit Windows und Intel-x86-Prozessoren arbeiten, sprechen andere Sprachen als solche mit Unix. Wenn man eine Anwendung auf eine andere Plattform portieren wollte, bedeutete es daher, mindestens den Quellcode neu zu kompilieren, damit er auf der neuen Plattform lief. Meistens erforderte es aber das Neu- oder Umschreiben des Codes. MSIL ist jedoch stets MSIL, ganz gleich, auf welcher Plattform sie sich befindet. Etwas, das in MSIL auf Windows funktioniert, wird auch in MSIL auf Unix funktionieren. Es ist lediglich jeweils ein entsprechender JIT-Compiler erforderlich, der den abschlieenden Schritt des Kompilierens in Maschinensprache ausfhrt; aber darum haben Sie sich in der Regel nicht zu sorgen. Groe Unternehmen wie Microsoft bieten hchstwahrscheinlich so einen Compiler fr jede Plattform, bevor Sie eine Anwendung portieren mssen. Ein weiterer Vorteil des .NET-Paradigmas gegenber dem herkmmlichen besteht darin, dass MSIL spezielle Attribute enthlt, die als Metadaten bezeichnet werden und die es einem Programmcode erlauben, sich selbst gegenber dem Betriebssystem zu beschreiben. Man erinnere sich daran, dass traditionelle Anwendungen sich oft selbst in einer zentralen Registrierdatenbank anmelden mussten, damit das Betriebssystem sie berwachen konnte. Das ist nun nicht mehr ntig, denn alle wichtigen Informationen liegen der Anwendung bei. Zu

31

Mit Windows Forms beginnen

solchen Informationen knnte gehren, nach welchen Abhngigkeiten man suchen soll, wo Sicherheitsanforderungen zu finden sind, Versionsnummern, Autoren usw. Die Bndelung solcher Informationen ist sehr hilfreich, wie Sie sehen werden, wenn Sie mit dem Codeschreiben anfangen. Nicht jede Programmiersprache oder jeder Compiler kann MSIL und Metadaten erzeugen. Daher hat Microsoft eine neue Klasse von Compilern geschaffen, die das .NET Framework untersttzt. Dadurch knnen alle Ihre bevorzugten Programmiersprachen .NET untersttzen. Mit der knftigen Verbreitung von .NET drfen Sie weitere Compiler von anderen Unternehmen erwarten. Lassen Sie uns nun mal darauf achten, was nach dem Kompilieren passiert: Das Programm wird ausgefhrt.

Die Common Language Runtime (CLR)


Die CLR bildet das Zentrum des .NET Frameworks. Stellen Sie sie sich wie den Hauptprozessor fr Ihre Anwendungen vor; sie behandelt die gesamte Ausfhrung und Verwaltung Ihrer Anwendungen. Sie hat zwar eine Menge Funktionen, doch Sie brauchen vorerst nur einige davon zu kennen. Zunchst einmal steuert die CLR den Ressourcenbedarf Ihrer Anwendung. Sie teilt deren Komponenten Speicher und Prozessorzeit zu und nimmt sie nach der Nutzung wieder zurck. Dieser Vorgang wird als Garbage Collection (Mllbeseitigung) bezeichnet, also als Speicherbereinigung. Entwickler mit einer gewissen Erfahrung, besonders in C und C++, werden sich an all die lstigen Routinen erinnern, die man schreiben musste, um Speicherressourcen zu verwalten. Diese Prozedur ist zum Glck nicht mehr ntig. Als zweiten Aspekt stellt die CLR eine vereinheitlichte Architektur bereit. Ganz gleich, welche Programmiersprache Sie nutzen, knnen Sie sich immer auf das gleiche Framework beziehen. C++-Anwendungen knnen leicht Komponenten nutzen, die in VB .NET erstellt wurden, und umgekehrt. Drittens verwendet die CLR Metadaten, die zusammen mit der jeweiligen MSIL erzeugt wurden, um eine saubere Ausfhrung zu gewhrleisten. Ein Aspekt der Metadaten ist beispielsweise, dass sie Informationen ber die Umgebung enthalten, also ber Komponenten, Versionen usw., die verwendet wurden, um Ihre Anwendung zu erstellen. Die CLR kann diese Daten untersuchen, und wenn das aktuelle System nicht die gleichen notwendigen Ressourcen besitzt, kann die CLR diese Komponenten automatisch aktualisieren. Code, der fr die CLR geschrieben wird, ist als verwalteter Code (Managed Code) bekannt: CLR verwaltet jeden Aspekt seiner Ausfhrung. Code, der nicht fr die CLR bestimmt ist (also Pr-.NET-Anwendungen), wird als nichtverwalteter Code (Non-Managed Code)

32

Was ist .NET?

bezeichnet. Von jetzt ab verwenden wir diese Begriffe. Nichtverwalteter Code lsst sich weiterhin in der CLR ausfhren, aber dafr mssen spezielle Vorkehrungen getroffen werden (darauf kommen wir am Tag 14 zu sprechen). Abbildung 1.3 zeigt die Pfade, denen verwalteter und nichtverwalteter Code in der CLR folgen.
Verwalteter Code mit MSIL Die CLR Ressourcen zuweisen JIT Kompilierung Metadaten untersuchen Sicherheitsprfungen Nichtverwalteter Code Import in .NET Ausfhrung Garbage Collection (Mllbeseitigung)

Abbildung 1.3: Die CLR verwaltet die komplette Ausfhrung von verwaltetem Code und sorgt so fr Sicherheit, Kompatibilitt und richtige Funktionsweise.

Das .NET Framework installieren


Bevor Sie die CLR einsetzen und Windows Forms schreiben knnen, mssen Sie zunchst das .NET Framework Software Development Kit (SDK) installieren. Das SDK gibt es kostenlos auf www.microsoft.com/net, aber beachten Sie, dass es ber 100 Mbyte gro ist und das Herunterladen mit einem 56K-Modem ber sechs Stunden dauern kann. Sie knnen es auch auf CD-ROM bei Microsoft bestellen (VisualStudio.NET liegt es bei). Das .NET Framework SDK erfordert Windows NT 4.0 mit Service Pack (SP) 6, Windows 2000 oder Windows XP, um richtig zu laufen. Wenn Sie ein anderes Betriebssystem besitzen, haben Sie Pech: Es gelingt Ihnen vielleicht, ein paar Beispiele zum Laufen zu bringen, aber Microsoft bietet Ihnen keinen Support, wenn das nicht gehen sollte. Sobald Sie ein Exemplar des SDKs haben, fhren Sie das Setup-Programm aus (Abbildung 1.4). Klicken Sie auf WEITER, um die Installation zu starten, und akzeptieren Sie die Lizenzvereinbarung. Klicken Sie noch dreimal auf WEITER und lehnen Sie sich zurck, whrend sich Ihnen die Tore zur .NET-Welt ffnen (in anderen Worten: whrend das SDK installiert wird). Nach dem Abschluss der Installation erscheint ein Verknpfungssymbol zu einer SDKbersicht auf Ihrem Desktop. Diese Webseite hat Verknpfungen, um die .NET-Musterbeispiele einzurichten (falls Sie sie in einem vorhergehenden Schritt installiert haben) wie auch Verknpfungen zu weiterer Dokumentation.

33

Mit Windows Forms beginnen

Abbildung 1.4: Der Installationsvorgang fr das .NET Framework SDK beginnt mit dem Setup-Bildschirm.

Entwicklungsumgebungen
In einer Entwicklungsumgebung verrichten Sie alle Ihre Arbeiten, wie etwa Anwendungserstellung und Eingeben von Code. Fr die Entwicklung mit Windows Forms brauchen Sie jedoch lediglich ein Textprogramm, um Code zu schreiben. Zustzlich zum Textprogramm bieten viele Entwicklungsumgebungen ausgetftelte Benutzeroberflchen, die fortgeschrittene Fhigkeiten fr die Verwaltung von Code und Projekten besitzen. Diese Funktionen sind hufig ntzlich, aber nicht Bedingung. Der gesamte Quellcode in diesem Buch (und auf der entsprechenden Webseite www.samspublishing.com) liegt in einfachem Text vor. Das bedeutet, dass Sie einen Texteditor wie den Windows-Editor (NotePad) als Ihre Entwicklungsumgebung nutzen knnen. (Die von Ihnen erzeugten Dateien werden zwar die Erweiterungen .cs oder .vb tragen, aber dennoch aus einfachem Text bestehen.) Auch Visual Studio .NET ist ein ausgezeichneter Editor fr die Anwendungen, die Sie erstellen. Es zeichnet Ihren Code in Farbe aus und stellt kontextabhngige Funktionen bereit, so etwa kleine Popup-Felder, die fr Sie Stze vervollstndigen. Darber hinaus stellt Visual Studio .NET zahlreiche Assistenten zur Verfgung, die Ihnen beim Erstellen von Windows Forms-Anwendungen beistehen. Obwohl es zahlreiche fr Entwickler wertvolle Funktionen in Visual Studio .NET gibt, wird sich dieses Buch nicht darauf konzentrieren. Vielmehr beschftigt es sich mit der Windows Forms-Technologie. Schlielich besteht die beste Methode, das Programmieren von Windows Forms zu erlernen, darin, sie selbst zu erstellen, und nicht, sie von einer grafischen Entwicklungsumgebung fr Sie erstellen zu lassen. Sobald Sie darin versiert sind, selbst Quellcode zu schreiben, werden Sie die Leistungsmerkmale von Visual Studio .NET umso mehr zu schtzen wissen. Fr dieses Buch brauchen Sie lediglich Notepad.

34

Was ist .NET Windows Forms?

In diesem Buch legen wir fr jede Tageslektion einen neuen Ordner an, damit sich alle Dinge leicht wiederfinden lassen. Zum Beispiel werden die Dateien von Tag 2 im Ordner c:\winforms\day2 abgelegt. Wenn Sie also fr die Beispiele in diesem Buch (oder aus dem Download von der Sams-Website) selbst Code schreiben, erstellen Sie diese Ordner und legen die Anwendungsdateien darin ab. Wenn Sie sich an dieses Bezeichnungsschema halten, knnen Sie dem weiteren Vorgehen leicht folgen.

1.3

Was ist .NET Windows Forms?

Sie fragen sich vielleicht, ob die berschrift nicht besser Was sind Windows Forms? lauten sollte. Nein, denn Windows Forms ist keine Sammlung von Formularen bzw. Masken, die man in Windows benutzt, sondern eine einzige Technologie, die Ihnen beim Entwerfen von Windows-Anwendungen hilft. Sie ist Microsofts neue Plattform fr die Entwicklung von Windows-Anwendungen, die auf dem .NET Framework basieren. Ein Blick auf die beiden Wrter Windows Forms: Sie kennen ja Windows bereits als Betriebssystem, das Benutzereingaben entgegennimmt und entsprechende Arbeiten ausfhrt. Ein Formular (form) ist genauso wie seine Papierversion etwas, das man mit Informationen ausfllt, um damit etwas Bestimmtes zu erreichen. Ein Papierformular lsst sich beispielsweise dazu verwenden, einen Pass zu beantragen, eine Prfung zu absolvieren oder sich zu bewerben. Ein digitales Formular wird blicherweise eingesetzt, um Informationen einzugeben oder um mit einer Anwendung zu interagieren. Wenn Sie schon einmal das Internet benutzt haben, haben Sie bereits etliche digitale Formulare zu Gesicht bekommen. Viele Webseiten fragen auf diese Weise Informationen vom Benutzer ab, um ihn beispielsweise fr eine Dienstleistung zu registrieren. Wahrscheinlich haben Sie bereits Dutzende, wenn nicht Hunderte solcher Formulare ausgefllt. Abbildung 1.5 zeigt ein solches Formular. Der Vorteil eines digitalen Formulars gegenber einem Papierformular besteht darin, dass das digitale mit Ihnen interagieren kann. Sobald Sie Informationen eintragen, kann das Formular die Eingabe verarbeiten oder Ihnen weitere Auswahlmglichkeiten anbieten. Genau auf dieser Idee fut die Windows Forms-Technologie. Sie verwendet digitale Formulare (die ebenfalls Windows Forms genannt werden), um Daten zu sammeln. Ein Windows Form ist ein Bildschirmfenster, mit dem der Benutzer interagiert. Es lsst sich fr das Sammeln oder Anzeigen von Daten verwenden, fr die Ausgabe von Alarmmeldungen usw. (Bitte beachten Sie, dass wir im Folgenden den Begriff sowohl fr die Technologie als auch fr das eigentliche Windows Forms-Fenster (= Formular) verwenden. Das ist zulssig, denn die Technologie besteht hauptschlich aus diesen Fenstern.)

35

Mit Windows Forms beginnen

Abbildung 1.5: Ein typisches Webformular enthlt Textfelder und eine Schaltflche.

Diese Beschreibung knnte den Eindruck erwecken, dass ein Windows Form nicht sonderlich funktionsreich sei, doch in Wirklichkeit stellt es eine Menge Leistung und Flexibilitt bereit. Weil die Windows Forms-Technologie auf dem .NET Framework beruht, steht ihr eine umfangreiche Bibliothek von ntzlichen Objekten und Methoden zur Verfgung, die es einem Windows Forms-Fenster gestatten, so ziemlich alles auszufhren, was Sie wnschen. Daher mag ein Windows Form einfach nur eine Schnittstelle sein, um so mit dem Benutzer zu interagieren, doch die Technologie hinter dem Formular liefert die entsprechende Leistungsfhigkeit.

Integration mit .NET


.NET und die CLR bieten Ihnen eine Flle von Funktionen, um die Anwendungsentwicklung viel einfacher zu machen. Wie bereits angesprochen, stellt die Garbage Collection sicher, dass alle unbenutzten oder brig gebliebenen Ressourcen, die Ihre Anwendung nicht mehr braucht, auf angemessene Weise behandelt werden und so fr den Gebrauch durch andere Anwendungen freigemacht werden. Die Einbindung in .NET erlaubt es Ihren Windows Forms, die umfangreiche Klassenbibliothek des .NET Framework zu nutzen. Diese enthlt fast alle Objekte, die Sie jemals fr Ihre Anwendungen brauchen werden, in vorgefertigter und einsatzbereiter Form. Sie brauchen eine Komponente, die auf Datenbanken zugreift? .NET stellt Ihnen mehrere zur

36

Was ist .NET Windows Forms?

Auswahl. Wie wr's mit XML-Dateien? Die hat .NET ebenfalls. Das Erlernen der Windows Forms-Programmierung besteht also zum Teil aus dem Aufbau eines mglichst groen Repertoires solcher Objekte. Das ist der Faktor, der die Experten von den Neulingen unterscheidet. Und das Beste dabei: Dieses Framework steht Ihnen zur Verfgung, gleichgltig, welche Programmiersprache Sie whlen. Apropos Datenbankzugriff: Mit Windows Forms verfgen Sie ber die Leistungsfhigkeit von ADO.NET, der nchsten Generation von ActiveX Data Objects (= ADO). Diese Weiterentwicklung fhrt viele Leistungsmerkmale ein, die die Reichweite und Flexibilitt Ihrer Anwendung erhhen. Sie knnen auf vorhandene Datenquellen zugreifen oder eigene erzeugen, knnen unverbundene Datenstze nutzen, XML-Dateien lesen und schreiben und anderes. Mehr ber diese Leistungsmerkmale erfahren Sie an Tag 9. In das .NET Framework sind auch leistungsfhige Sicherheitsmerkmale integriert. Diese gestatten es dem Betriebssystem bereits dann zu erkennen, wie sicher Ihre Anwendung ist, wenn diese noch gar nicht ausgefhrt worden ist. Es kann auch unbefugte Benutzer davon abhalten, auf Ihre Anwendungen zuzugreifen, ebenso wie es autorisierte Benutzer daran hindert, unautorisierte Anwendungen wie etwa Computerviren auszufhren. Auch das Konfigurieren und Installieren Ihrer Anwendungen ist ein Kinderspiel. Die Konfiguration erfordert hufig lediglich das Erstellen einer einzigen Textdatei mit der Endung .config. Implementierung bedeutet lediglich das berspielen Ihrer Dateien auf den Zielcomputer; die Notwendigkeit, Ihre Anwendungen zu registrieren und zu installieren, entfllt. All dies ist dank CLR und Metadaten mglich. Wie der Zusatz .NET andeutet, ist Windows Forms eng mit dem Internet und Web Services integriert. Web Services sind Komponenten von online laufenden Anwendungen, mit denen Sie eine Verbindung herstellen knnen, um so deren Funktionsumfang zu nutzen. Daher brauchen Sie diese Funktionen nicht mehr selbst zu erstellen. GDI+ (Graphical Device Interface: Schnittstelle zu Grafikgerten) erlaubt Ihnen, die grafischen Funktionen von Windows zu nutzen, um Ihre Anwendungen fr die visuelle Interaktion fit zu machen. So knnen Sie mit GDI+ beispielsweise Anwendungen einfrben, Grafiken laden und bearbeiten und sogar Fenster transparent machen. Mehr zu GDI+ finden Sie an Tag 13. Wir werden alle diese Funktionen in den nchsten 20 Tagen behandeln, so dass Sie nach dem Durcharbeiten dieses Buches ein Experte in der Windows Forms-Programmierung sind.

Windows Forms-Steuerelemente
Eine Windows Form ist fr sich allein nicht sonderlich interaktiv. Wie Sie spter an diesem Tag sehen werden, ist die Zahl der Mglichkeiten dessen, was ein Benutzer mit einem Formular tun kann, recht begrenzt. Windows Forms-Steuerelemente die es alle in der

37

Mit Windows Forms beginnen

Klassenbibliothek des .NET Framework gibt knnen Ihrer Anwendung erheblich mehr Dynamik verleihen, indem sie Ihnen interaktive Objekte bereitstellen, wie zum Beispiel Schaltflchen, Textfelder, Mens und Statusleisten. Alle Windows Forms-Steuerelemente (Controls) arbeiten in einer gemeinsamen Architektur das macht das Erlernen neuer Steuerelemente so einfach. Jedes Steuerelement lsst sich durch seine Eigenschaften vollstndig steuern. Sollte es noch keine Steuerelemente geben, die Ihnen zusagen, so knnen Sie leicht Ihre eigenen erstellen. Die meisten verbreiteten HTML-Steuerelemente haben in Windows Forms ein Gegenstck. Ab morgen werfen Sie einen Blick auf einige Steuerelemente, umfassender gehen wir ab Tag 4 auf dieses Thema ein.

1.4

Die objektorientierte Vorgehensweise

Das .NET Framework ist vollstndig objektorientiert. Und was bedeutet das? Objektorientierte Programmierung (OOP) ist ein Programmierparadigma, das eine logische Mglichkeit zur Anwendungsentwicklung bietet; es verknpft Programmierbegriffe mit der Realitt. In echter OOP ist alles ein Objekt, genauso wie alles, mit dem Sie tglich interagieren, ein Objekt ist. Ihr Auto, Ihre Kaffeemaschine, Ihr Stuhl alle sind Objekte. Jedes Objekt verfgt ber Eigenschaften, die es definieren. Ihre Auto etwa hat eine Farbe, gefahrene Kilometer, die Eigenschaft Hersteller und so weiter. Mit all diesen Eigenschaften lsst sich ein Objekt auf eindeutige Weise beschreiben. Meistens haben Sie in der OOP mit Objekten zu tun. Ein Windows Form ist ein Objekt, ebenso wie eine ASP.NET-Seite. Objekte verfgen auch ber Methoden bzw. Aktionen oder Verhaltensweisen. Ihr Auto hat Methoden anzuspringen, zu bremsen, anzuhalten usw. Ein Windows Form hat Methoden zu starten, zu schlieen, in der Gre verndert zu werden, die Farbe zu ndern und viele weitere Verhaltensweisen. Abbildung 1.6 zeigt Beispiele von Objekten und einige ihrer Eigenschaften und Methoden. Darber hinaus findet man oft Objekte, die auf dem gleichen grundlegenden Konzept basieren. Sowohl Pkws als auch Lastwagen sind Motorfahrzeuge, wenn auch im Detail verschieden. Manchmal gibt es Objekte, die auf einem anderen Objekt basieren; so etwa stammt der neue VW Kfer vom ursprnglichen Kfer ab. Dies sind Prinzipien (nmlich Schnittstellen bzw. Vererbung), die sich auch in der OOP wiederfinden. Sie bieten Ihnen die Flexibilitt, um Ihre vorhandenen Objekte zu erweitern, wenn sie nicht genau Ihre Wnsche erfllen.

38

So erstellen Sie Ihre erste Anwendung

Auto-Objekt
Eigenschaften: Farbe, Hersteller Methoden: Start, Tr ffnen

Scheinwerfer-Objekt
Eigenschaften: Helligkeit, Farbe Methoden: ein-/ausschalten

Rad-Objekt
Eigenschaften: Gre, Luftdruck Methoden: Abrieb des Gummis

Abbildung 1.6: Ein Auto weist viele Eigenschaften und Methoden auf, wie auch Unterobjekte, die ihrerseits ber Eigenschaften und Methoden verfgen.

Windows Forms basiert auf Prinzipien der OOP: Jeder Teil einer Windows Forms-Anwendung ist ein Objekt, vom sichtbaren Fenster bis hin zu den anklickbaren Schaltflchen, den ausfllbaren Textfeldern und sogar bis zur Tageszeit, zu der Sie die Anwendung nutzen. Selbst wenn Sie noch nie OOP eingesetzt haben, drfte Ihnen dies einleuchten. Solange Sie sich alles als ein selbststndiges Objekt vorstellen, werden Sie schnell lernen. Da alles in .NET ein Objekt ist, werden Sie, nachdem Sie den Umgang mit einem Objekt kennen, Bescheid wissen, wie Sie dieses Wissen auf alle brigen Objekte bertragen. Das macht Ihr Leben als Entwickler leichter. Wir werden uns diesem Thema an Tag 3 widmen, nachdem Sie ein paar Beispiele fr Objekte in Windows Forms gesehen haben.

1.5

So erstellen Sie Ihre erste Anwendung

Hchste Zeit, sich ein bisschen in Code zu vertiefen! Geben Sie den Code aus Listing 1.1 in Ihren Editor ein und speichern Sie ihn als Datei c:\winforms\day1\helloworld.vb. Denken Sie daran, im Dialogfeld SPEICHERN UNTER den Eintrag Alle Dateien im Feld DATEITYP auszuwhlen und nicht TXT-Dateien. Sonst wird nmlich Ihre Datei als helloworld.vb.txt gespeichert! Listing 1.1: Ihre erste Visual Basic .NET Windows Forms-Anwendung!
1: 2: 3: 4: 5: 6: 7: Imports System Imports System.Windows.Forms Public Class HelloWorld : Inherits Form Public Shared Sub Main() Application.Run(New HelloWorld)

39

Mit Windows Forms beginnen

8: 9: 10: 11: 12: 13: 14:

End Sub Public Sub New() Me.Text = "Hallo Welt!" End Sub End Class

Keine Sorge, wenn Sie diesen Code nicht verstehen sollten. Sie erfahren morgen, was dies bedeutet (wir untersuchen morgen auch C#-Code). Ein kurzer berblick soll vorerst gengen: Die Zeilen 2 und 3 verwenden eine spezielle Syntax, um Ihrer Anwendung zu gestatten, die Namensrume oder Objektgruppen (aus der .NET-Klassenbibliothek) System und System.Windows.Forms zu nutzen. Das Schlsselwort Imports referenziert diese Namensrume zwecks einfacherer Verwendung. In Zeile 4 deklarieren Sie Ihre Klasse bzw. Ihr Objekt. Die Zeilen 6-8 stellen den Startpunkt fr Ihre Anwendung dar und die Zeilen 10-12 sind ein so genannter Konstruktor. In Zeile 14 schlieen Sie Ihre Klasse ab. Ein Groteil dieses Codes konfiguriert Ihre Anwendung. Die einzige echte Ausfhrungsanweisung finden Sie in Zeile 11, die aussieht, als ob sie irgendwo in der Anwendung den Gru Hallo Welt! anzeigen wrde. Der nchste Schritt besteht darin, Ihre Anwendung zu MSIL zu kompilieren. ffnen Sie ein Fenster mit einer Befehlseingabeaufforderung (START / (ALLE) PROGRAMME / ZUBEHR / EINGABEAUFFORDERUNG) und navigieren Sie zu dem Ordner, in welchem Sie diese Datei gespeichert haben. Geben Sie den folgenden Befehl in das Fenster ein und drcken Sie ():
vbc /t:winexe /r:System.dll /r: System.Windows.Forms.dll helloworld.vb

Kmmern Sie sich hier noch nicht um die Syntax. Es gengt, dass Sie wissen, dass dieser Befehl eine Visual Basic .NET-Anwendung zu einer ausfhrbaren MSIL-Datei kompiliert. Sie sollten folgende Ausgabe sehen:
C:\winforms\day1>vbc /t:winexe /r:system.dll [ic:ccc] /r: system.windows.forms.dll helloworld.vb Microsoft (R) VisualBasic.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:\winforms\day1>

Falls keine Fehlermeldungen angezeigt werden, steht Ihre Anwendung zur Ausfhrung bereit. (Falls Sie helloworld.exe nicht finden, suchen Sie die Datei mit Ihrer Suchfunktion.) Geben Sie an der Eingabeaufforderung helloworld ein; es sollte dann ein Fenster wie in Abbildung 1.7 zu sehen sein.

40

Zusammenfassung

Abbildung 1.7: Hier sehen Sie Ihre erste Windows Forms-Anwendung namens Hallo Welt!

Diese Anwendung ist zwar noch nicht reich an Funktionen, beachten Sie aber bitte die Titelleiste Hallo Welt!. Herzlichen Glckwunsch zur Fertigstellung Ihrer ersten, wenn auch bescheidenen Windows Forms-Anwendung!

1.6

Zusammenfassung

Um Sie mit dem .NET Framework und Windows Forms bekannt zu machen, war dies heute nur eine kurze Lektion. Man muss zuerst ber diese grundlegenden Themen sprechen, so dass der Code spter einen Sinn ergibt, wenn man ihn sich genauer ansieht. Heute haben Sie gelernt, wie das Betriebssystem mit Ihren Anwendungen und Benutzereingaben zusammenarbeitet. Das .NET Framework ist eine neue Architektur, um damit Windows-Anwendungen zu erstellen. Damit knnen Sie unter Verwendung jeder beliebigen Programmiersprache eine Anwendung erstellen, wohlgemerkt: stets auf der gleichen standardisierten Architektur. Mit der Common Language Runtime (CLR) lassen sich Ihre Anwendungen zu Microsoft Intermediate Language (MSIL) kompilieren statt direkt zu einer ausfhrbaren Datei. MSIL enthlt Metadaten, die wiederum der CLR Informationen ber Ihre Anwendung, darunter Versionsnummer und Sicherheitsangaben, zur Verfgung stellen. Die Metadaten befreien Sie von der Notwendigkeit, Ihre Anwendung bei einem Betriebssystem anzumelden. Windows Forms ist als Teil von .NET ein Framework (Gerst) fr die Erstellung von Windows-Anwendungen, die die CLR nutzen. Sein Vorteil gegenber herkmmlicher Windows-Programmierung liegt in der vereinheitlichten, objektorientierten Architektur, die die Klassenbibliothek des .NET Frameworks nutzen kann. Von Ihrer ersten Windows Forms-Anwendung haben Sie gelernt, dass der Quellcode eine einfache Textdatei ist und sich im Editor erstellen lsst. Er muss zu MSIL kompiliert werden, bevor man ihn verwenden kann; Sie haben das getan, indem Sie einen Compiler an der Eingabeaufforderung benutzten.

41

Mit Windows Forms beginnen

1.7
F

Fragen und Antworten

Kann ich wirklich jede gewnschte Programmiersprache benutzen, um Windows Forms zu erstellen? A In der Theorie schon, aber in der Praxis nicht ganz. Sie brauchen einen Compiler, der Ihren Code in MSIL umwandelt und Metadaten ausgibt. Die einzigen Sprachen, die dies zur Zeit beherrschen, sind C++, C# und Visual Basic .NET, obwohl die Untersttzung fr viele weitere Sprachen gerade entwickelt wird.

Was versteht man unter ASP.NET? A ASP.NET ist eine weitere Facette von .NET. Es erlaubt Ihnen, interaktive Webseiten und Web Services zu erstellen. ASP.NET ist Windows Forms sehr hnlich beide haben die gleichen Begriffe und Frameworks gemeinsam , doch ASP.NET wird verwendet, um Internetanwendungen zu schreiben, whrend Windows Forms fr Windows-Anwendungen eingesetzt wird.

1.8

Workshop

Dieser Workshop soll Ihnen helfen, sich besser an die heute behandelten Begriffe zu erinnern. Es hilft Ihnen sehr, die Antworten vollstndig zu verstehen, bevor Sie weiterlesen. Die Antworten zu den Quizfragen und den bungen finden Sie in Anhang A.

Quiz
1. Wahr oder falsch? Windows Forms ist Win32-Programmierung. 2. Wahr oder falsch? Metadaten enthalten Angaben darber, in welcher Umgebung eine Anwendung erstellt wurde. 3. Auf welche Weise ermglicht MSIL plattformbergreifende Funktionsweise? 4. Wie nennt man die Weiterentwicklung von ActiveX Data Objects? 5. Wahr oder falsch? Man kann Windows Forms-Anwendungen mit jedem beliebigen Texteditor erstellen. 6. Was ist ein Windows Forms-Steuerelement? 7. Was bewirkt das Imports-Statement?

42

Workshop

bung
Was wrde der folgende Code bewirken, sobald er kompiliert und ausgefhrt wrde?
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: Imports System Imports System.Windows.Forms Public Class MyApp : Inherits Form Public Shared Sub Main() Application.Run(New MyApp) End Sub Public Sub New() Me.Height = 100 Me.Width = 50 End Sub End Class

43

Windows FormsAnwendungen erstellen

Windows Forms-Anwendungen erstellen

Nachdem Sie die Grundlagen verstanden haben, ist es nun an der Zeit, sich kopfber in die Erstellung von Windows Forms-Anwendungen zu strzen. In der heutigen Lektion werfen Sie einen weiteren Blick auf die gestrige Anwendung und konzentrieren sich dabei auf die verschiedenen Elemente einer Windows Forms-Anwendung. Sie betrachten die Erstellung von Windows Forms mit C#, derjenigen Sprache, die viele Entwickler fr Windows Forms bevorzugen. Sie untersuchen die Unterschiede im Code, den VB .NET und C# produzieren. Am Ende der heutigen Lektion werden Sie die eine Sprache leicht in die andere bersetzen knnen. Heute lernen Sie,

was Klassen, Assemblies und Namensrume sind, wie Vererbung funktioniert, auf welche Weise man die Main-Methode einsetzt, was Konstruktoren und Destruktoren sind, wie Sie Quellcode kompilieren, in welcher Weise Sie alles zusammensetzen, um eine Anwendung zu erhalten.

2.1

Ein weiterer Blick auf Ihre Anwendung

Bitte erinnern Sie sich an die gestrige Zusammenfassung. Alles Weitere baut darauf auf. Nun lassen Sie uns den gestrigen Code betrachten, den Sie in Listing 2.1 finden. Listing 2.2 zeigt den gleichen Code in C#. Weil alle Windows Forms-Anwendungen das gleiche .NET Framework verwenden, sind die Quellcodes in Visual Basic .NET und C# fast identisch, abgesehen von ein paar syntaktischen Unterschieden. Alle Konzepte in dem einen Quellcode beziehen sich auch auf den anderen. Listing 2.1: Ihre erste Visual Basic .NET-Windows Forms-Anwendung
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: Imports System Imports System.Windows.Forms Public Class HelloWorld : Inherits Form Public Shared Sub Main() Application.Run(New HelloWorld) End Sub Public Sub New() Me.Text = "Hello World!"

46

Ein weiterer Blick auf Ihre Anwendung

12: 13: 14:

End Sub End Class

Listing 2.2: Ihre erste C#-Windows Forms-Anwendung


1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: using System; using System.Windows.Forms; public class HelloWorld : Form { public static void Main() { Application.Run(new HelloWorld()); } public HelloWorld() { this.Text = "Hello World!"; } }

Beachten Sie, dass C# auf Gro-/Kleinschreibung achtet, so dass der Code genau so wie in Listing 2.2 geschrieben sein muss, sonst erhalten Sie Fehlermeldungen. Wir gehen hier nicht nher auf den Code ein, aber Ihnen werden einige hnlichkeiten auffallen. Es finden sich Namensrume, Klassen, Methoden und eine Main-Methode in beiden Listings. In den folgenden Abschnitten gehen wir auf diese einzelnen Begriffe ein.

Klassen
Wenn Sie noch keine OOP-Sprache verwendet haben, drfte Ihnen der obenstehende Code merkwrdig erscheinen. Lassen Sie uns zunchst Zeile 4 in beiden Listings betrachten, denn sie enthlt die wichtigsten Konzepte. Diese Zeile deklariert unsere Klasse namens HelloWorld. Eine Klasse ist im Wesentlichen die Definition eines Objekts. In der Klasse bestimmen Sie die individuellen Eigenschaften sowie die Funktionen, die andere Objekte vielleicht einmal nutzen. Stellen Sie sich eine Klasse wie eine Blaupause oder einen Bauplan fr ein Haus vor. Die Blaupause selbst ist nicht gerade reich an Funktionen; man kann kaum etwas damit tun. Sie legt jedoch alles fest, was das Haus bentigt: wohin die Tren und die Lampen kommen, die Zimmergre, die Leitungen und das ganze brige Innenleben eines Hauses. Ein Bauleiter benutzt die Blaupause bzw. einen Bauplan, um das tatschliche Haus zu

47

Windows Forms-Anwendungen erstellen

erbauen, das ein gebrauchsfertiges Objekt darstellt: Sie knnen die Tren ffnen und schlieen, die Wandfarbe ansehen (und ndern) usw. hnlich wie eine Blaupause legt eine Klasse das ganze Innenleben eines Objekts fest. In unserem Fall entsprche der Bauleiter der CLR, denn sie untersucht die Blaupausen und wandelt sie zu etwas Brauchbarem um: zu einem Objekt oder einer Anwendung. Somit knnen die Benutzer Ihres Objekts Methoden ausfhren, die Sie festgelegt haben, und verfgbare Eigenschaften untersuchen. Kurz und gut: Sie erstellen eine Klasse, die von der CLR in etwas Ntzliches umgewandelt wird. Der jeweilige Benutzer des Objekts braucht sich nicht darum zu kmmern, wie das Innere definiert ist, sondern benutzt es einfach genau wie ein Hausbewohner im Allgemeinen nicht wissen muss, wie die Klimaanlage funktioniert, solange sie nur zuverlssig ihren Dienst versieht. Diese Abstraktion von Komplexitt wird als Kapselung bezeichnet. Mit Hilfe von Klassen kapseln Sie die Einzelheiten und Implementierungsdetails, so dass sich der Benutzer nicht darum zu kmmern braucht. Abbildung 2.1 illustriert den Begriff der Kapselung.
Public Class Hello end class

1. Was der Benutzer nicht zu sehen bekommt

2. Die CLR erzeugt das Objekt

Object

3. Was der Benutzer sieht und verwendet

Abbildung 2.1: Die CLR kapselt Ihre Klassenbauplne in einem Objekt.

Wenn Ihnen diese Vorstellung immer noch nebuls vorkommt, sorgen Sie sich nicht. Wenn wir diese und andere Klassen untersuchen, werden Sie rasch ein grundlegendes Verstndnis entwickeln. Die Wrter Klasse und Objekt werden oft synonym verwendet. Solange Sie den Unterschied kennen eine Klasse legt die innere Funktionsweise eines Objekts fest , ist das in Ordnung. Zurck zu Zeile 4: Wir mssen noch zwei Teile untersuchen. Das Schlsselwort Public bedeutet, dass diese Klasse fr andere Klassen zugnglich ist. Wenn Sie umgekehrt das Schlsselwort Private benutzen, kann keine andere Klasse diese Klasse sehen oder benutzen. Wenn sie nicht ffentlich wre, knnte sie Ihre Anwendung nicht nutzen. Es lassen sich hier ein paar weitere Schlsselwrter verwenden, so etwa Protected oder Friend, aber wir werden diese zu gegebener Zeit betrachten.

48

Ein weiterer Blick auf Ihre Anwendung

Der letzte Bestandteil von Zeile 4, nmlich Inherits Form, ist etwas Besonderes fr objektorientierte Programme (die C#-Version verwendet das Schlsselwort Inherits nicht, sondern einfach einen Doppelpunkt). Alles in .NET ist ein Objekt (oder eine Klasse). Daher gibt es auch eine Klasse namens Form. Sie ist die Basisklasse fr alle Windows FormsAnwendungen: Sie enthlt Funktionen, die es gestatten, dass ein Formular angezeigt, geschlossen, bewegt und skaliert werden kann. Daher muss jede von Ihnen erstellte Windows Forms-Anwendung auf diese Form-Klasse zugreifen. Das Schlsselwort Inherits teilt der CLR mit, dass alle Eigenschaften und Methoden in dieser Form-Klasse automatisch Ihrer Klasse HelloWorld hinzugefgt werden sollen. Das bedeutet, die Klasse HelloWorld erbt sie von der Klasse Form. Ebenso wie Sie Ihre blauen oder braunen Augen von Ihren Eltern geerbt haben, erbt die HelloWorld-Klasse von ihrer Elternklasse Form die Fhigkeit, ein Fenster am Bildschirm anzuzeigen. Die Form-Klasse erbt ihrerseits von einer anderen Klasse namens ContainerControl, welche von der Klasse Control erbt, welche ihrerseits von der Klasse Component erbt und so weiter und so fort. Allen liegt die Object-Klasse zu Grunde, die Urahnin aller .NET-Klassen. Sie enthlt Eigenschaften, die alle anderen Klassen gemeinsam nutzen. Wenn Sie also wissen, wie Sie mit einem Objekt interagieren, knnen Sie demzufolge auch mit allen seinen Abkmmlingen interagieren. Jeder Abkmmling erbt Merkmale von allen seinen Vorgngern. Vererbung erspart uns eine ganze Menge Codeschreiben bzw. Umschreiben. Vererbung erleichtert auch das Erlernen neuer Klassen. Sofern Sie wissen, wie Sie mit ihren Basisklassen umgehen, verstehen Sie auch mit einer neuen Klasse umzugehen. An Tag 3 werden wir die Klasse Form genauer unter die Lupe nehmen. Da Sie nun ber Klassen Bescheid wissen, sollten Sie auch erfahren, dass auch die Form-Klasse Public ist. Sonst wre Ihre Klasse HelloWorld nicht in der Lage, darauf zuzugreifen. Nur nicht-Private Mitglieder der Form-Klasse werden vererbt. Nicht nur Klassen knnen erben, sondern auch Methoden, und zwar mit der gleichen Syntax. Klassen und Vererbung sind die zwei wichtigsten Konzepte in der OOP. Sobald Sie diese einmal verstanden haben, fllt Ihnen der Rest wesentlich leichter. ber diese Themen erfahren Sie mehr im ganzen restlichen Buch.

Namensrume und Assemblies


Die Zeilen 1 und 2 in Listing 2.1 enthalten ein weiteres wesentliches Element von Windows Forms-Anwendungen: das Schlsselwort Imports (in C# lautet es using, wie Sie in Listing 2.2 sehen). Wie Sie bereits erfahren haben, erlaubt Ihnen dieses Schlsselwort, Sammlungen anderer Klassen in Ihrer Anwendung zu verwenden. Solche Sammlungen werden als Namensrume bezeichnet. Sie dienen dazu, miteinander verwandte Klassen zu gruppieren.

49

Windows Forms-Anwendungen erstellen

In Zeile 1 importiert die Anwendung den Namensraum System. Innerhalb des Namensraumes System befinden sich zahlreiche Objekte und Klassen, die Ihre Anwendung nutzt, selbst wenn Sie es nicht wissen. Beispielsweise sind die gebruchlichen Datentypen Integer und String Bestandteil des Namensraumes System. Wenn Sie also eines dieser Objekte nutzen, ist es gut, den Namensraum System zu importieren (zwar eine gute Idee, aber nicht zwingend). Die Zeile 2 importiert den Namensraum System.Windows.Forms, welcher die Klasse Forms enthlt. Der Forms-Namensraum ist einer von vielen im Windows-Namensraum, und dieser ist wiederum nur einer von vielen im System-Namensraum. Namensrume sind hierarchisch organisiert, um ihre Nutzung intuitiv und einfacher zu machen, insbesondere dann, wenn man schon die OOP-Grundbegriffe versteht. Ein paar der gebruchlicheren Namensrume sind:

System System.Data System.Drawing System.Net System.Security System.Web System.Web.UI System.Web.Services System.Windows System.Windows.Forms System.Xml

Der Umstand, dass Namensrume hierarchisch untereinander angeordnet sind, bedeutet nicht notwendigerweise, dass der eine irgendetwas vom anderen erbt. Namensrume sind im Unterschied zu Klassen lediglich Sammlungen von Objekten aus ihnen werden keine nutzbaren Objekte erstellt. Man verwendet sie lediglich aus organisatorischen Grnden. Natrlich knnen Sie leicht Ihre eigenen Namensrume erstellen. Es mag an diesem Punkt noch nicht ntig sein, aber sobald Sie komplexere Anwendungen erzeugen, die aus mehr als einer Quellcodedatei bestehen, wollen Sie sie vielleicht in einem Namensraum gruppieren. Beispielsweise so:
Namespace MyNamespace Public Class MyClass : Inherits Form 'etwas mehr Code End Class End Namespace

50

Ein weiterer Blick auf Ihre Anwendung

Der restliche Code bleibt genau, wie er ist. Sie haben ihn nur mit einem Namensraum umgeben. In diesem Fall wird der Namensraum MyNamespace genannt. Sie knnen ihn auch vorhandenen Namensrumen hinzufgen:
Namespace System.Windows.Forms

Es hngt alles davon ab, wie Sie diese Dinge gruppieren mchten. Wie gesagt, brauchen Sie Namensrume nicht unbedingt zu importieren. Dieses Vorgehen erlaubt Ihnen jedoch, weiter unten in Ihrem Code Kurzbefehle, also Abkrzungen, zu verwenden. Wenn Sie beispielsweise Zeile 2 aus Listing 2.1 entfernen wrden, mssten Sie Zeile 4 von
Public Class HelloWorld: Inherits Form

in
Public Class HelloWorld: Inherits System.Windows.Forms.Form

ndern. Mit anderen Worten: Sie htten dann den gesamten Namensraum-Namen fr die Form-Klasse zu spezifizieren. Das Gleiche gilt fr die Klasse System: Wenn Sie sie nicht importieren, wrden Ihre Datentypen wie System.Integer statt nur wie Integer aussehen.
Ich empfehle Importieren Sie Namensrume mit dem Schlsselwort Imports, wenn Sie Klassen verwenden werden, die in diesem Namensraum enthalten sind; das hilft bei der Organisation und spart Zeit. Bitte beachten Sie Verlassen Sie sich nicht auf die Verwendung des kompletten Namensraum-Namens fr alle Ihre Objekte, selbst wenn Sie meinen, dass Ihnen das hilft, Klassen zu unterscheiden. Dieses Vorgehen blht Ihren Code auf und erschwert es, auf Ihr Objekt in der Hierarchie zu verweisen. Gelegentliche Verwendung ist in Ordnung, aber bertreiben Sie es nicht.

Wenn Sie in Ihrer Anwendung keinen Namensraum festlegen, wird automatisch ein Standardnamensraum auch als Globaler Namensraum bezeichnet erzeugt. Er hat keinen eigentlichen Namen und dient lediglich dazu, die Klassen zu gruppieren, die nicht explizit in einem Namensraum deklariert wurden. Namensrume sind also logische Gruppierungen verwandter Klassen, aber das sagt noch lange nichts ber ihre physische Position aus. Sie knnen diverse Quellcode-Dateien in verschiedenen Teilen Ihrer Festplatte abgelegt haben, aber alle knnen demselben Namensraum angehren. Um mehrere Dateien und Namensrume an einer einzigen Stelle zusammenzufassen, benutzt man Assemblies. Das sind physische Dateien auf Ihrer Festplatte, die die von Ihnen erstellten Klassen enthalten (meist mit der Endung .dll). Sie knnen mehrere Klassen oder Namensrume in einer einzelnen Assembly unterbringen.

51

Windows Forms-Anwendungen erstellen

Beispielsweise werden Sie in Ihrem Ordner c:\winnt\Microsoft.NET\Framework\version\ die Datei system.dll entdecken. Diese Assembly enthlt die Namensrume System, System.CodeDom (und dessen Unternamensrume), System.ComponentModel, System.Net (und dessen Unternamensrume) und noch ein paar andere. Wie man sie verwendet, untersuchen wir etwas spter heute im Abschnitt Das Kompilieren von Windows Forms. Tabelle 2.1 listet die verschiedenen .NET-Assemblies und die darin enthaltenen Namensrume auf.
Assembly
custommarshalers.dll mscorlib.dll

Namensrume
Runtime.InteropServices.CustomMarshalers Collections, Configuration.Assemblies, Diagnostics.SymbolStore, Globalization, IO, IO.IsolatedStorage, Reflection, Reflection.Emit, Resources, alle Runtime.*-Namensrume (auer jenen in CustomMarshalers.dll, System.Runtime.Remoting.dll und System.Runtime.Serialization.Formatters.Soap.dll), Security, Security.Cryptography, Security.Cryptography.X509Certificates, Security.Permissions. Security.Policy, Security.Principal, Text, Threading System, CideDom, CodeDom.Compiler, Collections.Specialized, ComponentModel, ComponentModel.Design, Configuration, Diagnostics, Net, Net.Sockets, Text.RegularExpressions, Timers Configuration.Install

system.dll

system.configuration.install.dll system.data.dll system.design.dll

Data, Data.Common, Data.OleDb, Data.SqlClient, Data.SqlTypes ComponentModel.Design.Serialization, Web.UI.Design, Web.UI.Design.WebControls, Windows.Forms.Design DirectoryServices

system.directoryservices.dll system.drawing.dll

Drawing,Drawing.Drawing2D, Drawing.Imaging, Drawing.Printing, Drawing.Text Drawing.Design

system. drawing.design.dll

system.enterpriseser- EnterpriseServices, System.EnterpriseServices.CompensatingResourcevices.dll Manager system.management.dll Management, Management.Instrumentation system.messaging.dll Messaging

Tabelle 2.1: NET-Assemblies und -Namensrume

52

Ein weiterer Blick auf Ihre Anwendung

Assembly
system.runtime. remoting.dll

Namensrume
Runtime.Remoting.Channels, Runtime.Remoting.Channels.Http, Runtime.Remoting.Channels.Tcp, Runtime.Remoting.MetadataServices system.Runtime.Serialization.Formatters.Soap.dll

Runtime.Serialization.Formatters.Soap
system.security.dll system.serviceprocess.dll system.web.dll Security.Cryptography.Xml ServiceProcess

Web, Web.Caching, Web.Configuration, Web.Hosting, Web.Mail, Web.Security, Web.SessionState, Web.UI, Web.UI.HtmlControls, Web.UI.WebControls Web.Services, Web.Services.Configuration, Web.Services.Description, Web.Services.Discovery, Web.Services.Protocols Windows Forms

system.web.services.dll system.windows.forms.dll system.xml.dll

Xml, Xml.Schema, Xml.Serialization, Xml.Path, Xml.Xsl

(Soweit nicht anders spezifiziert, sind alle Namensrume Sub-Namensrume des Namensraums System.) Tabelle 2.1: NET-Assemblies und -Namensrume (Forts.)

Es gibt eine Menge Namensrume in Tabelle 2.1, von denen Sie manche in diesem Buch nie verwenden werden. Hufig mssen Sie aber wissen, zu welchen Assemblies sie gehren, um Ihre Anwendungen kompilieren zu knnen. Wenn Sie mit lteren Methoden der Windows-Programmierung vertraut sind, drften Sie die .dll-Endung wiedererkennen, die fr Dynamically Linked Libraries steht diese DLLs sind Assemblies hnlich. Sie stellten Objekte bereit, die von anderen Anwendungen genutzt werden konnten. DLL-Dateien werden sowohl in .NET als auch in Pr-.NET gebraucht. Denken Sie aber daran, dass .NET-DLL-Dateien in MSIL geschrieben und vllig objektorientiert sind. Pr-.NET-DLLs sind weder das eine noch das andere und leider gibt es keine Methode, um den Unterschied schon auf den ersten Blick zu erkennen. Assemblies benutzt man nicht nur fr das Gruppieren von Namensrumen. Sie sind eine wichtige Komponente des .NET Frameworks, da sie die Grenzen zwischen Anwendungen darstellen, mit denen die CLR die Gltigkeitsbereiche Sicherheit, Versionierung und Metadaten erzwingt. Beim Entwickeln mit Windows Forms werden Sie Assemblies sehr hufig verwenden.

53

Windows Forms-Anwendungen erstellen

Die Main-Methode
Sehen wir uns noch einmal die Zeilen 6, 7 und 8 in Listing 2.1 an:
6: 7: 8: Public Shared Sub Main() Application.Run(New HelloWorld) End Sub

Wenn Sie wissen, was Methoden sind, haben Sie diesen Code bereits zur Hlfte verstanden. Eine Methode ist ein Codestck, das eine Funktion ausfhrt; sie bewirkt etwas. Code, der irgendeine Funktion ausfhrt, von der einfachsten Addition bis zu den kompliziertesten Threading-Routinen, muss immer in einer Methode untergebracht werden. In Visual Basic .NET gibt es zwei Methodentypen: Sub und Function. Sub macht etwas und endet dann. Function hingegen bewirkt etwas und sendet dann Daten an diejenige Instanz zurck, die die Methode aufgerufen hat. (Nichts hindert Sie, eine Function ohne Rckgabewert aufzusetzen; aber das Umgekehrte geht nicht: Eine Sub-Methode kann keine Daten zurckgeben.) In C# hingegen ist eine Methode eine Methode. Es wird keine Unterscheidung danach gemacht, ob die Methode Daten liefert oder nicht. Sie mssen in C# jedoch den Datentyp der zurckzugebenden Daten spezifizieren oder void, wenn keine Daten zurckgegeben werden. Beispielsweise sagt die Zeile
6: public static void Main() {

in Listing 2.2 aus, dass diese Methode nichts liefert, wohingegen die folgende Codezeile einen Integer zurckgibt:
6: public static int Main() {

Sie knnen automatisch ein paar Schlsse ziehen: Sie deklarieren eine void-Funktion (sub in VB .NET), sie wird Main genannt und ist public (Public in VB .NET), was bedeutet, dass andere Klassen darauf zugreifen knnen. Das Schlsselwort static (bzw. Shared) ist hingegen etwas Neues. Um es zu verstehen, mssen wir wieder zurckgehen zu Objekten und Klassen.

Objektinstanzen
Aus einer einzelnen Klasse (oder Blaupause) lassen sich mehrere Objekte dieser Klasse erzeugen. Jedes einzelne Objekt bezeichnet man als eine Instanz. Das Auto-Objekt eine wirkliche, physische, gebrauchsfertige Sache wre eine Instanz des Auto-Konzepts oder der Klasse. Man kann also ein Objekt haben, aber viele Instanzen: einen Mercedes SLK, einen VW Kfer oder einen Opel Tigra all dies sind Instanzen eines Autos. Sie gleichen einander nicht ganz, aber sie alle haben das grundlegende Konzept eines Autos gemeinsam.

54

Ein weiterer Blick auf Ihre Anwendung

Abbildung 2.2 zeigt zwei Instanzen des Objekts Standuhr. Beachten Sie, dass sie nicht die gleiche Zeit anzeigen, doch irgendwie wei man, dass beide Standuhren sind. Somit kann man viele Instanzen eines Objekts haben, von denen jede unterschiedliche Eigenschaften besitzt.

Standuhr A: 1:27

Standuhr B: 9:54

Abbildung 2.2: Zwei Instanzen eines Standuhr-Objekts knnen unterschiedliche Eigenschaften besitzen.

Das Gleiche lsst sich ber OOP und Objekte sagen. Die Klasse definiert den theoretischen Begriff eines Objekts HelloWorld, erzeugt aber keine Instanzen. Es ist anderen Objekten berlassen, diese Instanzen zu erzeugen, wenn sie gebraucht werden (gleich mehr darber). Wenn Sie vor einer Methode das Schlsselwort Shared oder static verwenden, bedeutet dies, dass die Methode oder Eigenschaft allen Instanzen dieses Objekts zur Verfgung steht und fr alle Instanzen gleich ist. Beispielsweise sind alle Autos aus Blech gefertigt und der Einfachheit halber sagen wir, dass das Blech das gleiche fr jedes Auto ist. Dies wre eine static-Eigenschaft: Die Metallart ndert sich nicht von Auto zu Auto. Alle Autos nutzen auerdem das Konzept der Farbe gemeinsam, haben aber nicht alle die gleiche Farbe; dies wre dann nicht static. Die Schlsselwrter sind selbsterklrend: Einzelwerte oder einzelne Methoden knnen Shared sein (in VB .NET), wenn alle Objekte sie gemeinsam nutzen (jedes Objekt hat nicht seine eigene Kopie), oder static (in C#), was bedeutet, dass sie sich nicht von einer Instanz zur nchsten ndern. Die Begriffe Shared und static sind im .NET Framework recht gebruchlich. Daher ist es sehr wichtig, dass Sie ein solides Grundverstndnis dafr erwerben. Fr den Moment mag es gengen zu wissen, dass Ihre Main-Methode stets entweder static oder Shared sein muss.

Das Eingangstor zum Code


Die Main-Methode ist auch als Einsprungspunkt zu einer Anwendung bekannt: Bei jedem Start Ihrer Anwendung wird die Main-Methode stets als erstes ausgefhrt. Verfgt Ihr Programm ber keine Main-Methode, wird es einfach nichts tun (man bekommt eine Fehler-

55

Windows Forms-Anwendungen erstellen

meldung wohl schon lange, bevor man es ausfhren will). Die Main-Methode wird auch als letztes ausgefhrt. Wenn Ihr Code mit der Ausfhrung fertig ist, kehrt er stets zu dieser Methode zurck. Der Main-Methode kommt also eine hohe Bedeutung zu. Entweder lsst sich der gesamte Programmcode in der Main-Methode unterbringen oder Main kann andere Methoden aufrufen. Von Main aus knnen Sie bestimmen, welches Formular anzuzeigen ist (viele Anwendungen knnen mehr als ein Windows Form-Objekt enthalten), Variablen festlegen, die von der restlichen Anwendung verwendet werden, oder beliebige weitere Vorbereitungsschritte ausfhren. In unserer Main-Methode wird eine einzige Codezeile ausgefhrt:
Application.Run(New HelloWorld)

Diese Zeile ruft die Run-Methode des Application-Objekts (einen Teil des Namensraums System.Windows.Forms) auf. Die Run-Methode befiehlt dem Programm, in eine Nachrichtenschleife einzutreten. Das ist im Grunde die Art und Weise eines Programms, auf Eingaben vom Benutzer zu warten. Der Eintritt in eine Nachrichtenschleife ist vom Standpunkt des Benutzers aus fast immer der Startpunkt einer Anwendung. Morgen werden Sie mehr ber Nachrichtenschleifen erfahren. Sie bergeben der Run-Methode eine Instanz Ihres Objekts HelloWorld. (Bitte beachten Sie, dass wir von einer Klasse zu einem Objekt bergegangen sind: Das New-Schlsselwort erstellt eine Objektinstanz.) Das weist die Run-Methode an, Ihr Formular auf dem Bildschirm fr den Dialog mit dem Benutzer anzuzeigen. Moment mal! Warum mssen wir eine Instanz der HelloWorld-Klasse an die Run-Methode bergeben? Drfte das nicht zu einer Endlosschleife fhren? Schlielich wird die MainMethode erneut ausgefhrt, bergibt eine neue Instanz und so weiter. Nicht ganz. Um dies zu verstehen, mssen Sie zwischen einer Klasse und einer Anwendung unterscheiden. Wenn eine neue Instanz einer Klasse erzeugt wird, passiert nichts (es sei denn, man htte einen Konstruktor erzeugt, aber so weit sind wir noch nicht). Wenn eine Anwendung startet, sucht sie zuerst nach einer Main-Methode, ganz gleich, wo sie diese findet oder ob sie sich in einer Klasse befindet, solange es sie nur gibt und sie in einer Klasse untergebracht ist. Main wird nur ein einziges Mal ausgefhrt, wenn Ihre Anwendung gestartet wird, und danach nicht mehr. Anders ausgedrckt, wird Main bei jeder Anwendungsausfhrung gestartet, aber nicht immer dann, wenn eine Klasse erzeugt wird. Obwohl die Main-Methode ganz normal aussieht, fhrt jeder Versuch, sie von einem anderen Punkt in Ihrem Programm aus aufzurufen, zu einer Fehlermeldung, wie sie in Abbildung 2.3 zu sehen ist.

56

Ein weiterer Blick auf Ihre Anwendung

Abbildung 2.3: Eine Fehlermeldung erfolgt, wenn man versucht, die Main-Methode im Code auszufhren.

Obwohl nun Zeile 7 in Listing 2.1


Application.Run(New HelloWorld)

eine neue Instanz der Klasse HelloWorld erzeugt, wird die Main-Methode nicht erneut ausgefhrt. Um die Trennung zwischen der HelloWorld-Klasse und der Main-Methode noch weiter zu unterstreichen, knnten Sie die Main-Methode in eine vllig andere Klasse stecken, und sie wrde weiterhin funktionieren, wie Sie in Listing 2.3 sehen. Kurz und gut, Sie brauchen fr eine Anwendung eine Main-Methode, die sich in einer Klasse befinden muss doch diese Klasse knnen Sie willkrlich auswhlen. Listing 2.3: Die Main-Methode lsst sich in jeder Klasse unterbringen
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: Imports System Imports System.Windows.Forms Public Class HelloWorld : Inherits Form Public Sub New() Me.Text = "HelloWorld!" End Sub End Class Public Class MyMainClass Public Shared Sub Main() Application.Run(New HelloWorld) End Sub End Class

Listing 2.3 ergibt wahrscheinlich etwas mehr Sinn, denn es zeigt ganz deutlich, dass die Main-Methode und die Windows Form-Klasse zwei vllig verschiedene Einheiten sind. Die Run-Methode in Zeile 12 erzeugt eine neue Instanz Ihrer Windows Form-Klasse (HelloWorld) und bewirkt, dass sie auf dem Bildschirm angezeigt wird.

57

Windows Forms-Anwendungen erstellen

Die Methode New (oder HelloWorld in C#) im Schlussteil von Listing 2.3 wird als Konstruktor bezeichnet. Das ist eine Methode, die ausgefhrt wird, sobald eine neue Instanz einer Klasse erzeugt wird. Sie hat eine Partner-Methode namens Destruktor. Wir sprechen im nchsten Abschnitt ber diese zwei Methoden.

Konstruktoren und Destruktoren


Einen Konstruktor benutzt man normalerweise, um die Instanz zu initialisieren, erforderliche Variablen anzulegen und so weiter. In Visual Basic .NET hat ein Konstruktor stets den Namen New, wie die Zeilen 10 bis 12 in Listing 2.3 zeigen:
10: 11: 12: Public Sub New() Me.Text = "HelloWorld!" End Sub

Das ist sinnvoll. Erinnern Sie sich an Zeile 7 aus Listing 2.1, wie Sie New klassenname verwendet haben:
Application.Run(New HelloWorld)

Dieser Befehl ruft die New-Methode auf. In C# nimmt der Konstruktor denselben Namen wie die Klasse selbst an, so wie in den folgenden Zeilen aus Listing 2.2:
Public HelloWorld() This Text = 'Hello World!" }

Die Me-Variable in Listing 2.1


11: Me.Text = "Hello World!"

und die this-Variable in Listing 2.2


11: thisText = "Hello World!";

sind spezielle Schlsselwrter, die auf das Objekt verweisen, in dem sich der Code jeweils befindet. In diesem Fall verweisen sie auf das Windows Form-Objekt HelloWorld. Auf diese Weise lsst sich leicht auf das umschlieende Objekt verweisen. Zeile 11 in Listing 2.1 und 2.2 weist die Zeichenfolge Hello World! der Eigenschaft Text in der HelloWorld-Klasse zu, welche, wie wir gesehen haben, die Titelzeile auf dem Windows Form festlegt. Umgekehrt wird ein Destruktor immer dann aufgerufen, wenn ein Objekt der Klasse zu entsorgen ist. In Visual Basic .NET wird dies bewerkstelligt, indem das Objekt mit nothing gleichgesetzt wird:
objHelloWorld = new HelloWorld() objHelloWorld = nothing

58

Ein weiterer Blick auf Ihre Anwendung

In C# erfolgt dies durch Gleichsetzung der Klasse mit null:


HelloWorld objHelloWorld = newHelloWorld(); objHelloWorld = null;

In VB .NET kann man jedoch keinen eigenen Destruktor erzeugen: Immer wenn Sie Ihr Objekt mit nothing gleichsetzen, bernimmt VB .NET die Entsorgung fr Sie. C# erlaubt es, Ihren eigenen Destruktor zu bestimmen, der durch den gleichen Namen wie der Konstruktor, aber mit einer Tilde davor deklariert wird:
~HelloWorld() { 'etwas verarbeiten }

Konstruktoren erben
In OOP gilt die Regel, dass der Konstruktor einer Klasse stets den Konstruktor seiner bergeordneten Klasse (Basisklasse) aufrufen muss. Beispielsweise erbt die Klasse HelloWorld von der Form-Klasse. Daher muss Ihre Klasse den Konstruktor der Form-Klasse aufrufen. Dieser wiederum muss den Konstruktor seiner bergeordneten Klasse aufrufen, nmlich den von ContainerControl, und so weiter, bis Sie die Wurzel des Stammbaums erreicht haben: Object. Werfen wir einen Blick darauf, warum dies notwendig ist. Sie erinnern sich, dass, sobald ein Objekt von einem anderen (seinem bergeordneten Objekt) erbt, es auch alle Eigenschaften und Methoden des anderen erbt. Manche davon knnen von bestimmten Variablen abhngen, die es zu initialisieren gilt. Stellen Sie sich beispielsweise eine Klasse vor, die eine Farbe-Eigenschaft erbt, die den Hintergrund des Formulars bestimmt. Dieser Eigenschaft muss ein echter Wert zugewiesen werden, bevor man sie verwenden kann, sonst erhlt man eine Fehlermeldung (schlielich lsst sich die Hintergrundfarbe nicht auf nichts setzen). Die Farbe-Eigenschaft wrde im Konstruktor Ihrer bergeordneten Klasse initialisiert werden. Daher mssen Sie den Konstruktor der Basisklasse aufrufen, damit die Farbe-Eigenschaft eine Bedeutung erhlt. Diese Abhngigkeit von Basisklassen besagt zweierlei. Erstens hngen viele Klassen, die von anderen erben, von ihren Basisklassen ab, um so Informationen zu erhalten. Und zweitens bedeutet es, dass Sie in Ihrem Konstruktor weniger Initialisierungscode schreiben mssen, weil schon vieles in den Konstruktoren der Ihrer Klasse bergeordneten Klassen erledigt worden ist. Der Aufruf des Konstruktors der Basisklasse ist in Ihrem Konstruktor als allererstes zu erledigen. In VB .NET erfolgt dies durch den Aufruf der Methode MyBase.New, in C#, indem Sie Ihren Konstruktor zum Ausfhren der Methode base() veranlassen:
'VB .NET Public Sub New() MyBase.New End Sub

59

Windows Forms-Anwendungen erstellen

'C# public HelloWorld(): base() { }

In all dem Code, den Sie heute geschrieben haben (Listings 2.1 bis 2.3), fehlen diese Aufrufe, doch warum erhalten wir dann keine Fehlermeldungen? Wenn Sie in Ihrer Klasse nur einen Konstruktor haben (mehr ber multiple Konstruktoren im nchsten Abschnitt), ruft die CLR automatisch den Konstruktor der Basisklasse auf. Daher knnen Sie diese Zeilen weglassen.

Konstruktoren berladen
Manchmal mchten Sie mehr als einen Konstruktor haben. Ein ntzliches Beispiel dafr bestnde darin, dass Sie dem Benutzer erlauben mchten, auf mehr als eine Weise zu initialisieren: so etwa eine, die einen Wert fr eine Variable erfordert, und eine, die keinen Wert erfordert. Sie knnen dies durch einen Vorgang namens berladen erledigen. Zum besseren Verstndnis dieses Konzepts schauen wir uns den Code an, wie er in Listing 2.4 abgedruckt ist. Listing 2.4: Konstruktoren in C# berladen
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: using System; using System.Windows.Forms; public class HelloWorld : Form { public static void Main() { Application.Run(new HelloWorld()); } public HelloWorld(): this("Hello World!") {} public HelloWorld(string strText): base() { thisText = strText; } }

Lassen Sie uns zunchst in Zeile 12 springen. Hier sehen wir einen Konstruktor, der einen string-Wert als Parameter bernimmt. Die Text-Eigenschaft des Formulars wird in Zeile 13 auf diesen Parameter eingestellt. Schlielich ruft dieser Konstruktor mit der base()-Methode den Konstruktor der Basisklasse auf. Soweit also nichts Neues.

60

Ein weiterer Blick auf Ihre Anwendung

In Zeile 10 stoen wir auf etwas, das wie ein weiterer Konstruktor aussieht. Doch dieser bernimmt keine Parameter und verfgt ber keinen Initialisierungscode. Schauen Sie aber mal auf den Code nach dem Doppelpunkt. Er ruft this auf, von dem wir wissen, dass es eine Referenz auf das aktuelle Objekt ist, und bergibt ihm die Zeichenfolge Hello World!. Was bewirkt dies? Zeile 10 besagt, dass im Fall des Aufrufs dieses Konstruktors statt dessen der Konstruktor in Zeile 12 aufgerufen und ihm als Parameter die Zeichenfolge Hello World! bergeben werden soll. Die Ausfhrung geht dann zu den Zeilen 12 und 13 ber, die Text-Eigenschaft wird auf Hello World! gesetzt. Im Grunde sagen Sie also, dass der Parameter strText optional ist: Wenn kein Parameter angegeben ist, verwendet der Konstruktor die Standardzeichenfolge Hello World!. Zeile 7, die einen Konstruktor aufruft, knnte also entweder so
Application.Run(new HelloWorld());

oder so
Application.Run(new HelloWorld("I love NY"));

aussehen. Dies ruft den jeweils passenden Konstruktor auf, je nachdem, ob Parameter bergeben wurden. Der Konstruktor in Zeile 10 knnte sogar seine eigenen Initialisierungsroutinen ausfhren und braucht den Konstruktor in Zeile 12 nicht aufzurufen, wenn man nicht will. In VB .NET sieht die Syntax etwas anders aus, wie man in Listing 2.5 sehen kann. Listing 2.5: Konstruktoren in VB .NET berladen
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: Imports System Imports System.Windows.Forms Public Class HelloWorld : Inherits Form Public Shared Sub Main() Application.Run(New HelloWorld) End Sub Public Sub New() Me.New("Hello World!") End Sub Public Sub New(strText as String) Me.Text = strText End Sub End Class

61

Windows Forms-Anwendungen erstellen

In Zeile 11 verwenden Sie das Schlsselwort Me und rufen die New-Methode auf, wobei Sie eine Standardzeichenfolge bereitstellen. Die dem Listing 2.5 zugrunde liegenden Konzepte sind die gleichen wie in Listing 2.4. Beachten Sie, dass Sie keineswegs einen Konstruktor erzeugen mssen. Wenn Sie keinen erzeugen, dann erzeugt VB .NET (oder C#) einen fr Sie. Dieser automatisch generierte Konstruktor ruft nur den Konstruktor der Basisklasse auf. Konstruktoren sind nicht die einzigen Methoden, die Sie berladen knnen. Doch in VB .NET mssen Sie das Schlsselwort Overloads vor jede Methode stellen, die berladen wird (Konstruktoren natrlich ausgenommen), und zwar vor dem Schlsselwort Public.

Ein abschlieender Blick auf den Programmcode


Nach der Untersuchung aller Codebestandteile lassen Sie uns rekapitulieren. Listing 2.1 ist erneut in Listing 2.6 zu sehen. Listing 2.6: Ihre erste Windows Forms-Anwendung in VB .NET
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: Imports System Imports System.Windows.Forms Public Class HelloWorld : Inherits Form Public Shared Sub Main() Application.Run(New HelloWorld) End Sub Public Sub New(strText as String) Me.Text = strText End Sub End Class

Die Zeilen 1 und 2 bestimmen die Namensrume, die in Ihre Anwendung importiert werden. Die Mitglieder dieser Namensrume stehen Ihrer Anwendung in Form ihrer Kurznamen (also Form anstelle von System.Windows.Forms.Form) zur Verfgung. Zeile 4 deklariert Ihre Klasse und die Tatsache, dass sie von der Form-Klasse erbt, der Basisklasse fr alle Windows Forms-Anwendungen. Zeile 6 startet die MainMethode, den Einsprungspunkt Ihrer Anwendung. Sie ruft die Run-Methode

62

Das Kompilieren von Windows Forms

auf, welche das Formular zur Darstellung auf dem Bildschirm und zum Warten auf Benutzereingaben veranlasst. Sie bergeben eine neue Instanz Ihrer Klasse, um ihr mitzuteilen, welches Formular angezeigt werden soll. Die Zeilen 10 bis 12 bilden einen Konstruktor fr die HelloWorld-Klasse. In Zeile 11 weisen Sie lediglich der Text-Eigenschaft in Ihrem Formular die Zeichenfolge Hello World! zu. Das ndert die Titelzeile Ihrer Anwendung. Sobald Sie einmal die Grundlagen verstanden haben, knnen Sie erkennen, wie einfach dieser Quellcode war. Jetzt kennen Sie alle Komponenten, die fr die Erstellung von Windows Forms notwendig sind, und knnen anfangen, Ihre eigene Windows Forms-Anwendung zu erstellen!

2.2

Das Kompilieren von Windows Forms

Der nchste Schritt bei der Anwendungserstellung besteht im Kompilieren Ihres Quellcodes zu MSIL. Die Vorgehensweise variiert je nach der verwendeten Programmiersprache, doch sie ist stets recht hnlich. Die einfachste Kompiliermethode besteht in der Verwendung von Visual Studio .NET. VS .NET ist eine integrierte Entwicklungsumgebung (IDE), die Ihnen die Erstellung beliebiger Anwendungstypen an einem zentralen Ort erlaubt, ganz gleich welche Programmiersprache Sie benutzen. Lassen Sie uns einen kleinen Rundgang durch diese Entwicklungsumgebung machen und betrachten, wie man hier Code schreiben und kompilieren kann. ffnen Sie VS .NET und starten ein neues Projekt, indem Sie NEU/PROJEKT aus dem DATEI-Men auswhlen. Das Dialogfeld NEUES PROJEKT (Abbildung 2.4) erscheint.

Abbildung 2.4: Das Dialogfeld NEUES PROJEKT stellt Ihnen eine VB .NET- oder C#-Anwendung zur Auswahl.

63

Windows Forms-Anwendungen erstellen

Whlen Sie mit Hilfe dieses Dialogs eine Anzahl verschiedener Optionen sowohl fr C# als auch fr Visual Basic .NET. Klicken Sie in der linken Fensterhlfte auf VISUAL BASICPROJEKTE und in der rechten auf WINDOWS-ANWENDUNG. Whlen Sie einen passenden Namen und einen Speicherplatz fr dieses Projekt und klicken Sie auf OK.
PEN-EXPLORER

Sobald VS .NET mit der Verarbeitung fertig ist, zeigt es ein neues Projekt im PROJEKTMAP(obere rechte Ecke der VS .NET-Anwendung) an, mit diversen Dateien darunter (siehe Abbildung 2.5).

Zunchst finden Sie einen Ordner namens VERWEISE, der alle Namensrume anzeigt, die Ihre Anwendung verwenden wird; beachten Sie bitte, dass sich innerhalb des Ordners (auf das Plus-Zeichen klicken, um ihn aufzuklappen) bereits einige Namensrume befinden, die fr Sie vorgegeben sind. Wenn Sie mit den Statements using oder Imports einen Namensraum haben wollen, der sich nicht hier befindet, mssen Sie den Namensraum hinzufgen, indem Sie mit der rechten Maustaste auf den Ordner klicken und VERWEIS HINZUFGEN auswhlen. Daraufhin erscheint ein Dialogfenster, das Ihnen bei der Auswahl einer Assembly hilft. Die nchste ist die Datei AssemblyInfo.vb, die verwendet wird, um Informationen ber Ihre Assembly bereitzustellen. Schlielich findet sich noch eine Datei namens Form1.vb, worin Ihr Code abgelegt wird. ffnen Sie diese Datei mit einem rechten Mausklick und der Auswahl CODE ANZEIGEN. Ihr Fenster sollte nun hnlich aussehen wie in Abbildung 2.5.

Abbildung 2.5: Ihre leere Windows Forms-Anwendung wird im Projektmappen-Explorer angezeigt.

In VS .NET wird ein einfach zu bedienender Quellcode-Editor mitgeliefert. Er gruppiert hnliche Dinge wie etwa Klassen, und er erlaubt Ihnen, sie auszublenden (indem Sie auf das Minus-Symbol links daneben klicken), um so die Arbeitsumgebung bersichtlicher zu

64

Das Kompilieren von Windows Forms

machen. Beachten Sie auch, dass VS .NET etwas Code fr Sie erzeugt hat, den Sie in dem Code-Bereich im Windows Forms-Designer finden. Diesen Code werden wir hier nicht besprechen, aber wenn Sie mal durchblttern, dann werden Sie zumindest eine Methode wiedererkennen: den Konstruktor der Klasse. Mit dem restlichen Code integriert VS .NET Ihre Anwendung in die Entwicklungsumgebung. (Das soll nicht bedeuten, dass Sie den Code nicht lschen knnten, wenn Sie wollen.) Sie knnen nun Ihren Code an beliebiger Stelle zwischen den Zeilen Public Class und End Class einfgen. Sie knnen auch den Namen der Klasse bearbeiten. Das Eigenschaftenfenster unten rechts gestattet Ihnen die nderung des gespeicherten Dateinamens, des Autorennamens und anderer Attribute. Sobald Sie den Code geschrieben haben, whlen Sie im Men ERSTELLEN den Befehl ERSTELLEN, um Ihre Anwendung zu erzeugen. Befinden sich Fehler im Code, werden sie in einem Ausgabe-Fenster angezeigt. Luft alles erfolgreich ab, dann erzeugt VS .NET eine .EXE-Datei (der Dateiname hngt vom gewhlten Projektnamen ab), die Sie wie jede beliebige Anwendung ausfhren knnen. VS .NET enthlt zahlreiche Funktionen, die hier nicht vorgestellt werden. Erforschen Sie die Entwicklungsumgebung weiter und schreiben Sie Ihren Code. Wenn Sie lieber nicht mit VS .NET arbeiten wollen oder keinen Zugang dazu haben, knnen Sie Ihren Code mit dem jeweils ntigen Befehlszeilen-Compiler kompilieren. Das ist ein einfaches Programm, das einige Parameter bernimmt (darunter Ihre QuellcodeDatei) und eine kompilierte EXE-Datei ausspuckt. Der ntige Compilerbefehl ist entweder vbc.exe (fr Quellcode in Visual Basic .NET) oder csc.exe (fr Quellcode in C#). Fr beide Methoden ist die Syntax dieselbe, daher betrachten wir hier nur die erste:
vbc optionen quellcodedatei

In seiner einfachsten Form sieht Ihr Befehl wie folgt aus:


vbc helloworld.vb

Tabelle 2.2 fhrt die gebruchlichen Befehlszeilenoptionen auf, die Sie nutzen knnen. Geben Sie vbc /? oder csc /? In der Befehlszeile ein, um alle Optionen anzusehen.
Option @
/bugreport

Beschreibung Spezifiziert eine Textdatei, die die fr den Befehl zu verwendenden Optionen enthlt. Sie mssen einen Dateinamen angeben: @dateiname. Erzeugt eine Fehlerberichtsdatei. Sie mssen einen Dateinamen angeben: /bugreport:dateiname.

Tabelle 2.2: Gebruchliche Befehlszeilen-Optionen fr den Compiler

65

Windows Forms-Anwendungen erstellen

Option
/debug

Beschreibung Gibt diverse Informationen aus. Verwenden Sie: /debug+ um Informationen auszugeben
/debug- um keine Informationen auszugeben /debug:full um alle Debugging-Informationen auszugeben (Voreinstellung)

/debug:pdbonly um lediglich die pdb-Symboldatei zu erstellen


/main

Gibt die Klasse an, die die Main-Methode enthlt. Verwenden Sie /m als Kurzform. Unterdrckt das Microsoft-Logo. Verwenden Sie binary oder text, um anzugeben, wie alle Zeichenfolgenvergleiche in Ihrem Code durchgefhrt werden sollen. Erfordert die explizite Deklaration aller Variablen vor dem Einsatz. Verwenden Sie /optionexplicit+ oder /optionexplicit-. Erzwingt strikte Befolgung der Sprachsemantik. Verwenden Sie /optionstrict+ oder /optionstrict-. Gibt die Ausgabestandort und den Namen Ihrer kompilierten MSIL-Datei an. Gibt zu referenzierende Assemblies in Ihrer Anwendung an (jene, die durch die Schlsselwrter using oder Imports spezifiziert wurden). Verwenden Sie /r als Kurzform. Gibt den Typ der zu generierenden Ausgabedatei an. Verwenden Sie /t als Kurzform. Setzen Sie ein: /t:exe fr eine ausfhrbare Konsolen-Anwendung;
/t:library fr eine DLL; /t:winexe fr eine ausfhrbare Windows-Anwendung;

/nologo /optioncompare

/optionexplicit

/optionstrict

/out /reference

/target

/t:modul fr ein Modul.


/win32icon /win32resource

Gibt das zu verwendende Symbol (.ico-Datei) fr das Anwendungssymbol an. Gibt die zu verwendenden Ressourcen-Dateien (.res-Dateien) an.

Tabelle 2.2: Gebruchliche Befehlszeilen-Optionen fr den Compiler (Forts.)

Fr unsere Anwendung verwenden wir normalerweise nur zwei Optionen: /target (Kurzform /t) und /reference (Kurzform /r). Um beispielsweise die Anwendung HelloWorld zu kompilieren, sollte das Ziel winexe sein, und wir mssen Referenzen auf die Assemblies hinzufgen, die gebraucht werden (je nach den Namensrumen, die wir importiert

66

Eine vollstndige Anwendung

haben). Wenn wir auf unseren Quellcode und die Tabelle 2.1 zurckblicken, sehen Sie, dass wir die Namensrume System und System.Windows.Forms verwenden. Sie befinden sich jeweils in den Assemblies system.dll und system.windows.forms.dll. Daher sieht unsere Befehlszeile schlielich so aus:
vbc /t:winexe /r:system.dll /r:system.windows.forms.dll HelloWorld.vb

Der Compiler erlaubt es Ihnen auch, mehrere Referenzen in einer einzigen Liste zu kombinieren. Ein Beispiel:
vbc /t:winexe /r:system.dll,system.windows.forms.dll HelloWorld.vb

Die Assembly mscorlib.dll aus Tabelle 2.1 steht automatisch allen Anwendungen zur Verfgung: Sie ist die grundlegende Assembly fr .NET. Aus diesem Grund brauchen Sie keinen expliziten Verweis dafr einzufgen. Von Ihrer Eingabeaufforderung aus veranlasst wrde der Befehl die in Abbildung 2.6 sichtbare Ausgabe erzeugen.

Abbildung 2.6: Eine erfolgreiche Kompilierung fhrt zu diesem Ergebnis.

Welchen Weg Sie auch whlen, die Kompilierung ist ein notwendiger Schritt zu Ihrer Windows Forms-Anwendung. Im restlichen Buch verwenden wir die Befehlszeilenmethode, falls nicht anders festgelegt. Anhand der Befehle knnen Sie leicht bestimmen, welche Referenzen Sie Ihrem VS .NET-Projekt hinzufgen mssen.

2.3

Eine vollstndige Anwendung

Da Sie nun mit allen Bestandteilen einer Windows Forms-Anwendung ausgestattet sind, knnen Sie mit dem Aufbau einer Applikation beginnen, die etwas ntzlicher als das Hello World-Programm ist. Im folgenden Abschnitt werden wir einen einfachen Taschenrechner erstellen, den Sie dann an Tag 5 und 6 erweitern.

67

Windows Forms-Anwendungen erstellen

Um einen Taschenrechner zu erstellen, brauchen Sie mindestens drei Dinge (neben Windows Forms):

eine Stelle, an der der Benutzer eine Zahl bzw. Zahlen eingeben kann, eine Stelle, an der er das Ergebnis sehen kann (mglicherweise an der gleichen Stelle), eine oder mehrere Schaltflchen, die es ihm erlauben, mathematische Operationen auszufhren. Der Einfachheit halber beschrnken wir uns hierbei auf die Addition.

Diese Komponenten werden von Windows Forms-Steuerelementen (Controls) bereitgestellt: interaktiven Elementen der Benutzeroberflche (englisch User Interface, abgekrzt UI). Ab Tag 4 werden wir diese Steuerelemente genauer unter die Lupe nehmen, so dass wir uns hier nicht allzu lange damit aufhalten mssen. Zunchst wollen wir einmal den grundlegenden Rahmen fr Ihre Anwendung erstellen. Dazu gehren alle Bestandteile, ber die Sie heute etwas erfahren haben. Werfen Sie einen Blick auf Listing 2.7 (der komplette C#-Code wird spter gezeigt). Listing 2.7: Das Fundament fr die Taschenrechner-Anwendung
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: Imports System Imports System.Windows.Forms Namespace TYWinforms.Day2 Public Class Calculator Public Shared Sub Main() Application.Run(New Calculator) End Sub Public Sub New() End Sub End Class End Namespace

Speichern Sie diesen Code unter dem Dateinamen Listing 2.7.vb. Im Listing gibt es nichts Neues zu entdecken. Es ist sogar fast identisch mit Listing 2.1, nur dass in Zeile 4 eine Namensraum-Deklaration auftaucht. Diese teilt der CLR mit, dass diese Klasse zum Namensraum TYWinforms.Day2 gehrt.

68

Eine vollstndige Anwendung

Die Calculator-Klasse wird in Zeile 6 deklariert, sie erbt von der Klasse System.Windows.Forms.Form. Die Zeilen 8 bis 10 enthalten die Main-Methode, die einfach die Run-Methode aufruft und eine neue Instanz der aktuellen Klasse als Parameter bergibt. Die Zeilen 12 bis 14 enthalten eine Konstruktor-Methode, die momentan noch leer ist in Krze fgen wir den entsprechenden Code hinzu. In dieser Anwendung bentigen wir vier Windows Forms-Steuerelemente: zwei Textfelder (TextBox) fr die Zahleneingabe, ein Bezeichnungsfeld (Label) fr die Ergebnisanzeige und eine Schaltflche (Button) fr die Erledigung der Addition. Alle diese Steuerelemente gehren dem Namensraum System.Windows.Forms an, so dass Sie sie genau wie die FormKlasse verwenden knnen. Geben Sie in Zeile 7 Ihrer Listing 2.7.vb-Datei folgenden Code ein:
Private Private Private Private WithEvents btnAdd as Button tNumber1 as TextBox tNumber2 as TextBox lblAnswer as Label

Dieser Abschnitt deklariert vier Variablen als Steuerelemente. Das Schlsselwort WithEvents bedeutet im Grunde, dass ein bestimmtes Steuerelement Aktionen ausfhren kann. Listing 2.8 zeigt die vollstndige Konstruktor-Methode. Listing 2.8: Ihre Anwendung initialisieren
12: 13: 12: 13: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: Public Sub New() Me.btnAddYour Application Public Sub New() Me.btnAddInitializing Your Application Public Sub New() Me.btnAdd = New Button Me.tbNumber1 = New TextBox Me.tbNumber2 = New TextBox Me.lblAnswer = New Label tbNumber1.Location = New Point(0,0) tbNumber2.Location = New Point(100,0) btnAdd.Location = New Point(0,25) btnAdd.Text = "addieren" AddHandler btnAdd.Click, new EventHandler(AddressOf Add) lblAnswer.Location = New Point(0,75) Me.Controls.Add(btnAdd)

69

Windows Forms-Anwendungen erstellen

28: 29: 30: 31:

Me.Controls.Add(tbNumber1) Me.Controls.Add(tbNumber2) Me.Controls.Add(lblAnswer) End Sub

Fgen Sie diesen Code Ihrer Datei Listing 2.7.vb hinzu. Wir gehen auch hier noch nicht auf die Spezifika der einzelnen Steuerelemente ein, sondern kmmern uns nur um die Grundlagen. Die Zeilen 13 bis 16 erzeugen neue Instanzen jedes Steuerelements. Sie fragen sich vielleicht, warum die Zeilen 13 bis 16 ntig sind, wo wir doch die Variablen schon zuvor deklariert haben. Der Grund: Im vorhergehenden Codestck erzeugten wir Variablen, gaben ihnen Namen und teilten der CLR mit, um welche Variablentypen es sich handelt. Wir haben jedoch diesen Variablen keinerlei Werte zugewiesen. Das erledigen die Zeilen 13 bis 16: Sie weisen den Variablen Objekte zu (wie durch die Gleichheitszeichen angezeigt wird). Das Erzeugen eines Objekts sei es ein Steuerelement, ein Formular oder ein anderes Objekt ist ein zweistufiger Vorgang: Deklaration der Variablen und Zuweisung eines Wertes. Es ist jedoch mglich, beide Schritte in einer einzigen Zeile auszufhren:
Dim btnAdd as Button = New Button

Oder sogar noch einfacher:


Dim btnAdd as New Button

Wir werden ab jetzt diese Kurzform verwenden. Die Zeilen 18, 19, 21 und 25 machen alle das Gleiche: Sie legen die Positionen (Locations) fr die Steuerelemente in unserem Windows Form fest. Die Eigenschaft Location bernimmt einen einzelnen Parameter des Typs Point, der die Stelle beschreibt, wo das Steuerelement platziert werden soll. Der Konstruktor fr Point bernimmt wiederum zwei Parameter (eine x- und eine y-Koordinate). Man kann zwar Steuerelemente bereinander legen, aber das ist meist nicht sonderlich ntzlich. Wir gehen morgen auf dieses Thema ein. Sie sollten bereits wissen, was Zeile 22 bewirkt: Sie setzt die Text-Eigenschaft der Schaltflche auf den Wert Addieren. Erinnern Sie sich, dass die Text-Eigenschaft des Formulars eine Titelzeile festlegt. Die gleiche Eigenschaft fr einen Button legt den Text fest, der darauf angezeigt wird. Zeile 23 bringt etwas Neues. Was sie genau bedeutet, erfahren wir an Tag 5. Fr den Augenblick gengt es zu wissen, dass diese Zeile der CLR mitteilt, dass die Add-Methode (siehe Listing 2.9) ausgefhrt werden soll, wenn diese Schaltflche angeklickt wird.

70

Eine vollstndige Anwendung

Die Zeilen 27-30 schlielich fgen Ihrem Formular die einzelnen Steuerelemente hinzu. Dieser Schritt ist notwendig, sonst wrde die CLR nicht wissen, wo sie die Steuerelemente anzeigen soll. Sie wrden nur im Speicher existieren und wertvolle Ressourcen belegen. Listing 2.9 zeigt Ihnen den Schlussteil unseres Codes: die Add-Methode. Fgen Sie diesen Code unter Zeile 31 ein, jedoch vor dem Ende der Klasse. Listing 2.9: Die Zahlen addieren
33: 34: 35: Public Sub Add(ByVal Sender as Object, ByVal e as EventArgs) lblAnswer.Text = CStr(CInt(tbNumber1.Text) + CInt(tbNumber2.Text)) End Sub

Diese Methode nimmt zwei Parameter entgegen, die wir aber fr den Augenblick ignorieren, da sie fr diese einfache Anwendung nicht von groem Nutzen sind. Zeile 34 bernimmt die Werte der zwei Textfelder (unter Nutzung der jeweiligen Text-Eigenschaft), addiert sie und zeigt sie im Bezeichnungsfeld (Label) mit Hilfe von dessen Text-Eigenschaft an. Die Funktionen CStr und CInt wandeln die Benutzereingaben in Zeichenfolgen bzw. Ganzzahlen um. Das ist notwendig, weil die CLR nicht wei, wie sie Benutzereingabewerte interpretieren soll. Beispielsweise knnte das Zeichen 9 als Zahl oder Zeichenfolge interpretiert werden, also mssen wir einen expliziten Datentyp dafr bereitstellen. Zeile 34 fhrt die folgenden Arbeitsschritte aus: 1. holt die Werte aus den zwei Textfeldern, 2. wandelt die Werte mit Hilfe von CInt in Integer um, 3. addiert die zwei Integer, 4. wandelt das Ergebnis in eine Zeichenfolge um, die im Bezeichnungsfeld angezeigt wird (dieser Schritt ist erforderlich, weil die Text-Eigenschaft stets eine Zeichenfolge erwartet und sich gegenber einer Ganzzahl verweigert). Das war's auch schon. Speichern Sie den zusammengefgten Code als calculator.vb in Ihrem Verzeichnis c:\winforms\day2 und kompilieren Sie ihn unter Verwendung von VS .NET oder eines Befehlszeilen-Compilers:
vbc /t:winexe /r:system.dll /r:system.windows.forms.dll /r:system.drawing.dll Calculator.vb

Dieser Befehl verwendet den Compiler von VB .NET, liefert eine ausfhrbare WindowsDatei und referenziert die Assemblies system.dll, system.windows.forms.dll sowie system.drawing.dll (diese wurde mit Hilfe der Tabelle 2.1 bestimmt), um die Anwendung zu kompilieren. Abbildung 2.7 zeigt eine Beispielausgabe.

71

Windows Forms-Anwendungen erstellen

Listing 2.10 zeigt den gleichen Quellcode, nur eben in C#.

Abbildung 2.7: Die Anwendung addiert erfolgreich zwei Zahlen.

Listing 2.10: Ein C#-Taschenrechner


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: using System; using System.Windows.Forms; using System.Drawing; namespace TYWinforms.Day2 { public class Calculator : Form { private Button btnAdd; private TextBox tbNumber1; pribate TextBox tbNumber2; private Label lblAnswer; public static void Main() { Application.Run(new Calculator()); } public Calculator() { this.btnAdd = new Button(); this.tbNumber1 = new TextBox(); this.tbNumber2 = new TextBox(); this.lblAnswer = new Label(); tbNumber1.Location = new Point(0,0); tbNumber2.Location = new Point(100,0); btnAdd.Location = new Point(0,25);

72

Eine vollstndige Anwendung

27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43:

btnAdd.Text = "Addieren"; btnAdd.Click += new EventHandler(this.Add); lblAnswer.Location = new Point(0,75); this.Controls.Add(btnAdd); this.Controls.Add(tbNumber1); this.Controls.Add(tbNumber2); this.Controls.Add(lblAnswer); } public void Add(object Sender, EventArgs e) { lblAnswer.Text = Convert.ToString(Convert.ToInt32 (tbNumber1.Text) + Convert.ToInt32(tbNumber2.Text)); } } }

Neben ein paar semantischen Unterschieden zum VB .NET-Code sollten Sie auch ber andere Dinge Bescheid wissen. Alle Zeilen sind bis zur Zeile 28 miteinander identisch. Beachten Sie, dass Sie die AddHandler-Methode nicht mehr verwenden und das Schlsselwort WithEvents aus Zeile 8 verschwunden ist. C# setzt eine andere Methode ein, um Objekte Aktionen ausfhren zu lassen. Darauf kommen wir an Tag 5 zu sprechen. Andere Unterschiede treten nur noch in den Zeilen 39 und 40 auf. Statt die Funktionen CStr und CInt fr die Datentypumwandlung zu verwenden, nutzen Sie nun die Methoden Convert.ToString und Convert.ToInteger. Wenn Sie mit C# vertraut sind, fragen Sie sich vielleicht, warum wir nicht den Casting-Operator fr die Konvertierung von Datentypen verwendet haben, beispielsweise (string) und (int) vor den jeweiligen Variablennamen: (int)tbNumber1.Text usw. Der Grund liegt in dem Umstand, dass man in C# weder implizit noch explizit einen string in einen anderen Basisdatentyp konvertieren kann. Die Klasse System.Convert verfgt hingegen ber alle ntigen Methoden, um jeden beliebigen vordefinierten Datentyp in einen anderen umzuwandeln (ToInt32, ToString, ToDouble usw.). Der Befehl zum Kompilieren ist der gleiche, nur dass Sie jetzt den C#-Compiler nutzen:
csc /t:winexe /r:system.dll /r:system.windows.forms.dll /r:system.drawing.dll Calculator.cs

73

Windows Forms-Anwendungen erstellen

2.4

Zusammenfassung

Die heutige Lektion war sehr intensiv. Durch das Untersuchen eines einfachen Quellcodes haben Sie eine ganze Menge ber das .NET Framework und seine Arbeitsweise gelernt, u.a. ber Klassen, Namensrume, Assemblies und Methoden. Klassen sind Bauplne fr Objekte. In einer Klasse definieren Sie die Eigenschaften, Methoden und Ereignisse, die ein Objektbenutzer verwenden kann. Diese offen gelegten also fr die Allgemeinheit bereitgestellten Elemente werden mit Hilfe des Schlsselworts public deklariert. Elemente, die Sie nicht offen legen mchten, erfordern das Schlsselwort private. Sie haben auch etwas ber Klassenvererbung gelernt. Eine Klasse kann von einer anderen erben, um so deren Funktionsumfang zu nutzen das erspart Ihnen viel Codeschreiben. Alle Ihre Windows Forms-Klassen erben von der Klasse System.Windows.Forms.Form. Klassen verfgen ber Konstruktoren und Destruktoren, also ber Methoden, die die von einer Klasse verwendeten Ressourcen initialisieren und wieder freigeben. In C# hat der Konstruktor den gleichen Namen wie die Klasse; der Destruktor erhlt den gleichen Namen, allerdings mit einer vorangestellten Tilde. VB .NET hingegen verfgt ber keine Destruktoren und der Konstruktor trgt immer den Namen New. Namensrume sind Gruppierungen von Klassen, und Assemblies sind physische Dateien, die Namensrume gruppieren. Namen von Assemblies enden auf .dll und lassen sich von Anwendungen mit Hilfe der Option /r des Compilers referenzieren. Die Main-Methode ist stets der Anfang einer Anwendung. Sie muss sich in einer Klasse befinden, aber es ist gleichgltig, in welcher. In der Regel ruft diese Methode die Methode Application.Run auf, welche ein Formular am Bildschirm anzeigt und auf Benutzereingaben wartet. Zum Schluss haben Sie gelernt, wie man alles zusammenfgt, indem Sie eine funktionierende Windows Forms-Anwendung erstellten. Mit diesem Wissen knnen Sie nun beinahe jeden Bestandteil einer Windows Forms-Anwendung identifizieren und Ihre eigene Anwendung erstellen.

2.5
F

Fragen und Antworten

Bedeutet der Import von Namensrumen zustzlichen Verwaltungsaufwand und somit Speicherbedarf fr meine Anwendung? A Nicht unbedingt. Die Objekte in importierten Namensrumen werden nur bei Bedarf geladen und nicht alle auf einmal. Daher bentigt die Verwendung externer Objekte zwar schon etwas (Speicher-) Ressourcen, aber nur in recht geringem Umfang.

74

Workshop

2.6

Workshop

Dieser Workshop soll Ihnen helfen, sich besser an die heute behandelten Begriffe zu erinnern. Es hilft Ihnen sehr, die Antworten vollstndig zu verstehen, bevor Sie weiterlesen. Die Antworten zu den Quizfragen und den bungen finden Sie in Anhang A.

Quiz
1. Wie heit die Basisklasse, von der alle anderen Klassen erben? 2. Was bewirken die Schlsselwrter shared und static? 3. Wahr oder falsch? Man kann vorhandene Namensrume um eigene Klassen erweitern. 4. Wahr oder falsch? Die Main-Methode wird jedes Mal ausgefhrt, wenn eine neue Instanz ihrer Klasse erzeugt wird. 5. Aus welchem Grund ist der folgende Code nicht ausreichend? Was muss auerdem verwendet werden?
dim MyObject as Button

6. Was stimmt mit dem folgenden Kompilierungsbefehl nicht? (Tipp: Er kann mehr als einen Fehler enthalten.)
csc /type:windowsexe /r:system.dll /r:system.drawing.dll / r:system.drawing.text.dll dateiname.vb

7. Was bedeuten die folgenden Eigenschaften?


Button.CanSelect Button.Cursor Form.AllowDrop Form.Visible

8. Nennen Sie drei semantische Unterschiede zwischen C# und Visual Basic .NET. 9. Wahr oder falsch? Jede Klasse muss einen Konstruktor haben.

bung
Erweitern Sie das heutige Taschenrechner-Beispiel. Fgen Sie weitere Schaltflchen hinzu, mit denen sich arithmetische Operationen ausfhren lassen: Subtraktion, Multiplikation und Division. Versuchen Sie, die Anwendung sowohl in C# als auch in Visual Basic .NET zu erstellen. Verschnern Sie die Benutzeroberflche Ihres Taschenrechners durch den grozgigen Gebrauch von Bezeichnungsfeldern (Label-Steuerelementen).

75

Mit Windows Forms arbeiten

Mit Windows Forms arbeiten

An den ersten zwei Tagen haben Sie eine ganze Menge ber die Funktionsweise von Windows Forms-Anwendungen gelernt. Sie knnen jeden ihrer Bestandteile identifizieren und beschreiben, wissen, wie er mit .NET zusammenarbeitet, und Sie knnen auf die Verwendung derjenigen Teile schlieen, die Ihnen noch unbekannt sind. Da Sie nun ber das Grundlagenwissen verfgen, um Windows Forms zu erstellen, ist es an der Zeit, mehr ins Detail zu gehen. Die heutige Lektion konzentriert sich auf alle Aspekte des Objekts System.Windows.Forms.Form und zeigt Ihnen die Vielfalt an Mglichkeiten, mit denen Sie Ihre Anwendungen anpassen knnen. Heute lernen Sie,

wie man das Object-Objekt verwendet, in welcher Weise Sie Layout und Aussehen eines Formulars steuern, wie man Interaktion in Formularen kontrolliert, was die Nachrichtenschleife ist und wie sie mit der CLR zusammenarbeitet, wie Sie mit Tastatur- und Mauseingaben umgehen, auf welche Weise man Formulare veranlasst, mit Drag & Drop zu arbeiten.

3.1

Das objektorientierte Windows-Formular

Nun sollten Sie langsam anfangen, sich Ihre Formulare als Objekte vorzustellen. Stellen Sie sich das Windows -Formular auf Ihrem Bildschirm als ein echtes Fenster in Ihrem Haus vor. Sie knnen die Farbe des Fensters (bzw. seines Rahmens) und seine Transparenz ndern, den Typ der Handgriffe und des Glases sowie die Vorhnge usw. Sie knnen praktisch alles, was Ihnen einfllt, mit Ihrem Fenster anstellen (vorausgesetzt, Sie verfgen ber die entsprechenden Mittel). Das Gleiche gilt auch fr Windows Forms. Aus der gestrigen Lektion wissen Sie, dass Sie die Titelzeile und die Gre eines Formulars ndern knnen. Das trifft auch auf die Titelleiste zu wie auch auf die Anordnung der Objekte im Formular, die Farbe, Transparenz und Sichtbarkeit, die Umrisslinien, die Schaltflchen usw. In Abbildung 3.1 sehen Sie nur ein paar der Layouts, die Ihr Formular annehmen kann. Daher ist es hilfreich, sich Windows Forms als generische Objekte vorzustellen, die sich in jeder gewnschten Weise bearbeiten und anpassen lassen. Sie knnen quasi in den Laden der .NET-Klassenbibliothek gehen und sich ein Form-Objekt heraussuchen. Dann schneiden Sie es sich nach Ihren Wnschen zu.

78

Das objektorientierte Windows-Formular

Abbildung 3.1: Formulare knnen transparent, rahmenlos und skalierbar sein.

Mit dem Objekt Object arbeiten


An den Tagen 1 und 2 haben Sie mit ein paar Objekten gearbeitet, aber Sie haben noch nicht das Objekt Object untersucht, die Basisklasse aller .NET-Klassen einschlielich der Windows Forms-Formulare. Da es von jeder Klasse geerbt wird, werden Sie von jetzt ab damit umgehen, und so ist es ratsam, ein wenig mehr darber zu erfahren, insbesondere ber die Methoden, die es Ihnen zur Verfgung stellt.
Object ist die allgemeinste Klasse, die Sie in .NET verwenden knnen. Daher wird sie verwendet, um jedes ansonsten unbekannte Objekt darzustellen. Wenn Sie beispielsweise folgendes Statement zum Erzeugen einer Variablen verwenden, wei die CLR nicht, als welchen Typ sie sie erzeugen soll, und erzeugt lediglich ein Object: Dim MyObjectVariable

Weil Ihre Variable nur ein Objekt ist, verlieren Sie eine Menge Funktionalitt, die Sie durch das Deklarieren eines expliziten Datentyps gewinnen wrden. So knnten Sie etwa einem Integer einen weiteren Integer hinzufgen, aber Sie knnen keine Objects aneinander hngen; ungeachtet Ihrer guten Absicht wrde dies zu einer Fehlermeldung fhren. Daher empfiehlt es sich, stets einen spezifischen Typ anzugeben, statt ein Object als Mdchen fr alles zu verwenden. Standardmig mssen Ihre Anwendungen stets einen Typ fr Ihre Variablen deklarieren. Das verhindert den falschen Einsatz der Variablen und verbessert das Leistungsverhalten. Sie knnen aber diese Sicherheitsfunktion durch Deaktivieren des Merkmals Option Strict abschalten. Um Nheres darber zu erfahren, lesen Sie bitte in Tag 2 den Abschnitt Das Kompilieren von Windows Forms.

79

Mit Windows Forms arbeiten

Sie werden in der Windows Forms-Programmierung viele Funktionen kennen lernen, die fr Sie Objekte erzeugen. So ruft etwa die Methode ConfigurationSettings.GetConfig Konfigurationseinstellungen aus einer Datei ab. Die Funktion liefert einen Datentyp Object, weil sie nicht genau wei, was Sie mit den zurckgegebenen Ergebnissen machen werden. Wird aber ein Object geliefert, knnen Sie die Ergebnisse durch Typumwandlung (Casting) in jeden gewnschten Datentyp transformieren. In C# wandeln Sie den Typ einer Variablen folgendermaen um:
myDataType myVariable; // Variable vom Typ myDataType deklarieren myVariable = (myDataType) ConfigurationSettings.GetConfig ("some setting"); // Einstellungen abrufen und in myDataType umwandeln

Das Objekt Object verfgt ber fnf Hauptmethoden: Equals, ReferenceEquals, GetHashCode, GetType und ToString. Sie sind allesamt ntzlich, daher wollen wir sie im Detail betrachten. Denken Sie daran, dass jedes Objekt von Object erbt, so dass diese Methoden jedem Objekt, das Sie verwenden, zur Verfgung stehen. Die Equals-Methode ist sowohl ein statisches als auch ein nicht-statisches Element. Sie knnen sie daher sowohl von einer bestimmten Objektinstanz aus als auch vom Typ Object selbst aus aufrufen. Listing 3.1 zeigt beispielsweise ein Codefragment, in dem sowohl statische als auch nicht-statische Versionen der Equals-Methode verwendet werden. Listing 3.1: Der Gebrauch der Equals-Methode
1: 2: 3: 4: 5: 6: 7: 8: int intA; int intB; intA = 4; intB = 5; Console.WriteLine("{0}", intA.Equals(intB)); // nicht-statische Methode Console.WriteLine("{0}", ObjectEquals(intA, intB)); // statische Methode

Die Zeilen 7 und 9 geben beide false in der Befehlszeile aus, weil die Objekte intA und intB nicht gleich sind (intA ist 4, und intB ist 5). Zeile 7 ruft jedoch die Equals-Methode von einer Instanz aus auf, whrend Zeile 9 Equals von der Object-Klasse selbst aus aufruft. Sehr hufig werden Sie zwei Objekte miteinander vergleichen mssen, so dass sich diese Methode als ntzlich erweisen drfte. Die Console.WriteLine-Methode gibt eine Zeichenfolge in der Befehlszeile aus. Der erste Parameter besteht in der auszugebenden Zeichenfolge. Eine Zahl in Klammern bedeutet, dass die Methode auf einen der zustzlich angegebenen Parameter verweisen sollte: {0} bedeutet also Gib den nchsten Parameter aus, {1} bedeutet Gib den darauf folgenden Parameter aus usw. Man knnte anstelle der numerischen Bezeichner auch einfach den Parameter selbst verwenden.

80

Das objektorientierte Windows-Formular

Um diese Methode verwenden zu knnen, mssen Sie Ihre Anwendung als regulre ausfhrbare Datei kompilieren (verwenden Sie dabei die Option /t:exe). Die Methode ReferenceEquals ist ein nur-statisches Element, das der Equals-Methode hnelt. Sie unterscheiden sich dahingehend, dass ReferenceEquals zwei Objekte miteinander vergleicht, um herauszufinden, ob sie sich in der gleichen Instanz befinden, also nicht, ob sie den gleichen Wert besitzen. Das lsst sich deswegen bewerkstelligen, weil in Computerbegriffen eine Instanz durch ihren Ort im Speicher (ihre Speicheradresse) definiert ist. Wenn also zwei Objekte auf dieselbe Speicheradresse verweisen, handelt es sich um dieselbe Instanz. Zwei verschiedene Speicheradressen knnen den gleichen Wert haben, aber es handelt sich nicht um dieselbe Instanz. (Beachten Sie, dass null oder Nothing in VB .NET stets mit derselben Instanz wie ein anderes null gleichzusetzen ist. Werfen Sie einen Blick auf Listing 3.2, das unterschiedliche Variablen miteinander vergleicht. Listing 3.2: Referenzen vergleichen
1: 2: 3: 4: 5: 6: 7: 8: object a = null; object b = null; object c = new Object(); Console.WriteLine(Object.ReferenceEquals(a, b)); Console.WriteLine(Object.ReferenceEquals(a, c)); a = c; Console.WriteLine(Object.ReferenceEquals(a, c));

Zeile 5 liefert true, weil sich alle null-Werte auf dieselbe Instanz beziehen und einander sind. Zeile 6 hingegen gibt false bzw. unwahr zurck, weil a und c zwei unterschiedliche Objekte sind. Zeile 7 sieht aus, als wrde sie a und c demselben Wert zuordnen, aber wenn Sie die eine Variable einer anderen zuweisen, setzen Sie eigentlich die Speicheradressen miteinander gleich, und daher liefert Zeile 8 true oder wahr. Falls Sie bereits ber Programmiererfahrung verfgen, wissen Sie, woher der Name ReferenceEquals kommt: Diese Methode vergleicht die Speicheradressen (bzw. Referenzen) der Variablen statt ihre Werte miteinander. In den meisten Fllen gengt die Equals-Methode, aber es ist hilfreich, auch ber die ReferenceEquals-Methode verfgen zu knnen. Die nchsten drei Methoden sind alle nicht-statisch. GetHashCode liefert einen Hash-Code fr Ihr Objekt. Ein Hash-Code ist eine numerische Darstellung eines Objekts und hat darber hinaus keine Bedeutung an sich. Die Objekte A und B knnen unterschiedliche

81

Mit Windows Forms arbeiten

Objekte sein, aber den gleichen Hash-Code generieren, wie etwa in Zeile 7. Sie knnen diesen Vorgang nicht umkehren und von der Zahl 7 auf ein Objekt schlieen; Hashing ist ein Einbahnstraen-Mechanismus. Wie ihr Name schon sagt, liefert die Methode GetType einfach nur den spezifischen Datentyp eines Objekts. Das ist sehr ntzlich, wenn man nicht wei, mit welcher Art von Daten man es zu tun hat, so etwa, wenn eine Funktion ein nicht identifizierbares Objekt liefert:
string a = "hallo"; Console.WriteLine(a.GetType());

Dieses Codestck wird System.String liefern, den Datentyp der Variablen a. Beachten Sie, dass diese Methode einen Datentyp zurckgibt, nicht etwa nur den Namen des Typs: Sie liefert einen Typ und keine Zeichenfolge. Diese Methode wird hufig zum Vergleichen von Datentypen verwendet, etwa im Zusammenhang mit der eben besprochenen Methode GetConfig:
myType myVariable; if (myVariable.GetType() != ConfigurationSettings.GetConfig ("mySettings").GetType()) return false;

Dieser Code prft, ob der von der GetConfig-Methode gelieferte Typ (ein Type-Objekt) der gleiche Typ ist wie myVariable. Und in diesem Fall ist er es nicht. Zum Schluss kommen wir auf die gebruchlichste Methode zu sprechen: ToString liefert die Zeichenfolgendarstellung eines Variablentyps. Das folgende Codefragment wrde beispielsweise die Zeichenfolge "system.object" in der Befehlszeile ausgeben:
Object objA = new Object(); Console.WriteLine(objA.ToString());

Beachten Sie, dass hier nicht der Wert eines Objekts ausgegeben wird, sondern der Typname. Manchmal wird die ToString-Methode fr eine bestimmte Klasse berschrieben. Der string-Datentyp berschreibt beispielsweise ToString, so dass er den tatschlichen Wert der Variablen ausgibt statt den Namen des Typs. Der folgende Code gibt die Zeichenfolge "hello world" aus:
string strA = "hello world"; Console.WriteLine(strA.ToString());

Formular-Eigenschaften
Das Objekt System.Windows.Forms.Form verfgt ber genau 101 Eigenschaften, die es Ihnen gestatten, beinahe jeden Aspekt des Formulars zu steuern. Da dies zu viele Eigenschaften fr nur einen Tag sind, lassen Sie uns nur auf ein paar der ntzlicheren Aspekte einen Blick werfen, gruppiert nach Funktion.

82

Das objektorientierte Windows-Formular

Gre und Position steuern


Ihnen sind bereits zwei Mglichkeiten, die Formulargre festzulegen, bekannt: die Eigenschaften Width und Height (Breite bzw. Hhe). Diese Eigenschaften bernehmen lediglich Zahlenwerte, um die Formulargre in Pixelwerten festzulegen. Zum Beispiel:
form1.Width form1.Height = 200

Sie knnen die Gre auch mit Hilfe der Size-Eigenschaft und eines Size-Objekts festlegen:
form1.Size = New Size(100, Form1.Size.Height) form1.Size = New Size(100, Form1.Size.Width)

Beide Methoden bewirken das Gleiche, aber meistens werden Sie die erste verwenden, da sie einfacher ist. Sie knnen die Gre auch abhngig von der Bildschirmgre des Benutzers festlegen, und zwar, indem Sie das Screen-Objekt verwenden. Listing 3.3 zeigt dafr ein Beispiel. Listing 3.3: Das Festlegen der Formularhhe abhngig von der Bildschirmgre (in VB .NET)
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: Imports System Imports System.Windows.Forms Public Class MultiForm : Inherits Form Public Sub New Me.Text = "Hauptformular" Me.Height = Screen.GetWorkingArea(Me).Height / 2 End Sub End Class Public Class StartForm Public Shared Sub Main() Application.Run(New MultiForm) End Sub End Class

Dieser Quellcode drfte Ihnen mittlerweile vertraut sein, daher betrachten wir lediglich Zeile 7 etwas genauer. Die Methode GetWorkingArea des ScreenObjekts liefert ein Rectangle-Objekt, das fr den Benutzerbildschirm steht. Sie bergeben das aktuelle Formularobjekt (mit Hilfe des Me-Schlsselworts), um der CLR mitzuteilen, welchen Bildschirm sie verwenden soll (nur fr den Fall, dass der Benutzer mehr als einen Bildschirm besitzt). Rectangle-Objekte haben wiederum Height- und Width-Eigenschaften, welche ihrerseits Integer liefern, die die

83

Mit Windows Forms arbeiten

Hhe und Breite des Bildschirms beschreiben. Danach teilen wir den Hhe-Wert durch 2. Kompilieren Sie diese Anwendung und fhren Sie sie aus. Beachten Sie, dass das Fenster jetzt die halbe Hhe Ihres Bildschirms beansprucht. Um festzulegen, wo das Fenster sich ffnet, knnen Sie die Eigenschaften Location, DesktopLocation, Top oder Left verwenden. Die letzten beiden legen die Position der oberen linken Ecke Ihres Formulars auf dem Bildschirm fest. Sie funktionieren genauso wie die Height- und Width-Eigenschaften. Beachten Sie, dass Sie diese Eigenschaften auf eine Position festlegen knnen, die sich weit auerhalb des Bildschirms befindet, etwa auf Top = 9999 und Left = 9999. Die Eigenschaften Location und DesktopLocation sind einander hnlich. Bei einem FormObjekt bewirken sie das Gleiche: Sie legen die Anfangsposition der oberen linken Ecke des Formulars fest. Die beiden folgenden Zeilen realisieren beispielsweise die gleiche Funktionalitt:
Form1.DesktopLocation = New Point(100,300) Form1.Location = New Point(100,300) Location ist eine Eigenschaft, die von Control, der Urgroelternklasse von Form, geerbt

wird. Diese Eigenschaft verwendet man, um die Position eines Steuerelements innerhalb eines anderen Steuerelements festzulegen. In unserem Fall ist das beinhaltende Steuerelement fr das Formular einfach der Benutzerbildschirm. Sie knnten die Location-Eigenschaft fr jedes beliebige Objekt verwenden, das vom Control-Objekt erbt, etwa fr die Steuerelemente TextBox oder Label (Beschriftung), um seine Position innerhalb eines anderen Objekts zu festzulegen zum Beispiel Ihres Formulars. Die Eigenschaft DesktopLocation gilt hingegen nur fr das Form-Objekt. Der Einheitlichkeit halber werden wir ab jetzt die Eigenschaft Location verwenden. Daher brauchen Sie nur eine Eigenschaft fr alle Windows Forms-Objekte zu verwenden.

Das Aussehen steuern


Sie wissen bereits ber die Text-Eigenschaft Bescheid, die den in der Titelzeile angezeigten Text steuert. Lassen Sie uns daher ein paar Dinge betrachten, die Sie mit Schriftarten (Fonts) anstellen knnen. Die Eigenschaft Font legt die Schriftart fest, die im Formular benutzt wird, es sei denn, die Font-Eigenschaft eines Steuerelements htte Prioritt. Die Eigenschaft ForeColor legt die Textfarbe fest. Betrachten Sie das folgende Codefragment:
Form1.Font = New Font(new FontFamily("WingDings"), 23) Form1.ForeColor = Color.Blue

Die erste Zeile erzeugt ein neues Font-Objekt (Sie merken schon, dass absolut alles in .NET ein Objekt ist, selbst Schriftarten und Farben). Es gibt eine ganze Reihe unterschiedlicher

84

Das objektorientierte Windows-Formular

Mglichkeiten, um Font-Objekte zu erzeugen (in anderen Worten: Es hat viele Konstruktoren), und dies ist lediglich eine davon. Sie legen die Schriftart fest, indem Sie das Objekt FontFamily verwenden, das vordefinierte Schriftartnamen enthlt. Der zweite Parameter besteht in der Gre der Schriftart (in der Maeinheit Punkt). Die zweite Zeile legt die Textfarbe mit Hilfe der Blue-Eigenschaft des Color-Objekts als blau fest. Wird dieser Code in einem Windows Forms-Formular verwendet, erzeugt er die Ausgabe, die Sie in Abbildung 3.2 sehen (Sie mssen mir glauben, dass die Farbe wirklich blau ist).

Abbildung 3.2: Aussehen und Farbe einer Schriftart werden in ihren jeweiligen Eigenschaften festgelegt.

Die Eigenschaften BackColor und BackgroundImage erlauben Ihnen, das vorgegebene Erscheinungsbild eines Formulars zu ndern. BackColor wird genauso verwendet wie ForeColor:
Form1.BackColor = Color.Lachsrosa

Die Eigenschaft BackgroundImage bernimmt ein Image-Objekt als Parameter. blicherweise verwenden Sie die FromFile-Methode des Image-Objekts, um eine Grafik zu laden; Sie mssen einen Pfadnamen angeben, z.B. so:
Form1.BackgroundImage = Image.FromFile("c:\winforms\day3\kaffeebohne.bmp") FromFile ist eine statische Methode, wie Sie wahrscheinlich schon herausgefunden haben, da Sie ja keine neue Image-Instanz erzeugen mssen, um sie verwenden zu knnen. Wenn wir von Abbildung 3.2 ausgehen, erhalten wir nun einen gekachelten Hintergrund, wie man in Abbildung 3.3 sehen kann.

Die Hintergrundfarbe des Label-Objekts (Bezeichnungsfeld) ist immer noch grau. An Tag 6 lernen Sie, wie Sie dies ndern knnen. Eine weitere Bildeigenschaft, die Sie anpassen knnen, ist die Icon-Eigenschaft. Das Icon oder Symbol wird in der oberen linken Ecke der Titelzeile Ihres Formulars verwendet, ebenso wie in jeder anderen Darstellung Ihrer Anwendung, wie etwa der Windows-Taskleiste. So legen Sie die Eigenschaft fest:
Me.Icon = New Icon("c:\winforms\day3\VSProjectApplication.ico")

85

Mit Windows Forms arbeiten

Abbildung 3.3: Sie knnen eine Grafik dazu verwenden, den Hintergrund Ihres Formulars damit zu kacheln.

Sie mssen eine neue Instanz des Icon-Objekts erzeugen, wobei Sie einen gltigen Pfad zu einer Grafik angeben. Die Grafik, die Sie auswhlen, muss eine Icon-Datei (.ico) sein, sonst erzeugt Ihre Anwendung eine Fehlermeldung. Das Bild des Mauszeigers lsst sich durch die Cursor-Eigenschaft steuern:
Me.Cursor = Cursors.Hand

Das Cursors-Objekt verfgt ber eine Menge Eigenschaften fr den Standardsatz an Windows-Mauszeigern, darunter Arrow, IBeam, WaitCursor und Help. In der .NET-Dokumentation finden Sie weitere Mauszeiger. Sie knnen auch einen eigenen Mauszeiger aus einer Datei laden:
Me.Cursor = New Cursor("Pfadname")

Die Mauszeigerdatei muss die Endung .cur tragen. Animierte Mauszeiger (jene mit der Endung .ani) werden von der CLR nicht untersttzt. Die Eigenschaft ShownInTaskBar legt fest, ob Ihre Anwendung in der Windows-Taskleiste zu sehen sein soll (dies betrifft nicht das Fenster am Bildschirm, sondern nur die Schaltflche in der Taskleiste). Der Standardwert lautet true (wahr). Unter bestimmten Gegebenheiten mchten Sie diese Einstellung vielleicht ndern, um einen Anwender daran zu hindern, ein Formular auszuwhlen. Wenn Sie etwa einen so genannten Splash Screen erzeugen, der etwa das Logo Ihrer Firma anzeigt, wollen Sie wahrscheinlich nicht, dass der Benutzer es auswhlt (es verschwindet ja auch sofort). Setzen Sie dann einfach die Eigenschaft ShownInTaskBar auf false, und der Benutzer kann es nicht mehr aus der Taskleiste auswhlen. Die Eigenschaft FormBorderStyle legt das Aussehen der Umrisslinie einer Windows FormsFormulars fest. Hauptschlich ndern Sie diese Eigenschaft, um die Grennderung des Formulars zu erlauben oder zu unterbinden. Manchmal betrifft das ndern einer Umrisslinie auch das Aussehen des Formulars, wie zum Beispiel:
Form1.FormBorderStyle = FormBorderStyle.Sizable

86

Das objektorientierte Windows-Formular

Die Aufzhlung fr FormBorderStyle (nicht zu verwechseln mit der Eigenschaft Form.FormBorderStyle) fhrt sieben verschiedene Typen von Umrisslinien auf, wie in Tabelle 3.1 zu sehen. (Eine Aufzhlung ist einfach eine Sammlung von Eigenschaften oder Stilen.) Abbildung 3.4 zeigt eine Sammlung der unterschiedlichen Stile.

Abbildung 3.4: Es stehen sieben vordefinierte Umrisslinienstile zur Auswahl.

Stil
Fixed3D FixedDialog FixedSingle FixedToolWindow

Beschreibung Nicht skalierbar. Dreidimensionale Umrisslinie. Nicht skalierbar. Dicke Umrisslinie. Nicht skalierbar. Dnne Umrisslinie. Formular mit schmalerer Titelleiste. Ntzlich fr die Anzeige von QuickInfos und Hilfefenstern. Nicht skalierbar. Enthlt keine Maximieren- oder Minimieren-Schaltflchen. Nicht skalierbar. Keine Umrisslinie. Skalierbar. Standardstil. Formular mit schmalerer Titelleiste. Ntzlich fr die Anzeige von QuickInfos und Hilfefenstern. Skalierbar. Enthlt keine Maximieren- oder MinimierenSchaltflchen.

None Sizable SizableToolWindow

Tabelle 3.1: Stile fr FormBorderStyle

87

Mit Windows Forms arbeiten

Zum Schluss gibt es noch drei Eigenschaften, die steuern, wie und ob Ihr Formular am Bildschirm angezeigt wird. Die Visible-Eigenschaft legt fest, ob Ihr Formular fr den Benutzer sichtbar ist. Ist ein Formular nicht zu sehen, kann man nicht damit interagieren. Auf diese Weise lassen sich bestimmte Dinge vor dem Anwender verbergen, etwa wenn Sie Ihre Anwendung geffnet lassen wollen, sie aber nicht mit der Benutzeroberflche interferieren soll. Standardmig ist ein solches Formular nicht sichtbar. Um ein Formular teilweise sichtbar zu machen (mit anderen Worten: transparent), mssen Sie die Eigenschaft Visible auf true (wahr) setzen und die Eigenschaft Opacity (Undurchsichtigkeit) verwenden. Diese Eigenschaft bernimmt einen Wert zwischen 1 (= vllig undurchsichtig, also vllig sichtbar) und 0,0 (= unsichtbar bzw. vllig durchsichtig). Um ein paar wirklich interessante Transparenztechniken vorzufhren, knnen Sie die Eigenschaft TransparencyKey so einstellen, dass eine bestimmte Farbe durchsichtig sein soll. Der folgende Code macht beispielsweise alle grauen Flchen im Formular unsichtbar, whrend alles andere undurchsichtig bleibt:
Form1.TransparencyKey = Color.Gray

Wenn Sie nun auch noch die BackColor des Formulars auf Grau setzen, erhalten Sie schlielich ein Formular wie das in Abbildung 3.5 zu sehende (das Formular wurde ber ein Fenster mit Befehlszeilenausgabe gelegt, um den Transparenzeffekt zu verdeutlichen).

Abbildung 3.5: Verwenden Sie die Eigenschaft TransparencyKey, um nur eine bestimmte Farbe sichtbar zu machen.

Interaktivitt steuern
Wenn Sie eine Windows-Anwendung betrachten, dann sehen Sie an deren Fenstern die Standardmerkmale: In der oberen rechten Ecke befinden sich Schaltflchen fr das Minimieren und Maximieren sowie das Schlieen des Fensters, manchmal auch eine HILFESchaltflche. Rechts unten befindet sich oft ein Anfasser, damit man mit der Maus die Fenstergre verndern kann. Diese Systemmenfelder sehen Sie in Abbildung 3.6.

88

Das objektorientierte Windows-Formular

Jedes Systemmenfeld lsst sich mit Hilfe der folgenden Eigenschaften in Ihrem Formular verstecken oder anzeigen:

MaximizeBox MinimizeBox HelpButton ControlBox SizeGripStyle

Abbildung 3.6: Diese Systemmenfelder gehren zu den Standardmerkmalen von Windows.

Die ersten vier Eigenschaften nehmen lediglich den Wert true oder false an. ControlBox legt fest, ob die vorhergehenden Schaltflchen berhaupt angezeigt werden sollen. Seien Sie hier jedoch vorsichtig: Wenn Sie ControlBox auf false setzen, knnte es sein, dass Sie Ihre Anwendung nicht mehr schlieen knnen!

Die HILFE-Schaltflche taucht nur auf, wenn die MINIMIEREN- und MAXIMIEREN-Schaltflchen nicht sichtbar sind. Dies ist ein Standardmerkmal von .NET.

Die Eigenschaft SizeGripStyle nimmt einen SizeGripStyle-Aufzhlungswert an: Auto, Hide oder Show. Auto zeigt den Skalierungsanfasser an, falls ntig (also abhngig vom FormBorderStyle), whrend Hide und Show die Grenziehpunkge (Skalierungsanfasser) jeweils verbergen oder zeigen. Mit einer Anwendung sind hufig zwei besondere Tasten verbunden: () und (ESC). Viele Anwendungen werden geschlossen, wenn Sie die (ESC)-Taste drcken. Sie knnen diese Funktionen mit den Eigenschaften AcceptButton und CancelButton steuern. Listing 3.4 zeigt in C# ein Beispiel, wie man diese Eigenschaften verwendet.

89

Mit Windows Forms arbeiten

Listing 3.4: Die Eigenschaften AcceptButton und CancelButton


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: 42: 43: 44: using System; using System.Windows.Forms; using System.Drawing; namespace TYWinForms.Day3 { public class Listing34 : Form { Button btAccept = new Button(); Button btCancel = new Button(); Label lblMessage = new Label(); public Listing34() { lblMessage.Location = new Point(75,150); lblMessage.Width = 200; btAccept.Location = new Point(100,25); btAccept.Text = "OK"; btAccept.Click += new EventHandler(this.AcceptIt); btCancel.Location = new Point(100,100); btCancel.Text = "Abbrechen"; btCancel.Click += new EventHandler(this.CancelIt); this.AcceptButton = btAccept; this.CancelButton = btCancel; this.Text = "Beispiel fr die Buttons OK und Abbrechen"; this.Height = 200; this.Controls.Add(lblMessage); this.Controls.Add(btAccept); this.Controls.Add(btCancel); } public void AcceptIt(Object Sender, EventArgs e) { lblMessage.Text = "Der OK-Button wurde gedrckt"; } public void CancelIt(Object Sender, EventArgs e) { lblMessage.Text = "Der Abbrechen-Button wurde gedrckt"; } } public class StartForm { public static void Main() { Application.Run(new Listing34());

90

Das objektorientierte Windows-Formular

45: 46: 47: 48:

} } }

Ein Groteil dieses Codes drfte bereits vertraut aussehen, daher knnen wir das meiste davon berfliegen. Die Zeilen 1 bis 3 importieren die notwendigen Namensrume. Zeile 5 deklariert den Namensraum, zu dem diese Anwendung gehrt; gem unserem Bezeichnungsschema heit dieser Namensraum TYWinForms.Day3. Zeile 7 deklariert die Windows Forms-Formularklasse. Die Zeilen 8 bis 10 deklarieren die Steuerelemente, die wir im Formular verwenden werden. Beachten Sie, dass sie auerhalb einer Methode deklariert werden, doch innerhalb einer Klasse. Dadurch lassen sie sich von jeder Methode in der Klasse verwenden. Im Konstruktor ab Zeile 12 findet der Groteil des Geschehens statt. Der Code in den Zeilen 13 bis 22 setzt einfach ein paar Eigenschaften fr unsere Steuerelemente. Beachten Sie insbesondere die Zeilen 18 und 22, die auf Methoden zeigen, die ausgefhrt werden, sobald eine der beiden Schaltflchen im Formular angeklickt wird. Diese Methoden, AcceptIt und CancelIt, befinden sich in den Zeilen 33 und 37 und geben einfach nur eine Meldung im Label-Steuerelement aus. Die Zeilen 24 bis 30 stellen einige Eigenschaften ein und fgen dem Formular die Steuerelemente hinzu. Die Zeilen 24 und 25 legen die Eigenschaften AcceptButton und CancelButton fr die jeweilige OK- oder Abbrechen-Schaltflche fest. Im Wesentlichen bewirkt das Anklicken der OK- oder Abbrechen-Schaltflche das Gleiche wie das Drcken der ()- oder der (ESC)-Taste. Schlielich enthalten die Zeilen 42 bis 46 eine weitere Klasse. Sie dient lediglich dazu, unsere Main-Methode aufzunehmen und die Methode Application.Run aufzurufen. Die Ausgabe der Anwendung nach dem Drcken der (ESC)-Taste wird in der Schaltflche AcceptButton und Abbildung 3.7 gezeigt. Erhlt die Abbrechen-Schaltflche im Formular den Fokus, veranlasst das Drcken der ()-Taste das Anklicken oder Niederdrcken dieser Schaltflche und folglich das Auslsen der CancelIt-Methode. Mit anderen Worten: Die Schaltflche erhlt die Eingabe noch vor dem eigentlichen Formular. Leider gibt es fr diese missliche Tatsache keinen einfachen Ausweg. Manche Steuerelemente, wie etwa die Button- und RichTextBox-Steuerelemente, behandeln das Drcken der ()-Taste automatisch, wenn sie den Fokus haben, also noch bevor irgendetwas anderes ausgefhrt werden kann. Sobald Sie dem Abbrechen-Button den Fokus geben (indem Sie ihn anklicken oder mit der Tabulatortaste darauf gehen), bewirkt die ()-Taste stets die Ausfhrung der CancelIt-Methode.

91

Mit Windows Forms arbeiten

Falls es Sie interessiert, knnen Sie diese Verhaltensweise umgehen, indem Sie das Button-Steuerelement selbst berschreiben. Wir werden dies an Tag 18 behandeln. Obwohl die AcceptButton- und CancelButton-Eigenschaften auf Button-Steuerelemente verweisen mssen, beachten Sie bitte, dass diese Buttons nicht unbedingt fr den Benutzer zu sehen sein mssen. Indem man die Visible-Eigenschaft auf false setzt, macht man die Schaltflchen unsichtbar, ohne ihre Funktionalitt zu verlieren. Die Eigenschaft AllowDrop schlielich legt fest, ob das Formular mit Drag & Drop-Funktionen umgehen kann, d.h. wenn ein Anwender mit der Maus ein Objekt in das Formular zieht und dort fallen lsst. Diese Eigenschaft kann einen true- oder false-Wert annehmen. Wie Sie Ihr Formular dazu bringen, etwas bei einem solchen Ereignis auszulsen, besprechen wir heute spter im Abschnitt Ereignisse im Formular.

Abbildung 3.7: Das Drcken der (ESC)-Taste hat die gleiche Wirkung wie das Anklicken der ABBRECHEN-Schaltflche.

Um eine vollstndige Liste der Eigenschaften des Form-Objekts nachzuschlagen, lesen Sie Anhang B Windows Forms-Steuerelemente.

Formular-Methoden
Das Form-Objekt verfgt ebenfalls ber zahlreiche Methoden: ber 57, um genau zu sein. In der Mehrzahl sind sie von den Klassen Object und Control geerbt, so dass sie den meisten Objekten, mit denen Sie in Windows Forms arbeiten, zur Verfgung stehen. Eingeteilt nach Kategorien sollen einige davon hier vorgestellt werden. Neben den hier behandelten Methoden sollten Sie auch die bereits vorgestellten ObjectMethoden wie Equals und ToString nicht vergessen. Das Form-Objekt kann auch sie nutzen.

Aspekte der Bildschirmanzeige


Die ersten zwei Methoden sind Show und Hide. Diese beiden Funktionen machen Ihr Formular jeweils sichtbar oder unsichtbar, indem sie die Visible-Eigenschaft ndern. Sie verndern das Formular selbst nicht etwa das Entfernen aus dem Speicher oder der Aufruf anderer Funktionen , sondern nur, ob der Benutzer es sehen kann.

92

Das objektorientierte Windows-Formular

Die Close-Methode jedoch entsorgt ein Formular (und seine Steuerelemente) vollstndig durch das Entfernen aus dem Speicher. Setzen Sie diese Methode ein, wenn Sie Ihre Anwendung schlieen wollen oder wenn Sie einfach kein Formular mehr brauchen. Da Windows ja ein Multitasking-Betriebssystem ist, drfen viele Fenster gleichzeitig geffnet sein. Jedes Fenster muss um die Aufmerksamkeit des Benutzers wetteifern. Das FormObjekt verfgt ber einige Methoden, die Ihnen helfen, diese Schwierigkeit zu bewltigen. Die Activate-Methode aktiviert Ihr Formular. Das kann zweierlei bedeuten:

Wenn Ihre Anwendung die aktive ist, also vom Benutzer gerade verwendet wird, stellt Activate das Formular in den Vordergrund der Bildschirmanzeige, so dass es ber allen anderen Fenstern liegt. Falls es nicht die aktive Anwendung ist, blinken die Titelleiste und das Symbol in der Taskleiste, um die Aufmerksamkeit des Benutzers auf sich zu lenken. Wahrscheinlich haben Sie diese Art von Blickfang schon einmal gesehen; die gebruchlichste Verwendung findet sich bei Instant-Messaging-Anwendungen. Hat Ihnen jemand eine Instant Message (IM) gesendet, whrend Sie gerade in einer anderen Anwendung arbeiten, ffnet sich pltzlich das IM-Fenster im Hintergrund und seine Titelleiste blinkt.

Beim Blickfang noch etwas direkter als Activate sind die Methoden BringToFront und SendToBack. Die erste Methode stellt ein Formular vor alle anderen Fenster in der Bildschirmanzeige, so dass der Benutzer gezwungen ist, es anzusehen. Das ist ntzlich, falls etwas mit Ihrer Anwendung geschieht, das die volle Aufmerksamkeit des Benutzers verlangt (beispielsweise der Erhalt einer Instant Message). Umgekehrt stellt SendToBack ein Formular hinter alle anderen Fenster auf dem Bildschirm. SendToBack wird zwar nicht hufig benutzt, ist aber fr alle Flle vorhanden. Sie knnen die Eigenschaft TopMost auf true setzen, um dafr zu sorgen, dass Ihr Formular immer im Vordergrund steht. Besonders ntzlich ist dieses Leistungsmerkmal, wenn Alarm- oder Fehlermeldungen ausgegeben werden sollen. Schlielich funktioniert die Refresh-Methode hnlich wie die Schaltflche AKTUALISIEREN in Ihrem Web-Browser: Sie zeichnet alles im Formular neu und aktualisiert mitunter sogar dessen Inhalte. Wir werden nher auf diese Methode zu sprechen kommen, wenn wir GDI+ an Tag 13 durchnehmen.

Eigenschaften zurcksetzen
Das Form-Objekt verfgt ber eine Reihe von Reset-Methoden, mit denen Sie genderte Eigenschaften auf ihre Vorgabewerte zurcksetzen knnen. Alle diese Methoden folgen dem Bezeichnungsschema ResetEigenschaft. Einige der gebruchlicheren sind:

ResetBackColor ResetCursor

93

Mit Windows Forms arbeiten

ResetFont ResetForeColor ResetText

Diese Methoden sind sehr ntzlich, wenn man etwas gendert hat und alles wieder rckgngig machen muss, man aber nicht wei oder sich nicht darum kmmert, welches jeweils der ursprngliche Wert war. Wenn der Benutzer beispielsweise in einem Textprogramm mehrmals die Schriftart ndert, aber dann wieder zu den Standardwerten zurckkehren mchte, knnte man ResetFont einsetzen.

3.2

Ereignisbehandlung

Ein Ereignis (event) ist etwas, das als Ergebnis einer Aktion oder Aktivitt passiert. Greifen Sie auf die Auto-Analogie zurck und stellen Sie sich bitte vor, dass Sie auf die Bremse treten (eine Aktion). Der Wagen hlt an (ein Ereignis). Treten Sie auf das Gaspedal (Aktion), fhrt der Wagen wieder an (Ereignis). Ein Ereignis ist stets die Folge einer Aktion, die stattgefunden hat. Auch Windows Forms verfgen ber Ereignisse, obwohl so manches davon nicht besonders augenfllig ist. ffnen und Schlieen etwa sind zwei Ereignisse, die erfolgen, wenn Sie Ihre Anwendung starten beziehungsweise beenden. Schieben Sie Ihren Mauszeiger ber das Formular, findet ein Ereignis statt, und wenn Ihr Mauszeiger es wieder verlsst, findet ein weiteres Ereignis statt. Ohne Ereignisse wren Windows Forms ziemlich fade, denn sie wrden nie etwas bewirken, ganz gleich, wie sehr der Anwender das auch erzwingen wollte.

Die Nachrichtenschleife
Ereignisse eignen sich in wunderbarer Weise dafr, Benutzereingaben zu handhaben. Lassen Sie uns zwei verschiedene Anwendungsmodelle betrachten eines mit, das andere ohne Ereignisse , bevor wir untersuchen, wie Anwendungen Ereignisse verwenden. Stellen Sie sich zunchst die ereignisgesteuerte Anwendung vor. Unter ereignisgesteuert versteht man, dass die Anwendung auf Ereignisse reagiert, die auf Benutzeraktionen hin erfolgen. Ohne diese Ereignisse wrde die Anwendung in der Tat berhaupt nichts tun. Die Ereignisse treiben die Anwendung quasi an. In diesem Modell ist die Anwendung unttig und wartet darauf, dass etwas passiert. Sie verwendet Ereignisse als ihr Stichwort, eine Aktion auszufhren. Ein Benutzer drckt beispielsweise eine Taste. Die Anwendung merkt, dass ein Ereignis stattgefunden hat

94

Ereignisbehandlung

(Tastendruck), fhrt eine Aktion aus, um das entsprechende Zeichen anzuzeigen, und wartet dann auf das nchste Ereignis. Eine Anwendung, die nicht ereignisgesteuert ist, gestattet dem Benutzer nicht die gewnschte Nutzung der Anwendung, vielmehr kann er lediglich auf ihre Aufforderungen reagieren. Mit einer ereignisgesteuerten Anwendung hingegen kann der Benutzer interagieren, ganz gleich, mit welchem ihrer Teile, zu welcher Zeit oder in welcher Reihenfolge. Stellen Sie sich einmal eine Taschenrechner-Anwendung vor, die nicht ereignisgesteuert wre. Sobald Sie die Anwendung gestartet haben, holt sie zwei Werte aus den Textfeldern, fhrt mathematische Berechnungen aus und gibt das Ergebnis aus. Befinden sich jedoch keine Werte in den Textfeldern, tut der Taschenrechner berhaupt nichts. Er kann nicht feststellen, ob sich eine Zahl gendert hat, da er keine Ereignisse erkennt. Jedes Mal, wenn Sie die Zahlen ndern, um einen anderen Wert zu berechnen, mssen Sie zuerst die Zahlen ndern und dann die Anwendung erneut ausfhren. Klassische Active Server Pages (also ASP als Vorgnger von ASP.NET) funktionieren auf diese Weise. Eine ASP-Anwendung wird ausgefhrt, sobald ein Benutzer die Seite von seinem Browser aus anfordert, dann gibt sie den ntigen HTML-Code aus und hlt an. Um das, was danach passiert, kmmern sich ASPs nicht, da sie nicht auf Ereignisse zu warten brauchen. Damit eine ASP-Anwendung Benutzereingaben verarbeitet, muss der Benutzer alle Eingaben in ein Webformular eintippen, bevor er es der ASP zur Verarbeitung zuschickt. Die ASP kennt nichts anderes als das, was ihr beim Start bekannt gegeben worden ist. Abbildung 3.8 illustriert diesen Vorgang.
Nicht ereignisbasiert Anwendung starten Benutzereingaben liefern

Verarbeitung

Stop

Abbildung 3.8: Anwendungen, die nicht ereignisgesteuert sind, erfordern einen dreistufigen Prozessablauf.

Beide Modelle haben ihre Vor- und Nachteile. Sobald sie einmal gestartet sind, laufen Anwendungen, die nicht ereignisgesteuert sind, ohne Benutzereingaben ab. Ereignisgesteuerte Anwendungen erfordern hingegen blicherweise eine Benutzereingabe, sind aber hufig reaktionsfhiger und interaktiv. Da aber Interaktion eine zwingende Eigenschaft einer jeden Windows-basierten Anwendung ist, werden alle Ihre Programme das ereignisgesteuerte Modell verwenden.

95

Mit Windows Forms arbeiten

Wie also entdeckt eine Anwendung, dass ein Ereignis stattgefunden hat? Sobald eine typische Windows-Anwendung gestartet wird, tritt sie in eine Nachrichtenschleife ein. Dies ist lediglich eine Zeitspanne, in der die Anwendung auf eine Eingabe wartet oder auf Mitteilungen vom Benutzer (einen Befehl etwa). Die Zeitspanne dauert so lange, bis die Anwendung beendet wird, so dass dies als eine Schleife (oder Zyklus) bezeichnet wird. Whrend dieser Schleife tut die Anwendung nichts, auer auf eine Benutzereingabe zu warten (die Spanne der Nicht-Aktivitt wird Leerlauf bzw. idling genannt). Sobald eine Eingabe entgegengenommen wird, verrichtet die Anwendung ihr Werk und fllt dann wieder zurck in ihre Nachrichtenschleife. Dieser Zyklus setzt sich wieder und wieder fort, bis die Anwendung geschlossen wird. Wenn Sie etwas eingeben, ist das Windows-Betriebssystem die erste Stufe in der Verarbeitung. Windows entscheidet darber, zu welcher Anwendung das Ereignis gehrt, und schickt es ihr zu. Diese Nachrichten sind als Windows-Nachrichten bekannt, wovon die Bezeichnung Nachrichtenschleife abgeleitet wurde.
Ereignisbasiert Anwendung starten

in Messageschleife eintreten

Verarbeitung

Benutzereingabe

Falls Benutzereingabe auf Stop lautet, dann

Stop

Abbildung 3.9: Die Nachrichtenschleife wartet auf eine Benutzereingabe.

Wenn Sie beispielsweise ein Microsoft Word-Dokument das erste Mal ffnen, passiert nichts. Word wartet auf Ihre Eingabe. Sobald Sie eine Taste drcken, findet ein Ereignis statt, eine Methode wird ausgefhrt (um das Zeichen am Bildschirm anzuzeigen), dann fllt Word wieder in die Nachrichtenschleife zurck, um weiteren Input abzuwarten. Jedes Mal wenn Sie eine Taste drcken, hlt die Nachrichtenschleife fr einen Augenblick an, um etwas zu verarbeiten, und setzt dann das Warten fort. Abbildung 3.9 illustriert diesen Zyklus. Windows Forms sind blicherweise die primre Benutzeroberflche Ihrer Anwendungen. Daher handhaben sie eine ganze Reihe unterschiedlicher Ereignisse.

96

Ereignisbehandlung

Form-Ereignisse
Sie haben bereits gesehen, wie sich einige Ereignisse handhaben lassen. Im Beispiel des Taschenrechners aus der gestrigen Lektion nutzten Sie das Click-Ereignis des ButtonObjekts. Die Handhabung von Ereignissen in Windows Forms ist immer ziemlich die gleiche, ungeachtet der Objekte, denen die Ereignisse zugeordnet sind. An Tag 5 besprechen wir Ereignisse eingehender, wie man sie handhabt und welche Unterschiede zwischen verschiedenen Ereignissen bestehen. Nun lassen Sie uns aber frs Erste einen Blick auf einige der 71 Ereignisse des Form-Objekts werfen.

Die Ausfhrung steuern


Um Ihnen als Entwickler die grtmgliche Kontrolle ber Ihre Anwendung zu verleihen, werden manche Ereignisse sowohl vor als auch nach dem Eintreten einer Aktion gestartet. Wenn Sie beispielsweise Ihr Formular schlieen, findet das Closing-Ereignis direkt vor dem Schlieen des Formulars statt, das Closed-Ereignis direkt danach. Diese Arten von Ereignisnamen folgen stets den Regeln fr das Partizip Prsens bzw. Partizip Perfekt, daher Closing (sich gerade schlieend) und Closed (also geschlossen). Nicht alle Ereignisse treten in solchen Paaren auf. Wenn Sie jedoch einen ing-Ereignisnamen lesen, knnen Sie sicher sein, dass auch ein ed-Ereignis folgt; das gilt aber nicht umgekehrt. Was ist der Grund fr die Methode, zwei Ereignisse pro Aktion zu verwenden? Stellen Sie sich bitte einen groen Gemseladen vor, der um 20:00 Uhr schliet. Fnf Minuten, bevor der Laden eigentlich zumacht, wird ber die Lautsprecheranlage eine Ansage zu hren sein, die die Kunden bittet, dass sie sich zu den Kassen begeben mchten. Dieses Vor-Schlieen-Ereignis dient dazu, die Kunden zu warnen, dass etwas geschehen wird. Um 20:00 Uhr schliet der Laden, und alle Kunden werden zum Verlassen des Ladens aufgefordert, alle Tren werden verriegelt. Diese zweistufige Vorgehensweise erlaubt es sowohl dem Ladeninhaber oder Filialleiter als auch den Kunden, sich auf das tatschliche eintretende Ereignis des Ladenschlusses vorzubereiten. Bei Windows-Anwendungen verhlt es sich hnlich. Wenn z.B. ein Benutzer eine groe Zahl von nderungen an einem Word-Dokument vornimmt, dann die Anwendung mit Hilfe des SCHLIEEN-Steuerelements schliet, aber das Speichern seines Dokuments vergisst, was wrde dann geschehen? Wenn man auf das Closed-Ereignis warten wrde, bevor man etwas unternhme, wre es bereits zu spt: Das Dokument wre schon geschlossen worden, und alle nderungen wren verloren. In .NET jedoch wird das Closing-Ereignis ausgelst, bevor das eigentliche Schlieen des Dokuments eintritt. In diesem Fall knnen Sie nun den Benutzer auffordern, sein Dokument zu speichern, bevor die nderungen verloren gehen, und dadurch die nderungen zu erhalten. Sobald das Fenster geschlossen ist,

97

Mit Windows Forms arbeiten

wird das Closed-Ereignis ausgelst. Danach knnen Sie jede Verarbeitungsmglichkeit nutzen, die Sie bentigen (etwa dem Benutzer eine Meldung anzeigen). Lassen Sie uns einen Blick auf ein Beispiel werfen, in dem dieser zweistufige Vorgang verwendet wird. Listing 3.5 verwendet die Closing- und Closed-Ereignisse im Form-Objekt, um das obige Beispiel zu illustrieren. Listing 3.5: Die Kontrolle der Programmausfhrung mit Hilfe von Ereignissen in VB .NET
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: Imports Imports Imports Imports System System.Windows.Forms System.Drawing System.ComponentModel

Namespace TYWinForms.Day3 public class Listing35 : Inherits Form public sub New() Me.Text = "Beispiel fr ein Ereignis" AddHandler Me.Closing, AddressOf Me.ClosingMsg AddHandler Me.Closed, AddressOf Me.ClosedMsg end sub public sub ClosingMsg(Sender as Object, e as CancelEventArgs) Microsoft.VisualBasic.MsgBox("Formular schliet sich") end sub public sub ClosedMsg(Sender as Object, e as EventArgs) Microsoft.VisualBasic.MsgBox("Formular ist geschlossen") end sub end class public class StartForm public shared sub Main() Application.Run(new Listing35) end sub end class End Namespace

In Zeile 4 wird ein Namensraum importiert, der noch nicht aufgetreten ist: System.ComponentModel. Er verfgt ber Objekte, die Ereignisse betreffen, die man weiter unten im Code bentigt. Die Zeilen 6 bis 10 sind nichts Besonderes. Die

98

Ereignisbehandlung

Zeilen 11 und 12 verwenden die AddHandler-Methode (die Ihnen an den Tagen 1 und 2 begegnet ist), um der CLR zu befehlen, die Methoden ClosingMsg und ClosedMsg (in den Zeilen 15 und 19) auszufhren, wenn die Closing- und Closed-Ereignisse jeweils ausgelst werden. Wir springen in Zeile 15 zur Closing-Methode, die ausgefhrt wird, sobald das Closing-Ereignis ausgelst worden ist und sich das Formular tatschlich schliet. Sehen Sie sich zunchst die Signatur (die erste Zeile) dieser Methode an. Sie bernimmt zwei Parameter: ein Object- und ein System.ComponentModel.CancelEventArgs-Objekt. Sie wissen bereits, was das Object-Objekt ist. Der zweite Parameter ist ein spezialisiertes Objekt, das nur fr Ereignisse gilt, insbesondere fr Closing-Ereignisse. Es verfgt ber eine besondere Eigenschaft namens Cancel, um damit den Vorgang in diesem Fall das Schlieen des Formulars zu unterbrechen, falls dies ntig sein sollte (wie etwa im Beispiel des Textprogramms). Wenn Sie festlegen, dass das Schlieen gestoppt werden soll (etwa weil der Benutzer vergessen hat, sein Dokument zu speichern), setzen Sie die Cancel-Eigenschaft auf true:
e.Cancel = true

Die Anwendung wird mit dem Schlieen innehalten und in die Nachrichtenschleife zurckkehren. In unserem Fallbeispiel befassen wir uns nicht damit zu verhindern, dass das Formular geschlossen wird. In Zeile 16 rufen wir vielmehr eine Methode auf, die eine Meldung auf dem Bildschirm anzeigt. Die MsgBox-Methode des Namensraums Microsoft.VisualBasic prsentiert lediglich ein Popup-Feld mit dem festgelegten Text darin. (Beachten Sie, dass wir anstatt den vollen Namen der MsgBox-Methode in Zeile 16 zu verwenden, auch den Namensraum Microsoft.VisualBasic mit Hilfe von Imports htten importieren knnen.) In Zeile 19 beginnt die Methode ClosedMsg. Sie wird ausgefhrt, nachdem das Formular sich geschlossen hat. Beachten Sie, dass sie als zweiten Parameter ein EventArgs-Objekt statt CancelEventArgs bernimmt. Den Grund dafr besprechen wir an Tag 5. Diese Methode wiederum ruft die MsgBox-Funktion auf, um eine weitere Meldung anzuzeigen, die den Benutzer darber informiert, dass das Formular geschlossen wurde. Kompilieren Sie zum Schluss diesen Code, entweder mit VS .NET oder mit folgendem Befehl:
vbc /t:winexe /r:system.windows.forms.dll /r:system.drawing.dll listing.3.5.vb

Sie erinnern sich bestimmt noch von Tag 2 daran, dass sich der Namensraum System.ComponentModel in der Assembly System.dll befindet. Das ist der Grund, warum wir hier keine

99

Mit Windows Forms arbeiten

neuen Assemblies referenzieren mssen. Abbildung 3.10 zeigt die Ausgabe, nachdem das Steuerelement zum Schlieen des Fensters angeklickt worden ist.

Abbildung 3.10: Das Closing-Ereignis erlaubt Ihnen, das ClosedEreignis abzufangen.

Nicht alle Ereignisse folgen diesem zweistufigen Ablauf. Man findet ihn aber bei einigen
Form-Objekten, die Tabelle 3.2 beschreibt. Ereignis
Closed

Beschreibung Erfolgt beim Schlieen eines Formulars

InputLanguage- Erfolgt, sobald der Benutzer die Sprache des Formulars zu ndern versucht Changed Validated

Erfolgt, sobald die Eingabe eines Steuerelements auf Gltigkeit geprft worden ist

Tabelle 3.2: Ereignisse, die vor dem Cursor-Einsatz stattfinden

Es gibt einige wichtige Ereignisse, von deren Existenz Sie Kenntnis haben sollten, wenn Sie mit Windows Forms zu tun haben. Closed haben Sie ja bereits kennen gelernt: Es wird ausgelst, sobald sich ein Formular schliet. Es gibt weiter ein Load-Ereignis, das sofort ausgelst wird, noch bevor ein Formular zum ersten Mal angezeigt wird. Der Ereignishandler fr dieses Ereignis ist hufig eine geeignete Stelle, um Komponenten fr Ihr Formular zu initialisieren, d.h. falls Sie sie noch nicht im Konstruktor initialisiert haben sollten. Das Activated-Ereignis findet statt, sobald Ihr Formular den Fokus erhlt und zur aktiven Anwendung wird es korrespondiert mit der Activate-Methode. Dementsprechend wird

100

Ereignisbehandlung

Deactivate ausgelst, sobald Ihr Formular deaktiviert wird, also den Fokus verliert, oder wenn eine andere Anwendung die aktive wird.

Maus- und Tastaturereignisse


Maus- und Tastaturaktionen gehren zu den wichtigsten Arten von Ereignissen. Schlielich sind dies wesentliche Formen von Benutzereingaben. Daher gibt es eine ganze Reihe von Ereignissen, die mit diesen zwei Eingabegerten verbunden sind. Lassen Sie uns mit Tastaturereignissen anfangen. Zuerst an der Reihe ist das KeyPress-Ereignis. Es erfolgt jedes Mal, wenn eine Taste gedrckt wird, ganz gleich welche (wir sehen gleich noch, wie man feststellt, welche Taste es war). Falls dieses Ereignis nicht gengend Kontrollmglichkeiten erffnet, gibt es noch die Ereignisse KeyDown und KeyUp, die jeweils ausgelst werden, sobald eine Taste heruntergedrckt und dann wieder losgelassen wird. Diese Ereignisse liefern zustzliche Informationen (etwa welche Taste gedrckt wurde), die Sie in Ihrer Anwendung nutzen knnen. Ihre Ereignishandler, also diejenigen Methoden, die ausgefhrt werden, sobald das Ereignis ausgelst worden ist, sind dementsprechend spezialisiert. In VB .NET erfolgt das Deklarieren dieser Handler auf die gleiche Weise, mit der Sie bereits vertraut sind:
'VB .NET AddHandler Form1.KeyPress, AddressOf methodenName AddHandler Form1.KeyDown, AddressOf methodenName AddHandler Form1.KeyUp, AddressOf methodenName

In C# mssen Sie jedoch die spezialisierten Handler bercksichtigen. Statt EventHandler in Zeile 18 von Listing 3.4 wie gewohnt zu verwenden
18: btAccept.Click += new EventHandler(this.AcceptIt);

mssen Sie die Objekte KeyPressEventHandler und KeyEventHandler einsetzen:


//C# Form1.KeyPress += new KeyPressEventHandler(methodenName) Form1.KeyDown += new KeyEventHandler(methodenName) Form1.KeyUp += new KeyEventHandler(methodenName)

Genau wie das CancelEventArgs-Objekt besitzen auch die Objekte KeyPressEventHandler und KeyEventHandler besondere Eigenschaften, die Ihrer Anwendung helfen festzustellen, welche Aktion das jeweilige Ereignis ausgelst hat. Der KeyPressEventHandler hat zwei Eigenschaften: Handled und KeyChar. Bei der ersten Methode handelt es sich lediglich um einen true- oder false-Wert, der anzeigt, ob Ihre Methode den Tastendruck verarbeitet hat (ist das nicht der Fall, wird der Tastendruck zur Verarbeitung an das Windows-Betriebssystem geschickt). Meistens werden Sie diese Eigen-

101

Mit Windows Forms arbeiten

schaft auf true setzen, es sei denn, Sie wnschen explizit, dass das Betriebssystem diesen speziellen Tastendruck verarbeitet (wenn Sie etwa die Taste (Druck) nicht handhaben mssen, leiten Sie dies an das Betriebssystem weiter). Die Eigenschaft KeyChar liefert einfach die Taste, die gedrckt wurde. Listing 3.6 zeigt ein Beispiel in VB .NET. Listing 3.6: Verarbeitung von Tastenbettigungen
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: Imports Imports Imports Imports System System.Windows.Forms System.Drawing System.ComponentModel

Namespace TYWinForms.Day3 public class Listing36 : Inherits Form public sub New() Me.Text = "Tastendruck-Beispiel" AddHandler Me.KeyPress, AddressOf Me.KeyPressed end sub public sub KeyPressed(Sender as Object, e as KeyPressEventArgs) Microsoft.VisualBasic.MsgBox(e.KeyChar) e.Handled = True end sub public shared sub Main() Application.Run(new Listing36) end sub end class End Namespace

In Zeile 11 stoen Sie auf den Ereignishandler namens KeyPressed, welcher dem Ereignis KeyPress zugeordnet ist. In Zeile 14 deklarieren Sie den Ereignishandler. Beachten Sie bitte, dass er ein KeyPressEventArgs-Objekt als Parameter bernimmt dieser Name korrespondiert mit dem KeyPressEventHandlerObjekt, das wir weiter oben besprochen haben. In Zeile 15 zeigen Sie einfach das gedrckte Zeichen in einem Meldungsfeld an und setzen dann in Zeile 16 die Handled-Eigenschaft auf true. Abbildung 3.11 zeigt die entsprechende Bildschirmausgabe, wenn das groe A gedrckt wurde (() + (a)). Beachten Sie jedoch, dass lediglich Buchstaben, Zahlen und die ()-Taste das KeyPressEreignis auslsen. Um andere Tasten wie etwa (Strg), (Alt) und die Funktionstasten (F1) bis (F12) zu handhaben, mssen Sie die KeyUp- und KeyDown-Ereignisse verwenden. Erin-

102

Ereignisbehandlung

nern Sie sich daran, dass diese Ereignisse Handler des Typs KeyEventhandler nutzen, so dass der zweite Parameter Ihrer Ereignishandler-Methode auf KeyEventArgs lauten muss:
Public Sub KeyReleased(Sender as Object, e as KeyEventArgs) 'ein wenig Code end Sub

Abbildung 3.11: Solange Ihr Formular den Fokus besitzt, fhrt jeder Tastendruck die KeyPressed-Methode aus.

Das Objekt KeyEventArgs verfgt ber mehrere Eigenschaften, die sich als ntzlich erweisen, um festzustellen, welche Taste gedrckt wurde:

Alt: Wahr- oder Falsch-Wert, der anzeigt, ob die (Alt)-Taste gedrckt wurde Control: zeigt an, ob die (Strg)-Taste gedrckt wurde Handled: entspricht der Handled-Eigenschaft des Objekts KeyPressEventArgs KeyCode: der Tastaturcode fr die gedrckte Taste KeyData: die Tastendaten fr die gedrckte Taste KeyValue: der Tastaturcode fr die gedrckte Taste Modifiers: gibt Flags zurck, die anzeigen, welche Tasten und Zusatztasten (wie etwa (), (Strg) oder (Alt)) gedrckt wurden Shift: zeigt an, ob die ()-Taste gedrckt wurde

Jede Taste auf der Tastatur hat eindeutige KeyCode-, KeyData- und KeyValue-Werte. Die Eigenschaften KeyCode und KeyValue sind blicherweise gleich. KeyData weist bei den meisten Tasten den gleichen Wert wie die beiden anderen Eigenschaften auf, ist jedoch unter-

103

Mit Windows Forms arbeiten

schiedlich bei Zusatztasten (eine vollstndige Referenz finden Sie bei der Keys- bzw. Tasten-Aufzhlung in der Dokumentation des .NET Frameworks). Mausereignisse finden in einer standardisierten Reihenfolge statt: 1. MouseEnter: sobald der Mauszeiger ber das Formular gelangt 2. MouseMove: wenn der Mauszeiger sich ber dem Formular bewegt 3. MouseHover: wenn der Mauszeiger einfach nur ber dem Formular schwebt (d.h. ohne Bewegung oder Mausklick) 4. MouseDown: wenn man ber dem Formular eine Maustaste drckt 5. MouseUp: wenn man die Maustaste wieder lst 6. MouseLeave: wenn der Mauszeiger das Formular wieder verlsst (sich vom Formular wegbewegt) Die Ereignisse MouseEnter, MouseHover und MouseLeave liefern keine besonderen Informationen und verwenden daher den standardmigen Ereignishandler EventHandler und die Ereignisparameter-Objekte EventArgs. Die Ereignisse MouseMove, MouseDown und MouseUp liefern hingegen spezifische Informationen und verwenden alle die MouseEventArgsObjekte des Ereignishandlers MouseEventHandler:
public Sub MouseClick(Sender as Object, e as MouseEventHandler)

Das MouseEventhandler-Objekt liefert Information wie etwa die genaue Position des Mauszeigers auf dem Bildschirm, welche Maustaste gedrckt wurde usw. Die Eigenschaften sind in Tabelle 3.3 zusammengefasst.
Eigenschaft
Button

Beschreibung Holt die Information ein, welche Maustaste gedrckt wurde (MouseButtons.Left, MouseButtons.Middle, MouseButtons.None, MouseButtons.Right, MouseButtons.XButton1 oder MouseButtons.XButton2) Die Anzahl von Mausklicks (ein Integerwert) Die Anzahl von Drehungseinheiten, um die sich das Mausrad bewegt hat Die x-Bildschirmkoordinate des Mauszeigers Die y-Bildschirmkoordinate des Mauszeigers

Clicks Delta X Y

Tabelle 3.3: Eigenschaften von MouseEventHandler

104

Ereignisbehandlung

Drag & Drop


Das mit Windows eingefhrte Merkmal des Drag & Drop, also des Klickziehens, erlaubt es dem Anwender, eine Art Abkrzung zu nehmen, indem er mit der Maus ein Symbol auf dem Bildschirm auf eine Anwendung zieht. Diese bernimmt die Datei, die vom Symbol dargestellt wird, und verarbeitet sie. Haben Sie beispielsweise Microsoft Word geffnet und ziehen das Symbol eines Word-Dokuments darauf, ffnet Word dieses Dokument automatisch zur Bearbeitung. Klickziehen erlaubt Ihnen auch, nur mit der Maus Dateien aus einem Ordner in einen anderen zu verschieben oder zu kopieren. Es ist recht einfach, eine Windows Forms-Anwendung fr Drag & Drop fit zu machen. Als Erstes mssen Sie die Eigenschaft DragDrop des Formulars auf true setzen und dann den Code fr die Ereignisse DragDrop, DragEnter, DragLeave oder DragOver schreiben. Die letzten drei Ereignisse sind den hnlich benannten Mausereignissen sehr hnlich; sie treten auf, wenn ein Symbol in oder aus Ihrem bzw. ber Ihr Formular bewegt wird. Alle diese Ereignisse verwenden den Objekt-Handler DragEventHandler und den Ereignisparameter DragEventArgs. Wir werfen einen Blick auf die Eigenschaften dieses Parameters. Die Eigenschaft AllowedEffects ist ein Indikator, der besagt, welche Drag & DropAktionen stattfinden knnen. Wenn Sie etwa versuchen, eine Datei, die nur gelesen werden darf, zu ziehen und abzulegen, knnen Sie diese Datei nur kopieren, nicht aber verschieben. Die Aktionen werden durch die DragDropEffects-Aufzhlung angezeigt: DragDropEffects.All, DragDropEffects.Copy, DragDropEffects.Link, DragDropEffects.Move, DragDropEffects.None und DragDropEffects.Scroll. Alle diese Effekte entsprechen einfachen Windows-Funktionen. Die Eigenschaft DragEventsArgs wiederum gibt den sich gerade ereignenden Effekt an. Dieser ist einer der oben aufgefhrten DragDropEffects-Werte. Wenn der Benutzer etwa eine Datei zieht, whrend er die (Strg)-Taste gedrckt hlt, wird versucht, eine Kopieroperation durchzufhren, und die Effect-Eigenschaft wird DragDropEffects.Copy anzeigen. Die Data-Eigenschaft enthlt das Element, das gerade Gegenstand einer Drag & DropOperation ist. Ungeachtet seines Typs wird dieses Element vom IdataObject-Objekt dargestellt, das zahlreiche unterschiedliche Objekttypen abbilden kann. (Nhere Informationen hierzu finden Sie in der Dokumentation zum .NET Framework.) Die Eigenschaft KeyState verrt Ihnen, ob jeweils die Taste (), (Strg) oder (Alt) gedrckt ist, genau wie die Alt-, Control- und Shift-Eigenschaften der KeyUp- und KeyDownEreignisse. Die X- und Y-Eigenschaften schlielich sind die gleichen wie jene fr Mausereignisse. Sie zeigen an, an welcher Stelle (im Koordinatensystem) sich zum Zeitpunkt des Ereignisses ein Element befand.

105

Mit Windows Forms arbeiten

nderungsereignisse
Jedes Mal wenn sich eine der Form-Eigenschaften ndert, ist damit normalerweise ein Ereignis verbunden. Wenn man beispielsweise die Text-Eigenschaft des Formulars ndert, wird das TextChanged-Ereignis ausgelst. Meist werden diese Ereignisarten fr Validierungsroutinen verwendet. Wenn Sie etwa die Zahl der in Ihrem Formular verwendbaren Schriftarten begrenzen wollen, knnten Sie einen Handler fr das FontChanged-Ereignis erzeugen, der ungltige Benutzerauswahlen berschreibt. Ich werde hier nicht alle diese Ereignisse auffhren. Sie knnen feststellen, welche das sind, indem Sie einfach das Wort Changed (gendert) an die jeweiligen Eigenschaften anhngen, ber die Sie heute gelesen haben: TextChanged, CursorChanged, VisibleChanged usw. Alle diese Methoden verwenden Handler vom Typ EventHandler.

3.3

Zusammenfassung

Das Basisobjekt fr alle anderen Objekte ist im .NET Framework nomen est omen das Object-Objekt. Da es so allgemeingltig gefasst ist, wird es an verschiedenen Stellen verwendet, an denen ein spezialisiertes Objekt entweder nicht gebraucht wird oder sich nicht genauer bestimmen lsst. Object verfgt ber fnf Methoden Equals, ReferenceEquals, GetHashCode, GetType und ToString , die alle anderen .NET-Objekte erben. Das indirekt von Object abgeleitete Objekt System.Windows.Forms.Form stellt blicherweise den Hauptbestandteil Ihrer Windows Forms-Anwendungen dar. Es stellt den Rahmen und Hintergrund fr weitere Bestandteile der Benutzerschnittstelle bereit und enthlt selbst eine reiche Palette an Funktionen. Das Form-Objekt besitzt 101 Eigenschaften, mit denen Sie praktisch jeden einzelnen Aspekt der Benutzeroberflche steuern knnen. Sie knnen Ihr Formular mit Hilfe der Opacity-Eigenschaft transparent machen, es mit der Eigenschaft FormBorderStyle nichtskalierbar machen, und Sie knnen die Art und Weise der Benutzerinteraktion mit Hilfe der Eigenschaften MaximizeBox, MinimizeBox, HelpButton, SizeGripStyle und ControlBox kontrollieren.
Form verfgt zudem ber zahlreiche Methoden wie etwa BringToFront oder Focus, die Sie zur Steuerung der Bildschirmanzeige ausfhren knnen.

Sie haben etwas ber Ereignisse und die Nachrichtenschleife erfahren, die einer Anwendung das Unttigsein erlaubt, bis ein Benutzer eine Eingabe vornimmt. Das Form-Objekt verfgt ber Ereignisse, die anlsslich zahlreicher Benutzeraktivitten ausgelst werden, darunter Mausklick, Tastendruck und Drag & Drop.

106

Fragen und Antworten

3.4
F

Fragen und Antworten

Kann ich von einem selbst erstellten Formular erben? Wie werden dabei Eigenschaften behandelt? A Das ist zutreffend; Sie knnen von jeder Klasse erben, es sei denn, sie ist mit dem Schlsselwort NonInheritable (in C# heit es sealed) deklariert. Der Umgang mit geerbten Eigenschaften ist hufig kompliziert. Schauen Sie sich als Beispiel folgenden Code an:
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: Imports System Imports System.Windows.Forms Imports System.Drawing Public Class ClassA : Inherits Form private lblMessage as New Label Public Sub New() lblMessage.Location = new Point(100,100) lblMessage.Height = 100 lblMessage.Width = 200 lblMessage.Text = "Hello World!" Me.Controls.Add(lblMessage) End Sub End Class Public Class ClassB : Inherits ClassA private lblMessage as New Label Public Sub New() lblMessage.Location = new Point(100,100) lblMessage.Height = 100 lblMessage.Width = 200 Me.Controls.Add(lblMessage) End Sub End Class Public Class StartForm Public Shared Sub Main() Application.Run(New ClassB) End Sub End Class

Wenn Sie diese Anwendung kompilieren und ausfhren, wird das von ClassB definierte Formular angezeigt. Beachten Sie, dass in dieser Klasse nirgends die Text-

107

Mit Windows Forms arbeiten

Eigenschaft des in Zeile 18 deklarierten Bezeichnungsfelds (Label) gesetzt wird. Wenn Sie jedoch die Anwendung ausfhren, enthlt das Bezeichnungsfeld den Text Hello World! Dieser Text wird in Zeile 12 in ClassA festgelegt. All dies passiert nicht deshalb, weil das Bezeichnungsfeld von ClassB von ClassA geerbt worden wre, sondern vielmehr, weil im Konstruktor von ClassA ein Bezeichnungsfeld mit dem fraglichen Text erstellt und dem Formular hinzugefgt wird. Erinnern Sie sich daran, dass in jedem Konstruktor als Erstes ein Aufruf an den Konstruktor seiner Basisklasse erfolgt. Daher ist zur Ausfhrungszeit des Konstruktors von ClassB bereits ein Bezeichnungsfeld mit den Worten Hello World! vorhanden. Sie knnen jedoch von ClassB aus dieses Bezeichnungsfeld nicht bearbeiten. Es ist nmlich in Zeile 6 als private deklariert und daher nur fr ClassA zugnglich. Wrden Sie es in public ndern, erhielten Sie eine Fehlermeldung, denn ClassB htte das Bezeichnungsfeld lblMessage geerbt und Zeile 18 wrde versuchen, es zu redeklarieren das ist leider keine gltige Operation. Unterm Strich lsst sich also sagen, dass Vorsicht beim Erben eigener Klassen angebracht ist. Achten Sie ebenfalls auf den Kontext der Sicherheit, in dem Variablen deklariert werden.

3.5

Workshop

Dieser Workshop soll Ihnen helfen, sich besser an die heute behandelten Begriffe zu erinnern. Es hilft Ihnen sehr, die Antworten vollstndig zu verstehen, bevor Sie weiterlesen. Die Antworten zu den Quizfragen und den bungen finden Sie in Anhang A.

Quiz
Die Fragen 1 bis 3 beziehen sich auf das folgende Codefragment:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: using System; using System.Windows.Forms; using System.Drawing; public class MyForm : Form { private Label lblMessage = new Label(); public MyForm() { this.Text = "Hello World!"; } }

108

Workshop

12: 13: 14: 15: 16: 17:

public class StartForm { public static void Main() { Application.Run(new MyForm()); } }

1. Was wrde das folgende Statement liefern, wenn man es unter Zeile 9 platzieren wrde?
Console.Write(this.ToString());

2. Was wrde der folgende Code liefern, wenn man ihn unter Zeile 9 platzieren wrde?
Label lblTemp = new Label(); Console.Write(lblTemp.Equals(this.lblMessage).ToString());

3. Was wrde der folgende Code liefern, wenn man ihn unter Zeile 9 platzieren wrde?
Label lblTemp = new Label(); Console.Write(Object.ReferenceEquals(lblTemp, this.lblMessage).ToString());

4. Wahr oder falsch? Das KeyPress-Ereignis erfordert einen Handler vom Typ KeyEventHandler. 5. Nennen Sie die fnf Eigenschaften des Objekts MouseEventArgs. 6. Schreiben Sie ein einzelnes Statement in VB .NET, das die Breite eines Formulars auf ein Drittel der Bildschirmhhe festlegt. 7. Welche Eigenschaft kontrolliert, welche Schaltflche aktiviert wird, sobald der Benutzer die (ESC)-Taste gedrckt hat? 8. Welches ist der Standardwert von FormBorderStyle? 9. Welche drei Ereignisse verwenden das Paradigma, dass mit einer einzelnen Aktion zwei Ereignisse verknpft sind?

bungen
1. Erstellen Sie in C# eine Anwendung, die alle sechs Mausereignisse berwacht. Zeigen Sie eine Meldung in einem Textfeld an, wenn die einzelnen Ereignisse auftreten. 2. Erstellen Sie in VB .NET eine Anwendung, mit der die Benutzer die Eigenschaften Text, Height, Width und Opacity durch Werteingaben in eine TextBox und das Drcken einer EINGEBEN-Schaltflche anpassen knnen.

109

Mens und Symbolleisten

Mens und Symbolleisten

Mittlerweile drften Sie mit dem Form-Objekt und dem Erstellen von Windows FormsAnwendungen vertraut sein. Sie haben bereits gelernt, wie Sie die Benutzeroberflche verndern, indem Sie einige Steuerelemente wie etwa Label oder TextBox einfgen sowie die Eigenschaften des Formulars verndern. Schauen wir uns einmal einige trickreichere Steuerelemente an. Mens und Symbolleisten gehren zur Standardausstattung einer Benutzeroberflche. Praktisch jede Windows-Anwendung weist sie auf, und die Benutzer wissen sie zu bedienen. Darber hinaus verleihen sie Ihrer Anwendung ein vollstndigeres und gepflegteres Aussehen. Dieses Kapitel zeigt, wie Sie diese gebruchlichen UI-Elemente erstellen und dem Benutzer gestatten, sie zu bedienen. Heute lernen Sie,

was Windows Forms-Steuerelemente sind, wie Sie Ihren Formularen interaktive Mens hinzufgen, wie Sie Ihre Anwendung kontextabhngig machen, wie Sie Schaltflchen in eine Symbolleiste einfgen, wie man Informationen in der Statusleiste anzeigt, wie Sie Ihrer Anwendung Bildlaufleisten hinzufgen.

4.1

Einfhrung in Windows Forms-Steuerelemente

An den Tagen 1 bis 3 haben Sie mit einigen Steuerelementen gearbeitet, doch Sie haben eigentlich nicht untersucht, was man unter einem Steuerelement versteht. Lassen Sie uns einen Schritt zurcktreten und uns ansehen, was Steuerelemente (Controls) sind und warum und wozu man sie verwendet. Fr den Benutzer ist ein Windows Forms-Steuerelement ein selbststndiger Bestandteil der Benutzeroberflche. Schaltflchen, Mens und Listenfelder sind allesamt Steuerelemente. Manche davon, wie etwa Schaltflchen, sind interaktiv, whrend andere, wie etwa LabelSteuerelemente (Bezeichnungsfelder), es nicht sind. Den Benutzer kmmert es nicht, wie ein Steuerelement genau funktioniert er kann es intuitiv nutzen, um seine Aufgaben zu erledigen. Stellen Sie sich ein Steuerelement wie eine Uhr vor. Der Benutzer sieht nur das uere der Uhr und daher nur uere Eigenschaften, wie etwa die Farbe oder die Uhrzeit. Der Benutzer kann die Standuhr verschieben und Dinge darauf platzieren, denn sie ist eine physische Entitt, die sich manipulieren lsst.

112

Einfhrung in Windows Forms-Steuerelemente

Der Entwickler hingegen betrachtet die Uhr (das Steuerelement) ein wenig anders, da er in der Lage ist, sie zu ffnen und ihr Innenleben zu manipulieren: die Zeit einstellen, sie aufziehen, die Zahnrder verndern, das Pendel in Gang setzen usw. Die Uhr steht immer noch fr einen Teil der Benutzeroberflche, doch sie ist auch ein Gegenstand mit Eigenschaften, Methoden und Ereignissen. Ein Steuerelement lsst sich ebenso steuern wie das Form-Objekt. Jedes Steuerelement wird ebenfalls aus einer Klasse erzeugt, die genau jenen Klassen hnelt, die Sie erstellt haben. Das Steuerelement erbt von anderen Klassen, die sich im .NET Framework befinden. Die Standuhr prsentiert Ihnen ebenso wie Windows Forms-Steuerelemente zwei Gesichter (vgl. Abbildung 4.1): ein ueres fr den Benutzer, ein tiefer reichendes inneres fr den Entwickler. (Es gibt sogar noch tiefer reichende. Schlielich kann der Hersteller der Zahnrder Elemente kontrollieren, auf die der Entwickler keinen Zugriff hat.) Ihnen ist bekannt, dass Forms, Objects und Steuerelemente Objekte sind, die Sie mit Hilfe ihrer Methoden und Eigenschaften manipulieren knnen. Wir wollen einmal unser Lieblingsobjekt, Form, mit Formular-Steuerelementen vergleichen.
Forms und Steuerelemente sind einander sehr hnlich. Sie erben beide (direkt oder indirekt) von der Control-Klasse, demzufolge teilen sie zahlreiche Eigenschaften und Methoden miteinander, so etwa ToString, Equals und GetType. Da Sie das Form-Objekt zu

verwenden wissen, kennen Sie die Grundlagen fr die Verwendung aller Steuerelemente. Auch Steuerelemente werden im Namensraum System.Windows.Forms gespeichert.

Entwickler kann mit Innenleben arbeiten

Benutzer sieht ueres

Abbildung 4.1: Ein Steuerelement stellt ein inneres und ein ueres Gesicht zur Schau.

Der Unterschied zwischen einem Form und einem Steuerelement besteht darin, dass ein Steuerelement in einer anderen Komponente enthalten sein muss das ist meistens, aber nicht zwingend ein Form. Steuerelemente bilden den Schwerpunkt der Interaktion des Benutzers, denn normalerweise wird der Hintergrund einer Anwendung (der leere FormBereich) einfach ignoriert. Werfen wir einen kurzen Blick auf die mglichen Typen von Steuerelementen (wir untersuchen sie detaillierter an Tag 6):

113

Mens und Symbolleisten

Containersteuerelement e: Eine Reihe von Steuerelementen wurde exklusiv dafr entworfen, andere Steuerelemente aufzunehmen. Steuerelemente wie etwa das PanelSteuerelemente gruppieren Elemente nicht nur, um die Benutzeroberflche aufzurumen, sondern um dem Entwickler ihre Bearbeitung zu erleichtern. Fgen Sie etwa einem Panel weitere Steuerelemente hinzu, knnen Sie deren gemeinsame Eigenschaften mit einem einzigen Befehl ndern, statt fr jedes Steuerelement einen Befehl auszugeben. Schaltflchen-Steuerelemente: Sie haben bereits mit dem Steuerelement Button gearbeitet. Diese Kategorie umfasst weitere Steuerelemente wie etwa CheckBox und RadioButton, die zwar nicht wie Schaltflchen aussehen mgen, sich aber tatschlich so verhalten. Eine CheckBox etwa, also ein ankreuzbares Kstchen wie jene in Web-Formularen, lsst sich anklicken, verfgt ber eine zugeordnete Textmeldung und kann auf einen Klick hin eine Aktion ausfhren genau wie ein regulrer Button. Diese Steuerelemente gehren zu den gebruchlichsten in Windows Forms. Listen-Steuerelemente: Listen-Steuerelemente werden blicherweise verwendet, um Daten, meist aus Datenbanken, anzuzeigen. Zu dieser Kategorie gehren Steuerelemente wie die ComboBox und die ListBox. Mit ihnen zu arbeiten, ist nicht einfach, da man dabei mit Sammlungen von Daten arbeitet (im Gegensatz dazu ist es einfach, den Umgang mit ihnen zu erlernen!). Wir berspringen ihre nhere Betrachtung, bis Sie an Tag 9 lernen, wie Sie mit Daten und Datenbanken arbeiten. Men-Steuerelemente: Mens gehren neben Schaltflchen zu den gebruchlichsten Steuerelementen in einer Anwendung: von normalen Symbolleisten-Mens bis hin zu Kontextmens. Der Rest dieses Kapitels beschftigt sich mit der Funktionsweise dieser Steuerelemente. Weitere Steuerelemente: Zu den Steuerelementen, die nicht in die obigen Kategorien passen, gehren etwa das Label-Steuerelement, Zeit- und Datum-Steuerelemente sowie das Steuerelement TreeView (Strukturansicht). Das bedeutet nicht, dass sie weniger wichtig wren, im Gegenteil: Wie Sie bereits gesehen haben, ist das Label-Steuerelement eines der gebruchlichsten berhaupt.

Lassen Sie uns ohne weiteren Verzug lernen, wie man Mens erstellt!

4.2

Mens hinzufgen

Wir alle knnen uns etwas unter Mens und ihrer Verwendung vorstellen, doch lassen Sie uns ein wenig rekapitulieren. Ein Men ist eine Gruppe hnlicher Befehle, die sich in einer direkt unter der Titelleiste befindlichen Leiste befinden. Die Titelzeile des Hauptmens dient lediglich der Gruppierung der Kategorien und fhrt ansonsten keine Funktionen

114

Mens hinzufgen

aus. Die Unterelemente sind die eigentlichen Arbeitsbienen unter den Mens. Manchmal knnen sogar Unterelemente als Mens dienen, um so weitere Befehle zu Unterkategorien zusammenzufassen. Abbildung 4.2 zeigt ein typisches Men.

Abbildung 4.2: Das DATEI-Men des Internet Explorers ist typisch fr ein Befehlsmen.

In Abbildung 4.2 beachten Sie bitte die waagrechten Linien, die Mengruppen im DATEI-Men voneinander trennen. Diese Zeilen werden Separatoren oder Trennlinien genannt und sie helfen dem Benutzer bei der Navigation in den Anwendungsmens. Wie Sie Trennlinien in Ihre Mens integrieren, lernen Sie im heutigen Abschnitt Mens anpassen weiter unten. In .NET werden die Menkopfzeilen durch das Objekt MainMenu dargestellt, Unterelemente jedoch durch MenuItem-Steuerelemente. Diese hneln den Ihnen bereits bekannten Steuerelementen, also lassen Sie uns daher in etwas Code eintauchen. Listing 4.1 ist ihr erster Vorsto ins Men-Territorium. Listing 4.1: Beispiel fr Ihr erstes Men, geschrieben in VB .NET
1: 2: 3: 4: 5: 6: 7: Imports System Imports System.Windows.Forms Namespace TYWinForms.Day4 public class Listing41 : Inherits Form private mnuFile as new MainMenu

115

Mens und Symbolleisten

8: private lblMessage as new Label 9: 10: public sub New() 11: Me.Text = "Listing 4.1" 12: Me.Menu = mnuFile 13: 14: dim miFile as MenuItem = mnuFile.MenuItems.Add("&Datei") 15: miFile.MenuItems.Add(new MenuItem("&ffnen...")) 16: 17: lblMessage.Text = "Ich liebe WinForms" 18: lblMessage.Width = 200 19: lblMessage.Height = 200 20: 21: Me.Font = new System.Drawing.Font(new  System.Drawing.FontFamily("Arial"), 15) 22: Me.Controls.Add(lblMessage) 23: End Sub 24: 25: public Shared Sub Main() 26: Application.Run(new Listing41) 27: end sub 28: end class 29: 30: End Namespace

Ab Zeile 7 erzeugen wir eine Instanz unseres ersten neuen Objekts, dem MainMenu. Dadurch erhalten wir ein Gerst fr das Einfgen weiterer Mens. Dies und entsprechende Befehle folgen spter. In Zeile 12 machen Sie Bekanntschaft mit einer neuen Eigenschaft des Form-Objekts: Menu. Menu muss stets auf ein MainMenu-Objekt zeigen, insbesondere auf das MainMenu des Formulars. Auf diese Weise knnen Sie so viele Mens erzeugen, wie Sie wnschen, und dann dasjenige, das angezeigt wird, austauschen einfach indem Sie die Menu-Eigenschaft bearbeiten. In Zeile 14 sehen Sie die erste Instanz des MenuItem-Objekts. Es lsst sich sehr vielseitig verwenden; es kann jedes Menelement (MenuItem) darstellen, ganz gleich, ob es sich in der Menhierarchie auf oberster oder drittklassiger Ebene befindet. Sie verwenden die AddMethode des MainMenu-Objekts, um der MenuItems-Auflistung neue Menelemente hinzuzufgen. Dieses MenuItem wird die oberste Ebene oder Kopfzeile fr ein Men bilden. In diesem Fall haben wir es Datei genannt, ein sehr gebruchliches Men in WindowsAnwendungen.

116

Mens hinzufgen

Das kaufmnnische Und-Zeichen (&, auch Ampersand genannt) im Mennamen bedeutet, dass das Zeichen, das unmittelbar darauf folgt, durch die Tastatur zugnglich ist. Dies wird als Zugriffstaste. In Zeile 14 folgt dem Ampersand der Buchstabe D. So kann der Benutzer das DATEI-Men mit der Tastenkombination (Alt)+(D) ffnen. Es darf nur eine solche Tastenkombination pro Menelement geben. Auch nach dem Erstellen eines Menelements knnen Sie dessen Titelzeile noch mit Hilfe der Text-Eigenschaft ndern. In Zeile 15 fgen Sie dem in Zeile 14 erzeugten MenuItem-Objekt ein weiteres hinzu. Weil dieses MenuItem zu einem anderen MenuItem-Objekt statt zum MainMenu-Objekt gehrt, wird es als Menelement im Men statt in der Hauptmenleiste erscheinen. Das neue MenuItem heit ffnen. Abbildung 4.3 zeigt das Ergebnis dieses Codes.

Abbildung 4.3: Listing 4.1 erzeugt ein DATEI-Men mit einem Menelement namens FFNEN.

Wenn Sie auf das Menelement klicken, passiert nichts; das liegt daran, dass wir noch einen Handler fr dieses Menelement erstellen mssen. Dafr ndern wir einfach die Zeile 15 wie folgt:
miFile.MenuItems.Add(new MenuItem("&ffnen...", new EventHandler(AddressOf Me.Open_Clicked)))

oder in C# wie folgt:


miFile.MenuItems.Add(new MenuItem("&ffnen...", new EventHandler(this.ffnen_Clicked)));

Erstellen Sie dann die ffnen_Clicked-Methode. Fgen Sie den folgenden Code irgendwo zwischen dem Schluss der New-Methode und dem Anfang der Main-Methode ein:
public sub ffnen_Clicked(Sender as Object, e as EventArgs) Microsoft.VisualBasic.MsgBox("ffnen-Men angeklickt!") end sub

117

Mens und Symbolleisten

Statt die vertraute AddHandler-Methode zu verwenden, legen Sie einfach nur die Methode fest, die nach der Menauswahl ausgefhrt werden soll ein weiterer Parameter bei der Erstellung des Menelements (wenn Sie wollen, knnten Sie auch AddHandler verwenden). Abbildung 4.4 zeigt das genderte Ergebnis.

Abbildung 4.4: Das Hinzufgen eines Handlers verleiht den Menelementen Funktionsfhigkeit.

Lassen Sie uns einen Blick auf ein weiteres Beispiel werfen, diesmal fr die Erstellung von Untermens. Listing 4.2 zeigt die Anwendung in C#-Code. Listing 4.2: Ein erweitertes Men in C#
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: using System; using System.Windows.Forms; namespace TYWinForms.Day4 { public class Listing42 : Form { private MainMenu mnuDatei = new MainMenu(); private Label lblMessage = new Label(); public Listing42() { this.Text = "Listing 4.2"; this.Menu = mnuDatei; MenuItem miDatei = mnuDatei.MenuItems.Add("&Fenster"); miDatei.MenuItems.Add(new MenuItem("&Forms")); miDatei.MenuItems[0].MenuItems.Add(new MenuItem("&Mens")); miDatei.MenuItems[0].MenuItems[0].MenuItems.Add(new MenuItem("&Sind")); miDatei.MenuItems[0].MenuItems[0].MenuItems.Add(new MenuItem("So")); miDatei.MenuItems[0].MenuItems[0].MenuItems[1].MenuItems.Add(new MenuItem("&Einfach wie"));

118

Mens hinzufgen

20: 21: 22: 23: 24: 25: 26: 27: 28: 29:

miDatei.MenuItems[0].MenuItems[0].MenuItems[1].MenuItems.Add(new MenuItem("Kuchenbacken")); } public static void Main(){ Application.Run(new Listing42()); } } }

Bis zur Zeile 14 ist Listing 4.2 mit Listing 4.1 identisch. Ab hier wird es interessant. Die Zeilen 14 und 15 fgen dem MainMenu genau wie zuvor weitere MenuItem-Objekte hinzu. An diesem Punkt haben Sie zwei Hierarchieebenen: die Kopfzeile des Mens (die wir 'Item A' nennen wollen) und ein einzelnes Menelement, Fenster (das wir 'Item B' nennen wollen). Denken Sie daran, dass die MenuItems-Eigenschaft eine Auflistung ist: Sie knnen auf jedes Element in der Auflistung mit Hilfe einer Indexzahl verweisen (0 ist stets das erste Mitglied in der Auflistung). In Zeile 16 greifen Sie auf das erste Element in der MenuItems-Auflistung von Item B zu, welches wiederum ein weiteres MenuItem darstellt. Dieses MenuItem wollen wir 'Item C' nennen. Diesem fgen Sie genau wie zuvor ein weiteres MenuItem hinzu. Weil Sie das MenuItem 'Item C' hinzugefgt haben, wird es zu einem weiteren Untermen. Jetzt verfgen wir ber drei Menebenen: Fenster, Formulare und Mens. Um auf ein Mitglied in einer Auflistung in VB .NET zuzugreifen, bentigen Sie die Item-Eigenschaft:
miDatei.MenuItems.Item(0).MenuItems.Add(new etc etc)

In Zeile 17 erweitern Sie dieses Konzept ein wenig. Sie greifen auf die MenuItems-Auflistung von Item C zu und fgen ein neues MenuItem ein, so dass Sie eine vierte Menebene erhalten. Zeile 18 tut das Gleiche, indem sie der vierten Ebene ein weiteres MenuItem hinzufgt. Die Zeilen 19 und 20 fhren diesen Vorgang fort, indem sie dem zweiten Element (wie durch den Index 1 angezeigt) weitere Untermens hinzufgen. Als Ergebnis unserer Bemhungen erhalten wir fnf Menebenen, wie in Abbildung 4.5 zu sehen ist. Der Vorgang des Verschachtelns von Mens mag zunchst ein wenig verwirrend erscheinen, aber denken Sie einfach daran, dass das erste MenuItem, das Sie einem anderen MenuItem hinzufgen, stets ein Untermen erzeugt.

119

Mens und Symbolleisten

Abbildung 4.5: Das Verschachteln von Mens erzeugt einen interessanten Quellcode.

Sie knnen auch Untermens dadurch hinzufgen, dass Sie fr jedes Element im Men explizit ein neues MenuItem erzeugen. Schauen Sie sich zum Beispiel folgendes Codefragment an:
MenuItem MenuItem MenuItem MenuItem miDatei = mnuDatei.MenuItems.Add("&Windows"); miForms = miDatei.MenuItems.Add("&Forms"); miMens = miFormulare.MenuItems.Add("&Mens"); miSind = miMens.MenuItems.Add("Sind");

Die erste Zeile erzeugt ganz normal das Element fr die Kopfzeile des Mens. Das nchste Menelement wird als neues MenuItem erzeugt und der MenuItems-Auflistung der vorhergehenden Ebene hinzugefgt, genau wie Sie das mit der Menkopfzeile gemacht haben. Beachten Sie, dass jede darauffolgende Zeile das neue MenuItem in die MenuItems-Auflistung der vorhergehenden Zeile hinzufgt. Beide Methoden zur Menerzeugung erledigen diese Aufgabe effizient. Die zweite Methode weist jedoch eine Reihe von Vorteilen auf: Sie erzeugt fr jedes Menelement einen neuen Namen, so dass wir es notfalls spter leicht wiederfinden knnen (statt seine Indexzahl zu benutzen); und der Code ist ebenfalls leichter zu lesen. Letztlich bleibt die Wahl Ihnen berlassen.

Mens anpassen
Mens sind in hchstem Ma anpassungsfhig. Sie knnen Mens zusammenfassen, visuelle Erweiterungen und Kurzbefehle hinzufgen und so weiter. Da diese Verschnerungen dem Endbenutzer eine bessere Bedienungserfahrung vermitteln, wollen wir einen Blick darauf werfen. Die einfachste Methode zur Anpassung Ihrer Mens besteht darin, hnliche Elemente zusammenzufassen. Das in Abbildung 4.2 gezeigte DATEI-Men des Internet Explorers zeigt, wie sich hnliche Menelemente gruppieren lassen; eine Trennlinie (Separator)

120

Mens hinzufgen

separiert eine Serie von Menelementen von der nchsten. Das Einfgen einer Trennlinie ist problemlos und eine gute Mglichkeit, den Benutzern beim Navigieren durch die Mens zu helfen. Sehen Sie sich bitte folgendes Stck Code an:
MenuItem miDatei = mnuDatei.MenuItems.Add("&Datei"); MenuItem miffnen = miDatei.MenuItems.Add("&ffnen"); MenuItem miSpeichern = miDatei.MenuItems.Add("&Speichern"); miFile.MenuItems.Add(""); MenuItem miBeenden = miDatei.MenuItems.Add("Beenden");

Die ersten drei Zeilen fgen einem vorhandenen MainMenu-Objekt einfache Menelemente hinzu. Um eine Trennlinie einzufgen, mssen Sie lediglich die Add-Methode aufrufen und ihr einen Gedankenstrich () bergeben. Die CLR versteht: Sie wollen die folgenden Menelemente zu einer anderen Gruppe zusammenfassen und abtrennen. Diese einfache Erweiterung fhrt zu dem folgenden Men in Abbildung 4.6.

Abbildung 4.6: Trennlinien helfen beim Organisieren Ihrer Mens.

Zustzlich zu den Zugriffstaten in den Menkopfzeilen knnen Sie auch Tastenkombinationen fr Ihre Menelemente einbauen. Microsoft Word verwendet beispielsweise (Strg) + (S) als Tastenkombination, um Dokumente zu speichern. Die meisten Anwendungen verwenden die Funktionstaste (F1) als Shortcut zu einem Element des HILFE-Mens. (Strg) + (O) fhrt den Menbefehl DATEI/FFNEN aus.
MenuItem miffnen = miDatei.MenuItems.Add("&ffnen", this.ffnen_Clicked, Shortcut.CtrlO);

Der dritte Parameter, den Sie hier sehen, ist die Eigenschaft Shortcut. Sie knnen die Shortcut-Aufzhlung verwenden, um die gewnschte Tastenkombination festzulegen. Diese Aufzhlung verfgt ber Werte fr die meisten Tastenkombinationen, wie etwa Ctrlx wobei x eine beliebige Zahl oder ein beliebiges Zeichen sein kann; in Fx steht x fr jede beliebige Zahl, und in CtrlShiftx ist x eine beliebige Zahl oder ein beliebiges Zeichen. In der .NET-Dokumentation erhalten Sie vollstndige Informationen dazu.

121

Mens und Symbolleisten

Wenn Sie whrend der Erstellung eines Menelements keine Tastenkombination festlegen wollen, knnen Sie das auch noch spter tun, indem Sie die Eigenschaft Shortcut des MenuItems nutzen:
miDatei.Shortcut = Shortcut.ShiftF9

Setzen Sie die ShowShortcut-Eigenschaft auf true, wenn Sie die damit verknpfte Tastenkombination im Men neben dem Menelement anzeigen lassen wollen:
MiDatei.Shortcut = ShowShortcut = true

Schlielich gibt es noch ein paar einfache boolesche Eigenschaften, die Sie fr jedes Menelement anpassen knnen:

Enabled legt fest, ob der Benutzer das Menelement auswhlen kann oder nicht (deaktivierte Elemente erscheinen grau). Visible zeigt an, ob der Benutzer das Element sehen kann (bedenken Sie, dass ein

Menelement selbst dann noch per Tastenkombination aktiviert werden kann, wenn es nicht sichtbar ist; sorgen Sie dafr, dass sowohl Enabled als auch Visible auf false gesetzt sind, wenn Sie verhindern wollen, dass Benutzer ein Menelement sehen oder auswhlen).

Checked zeigt an, ob sich neben dem Menelement ein Hkchen befindet. Das ist

ntzlich, wenn Sie beispielsweise kenntlich machen wollen, ob ein Menelement ausgewhlt worden ist.

DefaultItem zeigt an, ob das Menelement die voreingestellte Auswahl ist (Standardelemente erscheinen in fetter Schrift).

Um weitere Informationen ber MenuItem-Eigenschaften zu erhalten, schlagen Sie bitte in Anhang B nach.

Kontextmens
Wenn Sie bereits einmal auf ein Formular mit der rechten Maustaste (ein Rechtsklick) geklickt haben, wissen Sie, dass dann nichts passiert: kein Popup-Men ffnet sich, wie so oft in Windows-Anwendungen. Dieses Popup-Men nach dem Rechtsklick, welches hufig kontext-relevante Mens zur Auswahl bereitstellt (etwa Ausschneiden, Einfgen usw.), bezeichnet man als Kontextmen. Wenn Sie bereits einmal etwas lnger mit WindowsAnwendungen gearbeitet haben, wissen Sie, dass solch ein Men fr produktives Arbeiten einfach unerlsslich ist. Zum Glck lassen sich Kontextmens ebenso einfach einem Formular hinzufgen wie normale Mens. Alle Kontextmens werden durch das ContextMenu-Objekt dargestellt. Es ist dem MainMenuObjekt sehr hnlich. Menelemente im ContextMenu werden sogar als MenuItem-Objekte dargestellt. Folglich knnen Sie alles, was Sie bisher ber Mens gelernt haben, auch auf Kontextmens anwenden.

122

Mens hinzufgen

Am einfachsten lsst sich ein Kontextmen als Kopie des regulren Mens anfertigen, das Sie bereits erzeugt haben. Dies knnen Sie mit der CloneMenu-Methode bewerkstelligen. Der folgende Code erzeugt ein neues ContextMenu-Objekt und fgt die Kopie eines vorhandenen Mens hinzu:
ContextMenu mnuContext = new ContextMenu(); Form1.ContextMenu = mnuContext; MenuItem miContext = miDatei.CloneMenu(); mnuContext.MenuItems.Add(miContext);

Sobald Sie Ihrem Formular diesen Code hinzugefgt haben, wird ein Rechtsklick ein Popup-Men hervorbringen, das genau Ihrem DATEI-Men entspricht. Dieses Beispiel ist das allereinfachste Kontextmen. Der Grund jedoch, warum es Kontext-Men genannt wird, liegt darin, dass die Anwendung feststellen kann, in welchem Teil des Formulars sich der Mauszeiger beim Rechtsklicken befand, und kann so fr diese Stelle ein spezialisiertes Men bereitstellen. So kann etwa ein Rechtsklick auf eine leere Flche im Formular das DATEI-Men aufrufen, und ein Rechtsklick auf eine TextBox knnte ein spezielles Kontextmen mit den Befehlsoptionen AUSSCHNEIDEN und EINFGEN darin aufrufen. Um dieses Funktionsmerkmal bereitzustellen, verfgt jedes Windows Forms-Steuerelement (inklusive des Form-Objekts) ber die Eigenschaft ContextMenu, die das Men angibt, welches geffnet wird, wenn das Steuerelement rechts angeklickt wird. Dies macht es Ihnen recht leicht, Ihre Kontextmens anzupassen. Lassen Sie uns mit Listing 4.3 ein Beispiel dafr ansehen. Listing 4.3: Angepasste Kontextmens
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: using System; using System.Windows.Forms; using System.Drawing; namespace TYWinForms.Day4 { public class Listing43 : Form { private MainMenu mnuFile = new MainMenu(); private ContextMenu ctmTextBox = new ContextMenu(); private ContextMenu ctmLabel = new ContextMenu(); private Label lblMessage = new Label(); private TextBox tbMessage = new TextBox(); public Listing43() { this.Text = "Listing 4.3 "; this.Menu = mnuFile; AddMenus();

123

Mens und Symbolleisten

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:

lblMessage.Text = "Ich liebe Windows Forms "; lblMessage.Location = new Point(100,75); lblMessage.Width = 150; tbMessage.Text = "Ich liebe Windows Forms "; tbMessage.Location = new Point(75,125); tbMessage.Width = 150; this.Controls.Add(lblMessage); this.Controls.Add(tbMessage); } public void AddMenus() { MenuItem miDatei = mnuDatei.MenuItems.Add("&Datei"); MenuItem miffnen = miDatei.MenuItems.Add("&ffnen"); MenuItemSpeichern = miDatei.MenuItems.Add("&Speichern"); miDatei.MenuItems.Add("-"); MenuItem miBeenden = miDatei.MenuItems.Add("Beenden"); MenuItem ctiLabel = ctmLabel.MenuItems.Add("Label-Men"); lblMessage.ContextMenu = ctmLabel; MenuItem ctiTextBox = ctmTextBox.MenuItems.Add("Textbox-Men"); tbMessage.ContextMenu =ctmTextBox; } public static void Main() { Application.Run(new Listing43()); } } }

In den Zeilen 1 bis 30 findet sich nichts Neues. Beachten Sie jedoch, dass die Zeilen 9 und 10 Instanzen von ContextMenu-Objekten erzeugen; wir werden sie gleich verwenden. Die Zeile 18 ruft die AddMenus-Methode auf, die in Zeile 32 beginnt. Die Zeilen 33 bis 37 erzeugen ein normales DATEI-Men fr Ihre Anwendung (Sie haben diesen Code bereits in Listing 4.2 gesehen). Zeile 39 erzeugt ein neues MenuItem fr das ContextMenu-Objekt ctmLabel. Seine Beschriftung lautet Label-Men. In Zeile 40 verbinden Sie dieses ContextMenu-Objekt (und somit sein untergeordnetes MenuItem-Objekt) mit dem Label-Steuerelement. Dieses Kontextmen lsst sich nun auf dieses Label anwenden (wenn Sie wollen, knnten Sie dieses Kontextmen auch anderen Steuerelementen zuweisen).

124

Symbolleisten, Statusleisten und Bildlaufleisten hinzufgen

Die Zeilen 42 und 43 erledigen das Gleiche fr das TextBox-Objekt, indem sie ein Menelement erzeugen, das die Beschriftung Textbox-Men trgt. Da Sie an keiner Stelle auch dem Form-Objekt ein ContextMenu zuweisen, wird ein Rechtsklick auf eine leere Formularflche kein Kontextmen ffnen. Dieses Listing erzeugt das in Abbildung 4.7 gezeigte Ergebnis.

Abbildung 4.7: Wenn Sie mit der rechten Maustaste auf das Bezeichnungsfeld (Label) klicken, wird das damit verbundene Kontextmen angezeigt.

In diesem Beispiel zeigt ein Rechtsklick auf das Textfeld das Kontextmen TextboxMen an. Wenn Sie einmal die Zeilen 42 und 43 aus dem Listing auskommentieren und es neu kompilieren, zeigt ein Rechtsklick auf das Textfeld ein ganz anderes damit verknpftes Kontextmen an: eines, das Befehlsoptionen fr Ausschneiden und Einfgen anzeigt. Der Grund dafr ist, dass TextBox (und ebenso RichTextBox) bereits voreingestellte Kontextmens besitzen. Indem Sie Ihr eigenes Kontextmen erzeugen, setzen Sie die vorhandenen Mens auer Kraft.

4.3

Symbolleisten, Statusleisten und Bildlaufleisten hinzufgen

Symbolleisten, Statusleisten und Bildlaufleisten sind ebenso vertraute Bestandteile jeder Windows-Anwendung wie Mens. Eine Symbolleiste befindet sich gewhnlich am oberen Ende eines Anwendungsfensters, also direkt unter der Menleiste, und enthlt Schaltflchen mit Grafiken darauf, die die jeweilige Funktionalitt in der Anwendung signalisieren. Word etwa hat eine Schaltflche mit dem Bild eines geffneten Ordners, um das ffnen einer Datei zu bezeichnen, und ein Diskettensymbol fr die Speichern-Funktion. Eine Statusleiste befindet sich am unteren Rand des Anwendungsfensters und stellt dem Benutzer Verarbeitungsinformationen zur Verfgung. Ihre einzige interaktive Funktion besteht darin, Meldungen anzuzeigen. Bildlaufleisten schlielich erlauben dem Benutzer, durch ein Formular zu blttern, falls nicht alle Steuerelemente in die vorhandene Fensterflche passen. Abbildung 4.8 stellt alle drei Leistentypen dar.

125

Mens und Symbolleisten

Abbildung 4.8: In den meisten Anwendungen finden sich Symbolleisten, Statusleisten und Bildlaufleisten.

Trotz ihrer Namenshnlichkeit erweisen sich die drei Steuerelemente bei der Implementierung als recht unterschiedlich. Die Symbolleiste agiert als selbststndiges Steuerelement (obwohl es weitere Steuerelemente enthlt) und ist mit einem ImageList-Objekt verknpft. Die Statusleiste hingegen agiert hnlich einem Formular als Container fr andere Steuerelemente. Die Bildlaufleiste ist wieder eine andere Angelegenheit und verfgt nur ber wenige anpassbare Eigenschaften. Wir betrachten diese Steuerelemente der Reihe nach.

Symbolleisten
Eine Symbolleiste besteht aus einer Sammlung von Schaltflchen (normalerweise durch Grafiken dargestellt), die Kurzbefehle (Shortcuts) zu den Funktionen Ihrer Anwendung anbieten. Wahrscheinlich arbeiten Sie bereits seit lngerem mit Symbolleisten, so dass wir uns hier nicht bei konzeptionellen Details aufhalten. Die Symbolleiste und ihre Schaltflche werden durch die Objekte ToolBar und ToolBarButton dargestellt. Diese beiden Objekte hneln den Objekten MainMenu und MenuItem: Sie erstellen das zweite Objekt und fgen es dem ersten Objekt hinzu, die Schaltflchen also der Leiste. Lassen Sie uns einen Blick auf Listing 4.4 werfen, das Code zeigt, welcher eine einfache Symbolleiste erzeugt.

126

Symbolleisten, Statusleisten und Bildlaufleisten hinzufgen

Listing 4.4: Eine einfache Symbolleiste hinzufgen


1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: using System; using System.Windows.Forms; namespace TYWinForms.Day4 { public class Listing44 : Form { private ToolBar tlbStandard = new ToolBar(); public Listing44() { ToolBarButton tbbOpen = new ToolBarButton(); tbbOpen.Text = "ffnen "; tlbStandard.Buttons.Add(tbbOpen); this.Controls.Add(tlbStandard); } public static void Main() { Application.Run(new Listing44()); } } }

In Zeile 7 erzeugen Sie ein neues ToolBar-Objekt auf die gleiche Weise wie ein MainMenu-Objekt. In Zeile 15 fgen Sie es Ihrem Formular wie jedes andere Steuerelement hinzu. In Zeile 10 erstellen Sie ein neues ToolBarButton-Steuerelement: Darauf wird der Benutzer spter klicken knnen. In Zeile 11 setzen Sie die Text-Eigenschaft so, dass das Wort ffnen angezeigt wird (Sie werden gleich merken, dass diese Text-Eigenschaft nicht ganz das ist, wofr Sie sie halten). Schlielich fgen Sie das ToolBarButton-Steuerelement Ihrem ToolBarObjekt hinzu. Ohne diesen Schritt wre Ihre Symbolleiste leer und wrde in sich zusammenfallen. Kompilieren Sie diese Anwendung und fhren Sie sie aus. Das Ergebnis sollte wie in Abbildung 4.9 aussehen. Wir haben zwar jetzt eine Symbolleistenschaltflche, aber einige Dinge scheinen zu fehlen oder nicht zu stimmen. Augenfllig ist, dass sich ihr Text nicht in der Mitte befindet. Zweitens ist mit der Schaltflche keine Grafik verknpft. Und schlielich passiert nach einem Klick auf die Schaltflche berhaupt nichts. Alle diese Mngel lassen sich leicht beheben.

127

Mens und Symbolleisten

Abbildung 4.9: Listing 4.4 erzeugt eine einfache Symbolleiste mit nur einer Schaltflche.

Die Text-Eigenschaft sieht nicht nach dem aus, was wir gewohnt sind. Die Text-Eigenschaft auf einem Button-Steuerelement zeigt eine Beschriftung im Zentrum der Schaltflche. Die Text-Eigenschaft fr den ToolBarButton hingegen wird verwendet, um die Grafik auf der Schaltflche zu ergnzen (denken Sie mal an die ZURCK-Schaltflche im Internet Explorer). Der Grund fr das Erscheinen des Textes auerhalb des Zentrums ist also, dass er Platz lsst fr die Grafik. Wenn wir der Schaltflche eine Grafik zuweisen wollen, mssen wir etwas ber ein neues Objekt namens ImageList lernen. Es wird meist von anderen Steuerelementen wie etwa dem ToolBar-Steuerelement dazu verwendet, Grafiken mit einer Auflistung (wie etwa ToolBarButtons) zu verknpfen. ImageList verfgt ber Methoden, um die Grafiken, die es enthlt, hinzuzufgen, zu entfernen und neu zu sortieren. Nachdem Sie einer ImageList Grafiken hinzugefgt haben, werden die Schaltflchen in einer Symbolleiste auf die Grafiken mit Hilfe ihrer jeweiligen ImageIndex-Eigenschaft verweisen. Etwa so:
MyToolBar.ImageList = myImageList; MyToolBarButton.ImageIndex = 1;

Wir wollen eine neue Methode namens AddToolBar verwenden, um die Erstellung unserer Symbolleiste zu bewerkstelligen. Ersetzen Sie die Zeilen 10 bis 13 in Listing 4.4 durch einen Aufruf der Methode
AddToolbar();

und fgen Sie dann Ihrer Anwendung den Code, der in Listing 4.5 gezeigt wird, zwischen dem Konstruktor und der Main-Methode hinzu. Listing 4.5: Grafiken mit Symbolleistenschaltflchen verknpfen
1: 2: 3: private ImageList ilstToolbar =new ImageList(); public void AddToolbar(){

128

Symbolleisten, Statusleisten und Bildlaufleisten hinzufgen

4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24:

ilstToolbar.Images.Add(Image.FromFile("i_open.bmp ")); ilstToolbar.Images.Add(Image.FromFile("i_maximize.bmp ")); ilstToolbar.Images.Add(Image.FromFile("i_happy.bmp ")); ToolBarButton tbbOpen =new ToolBarButton(); tbbOpen.ImageIndex =0; tbbOpen.Text ="ffnen "; ToolBarButton tbbMaximize =new ToolBarButton(); tbbMaximize.ImageIndex =1; tbbMaximize.Text ="Maximieren "; ToolBarButton tbbHappy =new ToolBarButton(); tbbHappy.ImageIndex =2; tbbHappy.Text ="Werden Sie glcklich!"; tlbStandard.ImageList =ilstToolbar; tlbStandard.Buttons.Add(tbbOpen); tlbStandard.Buttons.Add(tbbMaximize); tlbStandard.Buttons.Add(tbbHappy); }

Zeile 1 erzeugt ein neues ImageList-Objekt. In den Zeilen 4 bis 6 fgen Sie diesem Objekt Grafiken hinzu, indem Sie die statische FromFile-Methode des Image-Objekts verwenden. (Beachten Sie, dass Sie die Namen der Dateien, die Sie oben sehen, zu solchen ndern sollten, die Sie tatschlich besitzen!) Diese Methode ldt die angegebene Datei und bergibt sie der Images-Auflistung von ImageList. Da Sie in Listing 4.5 das Image-Objekt verwenden, sollten Sie nicht vergessen, auch den Namensraum System.Drawing zu importieren! Sie knnten etwa folgenden Code am Anfang Ihrer Datei einfgen:
using System.Drawing;

Die Zeilen 8 bis 18 erzeugen drei neue ToolBarButton-Objekte, wobei die Eigenschaften fr Text und ImageIndex angegeben werden. Der ImageIndex zeigt auf den Index fr diejenige Grafik in der ImageList, die Sie mit der Schaltflche verknpfen wollen (der erste Index lautet stets 0). In Zeile 20 verknpfen Sie schlielich die in Zeile 1 erzeugte ImageList mit dem ToolBarObjekt. Dabei verwenden Sie die ImageList-Eigenschaft des Objekts. Die Zeilen 21 bis 23 fgen einfach die neu erstellten Schaltflchen der Symbolleiste hinzu. Abbildung 4.10 zeigt das Ergebnis dieser Datei.

129

Mens und Symbolleisten

Abbildung 4.10: Jetzt verfgt die Symbolleiste ber Schaltflchen mit Symbolen und Beschriftungen.

Die Anwendung sieht schon ein wenig besser gefllt aus. Aber es gibt noch einiges zu tun: Die Schaltflchen sollten auch eine Funktion erfllen. Erinnern Sie sich an meine Worte, wonach die ToolBar als ein einzelnes Steuerelement agiert? Gleich werden Sie den Grund dafr erkennen.
ToolBarButton-Objekte verfgen ber keine Ereignisse, die man behandeln kann (von dem Disposed-Ereignis einmal abgesehen, doch dies wird von einem anderen Objekt geerbt).

Von alleine wissen sie also nicht, dass sie angeklickt wurden. Wenn man also die Aktionen steuern mchte, sobald ein Benutzer auf eine Symbolleistenschaltflche geklickt hat, muss man sich dem ToolBar-Objekt zuwenden. Das ToolBar-Steuerelement verfgt ber ein ButtonClick-Ereignis, das ausgelst wird, sobald eine der in ihm enthaltenen ToolBarButtons angeklickt wird. Daher mssen alle Ereignisse und Aktionen ber die ToolBar erfolgen. Um Ihrer ToolBar einen Handler hinzuzufgen, verwenden Sie folgenden Code:
tlbStandard.ButtonClick += new ToolBarButtonClickEventhandler (this.MyToolBarHandler);

Das Listing 4.6 zeigt den MyToolBarHandler. Listing 4.6: Die Behandlung von Symbolleisten-Ereignissen in C#
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: private void MyToolBarHandler(Object Sender, ToolBarButtonClickEventArgs e){ switch(tlbStandard.Buttons.IndexOf(e.Button)){ case 0: //hier folgt etwas Code; break; case 1: // hier folgt etwas Code; break; case 2: // hier folgt etwas Code;

130

Symbolleisten, Statusleisten und Bildlaufleisten hinzufgen

11: 12: 13:

break; } }

Das Parameterobjekt ToolBarButtonClickEventArgs verfgt ber eine Button-Eigenschaft, wie in Zeile 2 zu sehen ist. Sie gibt an, welche Schaltflche angeklickt wurde. Wir verwenden die IndexOf-Eigenschaft der Buttons-Auflistung, um fr jede Schaltflche eine numerische Darstellung zurckzugeben, die wir fr die Entscheidung darber verwenden knnen, welcher Code auszufhren ist. Das switch-Statement (in VB .NET wird statt dessen Select Case verwendet) wertet die ihm gelieferte Bedingung gegenber jedem einzelnen case-Element aus. Wenn die Werte bereinstimmen, wird der Code im betreffenden case-Zweig ausgefhrt. IndexOf legt fest, welches Element in der Buttons-Auflistung der ToolBar mit dem im Parameterobjekt angegebenen bereinstimmt und gibt dann den Index des entsprechenden Steuerelements in der Auflistung zurck. Break ist ntig, um der CLR mitzuteilen, nach der Ausfhrung des case-Statements keine weiteren mehr auszufhren oder auszuwerten. Listing 4.7 zeigt den gleichen Code in VB .NET geschrieben. Listing 4.7: Die Behandlung von Symbolleisten-Ereignissen in VB .NET
1: private Sub MyToolBarHandler(Sender as Object,e as ToolBarButtonClickEventArgs) 2: Select Case tlbStandard.Buttons.IndexOf(e.Button) 3: Case 0 4: 'etwas Code ausfhren 5: Case 1 6: 'etwas Code ausfhren 7: Case 2 8: 'etwas Code ausfhren 9: end Select 10: End Sub

Symbolleisten anpassen
ToolBar- und ToolBarButton-Steuerelemente verfgen ber einige Eigenschaften, die Ihnen erlauben, deren Aussehen anzupassen. Wir werfen kurz einen Blick darauf, angefangen bei den Eigenschaften des ToolBar-Steuerelements.

Die Eigenschaft Appearance gibt an, ob Ihre Symbolleistenschaltflchen als normale dreidimensionale Objekte (erhabene Schaltflchen) oder flach (Browser-Stil) erscheinen sollen. Den Unterschied versteht man am besten, indem man sich beide Stile ansieht sie sind beide in Abbildung 4.11 zu sehen.

131

Mens und Symbolleisten

Abbildung 4.11: Normale Symbolleistenschaltflchen sehen dreidimensional aus; flache Schaltflchen hingegen sind eben das: flach. Appearance lsst sich auf zwei Werte setzen: Normal (3D) oder Flat. Beide Werte gehren zur Aufzhlung ToolBarAppearance. Das folgende Codefragment lsst die Schaltflche flach aussehen: tlb.Standard.Appearance = ToolBarAppearance.Flat;

Funktional gesehen besteht kein Unterschied zwischen den beiden Stilen; es ist lediglich eine Angelegenheit der visuellen Vorliebe. Die AutoSize-Eigenschaft des ToolBar-Steuerelements gibt an, ob sich die ToolBar je nach der Gre der ToolBarButtons, die sie enthlt, vergrern und verkleinern soll. Dementsprechend gibt es auch eine ButtonSize-Eigenschaft, die die Gre der Schaltflchen in der ToolBar vorgibt. Die Standardgre fr eine Schaltflche betrgt 24 (Breite) x 22 (Hhe) Pixel. Doch wenn ButtonSize nicht anderweitig angegeben ist, passen sich die Schaltflchen der grten Grafik und dem mit ihnen verknpften Text an. Um die Begrenzung Ihrer Symbolleiste zu steuern, stehen Ihnen die Eigenschaften Divider und BorderStyle zur Verfgung. Divider gibt an, ob eine Trennlinie zwischen der Symbolleiste und anderen Steuerelementen, wie etwa einem Men, eingezogen werden soll. BorderStyle kann BorderStyle.FixedSingle, BorderStyle.Fixed3D oder BorderStyle.None sein. Diese Eigenschaft funktioniert hier genauso wie fr das Form-Objekt. Das Steuerelement ToolBarButton verfgt nur ber eine Haupteigenschaft, nmlich Style, und ber verschiedene weitere, die diese Eigenschaft untersttzen. Style gibt an, welche Art von Schaltflche dem Benutzer gezeigt wird. Sie kann einen der Werte in der Aufzhlung ToolBarButtonStyle annehmen:

132

Symbolleisten, Statusleisten und Bildlaufleisten hinzufgen

DropDownButton: Zeigt auf Mausklick ein Dropdown-Men an. Verwenden Sie die Eigenschaft DropDownMenu, um ein anzuzeigendes MenuItem zuzuweisen. Verwenden Sie die DropDownArrows-Eigenschaft des ToolBar-Objekts, um anzugeben, ob ein Pfeil anzeigt wird, der auf Anklicken hin ein Men anzeigt. PushButton: Die standardmige 3D-Schaltflche, wie sie vom jeweiligen Betriebssystem definiert wird. (Die Schaltflche kann in manchen Windows-Versionen flach statt dreidimensional erscheinen.) Separator: Eine Trennlinie zwischen zwei Schaltflchen. ToggleButton: Eine Schaltflche, die, sobald sie angeklickt wurde, wie eingedrckt aussieht und so bleibt. Verwenden Sie die Pushed-Eigenschaft, um anzugeben, ob die Schaltflche gerade gedrckt ist. PartialPush hnelt Pushed, nur dass die Schaltflche

nicht gedrckt, sondern vielmehr grau eingefrbt ist. Abbildung 4.12 zeigt Beispiele fr all diese Stile.

Abbildung 4.12: Hier sind die Schaltflchenstile DropDown, PushButton und ToggleButton umgesetzt.

Statusleisten
In einer Windows Forms-Anwendung wird eine Statusleiste durch das StatusBar-Objekt und eine beliebige Anzahl von StatusBarPanels (Statusleistenbereiche) dargestellt. Die StatusBar sieht fast wie ein Label-Steuerelement aus: Sie verfgt ber keine Umrisslinien oder Trennlinien. Daher verwendet sie StatusBarPanels, um Informationen abzutrennen und anzuzeigen. Es gibt keine Regel, die besagt, man habe einer StatusBar irgendwelche StatusBarPanels hinzuzufgen schlielich besitzt die StatusBar eine Text-Eigenschaft, die dem Benutzer Text anzeigen wird das Standarderscheinungsbild einer Statusleiste sieht allerdings solche Bereiche vor. Die Steuerelemente StatusBar und StatusBarPanel sind den Men- und SymbolleistenSteuerelementen sehr hnlich, die Sie heute kennen gelernt haben. Daher drften Sie sie leicht verstehen knnen. Wir wollen wieder in etwas Code eintauchen: Listing 4.8 (fr dieses Beispiel verwenden wir VB .NET) zeigt, wie eine Statusleiste hinzugefgt wird.

133

Mens und Symbolleisten

Listing 4.8: Ihrer Anwendung eine in VB .NET geschriebene Statusleiste hinzufgen


1: Imports System 2: Imports System.Windows.Forms 3: 4: Namespace TYWinForms.Day4 5: 6: public class Listing48 : Inherits Form 7: private sbrMain as new StatusBar 8: private lblMessage as new Label 9: 10: public sub New() 11: Me.Text = "Listing 4.8 " 12: 13: AddStatusBar 14: 15: lblMessage.Text = "Ich liebe WinForms" 16: lblMessage.Width = 200 17: lblMessage.Height = 200 18: lblMessage.Font = new System.Drawing.Font(new  System.Drawing.FontFamily("Arial "), 15) 19: 20: Me.Controls.Add(lblMessage) 21: Me.Controls.Add(sbrMain) 22: 23: End Sub 24: 25: public sub AddStatusBar() 26: dim sbpName as StatusBarPanel = sbrMain.Panels.Add("Hallo, Dave!") 27: sbpName.Width = 100 28: sbpName.BorderStyle = StatusBarPanelBorderStyle.Raised 29: 30: dim sbpTime as StatusBarPanel = sbrMain.Panels.Add(" ") 31: sbpTime.Text = Datetime.Now.ToString 32: sbpTime.AutoSize = StatusBarPanelAutoSize.Spring 33: sbpTime.Alignment = HorizontalAlignment.Right 34: 35: sbrMain.ShowPanels = true 36: end sub 37: 38: public Shared Sub Main() 39: Application.Run(new Listing48) 40: end sub 41: end class 42: 43: End Namespace

134

Symbolleisten, Statusleisten und Bildlaufleisten hinzufgen

Das Listing demonstriert eine Reihe von Eigenschaften der Steuerelemente StatusBar und StatusBarPanel. In Zeile 7 erzeugen Sie das erste Objekt: die StatusBar. Um den Code irgendwie logisch zu sortieren, haben wir den Initialisierungscode fr die Statusleiste in einer Methode namens AddStatusBar untergebracht, die in Zeile 13 aufgerufen wird. Zeile 21 fgt die Statusleiste dem Formular hinzu. Die erste Zeile der AddStatusBar-Methode (Zeile 26) erzeugt ein neues StatusBarPanelObjekt und fgt es der Panels-Auflistung der StatusBar hinzu. Zeile 27 legt die Breite des Bereichs fest und Zeile 28 setzt den BorderStyle auf Raised (einen Wert in der Aufzhlung StatusBarPanelBorderStyle; die anderen Werte, die Sie verwenden knnen, sind None oder Sunken, welches der Standardstil ist). In Zeile 30 wird ein weiterer StatusBarPanel-Bereich erzeugt. Die Text-Eigenschaft ist zunchst auf eine leere Zeichenfolge eingestellt. In Zeile 21 setzen Sie sie mit Hilfe der DateTime.Now-Funktion auf die aktuelle Uhrzeit (verwenden Sie ToString, um die Zeit als Zeichenfolge zurckzugeben der Standardrckgabetyp ist ein DateTime-Objekt). Zeile 32 setzt die AutoSize-Eigenschaft, welche angibt, wie sich der Bereich vergrern bzw. verkleinern lsst, je nach seinem Inhalt und/oder der Gre des Formulars. Spring wirkt wie eine Feder, so dass der Bereich den gesamten verfgbaren Raum einnimmt, der noch nicht von anderen Bereichen belegt ist. AutoSize kann auch auf None oder Contents gesetzt sein, wodurch die Gre des Bereichs an seinen Inhalt angepasst wird. In Zeile 33 wird die Alignment-Eigenschaft verwendet, um den Inhalt des Bereichs rechtsbndig auszurichten. Dieser Wert kann Left, Center oder Right sein die Werte stammen aus der Aufzhlung HorizontalAlignment. Zum Schluss setzen Sie in Zeile 35 die ShowPanels-Eigenschaft der StatusBar auf true, damit die von Ihnen erzeugten Bereiche dem Benutzer angezeigt werden. Abbildung 4.13 zeigt, wie diese Anwendung nun aussieht. Sie knnen Ihrer Statusleiste so viele Bereiche hinzufgen, wie Sie wnschen. Nehmen Sie aber nicht zu viele, sonst kann sie der Benutzer nicht alle sehen. Das Steuerelement StatusBarPanel hat eine weitere wichtige Eigenschaft hinsichtlich seiner Anpassung: Icon. Die Eigenschaft lsst sich dazu nutzen, eine Grafik statt blo Text in der Statusleiste anzuzeigen (wie zum Beispiel die Statusleiste von Word mit einem Symbol den Status Ihres Dokuments anzeigt). Diese Grafikdatei muss eine .ico-Datei sein. Durch das Hinzufgen des folgenden Codes zur AddStatusBar-Methode in Listing 4.8 erhlt man einen neuen Bereich:
dim sbpIcon as StausBarPanel = sbrMain.Panels.Add("") sbpIcon.AutoSize = StatusBarPanelAutoSize.Contents sbpIcon.Icon = new Icon("VSProjectApplication.ico")

135

Mens und Symbolleisten

Abbildung 4.13: Ihre erste Statusleiste enthlt eine Meldung sowie die Uhrzeit und das Datum.
Event-driven Start application

Enter message loop

Do processing

User input

If user input is stop then

Stop

Abbildung 4.14: Die Nachrichtenschleife wartet auf eine Benutzereingabe.

Bildlaufleisten
Bildlaufleisten erlauben es Ihnen (und Ihren Benutzern), sich im Formular zu bewegen, wenn die darin enthaltenen Steuerelemente zu gro sind, um in das aktuelle Fenster zu passen. Es gibt zwei Arten von Bildlaufleisten: horizontale (verschieben das Formular zwischen linkem und rechtem Rand), dargestellt durch das HScrollBar-Steuerelement, und vertikale (bewegen das Formular auf und ab, dargestellt durch VScrollBar. Bildlaufleisten lassen sich Ihrem Formular sehr leicht hinzufgen, aber es ist ein wenig schwieriger, sie zum Funktionieren zu bringen. Um zu erfahren, wie Sie Bildlaufleisten verwenden, sehen Sie sich bitte den Code in Listing 4.9 an (eine modifizierte Version von Listing 3.4).

136

Symbolleisten, Statusleisten und Bildlaufleisten hinzufgen

Listing 4.9: Bildlaufleisten implementieren


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: 42: 43: 44: using System; using System.Windows.Forms; using System.Drawing; namespace TYWinForms.Day4 { public class Listing49 : Form { Button btAccept = new Button(); Button btCancel = new Button(); Label lblMessage = new Label(); VScrollBar vbarForm = new VScrollBar(); HScrollBar hbarForm = new HScrollBar(); public Listing49() { lblMessage.Location = new Point(75,150); lblMessage.Width = 200; btAccept.Location = new Point(100,25); btAccept.Text = "OK "; btAccept.Click += new EventHandler(this.AcceptIt); btCancel.Location = new Point(100,100); btCancel.Text = "Abbrechen"; btCancel.Click += new EventHandler(this.CancelIt); vbarForm.Dock = DockStyle.Right; vbarForm.Visible = false; hbarForm.Dock = DockStyle.Bottom; hbarForm.Visible = false; this.Resize += new EventHandler(this.Resized); this.AcceptButton = btAccept; this.CancelButton = btCancel; this.Text = "Beispiel fr OK- und Abbrechen-Schaltflchen"; this.Height = 200; this.Controls.Add(lblMessage); this.Controls.Add(btAccept); this.Controls.Add(btCancel); this.Controls.Add(vbarForm); this.Controls.Add(hbarForm); } public void Resized(Object Sender, EventArgs e) { if (this.Height < lblMessage.Top + lblMessage.Height) {

137

Mens und Symbolleisten

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:

vbarForm.Visible = true; }else { vbarForm.Visible = false; } if (this.Width < btAccept.Left + btAccept.Width) { hbarForm.Visible = true; }else { hbarForm.Visible = false; } this.Refresh(); } public void AcceptIt(Object Sender, EventArgs e) { lblMessage.Text = "Der OK-Button wurde gedrckt"; } public void CancelIt(Object Sender, EventArgs e) { lblMessage.Text = "Der Abbrechen-Button wurde gedrckt"; } } public class StartForm { public static void Main() { Application.Run(new Listing49()); } } }

Hier gibt es zwar eine Menge Code zu lesen, doch das meiste davon haben wir bereits gestern besprochen. Die Zeilen 15 bis 24 erzeugen und zeigen ein Labelund zwei Schaltflchen-Steuerelemente an. Die Zeilen 32 bis 38 fgen diese Steuerelemente dem Formular hinzu, und die Zeilen 58 bis 65 behandeln die Ereignisse der Schaltflchen. (Mehr Details finden sich nach Listing 3.4.) Die Zeilen 11 und 12 enthalten den ersten neuen Code; sie erzeugen neue VScrollBarund HScrollBar-Steuerelemente. Die Zeilen 26 bis 29 legen die Dock- und Visible-Eigenschaften fest. Da wir wissen, dass das Formular gro genug sein wird, um seine Inhalte komplett anzuzeigen, wollen wir die Bildlaufleisten zunchst einmal unsichtbar lassen. Um die Dock-Eigenschaft kmmern wir uns an Tag 6. Halten wir erst einmal fest, dass unsere Bildlaufleisten am rechten und am unteren Rand unseres Formulars angedockt sind.

138

Symbolleisten, Statusleisten und Bildlaufleisten hinzufgen

In Zeile 31 behandeln Sie ein neues Ereignis des Form-Objekts: Resize. Dieses Ereignis wird natrlich ausgelst, sobald die Gre des Formulars gendert wird. Zeile 31 besagt: Sobald dies passiert, fhre die Methode Resized aus, wie in den Zeilen 43 bis 56 zu sehen. Diese Methode muss zwei Dinge tun: Erstens muss sie bei einer Grennderung die Gre des Formulars bewerten (waagrecht wie senkrecht) und bestimmen, ob das Formular gro genug zur Darstellung seiner Inhalte ist. Ist das nicht der Fall, zeigt sie die Bildlaufleisten an. Zweitens muss sie die Refresh-Methode aufrufen, um das Formular neu zu zeichnen, so dass die gerade modifizierten Bildlaufleisten gezeichnet oder gelscht werden knnen. Die Mindesthhe des Formulars vor der Anzeige von vertikalen Bildlaufleisten liegt beim unteren Rand des untersten Steuerelements in unserem Fall ist dies lblMessage. Die Summe der Top- und Height-Eigenschaften des Bezeichnungsfeldes ergibt die richtige Hhe. Das if-Statement in Zeile 44 fhrt den Vergleich zwischen der Hhe des Formulars und der Mindesthhe aus. Ist die Formularhhe niedriger, zeigt man die vertikale Bildlaufleiste an (Zeile 45). Umgekehrt gilt: Ist die Formularhhe grer als die Mindesthhe, verbergen Sie die Bildlaufleiste, wie in Zeile 47 zu sehen. Die Zeilen 50 bis 54 machen das Gleiche fr die waagrechte Ausrichtung. In diesem Fall entspricht die Mindestbreite der Left-Eigenschaft des am weitesten rechts liegenden Steuerelements plus dessen Breite (Width). In Zeile 55 rufen Sie schlielich die Refresh-Methode auf. Kompilieren Sie diese Anwendung und versuchen Sie das Formular auf eine Gre zu reduzieren, die geringer ist als die minimalen Begrenzungen. Abbildung 4.14 zeigt ein typisches Beispiel.

Abbildung 4.15: Die richtigen Bildlaufleisten tauchen auf, sobald die Formulargre gendert wird.

Beachten Sie jedoch, dass auf einen Klick auf die Bildlaufleisten hin nichts passiert! Ihr Formular lsst sich nicht wie vorgesehen bewegen. Es gibt zwei Mglichkeiten, diesen Mangel zu beheben. Die erste und einfachere Mglichkeit besteht im Setzen der AutoScoll-Eigenschaft des Formulars auf true:
this.AutoScroll = true;

Ihr Formular lsst sich nun wie erwartet bewegen. Die zweite Mglichkeit besteht in einer manuellen Vorgehensweise, wobei man die Ereignisse der Bildlaufleisten behandelt. Normalerweise nutzt man diese Mglichkeit nur dann, wenn man vorher nicht wei, wie gro ein Formular werden kann (etwa wenn ein Benutzer die Gre dynamisch steigern kann).

139

Mens und Symbolleisten

Lassen Sie uns untersuchen, wofr der Bildlauf auszufhren ist, wenn die Bildlaufleisten angeklickt werden. Zunchst wrde man vermuten, es sei das Form-Objekt, doch das trfe nicht zu. Denken Sie daran, dass die Position des Formulars auf den Desktop bezogen ist; wenn Sie also einen Bildlauf bezglich des Formular-Objekts versuchen, bewegt es sich nur auf dem Desktop, doch seine Inhalte wrden an ihrem relativen Ort (im Formular) verbleiben. Entgegen unseren Erwartungen wrden sich die Steuerelemente nicht bewegen. Wir wollen aber die Steuerelemente innerhalb des Formulars bewegen, nicht aber das Formular selbst. Wenn also der Benutzer den Abwrtspfeil auf der senkrechten Bildlaufleiste anklickt, mssen sich alle Steuerelemente nach oben bewegen. Das Entsprechende gilt, wenn der Benutzer in der waagrechten Bildlaufleiste nach rechts klickt: Alle Steuerelemente mssen sich nach links bewegen. Wenn man, wie wir in unserem Beispiel, nur drei Steuerelemente im Formular hat, hlt sich der Aufwand in Grenzen. Was ist aber, wenn man mehr hat? Die Neupositionierung jedes einzelnen Steuerelements wird zur Schwerstarbeit. Unser Kniff besteht in der Verwendung eines weiteren Containersteuerelements: ein Panel-Steuerelement, das alle Steuerelemente enthlt, die bewegt werden mssen. Dann knnen Sie das PanelSteuerelement bewegen, und alle seine untergeordneten Steuerelemente werden sich entsprechend mitbewegen! Stellen Sie sich das Panel-Steuerelement als eine Art Mini-Form vor oder als Formular innerhalb eines Form-Objekts. Indem Sie die Steuerelemente in einem Panel-Steuerelement gruppieren, knnen Sie sie als eine Gruppe manipulieren das ist genau das, was Sie ja beim Bildlauf erreichen wollen. Das Erzeugen des Panel-Steuerelements ist einfach; fgen Sie den folgenden Code an einer beliebigen Stelle zwischen den Zeilen 8 und 13 in Listing 4.9 ein:
Panel pnlForm =new Panel();

Sie wollen sicher dafr sorgen, dass das Panel-Steuerelement die gleiche Gre besitzt wie das Formular, so dass alle Steuerelemente normal angezeigt werden. Fgen Sie dem Konstruktor folgenden Code hinzu:
pnlForm.Height =this.Height; pnlForm.Width =this.Width;

Als Nchstes ersetzen Sie die Zeilen 36 bis 38 durch folgenden Code:
pnlForm.Controls.Add(lblMessage); pnlForm.Controls.Add(btAccept); pnlForm.Controls.Add(btCancel);

Jetzt sind das Label- und die beiden Button-Steuerelemente nicht mehr mit dem FormObjekt verknpft, sondern stattdessen mit dem Panel-Steuerelement. Als Nchstes fgen Sie das Panel-Steuerelement dem Konstruktor des Form-Objekts hinzu:
this.Controls.Add(pnlForm);

140

Symbolleisten, Statusleisten und Bildlaufleisten hinzufgen

Um die Bildlaufereignisse zu behandeln, mssen Sie im Konstruktor Ereignishandler zum Scroll-Ereignis der Bildlaufleisten hinzufgen, etwa so:
vbarForm.Scroll +=new ScrollEventHandler(this.HandleVScroll); hbarForm.Scroll +=new ScrollEventHandler(this.HandleHScroll);

Beachten Sie die Verwendung des Objekts ScrollEventHandler. Dies bedeutet, dass Sie ein Parameterobjekt ScrollEventArgs fr Ihre Methoden verwenden. Zum Schluss mssen Sie noch die Methoden HandleVScroll und HandleHScroll erstellen, wie in Listing 4.10 gezeigt: Listing 4.10: Methoden zur Behandlung des Bildlaufs
1: 2: 3: 4: 5: 6: 7: public void HandleVScroll(Object Sender,ScrollEventArgs e){ pnlForm.Top =0 -e.NewValue; } public void HandleHScroll(Object Sender,ScrollEventArgs e){ pnlForm.Left =0 -e.NewValue; }

Diese zwei Methoden verrichten einfache Aufgaben: Sie bewegen das Panel-Steuerelement auf- oder abwrts und nach links oder rechts, wobei die entsprechenden Top- und Left-Eigenschaften des Panel-Steuerelements verwendet werden. Die Schwierigkeit besteht nun darin, wie man herausfindet, wie weit das Panel-Steuerelement in die jeweilige Richtung bewegt werden soll. Das Panel-Steuerelement sollte stets in Bezug zu seiner Startposition bewegt werden. Wenn die Bildlaufleiste beispielsweise um x Raumeinheiten abwrts bewegt wird, sollte sich das Panel-Steuerelement von seiner ursprnglichen Position aus um x Raumeinheiten aufwrts bewegen. In diesem Fall entspricht die Anfangsposition des Panel-Steuerelements den X-Y-Koordinaten (0,0), was der Grund dafr ist, warum Sie diese Nullen in den Zeilen 2 und 6 sehen. Von dieser Anfangsposition ziehen Sie den Betrag ab, um den sich die Bildlaufleiste bewegt hat. Durch das Subtrahieren stellen Sie sicher, dass sich das Panel-Steuerelement in die entgegengesetzte Richtung des Bildlaufs bewegt (der Benutzer klickt abwrts, also bewegen sich die Steuerelemente aufwrts). Tabelle 4.1 zeigt ein paar beispielhafte Situationen.
Aktion 1. Der Benutzer klickt den AufwrtsPfeil um zehn Einheiten (Wert der Bildlaufleiste = 10) Ergebnisse Das Panel-Steuerelement bewegt sich 10 Einheiten abwrts. Der neue Panel.Top-Wert lautet 10, Panel.Left = 0.

Tabelle 4.1: Beispiele fr Bewegungen beim Bildlauf

141

Mens und Symbolleisten

Aktion 2. Der Benutzer verschiebt die Bildlaufleiste bis ganz nach unten (Wert der Bildlaufleiste = Form.Height)

Ergebnisse Das Panel-Steuerelement bewegt sich ganz nach oben. Neuer Panel.Top-Wert = 0 Form.Height.Left = 0. (Beachten Sie, dass sich Bewegungen nicht akkumulieren, da die Bewegung nur relativ zur Ausgangsposition gemessen wird. Das heit, der Top-Wert in diesem Schritt ist nicht 10 Form.Height.)

3. Der Benutzer verschiebt die BildDas Panel-Steuerelement bewegt sich um 20 Einheiten laufleiste um 20 Einheiten nach links. nach rechts. Top = 0 Form.Height.Left-Wert = 20. Tabelle 4.1: Beispiele fr Bewegungen beim Bildlauf (Forts.)

Und welche Einheiten benutzen nun die Bildlaufleisten? Per Voreinstellung ist die Minimum-Eigenschaft auf 0 gesetzt (dies entspricht der Ausgangsposition der Bildlaufleiste; fr die senkrechte Bildlaufleiste also ganz oben, fr die waagrechte ganz links). Die Vorgabe fr Maximum lautet 100, was bedeutet, dass sich die Bildlaufleisten um 100 Einheiten bewegen knnen, nicht mehr. Sie knnen sowohl Minimum als auch Maximum ndern, aber der Effekt ist gleich null: Minimum entspricht immer waagrecht ganz links oder senkrecht ganz oben, und Maximum entspricht dem jeweils entgegengesetzten Ende. Die NewValue-Eigenschaft des ScrollEventArgs-Parameters bestimmt die neue Position der Bildlaufleiste (die der Benutzer durch Verschieben herbeigefhrt hat). Gem den Zeilen 2 und 6 des Listings 4.10 sind 100 Einheiten auf- und abwrts, nach links oder rechts der maximale Bewegungsradius des Panel-Steuerelements. Hat ein Formular kompliziertere Anforderungen an seine Gre, mssen Sie vielleicht den Betrag erhhen, um den sich ein Panel-Steuerelement bewegen kann. Sollte Ihr Formular also 1000 Pixel hoch sein, dann sind fr Ihre vertikale Bildlaufleiste 100 Einheiten nicht ausreichend. Somit sollte jede Einser-Einheit der Bildlaufleiste sich um mehr als nur eine Einheit der Hhe des Panel-Steuerelements bewegen. Der folgende Code knnte die notwendige Normalisierung erzielen:
int normalizedHeight = pnlForm.Height / vbarForm.Maximum; pnlForm.Top = 0 (e.NewValue * normalizedHeight);

Wenn Sie die Zeilen 2 und 6 des Listings 4.10 entfernen, erhalten Sie das zuvor besprochene Beispiel, bei dem sich das Formular ber den Desktop bewegt.

Formulare sind nicht die einzigen Objekte, die ber Bildlaufleisten verfgen knnen; Sie knnen beinahe jedem gewnschten Steuerelement Bildlaufleisten hinzufgen, so etwa einer PictureBox oder sogar Schaltflchen. Dies stellt Ihnen vielfltige Entwurfsmglichkeiten hinsichtlich der Interaktion Ihrer Anwendung mit dem Benutzer zur Verfgung.

142

Symbolleisten, Statusleisten und Bildlaufleisten hinzufgen

Wir wollen noch weitere Eigenschaften betrachten, die Sie im Zusammenhang mit Bildlaufleisten nutzen knnen. Die Eigenschaften LargeChange und SmallChange lassen sich dazu verwenden, die Anzahl Einheiten, um die sich die Bildlaufleiste nach dem Anklicken bewegt, anzupassen. SmallChange wird meist verwendet, wenn der Benutzer auf einen der Pfeile am Ende der Bildlaufleiste klickt oder eine der ()- bzw. ()-Pfeiltasten drckt. LargeChange wird benutzt, wenn die Bildlaufleiste selbst angeklickt wird oder die (Bild)und (Bild)-Pfeiltasten gedrckt werden. Die Type-Eigenschaft des Parameterobjekts ScrollEventArgs teilt Ihnen mit, was tatschlich whrend des Bildlaufereignisses passiert ist, also etwa eine groe oder kleine Bewegung oder wohin sich die Bildlaufleiste bewegt hat. Tabelle 4.2 fhrt diese Werte auf, von denen alle zur Aufzhlung ScrollEventType gehren.
Wert
EndScroll

Beschreibung Das Bildlauffeld (Scroll-Box; das Kstchen, das die Position der Bildlaufleiste anzeigt) hat aufgehrt sich zu bewegen. Das Bildlauffeld hat sich zum Minimum-Wert bewegt. Das Bildlauffeld hat sich um den LargeChange-Wert nach oben oder nach links bewegt (die Inhalte verschieben sich dann nach unten oder nach rechts). Das Bildlauffeld hat sich um den LargeChange-Wert nach unten oder nach rechts bewegt (die Inhalte verschieben sich dann nach oben oder nach links). Das Bildlauffeld hat sich zu dem durch Maximum angegebenen Wert bewegt. Das Bildlauffeld hat sich um den SmallChange-Wert nach oben oder nach links bewegt (die Inhalte verschieben sich dann nach unten oder nach rechts). Das Bildlauffeld hat sich um den SmallChange-Wert nach unten oder nach rechts bewegt (die Inhalte verschieben sich dann nach oben oder nach links). Das Bildlauffeld selbst wurde bewegt. Das Bildlauffeld bewegt sich noch.

First LargeDecrement

LargeIncrement

Last SmallDecrement

SmallIncrement

ThumbPosition ThumbTrack

Tabelle 4.2: Werte fr ScrollEventType

Listing 4.11 zeigt ein kurzes Beispiel (zumindest im Hinblick auf die Komplexitt) fr die Verwendung dieser Werte.

143

Mens und Symbolleisten

Listing 4.11: So stellt man den Typ des aufgetretenen Bildlaufs fest.
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: 42: using System; using System.Windows.Forms; using System.Drawing; namespace TYWinForms.Day4 { public class Listing410 : Form { Label lblMessage = new Label(); VScrollBar vbarForm = new VScrollBar(); HScrollBar hbarForm = new HScrollBar(); public Listing410(){ lblMessage.Location = new Point(75,75); lblMessage.Width = 200; vbarForm.Dock = DockStyle.Right; vbarForm.Visible = true; vbarForm.Scroll += new ScrollEventHandler (this.HandleScroll); hbarForm.Dock = DockStyle.Bottom; hbarForm.Visible = true; hbarForm.Scroll += new ScrollEventHandler (this.HandleScroll); this.Text = "Beispiel fr ScrollEventType-Wert"; this.Height = 200; this.Controls.Add(lblMessage); this.Controls.Add(vbarForm); this.Controls.Add(hbarForm); } public void HandleScroll(Object Sender, ScrollEventArgs e){ switch(e.Type){ case ScrollEventType.EndScroll: lblMessage.Text = "Die Scroll-Box bewegt sich nicht mehr"; break; case ScrollEventType.First: lblMessage.Text = "Die Scroll-Box befindet sich bei " + ((ScrollBar)Sender).Minimum.ToString(); break; case ScrollEventType.LargeDecrement: lblMessage.Text = "Die Scroll-Box hat sich bewegt " + ((ScrollBar)Sender).LargeChange.ToString(); break; case ScrollEventType.LargeIncrement: lblMessage.Text = "Die Scroll-Box hat sich bewegt" + ((ScrollBar)Sender).LargeChange.ToString();

144

Symbolleisten, Statusleisten und Bildlaufleisten hinzufgen

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:

break; case ScrollEventType.Last: lblMessage.Text = "Die Scroll-Box befindet sich bei " + ((ScrollBar)Sender).Maximum.ToString(); break; case ScrollEventType.SmallDecrement: lblMessage.Text = "Die Scroll-Box hat sich bewegt " + ((ScrollBar)Sender).SmallChange.ToString(); break; case ScrollEventType.SmallIncrement: lblMessage.Text = "Die Scroll-Box hat sich bewegt " + ((ScrollBar)Sender).SmallChange.ToString(); break; case ScrollEventType.ThumbPosition: lblMessage.Text = "Die Scroll-Box hat sich bewegt "; break; case ScrollEventType.ThumbTrack: lblMessage.Text = "Die Scroll-Box bewegt sich"; break; } } } public class StartForm { public static void Main(){ Application.Run(new Listing410()); } } }

Der Konstruktor drfte recht vertraut aussehen; er erzeugt einfach die Bildlaufleisten und ein Bezeichnungsfeld, danach fgt er die Steuerelemente dem Formular hinzu. Der Scroll-Ereignishandler namens HandleScroll sieht dagegen schon wesentlich interessanter aus. Diese in Zeile 30 beginnende Methode wertet die Type-Eigenschaft des Parameters ScrollEventArgs aus. Mit Hilfe eines switch-Statements vergleicht sie den Type-Wert mit jedem der ScrollEventType-Werte, die in Tabelle 4.2 aufgefhrt sind. Fr jeden Einzelfall gibt sie eine entsprechende Zeichenfolge an das Label-Steuerelement aus. Zu diesem Listing sind ein paar Bemerkungen angebracht. Erstens mssen Sie die Variablen in den Zeilen 36, 39, 42, 45, 48 und 51 konvertieren. Die Variable Sender ist ein Object-Typ (wie durch die Methodendeklaration in Zeile 30 angezeigt wird). Wir wissen zwei Dinge ber dieses Object: Es reprsentiert die angeklickte Bildlaufleiste (warum, verrate ich morgen) und sollte in dieser

145

Mens und Symbolleisten

Eigenschaft die verschiedenen Bildlaufleisten -Eigenschaften enthalten, darunter Minimum, Maximum usw. Ein Objekt hat jedoch diese Eigenschaft nicht, weshalb Sie es in den richtigen Typ ScrollBar umwandeln mssen, damit Sie die richtigen Eigenschaft erreichen knnen. (Mehr dazu morgen.) Als Zweites ist festzustellen, dass Sie in ein ScrollBar- und nicht in ein VScrollBaroder HScrollBar-Steuerelement umwandeln mssen (die beiden Letzteren erben ihre Eigenschaften vom ScrollBar-Steuerelement). Daher ist es unerheblich, ob das angeklickte Steuerelement nun ein VScrollBar- oder HScrollbar-Steuerelement ist, denn alles, was wir bentigen, ist der Zugang zu den entsprechenden Eigenschaften welche die ScrollBar-Klasse bereitstellt. (Mehr dazu morgen.) Abbildung 4.15 zeigt dieses Beispiel, nachdem die Bildlaufleiste unterhalb des Bildlauffeldes angeklickt wurde.

Abbildung 4.16: Wenn man unterhalb des Bildlauffeldes klickt, wird ein Bildlauf um den LargeChange-Wert ausgefhrt.

4.4

Zusammenfassung

Sie haben heute etwas ber drei wichtige und hufig eingesetzte Arten von Steuerelementen gelernt: Mens, Symbolleisten und Bildlaufleisten. Hinsichtlich ihrer Funktion sind sie sehr hnlich und teilen sich sogar viele hnliche Eigenschaften, doch hinsichtlich ihrer Implementierung sind sie vllig verschieden voneinander. Ein Steuerelement in Windows Forms ist ein Objekt, das dem Benutzer eine Schnittstelle prsentiert, mit der hufig eine Interaktion mglich ist. Es gibt unterschiedliche Arten von Steuerelementen, darunter Containersteuerelemente, Mens und Schaltflchen (Buttons). Mens lassen sich durch die Steuerelemente MainMenu und MenuItem darstellen. Das MainMenu nimmt Menelemente (MenuItems) auf, welche wiederum die Beschriftungen prsentieren, die der Benutzer sieht und mit denen er in Mens interagiert. Kontextmens, die sich ffnen, wenn der Benutzer auf ein Steuerelement rechtsklickt, verwenden ebenfalls MenuItem-Steuerelemente, setzen aber MainMenu anstelle von ContextMenu ein.

146

Fragen und Antworten

Eine Symbolleiste hnelt einem Men, nur dass man ein ToolBar-Steuerelement als bergeordnetes Objekt einsetzt und die ToolBarButton-Steuerelemente als untergeordnete Steuerelemente (ToolBarButtons knnen sogar MenuItem-Steuerelemente als untergeordnete Steuerelemente haben, wie es bei Dropdown-Schaltflchen der Fall ist). Von den beiden kann nur das ToolBar-Steuerelement verwendbare Ereignisse haben, so dass jeder Klick auf eine Schaltflche durch seinen Ereignishandler zu behandeln ist blicherweise mit einem switch- oder Select Case-Statement. Bei der Statusleiste handelt es um eine weitere Containerart. Sie verwendet die Steuerelemente StatusBar und StatusBarPanel. In der Regel sind Statusleisten nicht interaktiv, sondern zeigen lediglich Informationen an. Sowohl StatusBar als auch StatusBarPanel verfgen ber Eigenschaften, die sich zur Informationsanzeige nutzen lassen, doch das normale Vorgehen sieht so aus, dass man nur das StatusBarPanel-Steuerelement als Anzeigemechanismus verwendet und das Steuerelement StatusBar als Container. Bildlaufleisten schlielich gibt es in zwei Varianten: HScrollBar und VScrollBar stellen waagrechte bzw. senkrechte Bildlaufleisten dar. Sie haben erfahren, dass es recht leicht ist, sie Ihrem Formular hinzuzufgen, dass jedoch die Behandlung ihrer Ereignisse eine sorgfltige Planung erfordert insbesondere dann, wenn man herauszufinden versucht, fr welche Elemente, um wie viele Einheiten und in welche Richtung der Bildlauf stattfinden soll. Beide Steuerelemente erben von der Basisklasse ScrollBar.

4.5
F A

Fragen und Antworten


Ja, es gibt ein solches Ereignis: PopUp. Wenn Sie eine Aktion direkt vor dem Eintreten dieses Ereignisses ausfhren lassen wollen etwa das Verbergen oder Anzeigen von Menelementen knnen Sie fr das Ereignis PopUp einen Handler erstellen.

Gibt es ein Ereignis, das signalisiert, dass sich gleich ein Men ffnen wird?

Inwiefern unterscheidet sich die NewValue-Eigenschaft des ScrollEventArgs-Objekts von der Value-Eigenschaft? A
NewValue gibt den Wert an, um den sich die Bildlaufleiste whrend eines Ereignisses ndert. Value hingegen gibt den statischen Wert an. Hufig sind diese Werte identisch, manchmal aber auch nicht.

Wenn beispielsweise die Bildlaufleiste oder die Pfeile angeklickt werden, sind diese Werte identisch. Wenn jedoch das Bildlauffeld selbst mit der Maus bewegt wird, dann ist Value der vorhergehende Wert, whrend NewValue der aktuellen Position entspricht. Sobald das Bildlauffeld wieder losgelassen wird, nehmen beide Werte wieder den gleichen Wert an.

147

Mens und Symbolleisten

4.6

Workshop

Dieser Workshop soll Ihnen helfen, sich besser an die heute behandelten Begriffe zu erinnern. Es hilft Ihnen sehr, die Antworten vollstndig zu verstehen, bevor Sie weiterlesen. Die Antworten zu den Quizfragen und den bungen finden Sie in Anhang A.

Quiz
1. Wahr oder falsch? Alle Steuerelemente inklusive des Form-Objekts erben von der Klasse Control. 2. Welches Objekt muss mit Symbolleisten verknpft sein, damit Grafiken in den Symbolleistenschaltflchen angezeigt werden? 3. Wie heien die drei optionalen Parameter fr einen MenuItem-Konstruktor, und welches sind ihre Typen? 4. Welches Zeichen wird verwendet, um eine Tastenkombination fr einen Buchstaben in der Beschriftung eines Menelements bereitzustellen? 5. Schreiben Sie eine Zeile C#-Code, die ein ToolBarButton-Steuerelement namens MyFirstButton anweist, die vierte Grafik in der zugeordneten ImageList zu verwenden. 6. Wahr oder falsch? Das Ereignis, das fr die Behandlung von Mausklicks auf Symbolleistenschaltflchen verwendet wird, heit Click. 7. Welche sind die Standardwerte fr die Eigenschaften Minimum, Maximum, SmallChange und LargeChange einer Bildlaufleiste?

bung
Erstellen Sie in VB .NET eine Anwendung, die ein personalisiertes Men verwendet wie jene, die mit Microsoft Office 2000 und Windows 2000 eingefhrt wurden. Diese Mens zeigen nur die zuletzt benutzten Menelemente, whrend sie die anderen verbergen und es dem Benutzer gestatten, auf einen Pfeil zu klicken, um weniger hufig ausgewhlte Menelemente anzuzeigen. Ihre Anwendung sollte ein Men anbieten, das verborgene Menelemente besitzt. Wird ein bestimmtes Menelement angeklickt, sollen die verborgenen Elemente angezeigt werden. Kmmern Sie sich nicht um das Hinzufgen von Ereignishandlern fr jedes Menelement.

148

Workshop

Ihr Men kann recht einfach sein: Es braucht sich nicht daran zu erinnern, welche Menelemente der Benutzer am hufigsten auswhlt, und es wird keine verborgenen Menelemente anzeigen, nur weil der Mauspfeil ber dem Men schwebt. Sobald der Benutzer auf die Schaltflche WEITERE... (More) klickt, muss er das Men erneut ffnen, um die bislang verborgenen Elemente sehen zu knnen. (Sie werden sehen, wie man fortgeschrittenere Funktionen verwenden kann, wenn Sie an Tag 13 etwas ber GDI+ erfahren.

149

Ereignisse in Windows Forms

Ereignisse in Windows Forms

Einer der wichtigsten Aspekte an einer Anwendung ist sicherlich von der visuellen Oberflche abgesehen die Art und Weise, wie sie mit dem Benutzer interagiert. Wie Sie bereits wissen, wird diese Interaktion durch Ereignisse und deren Handler realisiert. Ein Ereignis ist das Resultat einer Aktion, also einer Meldung, die erzeugt wurde, um das Auftreten einer Aktion zu signalisieren. Die Aktion kann vom Benutzer oder von der Anwendung selbst verursacht worden sein. Ein Ereignishandler ist jene Methode, die reagiert, sobald ein Ereignis auftritt. Heute befassen wir uns mit Ereignissen und ihren Methoden, was sie fr Ihre Anwendung bedeuten und warum man sie bentigt. Ich werde Ihnen eine neue Art von Klasse namens Delegate vorstellen, die Ihrer Anwendung bei der Ereignisbehandlung hilft. Heute lernen Sie,

was man in technischer Hinsicht unter einem Ereignishandler versteht, wie Sie Ihre Ereignisse miteinander verdrahten, wie Sie Delegaten verwenden, wie Sie Ihre eigenen Ereignisse und Ereignisparameter erzeugen, was jeder Parameter eines Ereignishandlers bewirkt, woher er stammt und wie man ihn einsetzt.

5.1

Was sind Ereignishandler?

Aus den vorausgegangenen Kapiteln verfgen Sie bereits ber eine gewisse Erfahrung im Umgang mit Ereignissen. Sie wissen, was ein Ereignis ist und ungefhr, was ein Ereignishandler ist. Nun geht es um Letzteren. Um Ihre Anwendung zu einer Reaktion auf ein Ereignis zu veranlassen, mssen Sie einen Ereignishandler erstellen. Dieses Stck Code wird nur dann ausgefhrt, wenn ein Ereignis eintritt. Wenn Sie beispielsweise Ihr Auto starten wollen, muss zwischen dem Gaspedal, dem Motor und dem Benzintank eine gewisse Verbindung bestehen, sonst erfolgt keine Aktion: es ist eine Verknpfung ntig. Das Gleiche lsst sich fr Ereignisse in .NET sagen. Eine Aktion mag erfolgen und ein Ereignis passieren, doch sofern es keine Verbindung dazwischen gibt, wird nichts stattfinden. Dementsprechend nennt man den Vorgang der Erstellung eines Ereignishandlers fr ein Ereignis auch die Verknpfung eines Ereignisses.

152

Was sind Ereignishandler?

Ereignisse behandeln
Wir wollen uns zunchst eine Anwendung ansehen, die einige aus Menelementen erzeugte Ereignisse verarbeitet, wie in Listing 5.1 zu sehen. Listing 5.1: Mit Ereignishandlern arbeiten
1: Imports System 2: Imports System.Windows.Forms 3: Imports System.Drawing 4: 5: Namespace TYWinForms.Day5 6: 7: public class Listing51 : Inherits Form 8: private mnuFont as new MainMenu 9: private lblMessage as new Label 10: 11: public sub New() 12: Me.Text = "Listing 5.1 " 13: Me.Menu = mnuFont 14: 15: lblMessage.Text = "Testen von Ereignissen" 16: lblMessage.Location = new Point(75,75) 17: lblMessage.Height = 50 18: lblMessage.Width = 150 19: lblMessage.BackColor = Color.LightBlue 20: 21: dim miFont as MenuItem = mnuFont.MenuItems.Add("Font") 22: dim miTimes as MenuItem = miFont.MenuItems.Add("Times Roman ") 23: AddHandler miTimes.Click,new EventHandler(AddressOf  Me.TimesClicked) 24: 25: dim miArial as MenuItem = miFont.MenuItems.Add("Arial") 26: AddHandler miArial.Click, new EventHandler(AddressOf  Me.ArialClicked) 27: 28: dim miWing as MenuItem = miFont.MenuItems.Add("Wingdings") 29: AddHandler miWing.Click, new EventHandler(AddressOf Me.WingClicked) 30: 31: Me.Controls.Add(lblMessage) 32: End Sub 33: 34: public sub TimesClicked(Sender as Object, e as EventArgs) 35: lblMessage.Font = new Font("Times",15) 36: mnuFont.MenuItems(0).MenuItems(0).Checked = True

153

Ereignisse in Windows Forms

37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60:

mnuFont.MenuItems(0).MenuItems(1).Checked = False mnuFont.MenuItems(0).MenuItems(2).Checked = False end sub public sub ArialClicked(Sender as Object, e as lblMessage.Font = new Font("Arial", 15) mnuFont.MenuItems(0).MenuItems(0).Checked = mnuFont.MenuItems(0).MenuItems(1).Checked = mnuFont.MenuItems(0).MenuItems(2).Checked = end sub EventArgs) False True False

public sub WingClicked(Sender as Object, e as EventArgs) lblMessage.Font = new Font("Wingdings",15) mnuFont.MenuItems(0).MenuItems(0).Checked = False mnuFont.MenuItems(0).MenuItems(1).Checked = False mnuFont.MenuItems(0).MenuItems(2).Checked = True end sub public Shared Sub Main() Application.Run(new Listing51) end sub end class End Namespace

Diese Anwendung zeigt etwas Text an und erlaubt dem Benutzer, die Schriftart den Font mit Hilfe eines Mens zu ndern. Zunchst einmal die Grundelemente: Die Zeilen 1 bis 3 importieren die zu verwendenden Namensrume. In Zeile 8 und 9 werden die beiden Hauptsteuerelemente der Anwendung deklariert: ein MainMenu und ein Label. In Zeile 11 beginnt der Konstruktor fr die Windows Forms-Klasse. Die Zeilen 15 bis 19 initialisieren die Eigenschaft des Label-Steuerelements, wobei sie seine Gre, Position und Text-Eigenschaften festlegen. Der nchste Codeabschnitt drfte noch von gestern vertraut sein. Zeile 21 fgt dem MainMenu-Objekt ein Menelement mit der Aufschrift Font hinzu. Dieses erste Menelement wird die Kopfzeile des ganzen Mens. Zeile 22 fgt dieser Kopfzeile ein Menelement hinzu, nmlich die erste unserer zur Auswahl stehenden Schriftarten. Zeile 23 verwendet die AddHandler-Methode, um dem Click-Ereignis des Menelements einen Ereignishandler hinzuzufgen. Das Click-Ereignis wird stets ausgelst, wenn das zugehrige Menelement angeklickt wird. Diese Zeile benachrichtigt die Anwendung von einem Ereignishandler namens TimesClicked, der fr dieses Ereignis zu verwenden ist (mehr dazu heute im Abschnitt Delegaten). Die Zeilen 25 bis 29 erledigen die gleiche Aufgabe wie die Zeilen 22 und 23, nmlich weitere Menelemente fr weitere Fontoptionen zu erstellen (Arial und Wingdings) und den

154

Was sind Ereignishandler?

Click-Ereignissen dieser Menelemente Ereignishandler zuzuweisen. Diese Zeilen ver-

knpfen also die Ereignisse. Sobald eines dieser Menelemente angeklickt wird, beginnt die Ausfhrung der jeweiligen Methode (TimesClicked, ArialClicked oder WingClicked). Dieser Ablauf ist recht simpel, obwohl der Code vielleicht ein wenig seltsam aussehen mag. Die TimesClicked-Methode in Zeile 34 ndert die Schriftart des Label-Steuerelements in Zeile 35. An Tag 3 haben wir untersucht, wie man Schriftarten mit Hilfe der Objekte Font und FontFamily ndert. Heute lernen wir eine neue Vorgehensweise. Statt das FontFamilyObjekt einzusetzen, bergeben wir einfach eine Zeichenfolge mit der Schriftart, zu der wir wechseln mchten in diesem Fall Times. Der zweite Parameter ist wieder die anzuzeigende Schriftgre. Um den Benutzer wissen zu lassen, welche Schriftart gerade benutzt wird, wollen wir die Menelemente miTimes, miArial oder miWing mit Hkchen versehen, sobald sie jeweils ausgewhlt sind. Die folgende Zeile bedeutet, dass das erste MenuItem, das dem MainMenuObjekt mnuFont hinzugefgt wurde, ein Hkchen erhalten soll anders ausgedrckt, das miTimes-Menelement:
mnuFont.MenuItes(0).MenuItems(0).Checked = True

Die nchsten beiden Zeilen (37 und 38) verweisen auf die Elemente miArial und miWing (die zweiten und dritten Menelemente, die nach dem ersten MenuItem dem MainMenuObjekt mnuFont hinzugefgt wurden). Da diese Optionen offensichtlich gerade nicht ausgewhlt sind, sollten sie kein Hkchen aufweisen und ihre Checked-Werte auf False lauten:
mnuFont.MenuItes(0).MenuItems(1).Checked = False mnuFont.MenuItes(0).MenuItems(2).Checked = False

Da Sie nicht wissen, welches dieser Menelemente vorher mit Hkchen versehen war, setzen Sie beide auf False, um sicher zu gehen. Die ArialClicked- und WingClicked-Methoden in Zeile 41 bzw. 48 machen das Gleiche wie die TimesClicked-Methode, nur dass sie natrlich die Arial- bzw. Wingdings-Schriftarten anstelle von Times Roman verwenden. Zeile 55 schlielich deklariert den Eintrittspunkt zur Anwendung, nmlich die MainMethode. Sie ruft einfach die Run-Methode auf. Abbildung 5.1 zeigt diese Anwendung in Aktion. Lassen Sie uns kurz zur TimesClicked-Methode zurckkehren. Bitte beachten Sie die Signatur dieses Ereignishandlers:
public sub TimesClicked(Sender as Object, e as EventArgs)

Es handelt sich hierbei um die Standardsignatur eines Ereignishandlers (anders ausgedrckt: seine Deklaration). Alle Ereignishandler weisen beinahe identische Signaturen auf. Es ndert sich jeweils nur der Methodenname (hier also TimesClicked) und in seltenen Fllen auch der abschlieende Parametertyp EventArgs (Nheres dazu im Abschnitt Delegaten).

155

Ereignisse in Windows Forms

Abbildung 5.1: Die Schriftart ndert sich als Reaktion auf Ereignisse.

Der erste Parameter, Sender, stellt das das Ereignis erzeugende Objekt dar. In Listing 5.1 wre dies das MenuItem-Objekt miTimes. Der zweite Parameter enthlt jede nhere Information ber das Ereignis. Diese Parameter werden eingesetzt, um den Ereignishandler bei seiner Aufgabe zu untersttzen. Meistens werden Sie diese Standardsignatur fr alle Ihre Ereignishandler benutzen, wodurch sie nicht nur sehr leicht zu schreiben, sondern auch leicht aus bereits geschriebenem Code herauszugreifen sind. Dies war ein einfaches Beispiel, wie man Ereignishandler verwenden kann. Wir haben pro Ereignis, das uns interessierte, einen Ereignishandler erzeugt, sozusagen einen Draht pro Ereignis. Fr alle anderen Ereignisse erzeugen wir keine Handler, wodurch diese praktisch ignoriert werden.

Mehrfache Ereignisse behandeln


Ein sehr interessantes .NET-Merkmal besteht darin, dass ein Ereignis nicht unbedingt auch seinen eigenen Ereignishandler haben muss: Die Ereignisse knnen Handler gemeinsam nutzen! Eine Verknpfung kann von einem Handler zu mehreren Ereignissen bestehen. bertragen auf unseren Auto-Vergleich, hiee das, dass sich der Wagen auf mehrere Arten in Bewegung versetzen lsst, so etwa durch Anschieben oder durch das Loslassen der Bremse an einem abschssigen Hgel. Da aber so viele Ereignisse einen Ereignishandler auslsen knnen, ist es umso wichtiger herauszufinden, welches Objekt das Ereignis erzeugt hat. An diesem Punkt kommt der Einsatz der Parameter eines Ereignishandlers unseren Wnschen entgegen. Listing 5.2 zeigt ein Beispiel eines mehrere Ereignisse verarbeitenden Ereignishandlers. Fertigen Sie von Listing 5.1 eine Kopie an und ersetzen Sie die Zeilen 34 bis 53 durch den Code in diesem Listing.

156

Was sind Ereignishandler?

Listing 5.2: Die Behandlung mehrerer Ereignisse mit nur einem Handler
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: public sub FontClicked(Sender as Object, e as EventArgs) dim item as MenuItem if Sender is mnuFont.MenuItems(0).MenuItems(0)then lblMessage.Font = new Font("Times ", 15) elseif Sender is mnuFont.MenuItems(0).MenuItems(1)then lblMessage.Font = new Font("Arial ", 15) elseif Sender is mnuFont.MenuItems(0).MenuItems(2)then lblMessage.Font = new Font("Wingdings ", 15) end if for each item in mnuFont.MenuItems(0).MenuItems item.Checked = False next item CType(Sender,MenuItem).Checked = True end sub

Die FontClicked-Methode ersetzt die Methoden TimesClicked, ArialClicked und WingClicked aus Listing 5.1 sie erledigt die gleiche Aufgabe, doch auf etwas andere Weise. Beachten Sie die Signatur in Zeile 1: Sie ist immer noch die gleiche Standardsignatur. In Listing 5.1 verwendeten wir keinen dieser Parameter, doch diesmal verwenden wir sie, um zu bestimmen, welches Objekt das Ereignis erzeugt hat. In Zeile 2 deklarieren Sie ein neues MenuItem-Objekt; wir werden es gleich einsetzen. Die Zeilen 4 bis 10 stellen fest, welches Menelement das Ereignis erzeugt hat, auf das diese Methode reagiert. Zu der Reaktion gehrt die Auswertung des Sender-Parameters und dessen Vergleichen mit jedem der MenuItem-Objekte, die in der Auflistung miFont.MenuItems enthalten sind. Denken Sie daran, dass Sender das Objekt darstellt, das das Ereignis erzeugt hat. Die Schriftart im Label-Steuerelement ndert sich je nach dem Wert von Sender. Wir verwenden kein Gleichheitszeichen, um den Vergleich in den Zeilen 4, 6 und 8 zu bewerten, denn der Gleichheitsoperator funktioniert nur bei einfachen Datentypen wie etwa Zahlen oder Zeichenfolgen. Bei Objekten bentigen wir hingegen das Schlsselwort is. Im nchsten Schritt verleihen wir dem aktuellen Menelement ein Hkchen und entfernen die Hkchen bei den restlichen Menelementen. Zu diesem Zweck verwenden wir eine for each-Schleife wie in Zeile 12. Sie nimmt jedes MenuItem aus der Auflistung miFont.MenuItems, weist es der Variablen item, die wir in Zeile 2 erzeugten, zu und setzt die Eigenschaft Checked dieser Variablen auf False, so dass die Hkchen aller anderen

157

Ereignisse in Windows Forms

MenuItems entfernt werden. Die for each-Schleife htte man auch durch eine einfache forSchleife bewerkstelligen knnen, etwa so: for i = 0 to mnuFont.MenuItems(0).MenuItems.Count 1 mnuFont.MenuItems(0).MenuItems(i).Checked = False next i

Die for-Schleife setzt eine Variable i ein, um durch die Gesamtzahl aller Menelemente zu iterieren. Vergessen Sie nicht, die Variable i zu deklarieren, falls Sie diese Methode verwenden. Da nun alle MenuItems hkchenlos sind, brauchen wir nur das aktuelle Menelement mit Hkchen zu versehen. Das erfolgt in Zeile 15. Mit Hilfe der CType-Methode konvertieren wir den Sender-Parameter zu einem MenuItem-Objekt (zum nheren Verstndnis verweise ich auf den Abschnitt Ereignishandler spter in diesem Kapitel). Da Sender das aktuelle Menelement darstellt, knnen wir seine Checked-Eigenschaft auf True setzen. Zum Schluss mssen Sie die Zeilen 23, 26 und 29 in Listing 5.1 so ndern, dass sie Ihren neuen Allround-Ereignishandler nutzen:
23: ... 26: ... 29: ... AddHandler miTimes.Click, new EventHandler(AddressOf Me.FontClicked) AddHandler miArial.Click, new EventHandler(AddressOf Me.FontClicked) AddHandler miWing.Click, new EventHandler(AddressOf Me.FontClicked)

Die Ausfhrung des genderten Codes produziert die gleichen Ergebnisse wie das ursprngliche Listing 5.1. Der einzige Unterschied: Ein Ereignishandler erledigt die Arbeit von dreien. Nun sind Sie schon fast ein Profi in der Ereignis-Behandlung. Aber es gibt noch ein paar andere Puzzlestckchen, die wir noch zusammensetzen sollten. Woher stammen beispielsweise die Parameter Sender und EventArgs?

5.2

Delegaten

Ein Ereignis ist also, dies nur zur Erinnerung, eine Meldung, die ein Objekt abschickt, um zu signalisieren, dass eine Aktion stattgefunden hat. Wenn also eine Schaltflche angeklickt wird, schickt sie eine Ich wurde angeklickt-Meldung an die Common Language Runtime (CLR). Jedes Objekt hat die Verantwortung, die CLR ber Ereignisse zu informieren sonst wrde nie etwas passieren. Das das Ereignis auslsende Objekt etwa die Schaltflche in obigem Beispiel wird als Sender (sender) bezeichnet (jetzt wissen Sie, warum Sie einen Sender-Parameter bei der

158

Delegaten

Deklaration eines Ereignishandlers verwendet haben). Das Objekt, das die Meldung ber das Ereignis erhlt und darauf antwortet, wird als Empfnger (receiver) bezeichnet. Da nun in .NET alles etwas flexibler gehandhabt wird, kann auch jedes Objekt ein Ereignis auslsen und jedes Objekt kann ein Ereignis empfangen. Dafr bestehen kaum Einschrnkungen. Diese Flexibilitt fordert allerdings ihren Preis: Der Sender wei vorher nie genau, wer der Empfnger sein wird, noch kmmert er sich darum. Der Sender wei lediglich, das er ein Ereignis auslst, das durch etwas anderes verarbeitet wird. Man braucht also eine Art Vermittler, etwas, das als Mittelsmann zwischen Sender und Empfnger fungiert. Zum Glck stellt die CLR einen Delegaten (Delegierter) zur Verfgung, eine speziellen Typ fr einen solchen Vermittler. Ein Delegat ist eine Klasse, die einen Verweis auf eine Methode enthalten kann. Das mag noch nicht viel Sinn ergeben. Einfach ausgedrckt, agiert ein Delegat als Zeiger auf einen EreignisAbsender. Er zeigt den Weg zum Empfnger. In einem vereinfachten Football-Spiel beginnt das Spiel, sobald der Quarterback (der Absender) den Ball besitzt. Er muss den Ball zu jemand anderem (dem Empfnger) in seiner Mannschaft weiterspielen. Wenn das Spiel beginnt, verfgt der Quarterback ber eine beliebige Zahl von mglichen Empfngern. Die Entwicklung des Spiels entscheidet darber, wer den Ball tatschlich erhlt. Der Quarterback berschaut das Spielfeld, um zu entscheiden, wem er den Ball zuspielen soll. Manchmal versperren Spieler der gegnerischen Mannschaft die Sicht auf manche Empfnger, und zuweilen kann der Quarterback nicht alle der verfgbaren Empfnger sehen. Noch kurz bevor er den Ball wirft, wei der Quarterback nicht, wer diesen empfngt (manchmal sogar noch nach dem Wurf). In unserem vereinfachten Spiel sieht jedoch der Trainer an der Seitenlinie alles aus einem besseren Blickwinkel. Daher sieht er (besser als es der Quarterback jemals knnen wird), wer der Empfnger sein soll. Durch einen Funkempfnger hinten am Helm des Quarterbacks ruft der Trainer: Wirf den Ball Nummer 66 zu! (Wir wollen die Frage mal ignorieren, ob dies legal ist.) Daraufhin wirft der Quarterback den Ball zum Empfnger, den der Trainer angegeben hat. Abbildung 5.2 illustriert dieses Konzept. In diesem Beispiel stellt der Trainer den Delegaten dar, den Vermittler zwischen Absender und Empfnger, wobei er das Ereignis (den Ball) in die richtige Richtung lenkt. In .NET kann ein Objekt (der Absender) ein Ereignis werfen. Der Ereignisempfnger kann in jedem anderen Mitglied der Absendermannschaft bestehen beziehungsweise der Klasse, der das Objekt angehrt. Ein Delegat leitet die Ereignismeldung zum richtigen Empfnger. Wenn Sie in den Begriffen von Football denken, ergibt das durchaus einen Sinn!

159

Ereignisse in Windows Forms

Potenzielle Empfnger (Mannschaftsmitglieder des Quarterback)

22

66

17
Verteidigung Nummer 66

13
Quarterback Trainer

Abbildung 5.2: Ein Football-Quarterback der hier als Absender fungiert hat eine beliebige Anzahl von Empfngern (fr seinen Ball).

Wie sieht also ein Delegat in Codeform aus? Sie sind vielleicht berrascht zu erfahren, dass Sie bereits einige verwendet haben. Sehen Sie sich folgendes Codefragment an:
'VB .NET Addhandler Button.Click, new EventHandler(AddressOf method) //C# Button.Click += new EventHandler(this.method);

Sie erinnern sich, dass die Definition eines Delegaten in einer Klasse besteht, die eine Referenz auf eine Methode enthlt. Die Implementierung einer Klasse ist ein Objekt. Die Delegaten in diesem Codestck sind die EventHandler-Objekte. Die Objekte ScrollEventHandler, ToolBarClickEventHandler und KeyPressEventHandler, die Sie bereits in vorhergehenden Lektionen benutzt haben, sind ebenfalls Delegaten. Tatschlich ist jede Klasse in .NET, die das Wort EventHandler im Namen enthlt, ein Delegat. Das Codestck dient also im Endeffekt dazu, dem Click-Ereignis des Button-Steuerelements Delegaten hinzuzufgen. Sie zeigen den Weg zum Empfnger des Ereignisses, in diesem Fall dem method-Parameter, welcher nur eine Methode ist.

160

Delegaten

Zu den unterschiedlichen Implementierungen in VB .NET und C#: In C# besteht die einzige Mglichkeit, einem Ereignis einen Delegaten hinzuzufgen, in dem Operator +=. (Fr jene unter Ihnen, die mit C++ und C nicht vertraut sind, sei gesagt, dass += ein Kurzbefehl fr das Summieren und Zuweisen ist.) Zum Beispiel ist
myInteger += 1;

das Gleiche wie


myInteger = myInteger + 1;

Um einen Delegaten von einem Ereignis zu lsen, verwenden Sie den Operator -= (oder in Visual Basic .NET die RemoveHandler-Methode).

In VB .NET verfgen Sie andererseits ber eine Reihe weiterer Optionen. Die erste Wahlmglichkeit die Sie bereits eine Weile eingesetzt haben besteht im Gebrauch der AddHandler-Methode. Die Syntax bietet kaum Schwierigkeiten. Diese Methode fgt den Delegaten (der auf method zeigt) dem Click-Ereignis des Button-Objekts hinzu. Der Operator AddressOf ist lediglich ein Schlsselwort, das das Programmquivalent eines Zeigers auf die bereitgestellte Methode erzeugt. Als zweite und weniger gebruchliche Option gestattet VB .NET, das Schlsselwort Handles zu verwenden. Wie in Listing 5.3 zu sehen, fgen Sie einer Methodendeklaration das Handles-Schlsselwort hinzu, gefolgt vom Ereignis, das zu verarbeiten ist. Listing 5.3: Eine weitere Mglichkeit, Ereignisse zu verknpfen
1: 2: 3: public sub MyEventHandler(Sender as Object, e as EventArgs) Handles 'tu etwas end sub

 btTemp.Click

Das funktioniert genauso gut wie die anderen Methoden.


Ich empfehle Benutzen Sie in VB .NET mglichst immer die
AddHandler-Methode. Sie ist nicht nur die ver-

Bitte beachten Sie Benutzen Sie die Handles-Methode bitte sparsam. Sie schrnkt Ihre Mglichkeiten dadurch ein, dass man damit nur ein Ereignis pro Handler verarbeiten kann, und sie macht Code weniger gut lesbar.

stndlichste Methode, sondern erlaubt Ihnen auch, denselben Handler fr viele verschiedene Ereignisse zu verwenden. Benennen Sie Ihre Ereignishandler gem einer standardmigen Namenskonvention. Dies macht Ihren Code spter viel einfacher zu lesen und zu verstehen.

161

Ereignisse in Windows Forms

5.3

Ereignisse und Objekte

Wir wollen uns nun der Implementierung von Delegaten zuwenden. Alle vordefinierten Delegaten in .NET besitzen zwei Parameter: ein Object, welches das Objekt darstellt, das das Ereignis erzeugte, und einen zweiten Parameter, der detaillierte Angaben ber das erfolgte Ereignis bereitstellt. Der gebruchlichste Delegat ist natrlich der altbekannte EventHandler. Sobald ein Ereignis auftritt, erzeugt dieser Delegat zwei Objekte (ein Object- und ein EventArgs-Objekt) und bergibt sie dem Ereignishandler oder Empfnger, der damit machen kann, was er will. Die folgenden Abschnitte befassen sich mit der Erstellung von Delegaten und benutzerdefinierten Ereignissen.

Ereignishandler
Listing 5.4 zeigt einen typischen Ereignishandler in VB .NET. Er nimmt an, dass Sie bereits ein Button-Steuerelement erzeugt und ihm einen Delegaten hinzugefgt haben, der auf die HandleClick-Methode zeigt. Beachten Sie, dass das Ereignis Button.Click den standardmigen EventHandler-Delegaten verwendet. Listing 5.4: Ein typischer Ereignishandler
1: 2: 3: private sub HandleClick(Sender as Object, e as EventArgs) Microsoft.VisualBasic.MsgBox(CType(Sender, Button).Text) end sub

Denken Sie daran, dass jeder Delegat zwei Objekte erzeugt und sie an den Ereignishandler weiterleitet. Dieser wiederum, egal ob er sie verwendet oder nicht, muss diese zwei Objekte als Parameter bernehmen, wie Zeile 1 zeigt. Das erste Objekt, das wir in Zeile 1 Sender genannt haben, stellt das Objekt dar, das das Ereignis erzeugt hat. In diesem Fall wre das Objekt das zuvor erstellte Button-Objekt. Da der Delegat, der diese Methode aufruft, vom Typ EventHandler ist, ist der zweite Parameter vom Typ EventArgs. Dieser wird dazu verwendet, weitere Informationen ber das Ereignis aufzunehmen, doch im Fall von EventArgs enthlt er nichts; er wird nur als Platzhalter eingesetzt. Die Entwickler bei Microsoft, die fr .NET und seine Klassenbibliotheken verantwortlich zeichnen, haben dankenswerterweise standardisierte Namenskonventionen verwendet. Egal wie der Name Ihres Delegaten lautet, der zweite Parameter folgt dem gleichen Namensschema. Beispiele dafr sind EventHandler und EventArgs, ScrollEventHandler und ScrollEventArgs etc.)

162

Ereignisse und Objekte

Sie knnten sich nun Folgendes fragen: Wenn der erste Parameter das das Ereignis erzeugende Objekt darstellt, warum erstellt dann nicht statt dessen der Delegat diesen Objekttyp? Zum Beispiel ein Button-Objekt in Listing 5.4 statt eines Object-Objekts? Der Grund liegt in der Flexibilitt. Wenn der Delegat einen tatschlichen Button-Typ weitergbe, dann bruchte man eine Menge Typen von Delegaten: einen fr jeden Objekttyp, der ein Ereignis erzeugen knnte. Der Standard-EventHandler-Delegat wre nicht ausreichend. Daher verkleidet der Delegat das Button-Objekt als ein Object. Eine weitere Analogie: Stellen Sie sich vor, Sie erhalten per Post ein Buch. Es drfte sich in einer Schachtel befinden. Diese Schachtel stellt das Object dar. Das Transportunternehmen steckt das Buch in eine Standardschachtel, die sich fr alles eignet. Das spart dem Unternehmen die Kosten fr die Erzeugung einer jeweils anderen Schachtel fr unterschiedliche Warentypen, die es transportiert. Sie zumindest wissen, dass sich in der Schachtel ein Buch befindet, auch wenn Sie mit der Schachtel wenig anfangen knnen, und lesen wollen Sie sie auch nicht. Sie mssen daher die Schachtel ffnen und das Buch herausholen. Grob gesagt, Sie transformieren die Schachtel in ein Buch. Etwas hnliches passiert in der Methode in Listing 5.4. Der erste Parameter wird als Object bergeben, welches ber nur wenige ntzliche Eigenschaften verfgt (siehe Tag 3). Wollen Sie also mit diesem ersten Parameter arbeiten, mssen Sie ihn daher erst in den notwendigen Typ umwandeln (mit anderen Worten: die Schachtel ffnen). In Zeile 2 des Listings 5.4 verwenden Sie die CType-Methode, um den ersten Parameter von einem Object in einen Button-Typ umzuwandeln, der dann ber die gewnschte Text-Eigenschaft verfgt. In C# verwenden Sie den Casting-Operator. Das ist schlicht ein Klammernpaar, das den Zieltyp einklammert
((Button) Sender).Text;

Sie sollten das Absender-Objekt stets ganz explizit in den gewnschten Typ umwandeln. VB .NET kann (je nach der Komplexitt des das Ereignis erzeugenden Objekttyps) diese Umwandlung automatisch ausfhren, aber es wre nicht besonders klug, sich darauf zu verlassen, dass VB .NET Ihre Arbeit verrichtet.

Benutzerdefinierte Ereignisse erstellen


Mit unserem Wissen ber das Wesen von Klassen, wie man sie einsetzt und wie man Ereignisse verarbeitet, wollen wir nun einen Schritt weitergehen. Im folgenden Abschnitt erzeugen wir eine eigene Klasse, die ber ihre eigenen Ereignisse verfgt, fr welche Sie oder jeder andere Entwickler Ereignishandler bereitstellen knnen. Da Sie noch nicht wissen, wie man seine eigenen UI-Steuerelemente erstellt, erscheint dieses Beispiel zwar etwas an den Haaren herbeigezogen, aber es ist dessen ungeachtet eine gute Lernbung (mehr ber die Erstellung eigener Steuerelemente erfahren Sie an Tag 18).

163

Ereignisse in Windows Forms

In diesem Beispiel bentigen wir zweierlei: eine benutzerdefinierte Klasse, die das benutzerdefinierte Ereignis auslst, und eine andere Klasse, die den Handler fr das benutzerdefinierte Ereignis enthlt (das kann eine gewhnliche Windows Forms-Klasse sein). Wir erzeugen eine einfache Golfball-Klasse als Klasse, die unser benutzerdefiniertes Ereignis auslst. Der Code dafr ist in Listing 5.5 zu finden. Listing 5.5: Die GlfBall-Klasse
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: using System; using System.Windows.Forms; namespace TYWinforms.Day5 { public class GolfBall { public event EventHandler Sunk; protected virtual void OnSunk(EventArgs e) { if (Sunk != null) { Sunk(this,e); } } public void Putt(int intStrength) { if (intStrength = 5) { OnSunk(new EventArgs()); } } }

In diesem Code gibt es eine Menge Neues zu entdecken. In Zeile 5 deklarieren Sie Ihre benutzerdefinierte GolfBall-Klasse. Mit dieser Klasse soll ein wirklicher Golfball nachgebildet werden. Der Einfachheit halber erzeugen wir nur eine Methode dafr, Putt, die das Einputten oder Einlochen eines Golfballs simuliert. Die Methode ist in den Zeilen 14 bis 18 zu finden (gleich mehr dazu). Logischerweise wird das einzige Ereignis nur dann ausgelst, wenn der Golfball schlielich im Loch landet. Zeile 6 deklariert das benutzerdefinierte Ereignis fr diese Klasse: Sunk (eingelocht). Beachten Sie, wie einfach diese Codezeile ist; sie besteht aus lediglich vier Wrtern. Das Ereignis wird genau wie eine Klasse deklariert, doch statt das class-Schlsselwort zu benutzen, verwenden Sie das event-Schlsselwort. Sie brauchen ihr keine weitere Funktionalitt zu verleihen, schlielich sollen nicht Sie das Ereignis behandeln, sondern der Programmierer der Klasse. Sie mssen noch den Typ des Delegaten festlegen, der das Ereignis behandeln soll. In diesem Fall verwenden wir den Standard-EventHandler-Delegaten, obwohl wir auch unseren eigenen htten verwenden knnen (mehr dazu spter).

164

Ereignisse und Objekte

Die Zeilen 8 bis 12 deklarieren eine ungewhnliche Art von Methode. Sie trgt den gleichen Namen wie unser benutzerdefiniertes Ereignis, nur dass ihr das Prfix On vorangestellt ist (z.B. OnSunk). Jedes benutzerdefinierte Ereignis muss eine entsprechende Methode OnEreignisName besitzen. Diese Methode dient nur dem einen Zweck, das Ereignis auszulsen, wie man aus Zeile 10 ersieht; keine andere Methode kann das Ereignis direkt aufrufen, sondern muss dafr ber diese On-Methode gehen. Die On-Methode ist besonders interessant. Zunchst ist festzuhalten, dass sie einen Parameter des Typs EventArgs bernimmt d.h. des Typs, der dem in Zeile 6 verwendeten Typ von EventHandler entspricht. (Sie werden gleich sehen, woher dieser Parameter stammt.) Als Nchstes ist festzuhalten, dass sie in Zeile 9 den Wert von Sunk, unserem Ereignis, auszuwerten scheint. Was bedeutet das konkret? In Zeile 6 haben Sie ein neues Ereignis deklariert, dafr aber weder eine Implementierung bereitgestellt noch eine Instanz davon erzeugt. Erinnern Sie sich daran, dass Sie von den meisten Objekten zuerst eine Instanz erzeugen mssen, bevor Sie das jeweilige Objekt benutzen knnen. Ein Ereignis funktioniert genauso. Indem Sie hier keine Instanz erzeugten, berlassen Sie dies dem Benutzer der GolfBall-Klasse. Ereignet sich etwas, erzeugt der Benutzer Ihrer GolfBall-Klasse eine Instanz, indem er dem Ereignis einen Delegaten zuweist. Etwa so:
GolfBall myGolfBall = new GolfBall(); myGolfBall.Sunk += new EventHandler(this.eineMethode)

Bis das Ereignis einen Delegaten zugewiesen bekommt, ist es nicht instantiiert und trgt daher den Wert null (oder nothing in VB .NET). Der Zweck der On-Methode besteht darin zu prfen, ob ein Delegat zugewiesen wurde. Dies erfolgt in Zeile 9. Falls Sunk, das Ereignis, nicht gleich null ist, dann bedeutet das, dass ein Delegat zugewiesen wurde und das Ereignis ausgelst werden kann. Wrden Sie diese Prfung nicht vornehmen und es wre kein Delegat zugewiesen, wrden Sie beim ersten Auslsen des Ereignisses eine Fehlermeldung erhalten. Das in Zeile 6 deklarierte Ereignis verwendet einen Delegaten vom Typ EventHandler. Bekanntlich sendet der EventHandler-Delegat (wie jeder andere Delegat) zwei Parameter an den Ereignishandler: ein Object, welches das das Ereignis auslsende Objekt darstellt, und einen EventArgs-Parameter. Welches Objekt lst also unser Ereignis aus? Natrlich das GolfBall-Objekt, jene Klasse, in der sich all dieser Code befindet. Daher ist der dem Ereignis zu bergebende Parameter lediglich this, das fr die aktuelle Klasse steht. Merke: Der erste Parameter zur Auslsung eines Ereignisses auf diese Weise ist stets this (oder me in VB .NET). Um mehr ber den zweiten Parameter zu erfahren, mssen wir zu Zeile 14 springen. Diese Methode, Putt, fhrt die OnSunk-Methode unter bestimmten Bedingungen aus. Wollten Sie irgendwelche ereignisrelevanten Informationen bergeben, wrden Sie sie

165

Ereignisse in Windows Forms

hier erzeugen. Da es aber fr dieses Ereignis keine gibt, erzeugen wir einfach einen neuen EventArgs-Parameter und bergeben ihn an die OnSunk-Methode. Lassen Sie uns zusammenfassen. Als Erstes erzeugt der Benutzer unserer GolfBall-Klasse eine Instanz dieser Klasse und ein Delegat wird dem Sunk-Ereignis zugewiesen. Der Benutzer ruft die Putt-Methode auf, um zu versuchen, den Golfball einzulochen. Sobald festgestellt wurde, dass dem Putter gengend Kraft verliehen wurde (wie durch den InStrength-Parameter angegeben wird), wird die OnSunk-Methode ausgefhrt, wobei ihr ein EventArgs-Parameter bergeben wird. Die OnSunk-Methode prft wiederum, ob ein Delegat zugewiesen wurde. Fr diesen Fall lst sie das Sunk-Ereignis aus und bergibt ihm eine Reprsentation des aktuellen Objekts und des EventArgs-Parameters. Wenn das Sunk-Ereignis ausgelst wird, veranlasst der Delegat, dass der vom Benutzer zugeordnete Ereignishandler ausgefhrt wird. Wir wollen uns als Nchstes einmal die Code-Seite des Benutzers der GolfBall-Klasse ansehen (im Gegensatz zu der des Endanwenders). Sie ist in Listing 5.6 zu sehen. Listing 5.6: Die Benutzerseite des GolfBall-Beispiels
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: public class MainForm : Form { private Button btPutt = new Button(); private Label lblStrength = new Label(); private TextBox tbStrength = new TextBox(); private GolfBall gfBall = new GolfBall(); public MainForm(){ lblStrength.Text = "Spielstrke:"; lblStrength.Location = new Point(50,50); tbStrength.Text = 0.ToString(); tbStrength.Location = new Point(125,50); btPutt.Text = "Lochen Sie den Ball ein!"; btPutt.Location = new Point(100,100); btPutt.Click += new EventHandler(this.PuttBall); gfBall.Sunk += new EventHandler(this.BallSunk); this.Text = "Beispiel fr ein angepasstes Ereignis"; this.Controls.Add(btPutt); this.Controls.Add(tbStrength); this.Controls.Add(lblStrength); } public void PuttBall(Object Sender, EventArgs e) {

166

Ereignisse und Objekte

27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38:

gfBall.Putt(Convert.ToInt32(tbStrength.Text)); } public void BallSunk(Object Sender, EventArgs e) { MessageBox.Show("Eingelocht!"); } public static void Main() { Application.Run(new MainForm()); } } }

Speichern Sie dieses und Listing 5.5 in einer einzigen Datei. In Listing 5.6 findet sich nichts Besonderes. In den Zeilen 2 bis 5 erzeugen Sie ein paar Steuerelemente, darunter auch eine Instanz der GolfBall-Klasse. Die Zeilen 8 bis 16 platzieren die Steuerelemente im Formular und stellen ein paar Eigenschaften ein. Die Steuerelemente erlauben es dem Benutzer, einen Spielstrke-Wert einzugeben und eine Schaltflche anzuklicken, um den Ball einzulochen. In Zeile 18 fgen Sie der Sunk-Methode der GolfBall-Klasse einen Delegate hinzu, genau wie Sie das bei jeder anderen Klasse tten. Die Zeilen 20 bis 23 fgen Ihrem Formular alle Steuerelemente hinzu. Die PuttBall-Methode in Zeile 26 wird immer dann ausgefhrt, wenn die in Zeile 2 deklarierte Schaltflche angeklickt wird. Sie fhrt schlicht die SunkMethode der GolfBall-Klasse aus, welche bereits in Listing 5.5 in Zeile 14 deklariert worden war. Sie bergibt den Spielstrke-Wert, den der Benutzer in das Textfeld eingegeben hat. Jedes Mal wenn das Sunk-Ereignis ausgelst wird, gelangt die BallSunk-Methode zur Ausfhrung. In unserem Fall wird Sunk aufgerufen, sobald die Putt-Methode mit einem Spielstrke-Wert von genau 5 aufgerufen wird. BallSunk (der Ball wurde eingelocht) ruft die Show-Methode der MessageBox-Klasse auf (die sich von der MsgBox-Funktion des Namensraums Microsoft.VisualBasic unterscheidet), um eine einfache Meldung anzuzeigen, die angibt, dass der Ball im Loch versenkt (daher sunk) wurde. Kompilieren Sie diese Anwendung und fhren Sie sie aus. Tragen Sie verschiedene Werte in das Textfeld ein und beachten Sie, dass nichts passiert, wenn Sie auf die Schaltflche klicken, es sei denn, Sie htten einen Spielstrke-Wert 5 eingegeben. Das Ergebnis ist in Abbildung 5.3 zu sehen.

167

Ereignisse in Windows Forms

Abbildung 5.3: Unser benutzerdefiniertes Sunk-Ereignis in voller Aktion der Benutzer gewinnt!

Steuerelemente sezieren
Aufgrund des Wissens, ber das Sie nun verfgen, knnen Sie eine Hypothese darber aufstellen, wie Objekte, die Sie kennen, implementiert werden (was Ihnen helfen wird, sobald Sie Ihre eigenen Steuerelemente erstellen). Nehmen Sie das Button-Steuerelement als Beispiel. Obwohl Sie nicht wissen, wie es genau erstellt wurde, knnen Sie aufgrund dessen, was Sie heute gelernt haben, dennoch darauf schlieen. Lassen Sie uns eine Eigenschaft, nmlich Text, und ein Ereignis, Click, genauer untersuchen (den Darstellungsvorgang wollen wir vorerst ignorieren; wir besprechen ihn an Tag 13). Wir wollen uns mit der Frage befassen, wie wir das heute Gelernte auf ntzliche Objekte anwenden knnen. Als Erstes knnen Sie davon ausgehen, dass die Text-Eigenschaft eine einfache Variable ist, die wie folgt im Quellcode des Buttons deklariert wird:
public string Text;

Als Nchstes muss die Button-Klasse das Click-Ereignis deklarieren, so wie im folgenden Codestck:
public event EventHandler Click;

Da alle Ereignisse eine korrespondierende On-Methode besitzen mssen, deklarieren wir diese im nchsten Schritt:
protected virtual void OnClick(EventArgs e) { if (Click != null) { Click(this, e); } }

Das war schon alles! In Listing 5.7 kommt alles zusammen.

168

Ereignisse und Objekte

Listing 5.7: Die extrapolierte Button-Klasse


1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: using System; using System.Windows.Forms; namespace System.Windows.Forms { public class Button : ButtonBase { public string Text; public event EventHandler Click; protected virtual void OnClick(EventArgs e){ if (Click != null){ Click(this,e); } } public Button(){ // Code zur Initialisierung und Anzeige der Schaltflche } } }

Dieser Code ist natrlich vereinfacht. Wir wissen ja, dass das Button-Steuerelement ber mehr als nur eine Eigenschaft und ein Ereignis verfgt. Tatschlich wurde das Click-Ereignis von der Control-Klasse geerbt, daher mssten wir es eigentlich nicht hier deklarieren, aber das vermittelt Ihnen eine gute Vorstellung davon, was das Erstellen von Steuerelementen bedeutet.

Benutzerdefinierte Delegaten erstellen


Wenn Sie Ihre eigenen benutzerdefinierten Ereignisse erzeugen, mssen Sie hufig auch Ihre eigenen benutzerdefinierten Delegaten und EventArgs-Objekte erzeugen. Den Delegaten zu erstellen, ist einfach; das EventArgs-Objekt ist etwas komplizierter, doch Sie sind bereits in der Lage, das anzupacken. Die Deklaration des Delegaten besteht aus einer Zeile mit dem delegate-Schlsselwort:
public delegate void MyEventHandler(Object Sender, MyEventArgs e);

Diese Zeile erzeugt einen neuen Delegaten namens MyEventHandler, der sich fr jedes Ihrer benutzerdefinierten Ereignis verwenden lsst. Er stellt einen EventArgs-Parameter bereit, ein benutzerdefiniertes Objekt, das wir ebenfalls zu erstellen haben (beide Objekte befolgen die Standardnamenskonventionen). Genau wie bei einem Ereignis mssen Sie weder eine Implementierung noch Code fr diesen Delegaten bereitstellen; die CLR erledigt das alles fr Sie.

169

Ereignisse in Windows Forms

Zum Erstellen des zweiten Parameters gehrt das Erzeugen Ihrer eigenen benutzerdefinierten Klasse mit den Eigenschaften, die Ihr Ereignis bentigt, wie in Listing 5.8 demonstriert wird: Listing 5.8: Eine benutzerdefinierte EventArgs-Klasse erstellen
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: using System; using System.Windows.Forms; namespace TYWinforms.Day5 { public class GolfBallEventArgs : EventArgs { public readonly int Meter; public readonly bool Save; public GolfBallEventArgs(int Meter,bool Save){ this.Meter = Meter; this.Save = Save; } } }

Diese Klasse namens GolfBallEventArgs werden wir nutzen, um einem Ereignishandler benutzerdefinierte Ereignisinformationen zukommen zu lassen. Sie erbt von der Klasse EventArgs, so dass wir keine Funktionen der Letzteren duplizieren mssen. In den Zeilen 6 und 7 deklarieren Sie zwei Eigenschaften: Meter, die angibt, aus welcher Entfernung der Golfball beim Einputten eingelocht wurde; und Save, die angibt, ob der Einputt-Schlag fr Par gespeichert wurde. Diese Werte sind auf readonly (Nur-Lesen) gesetzt, so dass sie sich durch nichts anderes als durch den Konstruktor selbst ndern lassen. Dieses Vorgehen ist Standard, denn schlielich gilt: Nachdem diese Werte gesetzt sind, gibt es keinen Grund mehr, sie zu ndern. In VB .NET wrden die entsprechenden Zeilen wie folgt aussehen:
ReadOnly Meter as Integer ReadOnly Save as Boolean

In Zeile 9 stoen wir auf den Konstruktor unserer GolfBallEventArgs-Klasse. Sie bernimmt zwei Parameter, nmlich Meter und Save, die mit den readonly-Eigenschaften, die gerade erstellt wurden, korrespondieren. Dieser Konstruktor dient lediglich dazu, die Parameterwerte den entsprechenden Eigenschaften der Klasse zuzuweisen. Wir wollen einmal unsere GolfBall-Klasse so ndern, dass sie diesen neuen Delegaten und das EventArgs-Objekt verwendet. Der neue Code, der auf dem modifizierten Listing 5.5 basiert, ist in Listing 5.9 zu sehen.

170

Ereignisse und Objekte

Listing 5.9: Den benutzerdefinierten Delegaten verwenden


1: using System; 2: using System.Windows.Forms; 3: using System.Drawing; 4: 5: namespace TYWinforms.Day5 { 6: public class GolfBallEventArgs : EventArgs { 7: public readonly int Meter; 8: public readonly bool Save; 9: 10: public GolfBallEventArgs(int Meter,bool Save){ 11: this.Meter = Meter; 12: this.Save = Save; 13: } 14: } 15: 16: public delegate void GolfBallEventHandler(Object Sender,  GolfBallEventArgs e); 17: 18: public class GolfBall { 19: public event GolfBallEventHandler Sunk; 20: 21: protected virtual void OnSunk(GolfBallEventArgs e){ 22: if (Sunk != null){ 23: Sunk(this,e); 24: } 25: } 26: 27: public void Putt(int intStrength){ 28: if (intStrength == 5){ 29: Random rndGen = new Random(); 30: OnSunk(new GolfBallEventArgs(rndGen.Next(1,51),  Convert.ToBoolean(rndGen.Next(0,2)))); 31: } 32: } 33: }

Die Zeilen 5 bis 14 umfassen die GolfBallEventArgs-Klasse, die wir gerade entwickelt haben (Listing 5.8). Zeile 16 enthlt die Deklaration des Delegaten, den diese Klasse verwendet. Innerhalb der GolfBall-Klasse haben nur wenige nderungen stattgefunden. In Zeile 19 deklarieren Sie das Sunk-Ereignis mit Hilfe des GolfBallEventHandler-Delegaten anstelle von EventHandler. Auf hnliche Weise verwenden Sie in den Zeilen 21 und 30 GolfBallEventArgs statt EventArgs. Diesmal verhlt sich die Putt-Methode jedoch ein wenig anders als gewohnt.

171

Ereignisse in Windows Forms

Weil sie das GolfBallEventArgs-Objekt jetzt mit zwei Werten versorgen muss, nmlich Meter und Save, mssen wir ein paar berstunden einlegen. Es geht uns nicht darum, wie die Werte aussehen, Hauptsache, sie werden zurckgegeben. Daher bergeben wir einfach ein paar zufllige Zahlen. In Zeile 29 erzeugen wir eine Instanz der Random-Klasse, die alle Arten von Methoden enthlt, die Zufallszahlen generieren, darunter auch die von uns verwendete Methode, Next. Sie bernimmt zwei Parameter, beide Integer, die einen Wertebereich angeben, aus dem eine Zufallszahl zurckgegeben wird (Next liefert einen GanzzahlInteger, ohne Dezimalstellen). Fr Meter, den ersten Parameter, whlen wir irgendeine Zahl zwischen 1 und 50 aus (im Bereich darber drfte es kaum jemanden geben, der aus dieser Distanz einputten kann!). Fr den zweiten Parameter bentigen wir einen booleschen Wert. Dieser kann True, False, 1 oder 0 sein. Daher benutzen wir Next, um eine Zufallszahl im Bereich zwischen 0 und 1 zurckzugeben (und da Next nur Ganzzahlen zurckgibt, muss das entweder 0 oder 1 sein, aber nichts dazwischen). Mit Hilfe der Convert.ToBoolean-Methode wandeln wir die zurckgegebene Zufallszahl in einen booleschen Wert um, um sie dem GolfBallEventArgsKonstruktor zu bergeben. Wir knnen den Ereignishandler fr das Sunk-Ereignis so ndern, dass er dieses Objekt verwendet. ndern Sie mit Hilfe der gleichen MainForm-Klasse aus Listing 5.6 die Zeile 31 wie folgt:
MessageBox.Show("Eingelocht aus " + e.Meter.ToString() + " Metern Entfernung!");

Abbildung 5.4 zeigt das Ergebnis dieser letzten nderung.

Abbildung 5.4: Unser benutzerdefiniertes Sunk-Ereignis mit dem Objekt GolfBallEventArgs gibt dem Benutzer Anlass zum Jubeln!

172

Zusammenfassung

5.4

Zusammenfassung

Ereignisse sind ein zentraler Bestandteil einer Windows Forms-Anwendung. Ohne sie wrde man nichts damit erledigen knnen. Wie das Ereignissystem funktioniert, wissen Sie nun sowohl aus der Perspektive des Entwicklers als auch aus der des Benutzers. Ein Ereignishandler ist diejenige Methode, die ausgefhrt wird, sobald ein Ereignis stattfindet. Zur Erstellung des Handlers gehrt das Verknpfen von Ereignis und Handler mit Hilfe der AddHandler-Methode in VB .NET, oder mit dem +=-Operator in C#. Ereignishandler weisen eine Standardsignatur auf, die nur wenige Variationen zulsst. Ein Delegat ist eine Klasse, die eine Referenz auf einen Ereignishandler enthlt. Er stellt die Drhte zwischen einem Ereignis und seinen Handlern dar. In .NET befolgen alle Delegaten das Namensschema NameEventHandler. Delegaten bergeben den Ereignishandlern zwei Parameter: ein Object-Objekt, das das ereignisauslsende Objekt darstellt, und einen EventArgs-Parameter, der zustzliche Informationen bereitstellt. Der EventArgsParameter befolgt das gleiche Namensschema wie der Delegat. Um Ihr eigenes (benutzerdefiniertes) Ereignis zu erstellen, mssen Sie es lediglich mit Hilfe des event-Schlsselwortes deklarieren und einen passenden Delegaten dafr whlen. Jedes Ereignis muss eine entsprechende OnEreignisName-Methode besitzen, die zur Auslsung des Ereignisses verwendet wird. Diese Methode kann ebenso prfen, ob ein Delegat verdrahtet wurde, indem sie berprft, ob das Ereignis einen Wert null (oder nothing in VB .NET) ergibt. Mit Hilfe des delegate-Schlsselworts knnen Sie Ihre eigenen Delegaten auf die gleiche Weise erstellen wie ein benutzerdefiniertes Ereignis. Ein Delegat bentigt weder Implementierung noch Code die CLR erzeugt beides fr Sie. Ihre benutzerdefinierten Delegaten knnen jeweils einen vordefinierten EventArgs-Parameter verwenden, oder Sie knnen Ihren eigenen erstellen, indem Sie eine Klasse erzeugen, die vom EventArgs-Objekt erbt. Ihr benutzerdefiniertes EventArgs-Objekt muss nur die benutzerdefinierten readonlyEigenschaften bereitstellen, die Sie wollen, sowie einen Konstruktor, um diesen Eigenschaften Werte zuweisen zu knnen.

5.5
F A

Fragen und Antworten


Leider gibt es keine leichte Methode das herauszufinden. In den meisten Fllen verwendet man den Standard-EventHandler-Delegaten, so dass man damit auf der sicheren Seite ist. Ist dies aber nicht der korrekte Delegat, dann wird Ihnen normalerweise Ihr Compiler (Visual Studio .NET oder die Befehlszeilencompiler

Woher wei man, welche Art von Delegat man fr ein Ereignis benutzen muss?

173

Ereignisse in Windows Forms

csc.exe und vbc.exe) mitteilen, welchen Sie verwenden sollten. In diesem Buch weisen wir normalerweise auf den passenden Delegaten darauf hin.

Kann ich einem einzelnen Ereignis mehr als einen Delegaten zuweisen? A Durchaus. Sie drfen dies tun, wenn Sie wnschen, dass ein Einzelereignis mehr als eine Aktion ausfhrt. In unserem Vergleich mit dem Auto entsprche dies einer Ihrer Aktionen (sagen wir, Sie treten auf das Gaspedal), aber mehreren Aktionen des Wagens (bewegt sich, die Tachonadel zittert, Ausschalten des Tempomaten usw.). Das ist aber keine gebruchliche Praxis, da normalerweise ein Ereignishandler alle gewnschten Aktionen ausfhren kann. Es ist jedoch eine schlaue Mglichkeit, Ereignishandler zu erweitern. Mal angenommen, Sie htten mit ShowText und ShowTextAndAlert zwei Ereignisse. Sie mssen Text in einem Bezeichnungsfeld anzeigen, doch das zweite Ereignis muss auerdem ein Meldungsfenster mit einer Warnung anzeigen. Es besteht kein Grund dafr, den Code, der den Text zur Anzeige bringt, zu duplizieren. Daher kann man beiden Ereignissen einen einzigen Delegaten zuweisen. Das ShowTextAndAlert-Ereignis wrde einen weiteren Delegaten zugewiesen bekommen, der aber das Anzeigen des Meldungsfeldes veranlasst. Sobald das ShowTextAndAlert-Ereignis ausgelst wird, wrden beide Delegaten ausgefhrt werden, so dass Text angezeigt und ein Meldungsfeld geffnet wird. Die Ereignishandler werden in der Reihenfolge ausgefhrt, die ihnen die Delegaten zuweisen.

5.6

Workshop

Dieser Workshop soll Ihnen helfen, sich besser an die heute behandelten Begriffe zu erinnern. Es hilft Ihnen sehr, die Antworten vollstndig zu verstehen, bevor Sie weiterlesen. Die Antworten zu den Quizfragen und den bungen finden Sie in Anhang A.

Quiz
1. Wie sieht die standardmige Signatur eines Ereignishandlers aus? 2. Erzeugen Sie in C# ein Ereignis namens ShowText, das den Delegaten KeyPressEventHandler verwendet. 3. Erstellen Sie einen benutzerdefinierten Delegaten, der das Objekt KeyPressEventArgs verwendet.

174

Workshop

4. Wahr oder falsch? Um Objekttypen miteinander zu vergleichen, verwendet man den Gleichheitszeichenoperator (=). 5. Welche der folgenden Aussagen ist inkorrekt?
AddHandler btOne.Click, new EventHandler(AddressOf btOne_Click) public sub MyEventHandler(Sender as Object, e as EventArgs) Handles btTemp.Click Addhandler btOne.Click += new EventHandler(AddressOf btOne_Click)

6. Warum sollten die Eigenschaften eines benutzerdefinierten EventArgs-Objekts readonly sein? 7. Ist das folgende Codestck korrekt? Was sollte man falls ntig ndern?
public virtual void OnMyEvent(EventArgs e) { MyEvent(this, e); }

8. An welcher Stelle (im Quellcode) muss man benutzerdefinierte Ereignisse und Delegaten deklarieren?

bung
Erstellen Sie in C# eine Taschenrechner-Anwendung wie den Windows-Taschenrechner. Er sollte Zifferntasten aufweisen, mit denen sich Zahlen eingeben lassen, sowie Funktionstasten, die Berechnungen vornehmen, wenn man darauf klickt. Verwenden Sie einen Ereignishandler fr alle Zifferntasten und einen Handler fr alle Funktionstasten. (Tipp: Nehmen Sie verborgene Steuerelemente, also solche, deren Visible-Eigenschaft auf False gesetzt ist damit knnen Sie temporre Variablen speichern.)

175

Windows Forms mit Steuerelementen erweitern

Windows Forms mit Steuerelementen erweitern

In der heutigen Lektion runden Sie Ihr wachsendes Repertoire an Windows Forms-Steuerelementen ab. Bis jetzt haben Sie nur Mens, Bildlaufleisten, Statusleisten, Symbolleisten, Textbox, Bezeichnungsfelder und Schaltflchen als Steuerelemente zu sehen bekommen. Heute erfahren Sie etwas ber Kombinationsfelder und Optionsfelder, die dem Benutzer mehrere Auswahlmglichkeiten bereitstellen, sodann Timer, die sich zur Zeitmessung von Ereignissen verwenden lassen, und schlielich Strukturansichten (Treeviews), mit denen der Benutzer das Dateisystem erkunden kann, und viele andere Steuerelemente. Sie lernen darber hinaus, wie man die Windows Forms-Merkmale fr automatisches Layout nutzt, um eine bersichtlichere und leichter zu pflegende Benutzeroberflche zu prsentieren. Heute lernen Sie, wie Sie

vom Benutzer Informationen einholen und sie auf logische Art und Weise anzeigen, den Benutzer zwingen, Entscheidungen zu treffen, Benutzereingaben formatieren, Grafiken in Ihren Anwendungen anzeigen, zeitmessende Anwendungen erstellen , viele verfgbare Aufzhlungen nutzen, um Ihre Steuerelemente anzupassen, die Positionierung Ihrer Steuerelemente steuern.

Wenn Sie die heutige Lektion durcharbeiten, sollten Sie an ein paar Schlsselaspekte bezglich der Windows Forms-Steuerelemente denken. Jedes Windows Forms-Steuerelement im .NET Framework erbt direkt oder indirekt von der Control-Klasse, welche wiederum von der Object-Klasse erbt. Das bedeutet, dass diese Steuerelemente eine ganze Reihe von Eigenschaften und Methoden gemeinsam haben, darunter die GetTypeMethode, die Height- und Width-Eigenschaften, das Click-Ereignis und andere, die Sie heute kennen lernen. (Um eine vollstndige Aufzhlung dieser gemeinsamen Mitglieder zu erhalten, schlagen Sie bitte in Anhang B nach.) All diese Steuerelemente haben auch Regeln fr ihre Verwendung gemeinsam. Sie werden sehen, dass Sie nach dem Erlernen des Umgangs mit einem Steuerelement auch mit den anderen gut zurechtkommen werden. Natrlich verfgt jedes Steuerelement ber seine Eigenarten und eigenen Mitglieder, aber sie folgen alle dem gleichen Muster.

178

Das Steuerelement Button

6.1

Das Steuerelement Button

Das Steuerelement Button kennen Sie ja bereits. Seine grundlegendste Eigenschaft ist Text. Sie setzt die Aufschrift, die auf der Schaltflche angezeigt wird, um dem Benutzer einen Hinweis darauf zu geben, was die Schaltflche bewirkt. TextAlign positioniert die Aufschrift auf der Schaltflche und kann die folgenden Werte der ContentAlign-Aufzhlung annehmen: BottomCenter, BottomLeft, BottomRight, MiddleCenter, MiddleLeft, MiddleRight, TopCenter, TopLeft und TopRight ein Wert fr jede Position auf der Schaltflche. Die FlatStyle-Eigenschaft gestattet Ihnen die Anpassung des Aussehens der Schaltflche. Die Eigenschaft kann einen der Werte in der FlatStyle-Aufzhlung annehmen: Flat, Popup, wobei die Schaltflche flach aussieht, bis Sie mit der Maus darber geraten, Standard als Standardwert oder System, bei dem sich das Aussehen der Schaltflche nach dem Standard Ihres Betriebssystems richtet (wobei dieser Wert einem der drei vorhergehenden Werte entsprechen muss). Abbildung 6.1 zeigt die ersten drei Stile sowie die Verwendung der TextAlign-Eigenschaften.

Abbildung 6.1: Beachten Sie, dass sich die Beschriftung der Schaltflchen in diesen Beispielen fr FlatStyle-Werte jeweils an einer anderen Position befindet. Das ist eine Funktion der Eigenschaft TextAlign.

Mit Button-Steuerelementen knnen Sie Grafiken auf zweierlei Weise darstellen. Sie knnen entweder die Eigenschaften Image und ImageAlign verwenden oder die Eigenschaften ImageList und ImageIndex (wie bereits bei den Symbolleisten an Tag 4). ImageAlign verwendet die gleichen Werte der ContentAlign-Aufzhlung wie TextAlign, wohingegen Image ein Image-Objekt bernimmt:
button1.Image = Image.FromFile("C:\WinForms\Day6\MyBitmap.bmp")

Sie wissen bereits, wie man Click, das Hauptereignis eines Button-Steuerelements verarbeitet. Das folgende Codestck zeigt die Zuweisung eines Delegaten zur Click-Methode und einen exemplarischen Ereignishandler:
AddHandler Button1.Click, new EventHandler(AddressOf Me.Button1_Click) ... private sub Button1_Click(Sender as Object, e as EventArgs) MessageBox.Show("Sie haben angeklickt: " & Ctype(Sender, Button).ToString end sub

Das Button-Steuerelement besitzt die Methode PerformClick, die Ihnen die Simulation eines Schaltflchen-Klicks gestattet. Sie knnen also das Click-Ereignis genauso auslsen,

179

Windows Forms mit Steuerelementen erweitern

als htte ein Benutzer die Schaltflche angeklickt. Dieser Methode mssen Sie keine Parameter bereitstellen; die CLR wird sie fr Sie generieren:
Button1.PerformClick

Das ist recht ntzlich, wenn Sie einmal den Handler des Click-Ereignisses aufrufen mssen; durch PerformClick brauchen Sie sich nicht mehr um die Erstellung von EventArgsObjekten usw. zu kmmern.

6.2

Die Steuerelemente CheckBox und RadioButton

Die Steuerelemente CheckBox und RadioButton sind einander sehr hnlich. Beide gestatten dem Benutzer, eine (oder mehrere) Auswahl(en) aus einer Gruppe hnlicher Elemente zu treffen. Wahrscheinlich haben Sie diese Steuerelemente bereits einmal beim Ausfllen eines Webformulars benutzt. Sie unterscheiden sich dadurch, dass es das Kombinationsfeld CheckBox dem Benutzer gestattet, mehr als ein Element der Angebotsliste auszuwhlen, whrend das Optionsfeld RadioButton nur eine Auswahl gestattet. Listing 6.1 zeigt ein Beispiel fr die Verwendung dieser Steuerelemente. Listing 6.1: Ein Beispiel fr den Einsatz der Steuerelemente RadioButton und CheckBox
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: Imports System Imports System.Windows.Forms Imports System.Drawing namespace TYWinforms.Day6 public class Listing61 : Inherits Form private lblColor as new Label private lblSex as new Label private private private private private private chkColorBlue as new CheckBox chkColorRed as new CheckBox chkColorYellow as new CheckBox chkColorGreen as new CheckBox rbSexMale as new RadioButton rbSexFemale as new RadioButton

public sub New lblColor.Text = "Whlen Sie Ihre Lieblingsfarbe(n)" lblColor.Location = new Point(10,10) lblColor.AutoSize = True

180

Die Steuerelemente CheckBox und RadioButton

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:

chkColorBlue.Text = "Blau" chkColorBlue.Location = new Point(10,25) chkColorBlue.Width = 50 chkColorRed.Text = "Rot" chkColorRed.Location = new Point(60,25) chkColorRed.Width = 50 chkColorYellow.Text = "Gelb" chkColorYellow.Location = new Point(110,25) chkColorYellow.Width = 60 chkColorGreen.Text = "Grn" chkColorGreen.Location = new Point(170,25) chkColorGreen.Width = 60 lblSex.Text = "Whlen Sie Ihr Geschlecht" lblSex.Location = new Point(10,50) lblSex.AutoSize = True rbSexMale.Text = "Mnnlich" rbSexMale.Location = new Point(10,75) rbSexMale.Width = 50 rbSexFemale.Text = "Weiblich" rbSexFemale.Location = new Point(60,75) rbSexFemale.Width = 60 Me.Controls.Add(lblColor) Me.Controls.Add(lblSex) Me.Controls.Add(chkColorBlue) Me.Controls.Add(chkColorRed) Me.Controls.Add(chkColorYellow) Me.Controls.Add(chkColorGreen) Me.Controls.Add(rbSexMale) Me.Controls.Add(rbSexFemale) end sub public shared sub Main() Application.Run(new Listing61) end sub end class end namespace

181

Windows Forms mit Steuerelementen erweitern

Dieses Listing ist sehr einfach aufgebaut. Alle hier verwendeten Steuerelemente werden in den Zeilen 8 bis 16 deklariert, und die Eigenschaften Text, Location und Width werden in den Zeilen 19 bis 49 festgelegt. Die Steuerelemente werden dann dem Formular in den Zeilen 51 bis 58 hinzugefgt. Listing 6.1 produziert das in Abbildung 6.2 dargestellte Ergebnis.

Abbildung 6.2: Die Steuerelemente RadioButton und CheckBox sind in Formularen sehr ntzlich. CheckBox-Steuerelemente erlauben Ihnen, jede beliebige Kombination von Kstchen im Containersteuerelement auszuwhlen, welches in diesem Fall das Formular ist. RadioButtons andererseits erlauben Ihnen nur eine Auswahl je bergeordnetem Steuerelement also pro Formular in diesem Fall. Um getrennte Gruppen von RadioButtons mit nur jeweils

einem whlbaren Wert zu erstellen, gruppieren Sie die Steuerelemente durch Positionierung auf einem Panel-Steuerelement. (Ein Panel-Steuerelement gestattet lediglich die Gruppierung von Steuerelementen; fassen Sie einfach jeden Satz von RadioButton-Steuerelementen, die denselben Wert beschreiben, auf einem separaten Panel-Steuerelement zusammen.) RadioButtons, die sich auf verschiedenen bergeordneten Steuerelementen befinden, schlieen sich gegenseitig aus. Die Steuerelemente CheckBox und RadioButton haben ein paar Eigenschaften gemein. Die Checked-Eigenschaft lsst sich dazu verwenden, zu prfen oder zu bestimmen, ob das angegebene Steuerelement ausgewhlt ist. Die CheckAlign-Eigenschaft hnelt der TextAlignEigenschaft des Button-Steuerelements. Sie prft, wie das Auswahlfeld bezglich des Textes ausgerichtet ist. CheckAlign verwendet ebenfalls die ContentAlignment-Aufzhlung. Die AutoCheck-Eigenschaft stellt fest, ob sich das Aussehen des Steuerelements automatisch ndern sollte, wenn es angeklickt wird. Sollte also ein Hkchen erscheinen, sobald die Auswahl angeklickt wurde? Setzen Sie diese Eigenschaft auf False, wenn Sie anhand der Checked-Eigenschaft das Steuerelement selbst mit einem Hkchen versehen wollen. Der typische Fall dafr trte ein, wenn eine Art von Gltigkeitsprfung erfolgen msste, bevor

182

Das Steuerelement ComboBox

ein Steuerelement angeklickt werden darf (etwa bei der Gltigkeitsprfung von Benutzereingaben). Auch Appearance ist eine vielen Steuerelementen gemeinsame Eigenschaft. Mit Hilfe von Werten aus der Appearance-Aufzhlung (Button oder Normal) bestimmt sie, ob das Steuerelement normal aussieht (Voreinstellung) oder als Schaltflche erscheint, die sich ausoder einschalten lsst. Die Funktionalitt ist die gleiche, doch dies bietet dem Benutzer ein leicht verndertes Erscheinungsbild der Benutzeroberflche. Die beiden Steuerelemente haben ein Ereignis gemein: CheckChanged. Wie der Name schon andeutet, tritt es auf, sobald das Kontrollhkchen fr das Steuerelement gesetzt oder entfernt wird. Auf Grund seiner etwas komplexeren Funktionsmerkmale verfgt das CheckBox-Steuerelement ber einige weitere Eigenschaften. Die CheckState-Eigenschaft hnelt der CheckedEigenschaft, bietet aber etwas mehr Mglichkeiten. Sie muss einen der Werte aus der CheckState-Aufzhlung annehmen: Checked, Unchecked oder Indeterminate. Die ersten beiden bieten die gleiche Funktion wie die Checked-Eigenschaft (s.o.), whrend Indeterminate einen dritten Zustand des Steuerelements beschreibt; das Steuerelement ist mit Hkchen versehen, doch grau dargestellt, so dass es den Eindruck vermittelt, nur teilweise angekreuzt zu sein. Fgen Sie in Listing 6.1 nach Zeile 25 folgende Zeile hinzu und kompilieren Sie die Anwendung neu, um diesen Zustand zu sehen:
chkColorBlue.CheckState = CheckState.Indeterminate

Per Voreinstellung knnen Sie diesen unbestimmten Zustand nur im Code auswhlen. Wenn Sie jedoch die ThreeState-Eigenschaft auf true setzen, dann kann auch der Benutzer auf diesen Zustand umschalten. Um mit der CheckState-Eigenschaft fortzufahren: Das Ereignis CheckStateChanged wird ausgelst, sobald diese Eigenschaft sich ndert, was doch dem Vorgang bei CheckChanged recht hnlich ist, wenn sich die Eigenschaft Checked ndert. Da sowohl CheckBox als auch RadioButton von derselben Klasse erben wie das ButtonSteuerelement (ButtonBase), teilen sie viele der gleichen Eigenschaften: Text, TextAlign, PerformClick und FlatStyle, um nur ein paar Beispiele zu nennen.

6.3

Das Steuerelement ComboBox

Das ComboBox-Steuerelement (Kombinationsfeld) ist recht interessant; es kombiniert ein editierbares Textfeld (wie eine TextBox) mit einer Dropdown-Liste, aus der der Benutzer vorgegebene Optionen whlen kann. Wegen dieser zweifachen Funktionalitt verfgt das Steuerelement ber eine Reihe von Eigenschaften, um sein Aussehen zu bestimmen und ihm Wahlmglichkeiten zuzuweisen.

183

Windows Forms mit Steuerelementen erweitern

Das Kombinationsfeld instantiieren


Das Erstellen dieses Steuerelements verluft wie bei jedem anderen auch:
dim cboMyList as new ComboBox

Sie knnen die Eigenschaften Top, Left oder Location dazu verwenden, das Steuerelement im Formular zu positionieren, sowie die Eigenschaften Font, BackColor und andere, allen Steuerelementen zugngliche Eigenschaften, um sein Aussehen anzupassen. Sie knnen sogar die Text-Eigenschaft nutzen, um eine Zeichenfolge in der Textbox-hnlichen Sektion des Steuerelements anzuzeigen. Bei den interessanteren Eigenschaften geht es jedoch um das Fllen der Dropdown-Liste. Die einfachste Mglichkeit, dem Kombinationsfeld solche Elemente hinzuzufgen, besteht in der Verwendung der Items-Auflistung und der Methoden Add oder AddRange. Die erste fgt der Liste ein einzelnes Element hinzu, die zweite ein komplettes Array von Objekten. Listing 6.2 demonstriert beide Methoden. Listing 6.2: Einer Dropdown-Liste in einer ComboBox neue Elemente hinzufgen
1: Imports System 2: Imports System.Windows.Forms 3: Imports System.Drawing 4: 5: namespace TYWinforms.Day6 6: 7: public class Listing62 : Inherits Form 8: private cboMyList as new ComboBox 9: 10: public sub New 11: cboMyList.Location = new Point(75,50) 12: cboMyList.Text = "Whlen Sie ein Element aus" 13: cboMyList.Items.Add("Single Item") 14: cboMyList.Items.AddRange(new Object() {"Erster im Bereich",  "zweiter im Bereich ", "Dritter im Bereich "}) 15: 16: Me.Text = "Listing 6.2" 17: Me.Controls.Add(cboMyList) 18: end sub 19: 20: public shared sub Main() 21: Application.Run(new Listing62) 22: end sub 23: end class 24: end namespace

184

Das Steuerelement ComboBox

Wir fangen an, indem wir in den Zeilen 13 und 14 Elemente hinzufgen (gleich mehr dazu). Zeile 12 legt den Text fest, der im Textfeld des Steuerelements angezeigt wird. Dieser Text erscheint, falls kein anderes Element der Liste ausgewhlt wurde. Auf diese Weise fungiert er stellvertretend als Standardwert oder -auswahl. Zeile 13 fgt der Liste mit Hilfe der Add-Methode ein einzelnes Element hinzu. Zeile 14 fgt der ComboBox ein Array hinzu. Sowohl die Add- als auch die AddRange-Methode bernehmen Object-Datentypen als Parameter. Hier erzeugen Sie Objekte vom Typ string, dem gebruchlichsten Typ, doch Sie knnen leicht andere Typen hinzufgen. Wenn Sie der ComboBox wirklich einen anderen Objekttyp hinzufgen, dann stellen Sie bitte sicher, dass die Eigenschaft DisplayMember der Eigenschaft des Objekts entspricht, das Sie anzeigen wollen. So zum Beispiel die Text-Eigenschaft eines Button-Objekts:
cboMyList.Items.Add(new Button()) cboMyList.DisplayMember = "Text"

Wenn Sie jedoch keine Eigenschaft fr die Anzeige angeben, fhrt die CLR automatisch die ToString-Methode am fraglichen Objekt aus und zeigt das Ergebnis an. Beachten Sie bitte, dass Zeile 14 ein neues Object-Array erzeugt und es in derselben Zeile mit Werten fllt. Sie htten anstelle dieses Codes auch folgende Zeilen als Alternative verwenden knnen, um das Gleiche zu erzielen:
dim arrList as Object() = {"erstes im Bereich", "zweites im Bereich", "drittes im Bereich"} cboMyList.Items.AddRange(arrList)

Die Wahl zwischen diesen beiden Vorgehensweisen ist Ihnen berlassen. Abbildung 6.3 zeigt das Ergebnis des Listings. Statt die AddRange-Methode zu verwenden, um mehrere Werte auf einmal hinzuzufgen, knnen Sie auch die DataSource-Eigenschaft auf ein Objekt-Array setzen. Das folgende Codestck bewirkt das Gleiche wie Zeile 14 in Listing 6.2:
cboMyList.DataSource = new Object() {"erstes im Bereich", "zweites im Bereich", "drittes im Bereich"}

Mit Hilfe von DataSource knnen Sie die Elemente der ComboBox leicht auf jede beliebige Auflistung von Objekten setzen. An Tag 9 werden Sie eine weitere Einsatzmglichkeit fr diese Eigenschaft im Zusammenhang mit Datenbanken kennen lernen. Wenn Sie der ComboBox mit Hilfe der Add-Methode viele Elemente hinzufgen, rufen Sie als Erstes die BeginUpdate-Methode auf:
cboMyList.BeginUpdate()

185

Windows Forms mit Steuerelementen erweitern

Diese Methode zwingt die Anwendung, mit dem Aktualisieren der Oberflche der ComboBox so lange zu warten, bis alle Elemente hinzugefgt worden sind. Das Aktualisieren der Benutzeroberflche ist ein zeitraubender Vorgang, und wenn Sie viele Elemente auf einmal hinzuzufgen haben, knnten die Aktualisierungen Ihre Anwendung praktisch zum Stillstand bringen. Wenn Sie mit dem Hinzufgen von Elementen fertig sind, rufen Sie die EndUpdate-Methode auf:
cboMyList.EndUpdate()

Abbildung 6.3: Hier knnen Sie das Einzelelement ebenso sehen wie die Bereichselemente, die wir unserem Beispiel fr das ComboBoxSteuerelement hinzugefgt haben.

Die Anzeige des Steuerelements steuern


DropDownStyle ist die erste Eigenschaft, die Sie kennen mssen, um die Anzeige der ComboBox zu steuern. Diese Eigenschaft kontrolliert das Aussehen und die Bedienung des Steuerelements und kann die Art und Weise der Interaktion des Benutzers mit der ComboBox ndern. Diese Eigenschaft muss auf einen der Werte der ComboBoxStyle-Aufzhlung gesetzt

sein:

DropDown: Das ist der Standardwert. Der Benutzer kann den Textbereich bearbeiten und muss einen Pfeil anklicken, um die Dropdown-Inhalte ansehen zu knnen. DropDownList: Der Benutzer kann den Textbereich nicht bearbeiten. Der DropdownBereich ist der gleiche wie beim DropDown-Wert. Simple: Der Benutzer kann den Textbereich bearbeiten und die Dropdown-Liste wird

stets angezeigt (es gibt also keinen anzuklickenden Pfeil, um die Liste aufzuklappen, obwohl eine Bildlaufleiste vorhanden sein kann). Sobald Sie diese Eigenschaft festgelegt haben, gibt es eine Reihe weiterer Eigenschaften, mit denen sich das Aussehen des Textbereichs anpassen lsst. MaxLength legt die Anzahl derjenigen Zeichen fest, die maximal in das Textfeld eingegeben werden knnen (wenn

186

Das Steuerelement ComboBox

aber der Benutzer ein Element aus der Liste auswhlt, das mehr Zeichen hat als MaxLength erlaubt, wird trotzdem der Gesamttext des Elements angezeigt). SelectedText gibt immer den momentan im Textbereich markierten Wert zurck. Um zu steuern, welcher Teil des angezeigten Textes markiert wird, verwenden Sie die Eigenschaften SelectionStart und SelectionLength. Die erste bernimmt einen Integer, der das Zeichen angibt, mit dem die Markierung beginnt. Die zweite Eigenschaft bestimmt, wie viele Zeichen nach der SelectStart-Position markiert werden. Die Methoden Select und SelectAll erlauben Ihnen, einen Teil oder die Gesamtheit des Textes im Textbereich zu markieren. SelectAll bernimmt keine Parameter, aber Select bernimmt zwei Integerwerte, die den zu markierenden Zeichenbereich angeben. Text zu markieren ist hufig ntzlich, wenn es notwendig ist, Elemente in der ComboBox zu aktualisieren oder neue einzufgen. Auch die Dropdown-Liste verfgt ber mehrere eigene Eigenschaften. DroppedDown zeigt an, ob die Dropdown-Liste gerade angezeigt wird. DropDownWidth gibt hnlich wie MaxLength die Anzahl der Zeichen an, die in der Liste angezeigt werden knnen. Die Maeinheit lautet hier allerdings Pixel statt Zeichenzahl. MaxDropDownItems gibt die Anzahl der Elemente an, die auf einmal angezeigt werden, falls der Benutzer auf die Dropdown-Liste klickt. Wenn sich in der Liste mehr Elemente befinden als MaxDropDownItems, erscheint an der Seite der Liste eine Bildlaufleiste, um dem Benutzer zu gestatten, mehr Elemente anzusehen. Zwei Eigenschaften betreffen die Hhe des Kombinationsfeldes. ItemHeight gibt die Hhe jedes Listenelements in Pixeln an. Diese Eigenschaft erlaubt keine Anpassung der Elementhhe, nur die Betrachtung der Elemente (es sei denn, Sie berschreiben das Standardverhalten des Steuerelements, worber Sie an Tag 18 mehr erfahren). Der Wert der Eigenschaft PreferredHeight wird vom Betriebssystem des Benutzers zugewiesen und gibt die tatschliche Hhe des Steuerelements an. Schlielich knnen Sie noch die Sorted-Eigenschaft nutzen, um der ComboBox zu befehlen, die Listenelemente automatisch (alphabetisch) zu sortieren. Dies gilt sowohl fr vorhandene Elemente als auch fr jedes neu hinzugefgte Element. Wollen Sie diese Funktion, dann setzen Sie Sorted auf True, wenn nicht, dann auf False.

Die ComboBox verwenden


Da Sie nun dem Benutzer alle Werte in der ComboBox zur Auswahl stellen, mssen Sie wissen, wie Sie die Auswahl kontrollieren und berwachen. Zwei der gebruchlichsten ComboBox-Mitglieder sind die SelectedIndex-Eigenschaft und das SelectedIndexChangedEreignis. Listing 6.3 zeigt ein Beispiel, in dem diese Mitglieder eingesetzt werden.

187

Windows Forms mit Steuerelementen erweitern

Listing 6.3: Die Auswahl, die der Benutzer getroffen hat, berwachen
1: Imports System 2: Imports System.Windows.Forms 3: Imports System.Drawing 4: 5: namespace TYWinforms.Day6 6: 7: public class Listing63 : Inherits Form 8: private cboMyList as new ComboBox 9: 10: public sub New 11: cboMyList.Location = new Point(75,50) 12: cboMyList.Text = " Whlen Sie ein Element!" 13: dim arrList as Object() = {"erstes im Bereich", "zweites im  Bereich", "drittes im Bereich "} 14: cboMyList.Items.AddRange(arrList) 15: AddHandler cboMyList.SelectedIndexChanged,  new EventHandler(AddressOf Me.HandleSelect) 16: 17: Me.Text = "Listing 6.3" 18: Me.Controls.Add(cboMyList) 19: end sub 20: 21: public sub HandleSelect(Sender as Object, e as EventArgs) 22: MessageBox.Show("Sie haben folgendes Element ausgewhlt: " &  cboMyList.SelectedIndex.ToString) 23: end sub 24: 25: public shared sub Main() 26: Application.Run(new Listing63) 27: end sub 28: end class 29: end namespace

Dieses Listing hnelt dem Listing 6.2. Die ComboBox wird in den Zeilen 13 und 14 gefllt, wobei diesmal nur ein Array benutzt wird. In Zeile 15 wird dann dem SelectedIndexChanged-Ereignis ein Delegat zugewiesen. Er tritt nur dann in Aktion, wenn der Benutzer ein neues Listenelement auswhlt. Der Handler HandleSelect fr dieses Ereignis beginnt in Zeile 21. Er besteht aus einer einzigen Codezeile. Mit Hilfe der Methode MessageBox.Show zeigen Sie dem Benutzer eine Meldung an, in der Sie angeben, welches Listenelement er ausgewhlt hat. Denken Sie daran, dass alle Listen in .NET nullbasiert sind (d.h. die Zhlung ihrer Elemente beginnt

188

Das Steuerelement DateTimePicker

bei 0 statt bei 1), so dass bei Auswahl des zweiten Elements durch den Benutzer das Meldungsfeld Element 1 als ausgewhlt zeigen wird. Abbildung 6.4 zeigt das Ergebnis, das Listing 6.3 produziert.

Abbildung 6.4:
SelectedIndex zeigt die Indexnummer des ausgewhlten

Listenelements an.

In den meisten Fllen werden Ihnen diese beiden Mitglieder gengend Kontrolle ber das Steuerelement geben. Es gibt noch eine Reihe weiterer ntzlicher Methoden: FindString und FindStringExact. Die erste Methode sucht und liefert den Index fr das erste Listenelement, das mit der angegebenen Zeichenfolge beginnt. In Listing 6.3 wrde folgendes Codestck die Zahl 0 zurckgeben:
cboMyList.FindString("1st") FindStringExact hingegen gibt das erste Listenelement zurck, das der (gesuchten) Zei-

chenfolge exakt entspricht. Das obige Codestck wrde nichts zurckgeben, weil es keine Elemente gibt, die nur den Text 1st enthalten. Diese Methode erweist sich als ntzlich, wenn man in einer ComboBox nach einem bestimmten Wert suchen muss. Insbesondere ist sie fr den Benutzer von Vorteil, wenn die Liste recht umfangreich ist. Kurzum: Die ComboBox ist ein sehr leistungsfhiges Steuerelement; Sie mssen blo wissen, wie Sie es einsetzen.

6.4

Das Steuerelement DateTimePicker

Gem der Voreinstellung verhlt sich das Steuerelement DateTimePicker recht hnlich wie die ComboBox; es verfgt ber einen editierbaren Textbereich und eine aufklappbare Liste. Doch besteht der Textbereich aus einem nderbaren Datum und die Liste aus einem interaktiven Kalender. Abbildung 6.5 zeigt ein typisches DateTimePicker-Steuerelement. Dieses Steuerelement prsentiert uns eine sehr leistungsfhige Benutzeroberflche. Der Benutzer kann im Steuerelement das Datum ndern, indem er es direkt in den Textbe-

189

Windows Forms mit Steuerelementen erweitern

reich eintrgt oder indem er eines aus dem Kalenderbereich auswhlt. Der Kalenderbereich verfgt ber Schaltflchen, mit deren Hilfe der Benutzer monatsweise vor und zurck springen kann. Er kann auch Monate auswhlen, indem er nach einem Klick auf die Titelleiste aus der herunterklappenden Liste einen Monat auswhlt. Das Beste aber kommt noch: Der DateTimePicker gestattet nur die Auswahl oder Eingabe von realen, gltigen Daten. Daher kann ein Benutzer niemals das Datum 57. Macomber 1345 eingeben, wie sehr er es auch versuchen mag.

Abbildung 6.5: Ein DateTimePicker stellt Ihnen Daten und Uhrzeiten zur Auswahl bereit.

Der DateTimePicker weist eine Reihe von Eigenschaften auf, die sich zur Anpassung des Kalenderaussehens nutzen lassen:
Eigenschaft
CalendarFont CalendarForeColor CalendarMonthBackground CalendarTitleBackColor CalendarTitleForeColor CalendarTrailingForeColor

Wird zur Einstellung von x benutzt die im Kalender benutzte Schriftart die Farbe des Textes die Farbe hinter dem Monat die Farbe hinter dem Titel die Farbe des Titeltextes die Farbe der im Kalender nachfolgenden Datumswerte (die nicht mehr zum aktuellen Monat gehren)

Tabelle 6.1: So gestalten Sie den Kalender.

Sie knnen das Erscheinungsbild ganz Ihren Wnschen anpassen. Die Format-Eigenschaft erlaubt Ihnen zudem, die Anzeigeform der Daten im Textbereich zu steuern. Diese mssen die Werte aus der DateTimePickerFormat-Aufzhlung aufweisen:

Custom: ein angepasstes Anzeigeformat; verwenden Sie die CustomFormat-Eigenschaft. Long: das vom Betriebssystem des Benutzers ausgewhlte lange Datumsformat. Zum Beispiel Mittwoch, 7. November 2001.

190

Das Steuerelement DateTimePicker

Short: das vom Betriebssystem des Benutzers ausgewhlte kurze Datumsformat. Zum Beispiel 7.11.2001 Time: Die vom Betriebssystem des Benutzers vorgenommene Formatierung der Zeitangabe. Zum Beispiel 9:48:25

Falls das Format auf DateTimePickerFormat.Custom eingestellt ist, knnen Sie die CustomFormat-Eigenschaft dazu verwenden, die Art und Weise anzupassen, wie Datum und Zeit angezeigt werden. Diese Eigenschaft benutzt speziell formatierte Zeichenfolgen, um Datums- und Zeitangaben darzustellen. Das folgende Codestck wrde 01. Juni, 0930 anzeigen:
dtpMyDate.CustomFormat = "dd. MMMM, HHmm"

Tabelle 6.2 fhrt alle verfgbaren Zeichenfolgen auf (Achtung: bei den Zeichenfolgenwerten ist die Gro-/Kleinschreibung zu bercksichtigen).
Formatzeichenfolge
d dd ddd dddd h

Beschreibung Tagesangabe mit einer oder zwei Ziffern Tagesangabe mit zwei Ziffern Drei-Zeichen-Abkrzung fr Wochentag Vollstndiger Wochentagsname Ein- oder Zwei-Ziffern-Ausgabe der Stunde im 12-Stunden-Format (z.B. 1 p.m.) Zwei-Ziffern-Ausgabe der Stunde im 12-Stunden-Format Ein- oder Zwei-Ziffern-Ausgabe der Stunde im 24-Stunden-Format (z.B. 1300 Uhr) Zwei-Ziffern-Ausgabe der Stunde im 24-Stunden-Format Ein- oder Zwei-Ziffern-Ausgabe der Minute Zwei-Ziffern-Ausgabe der Minute Ein- oder Zwei-Ziffern-Ausgabe des Monats Zwei-Ziffern-Ausgabe des Monats Drei-Zeichen-Abkrzung fr Monat Vollstndiger Monatsname

hh H

HH m mm M MM MMM MMMM

Tabelle 6.2: Datum- und Zeit-relevante Zeichenfolgen fr CustomFormat

191

Windows Forms mit Steuerelementen erweitern

Formatzeichenfolge
s ss t tt y

Beschreibung Ein- oder Zwei-Ziffern-Ausgabe der Sekunde Zwei-Ziffern-Ausgabe der Sekunde Einbuchstabige Abkrzung fr AM/PM (z.B. wird AM als A angezeigt) Zweibuchstabige Abkrzung fr AM/PM Ein- oder Zwei-Ziffern-Ausgabe der Jahreszahl (z.B. wird 2001 als 1 angezeigt) Die letzten beiden Ziffern der Jahreszahl Alle vier Ziffern der Jahreszahl

yy yyyy

Tabelle 6.2: Datum- und Zeit-relevante Zeichenfolgen fr CustomFormat (Forts.)

Die Value-Eigenschaft gibt ein DateTime-Objekt zurck, das die gerade im Steuerelement angezeigte Zeit zurckgibt. MaxDate und MinDate geben die maximalen (am weitesten in der Zukunft liegenden) und minimalen (am weitesten in der Vergangenheit liegenden) Daten zurck, die der Benutzer aus dem Kalender auswhlen kann. Diese Eigenschaften bernehmen DateTime-Objekte als Werte. Das folgende Codestck setzt das MinimumDatum auf den 20. Juni 1985 fest:
dtpMinDate = new DateTime(1985, 6, 20) DropDownAlignment bernimmt einen Wert aus der LeftRightAlignment-Aufzhlung, um anzugeben, nach welcher Seite des Textbereichs der Kalender ausgerichtet sein soll. Mgliche Werte dafr sind LeftRightAlignment.Left (Standardwert) und LeftRightAlignment.Right.

Es gibt eine Reihe von Mglichkeiten, wie Sie dem Benutzer gestatten knnen, das Datum im Textbereich zu ndern. Bei der ersten Version (Standardeinstellung) kann der Benutzer den Wert eingeben. Der DateTimePicker erlaubt nur gltige Eintrge (der genaue Typ kann von der Format-Eigenschaft abhngen), daher mssen Sie sich nicht um ungltige Daten kmmern. Die zweite Methode erlaubt es dem Benutzer, ein Auf-Ab-Steuerelement zu verwenden (in der Art einer kleinen Bildlaufleiste), um die Werte zu ndern. Fr diese Methode mssen Sie die ShowUpDown-Eigenschaft auf True setzen. Setzt man ShowCheckBox auf True, wird ein Kontrollkstchen neben dem Datums im Textbereich angezeigt. Dadurch kann der Benutzer signalisieren, dass er ausdrcklich das angezeigte Datum auswhlt. Das kann sich als ntzlich erweisen, wenn Sie vom Benutzer verlangen, dass er einen Datumsbereich angibt. Indem er das Kontrollkstchen deaktiviert lsst, knnte der Benutzer ein Open-End- oder ein Startdatum angeben. Verwenden Sie die Checked-Eigenschaft, um herauszufinden, ob das Kontrollkstchen gerade angekreuzt ist.

192

Das Steuerelement ImageList

Es gibt eine paar wichtige Ereignisse im Steuerelement DateTimePicker. ValueChanged wird ausgelst, sobald sich der Wert im Textbereich ndert, ob nun durch direkte Bearbeitung oder durch eine Auswahl aus dem Kalender. Die CloseUp- und DropDown-Ereignisse finden statt, sobald der Kalenderbereich des Steuerelements geschlossen respektive geffnet wird. Sie knnten diese Ereignisse ausnutzen, um Gltigkeitsprfungen auszufhren, etwa um zu sehen, ob ein ausgewhltes Datum in einen gltigen Bereich fllt, bevor Sie dem Benutzer erlauben fortzufahren.

6.5

Das Steuerelement ImageList

Wir haben das ImageList-Steuerelement bereits an Tag 4 besprochen, so dass wir uns damit nicht allzu lange aufhalten wollen. Die herausragende Eigenschaft von ImageList ist Images, welche eine Auflistung von Grafiken aufnimmt, die sich von anderen Teilen Ihrer Anwendung nutzen lassen. Um Bilder hinzuzufgen, verwenden Sie einfach die AddMethode, etwa so:
private ImageList ilstToolbar = new ImageList(); ilstToolbar.Images.Add(Image.FromFile("i_open.bmp ")); ilstToolbar.Images.Add(Image.FromFile("i_maximize.bmp "));

Um Bilder zu entfernen, verwenden Sie Remove, wobei Sie den Index des zu entfernenden Bildes angeben. Es gibt nur wenige weitere Eigenschaften: ColorDepth, die zu jedem gelisteten Bild dessen Anzahl an Farben angibt (ausgedrckt als Bit-Wert: 4 Bit, 8 Bit usw.); ImageSize, die sich fr die Grennderung der Bilder einsetzen lsst; und schlielich TransparentColor, welche eine Farbe im Bild durchsichtig machen kann, wodurch Dinge hinter dem Bild zum Vorschein kommen. Listing 6.4 zeigt ein typisches Beispiel einer ImageList, die mit einem Button-Steuerelement interagiert. Listing 6.4: Die Verwendung von ImageList in C#
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: using System; using System.Windows.Forms; using System.Drawing; namespace TYWinForms.Day6 { public class Listing64 : Form { private ImageList ilstButton = new ImageList(); private Button btImage = new Button(); public Listing64() { ilstButton.Images.Add(Image.FromFile("i_open.bmp"));

193

Windows Forms mit Steuerelementen erweitern

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:

ilstButton.Images.Add(Image.FromFile("i_maximize.bmp")); ilstButton.Images.Add(Image.FromFile("i_happy.bmp")); btImage.ImageList = ilstButton; btImage.ImageIndex = 0; btImage.Location = new Point(100,100); btImage.Click += new EventHandler(this.CycleImages); this.Text = "Listing 6.4"; this.Controls.Add(btImage); } public void CycleImages(Object Sender, EventArgs e) { if (btImage.ImageIndex == ilstButton.Images.Count-1) { btImage.ImageIndex = 0; } else { btImage.ImageIndex += 1; } } public static void Main() { Application.Run(new Listing64()); } } }

Die ImageList wird in den Zeilen 12 bis 14 mit Bildern gefllt (diese Zeilen sollten auf tatschlich vorhandene Bilder zeigen). Button verfgt wie viele Windows Forms-Steuerelemente ber eine ImageList-Eigenschaft, die die Zuweisung einer ImageList zu diesem Steuerelement zulsst. Zeile 16 weist die ImageList dem Button-Steuerelement zu. Zeile 17 stellt die Schaltflche so ein, dass sie zunchst das erste Bild in der Liste anzeigt. Zeile 18 positioniert die Schaltflche auf dem Formular und Zeile 19 fgt dem Click-Ereignis einen Delegaten hinzu. Der Ereignishandler CycleImages (Zeile 25) ndert das auf der Schaltflche angezeigte Bild, fhrt aber zuvor Prfungen durch. Klickt der Benutzer auf die Schaltflche, soll das nchste Bild angezeigt werden. Dies wird erreicht, indem einfach die ImageIndex-Eigenschaft nach oben gezhlt (inkrementiert) wird (Zeile 29). Sobald man jedoch beim letzten Bild in der Liste angelangt ist, ndert sich das Bild mangels Vorrat nicht mehr. In diesem Fall setzt man den ImageIndex auf 0 zurck, so dass der Hochzhlzyklus von neuem beginnen kann. In Zeile 26 wird eine Prfung durchgefhrt, um herauszufinden, ob der aktuelle Wert ImageIndex-Wert bereits auf dem letzten Listenwert (Count 1) angelangt ist. Ist das der Fall, startet ImageIndex wieder auf 0, andernfalls wird auf den nchsten Wert weitergezhlt.

194

Das Steuerelement ListBox

Abbildung 6.6 zeigt das Ergebnis dieses Code-Beispiels.

Abbildung 6.6: Weist man einem Button eine ImageList zu, fhrt dies zur Anzeige von Grafiken statt Text auf der Schaltflche.

6.6

Das Steuerelement ListBox

Das ListBox-Steuerelement (Listenfeld) hnelt dem ComboBox-Steuerelement (Letzteres besteht aus einer Kombination von ListBox und TextBox). Ihnen gemeinsam sind solche Mitglieder wie Items, DataSource, DisplayMember, SelectedIndex, FindString usw. Sowohl ListBox als auch ComboBox sind von derselben Basisklasse abgeleitet, nmlich ListControl. Das die ListBox keine Kombination von Elementen darstellt, ist sie weitaus spezialisierter in ihren Aufgaben als die ComboBox. Daher verfgt sie ber einige Mitglieder mehr. Mit Hilfe der ListBox lassen sich dem Benutzer mehrere Elemente in einer Liste anzeigen. Eine ListBox ist stets sichtbar, denn sie wird nicht wie die Dropdown-Liste in der ComboBox ausgeblendet. Und anders als dort kann der Benutzer in der ListBox mehrere Elemente auf einmal auswhlen, indem er dabei einfach die (Strg)- oder ()-Taste gedrckt hlt. Dieses Verhalten der ListBox bedingt auch eine komplexere Funktionalitt mit sich. Das Beispiel in Listing 6.5 verwendet fast alle verfgbaren Eigenschaften von ListBox. Listing 6.5: Die Verwendung des ListBox-Steuerelements mit C#
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: using System; using System.Windows.Forms; using System.Drawing; namespace TYWinForms.Day6 { public class Listing65 : Form { private ListBox lbItems = new ListBox(); public Listing65() {

195

Windows Forms mit Steuerelementen erweitern

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:

lbItems.Location = new Point(75,75); lbItems.MultiColumn = true; lbItems.ColumnWidth = 75; lbItems.SelectedIndexChanged += new EventHandler(this.HandleSelect); lbItems.SelectionMode = SelectionMode.MultiExtended; Object [] arrColors = {"Weiss", "Rot", "Orange", "Gelb", "Grn", "Blau", "Indigo", "Violett", "Schwarz"}; lbItems.Items.AddRange(arrColors); this.Text = "Listing 6.5"; this.Controls.Add(lbItems); } public void HandleSelect(Object Sender, EventArgs e) { ListBox lbTemp = (ListBox)Sender; int i; if (lbTemp.SelectedIndices.Count == 1 ) { lbItems.BackColor = Color.FromName(lbTemp.SelectedItem.ToString()); } else { for (i = 0; i < lbTemp.SelectedIndices.Count; i++) { MessageBox.Show("Sie haben folgendes Element ausgewhlt: " + lbTemp.SelectedItems[i].ToString()); } } } public static void Main() { Application.Run(new Listing65()); } } }

Die Zeilen 11 bis 18 enthalten die meisten fr uns interessanten Eigenschaften. Zeile 11 kennen Sie ja bereits; sie legt die Position des Steuerelements im Formular fest. Die ListBox kann mehrere Spalten von Elementen zugleich anzeigen. Wenn Sie MultiColumn wie in Zeile 12 auf true setzen, erzeugt die ListBox so viele Spalten wie ntig sind, um ihre Elemente aufzunehmen, damit der Benutzer keinen Bildlauf nach unten ausfhren muss. (Beachten Sie, dass diese Bildlauffunktion bereits im ListBox-Steuerelement eingebaut ist; daher mssen Sie sich nicht um die betreffenden Ereignisse kmmern.) Die Abbildung 6.7 zeigt ein Beispiel fr dieses Konzept.

196

Das Steuerelement ListBox

Multicolumn = true; (Spalten werden auf angemessene Weise erzeugt, so dass vertikales Scrollen nicht ntig ist) Element 1 Element 6 Element 2 Element 7 Element 3 Element 8 Element 4 Element 5

Multicolumn = false; (Voreinstellung) (Der Benutzer muss vertikal scrollen, um alle Elemente sehen zu knnen)

Element 1 Element 2 Element 3 Element 4 Element 5 Element 6 Element 7 Element 8

Abbildung 6.7: MultiColumn gibt an, ob sich Listenelemente horizontal oder vertikal scrollen lassen.

Zeile 13 legt einfach die Breite jeder Spalte fest, sofern MultiColumn auf true gesetzt ist. Zeile 14 weist dem SelectedIndexChanged-Ereignis einen Delegaten zu, genau wie beim gleichnamigen Ereignis in der ComboBox. Die Zeile 15 legt den SelectionMode der ListBox fest. Diese Eigenschaft bestimmt, wie der Benutzer Elemente auswhlen kann (Mehrfach- oder Einzelauswahl usw.). Die mglichen Werte der SelectionMode-Aufzhlung sind:
MultiExtended: Der Benutzer kann mehrere Elemente mit Hilfe der ()-, (Strg)- und Pfeiltasten auswhlen. (Dies ist die bliche Vorgehensweise,

um Mehrfachauswahl zu gestatten.)
MultiSimple: Der Benutzer kann mehrere Elemente auswhlen, indem er

auf eines nach dem anderen klickt. Jeder weitere Klick nimmt ein neues Element in die bestehende Auswahl auf, daher ist keine Zusatztaste wie etwa () oder (Strg) ntig.
None: Es knnen keine Elemente ausgewhlt werden. One: Es kann nur ein Element ausgewhlt werden.

In den Zeilen 17 und 18 erstellen Sie schlielich ein Array von Zeichenfolgenobjekten und weisen es der ListBox mit Hilfe der AddRange-Methode zu. Beachten Sie, dass wir hier Farbennamen verwenden. Gleich werden Sie sehen, wie diese Farben eingesetzt werden. Springen wir hinunter in Zeile 24: Hier ist der Ereignishandler fr das Ereignis
SelectedIndexChanged zu finden. Zunchst sind in dieser Methode zwei Variablen zu erstellen: eine ListBox und ein Integer. Die erste Variable wird als

Abkrzung zum Objekt verwendet, das das Ereignis erzeugt hat. Denken Sie daran, dass die Sender-Variable zuerst in den richtigen Typ zu konvertieren ist, um sie benutzen zu knnen:
((ListBox)Sender).eigenschaft

Um Ihnen zu ersparen, den Typ jedes Mal konvertieren zu mssen, tun Sie das einmal in Zeile 25 und verwenden ab jetzt die neue lbTemp-Variable, die nun

197

Windows Forms mit Steuerelementen erweitern

identisch mit dem Objekt lbItems ist, das dieses Ereignis generiert hat. Dieser Vorgang ist zwar nicht unbedingt ntig, erspart aber einiges an Tipparbeit. Es gibt zwei mgliche Situationen: Der Benutzer hat ein Element markiert, und der Benutzer hat mehr als ein Element markiert. Im Einzelelement-Fall wollen wir die Hintergrundfarbe der ListBox auf die gewhlte Farbe einstellen. In der Mehrfachelement-Situation knnen wir offensichtlich den Hintergrund der ListBox nicht auf mehrere Farben gleichzeitig setzen; statt dessen zeigen wir einfach eine Meldung an, die den Benutzer ber seine Auswahl informiert (eigentlich wird nur seine letzte Auswahl in der Meldung angezeigt). In Zeile 28 benutzen Sie die SelectedIndices-Eigenschaft, die die Indizes der ausgewhlten Elemente zurckgibt, ganz gleich, wie viele Elemente ausgewhlt worden sind. Sollte die Anzahl der Indizes in SelectedIndices (wird durch die Count-Eigenschaft angegeben) gleich 1 sein, dann wissen Sie, dass nur ein Element ausgewhlt wurde. In Zeile 29 verwenden Sie dann die FromName-Methode des Color-Objekts, um den richtigen Color-Wert je nach dem ausgewhlten Element zurckzugeben. Denken Sie daran, dass die SelectedItem-Eigenschaft ein String-Objekt liefert, so dass Sie die ToString-Methode verwenden mssen, um statt dessen die Zeichenfolgen-Darstellung zu erhalten. Die Zeilen 30 bis 33 behandeln jenen Fall, bei dem es mehr als eine Auswahl gibt. Mit Hilfe der Integer-Variablen i, die Sie zuvor erzeugt haben, fhren Sie eine Programmschleife durch jedes der ausgewhlten Elemente aus. Die forSchleife setzt die Variable i auf 0 und fhrt fort, sie zu inkrementieren (und den Code in der Schleife auszufhren), bis sie nicht mehr kleiner als die Anzahl der ausgewhlten Indizes ist (welche durch SelectedIndices.Count angegeben wird). Der Code in der Programmschleife verwendet die MessageBox.ShowMethode, um eine Meldung an den Benutzer auszugeben. SelectedItems ist eine Auflistung der ausgewhlten Elemente (hnlich wie SelectedIndices eine Auflistung der Indexzahlen der ausgewhlten Elemente ist). Das war's schon. Abbildung 6.8 zeigt das Ergebnis, nachdem eine einzelne Farbe ausgewhlt wurde (die Hintergrundfarbe der ListBox hat sich gendert) und dann mehrere Elemente. Es gibt noch ein paar weitere Mitglieder der ListBox kennen zu lernen. ScrollAlwaysVisible lsst sich auf true setzen, damit die ListBox stndig eine senkrechte Bildlaufleiste anzeigt, selbst wenn alle Elemente auch ohne Bildlauf sichtbar wren.
UseTabStops ist eine interessante Eigenschaft. Falls das Element oder der Text, den Sie in der ListBox anzeigen wollen, Tabulatorzeichen enthlt, veranlasst das Setzen dieser Eigenschaft auf true die normale Anzeige dieser Tabulatoren. Ist sie jedoch auf false gesetzt,

werden die Tabulatorzeichen zu Leerzeichen umgewandelt.

198

Das Steuerelement PictureBox

Abbildung 6.8: Die ListBox gestattet die Mehrfachauswahl.

Die ListBox verfgt ber eine Text-Eigenschaft, die sich von den Text-Eigenschaften aller anderen Steuerelemente unterscheidet. Wurde ein Element bereits ausgewhlt, dann gibt Text den Text dieses Elements zurck. Wurden mehrere Elemente ausgewhlt, gibt Text den Text des ersten ausgewhlten Elements zurck. Wenn Sie jedoch versuchen, Text gleich einem Wert zu setzen, dann sucht es diesen Text und whlt dieses Element genau wie die FindStringExact-Methode aus. Der folgende Code wrde beispielsweise das Element mit dem Text Hallo Welt auswhlen (vorausgesetzt, es gibt ein Element mit diesem Text):
lbTemp.Text = "Hallo Welt";

6.7

Das Steuerelement PictureBox

Das Steuerelement PictureBox ist sehr einfach. Seine einzige Aufgabe besteht in der Anzeige einer Grafik. Werfen wir einen Blick auf seine Eigenschaften. Die wahrscheinlich wichtigste ist wohl die Image-Eigenschaft. Sie gibt an, welches Bild die PictureBox anzeigen wird. Bei diesem Bild kann es sich um praktisch jede Grafikdatei handeln, so etwa im GIF-, JPG- oder BMP-Format. Zum Beispiel:
dim pbMyPicture as new PictureBox pbMyPicture.Image = Image.FromFile("i_happy.bmp")

Stellen Sie auf jeden Fall sicher, dass das zu verwendende Bild eine gltige, also vorhandene Datei ist und Sie dafr die richtige Pfadangabe bereitstellen (Grafikdateien werden blicherweise im selben Ordner wie die Anwendung gespeichert).
SizeMode steuert, wie die Grafiken angezeigt werden und nimmt einen der folgenden Werte der PictureBoxSizeMode-Aufzhlung an:

199

Windows Forms mit Steuerelementen erweitern

AutoSize: Die PictureBox passt sich an die Gre der enthaltenen Grafik an. CenterImage: Das Bild wird in der Mitte der PictureBox angezeigt. Ist das Bild grer als die PictureBox, werden seine ueren Rnder abgeschnitten. Normal: Das Bild wird an der linken oberen Ecke der PictureBox ausgerichtet. Ist das Bild grer als die PictureBox, werden seine ueren Rnder abgeschnitten. StretchImage: Das Bild wird entweder gestaucht oder gestreckt, um die PictureBox

vollstndig auszufllen. An Tag 13 erfahren Sie, wie man Bildlaufleisten in Verbindung mit der PictureBox einsetzt, um in einem Bild zu navigieren, das grer als die PictureBox ist.

6.8

Das Steuerelement TabControl

Im Betriebssystem Windows findet man hufig Fenstern, die in der Art von Karteikarten gestaltet sind Fenster mit mehreren so genannten Registerkarten (auch als Tabs oder Tabseiten bezeichnet), die bestimmte Funktionen gruppieren. Abbildung 6.9 beispielsweise zeigt das Fenster EIGENSCHAFTEN VON ANZEIGE in Windows, das ber fnf Registerkarten verfgt. hnlich wie die ToolBar- und MainMenu-Steuerelemente besteht auch TabControl aus dem Hauptsteuerelement und mehreren untergeordneten Steuerelementen. Jede Tabseite wird durch ein TabPage-Steuerelement dargestellt. Um dem TabControl eine neue Tabseite hinzuzufgen, ruft man einfach die Add-Methode auf:
TabControl tcMain = new TabControl(); tcMain.TabPages.Add(new TabPage("Tab 1");

Diese Steuerelemente sind am einfachsten mit Hilfe eines Beispiels zu verstehen. Es ist ein recht umfangreiches Beispiel, weswegen ich es auf mehrere Abschnitte aufteilen werde, um die Untersuchung zu erleichtern. Der erste Teil, das Anwendungsgerst, ist in Listing 6.6 zu sehen. Listing 6.6: So erzeugen Sie in C# Registerkarten fr Ihre Anwendung.
1: 2: 3: 4: 5: 6: 7: using System; using System.Windows.Forms; using System.Drawing; namespace TYWinForms.Day6 { public class Listing66 : Form {

200

Das Steuerelement TabControl

8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27:

private TabControl tcMain = new TabControl(); private TabPage tpGeneral = new TabPage("Allgemein"); private TabPage tpDisplay = new TabPage("Anzeige"); private TabPage tpLocation = new TabPage("Position"); private TabPage tpAdvanced = new TabPage("Extras"); private private private private private private private private Label lbl1Name = new Label(); TextBox tb2Color = new TextBox(); Button bt2Color = new Button(); TextBox tb3Top = new TextBox(); TextBox tb3Left = new TextBox(); Button bt3Location = new Button(); TextBox tb4Tab = new TextBox(); Button bt4Tab = new Button();

public static void Main() { Application.Run(new Listing66()); } } }

Abbildung 6.9: Mit dem Steuerelement TabControl lassen sich Fenster mit Registerkarten erstellen.

201

Windows Forms mit Steuerelementen erweitern

Dieses Listing ist sehr einfach. Die passenden Namensrume werden in den Zeilen 1 bis 3 importiert. Die Klasse und die Namensrume werden in den Zeilen 5 und 7 deklariert und alle bentigten Steuerelemente in den Zeilen 8 bis 21. Zu diesen gehren das TabControl, vier TabPage- und diverse Button- und Label-Steuerelemente. Zeile 23 deklariert die Methode Main, die wie blich lediglich die Run-Methode aufruft. Listing 6.7 zeigt den Konstruktor fr diese Klasse, der zwischen die Zeilen 22 und 23 des Listings 6.6 platziert werden sollte. Listing 6.7: Beispiel fr einen TabControl-Konstruktor
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 Listing66() { // Tabseite 1 lbl1Name.Text = "Listing 6.6: Windows Forms in 21 Tagen"; lbl1Name.AutoSize = true; lbl1Name.Location = new Point(10,75); // Tabseite 2 tb2Color.Location = new Point(25,75); bt2Color.Text = "ndern!"; bt2Color.Location = new Point(125,75); bt2Color.Click += new EventHandler(this.ChangeColor); // Tabseite 3 tb3Top.Text = this.DesktopLocation.Y.ToString(); tb3Top.Location = new Point(25,50); tb3Left.Text = this.DesktopLocation.X.ToString(); tb3Left.Location = new Point(25,75); bt3Location.Text = "ndern!"; bt3Location.Location = new Point(125,75); bt3Location.Click += new EventHandler(this.ChangeLocation); // Tabseite 4 tb4Tab.Location = new Point(25,50); bt4Tab.Text = "Neuer Tab!"; bt4Tab.Location = new Point(75,75); bt4Tab.Click += new EventHandler(this.AddTab); tpGeneral.Controls.Add(lbl1Name); tpDisplay.Controls.Add(tb2Color); tpDisplay.Controls.Add(bt2Color); tpLocation.Controls.Add(tb3Top);

202

Das Steuerelement TabControl

35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49:

tpLocation.Controls.Add(tb3Left); tpLocation.Controls.Add(bt3Location); tpAdvanced.Controls.Add(tb4Tab); tpAdvanced.Controls.Add(bt4Tab); tcMain.Width = this.Width; tcMain.Height = this.Height; tcMain.TabPages.Add(tpGeneral); tcMain.TabPages.Add(tpDisplay); tcMain.TabPages.Add(tpLocation); tcMain.TabPages.Add(tpAdvanced); this.Text = "Listing 6.6"; this.Controls.Add(tcMain); }

Dieser Teil des Codes ist in Abschnitte fr jede Registerkarte aufgeteilt. Unser Entwurf erfordert vier Registerkarten: Allgemein, die allgemeine Informationen zur Anwendung anzeigt; Anzeige, die dem Benutzer die Anpassung der Farben der Anwendung erlaubt; Position, die dem Benutzer erlaubt, die Position der Anwendung auf dem Desktop mit Programmschritten anzupassen; und schlielich Extras, die dem Benutzer das Hinzufgen weiterer Tabseiten zur Anwendung gestattet. Die Zeilen 2 bis 5 richten die Steuerelemente ein, die auf der ersten Tabseite erscheinen werden. Hier haben wir lediglich ein Label-Steuerelement. In den Zeilen 8 bis 12 findet die Initialisierung der zweiten Tabseite statt. Wir erzeugen eine TextBox, in der der Benutzer eine neue Farbe eintragen kann, sowie eine Schaltflche, um den Wert zu besttigen. (Der Ereignishandler ChangeColor ist unten in Listing 6.8 zu finden.) Die Tabseite 3, in den Zeilen 15 bis 22 zu finden, enthlt zwei Textfelder (eines fr den oberen Rand der Anwendung und eines fr den linken) und eine Schaltflche, um die neuen Werte bernehmen zu knnen. Tabseite Nummer 4 weist ein Textfeld und eine Schaltflche auf. (Beide Ereignishandler fr diese Steuerelemente finden sich unten in Listing 6.8.) Nichts Aufregendes also bis jetzt. Als Nchstes mssen wir die Steuerelemente den passenden Tabseiten hinzufgen. Dies erfolgt hnlich wie das Hinzufgen von Steuerelementen zu einem Formular: mit Hilfe der Controls.AddMethode. Die Zeilen 31 bis 38 fgen alle vorgestellten Steuerelemente in die entsprechenden Tabseiten ein. Die Zeilen 40 bis 45 stellen die Eigenschaften fr das zentrale TabControlObjekt ein. Nun mssen wir alle Tabseiten dem TabControl hinzufgen, analog dem Einfgen von Steuerelementen in Tabseiten. In Zeile 48 mssen Sie das TabControl dem Form hinzufgen.

203

Windows Forms mit Steuerelementen erweitern

Sie knnen die Anwendung bereits in dieser Form kompilieren (vergessen Sie jedoch nicht, zuvor die Zeilen 12, 22 und 29 in Listing 6.7 auszukommentieren diese Zeilen wrden Fehler verursachen, da wir die entsprechenden Ereignishandler noch nicht erstellt haben). Beachten Sie, dass die Registerkarten bereits voll funktionsfhig sind: Sie knnen darin navigieren, ohne dafr extra Code geschrieben zu haben! Wir wollen die Ereignishandler aus Listing 6.8 nher untersuchen. Dieser Code sollte nach dem Konstruktor eingefgt werden, jedoch erst hinter der Main-Methode aus Listing 6.6 (beachten Sie, dass der Code nicht unbedingt hier stehen muss; das ist lediglich ein Vorschlag). Listing 6.8: Ereignisverarbeitung mit dem TabControl
1: public void ChangeColor(Object Sender, EventArgs e) { 2: int i; 3: 4: for (i = 0; i < tcMain.TabPages.Count; i++) { 5: tcMain.TabPages[i].BackColor = Color.FromName(tb2Color.Text); 6: } 7: } 8: 9: public void ChangeLocation(Object Sender, EventArgs e) { 10: this.DesktopLocation = new Point(Convert.ToInt32 (tb3Top.Text),  Convert.ToInt32(tb3Left.Text)); 11: } 12: 13: public void AddTab(Object Sender, EventArgs e) { 14: tcMain.TabPages.Add(new TabPage(tb4Tab.Text)); 15: }

Die Zeilen 1 bis 7 umfassen die ChangeColor-Methode. Sie ist dafr zustndig, die vom Benutzer angegebene Farbe zu bernehmen und sie auf die Applikation anzuwenden. Die Methode wird ausgefhrt, sobald die bt2color-Schaltflche auf der 2. Tabseite angeklickt wird. Das ndern der Farbe ist jedoch nicht so einfach, wie es aussieht. Man knnte die BackColor-Eigenschaft des Formulars auf die neue Farbe umstellen, doch da die Tabseiten das gesamte Formular berdecken, wrde man die Farbnderung nicht sehen. Daher muss man die Farbe der Tabseiten selbst ndern. Zeile 4 verwendet eine weitere for-Schleife, um von der ersten Tabseite bis zur letzten (wie durch TabPages.Count angegeben) zu zhlen. In jeder TabPage im TabControl-Steuerelement setzen Sie die BackColor-Farbe auf die vom Benutzer angegebene. Verwenden Sie die FromName-Methode des Color-Objekts, um den eingegebenen Text in einen Color-Wert umzuwandeln.

204

Das Steuerelement TabControl

Die Zeilen 9 bis 11 enthalten die ChangeLocation-Methode, die das Formular auf der Benutzeroberflche bewegt, wobei Werte aus der 3. Tabseite bercksichtigt werden. Diese Methode ist sehr einfach: Sie benutzen lediglich die Eigenschaft DesktopLocation des Form-Objekts, um das Formular auf den benutzerdefinierten Koordinaten zu positionieren. Denken Sie daran, den Text in den Textfeldern jeweils mit Hilfe der Convert.ToInt32-Methode zu einem Integer umzuwandeln, wie in Zeile 10 zu sehen. Die AddTab-Methode in Zeile 13 ist schlielich die einfachste von allen. Sie erstellt eine neue Tabseite und fgt sie dem TabControl-Objekt hinzu. Der Text, den der Benutzer in tb4Tab angibt, wird als Titel der Tabseite verwendet. Kompilieren Sie die vollstndige Anwendung und experimentieren Sie mit dem Funktionsumfang. Abbildung 6.10 zeigt das Ergebnis, nachdem eine neue Tabseite hinzugefgt wurde.

Abbildung 6.10: Mit dem TabControl-Steuerelement knnen Sie dynamisch weitere Tabseiten erstellen.

Es gibt noch ein paar TabControl-Eigenschaften, die Sie kennen sollten. Das TabControl hat die gleichen SelectedIndex- und SelectedIndexChanged-Mitglieder wie die Steuerelemente ComboBox und ListBox, so dass Sie damit bereits umgehen knnen. Zudem besitzt es eine SelectedTab-Eigenschaft, die das TabPage-Objekt zurckgibt, das gerade ausgewhlt ist. Anstelle von Text fr den Titel der Tabseite, wie Sie ihn in den Listings 6.6 bis 6.8 verwendet haben, kann TabControl auch Grafiken verwenden. Erstellen Sie einfach wie immer eine ImageList und weisen Sie sie der ImageList-Eigenschaft des TabControl-Steuerelements zu. Mit Hilfe der Alignment-Eigenschaft knnen Sie die Positionierung der Tabseiten im Formular ndern. Diese Eigenschaft bernimmt einen der Werte aus der TabAlignment-Aufzhlung: Bottom, Left, Right oder Top. Eine kleine nderung, die Sie hier vornehmen, kann Ihrer Anwendung ein vllig neues Aussehen verleihen.

205

Windows Forms mit Steuerelementen erweitern

Auch das TabControl verfgt ber eine Appearance-Eigenschaft. Sie verwendet Werte aus der TabAppearance-Aufzhlung und bestimmt, wie die einzelnen Tabseiten angezeigt werden sollen. Mgliche Werte sind Buttons, FlatButtons und Normal. Die ersten beiden Optionen vermitteln den visuellen Eindruck von Schaltflchen statt Registern, doch die Funktionalitt ist die gleiche. Einige der Eigenschaften betreffen die Gre der Registerkarten. ItemSize holt oder setzt die Breite und Hhe der Registerkarten. In der Standardeinstellung passt sich die Registerkarte automatisch ihrem Inhalt an. Die Padding-Eigenschaft ndert den Abstand zwischen den Elementen auf der Tabseite und deren Rndern. Diese Eigenschaft bernimmt jedoch keinen Integerwert, sondern einen Point-Wert:
tcMain.Padding = new Point(20,20);

Stellen Sie sich vor, Sie erzeugen ein neues Point-Objekt der Gre 20 x 20 Pixel, das auf jeder Seite des Tabseitentitels liegt. Dadurch erweitert sich die Registerkarte. Des Weiteren gibt es die SizeMode-Eigenschaft, die bestimmt, wie stark ein TabControl die Registerkarten expandieren sollte. Sie verwendet Werte aus der Aufzhlung TabSizeMode: FillToRight bewirkt die Expansion bis zur Gesamtbreite des TabControl-Steuerelements, bei Fixed haben alle Registerkarten die gleiche, feste Gre und Normal legt fest, dass die Gre jede Tabseite durch die bereits genannten ItemSize- und Padding-Eigenschaften gesteuert wird. Erzeugen Sie mehr Tabseiten, als sich zugleich darstellen lassen, erscheinen auf dem TabControl Pfeile, mit deren Hilfe der Benutzer blttern kann, um alle Tabseiten zu sehen. Wenn Sie die Multiline-Eigenschaft auf true setzen, bewegen sich die Tabseiten, die normalerweise verborgen wren, in eine andere Zeile, so dass sie immer angezeigt werden es werden so viele Zeilen erstellt wie zur Anzeige aller Tabseiten ntig sind. Um den berblick ber Ihre Steuerelemente behalten zu knnen, gibt es die Eigenschaften RowCount und TabCount, die, falls Multiline auf true gesetzt ist, die Anzahl der Zeilen bzw. die Gesamtzahl der Tabseiten zurckgeben. Die HotTrack-Eigenschaft schlielich veranlasst die Tabseiten, ihr Erscheinungsbild zu ndern (genauer gesagt: der Titelzeilentext ndert die Farbe), sobald die Maus darber bewegt wird.

6.9

Die Steuerelemente TextBox und RichTextBox

Ihnen ist das TextBox-Steuerelement in diesem Buch bereits einige Male begegnet. Meistens haben Sie nur seine Text-Eigenschaft genutzt und meist brauchen Sie auch nicht mehr. Es besitzt aber noch ein paar Eigenschaften, die sich als ntzlich erweisen knnten.

206

Die Steuerelemente TextBox und RichTextBox

Wir werfen einen kurzen Blick darauf, bevor ich Ihnen erzhle, was sie mit dem RichTextBox-Steuerelement zu tun haben. AcceptsReturn und AcceptsTab sind zwei Eigenschaften, die angeben, ob die ()- und die (_)-Tasten Zeilenumbrche respektive Einzge in der TextBox verursachen. Wenn der

Benutzer die Tabulatortaste drckt, geht gem der Voreinstellung der Fokus auf das nchste Steuerelement ber und die ()-Taste aktiviert die Standardschaltflche (falls es eine gibt). Indem Sie diese Eigenschaft auf true setzen, knnen Sie den Funktionsumfang Ihres Steuerelements erhhen. Wenn Sie AcceptReturn gestatten, drfen Sie damit rechnen, mehr als eine Zeile Daten zu erhalten. Tritt dies ein, dann sollten Sie Multiline auf true setzen, sonst knnte AcceptReturn nutzlos werden. Lines gibt ein Array von Zeichenfolgen zurck, das den Text aus jeder Zeile in der TextBox enthlt. WordWrap gibt vor, ob der Text in die nchste Zeile weiterlaufen (also umgebrochen werden) soll, wenn die TextBox nicht gro genug ist, um ihn komplett auf einmal anzuzeigen. Ist jedoch WordWrap gleich false und Multiline gleich true, dann muss der Benutzer die ()-Taste drcken, um in die nchste Zeile zu gelangen. Sie knnen auch die Art und Weise, wie der Benutzer mit der TextBox interagiert, modifizieren. Die Eigenschaft CharacterCasing bernimmt einen Wert der CharacterCasing-Aufzhlung, der angibt, wie die Groschreibung gehandhabt werden soll, wenn der Benutzer Text eingibt. Die Eigenschaft kann einen von drei Werten annehmen: Lower, welcher alle Eingaben in Kleinschreibung umwandelt; Normal, welcher Text so lsst, wie er eingegeben wird; und Upper, der jede Eingabe in Groschreibung umwandelt. Mit Hilfe von CanUndo kann der Benutzer seine letzten Aktionen rckgngig machen (fr viele Benutzer ein Glcksfall!). Die Undo-Methode veranlasst die Rckgngigmachung, und ClearUndo lscht die Rckgngig-Liste, so dass der Benutzer keine Rckgngig-Aktionen mehr vornehmen kann, da die Anwendung sich nun nicht mehr erinnern kann, was passiert ist. Modified gibt an, ob der Text in der TextBox gendert wurde. Diese Eigenschaft wird hufig dazu verwendet, um festzustellen, ob Inhalte gespeichert werden sollten, bevor die Anwendung geschlossen wird. ReadOnly (schreibgeschtzt) bedeutet, dass sich der Text nicht bearbeiten lsst. PasswordChar gibt das Zeichen an, welches zur Maskierung einer Benutzereingabe verwendet werden soll. Beim Eingeben von Kennwrtern haben Sie sicher schon oft bemerkt, dass Ihre Eingabe durch Sternchen (*) ersetzt wird. Die TextBox verfgt ber eine Reihe von Methoden, die den Text behandeln. Clear lscht alle Textinhalte in der TextBox. Copy, Cut und Paste folgen alle den vertrauten WindowsOperationen Kopieren, Ausschneiden und Einfgen. AppendText hngt eine angegebene Zeichenfolge an das Ende des TextBox-Inhalts an.

207

Windows Forms mit Steuerelementen erweitern

Rich Text
Das TextBox-Steuerelement eignet sich hervorragend, um Benutzereingaben zu handhaben. Sein Hauptnachteil: Es kann nur mit reinem Text oder Text ohne Formatierung umgehen. In ihrer einfachsten Form verhlt sich die RichTextBox, die auch formatierten Text verarbeiten kann, wie jedes andere Steuerelement auch. Mit seinen Brdern teilt es sich viele Mitglieder, darunter die Eigenschaften AcceptsTab, CanUndo und Multiline aus der TextBox. Doch RichTextBox bietet weit mehr Funktionen, darunter das Speichern und Laden von Dateien. Wir wollen einen Blick auf ein einfaches Beispiel fr ein RichTextBox-Steuerelement werfen. Es erlaubt die Formatierung von Text. Wieder werde ich den Code aufteilen, um die Abschnitte leichter analysieren zu knnen. Das Listing 6.9 enthlt den ersten Teil dieser Anwendung. Listing 6.9: Der Einsatz des Steuerelements RichTextBox in VB .NET
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: Imports System Imports System.Windows.Forms Imports System.Drawing Namespace TYWinForms.Day6 Public Class Listing69 : Inherits Form private rtbText as New RichTextBox private intFontSize as Integer private strFontColor as String private strFontStyle as String private private private private private private private private private private private private private private private cmnuText as New ContextMenu cmniFont as New MenuItem cmniBold as New MenuItem cmniItalics as New MenuItem cmniNormal as New MenuItem cmniFontSize as New MenuItem cmniSize8 as New MenuItem cmniSize10 as New MenuItem cmniSize12 as New MenuItem cmniSize14 as New MenuItem cmniColor as New MenuItem cmniColorRed as New MenuItem cmniColorBlue as New MenuItem cmniColorGreen as New MenuItem cmniColorBlack as New MenuItem

public shared sub Main()

208

Die Steuerelemente TextBox und RichTextBox

30: 31: 32: 33:

Application.Run(new Listing69) end sub End Class End Namespace

Listing 6.9 zeigt den grundlegenden Rahmen fr unsere Anwendung. Die Zeilen 8 bis 27 weisen eine Reihe von Variablen auf, von denen viele Steuerelemente vom Typ ContextMenu und MenuItem sind, die der Benutzer verwenden wird, um die Schriftart im RichTextBox-Steuerelement zu ndern. Die drei Variablen in den Zeilen 9 bis 11 sind globale Variablen, die registrieren, welche Gre, Farbe und Auszeichnung die aktuelle Schriftart hat; diese Werte werden sich spter als recht ntzlich erweisen. Listing 6.10 zeigt den Konstruktor dieser Beispielanwendung. Listing 6.10: Der Konstruktor fr das RichTextBox-Steuerelement der 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: 25: 26: 27: 28: public sub New() intFontSize = 10 strFontColor = "Schwarz" strFontStyle = "Normal" rtbText.Dock = DockStyle.Fill rtbText.ScrollBars = RichTextBoxScrollBars.Both rtbText.ContextMenu = cmnuText cmnuText.MenuItems.Add(cmniFont) cmnuText.MenuItems.Add(cmniFontSize) cmnuText.MenuItems.Add(cmniColor) cmniFont.Text = "Font" cmniBold.Text = "Fett" cmniFont.MenuItems.Add(cmniBold) cmniItalics.Text = "Kursiv" cmniFont.MenuItems.Add(cmniItalics) cmniNormal.Text = "Normal" cmniNormal.Checked = true cmniFont.MenuItems.Add(cmniNormal) AddHandler cmniBold.Click, new EventHandler(AddressOf Me.HandleFont) AddHandler cmniItalics.Click, new EventHandler(AddressOf Me.HandleFont) AddHandler cmniNormal.Click, new EventHandler(AddressOf Me.HandleFont) cmniFontSize.Text = "Grsse" cmniSize8.Text = "8" cmniFontSize.MenuItems.Add(cmniSize8)

209

Windows Forms mit Steuerelementen erweitern

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:

cmniSize10.Text = "10" cmniSize10.Checked = true cmniFontSize.MenuItems.Add(cmniSize10) cmniSize12.Text = "12" cmniFontSize.MenuItems.Add(cmniSize12) cmniSize14.Text = "14" cmniFontSize.MenuItems.Add(cmniSize14) AddHandler cmniSize8.Click, new EventHandler(AddressOf Me.HandleFontSize) AddHandler cmniSize10.Click, new EventHandler(AddressOf Me.HandleFontSize) AddHandler cmniSize12.Click, new EventHandler(AddressOf Me.HandleFontSize) AddHandler cmniSize14.Click, new EventHandler(AddressOf Me.HandleFontSize) cmniColor.Text = "Farbe" cmniColorRed.Text = "Rot" cmniColor.MenuItems.Add(cmniColorRed) cmniColorBlue.Text = "Blau" cmniColor.MenuItems.Add(cmniColorBlue) cmniColorGreen.Text = "Grn" cmniColor.MenuItems.Add(cmniColorGreen) cmniColorBlack.Text = "Schwarz" cmniColorBlack.Checked = true cmniColor.MenuItems.Add(cmniColorBlack) AddHandler cmniColorRed.Click, new EventHandler(AddressOf Me.HandleFontColor) AddHandler cmniColorBlue.Click, new EventHandler(AddressOf Me.HandleFontColor) AddHandler cmniColorGreen.Click, new EventHandler(AddressOf Me.HandleFontColor) AddHandler cmniColorBlack.Click, new EventHandler(AddressOf Me.HandleFontColor) Me.Text = "Listing 6.9" Me.Font = New Font("Times New Roman", intFontSize) Me.Controls.Add(rtbText) end sub

Dies ist ein Standard-Konstruktor, doch er hat eine Menge Code zu verarbeiten. Die Zeilen 2 bis 4 setzen Anfangswerte fr die Schriftstile Ihrer Anwendung fest. Die Zeilen 6 bis 8 definieren Eigenschaften fr das RichTextBox-Steuerelement. Dock gibt an, wie sich das Steuerelement im Verhltnis zum Formular bewegt. ScrollBars diktiert, ob und wie Bildlaufleisten fr die RichTextBox angezeigt werden sollen. Diese Eigenschaft kann einen der Werte aus der RichTextBoxScrollBar-Aufzhlung annehmen:

210

Die Steuerelemente TextBox und RichTextBox

Both: Falls ntig, werden waagrechte und senkrechte Bildlaufleisten

erscheinen.
ForcedBoth: Egal, ob bentigt oder nicht, es werden immer waagrechte und senkrechte Bildlaufleisten erscheinen. ForcedHorizontal: Es wird stets eine waagrechte Bildlaufleiste angezeigt. ForcedVertical: Es wird stets eine senkrechte Bildlaufleiste angezeigt. Horizontal: Bei Bedarf wird eine waagrechte Bildlaufleiste angezeigt. None: Es werden keine Bildlaufleisten angezeigt. Vertical: Bei Bedarf wird eine senkrechte Bildlaufleiste angezeigt.

Zeile 8 weist diesem Steuerelement ein ContextMenu zu, so dass der Benutzer darauf rechtsklicken und die Schrifteigenschaften ndern kann. Die Zeilen 10 bis 12 fgen dem ContextMenu-Steuerelement Menelemente der hchsten Hierarchieebene hinzu. Fast der ganze Rest des Listings beschftigt sich mit den Eigenschaften fr diese MenuItems. Die Zeilen 14 bis 24 initialisieren die Menelemente, die den Schriftstil steuern (fett, kursiv oder normal). Die Zeilen 26 bis 39 initialisieren diejenigen Elemente, die sich mit der Schriftgre befassen, und die Zeilen 41 bis 54 betreffen die Elemente, die die Schriftfarbe steuern. Beachten Sie, dass jede Codegruppe nur einen Ereignishandler verwendet: Die Zeilen 14 bis 24 benutzen HandleFont, die Zeilen 26 bis 39 HandleFontSize und die Zeilen 41 bis 54 HandleFontColor. Die Zeilen 56 bis 59 initialisieren schlielich das Formular, wobei sie die zu benutzende Standardschrift festlegen. Listing 6.11 zeigt die Ereignishandler und diverse Funktionen der Anwendung. Listing 6.11: Die Behandlung der RichTextBox-Ereignisse
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: public sub HandleFont(Sender as Object, e as EventArgs) strFontStyle = CType(Sender, MenuItem).Text rtbText.SelectionFont = new Font("Times New Roman", intFontSize, ConvertToFontStyle(strFontStyle)) cmniBold.Checked = false cmniItalics.Checked = false cmniNormal.Checked = false CType(Sender, MenuItem).Checked = true end sub

211

Windows Forms mit Steuerelementen erweitern

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:

public sub HandleFontSize(Sender as Object, e as EventArgs) intFontSize = Convert.ToInt32(CType(Sender, MenuItem).Text) rtbText.SelectionFont = new Font("Times New Roman", intFontSize, ConvertToFontStyle(strFontStyle)) cmniSize8.Checked = false cmniSize10.Checked = false cmniSize12.Checked = false cmniSize14.Checked = false CType(Sender, MenuItem).Checked = true end sub public sub HandleFontColor(Sender as Object, e as EventArgs) strFontColor = CType(Sender, MenuItem).Text rtbText.SelectionColor = Color.FromName(strFontColor) cmniColorRed.Checked = false cmniColorBlue.Checked = false cmniColorGreen.Checked = false cmniColorBlack.Checked = false CType(Sender, MenuItem).Checked = true end sub private function ConvertToFontStyle(strStyle) as FontStyle select strStyle case "Fett" return FontStyle.Bold case "Kursiv" return FontStyle.Italic case "Normal" return FontStyle.Regular end select end function

Beachten Sie bitte zunchst, dass keine dieser Methoden eines der Ereignisse in RichTextBox direkt verarbeitet. Vielmehr behandeln wir die Ereignisse der MenuItem-Steuerelemente, die die Eigenschaften der RichTextBox entsprechend anpassen. Lassen Sie uns als Nchstes hinunter zur Funktion ConvertToFontStyle springen, die in Zeile 36 beginnt. Wie Sie aus ihrer Deklaration schlieen knnen, gibt diese Funktion einen Wert der FontStyle-Aufzhlung zurck. Wenn Sie nacheinander jedes Ereignis behandeln, werden Sie bemerken, dass es ntig wird, das vom Benutzer ausgewhlte Menu-

212

Die Steuerelemente TextBox und RichTextBox

Item in einen passenden Typ umzuwandeln, den die RichTextBox verwenden kann. So kn-

nen Sie etwa mit folgendem Befehl keine Schrift erzeugen:


new Font("Times New Roman", 8, "Fett")

Statt dessen mssen Sie den Wert der FontStyle-Aufzhlung verwenden:


new Font("Times New Roman", 8, FontStyle.Fett)

Die Funktion ConvertToFontStyle wandelt den Text Fett in einen FontStyle.Bold-Wert um, den Sie an anderer Stelle in Ihrem Code benutzen knnen. Wie genau, werden Sie gleich sehen. Wenn Sie zu Zeile 1 und der HandleFont-Methode zurckgehen, denken Sie bitte daran, dass diese Methode das Click-Ereignis fr die Menelemente cmniBild, cmniItalics und cmniNormal behandelt, die die Schriftart jeweils fett oder kursiv auszeichnen bzw. auf normal zurcksetzen. In Zeile 2 holen Sie den Text-Wert vom gerade ausgewhlten MenuItem in die strFontStyle-Variable (Sie mssen die Sender-Variable in einen passenden Typ umwandeln in diesem Fall MenuItem); dieser Wert wird Fett sein, Kursiv oder Normal, wie in den Zeilen 15, 17 und 19 des Listings 6.10 gezeigt wird. In Zeile 3 verwenden Sie die Eigenschaft SelectionFont der RichTextBox. Sie holt und setzt Informationen ber die gerade in der RichTextBox verwendete Schriftart, insbesondere den gerade ausgewhlten (oder markierten) Text. Ist momentan kein Text ausgewhlt, betrifft diese Eigenschaft jeden neuen Text, den der Benutzer eingibt. Zeile 3 weist SelectionFont ein neues Font-Objekt zu, wobei die in den Variablen intFontSize und strFontStyle gesetzten Werte verwendet werden. Nun verstehen Sie, warum diese globalen Variablen wichtig sind ohne sie wrde es viel mehr Mhe bereiten, neue Schriftstile festzulegen. Ohne globale Variablen wrde es sicherlich mehr Arbeit bedeuten, doch sie wre nicht schwieriger. Wenn Sie diese globalen Variablen nicht verwenden wollen, dann ndern Sie die Zeile 3 wie folgt:
rtbText.SelectionFont = new Font("Times New Roman"), rtbText.SelectionFont.Size, ConvertToFontStyle(Ctype(Sender, MenuItem).Text))

Mit anderen Worten: Die globalen Variablen spielen Platzhalter fr Werte der SelectionFont-Eigenschaft und der Sender-Variablen. Lassen Sie uns kurz zur ConvertToFontStyle-Funktion zurckkehren. Bekanntlich kann man die Zeichenfolge, die vom ausgewhlten Menelement abgerufen wird, nicht direkt verwenden. Daher gibt diese Methode den richtigen Wert der FontStyle-Aufzhlung zurck, um die Schrift richtig festzulegen. An Tag 4 behandelten wir Menelemente. Wenn man zulsst, dass Menelemente mit Hkchen versehen werden, dann muss man dafr sorgen, dass alle momentan nicht rele-

213

Windows Forms mit Steuerelementen erweitern

vanten Menelemente kein Hkchen aufweisen. Die Zeilen 5 bis 9 entfernen alle Hkchen von den Schriftstil-Menelementen; dann wird das passende Menelement mit Hkchen versehen, je nachdem, was die Sender-Variable angibt. Die Methoden HandleFontSize und HandleFontColor tun praktisch das Gleiche wie HandleFont. Beide holen den Text des ausgewhlten Menelements (die Zeilen 13 respektive 25). HandleFontSize weist der SelectionFont-Eigenschaft unter Zuhilfenahme der globalen Variablen eine neue Schrift zu und versieht die Menelemente dann mit Hkchen oder entfernt diese, je nachdem. HandleFontColor verwendet eine etwas andere Eigenschaft, nmlich SelectionColor. Diese Eigenschaft holt oder setzt die fr den gerade ausgewhlten Text verwendete Farbe (ist gerade keiner ausgewhlt, dann fr jeden neuen Text). In Zeile 26 verwenden Sie die FromName-Methode des Color-Objekts, um die Farbe zuzuweisen. In den Zeilen 28 bis 33 werden die Hkchen gesetzt bzw. entfernt. Diese Anwendung enthlt eine Menge Code; tatschlich ist sie das umfangreichste Codebeispiel, das Sie bis jetzt gesehen haben. Doch das meiste davon ist recht einfach, denn es besteht vor allem aus dem Initialisierungscode des Konstruktors. Kombinieren Sie diese drei Listings, indem Sie Listing 6.10 und 6.11 in Zeile 28 des Listings 6.9 einfgen. Abbildung 6.11 zeigt ein typisches Ergebnis aus dieser Anwendung.

Abbildung 6.11: Die voll funktionsfhige RichTextBox-Anwendung bietet dem Benutzer zahlreiche Wahlmglichkeiten an.

Die Hauptarbeit bei der Verwendung des RichTextBox-Steuerelements besteht also vor allem in der Bereitstellung von Benutzerschnittstellen (wie Menelementen), um Schrifteigenschaften bearbeiten zu knnen. Die zwei Haupteigenschaften SelectionFont und SelectionColor betreffen den gerade ausgewhlten Text. Die RichTextBox besitzt viele solcher Eigenschaften, darunter SelectionAlignment, die anzeigt, wie der momentan markierte Text auszurichten ist, SelectionBullet, die festlegt, ob der Text eine Liste mit Aufzhlpunkten sein soll, und schlielich SelectionIndent, die festlegt, wie gro Einrckungen sein sollen. (Um etwas ber die anderen Eigenschaften zu erfahren, schlagen Sie bitte in Anhang B nach.)

214

Das Steuerelement Timer

Bevor wir zum nchsten Steuerelement bergehen, sind drei RichTextBox-Mitglieder vorzustellen, die jeder Entwickler kennen sollte. Das erste, DetectUrls, ist interessant, da seine Funktion zunehmende Bedeutung in heutigen Anwendungen erlangt. Wird diese Eigenschaft auf true gesetzt, formatiert die RichTextBox jeden Text, der wie eine Webadresse (URL) aussieht, auf solche Weise, dass er anklickbar ist genau wie eine normale Verknpfung in einem Webbrowser. Der fragliche Text ist blau und unterstrichen (je nach Ihren Internet-Einstellungen). Um Klicks auf solche Links handhaben zu knnen, mssen Sie dem LinkClicked-Ereignis der RichTextBox einen Delegaten zuweisen und einen Ereignishandler hinzufgen, so etwa:
public Sub Link_Clicked(Sender As Object, e As LinkClickedEventArgs) System.Diagnostics.Process.Start(e.LinkText) end Sub

Die Start-Methode in diesem Codestck ruft den Standard-Browser des Benutzers mit dem Link, den er angeklickt hat, auf (eigentlich ruft sie die Anwendung auf, die dafr konfiguriert ist, Internet-Links zu verarbeiten, was aber meist ein Browser ist). Das zweite kennen zu lernende RichTextBox-Mitglied ist die Find-Methode. Sie durchsucht die RichTextBox nach einer angegebenen Zeichenfolge eine sehr ntzliche Funktion fr jede textverarbeitende Anwendung. Diese Methode kann etliche verschiedene Parameter bernehmen, doch die gebruchlichsten sind eine Zeichenfolge sowie eine Zeichenfolge mit einem Index fr das erste Zeichen, bei dem die Suche beginnen soll. Beim letzten Mitglied handelt es sich eigentlich um zwei, die eine hnliche Funktion haben. Rtf und SelectedRtf liefern den gesamten bzw. nur den markierten Text in der RichTextBox, wobei sie alle Formatierungen bernehmen. Die Copy-Methode, die auch in der TextBox verfgbar ist, kopiert Text, doch verlieren Sie dabei alle Formatierungen. Verwenden Sie also Rtf oder SelectedRtf, um die Formatierung beizubehalten, wenn Sie den Text an eine andere Anwendung (wie zum Beispiel Microsoft Word) bergeben wollen.

6.10 Das Steuerelement Timer


Das Timer-Steuerelement ist eines der ntzlichsten Steuerelemente berhaupt. Einfach ausgedrckt, knnen Sie damit ein Ereignis nach einem festgelegten Intervall auslsen. Bis jetzt hatten Sie keine Mglichkeit, in einer Anwendung Zeit oder Dauer festzuhalten. Denken Sie einmal an all die Einsatzmglichkeiten von Timer: Sie knnen eine Anwendung erstellen, die den Benutzer alle fnf Minuten daran erinnert, sein Dokument zu speichern; Sie knnen Echtzeituhren und -zhler bereitstellen und eine Leistungsberwachung einrichten. Praktisch jede interaktive Aufgabe, die Sie automatisieren wollen, erfordert das TimerSteuerelement.

215

Windows Forms mit Steuerelementen erweitern

Da er kaum etwas anderes tut, als die Zeit zu stoppen, verfgt der Timer ber sehr wenige Mitglieder. Er hat nur zwei Eigenschaften: Enabled zeigt an, ob der Timer aktiv sein soll, und Interval legt die Dauer in Millisekunden fest, nach der der Timer ticken soll. Jeder Tick im Timer erzeugt ein Ereignis, das treffend einfach Tick genannt wird. Damit knnen Sie angepasste automatische Routinen ausfhren lassen. Schlielich gibt es noch zwei interessante Methoden, nmlich Start und Stop, die einfach den Timer starten bzw. anhalten. Eine der besten Anwendungsmglichkeiten von Timer besteht in einer Weckuhr. Listing 6.12 zeigt eine solche Anwendung. Listing 6.12: Eine Weckuhr
1: Imports System 2: Imports System.Windows.Forms 3: Imports System.Drawing 4: 5: namespace TYWinForms.Day6 6: 7: public class Listing612 : Inherits Form 8: private tmrClock as New Timer 9: private tmrAlarm as New Timer 10: private lblClock as New Label 11: private intSnoozeTime as Integer 12: private intSnoozeCounter as Integer 13: 14: public sub New() 15: intSnoozeTime = 10000 '10 Sekunden 16: 17: tmrClock.Interval = 1000 18: tmrClock.Enabled = true 19: AddHandler tmrClock.Tick, new EventHandler(AddressOf  Me.UpdateClock) 20: 21: tmrAlarm.Interval = 1000 22: tmrAlarm.Enabled = true 23: AddHandler tmrAlarm.Tick, new EventHandler(AddressOf Me.CheckAlarm) 24: 25: lblClock.Width = 300 26: lblClock.Height = 150 27: lblClock.Location = new Point(0,100) 28: lblClock.Text = DateTime.Now.ToString 29: 30: Me.Text = "Listing 6.12" 31: Me.Font = new Font("Arial", 20) 32: Me.Controls.Add(lblClock)

216

Das Steuerelement Timer

33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52:

end sub public sub UpdateClock(Sender as Object, e as EventArgs) lblClock.Text = DateTime.Now.ToString end sub public sub CheckAlarm(Sender as Object, e as EventArgs) intSnoozeCounter += tmrAlarm.Interval if intSnoozeCounter = intSnoozeTime then intSnoozeCounter = 0 MessageBox.Show("Aufwachen, Schlafmtze!!!") end if end sub public shared sub Main() Application.Run(new Listing612) end sub end class end namespace

In dieser Anwendung erzeugen Sie zwei Timer-Steuerelemente: eines, um die dem Benutzer sichtbare Uhr zu aktualisieren, und eines, um den Weckruf zu steuern. In den Zeilen 8 bis 12 werden diese beiden Timer neben ein paar weiteren Variablen deklariert. In Zeile 15 stellen Sie im Konstruktor die Schlummerzeit ein oder die Zeit, bevor der Alarm losgeht in unserem Fall nach 10 Sekunden (= 10.000 Millisekunden). In den Zeilen 17 bis 19 stellen Sie das Intervall des Timers auf eine Sekunde (oder 1000 Millisekunden) ein, aktivieren bzw. starten den Timer und bergeben ihm einen Ereignishandler. Die Zeilen 21 bis 23 erledigen das Gleiche fr den Weckruf-Timer. Ein paar Eigenschaften fr ein Label-Steuerelement, das die Uhr anzeigt, werden in den Zeilen 25 bis 28 festgelegt, und die Zeilen 30 bis 32 bestimmen ein paar allgemeine Eigenschaften fr das Formular. Beachten Sie, dass Sie die DateTime.Now-Methode verwenden, um die aktuelle Zeit zurckzugeben, wie sie von der Uhr des Benutzer-Computers definiert wird. Die ToString-Methode wandelt das DateTime-Objekt in Text um, der im Label-Steuerelement angezeigt wird. Die UpdateClock-Methode in Zeile 35 wird alle 1000 Millisekunden durch den Timer tmrClock ausgefhrt. Sie ruft einfach fortlaufend DateTime.Now auf, um die sichtbare Uhr zu aktualisieren. Auch CheckAlarm wird alle 1000 Millisekunden ausgefhrt und bestimmt, ob das vorgegebene Schlummerintervall bereits verstrichen ist. In Zeile 40 erhht

217

Windows Forms mit Steuerelementen erweitern

diese Methode die intSnoozeCounter-Variable, die in Zeile 42 mit der intSnoozeTime-Variablen verglichen wird. Sind beide gleich sind also 10 Sekunden verstrichen wird eine Meldung mit einer freundlichen Weckrufbotschaft angezeigt. Die Zhlervariable wird dann auf 0 zurckgesetzt, um den Weckrufvorgang von neuem zu starten. Ansonsten passiert nichts, und der Timer tickt unverdrossen weiter. Abbildung 6.12 zeigt das Ergebnis nach zehn Sekunden.

Abbildung 6.12: Ihre einfache Weckuhr besitzt einen stillen Alarmruf.

6.11 Das Steuerelement TreeView


Das Steuerelement TreeView wird fr die Anzeige hierarchischer Listen von Informationen verwendet. Die gebruchlichste Einsatzweise besteht in Oberflchen vom Typ des Windows Explorers: Sie knnen damit durch eine Verzeichnisstruktur blttern, wie in Abbildung 6.13 zu sehen. Eine Verzeichnisstruktur ist jedoch nicht das Einzige, das das TreeView-Steuerelement darstellen kann. Jede beliebige hierarchisch aufgebaute Informationsstruktur Kunden- und Bestelldaten, Stammbume, Speisekarten usw. lsst sich mit TreeView handhaben. Jedes Element oder Strukturknoten (node) in der TreeView-Liste wird durch ein TreeNode-Objekt dargestellt. Natrlich kann jeder TreeNode unter sich weitere TreeNodes haben, so dass das Steuerelement seine hierarchische Charakteristik erhlt. Der erste Knoten wird als Wurzel- oder Stammknoten (root node) bezeichnet. Da Sie jeden Knoten in der TreeView einzeln anlegen mssen, knnte das Anlegen dieses Steuerelements etwas mhselig sein, doch es ist zum Glck nicht schwierig. Das folgende Codestck erstellt z.B. neue Knoten einfach durch Einsatz von Programmschleifen:

218

Das Steuerelement TreeView

Abbildung 6.13: Die Benutzeroberflche des Windows Explorers verwendet eine Strukturansicht. dim myTreeView as new TreeView For i = 0 to 20 MyTreeView.Nodes.Add(New TreeNode("Node " &&i.ToString)) For j = 0 to 20 MyTreeView.Nodes(i).Nodes.Add(New TreeNode("SubNode " &&j.ToString)) For k = 0 to 20 MyTreeView.Nodes(i).Nodes(j).Nodes.Add(New TreeNode("SubSubNode " &&k.ToString)) Next k Next j Next i

In diesem Codestck werden Schleifen durch drei verschiedene Variablen zwischen 0 und 20 gefhrt. Das Ergebnis ist ein dreischichtiges TreeView-Steuerelement, wobei jede Schicht ber 21 Knoten verfgt. Neue Knoten lassen sich einfach mit der Nodes.AddMethode hinzufgen, wobei ein neues TreeNode-Objekt mit einer festgelegten Beschriftung bergeben wird. Auf diese Beschriftung kann man auch ber die Text-Eigenschaft jedes Knotens zugreifen. Das TreeView-Steuerelement lsst sich weitgehend anpassen; jeder Aspekt der Benutzeroberflche lsst sich steuern. Tabelle 6.3 zeigt die gebruchlichsten Eigenschaften von TreeView.

219

Windows Forms mit Steuerelementen erweitern

Eigenschaft
CheckBoxes

Beschreibung Gibt an, ob neben jedem Knoten Kontrollkstchen angezeigt werden sollen. Dadurch kann der Benutzer bestimmte Knoten zur Bearbeitung auswhlen. Sie verwenden die Checked-Eigenschaft des TreeNode-Objekts, um zu bestimmen, ob ein Objekt mit Hkchen versehen (checked) ist. Gibt an, ob die Markierung, die bei der Auswahl eines Knotens erscheint, die gesamte Breite des Steuerelements einnehmen oder sich auf die Beschriftung des Knotens beschrnken soll. Gibt an, ob sich das Aussehen des Knotens ndert, wenn sich der Mauszeiger darber befindet (z.B. der Text sich blau frbt) Ein ImageList-Steuerelement, das sich dazu einsetzen lsst, Grafiken anstelle der Knotensymbole anzuzeigen. Sie verwenden dies mit der ImageIndex-Eigenschaft genau wie bei anderen Steuerelementen. Die Anzahl an Pixeln, um die jeder untergeordnete Knoten gegenber seinem bergeordneten Knoten eingerckt werden soll. Gibt an, ob sich die Beschriftung eines Knotens bearbeiten lsst. Gibt eine Auflistung von Strukturknoten (TreeNode) zurck. Das fr die Abtrennung von Knoten verwendete Zeichen (mehr dazu gleich). Gibt an, ob in der Strukturansicht Verbindungslinien zwischen den Knoten gezeichnet werden. Gibt an, ob neben Knoten mit untergeordneten Knoten Plus- und Minuszeichen angezeigt werden sollen. Gibt an, ob zwischen Stammknoten der Strukturansicht Verbindungslinien gezeichnet werden sollen. Gibt an, ob nach Bedarf Bildlaufleisten angezeigt werden sollen. Die Anzahl der gleichzeitig sichtbaren Knoten. Gibt das oberste sichtbare TreeNode-Objekt zurck.

FullRowSelect

HotTracking

ImageList

Indent

LabelEdit Nodes PathSeparator ShowLines

ShowPlusMinus

ShowRootLines

Scrollable VisibleCount TopNode

Tabelle 6.3: Die gebruchlichsten Eigenschaften des TreeView-Steuerelements

Das TreeNode-Objekt weist ebenfalls einige ntzliche Eigenschaften auf. FullPath kombiniert die Bezeichnungstexte aller Knoten, die anzusteuern sind, um den aktuellen Knoten zu erreichen. Die Angabe ist durch das PathSeparator-Zeichen (Standardwert: \) aufgeteilt. Beispielsweise she der FullPath, also die vollstndige Pfadangabe, zum 14. Knoten,

220

Zustzliche ntzliche Steuerelemente

der zum Knoten gehrt, der als 2. Knoten dem 3. Wurzelknoten untergeordnet ist, mit Hilfe des PathSeparator-Zeichens wie folgt aus:
"Node2\SubNode1\SubSubNode13\"

Die Eigenschaften IsEditing, IsExpanded, IsSelected und IsVisible geben true- oder false-Werte zurck, um anzugeben, ob der aktuelle TreeNode gerade bearbeitet wird oder sichtbar, erweitert oder ausgewhlt ist. Um Strukturknoten anzusteuern, stehen die Eigenschaften NextNode und NextVisibleNode sowie PrevNode und PrevVisibleNode zur Verfgung. Diese Eigenschaften geben die spezifizierten Strukturknoten zurck. Zwei ntzliche TreeView-Methoden sind CollapseAll und ExpandAll, die entweder alle erweiterten Knoten reduzieren oder umgekehrt alle reduzierten Knoten erweitern. In analoger Weise verfgt TreeNode ber Collapse, Expand, ExpandAll und Toggle. Letztere schaltet zwischen den Zustnden erweitert/reduziert hin und her. Das TreeView-Steuerelement weist eine Reihe von Ereignissen auf, die sowohl vor als auch nach bestimmten Aktionen ausgelst werden. Diese Ereignisse verwenden die Prfixe Before (vor) und After (nach), so etwa in BeforeCheck, BeforeCollapse, BeforeExpand, BeforeLabelEdit und BeforeSelect (zusammen mit den entsprechenden After-Methoden).

6.12 Zustzliche ntzliche Steuerelemente


In der heutigen Lektion haben wir gerade mal die Oberflche der Windows Forms-Steuerelemente angekratzt. Wir haben die gebruchlichsten Steuerelemente erforscht, doch es gibt insgesamt 42 Steuerelemente. Hier eine kleine Kostprobe:

CheckListBox: hnelt der ListBox, platziert aber neben jedem Listenelement ein kleines Kstchen. Da es sich um eine Kombination aus CheckBox und ListBox handelt,

knnen Sie sich die Eigenschaften in diesem Steuerelement vorstellen.

DataGrid: Sehr ntzlich beim Anzeigen von Informationen aus gespeicherten Datenquellen wie etwa Datenbanken. (Mehr dazu an Tag 9.) ProgressBar: hnlich den Leisten- Steuerelementen, die Sie an Tag 4 kennen gelernt haben. Die Fortschrittsleiste wird von links nach rechts angefllt, um anzuzeigen, wie weit die Vollendung einer Aufgabe vorangeschritten ist. ToolTip: Stellt eine QuickInfo dar, die erscheint, wenn der Mauszeiger ber einem

Steuerelement schwebt. Windows ist voll von solchen QuickInfos, so dass dieses Steuerelement sich als recht hilfreich erweist.

221

Windows Forms mit Steuerelementen erweitern

6.13 Das Layout steuern


Es ist an der Zeit zu lernen, wie man all diese Steuerelemente ordentlich auf einem Formular anordnet, denn sonst wrde man ein unverstndliches Durcheinander erhalten. Sie haben bereits eine Organisationsmethode kennen gelernt: das Hinzufgen von Steuerelementen zu anderen, bergeordneten Steuerelementen, wie etwa Panel- oder TabControlSteuerelementen. Mit diesen beiden lassen sich Steuerelemente zu logischen Gruppen zusammenfassen, die sowohl verwaltbar (als auch leicht darstellbar) sind. Es gibt noch andere Mglichkeiten zur Organisation von Steuerelementen. Eine davon besteht in der Dock-Eigenschaft der Control-Klasse. Mit Dock lassen sich die Position sowie die Art und Weise angeben, in der ein fragliches Steuerelement an sein Containersteuerelement angedockt ist. Indem man Werte aus der DockStyle-Aufzhlung verwendet, kann man Steuerelemente dazu bringen, am oberen oder unteren Rand eines Formulars oder eines Panel-Steuerelements (wie etwa Symbolleisten oder Bildlaufleisten) zu kleben oder den Container vollstndig auszufllen. Folgende sind die DockStyle-Werte:

Bottom: Dockt das Steuerelement am unteren Rand des Containersteuerelements an. Fill: Alle Rnder des Steuerelements werden an die entsprechenden gegenberliegenden Seiten des Containers angedockt, wodurch das Steuerelement den Container vollstndig ausfllt, selbst wenn dessen Gre sich ndert. Left: Dockt das Steuerelement am linken Rand des Containersteuerelements an. None: Standardwert; das Steuerelement wird nirgendwo angedockt. Right: Dockt das Steuerelement am rechten Rand des Containersteuerelements an. Top: Dockt das Steuerelement am oberen Rand des Containersteuerelements an.

Eine weitere das Layout beeinflussende Eigenschaft ist Anchor. Sie ist ebenfalls in jedem Steuerelement vorhanden und hnelt Dock insofern, als sie festlegt, welche Rnder eines Steuerelements mit dem bergeordneten Formular verknpft sein sollen, doch im Unterschied zu Dock werden die Steuerelemente dadurch nicht an den Formularrndern angedockt. Vielmehr bleibt die angegebene Seite des Steuerelements an ihrer Position relativ zum Rand des Formulars, unabhngig von seiner Position auf dem Formular oder von der Grennderung des Formulars. Anchor nimmt einen der Werte der AnchorStyle-Aufzhlung an: Bottom, Left, None, Right oder Top. Damit ein Steuerelement seine Gre relativ zu der des Formulars ndert, setzen Sie die Verankerung auf gegenberliegende Seiten des Formulars. Dies erfolgt in VB .NET mit dem Or-Operator und in C# mit dem Zeichen | etwa so:
textBox1.Anchor = AnchorStyles.Top Or AnchorStyles.Bottom textBox1.Anchor = AnchorStyles.Top | AnchorStyles.Bottom;

222

Das Layout steuern

Listing 6.13 zeigt ein gutes Beispiel fr die Manipulation der Eigenschaften Dock und Anchor. Listing 6.13: Das dynamische ndern eines Steuerelementlayouts (Visual Basic .NET)
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: Imports System Imports System.Windows.Forms Imports System.Drawing Namespace TYWinForms.Day6 Public Class Listing613 : Inherits Form Private WithEvents pnlButton As New Panel Private WithEvents pnlControls As New Panel Private WithEvents gboxAnchor As New GroupBox Private WithEvents gboxDock As New GroupBox Private WithEvents btnDemo As New Button Private WithEvents rbNone As New RadioButton Private WithEvents rbTop As New RadioButton Private WithEvents rbLeft As New RadioButton Private WithEvents rbBottom As New RadioButton Private WithEvents rbRight As New RadioButton Private WithEvents rbFill As New RadioButton Private WithEvents chkTop As New CheckBox Private WithEvents chkLeft As New CheckBox Private WithEvents chkBottom As New CheckBox Private WithEvents chkRight As New CheckBox Public Sub New() rbRight.Location = New Point(8,120) rbRight.Size = New Size(72,24) rbRight.Text = "Rechts" rbNone.Location = New Point(8,24) rbNone.Size = New Size(72,24) rbNone.Text = "Kein" rbNone.Checked = True rbBottom.Location = New Point(8,96) rbBottom.Size = New Size(72,24) rbBottom.Text = "Unten" rbTop.Location = New Point(8,48) rbTop.Size = New Size(72,24) rbTop.Text = "Oben"

223

Windows Forms mit Steuerelementen erweitern

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: 85: 86: 87:

rbLeft.Location = New Point(8,72) rbLeft.Size = New Size(72,24) rbLeft.Text = "Links" rbFill.Location = New Point(8,144) rbFill.Size = New Size(72,24) rbFill.Text = "Fllen" gboxAnchor.Location = New Point(16,16) gboxAnchor.Size = New Size(88,128) gboxAnchor.Text = "Verankern" gboxDock.Location = New Point(16,152) gboxDock.Size = New Size(88,176) gboxDock.Text = "Andocken" btnDemo.Size = New Size(120,24) btnDemo.Anchor = AnchorStyles.None btnDemo.Location = New Point(64,72) btnDemo.Text = "Spiel mit mir!" chkBottom.Location = New Point(8,72) chkBottom.Size = New Size(72,24) chkBottom.Text = "Unten" chkLeft.Location = New Point(8,48) chkLeft.Size = New Size(72,24) chkLeft.Text = "Links" chkTop.Location = New Point(8,24) chkTop.Size = New Size(72,24) chkTop.Text = "Oben" chkRight.Location = New Point(8,96) chkRight.Size = New Size(72,24) chkRight.Text = "Rechts" pnlButton.BorderStyle = FormBorderStyle.Fixed3D pnlButton.Dock = DockStyle.Fill pnlButton.Size = New Size(448,400) pnlButton.Text = "ButtonPanel" pnlButton.Controls.Add(btnDemo) pnlControls.BorderStyle = FormBorderStyle.Fixed3D pnlControls.Dock = DockStyle.Right pnlControls.Location = New Point(328,0)

224

Das Layout steuern

88: pnlControls.Size = New Size(120,400) 89: pnlControls.Text = "ControlsPanel" 90: pnlControls.Controls.Add(gboxAnchor) 91: pnlControls.Controls.Add(gboxDock) 92: 93: gboxAnchor.Controls.Add(chkRight) 94: gboxAnchor.Controls.Add(chkBottom) 95: gboxAnchor.Controls.Add(chkLeft) 96: gboxAnchor.Controls.Add(chkTop) 97: gboxDock.Controls.Add(rbBottom) 98: gboxDock.Controls.Add(rbLeft) 99: gboxDock.Controls.Add(rbNone) 100: gboxDock.Controls.Add(rbRight) 101: gboxDock.Controls.Add(rbFill) 102: gboxDock.Controls.Add(rbTop) 103: 104: Me.Text = "Listing 6.13" 105: Me.Size = New Size(800,600) 106: Me.Controls.Add(pnlButton) 107: Me.Controls.Add(pnlControls) 108: End Sub 109: 110: Private Sub AnchorClicked(Sender As Object,e As EventArgs) Handles  chkBottom.Click,chkLeft.Click,chkRight.Click,chkTop.Click 111: If chkTop.Checked Then 112: btnDemo.Anchor = btnDemo.Anchor Or AnchorStyles.Top 113: End If 114: 115: If chkLeft.Checked Then 116: btnDemo.Anchor = btnDemo.Anchor Or AnchorStyles.Left 117: End If 118: 119: If chkBottom.Checked Then 120: btnDemo.Anchor = btnDemo.Anchor Or AnchorStyles.Bottom 121: End If 122: 123: If chkRight.Checked Then 124: btnDemo.Anchor = btnDemo.Anchor Or AnchorStyles.Right 125: End If 126: End Sub 127: 128: Private Sub DockClicked(Sender As Object,e As EventArgs) Handles  rbBottom.Click,rbFill.Click,rbLeft.  Click,rbRight.Click,rbTop.Click,rbNone.Click 129: dim rbSet as RadioButton = CType(sender,RadioButton) 130:

225

Windows Forms mit Steuerelementen erweitern

131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150:

If rbSet Is rbNone Then btnDemo.Dock = DockStyle.None ElseIf rbSet Is rbTop Then btnDemo.Dock = DockStyle.Top ElseIf rbSet Is rbLeft Then btnDemo.Dock = DockStyle.Left ElseIf rbSet Is rbBottom Then btnDemo.Dock = DockStyle.Bottom ElseIf rbSet Is rbRight Then btnDemo.Dock = DockStyle.Right Else btnDemo.Dock = DockStyle.Fill End If End Sub public shared sub Main() Application.Run(New Listing613) end sub End Class End Namespace

In den Zeilen 8 bis 22 richten Sie einfach einige Steuerelemente ein, mit denen der Benutzer die Anchor- und Dock-Eigenschaften ndern kann; Sie bentigen ein Optionsfeld oder ein Kontrollkstchen fr jeden der Werte in den DockStyle- und AnchorStyle-Aufzhlungen (verwenden Sie Optionsfelder fr DockStyle, denn es lsst sich nur jeweils ein Wert zuweisen; Kontrollkstchen werden fr AnchorStyle benutzt, denn man kann ja mehrere Verankerungen zugleich haben). Beachten Sie den Gebrauch des Schlsselwortes WithEvents: Dies teilt der CLR mit, dass jedes einzelne der Steuerelemente Ereignisse auslsen wird (in C# ist das Einbeziehen solcher Schlsselworte nicht ntig). In den Zeilen 25 bis 107, also dem Groteil des Codes, initialisieren und platzieren wir Steuerelemente im Formular. In diesem Listing findet sich ein neues Steuerelement namens GroupBox (Gruppenfeld). Es hnelt dem Panel-Steuerelement, weil es andere Steuerelemente enthlt, doch es zeichnet einen Kasten um seine untergeordneten Steuerelemente, so dass der Benutzer wei, dass diese Steuerelemente zusammengehren. In den Zeilen 93 bis 102 werden die einzelnen Steuerelemente diesen Gruppenfeldern hinzugefgt, welche wiederum (in den Zeilen 90 und 91) Panel-Steuerelementen hinzugefgt werden. Die Panel-Steuerelemente finden in den Zeilen 106 und 107 endlich Aufnahme in das Formular. Es gilt nur noch zwei Methoden zu untersuchen: AnchorClicked in Zeile 110 und DockClicked in Zeile 128. Sie verwenden das Schlsselwort Handles, um der CLR mitzuteilen, auf welche Ereignisse diese Methoden angewendet werden sollen (mehr zur Ereignisbehand-

226

Das Layout steuern

lung in Windows Forms finden Sie an Tag 5). In unserem Fall handhabt die AnchorClicked-Methode alle Click-Ereignisse der Kontrollkstchen, und DockClicked tut das Gleiche fr die Optionsfelder. Wir wollen zuerst AnchorClicked betrachten. Da sich Anchor-Eigenschaften kumulativ auf ein Steuerelement anwenden lassen, mssen Sie jedes einzelne Anker-Kontrollkstchen untersuchen. Ist das Kontrollkstchen fr AnchorStyles.Top angekreuzt, mssen Sie den AnchorStyles.Top-Wert anwenden, und so weiter fr alle weiteren Werte. Dies lsst sich leicht bewerkstelligen, indem man mit Hilfe des Or-Operators die Werte in if-Statements der Anchor-Eigenschaft einer Schaltflche hinzufgt. Als Folge dieser Vorgehensweise wird jeder AnchorStyles-Wert nach Bedarf in der Reihenfolge der if-Statements ausgewertet und kumulativ angewendet. Mit DockClicked verhlt es sich hnlich. Doch da sich DockStyle-Werte nur einzeln anwenden lassen, mssen Sie die Stile nicht zusammen hinzufgen. Mit Hilfe von ifStatements bestimmen Sie einfach, welches Optionsfeld angeklickt wurde, und wenden den passenden DockStyle-Wert an (beachten Sie den Gebrauch des Is-Schlsselworts: Denken Sie beim Vergleichen von Objekten daran, diesen Operator anstelle des Gleichheitszeichens (=) zu verwenden). Probieren Sie die Steuerelemente der Anwendung, die in Abbildung 6.14 zu sehen ist, aus und beachten Sie, wie die Schaltflche dadurch beeinflusst wird. Obwohl die Eigenschaften TabStop und TabIndex das Layout von Steuerelementen nicht direkt betreffen, so gestatten sie doch das Navigieren zwischen den einzelnen Steuerelementen, so dass sie an dieser geeigneten Stelle vorgestellt werden sollen. Als WindowsBenutzer wissen Sie, dass Sie die (_)-Tasten dazu benutzen knnen, von einem Steuerelement zum nchsten zu gelangen. Dadurch knnen Sie sich ohne Maus durch eine Anwendung manvrieren. Die TabIndex-Eigenschaft gibt an, in welcher Reihenfolge bestimmte Steuerelemente angesteuert werden, wenn die (_)-Taste gedrckt wird. Ein Steuerelement mit dem TabIndex von 0 ist das erste zu markierende Steuerelement, sobald eine (_)-Taste gedrckt wird; TabIndex gleich 1 ist das nchste und so weiter. Jedes Steuerelement im Formular lsst sich auf diese Weise ansteuern. Wenn Sie die TabIndex-Werte nicht ausdrcklich zuweisen, erledigt dies die CLR fr Sie, allerdings gem der Reihenfolge, in der das jeweilige Steuerelement dem Formular hinzugefgt wurde (und falls Sie aus Versehen eine TabIndex-Zahl berspringen, wird das die CLR ignorieren und einfach zur nchsten verfgbaren Indexzahl springen). Dies ist ein wichtiges Merkmal der Benutzeroberflche, das man bercksichtigen muss. Es mag aber Situationen geben, in denen Sie nicht wnschen, dass der Benutzer mit der (_)-Taste auf ein Steuerelement springt. In der Regel sollten z.B. Benutzer nicht in der Lage sein, ein Label-Steuerelement zu manipulieren, daher sollte es nicht auswhlbar sein. In solchen Fllen setzen Sie die TabStop-Eigenschaft auf false.

227

Windows Forms mit Steuerelementen erweitern

Abbildung 6.14: Das Modifizieren der Anchor- und Dock-Eigenschaften einer Schaltflche.

6.14 Zusammenfassung
Am besten verwenden Sie dieses umfangreiche Kapitel als eine Art Nachschlagewerk und Referenz Sie mssen das nicht alles auswendig lernen. Sie haben mehrere Schaltflchen kennen gelernt: Button, RadioButton und CheckBox. Obwohl sie sich in ihrer Implementierung leicht unterscheiden, gestatten sie alle dem Benutzer, eine Auswahl zu treffen. Die gebruchlichsten Mitglieder diese Steuerelemente sind die Text-Eigenschaft und das Click-Ereignis. Mit Hilfe des DateTimePicker-Steuerelements kann der Benutzer Zeiten und Daten whlen, indem er sie entweder manuell eingibt oder aus der Dropdown-Liste des Kalenders auswhlt. Was uns zum nchsten Steuerelement fhrt: der ComboBox. Sie ist eine Kombination aus TextBox und ListBox; ein Benutzer kann Text von Hand eingeben oder Elemente aus einer Dropdown-Liste auswhlen. Anders als die ComboBox erlaubt es die ListBox dem Benutzer, mehrere Elemente auf einmal auszuwhlen. Dieses Steuerelement ist sehr ntzlich, wenn Sie dem Benutzer mehrere Auswahlmglichkeiten anbieten wollen.
ImageList und PictureBox haben beide mit Bildern zu tun, unterscheiden sich aber stark voneinander. ImageList ist im Grunde ein Array von Grafiken, das Sie mit anderen Steuerelementen verknpfen knnen, um Grafiken anzuzeigen (so wie die Schaltflchen fr eine ToolBar). Eine PictureBox wird lediglich dazu verwendet, dem Benutzer eine einzelne Grafik anzuzeigen.

228

Fragen und Antworten

Mit Hilfe von TabControl knnen Sie Steuerelemente auf logische Weise gruppieren, indem Sie Registerkarten (Tabseiten) verwenden. Die Steuerelemente TextBox und RichTextBox bieten dem Benutzer die Mglichkeit, Text in ein Feld einzugeben. Letzteres bietet den weitaus greren Funktionsumfang: Der Benutzer kann u.a. Schriftart, Farbe, Gre und Ausrichtung des Textes ndern. Dieses Steuerelement hnelt WordPad. Mit Hilfe des Timer-Steuerelements lassen sich Ereignisse in bestimmten Zeitabstnden auslsen. Unter anderem knnen Sie damit Weckuhren erstellen oder automatisierte Datensicherungsprozeduren durchfhren lassen. Mit TreeView knnen Sie hierarchisch angeordnete Informationen in einer einfach zu bedienenden Strukturansicht anzeigen. Zum Schluss haben Sie gelernt, wie Sie das Layout Ihrer Steuerelemente in Formularen steuern, indem Sie Dock, Anchor, TabIndex und TabStop verwenden. Die ersten beiden Eigenschaften fhren hnliche Funktionen aus, der Unterschied besteht darin, dass Dock ein Steuerelement an den Rand eines Formulars bewegt, Anchor aber die Position eines Steuerelements relativ zu den Rndern des Formulars fixiert hier ist kein Andocken an den Rand ntig.

6.15 Fragen und Antworten


F GDI+ wurde heute mehrmals erwhnt. Um was handelt es sich dabei? A GDI+ ist die erweiterte Schnittstelle zu Grafikgerten (Graphics Device Interface, GDI), mit deren Hilfe Sie verschiedene Grafiktypen zeichnen und bearbeiten knnen und die Sie in die Lage versetzt, Zeichenverfahren in Windows zu handhaben. GDI+ wurde entwickelt, um Entwicklern zu ermglichen, sich auf die Grafikfunktionen zu konzentrieren, ohne dabei an die vielen unterschiedlichen Typen von Grafik-Hardware (wie etwa 3D-Grafikkarten) und Treiber denken zu mssen. GDI+ ist das Hauptthema an Tag 13. F Verwendet Microsoft Word ein RichTextBox-Steuerelement als seine Benutzeroberflche? A Nein. Word verwendet eine besondere, speziell dafr entwickelte Steuerelementart, die der ffentlichkeit nicht zugnglich ist. Rich Text wird als normaler Text dargestellt, doch mit speziellem Code fr die Formatierung versehen. Word benutzt fr das Speichern seiner Dateien ein Binrformat, das sie nur fr Word brauchbar macht. Wenn Sie also eine Textverarbeitung mit dem gleichen Funktionsreichtum wie Word erstellen wollen, mssen Sie entweder Ihr eigenes Steuerelement schreiben oder auf dem RichTextBox-Steuerelement aufbauen.

229

Windows Forms mit Steuerelementen erweitern

6.16 Workshop
Dieser Workshop soll Ihnen helfen, sich besser an die heute behandelten Begriffe zu erinnern. Es hilft Ihnen sehr, die Antworten vollstndig zu verstehen, bevor Sie weiterlesen. Die Antworten zu den Quizfragen und den bungen finden Sie in Anhang A.

Quiz
1. Wodurch werden RadioButton-Auswahlen begrenzt? 2. Was bedeutet die folgende Datums-/Zeit-Formatzeichenfolge?
"hh:MM:yy-dddd"

3. Welches Objekt ist mit dem TreeView-Steuerelement verknpft? 4. Setzen Sie fr ein Button-Steuerelement namens btHappy Eigenschaften, die es dazu veranlassen, senkrecht zu expandieren, sobald sein Containerformular seine Gre ndert. 5. Nennen Sie zehn Mitglieder, die allen heute besprochenen Steuerelementen gemeinsam sind! 6. Wie heien die fnf Mitglieder des Timer-Steuerelements? 7. Wie fgt man dem Steuerelement TabControl Tabseiten hinzu? Beschreiben Sie dies in Worten und mit Hilfe einer Zeile Code. 8. Welche beiden Methoden sollte man ausfhren, wenn man vorhat, einer ComboBox mit Hilfe der Add-Methode viele Elemente hinzuzufgen? Wenn Sie an Ihr Wissen ber Windows Forms-Steuerelemente denken: Welche anderen heute vorgestellten Steuerelemente haben Ihrer Ansicht nach ebenfalls diese Methoden? 9. Wahr oder falsch? Das PictureBox-Steuerelement kann nur Bitmap- (.BMP) und GIFGrafiken anzeigen.

bung
Erstellen Sie in C# eine Anwendung, mit deren Hilfe Benutzer ihre CD-Sammlung katalogisieren und betrachten knnen. Sie mssen neue Knstler/Alben/Stcke in den Katalog aufnehmen und mit einer hierarchischen Liste betrachten knnen. (Kmmern Sie sich nicht um das Speichern der Eingaben das besprechen wir an Tag 9, wenn es um ADO.NET geht.) Denken Sie daran, dass Alben nur Knstlern hinzugefgt werden und Stcke nur Alben.

230

Mit Dialogfeldern arbeiten

Mit Dialogfeldern arbeiten

Dialogfelder sind eine spezielle Art von Windows Forms, die hauptschlich fr die Interaktion mit dem Benutzer und die Anzeige von Meldungen eingesetzt wird. Whrend der Arbeit mit Windows haben Sie wahrscheinlich bereits Tausende davon gesehen, so etwa wenn Sie sich in Windows einloggen wollen. Dialogfelder sind ein sehr vielseitiges Werkzeug fr Windows Forms und bieten zahlreiche komplexe Optionen fr Ihre Anwendungen und deren Benutzer. Wir werden den ganzen Tag damit verbringen, den nicht ganz einfachen Umgang mit Dialogfeldern und den speziellen Dialogarten in Windows Forms zu erlernen. Heute erfahren Sie etwas ber die verschiedenen Arten von Dialogfeldern in Windows und wie Sie

das Ergebnis eines Dialogfeldes bestimmen, jedes beliebige Dialogfeld anpassen, mit Hilfe von Dialogfeldern hufig auftretende Aufgaben in Windows bewltigen.

7.1

Informationen holen und anzeigen

Die wichtigste Aufgabe eines Dialogfeldes besteht darin, den Benutzer aufzufordern, Informationen einzugeben. Dies geschieht auf eine ganz andere Weise als bei Ihrem zentralen Windows Formular-Objekt, denn Sie knnen die Aufmerksamkeit des Benutzers auf recht direkte Weise gewinnen. Whrend Benutzer in Ihrem Formular hufig die Bitte um Information nur berfliegen, knnen Sie hingegen recht sicher sein, dass sie die angeforderten Informationen in einem Dialogfeld eingeben. Da Dialogfelder sich gut dazu eignen, die Aufmerksamkeit des Benutzers zu fesseln (Windows begleitet die erste Anzeige eines Dialogfeldes hufig mit einem Warnton), scheinen sie auch geradezu perfekt fr das Anzeigen wichtiger Informationen zu sein. Im gestrigen Beispiel einer Weckuhr haben Sie das Meldungsfeld, einen speziellen Dialogfeldtyp, verwendet, um dem Benutzer eine Weckmeldung zukommen zu lassen. In hnlicher Weise haben Sie in der gestrigen bung, dem Aufbau eines Musikkatalogs, den Benutzer per Dialogfeld darauf hingewiesen, keine Objekte der falschen Kategorie zuzuordnen (etwa einen Knstler einem Album hinzuzufgen statt andersherum). Ob nun beim Anzeigen oder Einholen von Information, ein Dialogfeld schickt stets Daten zurck an das Hauptformular (mehr dazu spter). Nachdem die erbetenen Informationen beschafft wurden, kann der Aufrufer des Dialogfeldes, normalerweise ein Form-Objekt, diese Informationen dazu verwenden, um einige Aktionen auszufhren. Wenn Sie beispielsweise in Word ein Dokument bearbeiten und versuchen, es ohne Speichern zu schlieen, erscheint ein Dialogfeld, die Sie zum Spei-

232

Informationen holen und anzeigen

chern auffordert, wie in Abbildung 7.1 zu sehen. Je nachdem auf welche Schaltflche der Benutzer klickt (JA, NEIN oder ABBRECHEN), fhrt Word die angemessene Aktion aus (also jeweils speichern, nicht speichern oder das Schlieen des Dokuments abbrechen). Auf diese Weise zeigt das Dialogfeld einerseits eine Meldung an und holt auf der anderen Seite Informationen ein.

Abbildung 7.1: Dieses Dialogfeld in Microsoft Word erfordert eine Reaktion vom Benutzer.

Wie Sie heute weiter unten sehen werden, knnen Dialogfelder weitaus komplizierter sein, als dieses einfache Beispiel vermuten lsst. Sie bieten Ihnen als Entwickler eine Flle von Mglichkeiten, Informationen zu beschaffen, und das Beste daran ist, dass .NET bereits die meisten komplizierten Arbeiten fr Sie erledigt hat. Im Grunde ist ein Dialogfeld ein Form-Objekt, dessen FormBorderStyle-Eigenschaft auf FixedDialog gesetzt ist. (Wie Sie von Tag 3 noch wissen, bedeutet dies, dass das Fenster genau wie ein typisches Windows Forms-Fenster aussieht, nur dass sich seine Gre nicht ndern lsst.) Zusammen mit einer Eigenschaft, die als Modalitt bezeichnet wird (mehr dazu spter), macht dies Dialogfelder sehr effizient beim Einholen von Benutzereingaben. Der Benutzer kann nicht mit seiner Ttigkeit fortfahren, bis er das Dialogfeld beantwortet hat, um sie aus dem Weg zu schaffen. Obwohl es zutrifft, dass die einfachsten Dialogfelder nur etwas Text und zwei Schaltflchen fr OK und ABBRECHEN aufweisen, sollte Sie dies nicht davon abhalten, dem Dialogfeld eigene Steuerelemente hinzuzufgen. Sie knnen sogar jedes Windows FormsSteuerelement selbst Mens und Statusleisten integrieren, als ob es ein Form-Objekt wre. Doch wozu sollte man ein Dialogfeld zu einer kompletten Anwendung ausbauen? Sicher nicht bis in Word-Dimensionen, doch vielleicht bis zur Komplexitt eines Taschenrechners oder eines Temperaturumrechners (Fahrenheit in Celsius und umgekehrt). Doch letzten Endes luft es auf Folgendes hinaus: Forms wurden dafr entwickelt, als Anwendungen zu agieren, und Dialogfelder nicht.
Ich empfehle Verwenden Sie wie bisher Forms fr die Anwendungserstellung, es sei denn, Sie htten einen triftigen Grund, der dagegen sprche. Bitte beachten Sie Verlassen Sie sich nicht nur auf Dialogfelder, um Benutzereingaben zu erhalten. Statt dessen sollten Sie sie nur einsetzen, um dem Benutzer etwas mitzuteilen.

233

Mit Dialogfeldern arbeiten

7.2

Unterschiedliche Arten von Dialogen

Am gebruchlichste ist das OK/ABBRECHEN-Dialogfeld, doch es sind noch einige andere Typen verfgbar. Das .NET Framework stellt verschiedene Dialogfelder fr die Mehrzahl der blichen Windows-Aufgaben, die Benutzereingaben erfordern, zur Verfgung. Es steht Ihnen sogar frei, einen Benutzer dazu zu zwingen, auf ein Dialogfeld zu antworten, bevor er irgendetwas anderes tun kann. Wir wollen einen Blick auf die Eigenschaften werfen, die Dialogfelder steuern.

Modal vs. nichtmodal


Ein modales Formular oder Dialogfeld fordert eine Eingabe vom Benutzer, bevor er mit seiner Ttigkeit in der Anwendung fortfahren kann. Ein Beispiel dafr ist die nderungen speichern?-Abfrage in Word. Erscheint sie, kann man in Word nichts anderes tun als sie zu beantworten. Versucht man in das Hauptdokumentfenster zurckzugehen, erhlt man einen Fehlerton und das Dialogfeld fordert erneut zur Antwort auf. Daher erfordert ein modaler Dialog eine Benutzereingabe. Modale Dialoge halten Sie zwar in der jeweiligen Anwendung auf, Sie knnen aber jederzeit zu einer anderen Anwendung wechseln, etwa zu Excel oder Photoshop. Ein nichtmodales Dialogfeld ist das genaue Gegenteil: Es verlangt keine Benutzereingabe, und der Benutzer kann zwischen der Anwendung und dem Dialog nach Belieben hin und her wandern. Der typische Einsatzbereich dafr ist ein Hilfe-Fenster, das sich neben der Anwendung positioniert und hufig auftretende Fragen beantwortet. Solche Dialoge verlangen nicht, dass Sie mit ihnen interagieren, obwohl Sie das natrlich tun knnen. Ein gutes Beispiel wre der Microsoft Office-Assistent in Form einer Broklammer. Es ist fr den Entwickler oftmals schwieriger, mit nichtmodalen Dialogen umzugehen als mit modalen, denn er muss praktisch zwei Formulare gleichzeitig berwachen und passende Daten hin und her leiten. Bei einem modalen Dialog hingegen hat man es entweder mit dem Dialogfeld oder mit der Anwendung zu tun, nie aber mit beiden zugleich, was die Sache erleichtert. Aus diesem Grund werden nichtmodale Dialoge fast ausschlielich fr die Informationsprsentation eingesetzt, aber nicht fr die Datenbeschaffung.

234

Dialogfelder

Die Standarddialogfelder
Standarddialogfelder (Common Dialog Boxes) dienen dazu, allgemein bliche Aufgaben vorzubereiten wie etwa das ffnen und Speichern einer Datei, die Auswahl einer Schriftart usw. Da sie fr hufige Aufgaben eingesetzt werden, sind sie an vielen Stellen anzutreffen man denke nur an jede Anwendung, die das ffnen einer Datei erlaubt. Sie setzt die gleiche Benutzeroberflche fr diese Aufgabe ein wie viele andere Anwendungen auch: ein Standarddialogfeld. Fr den Entwickler ist dies enorm hilfreich, denn er muss fr jede Anwendung, die er schreibt, keine eigenes Dialogfeld programmieren. In .NET Framework sind bereits sieben Klassen fr Standarddialogfelder fix und fertig enthalten:

ColorDialog: Damit kann der Benutzer eine Farbe aus einer vorgegebenen Palette aus-

whlen (etwa um die Farbe einer Schriftart zu ndern).


FontDialog: Damit kann der Benutzer eine Schriftart auswhlen. OpenFileDialog: Damit kann der Benutzer eine zu ffnende Datei auswhlen. Das

Dialogfeld ffnet die Datei nicht selbst, sondern erlaubt nur ihre Auswahl; Sie mssen in Ihrer Anwendung dafr sorgen, die ausgewhlte Datei zu holen und zu ffnen; das Dialogfeld erledigt das nicht automatisch.

PageSetupDialog: Damit kann der Benutzer in Windows Dokumente formatieren (Sei-

tenrnder einstellen, Papiergre whlen usw.).

PrintDialog: Damit kann der Benutzer einen Drucker auswhlen, Druckoptionen einstellen und ein Dokument drucken. PrintPreviewDialog: Seiten- oder Druckvorschau. Damit kann der Benutzer prfen, wie ein Dokument auf Papier aussehen wird, bevor er es ausdruckt. SaveFileDialog: Damit kann der Benutzer Dateien in einem ausgewhlten Ordner

speichern (gleicht dem SPEICHERN UNTER...-Dialog in Word). Diese Klassen verhalten sich genau wie andere Windows Forms-Steuerelemente oder -Dialogfelder, daher sollten Sie sie rasch erlernen knnen. Wir kommen heute noch auf sie zurck. Wir wollen zunchst sehen, wie man einfache Dialogfelder verwendet.

7.3

Dialogfelder

Das einfachste Dialogfeld ist die MessageBox-Klasse (Meldungsfeld). Sie verfgt ber ein einziges (nicht geerbtes) Mitglied, die Show-Methode, die das Dialogfenster, das nur eine OK-Schaltflche aufweist, auf den Bildschirm bringt:

235

Mit Dialogfeldern arbeiten

MessageBox.Show("Hallo Welt")

Diese Codezeile zeigt ein Dialogfenster mit dem Text Hallo Welt an sowie eine OKSchaltflche. Fr eine Reihe von Situationen reicht dieser Funktionsumfang aus. Die Show-Methode verfgt jedoch noch ber einige Parameter, mit denen Sie sie Ihren Wnschen anpassen knnen. Sie knnen eine Instanz der MessageBox nicht direkt erzeugen. Der folgende Code etwa erzeugt einen Fehler:
dim msgMyMessage as New MessageBox

Der einzige Weg, auf dem Sie mit der MessageBox interagieren knnen, besteht im Aufruf ihrer Show-Methode:
MessageBox.Show("Hallo Welt!")

Tabelle 7.1 fhrt die verschiedenen Parameter fr die Show-Methode auf, und zwar in der Reihenfolge, in der sie im Methodenaufruf stehen mssen (bis auf Nummer 2 sind alle Parameter optional).
Nr. des Parameters 1 2 3 4 5 6 7 Typ
IWin32Window

Beschreibung Das Fenster, vor dem das Dialogfeld angezeigt werden soll (z.B. Me in VB .NET oder this in C#). Der im Dialogfenster anzuzeigende Text. Dies ist der einzige obligatorische Parameter. Der in der Titelzeile anzuzeigende Text. Anzahl und Typ der anzuzeigenden Schaltflchen. Das im Dialogfenster anzuzeigende Symbol. Legt die (vorausgewhlte) Standardschaltflche fest. Diverse Optionen fr das Dialogfeld.

String

String MessageBoxButtons MessageBoxIcon MessageBoxDefaultButton MessageBoxOptions

Tabelle 7.1: Parameter fr die Methode MessageBox.Show

Sie knnten die MessageBox auf folgende Art und Weise anzeigen:
MessageBox.Show(Me, "Hallo Welt", "Mein Dialogfeld") MessageBox.Show("Hallo Welt", "Mein Dialogfeld", MessageBoxButtons.OKCancel) MessageBox.Show(Me, "Hallo Welt", "Mein Dialogfeld", MessageBoxButtons.OKCancel, MessageBoxIcon.Hand)

236

Dialogfelder

Diese drei Aufrufe erzeugen die drei Dialogfelder, die Sie in Abbildung 7.2sehen.
Abbildung 7.2: Sie knnen einem Meldungsfeld auf einfache Weise weitere (bis zu drei) Schaltflchen hinzufgen.

Die ersten drei in Tabelle 7.1 aufgefhrten Parameter sind leicht zu verstehen. Die vier letzten hingegen sind Aufzhlungen, die Anzeigeoptionen fr das Dialogfeld angeben. Die MessageBoxButtons-Aufzhlung enthlt die Werte AbortRetryIgnore, OK, OKCancel, RetryCancel, YesNo und YesNoCancel. Der Zweck jeder einzelnen ist einfach herauszufinden: AbortRetryIgnore zeigt drei Schaltflchen an, die die Aufschriften BEENDEN, WIEDERHOLEN und IGNORIEREN tragen; OK zeigt einfach eine Schaltflche mit OK an, usw.
MessageBoxIcon zeigt neben dem Text im Meldungsfenster ein Symbol an. Die Werte knnen in Asterisk, Error, Exclamation, Hand, Information, None, Question, Stop oder Warning

bestehen. Die tatschlich angezeigten Symbole knnen von Betriebssystem zu Betriebssystem variieren (in Windows sind es oftmals die gleichen). Die Abbildung 7.3 zeigt jedes dieser Symbole in Windows NT.

Abbildung 7.3: Der neunte Wert der MessageBoxIconAufzhlung lautet None (keines). MessageBoxDefaultButton ist eine leicht zu erlernende Eigenschaft sie besitzt nur drei Werte: Button1, Button2 und Button3. Die Eigenschaft legt fest, welche Schaltflche im

Dialogfenster den voreingestellten Fokus hat. Diese Schaltflche ist vorausgewhlt, wenn das Dialogfeld angezeigt wird, und ist auch diejenige, die angeklickt wird, falls der Benut-

237

Mit Dialogfeldern arbeiten

zer die ()-Taste drckt. (Nochmals: Es passen maximal drei Schaltflchen in ein Meldungsfeld.)
MessageBoxOptions schlielich besitzt vier nicht miteinander verwandte Werte. DefaultDesktopOnly und ServiceNotification geben an, dass das Meldungsfeld auf dem gerade

aktiven Bildschirm erscheinen soll (falls der Benutzer mehr als einen Monitor einsetzt). RightAlign gibt vor, dass der Text im Meldungsfeld rechtsbndig ausgerichtet sein soll, und RtlReading gibt an, dass der Text so formatiert sein soll, dass man ihn von rechts nach links lesen kann (was etwa in arabischen Lndern hilfreich ist). Mit Hilfe der Or- und |Operatoren knnen Sie diese Werte miteinander kombinieren.
MessageBox.Show("Hallo Welt","Mein Dialogfeld"", MessageBoxButtons.OKCancel, MessageBoxIcon.None, MessageBoxDefaultButton.Button1, MessageBoxOptions.RightAlign Or MessageBoxOptions.ServiceNotification)

Eigene Dialogfelder erstellen


Die MessageBox-Klasse ist ein ausgezeichnetes vorgefertigtes Dialogsteuerelement, das eine ganze Reihe von Optionen anbietet. Doch in manchen Fllen gengt die MessageBox Ihren Ansprchen nicht. In diesen Fllen knnen Sie Ihre eigene Dialogfeldklasse erstellen. Diese aufzubauen entspricht fast dem Aufbau einer Windows Forms-Klasse; man erbt von System.Windows.Forms.Form, erstellt Steuerelemente, fgt sie dem Formular hinzu und sieht auch Ereignishandler vor. Der einzige Unterschied besteht darin, dass man dem bergeordneten Formular (das das Dialogfeld anzeigt) alle notwendigen Eigenschaften zur Verfgung stellt. Lassen Sie uns als Beispiel einmal das DATEI/FFNEN-Dialogfeld von Microsoft Word, das in Abbildung 7.4 zu sehen ist, auf seine Bestandteile hin untersuchen.

Abbildung 7.4: Das Dialogfeld zum ffnen von Dateien enthlt verschiedene Arten von Steuerelementen.

238

Dialogfelder

Sie vermuten sicher schon, welche Arten von Steuerelementen ntig sind, um dieses Dialogfenster aufzubauen: eine ComboBox fr die Dropdown-Listen der Verzeichnisse und der Dateitypen, eine Reihe von Button-Steuerelementen fr die FFNEN- und ABBRECHENSchaltflchen, ein paar Label-Steuerelemente, mglicherweise ein TreeView-Steuerelement und ein paar TextBox-Steuerelemente. Sobald der Benutzer eine Datei ausgewhlt hat, will die Hauptanwendung nur eines wissen: Welche Datei ist das? Daraufhin wird der ausgewhlte Dateiname im Textfeld DATEINAME angezeigt. Daher muss die Hauptanwendung die Eigenschaft Text dieser TextBox wissen, damit sie die Datei angemessen behandeln kann. Um diese Eigenschaftstypen dem Hauptformular offen zu legen, mssen wir Eigenschaften erstellen (hnlich wie die Text-Eigenschaft). Listing 7.1 zeigt eine benutzerdefinierte Dialogfeldklasse, mit der der Benutzer die Titelzeile seines Formulars ndern kann. Listing 7.1: Ihre erste Anwendung mit einem benutzerdefinierten Dialogfeld
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: Imports System Imports System.Windows.Forms Imports System.Drawing namespace TYWinForms.Day7 public class ChangeCaption : Inherits Form private btnChange as new Button private tbCaption as new TextBox public readonly property Caption as String Get Return tbCaption.Text End Get end property public sub New tbCaption.Location = new Point(10,10) btnChange.Text = "ndern!" btnChange.Location = new Point(125,10) AddHandler btnChange.Click, new EventHandler(AddressOf Me.Clicked) Me.Text = "berschrift ndern" Me.Size = new Size(250,100) Me.Controls.Add(btnChange) Me.Controls.Add(tbCaption) end sub

239

Mit Dialogfeldern arbeiten

30: 31: 32: 33: 34:

private sub Clicked(Sender as Object, e as EventArgs) Me.Hide end sub end class End Namespace

Speichern Sie diese Datei als listing7.1.vb. Auf den ersten Blick sieht der Code wie jede andere Windows Forms-Anwendung aus. In den Zeilen 8 und 9 erstellen Sie zwei Steuerelemente, richten in den Zeilen 18 bis 27 Eigenschaften in einem Konstruktor ein und erstellen einen Ereignishandler in den Zeilen 30 bis 32. Der Hauptunterschied besteht darin, dass es in diesem Code keine Main-Methode gibt. Das liegt daran, dass diese Klasse niemals selbststndig ausgefhrt wird sie sollte nur in einem Formular oder einer Anwendung verwendet werden. Werfen Sie einen Blick auf Zeile 30 und den Ereignishandler. Er tut nur eines, nmlich die Hide-Methode des Formulars aufzurufen. Ohne dies wrde nichts passieren, sobald der Benutzer auf die Schaltflche Titelzeile ndern geklickt hat. Da wir ein modales Dialogfeld erstellen wollen, wrde dies bedeuten, dass der Benutzer nicht wieder zu der ursprnglichen Anwendung zurckkehren knnte. Indem Hide aufgerufen wird, bringen wir das Dialogfeld zum Verschwinden und holen statt dessen die ursprngliche Anwendung in den Vordergrund, so dass wieder eine Interaktion mglich ist. Beachten Sie schlielich die Zeilen 11 bis 15, die das Erstellen von Eigenschaften in einer Klasse illustrieren. Die Deklaration hnelt der einer Funktion, verwendet aber das Schlsselwort property statt function. Innerhalb dieser Eigenschaft finden sich zwei besondere Schlsselwrter: Get und Set. Der Code in Get wird ausgefhrt, sobald eine andere Klasse auf diese Eigenschaft zugreifen will. Set hingegen wird eingesetzt, wenn eine andere Klasse fr diese Eigenschaft einen Wert setzt. In unserem Fall brauchen wir jedoch keinen Set-Teil, daher schreiben wir nur den fr Get und sorgen dafr, dass die Eigenschaft als readonly (Zeile 11) markiert ist. In die Get- und Set-Teile knnen Sie jeden beliebigen Code einfgen; hier jedoch wollen wir nur den Wert zurckgeben, der sich in der TextBox befindet, wobei wir das Return-Schlsselwort verwenden. In C# wrde diese Eigenschaft wie in folgendem Codestck aussehen:
public string Caption { get { return tbCaption.Text; } }

Beachten Sie, dass C# nicht die Schlsselwrter property oder readonly erfordert. Wenn Sie die Eigenschaft value setzen wollen, verwenden Sie das Schlsselwort value:

240

Dialogfelder

'VB .NET Set tbCaption.Text = value End Set //C# set { tbCaption.Text = value; }

Die einzigen public-Mitglieder in der ChangeCaption-Klasse sind der Konstruktor und die Caption-Eigenschaft. Da wir nicht wnschen, dass andere Klassen direkt auf den Rest der Mitglieder zugreifen, machen wir sie alle private. Sie knnen diese Vorgehensweise bei der Deklaration von public- und privateVariablen auch fr die Erstellung von Eigenschaften Ihrer regulren Klassen verwenden. Wir wollen uns die Anwendung ansehen, die diese benutzerdefinierte Dialogfeldklasse verwenden soll. Listing 7.2 zeigt eine einfache Applikation, mit der der Benutzer die Titelzeile des Formulars ndern kann, indem er das ChangeCaption-Dialogfeld aus Listing 7.1 sowie dessen Caption-Eigenschaft einsetzt. Listing 7.2: Die Verwendung des benutzerdefinierten Dialogfeldes
1: Imports System 2: Imports System.Windows.Forms 3: Imports System.Drawing 4: 5: namespace TYWinForms.Day7 6: 7: public class Listing72 : Inherits Form 8: private btnChange as new Button 9: 10: public sub New 11: btnChange.Text = "berschrift ndern" 12: btnChange.Location = new Point(75,75) 13: btnChange.Size = new Size(100,75) 14: AddHandler btnChange.Click, new EventHandler(AddressOf  Me.ShowChangeBox) 15: 16: Me.Text = "berschrift ndern" 17: Me.Controls.Add(btnChange) 18: end sub 19: 20: public sub ShowChangeBox(Sender as Object, e as EventArgs) 21: dim dlgChangeCaption as new ChangeCaption 22: dlgChangeCaption.ShowDialog

241

Mit Dialogfeldern arbeiten

23: 24: 25: 26: 27: 28: 29: 30: 31:

Me.Text = dlgChangeCaption.Caption end sub public shared sub Main() Application.Run(new Listing72) end sub end class End Namespace

Speichern Sie diese Datei unter dem Namen listing7.2.vb. Listing 7.2 zeigt eine weitere einfache Windows Forms-Klasse, nur dass diese die Hauptanwendung darstellt und daher eine Main-Methode aufweist. Die Zeilen 1 bis 18 sind typisch; sie erstellen und instantiieren das Formular und seine Steuerelemente. Erst in Zeile 20 beginnt es interessant zu werden. Die Methode ShowChangeBox behandelt das Click-Ereignis der Schaltflche btnChange. In Zeile 21 erzeugen Sie eine neue Instanz der in Listing 7.1 erstellten ChangeCaption-Klasse. Beachten Sie, dass dieses Vorgehen dem beim Erstellen jedes anderen .NET-Objekts entspricht. In Zeile 22 rufen Sie die
ShowDialog-Methode auf, die das Dialogfenster zur modalen Anzeige veranlasst.

Moment mal! In Ihrer ChangeCaption-Klasse haben Sie keine ShowDialogMethode erstellt. Wo kam sie also her? Die Klasse System.Windows.Forms.Form enthlt die ShowDialog-Methode, weshalb Ihre Dialogfeldklasse sie erben konnte. Das bedeutet aber auch, dass absolut jedes Windows Form-Formular als Dialogfeld eingesetzt werden kann. Das bietet Ihnen eine Menge Flexibilitt, wenn Sie Ihre Anwendungen schreiben. Nachdem Zeile 22 ausgefhrt und das Dialogfeld angezeigt wurde, geht die Kontrolle ber die Anwendung an das modale Dialogfeld ber bis dieses Fenster wieder geschlossen wird, kann die Hauptanwendung nichts tun. Sobald Sie etwas in das Dialogfeld eingeben und die Schaltflche NDERN! anklicken, geht die Kontrolle wieder an die Hauptanwendung ber. Die Ausfhrung geht in Zeile 24 weiter, die die Caption-Eigenschaft des Dialogfeldes holt und sie der Text-Eigenschaft des aktuellen Formulars zuweist. Nun verstehen Sie, wie die Caption-Eigenschaft aus Listing 7.1 eingesetzt wird. Da diese beiden Listings in unterschiedlichen Dateien gespeichert sind, mssen Sie beide zu einer Anwendung kompilieren. Mit Hilfe von Visual Studio .NET ist dies einfach: Sorgen Sie nur dafr, dass sich beide Dateien im selben Projekt befinden und kompilieren Sie sie dann ganz normal (indem Sie ERSTELLEN aus dem ERSTELLEN-Men auswhlen).

242

Dialogfelder

Wenn Sie den Befehlszeilencompiler verwenden, geben Sie nur die beiden Dateien im Befehl an:
vbc /t:winexe /r:system.windows.forms.dll /r:system.drawing.dll listing7.1.vb listing7.2.vb

Solange sich diese beiden Dateien im selben Ordner befinden, funktioniert dieser Befehl und Sie erhalten eine ausfhrbare Datei namens Listing7.1.exe (die Reihenfolge, die Sie fr die zu kompilierenden Dateien angeben, bestimmt den Namen der ausfhrbaren Datei). Das war's schon. Abbildung 7.5 zeigt das Ergebnis.

Abbildung 7.5: Es ist recht einfach, ein benutzerdefiniertes modales Dialogfeld zu erstellen.

Beachten Sie, dass Sie keine besonderen Eigenschaften fr Ihre benutzerdefinierte Dialogfeldklasse einrichten mussten. Indem Sie die ShowDialog-Methode aufrufen, stellt die CLR sie automatisch als modales Dialogfeld dar. Doch genau wie in einem gewhnlichen Formular knnen Sie Eigenschaften beliebig modifizieren, so etwa SizeGripStyle, um den Grenziehpunkt in der unteren rechten Ecke des Formulars zu ndern, oder ControlBox, um die rechts oben verfgbaren Steuerschaltflchen festzulegen. Wollen Sie Ihr Dialogfeld hingegen nichtmodal prsentieren, rufen Sie statt ShowDialog die Methode Show auf. Der Benutzer wird zwischen Dialogfenster und Hauptanwendung hin und her wechseln knnen. Doch in der soeben erstellten Anwendung wrde dies eine Reihe von Problemen verursachen:

Da die Ausfhrung im Hauptformular nicht angehalten wird, sobald die Show-Methode aufgerufen wurde, wird Zeile 24 des Listing 7.2 (wo die Titelzeile aus dem Dialogfeld geholt wird) sofort ausgefhrt und die Titelzeile des Formulars wird sich in eine leere Zeichenfolge verwandeln. Denn schlielich ist bei der Dialogfelderstellung das Textfeld tbCaption leer, und Zeile 24 wird ausgefhrt, bevor Sie dazukommen, Text einzutragen und auf die NDERN!-Schaltflche zu klicken. Der Benutzer kann fortwhrend auf die Schaltflche TITELZEILE NDERN im Hauptformular klicken, was dazu fhrt, dass viele Dialogfelder auf einmal angezeigt werden.

243

Mit Dialogfeldern arbeiten

Wenn der Benutzer Text in das tbCaption-Feld eingibt und die Schaltflche NDERN! anklickt, verschwindet in diesem Fall das Dialogfeld und sonst passiert nichts; die Titelzeile des Formulars ndert sich nicht, denn Zeile 24 wurde bereits lange vor dem Anklicken der NDERN!-Schaltflche ausgefhrt. Um also die Titelzeile dennoch zu bestimmen, mssen Sie irgendwie auf die Eigenschaft tbCaption.Text des Dialogfeldes zugreifen. Dies gelingt, wenn Sie eine neue public-Eigenschaft erstellen:
Public property Caption As String Get return tbCaption.Text End Get Set tbCaption.Text = value End Set End Property

Die Lsung fr das zweite Problem besteht im Einsatz eines modalen Dialogfeldes. Doch dann muss der Benutzer das Dialogfeld explizit schlieen, bevor ihm erlaubt ist, zum Hauptformular zurckzukehren. Sorgen Sie dafr, dass Sie nur modale Dialogfenster verwenden, wenn Sie diese Funktionsweise nutzen wollen.

Informationen abrufen
Wir haben bereits gesehen, wie man mit Hilfe von Eigenschaften Daten aus einem Dialogfeld beschafft. Doch manchmal reichen Eigenschaften nicht aus, um jede gewnschte Information zu bekommen. Gelegentlich mssen Sie auch wissen, auf welche Weise der Benutzer das Dialogfeld geschlossen hat: Hat er auf die OK-Schaltflche geklickt? Oder doch auf die ABBRECHEN-Schaltflche? Diese Fakten, die man als das Ergebnis (result) des Dialogfeldes bezeichnet, knnen die Arbeitsweise Ihrer Anwendung verndern, so dass es ntig ist, auch diese Werte zu beschaffen. Die Aufzhlung DialogResult enthlt mehrere Werte, mit deren Hilfe Sie das Ergebnis eines Dialogfeldes bestimmen. Die ShowDialog-Methode gibt einen Wert aus dieser Aufzhlung zurck, so dass Sie damit tun knnen, was Sie wollen, etwa so:
dim objDR as DialogResult objDR = dlgChangeCaption.ShowDialog if objDR = DialogResult.OK then 'die Titelzeile ndern elseif objDR = DialogResult.No then 'nichts tun end if

244

Dialogfelder

Je nach dem vom Dialogfeld gelieferten Ergebnis knnen Sie eine bedingte Ausfhrung beginnen. Fr jede Schaltflche in einem Dialogfeld gibt es einen DialogResult-Wert (und noch mehr):

Abort/Beenden Cancel/Abbrechen Ignore/Ignorieren No/Nein None/Kein OK Retry/Wiederholen Yes/Ja

Listing 7.3 zeigt ein Beispiel dafr, wie man DialogResult in einem Meldungsfeld einsetzt. Listing 7.3: Das Ergebnis eines Dialogfeldes abrufen (in C#)
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: using System; using System.Windows.Forms; using System.Drawing; namespace TYWinForms.Day7 { public class Listing73 : Form { public Listing73() { if (MessageBox.Show("Wnschen Sie eine Vollbilddarstellung dieser Anwendung?", "Listing 7.3", MessageBoxButtons.YesNo) == DialogResult.Yes) { this.Size = Screen.GetWorkingArea(this).Size; this.DesktopLocation = new Point(0,0); } } public static void Main() { Application.Run(new Listing73()); } } }

In Zeile 9 prfen Sie das Ergebnis der MessageBox.Show-Methode. Wenn es dem DialogResult.Yes-Wert entspricht (der Benutzer also die JA-Schaltflche angeklickt hat), fhren Sie den Code in den Zeilen 10 bis 11 aus: Er setzt die Gre des Formulars auf die Gre des Benutzerbildschirms und seine Position

245

Mit Dialogfeldern arbeiten

dort auf die obere linke Ecke. Alternativ knnen Sie die Zeilen 10 bis 11 durch folgenden Code ersetzen, um die Anwendung zu maximieren:
this.WindowState = FormWindowState.Maximized;

Weil Sie die MessageBox mit Hilfe des Schaltflchensatzes MessageBoxButtons.YesNo angezeigt haben, wei die CLR, dass die JA-Schaltflche den Wert DialogReturn.Yes und die NEIN-Schaltflche DialogResult.No zurckgeben soll. Erinnern Sie sich an das Beispiel des Word-Dialogfensters Mchten Sie die nderungen in [Dokumentname] speichern?. Klicken Sie auf JA, dann gibt das Dialogfeld Dialogresult.Yes zurck und Word wei Bescheid: Es fhrt den Code zum Sichern des Dokuments aus (es ffnet das Dialogfeld DATEI/SPEICHERN UNTER nur, falls es sich um eine neues Dokument handelt) und schliet dann das Fenster. Klicken Sie hingegen auf NEIN, wird DialogResult.No bergeben, das Dokument wird nicht gespeichert und Word wird beendet (sofern Sie den BEENDEN-Befehl in irgendeiner Form erteilt haben). Klicken Sie auf ABBRECHEN, lautet das Ergebnis DialogResult.Cancel und nichts passiert (das Dokument wird weder gespeichert noch geschlossen, auch Word wird nicht beendet). Das Ergebnis eines Dialogfeldes auf diese Weise abzurufen ist eine hervorragende, standardisierte Mglichkeit, um Anwendungen ber Benutzeraktionen zu informieren. Was passiert jedoch, wenn Sie Ihr eigenes Dialogfeld erstellt haben? Woher wei die CLR, dass Ihre NDERN!-Schaltflche einen Wert DialogResult.Yes zurckgeben soll? Tatschlich ist es viel einfacher als Sie befrchten. Schaltflchen und Formulare verfgen ber eine DialogResult-Eigenschaft, mit der Sie angeben knnen, welches Ergebnis zurckgegeben werden soll. Schauen wir uns eine modifizierte Version des benutzerdefinierten Dialogfeldes aus Listing 7.1 an, die hier in Listing 7.4 in C#-Code zu sehen ist. Listing 7.4: Eine modifiziertes benutzerdefiniertes Dialogfeld in C#
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: using System; using System.Windows.Forms; using System.Drawing; namespace TYWinForms.Day7 { public class ChangeCaption2 : Form { private Button btnChange = new Button(); private TextBox tbCaption = new TextBox(); public String Caption { get { return tbCaption.Text; } }

246

Dialogfelder

16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30:

public ChangeCaption2() { tbCaption.Location = new Point(10,10); btnChange.Text = "ndern!"; btnChange.Location = new Point(125,10); btnChange.DialogResult = DialogResult.OK; this.Text = "berschrift ndern "; this.Size = new Size(250,100); this.Controls.Add(btnChange); this.Controls.Add(tbCaption); } } }

Von der Sprache (und der Namensnderung der Klasse in Zeile 7) einmal abgesehen, gibt es in diesem Listing nur zwei Unterschiede zum Listing 7.1. Der Ereignishandler fr das Clicked-Ereignis ist jetzt verschwunden, ebenso die Zuweisung eines Delegaten zu der Schaltflche. Das wird gleich erklrt. Der zweite Unterschied besteht im Hinzufgen der Zeile 22. Hier weisen Sie der btnChange-Schaltflche den Wert DialogResult.OK zu. Dies bewirkt zweierlei. Erstens gibt es dem bergeordneten Formular den Wert DialogResult.OK zurck (so wie die MessageBox in Listing 7.3 DialogResult.Yes zurckgab). Wird zweitens diese Schaltflche angeklickt, schliet die CLR das Formular automatisch fr Sie. Daher brauchen Sie nicht mehr die Hide-Methode aufzurufen und demzufolge auch keinen Ereignishandler fr das Clicked-Ereignis. Indem Sie also die DialogResult-Eigenschaft angegeben haben, wei die CLR, dass diese Klasse ein Dialogfeld ist und sich entsprechend verhlt. Die Hauptanwendung muss sich ebenfalls ein klein wenig ndern. Die ShowChangeBoxMethode aus Listing 7.2 muss wie im folgenden Codestck aussehen:
public void ShowChangeBox(Object Sender, EventArgs e) { DialogResult objDR; ChangeCaption2 dlgChangeCaption = new ChangeCaption2(); objDR = dlgChangeCaption.ShowDialog(); if (objDR == DialogResult.OK) { this.Text = dlgChangeCaption.Caption; } this.Text = dlgChangeCaption.Caption; }

247

Mit Dialogfeldern arbeiten

(Vergessen Sie nicht, auch den Rest des Listings in C#-Code umzuwandeln.) Das Ergebnis des neuen Dialogfeldes wird in der objDR-Variablen gespeichert, die in der zweiten Zeile erstellt wurde. Dieser Wert wird dann mit DialogResult.OK verglichen, und falls sie gleichwertig sind, ndert sich die Titelzeile des Formulars. Ist das nicht der Fall, passiert nichts.
Ich empfehle Weisen Sie den Schaltflchen in Ihren benutzerdefinierten Dialogfeldern DialogResult-Werte zu. Dies ist eine Standardkonvention und erleichtert Ihnen und denjenigen, die Ihre Klasse verwenden, die Verwendung Ihrer benutzerdefinierten Dialogfelder. Bitte beachten Sie Verlassen Sie sich nicht auf das bergeordnete Formular, dass es Ihr Dialogergebnis interpretiert, selbst wenn Sie der Einzige sind, der es je zu sehen bekommt. Das Zuweisen von DialogResult-Werten sorgt dafr, dass Ihre Anwendung standardisierte Werte verwendet, die leicht auszuwerten sind.

7.4

Standarddialogfelder

Wie gesagt gibt es sieben Standarddialogfelder in .NET (vgl. Aufzhlung oben). Sie sind bereits vorgefertigt, um das Ausfhren der entsprechenden Aufgaben zu erleichtern. In den folgenden Abschnitten wollen wir etwas ber ihren jeweiligen Funktionsumfang erfahren. (Nhere Informationen finden Sie in Anhang B.) Alle sieben Dialogfelder auer PrintPreviewDialog weisen folgende zwei Mitglieder auf: ShowHelp und Reset. Das erste gibt an, ob im Dialogfeld eine HilfeSchaltflche angezeigt werden soll. Die Reset-Methode setzt alle Eigenschaften eines bestimmten Dialogs auf ihre Vorgabewerte zurck. Diese Methode erweist sich im Umgang mit diesen vielschichtigen Steuerelementen als besonders hilfreich.

Ein- und Ausgabe von Dateien


Die zwei Standarddialogfelder fr die Ein- und Ausgabe (E/A bzw. I/O) von Dateien sind die Steuerelemente OpenFileDialog und SaveFileDialog. Sie haben eine Reihe von Funktionen gemeinsam, weshalb die meisten ihrer Eigenschaften, die wir hier besprechen, in beiden zu finden sind. Natrlich ist ihre wichtigste Eigenschaft FileName (und FileNames), denn sie gibt an, welche Datei der Benutzer zum ffnen oder Speichern ausgewhlt hat. Ihr Hauptformular kann diesen Wert holen und die betreffende Datei nach Bedarf ffnen oder speichern (mehr dazu an Tag 11). Das Instantiieren und Anzeigen dieser Steuerelemente ist einfach:

248

Standarddialogfelder

dim dlgOpen as new SaveFileDialog dlgOpen.ShowDialog

Dieser Code ergibt das in Abbildung 7.6 dargestellte Dialogfeld.

Abbildung 7.6: Das Steuerelement fr den DATEI/SPEICHERN-Dialog ist in den meisten WindowsAnwendungen zu finden.

Beide Dialogfelder geben entweder DialogResult.OK oder DialogResult.Cancel zurck, je nachdem, ob der Benutzer SPEICHERN bzw. FFNEN oder ABBRECHEN angeklickt hat. Daraufhin lsst sich die FileName-Eigenschaft abfragen:
if dlgOpen.ShowDialog = DialogResult.OK then Me.Text = dlgOpen.FileName end if

Im OpenFileDialog knnen Sie dem Benutzer erlauben, mehr als eine Datei auszuwhlen, indem Sie MultiSelect auf true setzen. Die FileName-Eigenschaft nimmt dann ein Array von Zeichenfolgen auf, die diese Dateien reprsentieren. Zustzlich zu diesen Haupteigenschaften (FileName bzw. FileNames) gibt es noch weitere, mit denen Sie die Funktionen dieser Dialogfelder anpassen knnen. Die Filter-Eigenschaft lsst sich dazu verwenden, die Dateitypen festzulegen, die in den Feldern SPEICHERN UNTER/DATEITYP und FFNEN/DATEITYP zu sehen sind. Diese Filter begrenzen die Typen von Dateien, die der Benutzer speichern oder ffnen kann. Der Filter selbst ist eine speziell formatierte Zeichenfolge. Der folgende Code lsst nur das ffnen von Word(.doc) und Text- (.txt) Dateien zu:
dlgOpen.Filter ="Nur-Text-Dateien (*.txt)|*.txt|Word-Dokumente (*.doc)|*.doc "

Die Zeichenfolge lsst sich in folgende Bestandteile zerlegen: Beschreibung (Erweiterung) | Filterzeichenfolge

249

Mit Dialogfeldern arbeiten

Die Beschreibung ist der benutzerfreundliche Text, den der Benutzer im jeweiligen Dialogfeld angezeigt bekommt. Der senkrechte Strich (das Pipe-Symbol, | ) trennt in dieser Zeichenfolge die Felder. Die Filterzeichenfolge wird vom Dialogfeld dazu verwendet, Dateinamenendungen zu filtern; *.txt bedeutet, dass nur Dateien mit der .txt-Endung im Dialogfeld erscheinen werden. Nachfolgende Dateitypen lassen sich mit dem senkrechten Strich abtrennen.
FilterIndex lsst sich verwenden, um den Standardfilter, der vorausgewhlt ist, anzugeben. InitialDirectory ist eine Zeichenfolge, die das Anfangsverzeichnis angibt, in dem das

Dialogfeld mit der Anzeige beginnt. Eine Reihe von Eigenschaften fordern den Benutzer unter bestimmten Bedingungen zu Eingaben auf. Setzt man die CheckPathExists-Eigenschaft in beiden Steuerelementen auf true, dann gibt das Dialogfeld eine Warnung aus, falls der aktuell angegebene Pfad nicht existiert. Die CheckFileExists-Eigenschaft des Dialogfeldes OpenFileDialog erledigt die gleiche Aufgabe hinsichtlich einer bestimmten Datei. CreatePrompt und OverwritePrompt im SaveFileDialog hneln einander; beim ersten wird der Benutzer gefragt, ob eine neue Datei angelegt werden soll, falls sie noch nicht existiert, und das zweite verlangt vom Benutzer eine Entscheidung, ob eine vorhandene Datei berschrieben werden soll.

Farben und Schriftarten whlen


Das Steuerelement ColorDialog ist eines der am leichtesten zu erlernenden Dialog-Steuerelemente. Es besitzt nur wenige Eigenschaften, die Sie kennen mssen. Listing 7.5 zeigt ein Beispiel fr den Gebrauch dieses Steuerelements beim Einstellen der Hintergrundfarbe fr Ihr Formular. Listing 7.5: Die Verwendung des Steuerelements ColorDialog
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: using System; using System.Windows.Forms; using System.Drawing; namespace TYWinForms.Day7 { public class Listing75 : Form { public Button btColor = new Button(); public Listing75() { btColor.Text = "Farbe ndern!"; btColor.Location = new Point(100,100); btColor.Size = new Size(100,25); btColor.Click += new EventHandler(this.ChangeColor);

250

Standarddialogfelder

15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30:

this.Controls.Add(btColor); } public void ChangeColor(Object Sender, EventArgs e) { ColorDialog dlgColor = new ColorDialog(); if (dlgColor.ShowDialog() == DialogResult.OK) { this.BackColor = dlgColor.Color; } } public static void Main() { Application.Run(new Listing75()); } } }

Dieses Listing zeigt eine einfache Anwendung mit nur einer Schaltflche, die das Standarddialogfeld fr die Farbauswahl anzeigt. Whlt der Benutzer eine Farbe in ColorDialog aus, wird diese in der Eigenschaft ColorDialog.Color abgelegt. In Zeile 22 nehmen wir diese Farbe und weisen sie dem Wert BackColor des Hauptformulars zu. Abbildung 7.7 zeigt das Ergebnis dieser Anwendung und das Dialogfeld ColorDialog.

Abbildung 7.7: Das Steuerelement ColorDialog ist ein leicht zu bedienendes Dialogfeld.

251

Mit Dialogfeldern arbeiten

Das ist schon alles, was Sie fr den Einsatz von ColorDialog bentigen. Das Steuerelement hat noch weitere Funktionen, mit denen der Benutzer mehr Farben als die dargestellten auswhlen kann. Wenn Sie AllowFullOpen auf true setzen, kann der Benutzer ein zustzliches Fenster ffnen (indem er auf die Schaltflche FARBEN DEFINIEREN klickt, die sich am unteren Rand des Dialogfeldes in Abbildung 7.7 befindet), um eigene Farben zu definieren, die er aus allen Farben auswhlt, die ihm das jeweilige Betriebssystem anbietet. (Wenn Sie diese Funktion nicht gestatten wollen, setzen Sie AllowFullOpen auf false und setzen auch die Eigenschaft FullOpen, die die Schaltflche fr benutzerdefinierte Farben anzeigt, auf false.) Nachdem der Benutzer einige selbst definierte Farben ausgewhlt hat, knnen Sie die Eigenschaft CustomColors dazu verwenden, um ein Array von Color-Objekten zurckzugeben. Das Schriftartdialogfeld FontDialog ist ein wenig vielschichtiger, denn es gibt ja eine Vielzahl unterschiedlicher Schriftstile. Die Farbe Blau ist nun mal blau, aber die Schriftart Arial kann Arial fett, kursiv oder Standard sein, und als Gren sind beispielsweise 10, 20 oder 36 Punkt mglich. Und schlielich gibt es noch unterschiedliche Schriftstile wie etwa Skript (Nicht-OEM- und ANSI-Zeichenstze), Vektorschriften, vertikale Schriften und so weiter. Wenn Sie nichts von diesen Details halten, knnen Sie einfach die Eigenschaft FontDialog.Font verwenden, um die vom Benutzer ausgewhlte Schrift zu zurckzugeben das haben Sie sicherlich schon vermutet:
if (dlgFont.DialogResult == DialogResult.OK) { this.Font = dlgFont.Font; }

Das ist wesentlich einfacher als jedes Mal, wenn man die Schriftart ndern mchte, ein neues Font-Objekt erstellen zu mssen! Abbildung 7.8 zeigt das Standarddialogfeld fr die Schriftartauswahl.

Abbildung 7.8: Das Standarddialogfeld FontDialog bietet zahlreiche Auswahlmglichkeiten an.

252

Standarddialogfelder

Alle Felder im Schriftartdialogfeld (Unterstreichen, Gre, Zeichenstze usw.) lassen sich anpassen. Mit AllowScriptChange kann der Benutzer den Standardzeichensatz ndern, whrend ScriptsOnly nur Skriptschriften anzeigt. AllowSimulations, AllowVectorFonts und AllowVerticalFonts gestatten die Verwendung der angegebenen Schriftarttypen (sie sind per Vorgabe alle auf true gesetzt).
FixedPitchOnly erlaubt nur die Anzeige von Schriftarten mit festem Zeichenabstand (z.B. Courier). Mit ShowColor kann der Benutzer die Farbe seiner Schrift ndern (die ColorEigenschaft gibt diese Farbe zurck) und ShowEffects zeigt die Ergebnisse der Unterstrei-

chungsoptionen. Sie knnen schlielich noch die Eigenschaften MaxSize und MinSize einstellen, damit der Benutzer nur im Bereich zwischen einer maximalen und minimalen Schriftgre auswhlen kann. Die Einstellung dieser Werte auf null versetzt das System in die Lage, diese Werte zu steuern es wird normalerweise die komplette Bandbreite der Gren erlauben.

Drucken
Die letzte Reihe von Standarddialogfeldern, die wir betrachten, befasst sich mit dem Seitendruck: PageSetupDialog, PrintDialog und PrintPreviewDialog. Sie sollten allen vertraut sein, die schon einmal Dokumente in Windows gedruckt haben.
PageSetupDialog gestattet die Randeinstellung fr ein Dokument, das ndern der Ausrichtung (Hoch- oder Querformat ), das ndern des eingestellten Papierformats usw.

Bevor Sie jedoch einen PageSetupDialog-Dialogfeld verwenden knnen, mssen Sie Ihre Einstellungen auf etwas anwenden knnen. Dies wird durch das Erstellen eines PageSettings-Objekts bewerkstelligt, woraufhin es der PageSettings-Eigenschaft von PageSetupDialog zugewiesen wird. Sie gibt an, auf welche Objekte diese Einstellungen angewendet werden sollen. Listing 7.6 zeigt eine Beispielanwendung, die genau dies tut. Listing 7.6: Das Einrichten einer Seite
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: using using using using System; System.Windows.Forms; System.Drawing; System.Drawing.Printing;

namespace TYWinForms.Day7 { public class Listing76 : Form { private PageSettings objPageSettings; private Button btShowDialog = new Button(); public Listing76() {

253

Mit Dialogfeldern arbeiten

12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32:

btShowDialog.Text = "Seiteneinstellungen"; btShowDialog.Click += new EventHandler(this.ShowSettings); this.Controls.Add(btShowDialog); } private void ShowSettings(Object Sender, EventArgs e) { if (objPageSettings == null) { objPageSettings = new PageSettings(); } PageSetupDialog dlgSettings = new PageSetupDialog(); dlgSettings.PageSettings = objPageSettings; dlgSettings.ShowDialog(); } public static void Main() { Application.Run(new Listing76()); } } }

Die Verwaltung von Seiteneigenschaften kann etwas kompliziert sein. Erstellen Sie hierzu zunchst ein Objekt System.Drawing.Printing.PageSettings (siehe Zeile 8; beachten Sie das neue using-Statement in Zeile 4). Dieses Objekt wird im Rest Ihrer Anwendung verwendet, daher ist es ntig, ein der ganzen Klasse zur Verfgung stehendes Objekt zu erstellen. In der ShowSettings-Methode in Zeile 18 mssen Sie dieses PageSettings-Objekt berprfen. Ist es null (oder Nothing in VB .NET), sind diesem Dokument noch keine Eigenschaften zugewiesen worden, so dass Sie dieses Objekt (in Zeile 20) instantiieren mssen. Ist es aber nicht null, wurde das PageSettings-Objekt bereits instantiiert und es wurden Eigenschaften zugewiesen; daher wollen wir es wieder und wieder verwenden. Zeile 23 erzeugt einen neuen PageSetupDialog. In Zeile 24 wird das PageSettingsObjekt (ob neu oder bereits vorhanden) der PageSettings-Eigenschaft zugewiesen. Nun knnen die vom Benutzer gewhlten Einstellungen gespeichert und nach Bedarf auf das Dokument angewendet werden. Die Methode ShowDialog wird in Zeile 25 aufgerufen; beachten Sie, dass Sie ihren DialogResult-Wert nicht prfen mssen. Der Grund: Der PageSetupDialog kmmert sich selbst darum, die Einstellungen anzuwenden, je nachdem, welche Schaltflche der Benutzer angeklickt hat. Uns kmmert es daher nicht, ob der Benutzer OK oder ABBRECHEN angeklickt hat, denn der PageSetupDialog handhabt dies intern.

254

Standarddialogfelder

Kompilieren Sie diese Anwendung und fhren Sie sie aus. (Wie Sie noch von Tag 2 wissen, befindet sich der Namensraum System.Drawing.Printing in der Assembly System.drawing.dll, so dass Sie beim Kompilieren keine zustzlichen Referenzen hinzufgen mssen.) Klicken Sie auf die Schaltflche SEITE EINRICHTEN und ndern Sie einige Seiteneigenschaften. Schlieen Sie den Dialog und ffnen ihn erneut mit der Schaltflche. Beachten Sie, dass die vorgenommenen nderungen sich immer noch im PageSetupDialog zeigen. Der Grund dafr ist, dass die vorgenommenen nderungen dem PageSettingsObjekt zugewiesen wurden, welches immer wieder verwendet wird. Abbildung 7.9 zeigt das Ergebnis dieser Anwendung.

Abbildung 7.9:
PageSetupDialog stellt ein Dialogfeld zur Seiteneinrichtung zur Verfgung.

Wie die anderen Standarddialogfelder, die wir heute besprochen haben, weist auch PageSetupDialog einige Eigenschaften auf, mit denen Sie das Dialogfeld anpassen knnen. AllowMargins, AllowOrientation, AllowPaper und AllowPrinter zeigen die Fenster an, in denen der Benutzer jeweils Rnder, Seitenausrichtung (hoch/quer), Papiergre und Druckeinstellungen ndern kann. Wird die AllowPrinter-Einstellung gendert, bentigen Sie ein PrinterSettings-Objekt, doch dies werden wir an Tag 11 bezglich Datei-E/A besprechen. MinMargins gibt den geringsten Seitenrand an, den Ihre Anwendung zulsst. Document gibt das PrintDocument-Objekt an, von dem man Seiteneinstellungen bezieht. Das PrintDialog-Steuerelement ist einfacher zu handhaben. hnlich wie der PrintSetupDialog enthlt es die Eigenschaften Document und PrinterSettings, um die Einstellungen zu berwachen. Damit dieses Steuerelement funktioniert, mssen Sie der PrinterSettings-Eigenschaft ein PrinterSettings-Objekt zuweisen. Doch anders als im PageSetupDialog sollte man die DialogResult-Eigenschaft prfen, sobald das Dialogfeld geffnet wird. Abbildung 7.10 zeigt ein typisches DRUCKEN-Dialogfeld.

255

Mit Dialogfeldern arbeiten

Abbildung 7.10: Das Steuerelement PrintDialog stellt ein ntzliches Dialogfeld fr das Drucken zur Verfgung.

Das letzte Standarddialogfeld, das wir heute errtern, ist das PrintPreviewDialog-Objekt. Es unterscheidet sich von den anderen Steuerelementen, denn anders als die anderen Standarddialogfelder erbt es nicht von der CommonDialog-Klasse. Es sieht nicht einmal wie ein Standarddialogfeld aus. Auch sein DialogResult muss nicht berwacht werden. Wir brauchen uns beim PrintPreviewDialog-Steuerelement nur um drei Eigenschaften zu kmmern. Document, die erste, gibt das Dokument fr die Vorschau an. Doch Vorsicht: Diese Eigenschaft akzeptiert nicht jedes x-beliebige Dokument; sie ist sehr whlerisch. Sie mssen ihr ein PrintDocument-Objekt zuweisen, welches wiederum das Dokument reprsentiert, dessen Vorschau man sehen will. Wir werden dieses Objekt nher an Tag 11 besprechen. Die nchsten beiden Eigenschaften sind einfach. PrintPreviewControl gibt natrlich das Steuerelement PrintPreviewControl im PrintPreviewDialog-Fenster zurck. Dieses Steuerelement zeigt die eigentliche Vorschau an, denn der Rest der Schaltflchen und Steuerelemente von PrintPreviewDialog besteht aus separaten Elementen. Wenn Sie Ihr eigenes benutzerdefiniertes Druckvorschau-Dialogfeld erstellen wollen, werden Sie hchstwahrscheinlich das Steuerelement PrintPreviewDialog dafr verwenden.
UseAntiAlias gibt an, ob das Vorschau-Dokument mit Antialiasing behandelt wird, das

heit, ob die Kanten des Dokuments (genauer: der verwendeten Schriftzeichen) geglttet werden sollen, so dass keine Treppchen oder harten Kanten auftauchen. Das Antialiasing lsst die Darstellung weicher und angenehmer aussehen, natrlich auf Kosten der Schrfe. Abbildung 7.11 zeigt das Steuerelement PrintPreviewDialog in Aktion.

256

Zusammenfassung

Abbildung 7.11: Sie knnen das Dialogfenster PrintPreviewDialog

dazu verwenden, eine Seitenvorschau eines Dokuments zu erhalten.

7.5

Zusammenfassung

Wie Sie heute erfahren haben, knnen Dialogfelder weitaus vielschichtiger und funktionsreicher sein als die einfache MessageBox, die Sie bislang verwendet haben. Dialogfelder werden fr das Beschaffen von Benutzereingaben eingesetzt, ob das nun ein einfaches Ja oder Nein ist oder eine Auswahl unter Schriftarten und Farben. Zunchst muss man sich merken, dass Dialogfelder modal oder nichtmodal sein knnen. Ein modaler Dialog hindert den Benutzer daran, zur Hauptanwendung umzuschalten, es sei denn, er antwortet zuerst auf das Dialogfeld. Nichtmodale Dialoge hingegen erlauben das Wechseln zwischen Dialogfeld und Hauptanwendung, doch knnen sie zu Problemen fhren, da man mehr als ein Formular auf einmal steuern muss. Verwenden Sie daher nichtmodale Dialogfelder nur, wenn Sie dem Benutzer lediglich Informationen (z.B. im Hilfe-Fenster) anzeigen mssen, ohne zugleich Informationen einzuholen. Das Ihnen vertrauteste Dialogfeld ist die MessageBox (Meldungsfeld). Dieses Steuerelement besitzt nur eine Methode namens Show, die bis zu sieben Parameter bernehmen kann, um z.B. festzulegen, welches Symbol im Meldungsfeld erscheint, welche Schaltflchen verfgbar sind sowie die Titelzeile und der Text, die anzuzeigen sind. Wenn Sie mehr Funktionen bentigen, als die einfache MessageBox bereitstellt, mssen Sie Ihre eigenen Dialogfeldklassen erstellen. Man erstellt diese Klassen genau wie StandardForm-Klassen; man erbt von der Klasse System.Windows.Forms.Form und stellt Konstruktoren

257

Mit Dialogfeldern arbeiten

und Ereignishandler bereit. Sie knnen Werte Ihres benutzerdefinierten Dialogfeldes gegenber dem Hauptformular offen legen, indem Sie (mit Hilfe der Get- und Set-Statements) public-Eigenschaften erzeugen. Ihre Dialogfelder knnen Werte zurckgeben, die angeben, auf welche Schaltflche (wie etwa OK oder ABBRECHEN) der Benutzer zum Schlieen des Dialogfeldes geklickt hat. Diese Werte befinden sich in der DialogResult-Aufzhlung. Das bergeordnete Formular kann auf diese DialogResult-Werte hin prfen, um herauszufinden, was der Benutzer mit dem Dialogfeld getan hat. Sie haben auch von der Vielzahl der Standarddialogfeld-Steuerelemente erfahren, die .NET bereitstellt. Diese Steuerelemente kapseln gebruchliche Windows-Funktionen wie etwa die Auswahl von Schriftarten oder Farben, das Drucken und die Druckvorschau, um nur einige zu nennen. Die Themen der nchsten Woche befassen sich mit der Erhhung des Funktionsumfangs Ihrer Anwendung, so etwa dem Hinzufgen einer Datenbank und von Grafikfhigkeiten. Vergessen Sie nicht das Bonusprojekt Eine Textverarbeitung erstellen am Ende dieser Woche anzusehen, bevor Sie fortfahren.

7.6
F

Fragen und Antworten

Worin besteht der Unterschied zwischen den heute erstellten Steuerelementen und ffentlichen Variablen? A Mit beiden knnen Sie Daten von einer Klasse an eine andere bergeben. In Listing 7.1 htten wir beispielsweise die Eigenschaft mit der folgenden Codezeile deklarieren knnen:
public property Caption as String

Und solange Sie die Eigenschaft tbCaption.Text dieser Variable zuweisen, wre die Funktionalitt die gleiche. Der Unterschied in der Verwendung von Eigenschaft und ffentlichen Variablen besteht darin, dass Sie mit der Eigenschaft zustzlichen Code in den get- und set-Statements ausfhren knnen. Das erweist sich als ntzlich, wenn Sie etwa Werte verifizieren mssen, bevor Sie sie zurckgeben:
public property Caption as String Get if tbCaption.Text <> "" then return tbCaption.Text else return "Hier befindet sich nichts!"

258

Workshop

end if End Get End Property

Mit einer ffentlichen Variablen lsst sich keine zustzliche Funktion ausfhren. F Wie bringe ich das Dokument dazu, in meinem Steuerelement PrintPreviewDialog zu erscheinen? A Die Antwort erfordert Wissen ber eine neue Klasse namens PrintDocument und neue Fertigkeiten, die Sie noch nicht erworben haben. Um nhere Informationen hierzu zu erhalten, lesen Sie bitte Tag 11.

7.7

Workshop

Dieser Workshop soll Ihnen helfen, sich besser an die heute behandelten Begriffe zu erinnern. Es hilft Ihnen sehr, die Antworten vollstndig zu verstehen, bevor Sie weiterlesen. Die Antworten zu den Quizfragen und den bungen finden Sie in Anhang A.

Quiz
1. Wahr oder falsch? Jedes Form-Objekt kann ein Dialogfeld sein. 2. Wie macht man ein Dialogfeld modal bzw. nichtmodal? 3. Wahr oder falsch? Sie knnen ein MessageBox-Objekt direkt instantiieren. 4. Wie heien die sieben Parameter, die die MessageBox.Show-Methode bernehmen kann? (Fhren Sie nur ihre Typen mit einer kurzen Beschreibung auf.) 5. Welche zwei Vorteile erzielen Sie, wenn Sie in Ihrer benutzerdefinierten Dialogklasse einen DialogResult-Wert zuweisen? 6. Ist das folgende Codestck korrekt? Falls nicht, nennen Sie die Fehler.
public property IsClicked as string Get return blnClicked end property

7. Welche zwei Mitglieder haben fast alle Standarddialogfeld-Steuerelemente gemeinsam? Nennen Sie die Ausnahme(n). 8. Schreiben Sie eine Filterzeichenfolge fr ein OpenFileDialog-Steuerelement, das Textdateien (*.txt), Bilddateien (*.gif) und Alle Dateien (*.*)anzeigt.

259

Mit Dialogfeldern arbeiten

9. Nennen Sie die Haupteigenschaften (also die Eigenschaften, die Sie am meisten interessieren, wenn der Benutzer eine Auswahl trifft) fr die Steuerelemente OpenFileDialog, SaveFileDialog, ColorDialog und FontDialog.

bung
Erstellen Sie in C# eine voll funktionsfhige Anwendung mit Mens, welche alle Standarddialogfeld-Steuerelemente verwendet. Setzen Sie auch ein nichtmodales Dialogfeld ein, um kontextabhngige Hilfeinformationen anzuzeigen. Wenn sich ein Benutzer von einem Menelement zum nchsten bewegt, sollte sich der Inhalt dieses Dialogfeldes ndern. (Tipp: Verwenden Sie das MenuItem.Select-Ereignis, um festzustellen, ber welchem Menelement sich die Maus gerade befindet.)

260

Woche 1 Rckblick
Projekt 1: Eine Textverarbeitung erstellen
Herzlichen Glckwunsch zum berstehen der ersten Woche! Sie drften nun mit dem Schreiben von Windows Forms-Anwendungen in C# und Visual Basic .NET vertraut sein. Ihnen drften auch die Schwierigkeiten beim Umgang mit Ereignissen und Windows Forms-Steuerelementen bewusst sein. Mit dem bislang erworbenen Wissen sind Sie bereit, praktisch jede einfache Anwendung anzupacken, aber auch einige der vielschichtigeren. Als eine Form der Rekapitulation der vergangenen sieben Tage wird dieses Rckblickkapitel (sowie diejenigen der Wochen 2 und 3) Sie durch den Vorgang der Erstellung Ihres eigenen Textverarbeitungsprogramms fhren, und zwar eines Programms, das es mit Microsoft Word aufnehmen knnte. In dieser Lektion legen Sie erst einmal das Fundament fr diese Anwendung; das Bonusprojekt am Ende der zweiten Woche wird Ein- und Ausgabefunktionen fr Daten und Dateien hinzufgen. Das Projekt 3 wird einige fortgeschrittenere Leistungsmerkmale integrieren. Es wird empfohlen, dass Sie diese Projekte durcharbeiten. Sie machen Sie besser mit Windows Forms-Konzepten vertraut, da Sie Situationen aus der Praxis handhaben werden.

ber das Textverarbeitungsprojekt


Dieses Projekt soll Ihnen Gelegenheit geben, an einer Anwendung in jeder Phase ihrer Erstellung zu arbeiten. In dieser Lektion fangen Sie erst einmal mit einem einfachen Entwurf an, installieren die grundlegenden Funktionen und erweitern diese um peppige Zusatzelemente. Dieses Projekt dient auch dazu, Ihre Fhigkeiten zu sthlen, was die Entwicklung von Windows Forms-Anwendungen anbelangt. So knnen Sie sich spter in Ihren anderen Projekten sicherer fhlen. Am Ende der zweiten bzw. dritten Woche werden Sie zu diesem Projekt zurckkehren, um die Ergebnisse der weiteren Lektionen zu integrieren. Diese Anwendung, die wir NetWord taufen wollen, wird es dem Benutzer gestatten, Dokumente zu bearbeiten und zu speichern, Suchoperationen aus zufhren, Text zu formatieren und schlielich Dokumente zu drucken und im Web zu surfen. In dieser Lektion jedoch werden wir lediglich das grundlegende Gerst sowie die Benutzeroberflche erstellen.

261

Woche 1 Rckblick

Was Sie bentigen


Der wichtigste Teil jedes Textverarbeitungsprogramms ist natrlich die Hauptbedienoberflche: der Textbereich, in dem der Benutzer Dokumente tippen und bearbeiten kann. Dreimal drfen Sie raten, wie wir diese Komponente erstellen. Ich hoffe, Sie haben auf das Steuerelement RichTextBox getippt. Bereits an Tag 6 haben wir gesehen, dass dieses Steuerelement beinahe alle Funktionen eines Textverarbeitungsprogramms ausfhren kann, vom Formatieren eines Textes bis zur Durchfhrung einer Suchoperation. Das Einrichten des RichTextBox-Steuerelements bedeutet jedoch nur den halben Sieg. Man muss auch Mens hinzufgen, mit denen der Benutzer zum Beispiel Dateien ffnen und speichern, Text formatieren und Farben auswhlen kann. Die Mens sowie das RichTextBox-Steuerelement stellen in diesem Projekt die Hauptteile der Anwendung dar. Um die Anwendung noch bedienungsfreundlicher zu machen, sind noch weitere Steuerelemente ntig, so etwa eine Statusleiste und Kontextmens. Sie werden im Abschnitt Einige ntzliche Dinge mit Pfiff hinzufgen weiter unten beschrieben. Obwohl die meisten Textverarbeitungsprogramme MDI-Anwendungen (Multiple Document Interface, Mehrfachdokumentschnittstelle) sind sie knnen mehr als ein Dokument zugleich ffnen , wird unsere erst einmal nur ein einzelnes Dokument anzeigen knnen. Auf MDI-Anwendungen kommen wir an Tag 10 zurck.

Die Benutzeroberflche aufbauen


Listing R1.1 zeigt das grundlegende Gerst fr unsere Anwendung. Listing R1.1: Die NetWord-Klasse
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: using System; using System.Windows.Forms; using System.Drawing; namespace TYWinforms.P1 { public class NetWord : Form { public NetWord() { this.Text = "NetWord v.1"; this.Size = new Size(800,600); } public static void Main() {

262

Woche 1 Rckblick

13: 14: 15: 16:

Application.Run(new NetWord()); } } }

Speichern Sie dieses Listing unter dem Namen NetWord.cs. Wie Sie sehen, weist der Code auer einer Main-Methode und einem einfachen Konstruktor kaum etwas Bemerkenswertes auf. Das Kompilieren und Ausfhren dieser Anwendung wird zu einer leeren Applikation wie in Abbildung R1.1 fhren. Wir wollen als Nchstes die Benutzeroberflche erstellen. Wir mssen das RichTextControl-Steuerelement hinzufgen und alle anderen Steuerelemente, die damit verknpft sind (wie etwa Mens). Werfen Sie einen Blick auf Listing R1.2.

Abbildung R1.1: Die bescheidenen Anfnge von NetWord

Listing R1.2: Die Document-Klasse


1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: using using using using using System; System.Windows.Forms; System.Drawing; System.Drawing.Printing; System.ComponentModel;

namespace TYWinforms.P1 { public class NetWord : Form { private RichTextBox rtbDocument = new RichTextBox(); private MainMenu mnuMain = new MainMenu(); private MenuItem mniFile = new MenuItem("Datei");

263

Woche 1 Rckblick

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:

private private private private private private private private private private private private

MenuItem MenuItem MenuItem MenuItem MenuItem MenuItem MenuItem MenuItem MenuItem MenuItem MenuItem MenuItem

mniOpen = new MenuItem("ffnen..."); mniSave = new MenuItem("Speichern"); mniPageSetup = new MenuItem("Seite einrichten..."); mniPrintPreview = new MenuItem ("Druckvorschau"); mniPrint = new MenuItem("Drucken..."); mniExit = new MenuItem("Beenden"); mniEdit = new MenuItem("Bearbeiten"); mniUndo = new MenuItem("Rckgngig"); mniCut = new MenuItem("Ausschneiden"); mniCopy = new MenuItem("Kopieren"); mniPaste = new MenuItem("Einfgen"); mniFind = new MenuItem("Suchen...");

private MenuItem mniFormat = new MenuItem("Format"); private MenuItem mniFont = new MenuItem("Schriftart..."); private private private private private private private private private private Font fntCurrent = new Font("Times New Roman", 10); Color fntColor = Color.Black; FontDialog dlgFont = new FontDialog(); OpenFileDialog dlgOpen = new OpenFileDialog(); SaveFileDialog dlgSave = new SaveFileDialog(); PrintPreviewDialog dlgPrintPreview = new PrintPreviewDialog(); PageSetupDialog dlgPageSetup = new PageSetupDialog(); PrintDialog dlgPrint = new PrintDialog(); PageSettings objPageSettings = new PageSettings(); PrinterSettings objPrintSettings = new PrinterSettings();

public NetWord() { rtbDocument.Dock = DockStyle.Fill; rtbDocument.Font = fntCurrent; rtbDocument.HideSelection = false; mniOpen.Click += new EventHandler(this.FileClicked); mniSave.Click += new EventHandler(this.FileClicked); mniPageSetup.Click += new EventHandler (this.FileClicked); mniPrintPreview.Click += new EventHandler (this.FileClicked); mniPrint.Click += new EventHandler(this.FileClicked); mniExit.Click += new EventHandler(this.FileClicked); mniUndo.Click += new EventHandler(this.EditClicked); mniCut.Click += new EventHandler(this.EditClicked); mniCopy.Click += new EventHandler(this.EditClicked); mniPaste.Click += new EventHandler(this.EditClicked); mniFind.Click += new EventHandler(this.EditClicked);

264

Woche 1 Rckblick

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:

mniFont.Click += new EventHandler (this.FormatClicked); mnuMain.MenuItems.Add(mniFile); mnuMain.MenuItems.Add(mniEdit); mnuMain.MenuItems.Add(mniFormat); mniFile.MenuItems.Add(mniOpen); mniFile.MenuItems.Add(mniSave); mniFile.MenuItems.Add("-"); mniFile.MenuItems.Add(mniPageSetup); mniFile.MenuItems.Add(mniPrintPreview); mniFile.MenuItems.Add(mniPrint); mniFile.MenuItems.Add("-"); mniFile.MenuItems.Add(mniExit); mniEdit.MenuItems.Add(mniUndo); mniEdit.MenuItems.Add("-"); mniEdit.MenuItems.Add(mniCut); mniEdit.MenuItems.Add(mniCopy); mniEdit.MenuItems.Add(mniPaste); mniEdit.MenuItems.Add("-"); mniEdit.MenuItems.Add(mniFind); mniFormat.MenuItems.Add(mniFont); this.Text = "NetWord v.1"; this.Name = "NetWord"; this.Size = new Size(800,600); this.Menu = mnuMain; this.Closing += new CancelEventHandler(this.DocumentClosing); this.Controls.Add(rtbDocument); }

Das gesamte Listing besteht lediglich aus dem Konstruktor und der Initialisierung unserer Anwendung. Inzwischen wissen Sie, dass der Groteil des von uns geschriebenen Codes (zumindest in der ersten Woche) aus der Initialisierung der Benutzeroberflche besteht. Diesem Code kann man sehr leicht folgen. Zeile 9 erzeugt das RichTextBoxSteuerelement, das fr die Hauptbenutzeroberflche bentigt wird. Die Zeilen 11 bis 28 richten die diversen Mens ein und in Zeile 30 bis 39 werden Einstellungen deklariert, die durchweg verwendet werden (die aktuelle Schriftart, Farbe, Druckeinstellung usw.). Die Zeilen 42 bis 90 richten einige allgemeine Eigenschaften ein und weisen den Menelementen Delegaten zu.

265

Woche 1 Rckblick

Es gibt hierzu noch einiges anzumerken. Erstens setzt Zeile 44 HideSelection auf false, was dazu fhrt, dass markierter Text im RichTextBox-Steuerelement auch dann noch als markiert zu sehen ist, wenn dieses Steuerelement nicht den Fokus hat (das wird bei der Erstellung der Suchfunktion wichtig). Zweitens weist Zeile 88 dem Closing-Ereignis des Formulars einen Delegaten zu. Wir wollen dieses Ereignis berwachen, um herauszufinden, ob sich der Inhalt vor dem Schlieen des Fensters gendert hat; falls dem so ist, wollen wir dem Benutzer Gelegenheit zum Speichern seines Dokumentes geben. Wir wollen nun einen Blick auf die Ereignishandler werfen, die in Listing R1.3 zu sehen sind. Listing R1.3: Die Ereignishandler fr NetWord
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: private void FileClicked(Object Sender, EventArgs e) { MenuItem mniTemp = (MenuItem)Sender; switch (mniTemp.Text) { case "ffnen...": if (dlgOpen.ShowDialog() == DialogResult.OK) { //Datei ffnen } break; case "Speichern": if (dlgSave.ShowDialog() == DialogResult.OK) { //Datei speichern } break; case "Seite einrichten...": dlgPageSetup.PageSettings = objPageSettings; dlgPageSetup.ShowDialog(); break; case "Druckvorschau": dlgPrintPreview.Document = new PrintDocument(); dlgPrintPreview.ShowDialog(); break; case "Drucken...": dlgPrint.PrinterSettings = new PrinterSettings(); if (dlgPrint.ShowDialog() == DialogResult.OK) { //drucken

266

Woche 1 Rckblick

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: 76: 77:

} break; case "Beenden": this.Close(); break; } } private void EditClicked(Object Sender, EventArgs e) { MenuItem mniTemp = (MenuItem)Sender; switch (mniTemp.Text) { case "Rckgngig": rtbDocument.Undo(); break; case "Ausschneiden": if (rtbDocument.SelectedRtf != "") { rtbDocument.Cut(); } break; case "Kopieren": if (rtbDocument.SelectedRtf != "") { rtbDocument.Copy(); } break; case "Einfgen": rtbDocument.Paste(); break; case "Suchen...": FindDialog dlgFind = new FindDialog(rtbDocument); dlgFind.Show(); break; } } private void FormatClicked(Object Sender, EventArgs e) { MenuItem mniTemp = (MenuItem)Sender; switch (mniTemp.Text) { case "Schriftart...": dlgFont.ShowColor = true; if (dlgFont.ShowDialog() == DialogResult.OK) { fntCurrent = dlgFont.Font; fntColor = dlgFont.Color; rtbDocument.SelectionFont = fntCurrent; rtbDocument.SelectionColor = fntColor;

267

Woche 1 Rckblick

78: } 79: break; 80: } 81: } 82: 83: private void DocumentClosing(Object Sender, CancelEventArgs e) { 84: if (rtbDocument.Modified) { 85: DialogResult dr = MessageBox.Show("Mchten Sie die nderungen  speichern?", this.Name, MessageBoxButtons.YesNoCancel); 86: 87: if (dr == DialogResult.Yes) { 88: MessageBox.Show("Schade!", this.Name); 89: } else if (dr == DialogResult.Cancel) { 90: e.Cancel = true; 91: } 92: } 93: } 94: 95: public static void Main() { 96: Application.Run(new NetWord()); 97: } 98: } 99: }

Nach unseren Arbeiten an Tag 4 drfte Ihnen ein Groteil dieses Codes bekannt vorkommen. Sie finden in Zeile 1 die Methode, die alle MenuItems im DATEI-Men behandelt, den Ereignishandler fr das BEARBEITEN-Men in Zeile 40, denjenigen fr das FORMAT-Men in Zeile 67 und den fr das Closing-Ereignis schlielich in Zeile 83. Der Lwenanteil dieses Codes wird verwendet, um genau auszuwerten, welches Menelement angeklickt wurde. Die Zeilen 4 bis 36 drften vertraut erscheinen: Sie sind fast identisch mit der Dialogfeld-Lektion an Tag 7. Der einzige Unterschied: Es wurde ein BEENDENMen hinzugefgt, das die Close-Methode aufruft. Je nachdem, welches Men angeklickt wurde, erzeugen Sie einen FileOpenDialog, einen FileSaveDialog, einen PageSetupDialog, einen PrintPreviewDialog oder einen PrintDialog. Mit Hilfe der ShowDialog-Methode zeigen Sie das jeweilige Dialogfeld an. Bei den FFNEN-, SPEICHERN- und DRUCKEN-Menelementen bewerten Sie das Ergebnis des Dialogfelds mit Hilfe der DialogResult-Aufzhlung und fhren eine dementsprechende Aktion aus. Die Zeilen 43 bis 63 sind sehr hnlich und fhren die Arbeitsschritte RCKGNGIG, AUSSCHNEIDEN, KOPIEREN und EINFGEN aus, indem sie die verschiedenen Methoden des RichTextBox-Steuerelements aufrufen. Bei den Cutund Copy-Methoden mssen Sie zunchst prfen, ob Text markiert ist, indem

268

Woche 1 Rckblick

Sie die Eigenschaft SelectedRtf bewerten. Das SUCHEN-Menelement in Zeile 60 ist etwas Besonderes; es erzeugt ein benutzerdefiniertes Dialogfeld, das Sie im nchsten Abschnitt erstellen werden, und zeigt es an. Die Zeilen 70 bis 78 verhalten sich wie die anderen Flle. Man zeigt dem Benutzer das Dialogfeld FontDialog an, um ihm die Auswahl einer Schriftart und einer Schriftfarbe zu gestatten. Diese Werte werden in den globalen Einstellungen gespeichert, die Sie in den Zeilen 30 und 31 erzeugt haben, und dann der Schriftart im RichTextBox-Steuerelement zugewiesen. Schlielich findet sich in Zeile 83 noch die DocumentClosing-Methode. Sie erinnern sich, dass das Closing-Ereignis direkt vor dem Schlieen des Dokuments stattfindet, was bedeutet, dass man es zur Bewertung aller in letzter Minute genderten Bedingungen einsetzen kann. Die Zeile 84 prft die Modified-Eigenschaft der RichTextBox, die Ihnen verrt, ob das Dokument seit dem letzten Speichervorgang (oder in diesem Fall, seit dem letzten ffnen) gendert wurde. Zeile 85 zeigt eine MessageBox an, die den Benutzer fragt, ob er ohne Speichern der letzten nderungen fortfahren mchte. Je nach der Antwort tun Sie eines von drei Dingen: Sie speichern das Dokument nicht. Sie halten die Anwendung vom Beenden ab (indem Sie die Eigenschaft CancelEventArgs.Cancel auf true setzen). Sie speichern das Dokument (momentan nicht wirklich, aber wenigstens wissen Sie, wohin der entsprechende Code gehrt). Das ist bereits alles wenn auch nicht fr die Anwendung. Wenn Sie versuchen, diesen Code auszufhren, erhalten Sie eine Fehlermeldung, denn Sie haben bis jetzt noch nicht das Objekt FindDialog erstellt, das von Zeile 61 gefordert wird. Diese Klasse ist in Listing R1.4 zu sehen. Listing R1.4: Die FindDialog-Klasse
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: using System; using System.Windows.Forms; using System.Drawing; namespace TYWinforms.P1 { public class FindDialog : Form { private Label lblFind = new Label(); private Label lblReplace = new Label(); private TextBox tbFind = new TextBox(); private TextBox tbReplace = new TextBox(); private Button btFind = new Button();

269

Woche 1 Rckblick

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:

private Button btReplace = new Button(); private Button btReplaceAll = new Button(); private Button btCancel = new Button(); private RichTextBox rtbDoc; public FindDialog(RichTextBox rtbParentDoc): base() { rtbDoc = rtbParentDoc; lblFind.Text = "Suche: "; lblFind.Location = new Point(10,10); lblFind.Width = 50; lblReplace.Text = "Ersetze: "; lblReplace.Location = new Point(10,35); lblReplace.Width = 50; tbFind.Width = 100; tbFind.Location = new Point(60,10); tbReplace.Width = 100; tbReplace.Location = new Point(60,35); btFind.Size = new Size(75,25); btFind.Text = "Suchen"; btFind.Location = new Point(170,10); btFind.Click += new EventHandler(this.ButtonClicked); btReplace.Size = new Size(75,25); btReplace.Text = "Ersetzen"; btReplace.Location = new Point(170,35); btReplace.Click += new EventHandler(this.ButtonClicked); btReplaceAll.Size = new Size(75,25); btReplaceAll.Text = "Alle ersetzen"; btReplaceAll.Location = new Point(170,60); btReplaceAll.Click += new EventHandler(this.ButtonClicked); btCancel.Size = new Size(75,25); btCancel.Text = "Abbrechen"; btCancel.Location = new Point(170,85); btCancel.Click += new EventHandler(this.ButtonClicked); this.Text = "Suchen"; this.Size = new Size(255,140); this.MaximizeBox = false;

270

Woche 1 Rckblick

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: 99: 100: 101:

this.MinimizeBox = false; this.FormBorderStyle = FormBorderStyle.FixedDialog; this.TopMost = true; this.Controls.Add(lblFind); this.Controls.Add(lblReplace); this.Controls.Add(tbFind); this.Controls.Add(tbReplace); this.Controls.Add(btFind); this.Controls.Add(btReplace); this.Controls.Add(btReplaceAll); this.Controls.Add(btCancel); } private void ButtonClicked(Object Sender, EventArgs e) { Button btTemp = (Button)Sender; int intLocation; int intCount = 0; switch (btTemp.Text) { case "Suchen": intLocation = rtbDoc.Find(tbFind.Text, rtbDoc.SelectionStart + rtbDoc.SelectionLength, RichTextBoxFinds.None); if (intLocation != -1) { rtbDoc.SelectionStart = intLocation; rtbDoc.SelectionLength = tbFind.Text.Length; rtbDoc.Focus(); } break; case "Ersetzen": intLocation = rtbDoc.Find(tbFind.Text, rtbDoc.SelectionStart + rtbDoc.SelectionLength, RichTextBoxFinds.None); if (intLocation != -1) { rtbDoc.SelectionStart = intLocation; rtbDoc.SelectionLength = tbFind.Text.Length; rtbDoc.SelectedText = tbReplace.Text; rtbDoc.SelectionStart = intLocation; rtbDoc.SelectionLength = tbReplace.Text.Length; } break; case "Alle ersetzen": intLocation = rtbDoc.Find(tbFind.Text); while (intLocation != -1) { rtbDoc.SelectionStart = intLocation; rtbDoc.SelectionLength = tbFind.Text.Length; rtbDoc.SelectedText = tbReplace.Text;

271

Woche 1 Rckblick

102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113:

intCount += 1; intLocation = rtbDoc.Find(tbFind.Text, rtbDoc.SelectionStart + rtbDoc.SelectionLength, RichTextBoxFinds.None); } MessageBox.Show(intCount.ToString() + "Vorkommen ersetzt","Suchen"); break; case "Abbrechen": this.Close(); break; } } } }

Dieses Listing definiert ein benutzerdefiniertes Dialogfeld, das ber SUCHENund ERSETZEN-Textfelder sowie Schaltflchen fr SUCHEN, ERSETZEN und ALLE ERSETZEN verfgt. Der Code dafr mag ein wenig komplizierter aussehen als in den bisherigen Arbeitsschritten, doch ist er nicht wirklich schwierig. Die Zeilen 1 bis 70 enthalten den Konstruktor- und Initialisierungscode. Hier gibt es nichts Neues, lediglich ein paar Dinge, die zu beachten wren. Erstens erfolgt die Deklaration des RichTextBox-Steuerelements in Zeile 16; diesem Steuerelement wird dann dasjenige Steuerelement zugewiesen, das an den Konstruktor in Zeile 18 bergeben wurde. Wenn wir in Listing P 1.3 zur Zeile 61 zurckgehen, sehen wir, dass wir an den Konstruktor der FindDialog-Klasse das Haupt-RichTextBox-Steuerelement bergeben. Dies wird so gehandhabt, damit Sie beim Suchen von Ihrem SUCHEN-Dialogfeld aus eine einfache Referenz auf dieses Steuerelement haben. Wie man es einsetzt, sehen Sie gleich. Als Nchstes finden Sie im Konstruktor in Zeile 18 einen Aufruf der baseMethode. Das bedeutet, dass der Konstruktor in Zeile 18 vom Konstruktor des RichTextBox-Steuerelements erben sollte und dass alle notwendigen Initialisierungsprozeduren ausgefhrt werden. Da .NET dies fr Sie tut, ist es im Grunde nicht ntig, dies hier einzufgen, aber es macht es fr Sie deutlicher.
ButtonClicked in Zeile 72 verwendet ein switch-Statement, um die angeklickte Schaltflche zu bewerten. In Zeile 79 stoen Sie auf die Find-Methode, die, wie Sie wissen, bestimmten Text in der RichTextBox sucht. Doch sie weist ein paar zustzliche Parameter auf: tbFind.Text ist der zu suchende Text, rtbDoc.SelectionStart bezeichnet die Startposition fr den Suchvorgang im Text (also die aktuelle Cursorposition in der RichTextBox) und RichTextBoxFinds.None gibt an, wie die Suche durchgefhrt werden soll. Die Aufzhlung RichTextBoxFinds enthlt die Werte MatchCase (Gro-/Kleinschreibung beachten), NoHighlight

272

Woche 1 Rckblick

(keine Markierung), None, Reverse (umgekehrt) und WholeWord (nach ganzem Wort suchen). Diese Werte lassen sich wie Anchor-Werte kombinieren:
RichTextBoxFinds.MatchCase | RichTextBoxFinds.Reverse

Mit Hilfe von RichTextBoxFinds kann der Benutzer spezialisierte Suchvorgnge starten, aber im Augenblick brauchen wir uns nicht darum zu kmmern. Falls die Find-Methode den gesuchten Text nicht findet, gibt sie den Wert 1 zurck. Wir prfen dies in Zeile 80 und falls Text gefunden wurde, markieren wir ihn mit Hilfe der Eigenschaften SelectionStart und SelectionLength. Am Schluss geben Sie der RichTextBox den Fokus zurck. (Doch weil wir in Zeile 60 fr die FindDialog-Klasse TopMost auf true gesetzt haben, wird das Dialogfeld nicht verschwinden, aber die Benutzereingaben gelangen trotzdem zum RichTextBox-Steuerelement.) Bei den case-Zweigen fr Suchen und Ersetzen startet die Suche hinter der aktuellen Cursorposition (SelectionStart + SelectionLength). Wird etwas Text markiert, beginnt sie direkt hinter der Markierung. Sie knnen dieses Verhalten durch Umstellen des zweiten Parameters der Find-Methode auf die Zahl 1 ndern oder indem Sie einfach die zweiten zwei Parameter weglassen, wie es der case-Zweig Alle ersetzen in Zeile 97 tut.

Abbildung R1.2: NetWord in Aktion

Die nchsten zwei Flle, ERSETZEN und ALLE ERSETZEN, funktionieren in hnlicher Weise. ERSETZEN verwendet die Find-Methode, markiert den angegebenen Text und ersetzt ihn mit Hilfe der SelectedText-Eigenschaft. Daraufhin markiert es das gerade ersetzte Wort. ALLE ERSETZEN tut das Gleiche, nur in einer while-Schleife, so dass man

273

Woche 1 Rckblick

alle Vorkommen statt nur eines ersetzen kann. Durch jede Iteration der Schleife suchen Sie den angegebenen Text, markieren und ersetzen ihn, woraufhin es zum nchsten Auftreten des gesuchten Textes geht und so weiter, bis es keine Vorkommen davon mehr gibt (also bis Find 1 zurckgibt). Anschlieend wird eine MessageBox angezeigt, um dem Benutzer mitzuteilen, wie viele Textvorkommen ersetzt wurden. Ihre Anwendung ist endlich vollstndig. Erledigen Sie Kompilierung und Ausfhrung und erfreuen Sie sich an den Frchten Ihrer Arbeit. Abbildung R1.2 zeigt das Ergebnis dieser Anwendung.

Einige ntzliche Dinge mit Pfiff hinzufgen


Ihre Anwendung mag ja vollstndig sein, doch sie ist nicht sonderlich benutzerfreundlich. Wir wollen mit ein paar Extrafunktionen NetWord etwas mehr Leben einhauchen. Zunchst fgen wir Ihren Menelementen Tastenkombinationen (Shortcuts) hinzu. Fgen Sie den folgenden Code in den NetWord-Konstruktor ein:
mniOpen.Shortcut = Shortcut.CtrlO; mniSave.Shortcut = Shortcut.CtrlS; mniPrint.Shortcut = Shortcut.CtrlP; mniUndo.Shortcut = Shortcut.CtrlZ; mniCut.Shortcut = Shortcut.CtrlX; mniCopy.Shortcut = Shortcut.CtrlC; mniPaste.Shortcut = Shortcut.CtrlV; mniFind.Shortcut = Shortcut.CtrlF; mniOpen.ShowShortcut = true; mniSave.ShowShortcut = true; mniPrint.ShowShortcut = true; mniUndo.ShowShortcut = true; mniCut.ShowShortcut = true; mniCopy.ShowShortcut = true; mniPaste.ShowShortcut = true; mniFind.ShowShortcut = true;

Die von uns zugewiesenen Tastenkombinationen entsprechen den Standardabkrzungstasten von Windows fr die betreffenden Menoptionen. Als nchsten Schritt erstellen wir ein Kontextmen, das auf den bereits erzeugten Menelementen (MenuItem-Steuerelementen) basiert. Je nachdem, was der Benutzer ausgewhlt hat, werden unterschiedliche Optionen erscheinen. Das Erstellen des Kontextmens ist einfach, doch die Bestimmung dessen, was darin erscheinen soll, ist ein wenig kniffliger. Werfen Sie einen Blick auf folgenden Code:

274

Woche 1 Rckblick

'in der Klasse private ContextMenu cmnuDocument = new ContextMenu(); ... 'im Konstruktor cmnuDocument.MenuItems.Add(mniCut.CloneMenu()); cmnuDocument.MenuItems.Add(mniCopy.CloneMenu()); cmnuDocument.MenuItems.Add(mniPaste.CloneMenu()); cmnuDocument.MenuItems.Add("-"); cmnuDocument.MenuItems.Add(mniFont.CloneMenu()); cmnuDocument.Popup += new EventHandler(this.HandleContext); rtbDocument.ContextMenu = cmnuDocument; ... 'eine neue Methode private void HandleContext(Object Sender,EventArgs e) { if (rtbDocument.SelectionLength == 0){ cmnuDocument.MenuItems [0 ].Enabled = false; cmnuDocument.MenuItems [1 ].Enabled = false; } else { cmnuDocument.MenuItems [0 ].Enabled = true; cmnuDocument.MenuItems [1 ].Enabled = true; } }

Die erste Zeile erstellt das ContextMenu-Objekt und die nchsten Zeilen weisen ihm diverse Menelemente zu (mit dem Kontextmen soll der Benutzer nur ausschneiden, kopieren, einfgen und die Schriftart ndern knnen). Beachten Sie, dass Sie die CloneMenu-Methode verwenden, um Kopien der fraglichen Menelemente anzufertigen. Tun Sie dies nicht, dann richten Sie in Ihren Standard-Menelementen ein Chaos an. Sie mssen das Kontextmen dem Kontext der Anwendung anpassen (also dem, was der Benutzer gerade tut). Dies erlaubt Ihnen das Popup-Ereignis; es wird direkt vor der Anzeige des Mens ausgelst. Wir wollen uns die HandleContext-Methode ansehen, die das Popup-Ereignis handhabt. Es gilt zwei einfache Flle zu bewerten. Ist in der RichTextBox kein Text markiert, dann sollte man die Mens AUSSCHNEIDEN und KOPIEREN abschalten (sie sind dann nmlich nutzlos). Dies erfolgt einfach durch das Setzen der Enabled-Eigenschaft auf false die Menelemente sind dann zwar noch zu sehen, doch der Benutzer kann sie nicht auswhlen. Ist jedoch Text ausgewhlt, dann sollte man dafr sorgen, dass diese zwei Menelemente aktiviert sind; sie sind das zwar per Voreinstellung, doch wenn beim ffnen des Kontextmens kein Text markiert ist, werden sie ausgeschaltet. Mit Hilfe der else-Bedingung aktivieren Sie sie einfach wieder. Lassen Sie uns zum Schluss eine Statusleiste hinzufgen, die dem Benutzer Informationen liefert, etwa die aktuelle Zeilennummer oder die aktuelle Spalte in der Zeile, wo sich der Cursor befindet. Wir brauchen also eine Statuszeile (StatusBar) und einen Statuszeilenbereich (StatusBarPanel).

275

Woche 1 Rckblick

private StatusBar sbarMain = new StatusBar(); private StatusBarPanel spnlLine = new StatusBarPanel();

Als Nchstes initialisieren Sie diese Steuerelemente im Konstruktor:


sbarMain.ShowPanels = true; sbarMain.Panels.Add(spnlLine); spnlLine.AutoSize = StatusBarPanelAutoSize.Spring; spnlLine.Alignment = HorizontalAlignment.Right;

Nun ist Ihre Statusleiste eingerichtet, doch leider macht sie noch nichts. Da Sie die aktuelle Zeile und Spalte berwachen wollen, mssen Sie die Statusleiste bei jeder nderung in der RichTextBox aktualisieren. Hierfr eignet sich das SelectionChanged-Ereignis hervorragend; es wird bei jeder Vernderung der Cursorposition ausgelst (durch Tippen, Mausklicks, Drcken der ()-Taste usw.). Fgen Sie diesem Ereignis der RichTextBox einen Delegaten hinzu:
rtbDocument.SelectionChanged += new EventHandler(this.UpdateStatus);

Der erste Teil der UpdateStatus-Methode kmmert sich um das Auffinden der Zeilennummer. Das ist ein recht einfacher Vorgang und lsst sich mit dem folgenden Code bewerkstelligen:
private void UpdateStatus(Object Sender,EventArgs e) { String text = rtbDocument.Text; int intLine = rtbDocument.GetLineFromCharIndex(rtbDocument.SelectionStart) + 1;

Zuerst holen Sie den in der RichTextBox enthaltenen Text, um ihn leichter bearbeiten zu knnen. Das RichTextBox-Steuerelement verfgt ber eine elegante Methode namens GetLineFromCharIndex, die Ihnen verrt, in welcher Zeile sich der Cursor gerade befindet. Dies liefert Ihnen die richtige Zeilennummer selbst dann, wenn es keine Zeilenumbrche gibt. Wenn etwa die erste Zeile im Steuerelement ber zwei Zeilen verluft, werden diese beiden Zeilen 1 und 2 zurckgeben. Die Lines-Eigenschaft der RichTextBox lsst sich andererseits nicht zum Zeilenzhlen verwenden, weil sie keine Zeilenumbrche bercksichtigt (sie zhlt zwei umbrochene Zeilen als eine Zeile). Die Zeichenposition bzw. Spaltennummer in der aktuellen Zeile zu ermitteln, ist ein wenig schwieriger. Um festzustellen, ob man sich am Zeilenanfang befindet, kann man die GetPositionFromCharIndex-Methode der RichTextBox verwenden, welche einen Point mit den Cursorkoordinaten zurckgeben wird. Ist die x-Koordinate gleich 1, dann befinden Sie sich am Rand ganz links auen oder, mit anderen Worten, am Zeilenanfang. Das folgende Codestck fhrt diese Prozedur aus:
int intLine = rtbDocument.SelectionStart; while (rtbDocument.GetPositionFromCharIndex(i).X > 1) --i; String StrChar = (rtbDocument.SelectionStart i + 1).ToString();

276

Woche 1 Rckblick

Zuerst setzen wir die Variable i auf die aktuelle Cursorposition. In einer while-Schleife verringern wir i, solange die x-Koordinate fr den Point bei Zeichen i grer als 1 ist. Schlielich subtrahieren wir i von unserer Ausgangsposition, um festzustellen, wie weit die Ausgangsposition vom Zeilenanfang entfernt ist. Weisen Sie zum Schluss die sich ergebende Zeichenfolge dem Statusleistenbereich zwecks Ausgabe zu:
spnlLine.Text = "Zeile " + intLine.ToString() + " Spalte " + strChar;

Abbildung R1.3 zeigt das Ergebnis. Die Projekte in Woche 2 und 3 werden diese Anwendung erweitern. Am Ende der Woche 2 werden Sie Internetfhigkeiten hinzufgen und NetWord in eine MDI-Anwendung umwandeln. Danach lernen Sie, wie Sie Ihre Anwendung installieren und sie mit anderen Windows-Anwendungen zusammenarbeiten lassen.

Abbildung R1.3: In der Statusleiste werden nun die aktuelle Zeilennummer und Cursorposition in der Zeile korrekt angezeigt.

277

Tag 1 Tag 2 Tag 3 Tag 4 Tag 5 Tag 6 Tag 7

Mit Windows Forms beginnen Windows Forms-Anwendungen erstellen Mit Windows Forms arbeiten Mens und Symbolleisten Ereignisse in Windows Forms Windows Forms mit Steuerelementen erweitern Mit Dialogfeldern arbeiten

25 45 77 111 151 177 231

W O C H E

Tag 8 T ag 9 Tag 10 Tag 11 Tag 12 Tag 13 Tag 14

Datenbindung in Windows Forms ADO.NET einsetzen MDI-Anwendungen erstellen Arbeiten mit der Windows-Ein-/Ausgabe Formulare Internet-fhig machen Grafische Anwendungen mit GDI+ ActiveX

281 309 349 377 415 439 477

W O C H E

Tag 15 Tag 16 Tag 17 Tag 18 Tag 19 Tag 20 Tag 21

Webdienste Windows-Dienste Fortgeschrittene Techniken fr Steuerelemente in Windows Forms Benutzerdefinierte Steuerelemente in Windows Forms Multithreading-Anwendungen Windows Forms konfigurieren und bereitstellen Debugging und Profiling

515 539 565 589 621 655 681

W O C H E

Woche 2: Funktionen hinzufgen


Willkommen zu Ihrer zweiten Woche in der Gesellschaft von Windows Forms! In der vorigen Woche wurden Sie in die verschiedenen Konzepte eingefhrt, die bei Windows Forms erforderlich sind. Sie erfuhren, worum es sich bei Windows Forms handelt, wie man sie erstellt und wie man Windows Forms-Steuerelemente verwendet. Nun, da Sie ber die Grundlagen verfgen, ist es an der Zeit, zu fortgeschritteneren Themen berzugehen. In dieser Woche lernen Sie, wie Sie Ihren Anwendungen leistungsfhige Funktionen hinzufgen, darunter Datenbankfhigkeiten und viele andere moderne Merkmale. In der dritten Woche werden Sie dann das komplexe Innenleben von Windows Forms kennen lernen und erfahren, wie Sie es nutzen knnen, um richtig ausgefuchste Anwendungen zu schreiben. Der Tag 8 mit dem Motto Datenbindung in Windows Forms lehrt Sie, wie Windows Forms entworfen sind, um mit allen Datentypen umgehen zu knnen, ohne auf Datenbanken zugreifen zu mssen. Diese Daten knnen Sie auf jede erdenkliche Weise manipulieren. An Tag 9 mit dem Titel ADO.NET lernen Sie, wie Sie anhand des Wissens aus Tag 8 Datenbanken in Ihre Anwendungen integrieren. Tag 10 (MDI-Anwendungen) konzentriert sich auf Anwendungen mit der Mehrfachdokumentschnittstelle (Multiple Document Interface), kurz MDI. Mit diesem Applikationstyp knnen Sie mehrere Fenster zugleich geffnet halten. Arbeiten mit der Windows-Ein-/Ausgabe ist das Motto des elften Tages. Danach beherrschen Sie die Interaktion mit Windows-Dateien also Speichern, ffnen, berschreiben usw. sowie das Drucken. Wie Sie das Internet in Ihren Anwendungen nutzen, lernen Sie an Tag 12, Formulare Internet-fhig machen. Sie erstellen einen einfachen Webbrowser und surfen nach Herzenslust. An Tag 13 unter dem Motto Grafische Anwendungen mit GDI+ erfahren Sie etwas ber GDI+, das Paradigma fr die Handhabung von Windows-Grafikaspekten. Sie erlernen nicht nur das Erstellen von zeichnenden Anwendungen, sondern auch, auf welche Weise sich GDI+ auf Ihre normalen Anwendungen auswirkt. Tag 14 trgt den Titel ActiveX, so dass Sie nun lernen, wie Sie andere Anwendungen und Komponenten mit Ihren eigenen Windows Forms-Anwendungen integrieren! Lassen Sie uns in die zweite Woche starten!

Datenbindung in Windows Forms

Datenbindung in Windows Forms

Die Datenbindung ist ein interessanter Begriff in .NET; mit ihrer Hilfe knnen Sie Daten mit den Steuerelementen in Windows Forms verknpfen. Diese Fhigkeit erlaubt es Ihnen wiederum, diese Daten leichter anzuzeigen und zu bearbeiten. Nach der heutigen Lektion werden Sie verstehen, dass Datenbindung ein leistungsfhiger Partner sein kann. Heute lernen Sie,

was man unter Datenbindung versteht, wie Sie Daten mit Ihren Steuerelementen verknpfen, was ein DataGrid ist, wie Sie feststellen, welche Daten ein Benutzer in einem Steuerelement ausgewhlt hat.

8.1

Einfhrung in die Datenbindung

Das Konzept der Datenbindung zu verstehen, ist manchmal nicht einfach, daher wollen wir ein paar Augenblicke dafr aufbringen. Lassen Sie uns zunchst den Begriff Daten errtern. In Bezug auf die Datenbindung sind Daten beliebige Arten von Informationen, mit denen eine Anwendung arbeitet (oftmals als Datenmodell bezeichnet). Es knnte sich also um die in Ihrer Anwendung verwendeten Farben, um die Kundenliste in einer Datenbank oder um die Text-Eigenschaft Ihres Formulars handeln. Alle diese Beispiele stehen fr Informationen, die Sie in Ihren Anwendungen verarbeiten. In den meisten Fllen will man solche Daten auch einem Benutzer prsentieren. Wenn Sie etwa einen CD-Katalog erstellen, muss der Benutzer seine eigenen Eingaben sehen knnen. Sie knnen eine solche Mglichkeit hinzufgen, indem Sie den Text-Eigenschaften diverser Steuerelemente Werte zuweisen, Kombinationsfelder und Listenfelder hinzufgen usw. Die Definition von Daten ist recht unscharf; doch eine exaktere Erklrung ist schwierig, da Daten in so vielfltiger Form vorkommen. Das ist einer der Grnde, warum Datenbindung so wichtig und leistungsfhig ist. Denn einfach ausgedrckt, erlaubt Ihnen Datenbindung, Informationen in einem Windows Forms-Steuerelement zusammenzufhren. Diese Bindung stellt eine Lese-/Schreibbeziehung zwischen den Daten und dem Steuerelement her; das Steuerelement zeigt die Daten in einer benutzerfreundlichen Weise an, und nderungen im Steuerelement spiegeln sich in den Daten wider. Durch die Datenbindung eines Steuerelements machen Sie es praktisch zum Vehikel fr die Anzeige und Bearbeitung von Daten.

282

Einfhrung in die Datenbindung

Inwiefern unterscheidet sich also Datenbindung von der Wertezuweisung in einem Steuerelement? Fr die einfache Datenbindung besteht kaum ein Unterschied. Doch wenn Sie einmal mit komplexer Datenbindung anfangen, erspart sie Ihnen viel Mhe, wenn Sie eine Benutzeroberflche erstellen, wobei zugleich Ihr Code schlanker und einfacher wird. Stellen Sie sich ein typisches Szenario vor, etwa den erwhnten CD-Katalog. Gibt der Benutzer Informationen zu einem neuen Album ein, lassen sich diese direkt in einem TreeView-Steuerelement anzeigen (siehe Tag 6). Dieser Prozess ist zwar einfach und schnell, doch leider verlieren Sie auf diese Weise Funktionalitt. Erstens verfgen Sie ber keine besonders gute Aufzeichnung der Daten, auer im TreeView-Steuerelement, welches nicht gerade der optimale Speicherort ist. Und zweitens bleiben die Daten nach der Anzeige auch weiterhin im Steuerelement. Auer durch lange und komplizierte Prozeduren lassen sie sich dort nicht mehr bearbeiten. Statt die Informationen direkt anzuzeigen, knnen Sie sie auch in eine Datenbank eingeben, sobald der Benutzer sie eingetippt hat. Sie verfgen dann ber eine Datenaufzeichnung, die Sie nach Wunsch bearbeiten und in so vielen Steuerelementen anzeigen knnen, wie Sie wollen, statt in einem einzigen TreeView-Steuerelement. Innerhalb dieses Rahmens verfgen Sie nun ber zwei verschiedene Objekte: die Daten und die Steuerelemente, um diese Daten anzuzeigen. Die Daten knnen Sie an das Steuerelement binden und das Steuerelement realisiert dann alle Aspekte der Anzeige fr Sie. Die Interaktion mit dem Steuerelement berhrt die Daten direkt. Dieses zweite Modell bietet mehrere Vorteile. Sie erzielen eine Trennung der Anwendungsdaten von der Benutzeroberflche (die jeweils als die Daten- und die Prsentationsbzw. Benutzeroberflchenschicht bezeichnet werden). Haben Sie sich bereits einmal ber Anwendungsentwicklungsmodelle informiert, kennen Sie schon das Dreischichtenmodell: eine Schicht fr die Prsentation (in der Benutzeroberflche), eine fr die Daten und eine (in der Mitte) fr die Behandlung der Logik (Verarbeitungsregeln) und der Beziehungen zwischen den Daten und der Benutzeroberflche (User Interface, UI). Durch diese Aufteilung wird Ihre Anwendung weitaus eleganter und anspruchsvoller. Dieses Modell lsst sich einfacher erstellen und verstehen. Sollte auerdem eine der Schichten veraltet oder berflssig werden, knnen Sie sie leicht austauschen, ohne Ihre gesamte Anwendung komplett neu oder umschreiben zu mssen. Die Aufteilung in Benutzeroberflche und Datenebene sollte stets zu Ihren Zielen bei der Anwendungsentwicklung gehren. Datenbindung ist ein ausgezeichnetes Hilfsmittel bei diesem Unterfangen.

Daten aus jeder beliebigen Quelle


Daten knnen in vielfltiger Form auftreten und in den verschiedensten Transportformen vorkommen. Ob Ihre Daten nun aus einer Datenbank stammen oder willkrlich von einem Benutzer eingegeben werden, ist gleichgltig, denn beide knnen an Windows

283

Datenbindung in Windows Forms

Forms-Steuerelemente gebunden werden. Diese immense Flexibilitt erspart Ihnen als Entwickler eine Menge Arbeit bei der Umwandlung von Datentypen und -formen. Sie knnen einfach die Daten anbinden und dann gehen. XML-Daten werden beispielsweise fast wie solche aus einer relationalen Datenbank behandelt. Es ist gleichgltig, ob einige Daten aus einer Textdatei stammen und die brigen aus Oracle oder SQL Server beide Datentypen lassen sich fr die Anzeige leicht an Steuerelemente binden. nderungen im Steuerelement lassen sich in die XML-Datei oder die Datenbank zurckschreiben, so dass man nie Informationen verliert. Wenn Sie morgen etwas ber ADO.NET lernen, werden Sie verstehen, wie diese Lese-/Schreibverbindung bei Datenbanken funktioniert.

8.2

Einfache Datenbindung

Wie bereits erwhnt brauchen Sie fr die Datenbindung ein paar Daten und ein Steuerelement, um sie daran zu binden. Bei den Daten ist lediglich darauf zu achten, dass sie in einer Form vorliegen, die die IList-Schnittstelle implementiert. Schnittstellen sind ja lediglich wie eine Anforderungsliste: Sie teilen Ihnen mit, was eine Klasse alles bentigt, sagen Ihnen aber nicht, wie Sie das Zeug erstellen sollen. Die IList-Schnittstelle definiert die Anforderungen fr jedes Objekt, das Daten enthalten kann. ber 40 Objekte verwenden IList, angefangen bei Arrays und StringCollection-Auflistungen bis hin zu etwas selteneren Objekten wie etwa EventDecriptorCollection fr gewhnlich haben Sie keine Mhe, ein IList verwendendes Objekt zu finden. Um Daten zu binden, fgen Sie der DataBindings-Auflistung ein neues Binding-Objekt hinzu. Dieser Vorgang verluft genau so wie das Hinzufgen neuer Steuerelemente zur Controls-Auflistung eines Formulars. Alle Windows Forms-Steuerelemente verfgen ber diese Auflistung, knnen also alle Datenbindung nutzen. Ein Blick auf die Syntax:
steuerelement.DataBindings.Add("eigenschaft", objekt, "feld")

Steuerelement ist dabei natrlich das Steuerelement, fr das Sie eine Datenbindung einrichten wollen. Der erste Parameter namens eigenschaft gibt an, an welche Eigenschaft des Steuerelements Sie Daten binden wollen. Das kann je nachdem variieren, welche Daten Sie anbinden wollen. So knnten Sie etwa eine Namensliste an die Text-Eigenschaft einer Schaltflche binden, aber eine Farbenliste an die BackColor-Eigenschaft. Datenbindung ist flexibel genug fr beides. Der zweite Parameter gibt die datenquelle an. Das kann ein Array, eine Datenbank, eine StringCollection usw. sein. Der dritte Parameter gibt an, welcher Teil oder welches Feld der Datenquelle gebunden werden soll. Fr ein Array ergibt diese Information wenig Sinn, denn es enthlt nur willkrliche Daten. Bei einer Datenbank knnten Sie aber etwa das Feld Name einer Tabelle binden. In diesem dritten Parameter wrden Sie also Name angeben.

284

Einfache Datenbindung

In den nchsten Abschnitten lernen Sie konkretere Beispiele fr Datenbindung kennen und erfahren, wie man gebundene Daten bearbeitet.

Eine Schaltflche mit Daten verbinden


Anhand eines simplen Beispiels verwenden wir fr die Datenbindung ein Array. Das Beispiel ist in Listing 8.1 zu sehen. Listing 8.1: Eine Schaltflche mit Daten verbinden
1: 2: 3: 4: 5: 6: 7: 8: using System; using System.Windows.Forms; using System.Drawing;

namespace TYWinforms.Day8 { public class Listing81 : Form { private Button btData = new Button(); private Color[] arrColors = {Color.Green, Color.Blue,Color.Red,  Color.Orange}; 9: 10: public Listing81() { 11: btData.Location = new Point(100,100); 12: btData.DataBindings.Add("BackColor", arrColors, ""); 13: btData.DataBindings.Add("Text", arrColors, ""); 14: 15: this.Controls.Add(btData); 16: } 17: 18: public static void Main() { 19: Application.Run(new Listing81()); 20: } 21: } 22: }

In Zeile 7 erstellen Sie das Steuerelement Button fr die Datenbindung. In der Zeile darunter wird ein Array von Color-Objekten erzeugt und instantiiert: Grn, Blau, Rot und Orange. Sie haben also Ihr Steuerelement und die Daten dafr, nun knnen Sie sie miteinander verbinden. In Zeile 12 verwenden Sie die oben beschriebene Syntax, um das Array an die Schaltflche zu binden. Beachten Sie die drei Parameter: Der erste gibt die Steuerelementseigenschaft fr die Bindung an; da wir ein Array von ColorObjekten verwenden, binden Sie die Farben an die BackColor-Eigenschaft. Der zweite Parameter gibt die Datenquelle an: das in Zeile 8 erstellte Array. Beim

285

Datenbindung in Windows Forms

dritten Parameter handelt es sich lediglich um eine leere Zeichenfolge, da keine bestimmte Eigenschaft des Arrays gebunden werden muss (und kann); daher soll das gesamte Array angebunden werden. Sie haben nun die Farben an die Schaltflche gebunden. Moment mal!. Zeile 13 scheint eine andere Datenbindungsprozedur aufzuweisen. Hier binden Sie das gleiche Farbenarray an die Text-Eigenschaft des Button-Steuerelements. Die CLR ruft automatisch eine ToString-Methode fr die Array-Objekte auf, um die Text-Eigenschaft den Farben richtig zuzuweisen. Zum Schluss wird die Schaltflche in Zeile 15 dem Formular hinzugefgt. Abbildung 8.1 zeigt das Ergebnis dieser Anwendung.

Abbildung 8.1: Die Datenbindung einer Eigenschaft veranlasst das Steuerelement zur Anzeige der Daten.

Was ist also geschehen? Die BackColor-Eigenschaft des Button-Steuerelements ist nun also auf die erste Farbe im Array, grn, gesetzt worden. Diese Farbe wird gesetzt, ohne dass man der BackColor-Eigenschaft manuell eine Farbe zuweisen msste. Die Datenbindung kmmert sich selbst darum. Zweitens wird das Wort Green das Sie sehen, wenn Sie Color.Green.ToString() aufrufen als Beschriftung des Button-Steuerelements angezeigt. Und wieder brauchen Sie diese Eigenschaft nicht selbst zuzuweisen; das wird fr Sie erledigt. Sie knnen noch mehr erreichen. Da das an das Button-Steuerelement gebundene Array mehr als eine Farbe enthlt, knnen Sie alle Farben durchlaufen lassen. Zu diesem Zweck mssen Sie aber erst ein neues Objekt kennen lernen: BindingContext. In einem Steuerelement zeigt es die Beziehung zwischen Daten und der sie bindenden Steuerelementeigenschaft an. Es gibt an, welches Element in der Datenauflistung auf welche Weise angezeigt werden soll. Um seinen Gebrauch anzusehen, fgen Sie folgenden Code in das Listing 8.1 ein:
//im Konstruktor btData.Click += new EventHandler(this.Cycle); ...

286

Einfache Datenbindung

//neue Methode public void Cycle(Object Sender, EventArgs e) { btData.BindingContext [arrColors ].Position += 1; }

Die erste Zeile fgt einen Ereignishandler fr das Click-Ereignis hinzu. Sie benutzen dieses Ereignis, um alle Array-Elemente zu durchlaufen. Der Ereignishandler Cycle verwendet den BindingContext des Button-Steuerelements, um den gebundenen Wert zu ndern. BindingContext enthlt eine Liste aller an das Steuerelement gebundenen Objekte, so dass Sie das korrekte angeben mssen; dieses referenziert das Array von Farben. Die PositionEigenschaft gibt an, welches Element gerade angezeigt wird, so dass Sie es nur um eins zu inkrementieren brauchen, um zum nchsten zu gelangen. Nun kompilieren Sie die Anwendung und klicken auf die Schaltflche. Die Eigenschaften BackColor und Text des Button-Steuerelements ndern sich mit jedem Klick und ohne dass Sie sich mit einer dieser Eigenschaften htten einzeln abgeben mssen. Dieses Beispiel mag Ihnen ein wenig an den Haaren herbeigezogen erscheinen, doch wenn Sie eine Datenbank mit Tausenden von Kunden haben, knnen Sie sich vorstellen, wie diese einfache Datenbindungsprozedur das Entwicklerleben leichter machen kann. Zudem spiegeln sich alle im Array vorgenommenen nderungen auch in der Schaltflche wider. Sie knnten in Ihrer Anwendung etwa Folgendes hinzufgen:
arrColors[2] = Color.Magenta

Die Schaltflche wrde sich dem ohne weiteres anpassen. Einfache Datenbindung ist also nicht schwer.

Das Bearbeiten gebundener Daten


Wir wollen uns ein etwas anspruchsvolleres Beispiel ansehen, bei dem ein Kombinationsfeld verwendet wird, mit dem der Benutzer Elemente bearbeiten kann. Mit Kombinationsfeldern und anderen Listenanzeige-Steuerelementen gestaltet sich der Umgang mit den Daten etwas einfacher. Statt Datenbindungen zu entwerfen, knnen Sie einfach die DataSource-Eigenschaft einstellen, um die Standardeigenschaft des Steuerelements an die Standardeigenschaft der betreffenden Daten zu binden. Im Beispiel des Listings 8.2 prsentiert die Anwendung zwei Steuerelemente: ein Kombinationsfeld (ComboBox) zur Datenanzeige und eine Schaltflche fr die Datenbearbeitung. Whlt der Benutzer eine Farbe aus, kann er die Auswahl wieder ndern, indem er einen neuen Farbenwert in das Textfeld der ComboBox eintippt.

287

Datenbindung in Windows Forms

Listing 8.2: Komplexe Datenbindung


1: 2: 3: 4: 5: 6: 7: 8: 9: using System; using System.Windows.Forms; using System.Drawing;

namespace TYWinforms.Day8 { public class Listing82 : Form { private ComboBox cboData = new ComboBox(); private Button btChange = new Button(); private Color[] arrColors = {Color.Green, Color.Blue, Color.Red,  Color.Orange}; 10: private int intLastIndex; 11: 12: public Listing82() { 13: cboData.Location = new Point(50,75); 14: cboData.DropDownStyle = ComboBoxStyle.Simple; 15: cboData.DataBindings.Add("BackColor", arrColors, ""); 16: cboData.DataSource = arrColors; 17: cboData.SelectedIndexChanged += new EventHandler(this.ChangeColor); 18: 19: btChange.Location = new Point(175,75); 20: btChange.Text = "ndern"; 21: btChange.Click += new EventHandler(this.EditColor); 22: 23: this.Controls.Add(cboData); 24: this.Controls.Add(btChange); 25: } 26: 27: public void ChangeColor(Object Sender, EventArgs e) { 28: cboData.BindingContext[arrColors].Position = cboData.SelectedIndex; 29: intLastIndex = cboData.SelectedIndex; 30: } 31: 32: public void EditColor(Object Sender, EventArgs e) { 33: if (Color.FromName(cboData.Text).IsKnownColor) { 34: arrColors[intLastIndex] = Color.FromName (cboData.Text); 35: 36: cboData.SelectedText = Color.FromName (cboData.Text).ToString(); 37: cboData.SelectedIndex = intLastIndex; 38: } 39: } 40: 41: public static void Main() { 42: Application.Run(new Listing82()); 43: } 44: } 45: }

288

Einfache Datenbindung

Der Anfang dieses Listings hnelt dem von Listing 8.1. In Zeile 9 erzeugen Sie ein Farben-Array und eine Schaltflche in Zeile 8. Doch binden Sie diesmal keine Daten an die Schaltflche, sondern vielmehr an die ComboBox (Zeile 7). In Zeile 16 setzen Sie die DataSource-Eigenschaft auf das Farben-Array. Das ist alles, was Sie fr das Datenbinden tun mssen. Wenn Sie dieses Steuerelement ansehen, entsprechen die Listenelemente den Farben im Array (und der angezeigte Text entspricht der Textdarstellung dieser Farben). Um das Steuerelement etwas zu beleben, binden Sie auch die BackColor-Eigenschaft der ComboBox wie beim Button-Steuerelement in Listing 8.1. Sobald sich der ausgewhlte Index ndert, soll sich auch die Farbe des Kombinationsfeldes ndern, daher fgen Sie dem Ereignis SelectedIndexChange einen Ereignishandler hinzu. In den Zeilen 19 bis 21 richten Sie das Button-Steuerelement ein. Der Benutzer wird diese Schaltflche zur Besttigung seiner Datennderungen in der ComboBox verwenden. Klickt er auf die Schaltflche, modifizieren Sie die Daten und aktualisieren nach Bedarf auch das Kombinationsfeld. Die ChangeColor-Methode der Ereignishandler fr das SelectedIndexChangedEreignis ist sehr einfach. Mit Hilfe der BindingContext-Eigenschaft der ComboBox setzen Sie die Position auf den gerade ausgewhlten Index. Als Nchstes speichern Sie den Index fr den spteren Gebrauch; sobald der Benutzer etwas in das Kombinationsfeld eintippt, verliert das gerade ausgewhlte seine Markierung. Das fhrt zu Problemen, wenn Sie versuchen Elemente zu ndern, so dass Sie eine exakte Aufzeichnung des zuletzt ausgewhlten Elements bereithalten mssen. In der EditColor-Methode spielen sich die wirklich interessanten Dinge ab. Als Erstes ndern Sie das Array; Sie bernehmen die neue vom Benutzer in das Kombinationsfeld eingegebene Farbe, rufen das damit verknpfte Color-Objekt mit Hilfe der ColorFromName-Methode ab und weisen es dem ausgewhlten Index im Array zu. Klickt der Benutzer nun wieder auf dieses Element, wechselt die Hintergrundfarbe auf den neuen Wert. Beachten Sie jedoch, dass sich der im Kombinationsfeld angezeigte Text nicht ndert, selbst wenn dies der aktuelle Farbwert tut. Der Text ndert sich nicht, weil die ComboBox ber keinen Mechanismus verfgt, mit dem sie nderungen in dem in ihrer DataSourceEigenschaft festgelegten Objekt aufspren knnte. Daher mssen Sie einfach noch einmal den Text mit Hilfe der ColorFromName-Methode aktualisieren. Zum Schluss setzen Sie das aktuell ausgewhlte Element auf das letzte vom Benutzer ausgewhlte Element, welches jetzt der neue Wert ist. Kompilieren Sie Listing 8.2 und probieren Sie es aus. Abbildung 8.2 zeigt das Ergebnis.

289

Datenbindung in Windows Forms

Abbildung 8.2: Die ComboBox verwendet gebundene Daten fr ihre Hintergrundfarbe.

Formulare mit Daten verbinden


Sie werden hufig eine Datenquelle mit einer ganzen Anwendung verknpfen wollen. Angenommen, Sie haben eine Applikation erstellt, die nur fr die Anzeige eines CD-Katalogs verwendet wird. Die Anwendung verfgt ber mehrere Steuerelemente, die jeweils mit den Aspekten einer einzelnen CD verbunden sind eine Beschriftung fr den Knstler, ein Textfeld fr den Albumtitel, ein Textfeld fr den laufenden Musiktitel usw. Sobald der Benutzer zum nchsten Album bzw. Datensatz weiterspringt, gibt es eine Alternative: Statt fr jedes Steuerelement des Formulars den Wert BindingContext.Position ndern zu mssen, knnen Sie ihn einfach fr das Form-Objekt ndern:
'VB .NET Me.BindingContext[object].Position += 1 //C# this.BindingContext[object].Position += 1; BindingContext ndert sich dann in jedem einzelnen Steuerelement, das im Formular

angebunden ist.

8.3

Das Steuerelement DataGrid

Das leistungsstarke DataGrid-Steuerelement wurde speziell fr den Einsatz bei der Datenbindung entworfen. Es ist bei der Datenanzeige in allen unterschiedlichen Formaten effizient, dabei verfgt es ber eine Vielzahl von Anzeigeoptionen, die Sie zur Anpassung einer Benutzeroberflche verwenden knnen. Das DataGrid verfgt sogar ber eingebaute Funktionen fr das Bearbeiten, Hinzufgen und Lschen von Elementen.

290

Das Steuerelement DataGrid

Die Einrichtung des Steuerelements ist einfach: Setzen Sie seine DataSource-Eigenschaft auf die Datenquelle, und schon kann's losgehen. Listing 8.3 zeigt ein einfaches Beispiel auf der Grundlage der Listings 8.1 und 8.2. Listing 8.3: Datenbindung eines DataGrid-Steuerelements
1: 2: 3: 4: 5: 6: 7: 8: using System; using System.Windows.Forms; using System.Drawing;

namespace TYWinforms.Day8 { public class Listing83 : Form { private DataGrid dgData = new DataGrid(); private Color[] arrColors = {Color.Green, Color.Blue, Color.Red,  Color.Orange}; 9: 10: public Listing83() { 11: dgData.DataSource = arrColors; 12: dgData.Size = new Size(800,600); 13: 14: this.Size = new Size(800,600); 15: this.Controls.Add(dgData); 16: } 17: 18: public static void Main() { 19: Application.Run(new Listing83()); 20: } 21: } 22: }

Dieser Code ist fast identisch mit dem in Listing 8.1. Der Unterschied: Statt einer ComboBox verwenden Sie hier ein DataGrid. Kompilieren Sie das Listing und betrachten Sie das Ergebnis, wie es in Abbildung 8.3 zu sehen ist. Was ist nun passiert? Sie haben dem DataGrid blo ein Array von Farben zugewiesen, also wo kamen dann die ganzen zustzlichen Informationen her? Die in Abbildung 8.3 zu sehenden Spalten IsEmpty, A, B, IsNamedColorIsKnownColor, Name, G, R und IsSystemColor sind alles Eigenschaften des Color-Objekts. Ungeachtet seiner Farbe verfgt jedes ColorObjekt ber diese Eigenschaften. Da Sie dem DataGrid eine Sammlung von Color-Objekten bergaben, nahm es an, dass Sie alle Eigenschaften angezeigt haben wollten. Htten Sie ihm ein Array von Zeichenfolgen bergeben, wrde es einfach die Lnge des Textinhalts dieser Zeichenfolgen anzeigen. Tabelle 8.1 fhrt die gebruchlichsten UI-Eigenschaften des DataGrid-Steuerelements auf.

291

Datenbindung in Windows Forms

Abbildung 8.3: Das DataGrid zeigt mhelos umfangreiche Datenmengen an. Eigenschaft
AlternatingBackcolor

Beschreibung Legt die Farbe fr jede zweite anzuzeigende Zeile fest (damit der Benutzer die Zeilen besser unterscheiden kann). Legt fest, welche Hintergrundfarbe das Datenblatt an. Legt fest, welche Hintergrundfarbe die Flche auerhalb des Datenblattes hat. Gibt einen der Werte der BorderStyle-Aufzhlung an. Legt fest, welche Hintergrundfarbe der Titelbereich hat. Legt die Schriftart fr den Titelbereich fest. Legt die Farbe der Schriftart im Titel fest. Enthlt den im Titel anzuzeigenden Text. Gibt an, ob die Titelzeile sichtbar ist Gibt an, ob die Spaltenkpfe sichtbar sind. Gibt an, ob das Datenblatt im Flachmodus angezeigt wird (dieser Modus zeigt keine sichtbaren Linien zwischen Spalten- und Zeilenberschriften an). Gibt die Schriftfarbe im Datenblatt an. Gibt die Farbe der Datenblattlinien an. Gibt einen der Werte der DataGridLineStyle-Aufzhlung an: None: keine Datenblattlinien; Solid: durchgehende Datenblattlinien. Legt die Hintergrundfarbe der Zeilen- und Spaltenkpfe fest. Legt die Schriftart der Spaltenkpfe fest. Legt die Schriftfarbe der Kopfzeile fest.

BackColor BackGroundColor

BorderStyle CaptionBackColor CaptionFont CaptionForeColor CaptionText CaptionVisible ColumnHeadersVisible FlatMode

ForeColor GridLineColor GridLineStyle

HeaderBackColor HeaderFont HeaderForeColor

Tabelle 8.1: Eigenschaften der DataGrid-Benutzeroberflche

292

Das Steuerelement DataGrid

Eigenschaft
LinkColor

Beschreibung Gibt die Farbe von Links an, mit deren Hilfe man zu untergeordneten Tabellen navigieren kann (mehr dazu morgen). Gibt die alternative Farbe von Links an, falls der Mauszeiger darber schwebt. Legt die Hintergrundfarbe von bergeordneten Zeilen fest. Legt die Schriftfarbe von bergeordneten Zeilen fest. Enthlt einen Wert der DataGridParentRowsLabelStyle-Aufzhlung: ColumnName: der Name der bergeordneten Spalte TableName: der Name der bergeordneten Tabelle Both: beide Namen None: weder der eine noch der andere Name Gibt an, ob bergeordnete Zeilen sichtbar sind. Legt die Breite angezeigter Spalten fest. Legt die Hhe angezeigter Zeilen fest. Gibt an, ob Titel fr Zeilen sichtbar sind. Legt die Breite von Zeilentitelzellen fest. Legt die Hintergrundfarbe einer ausgewhlten Zelle fest. Legt die Schriftfarbe einer ausgewhlten Zelle fest.

LinkHoverColor

ParentRowsBackColor ParentRowsForeColor ParentRowsLabelStyle

ParentRowsVisible PreferredColumnWidth PreferredRowHeight RowHeadersVisible RowHeaderWidth SelectionBackColor SelectionForeColor

Tabelle 8.1: Eigenschaften der DataGrid-Benutzeroberflche (Forts.)

Ein kurzer Blick auf ADO.NET


Bevor Sie die Interaktion mit dem DataGrid-Steuerelement erlernen, sollten Sie noch einiges mehr ber komplexe Datenstrukturen wissen. Arrays mgen ja schn und gut sein, sind aber leider nicht ganz optimal fr die Speicherung und Darstellung von Daten. Sie lassen sich nicht vollstndig modifizieren, und Sie knnen zwar einzelne Elemente im Array ndern, sie aber nicht vollstndig entfernen. Wir werfen daher einen kurzen Blick auf einige der Objekte, die Teil von ADO.NET sind, insbesondere auf DataTable und seine Begleiter. Wenn Sie die morgige Lektion beginnen, haben Sie einen Vorsprung.

293

Datenbindung in Windows Forms

Eine DataTable ist eine vollstndige Darstellung von Daten in einer Tabelle. Stellen Sie es sich als zweidimensionales Array mit Namen vor. Werfen Sie einen Blick auf Tabelle 8.2, die Informationen ber Namen und Handorientierung bereithlt.
Vorname Nachname Handorientierung

Walter Eva Joel


Tabelle 8.2: Musterdaten

Saravia Payne Andres

Rechtshnder Rechtshnder Linkshnder

Diese Tabelle weist drei Datenzeilen und drei Spalten auf, die Vorname, Nachname und Handorientierung heien. Aus dieser Tabelle knnen Sie ersehen, dass eine Spalte einen Informationstyp darstellt und eine Zeile die eigentlichen Daten (die Zeile wird im Deutschen auch als Datensatz bezeichnet). Eine DataTable ist genau das Gleiche. DataRow-Objekte stellen Zeilen in der DataTable dar und DataColumn-Objekte die Datentypen. Das DataGrid ist demnach optimal fr die Informationsdarstellung in einer DataTable geeignet, denn es zeigt Daten im Tabellenformat an, wie Sie noch aus Abbildung 8.3 wissen. Wir wollen lernen, wie man Daten in einer DataTable speichert; diese Prozedur wird Ihnen sehr bekannt vorkommen. Sie mssen zuerst Ihre DataTable erstellen das geht wie bei jedem anderen Objekt:
//C# DataTable objMyTable =new DataTable("Meine Tabelle "); 'VB .NET dim objMyTable as New DataTable("Meine Tabelle ")

Die in Klammern stehenden Wrter stellen den Tabellennamen dar (sie werden fr das sptere leichte Auffinden gebraucht). Als Nchstes erzeugen Sie DataColumn-Objekte, die jeweils einen der zu speichernden Informationstypen darstellen, und fgen Sie Ihrer DataTable hinzu. Dieser Vorgang hnelt dem Hinzufgen von Steuerelementen zu einem Formular:
//erzeugen Sie eine Spalte fr Integer DataColumn ColumnA = new DataColumn(); ColumnA.DataType = System.Type.GetType("System.Int32 "); ColumnA.ColumnName = "Spalte A "; //erzeugen Sie eine Spalte fr Zeichenfolgen DataColumn ColumnB = new DataColumn(); ColumnB.DataType = System.Type.GetType("System.String "); ColumnB.ColumnName ="Spalte B ";

294

Das Steuerelement DataGrid

//fgen Sie sie der Datentabelle hinzu objMyTable.Columns.Add(ColumnA); objMyTable.Columns.Add(ColumnB);

Nun verfgen Sie ber eine vollstndige, wenngleich etwas leere DataTable (also eine ohne Daten). Sie wird je zwei Datenelemente pro Zeile oder Datensatz enthalten: einen Integerwert und einen Zeichenfolgenwert. Um Daten hinzuzufgen, integrieren Sie einfach DataRows. Das folgende Stck Code zeigt eine einfache Mglichkeit die DataTable mit Daten zu fllen, indem man eine for-Schleife verwendet:
DataRow myDataRow; for (int i =0; i <= 10; i++) { myDataRow = objMyTable.NewRow(); myDataRow ["Column A "] == i; myDataRow ["Column B "] == i.ToString(); objMyTable.Rows.Add(myDataRow); }

Beachten Sie, dass Sie eine neue DataRow mit Hilfe der NewRow-Methode des bereits vorhandenen DataTable-Objekts anlegen. Durch diese Vorgehensweise stellen Sie sicher, dass Ihre neue Zeile ber alle richtigen Spalten und Datentypen verfgt. Sie fllen daraufhin diese Spalten auf, indem Sie sie nach ihren Namen referenzieren. Zum Schluss fgen Sie die neue Zeile der Tabelle hinzu, wie Sie es bei den Spalten taten. Wenn Sie diesen Code ausfhren, erhalten Sie die Daten in Tabelle 8.3.
Column A
0 1 2 3 4 5 6 7 8 9 10

Column B
"0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "10"

Tabelle 8.3: Ihre neue DataTable

295

Datenbindung in Windows Forms

Jetzt knnen Sie Ihre DataGrid.DataSource-Eigenschaft auf Ihre neue DataTable setzen und erhalten schlielich eine Tabelle wie in Abbildung 8.4.

Abbildung 8.4: Das DataGrid zeigt Ihre DataTable-Struktur und -Daten perfekt an.

Die Objekte DataTable, DataColumn und DataRow verfgen ber weitaus mehr interessante Eigenschaften als die hier beschriebenen, doch spare ich mir diese Errterung fr morgen auf. Vielmehr wollen wir uns nun ansehen, wie wir diese Daten mit Hilfe des DataGridSteuerelements manipulieren.

Die Interaktion mit einem DataGrid


Wenn Sie mit dem vorhergehenden Beispiel etwas experimentiert haben, wissen Sie, dass man Daten ganz leicht bearbeiten kann: Man whlt einfach eine gewnschte Zelle zum Editieren aus und gibt neue Daten ein. Mit dem DataGrid knnen Sie steuern, wie und ob die Zellen sich editieren lassen, ebenso ihren Stil und ihre Formatierung. Mit Hilfe einer Reihe von Stilobjekten, die Sie auf das DataGrid anwenden, knnen Sie praktisch jeden Aspekt des DataGrid-Steuerelements ndern. Wir wollen uns zunchst das DataGridTableStyle-Objekt ansehen. Das Erstellen eines Stils ist einfach:
DataGridTableStyle dgStyle = new DataGridTableStyle(); dgStyle.MappingName = "Meine Tabelle "; dgStyle.BackColor = Color.Blue; dgData.TableStyles.Add(dgStyle);

Die erste Zeile ist klar: Sie erzeugt ein neues DataGridTableStyle-Objekt. Die zweite Zeile, MappingName, teilt dem DataGrid mit, welche Tabelle die Stile zugewiesen bekommt. Hier verwenden Sie den DataTable-Namen, den Sie im vorhergehenden Abschnitt erzeugt haben. Als Nchstes stellen Sie jede gewnschte Eigenschaft ein und fgen schlielich das DataGridTableStyle-Objekt dem DataGrid hinzu.

296

Das Steuerelement DataGrid

Zu den einstellbaren Stileigenschaften gehren:


AlternatingBackColor und BackColor HeaderBackColor und HeaderForeColor GridLineColor und GridLineStyle LinkColor


(gibt an, ob sich das DataGrid bearbeiten lsst)

Kurz gesagt: Das DataGridTableStyle-Objekt verfgt ber praktisch jede Eigenschaft wie das DataGrid selbst. Warum also zwei Objekte benutzen? Weil sich das DataGrid an mehr als eine Tabelle zugleich binden lsst (wie Sie morgen sehen). Daher knnen Sie auch mehrere Stile benutzen einen fr jede Tabelle ohne das gesamte DataGrid neu formatieren zu mssen. (Deshalb muss unbedingt der MappingName siehe unten angegeben werden!) Das DataGridTableStyle gestattet Ihnen die Formatierung jeder Spalte (wie auch ihr Entfernen und Hinzufgen; es gibt brigens kein DataGridRowStyle). Das DataGridTableStyleObjekt weist einige Eigenschaften auf, die Spalten betreffen:

Alignment: Gibt die Ausrichtung von Text in der Spalte an. HeaderText: Legt den Text in der Spaltenkopfzelle fest. NullText: Gibt an, was angezeigt werden soll, falls der Wert fr eine bestimmte Zelle gleich null ist (oder Nothing in VB .NET). MappingName: Das Gleiche wie fr das DataGridTableStyle-Objekt. ReadOnly: Gibt an, ob die Daten in der angegebenen Spalte nur gelesen werden drfen. Width: Gibt die Spaltenbreite an.

Alle diese Eigenschaften bieten keine Besonderheiten. Um ein DataGridColumnStyle zuzuweisen, muss man zuerst einen Typ des DataGridTableStyle erstellen. Ein DataGridColumnStyle-Objekt lsst sich nicht direkt erstellen. Statt dessen verwenden Sie entweder DataGridBoolColumn (das Kontrollkstchen in den Zellen anzeigt) oder DataGridTextBoxColumn (das Textfelder anzeigt der Standardstil), je nach Bedarf. (Jedes Objekt verfgt ber weitere anpassbare Eigenschaften; wir kommen gleich darauf zu sprechen.) Dann fgen Sie der Eigenschaft GridColumnStyles den neuen Spaltenstil hinzu:
DataGridTableStyle dgTableStyle = new DataGridTableStyle(); // einige Eigenschaften einstellen DataGridTextBoxColumn dgColumnStyle = new DataGridTextBoxColumn(); //einige Spalteneigenschaften einstellen dgTableStyle.GridColumnStyles.Add(dgColumnStyle); dgData.TableStyles.Add(dgTableStyle);

297

Datenbindung in Windows Forms

Wenn Sie eine DataGridTextBoxColumn oder DataGridBoolColumn erstellen, berschreibt dies einige der Standardwerte fr jede Spalte. So zeigen etwa die Spaltenkopfzellen die Mapping-Namen. Wenn Sie nun einen Stil mit DataGridTextBoxColumn oder DataGridBoolColumn erstellen, wird der Kopfzeilentext berschrieben und bleibt schlielich leer. Verwenden Sie in diesem Fall die HeaderText-Eigenschaft, um die Kopfzellen einzurichten. So knnen Sie sich die Bezeichnungsregel merken: Ein DataGrid zeigt eine Tabelle an, und die TableStyles-Eigenschaft definiert den Style (Stil) dieser table (Tabelle) mit Hilfe eines DataGridTableStyle-Objekts. Eine Tabelle wiederum stellt columns (Spalten) in einem grid (Datenblatt) dar. Die GridColumnStyles-Eigenschaft definiert den Style fr diese Spalte mit Hilfe eines DataGridColumnStyle-Objekts. In freier Notation sieht dies wie folgt aus:
DataGrid.TableStyles = DataGridTableStyle DataGridTableStyle.GridColumnStyle = DataGridColumnStyle

Lassen Sie uns mit der Farbenanwendung fortfahren, die wir oben mit Hilfe von DataGrid- und Stilobjekten erstellt haben. Listing 8.4 zeigt die grundlegende Struktur dieser Anwendung. Listing 8.4: Die erweiterte Farbenanwendung
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: using using using using System; System.Windows.Forms; System.Drawing; System.Data;

namespace TYWinforms.Day8 { public class Listing84 : Form { private DataGrid dgData = new DataGrid(); private DataTable objMyTable = new DataTable("Colors"); public Listing84() { DataColumn colName = new DataColumn(); colName.DataType = System.Type.GetType("System.String"); colName.ColumnName = "Farbbezeichnung"; DataColumn colR = new DataColumn(); colR.DataType = System.Type.GetType("System.Int32"); colR.ColumnName = "Rot"; DataColumn colG = new DataColumn(); colG.DataType = System.Type.GetType("System.Int32"); colG.ColumnName = "Grn";

298

Das Steuerelement DataGrid

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: 68: 69:

DataColumn colB = new DataColumn(); colB.DataType = System.Type.GetType("System.Int32"); colB.ColumnName = "Blau"; objMyTable.Columns.Add(colName); objMyTable.Columns.Add(colR); objMyTable.Columns.Add(colG); objMyTable.Columns.Add(colB); DataRow myDataRow; myDataRow = objMyTable.NewRow(); myDataRow["Farbbezeichnung"] = Color.Blue.Name; myDataRow["Rot"] = Color.Blue.R; myDataRow["Grn"] = Color.Blue.G; myDataRow["Blau"] = Color.Blue.B; objMyTable.Rows.Add(myDataRow); myDataRow = objMyTable.NewRow(); myDataRow["Farbbezeichnung"] = Color.Red.Name; myDataRow["Rot"] = Color.Red.R; myDataRow["Grn"] = Color.Red.G; myDataRow["Blau"] = Color.Red.B; objMyTable.Rows.Add(myDataRow); myDataRow = objMyTable.NewRow(); myDataRow["Farbbezeichnung"] = Color.Green.Name; myDataRow["Rot"] = Color.Green.R; myDataRow["Grn"] = Color.Green.G; myDataRow["Blau"] = Color.Green.B; objMyTable.Rows.Add(myDataRow); myDataRow = objMyTable.NewRow(); myDataRow["Farbbezeichnung"] = Color.PapayaWhip.Name; myDataRow["Rot"] = Color.PapayaWhip.R; myDataRow["Grn"] = Color.PapayaWhip.G; myDataRow["Blau"] = Color.PapayaWhip.B; objMyTable.Rows.Add(myDataRow); dgData.DataSource = objMyTable; dgData.Dock = DockStyle.Fill; this.Size = new Size(800,600); this.Controls.Add(dgData); this.Text = "Listing 8.4"; }

299

Datenbindung in Windows Forms

70: 71: 72: 73: 74:

public static void Main() { Application.Run(new Listing84()); } } }

Die Zeilen 1 bis 4 importieren die notwendigen Namensrume. Vergessen Sie System.Data nicht. Er enthlt die diversen DataTable-Objekte. Als Nchstes deklarieren Sie in den Zeilen 8 und 9 ein DataGrid und eine DataTable. In den Zeilen 12 bis 26 findet die Erstellung neuer Spalten fr Ihre DataTable statt; in den Zeilen 28 bis 31 fgen Sie sie der Tabelle hinzu. Die Zeilen 33 bis 60 fgen ihr neue Zeilen hinzu. Beachten Sie, dass Sie auf jede Spalte mit dem ihr zuvor gegebenen Namen verweisen. Im Grunde bestehen diese Zeilen aus demselben viermal wiederholten Code, wobei jedes Mal eine andere Farbe verwendet wird: Red, Green, Blue und PapayaWhip (ein zugegebenermaen interessanter Name). Wenn Sie diese Anwendung ausfhren, zeigt sie einfach diese Werte in editierbaren Feldern an. Wir sollten sie etwas interessanter gestalten, etwa indem wir Stile hinzufgen. Listing 8.5 zeigt den im Konstruktor zu platzierenden Code dafr. Listing 8.5: Die Stile fr Ihre Farbenanwendung
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: DataGridTableStyle dgtStyle = new DataGridTableStyle(); dgtStyle.MappingName = "Farben"; DataGridTextBoxColumn dgcNameStyle = new DataGridTextBoxColumn(); dgcNameStyle.MappingName = " Farbbezeichnung"; dgcNameStyle.HeaderText = " Farbbezeichnung"; DataGridTextBoxColumn dgcRedStyle = new DataGridTextBoxColumn(); dgcRedStyle.MappingName = "Rot"; dgcRedStyle.ReadOnly = true; dgcRedStyle.HeaderText = "Rot"; DataGridTextBoxColumn dgcGreenStyle = new DataGridTextBoxColumn(); dgcGreenStyle.MappingName = "Grn"; dgcGreenStyle.ReadOnly = true; dgcGreenStyle.HeaderText = "Grn"; DataGridTextBoxColumn dgcBlueStyle = new DataGridTextBoxColumn(); dgcBlueStyle.MappingName = "Blau"; dgcBlueStyle.ReadOnly = true;

300

Das Steuerelement DataGrid

21: 22: 23: 24: 25: 26: 27: 28:

dgcBlueStyle.HeaderText = "Blau"; dgtStyle.GridColumnStyles.Add(dgcNameStyle); dgtStyle.GridColumnStyles.Add(dgcRedStyle); dgtStyle.GridColumnStyles.Add(dgcGreenStyle); dgtStyle.GridColumnStyles.Add(dgcBlueStyle); dgData.TableStyles.Add(dgtStyle);

In Listing 8.5 legen Sie den Kopfzellentext fr jede Spalte fest und setzen alle auer der Farbbezeichnung-Spalte auf readonly. Auf diese Weise kann der Benutzer nur die erste Spalte ndern, nmlich die Farbbezeichnung. Doch die Anwendung ist weiterhin langweilig. Wir wollen eine Methode hinzufgen, die die Farbe des DataGrid-Steuerelements dynamisch ndert. Das CurrentCellChanged-Ereignis wird immer dann ausgelst, wenn der Benutzer zu einer neuen Zelle im Datenblatt springt. Wir wollen dieses Ereignis berwachen, um Farbwechsel anzuwenden: Klickt der Benutzer auf eine Zelle, ndern Sie die Farbe auf die fr diese bestimmte Zelle festgelegte Farbe. Um dies zu bewerkstelligen, fgen Sie Ihrem Konstruktor die folgende Delegaten-Zuweisung hinzu:
dgData.CurrentCellChanged += new EventHandler(this.HandleColor);

Dann bauen Sie Ihrer Anwendung folgende Methode ein:


public void HandleColor(Object Sender, EventArgs e) { if (Color.FromName(dgData [dgData.CurrentCell ].ToString()).IsKnownColor){ dgData.TableStyles [0 ].ForeColor = Color.FromName(dgData [dgData.CurrentCell ].ToString()); } }

Obwohl sie kompliziert aussieht, ist die Methode doch recht simpel. Zuerst muss man auswerten, ob die Farbe in der Zelle eine gltige ist; da die Zelle editierbar ist, knnte der Benutzer ja hergehen und sie auf einen unzulssigen Wert setzen. Wrden Sie versuchen die Farbe mit diesem ungltigen Wert zu modifizieren, wrde sich alles in Wei verwandeln (die Standardfarbe). In der 2. Zeile verwenden Sie ein if-Statement. Zuerst holen Sie die in der Zelle angegebene Farbe mit dem folgenden Befehl:
dgData[dgData.CurrentCell].ToString()

Die CurrentCell-Eigenschaft gibt natrlich den aktuellen Wert in der gerade markierten Datenblattzelle zurck. Diese Eigenschaft gibt ein DataGridCell-Objekt zurck, welches Eigenschaften besitzt, um die genaue Position der Zelle im Datenblatt festzustellen. Leider hat es keine Eigenschaften, um Ihnen den Zelleninhalt zu verraten. Sie mssen hierfr die

301

Datenbindung in Windows Forms

Elementauflistung des DataGrid-Steuerelements verwenden, um diesen Wert zu ermitteln, und ihn schlielich noch obendrein in eine Zeichenfolge konvertieren. In Visual Basic .NET wrde Ihr Code so aussehen:
dgData.Item(dgData.CurrentCell).ToString()

Als Nchstes konvertieren Sie diesen ermittelten Wert mit Hilfe der FromName-Methode in ein Color-Objekt. IsKnownColor liefert einen wahren oder falschen Wert, um anzuzeigen, ob es die Farbe wirklich gibt. In diesem Fall geht es mit folgendem Schritt weiter:
dgData.TableStyles [0].ForeColor = Color.FromName(dgData[dgData.CurrentCell ].ToString());

Der erste Teil ist einfach: Sie ndern die ForeColor-Eigenschaft des DataGridTableStyleObjekts im DataGrid. Der zweite Teil ist identisch mit dem vorigen if-Statement, nur dass Sie diesmal die IsKnownColor-Eigenschaft auslassen. Versuchen Sie nun diese Anwendung auszufhren. Sie sollten bemerken knnen, dass die Schriftfarbe wechselt, sobald Sie zu einer neuen Zelle springen. Sie knnen sogar eine neue Farbe eintippen und die Zellenfarbe ndert sich entsprechend (vorausgesetzt, Ihre Eingabe bezeichnet eine echte Farbe). Abbildung 8.5 zeigt das Ergebnis.

Abbildung 8.5: Ihr DataGrid wechselt seine Farbe.

Die Klassen DataGridTextBoxColumn und DataGridBoolColumn Wie Sie bereits wissen, knnen Sie ein DataGridColumnStyle-Objekt nicht direkt erstellen; Sie knnen nur Instanzen der von ihm abgeleiteten Klassen erzeugen (dieser Klassentyp wird als abstrakte Klasse bezeichnet; eine Klasse, von der man keine Instanz erstellen kann, ist abstrakt). Es liegen nur zwei dieser Klassen vor, nmlich DataGridTextBoxColumn und DataGridBoolColumn, und jede besitzt nur wenige interessante Eigenschaften. Die Klasse DataGridBoolColumn zeigt fr jede Zeile im DataGrid ein Kontrollkstchen an, mit dem ein wahrer oder falscher Wert angezeigt wird. Diese Spalte verfgt ber TrueValue- und FalseValue-Eigenschaften, die die tatschlichen Werte der Spalte angeben (ob wahr oder falsch, das heit 1 oder 0, und so weiter). In hnlicher Weise gibt NullValue den zurckgegebenen Wert an, wenn die gebundenen Daten einen Nullwert enthalten. Man verwendet die AllowNull-Eigenschaft, um Nullwerte zu erlauben oder zu unterbinden.

302

Das Steuerelement DataGrid

Die Klasse DataGridTextBoxColumn zeigt fr jede Zeile in der Spalte ein Textfeld an. Eine besonders interessante Eigenschaft ist Format, die festlegt, wie der Text in der Spalte angezeigt werden soll. (Die Formatierung wird nur auf angezeigte Werte angewandt; der eigentliche Wert ndert sich nicht.) Angenommen, Sie haben einen Datumswert wie etwa "1/1/ 00" mit dem DataGrid verbunden. Sie setzen die Format-Eigenschaft auf die Zeichenfolge "D" und der Wert wird als "Samstag, 1. Januar 2000" (bitte auch die Systemeinstellungen beachten!) ausgegeben. Tabelle 8.4 fhrt die Formatzeichenfolgen und ihre Beschreibungen auf.
Zeichenfolgen
c

Beschreibung Whrungsformat (currency). Legt fest, dass die Zeichenfolge als Whrung anzuzeigen ist. Dezimalformat (decimal). Die Zeichenfolge soll als Dezimalzahl angezeigt werden (z.B. 45, 12345 usw.) Exponentialformat. Beispiel: 8,32e+12; x2. Festkommaformat. Standardmig fgt dieses Format zwei Nullen rechts des Dezimalpunkts und jedes Dezimalwertes ein. Allgemein (general). Diese Zeichenfolge erhlt entweder das Dezimal- oder Exponentialformat. Er gibt das betreffende Format zurck, das die angegebene Zeichenfolge mit der geringsten Zeichenzahl reprsentiert. Zahlenformat (number). Dem Dezimalformat hnlich, jedoch mit Kommas und zwei Dezimalstellen nach dem Komma, etwa 45.324,00. Prozentformat. Schleifenformatbezeichner (Round-trip-Format). Dieses Format wird nur benutzt, wenn exakte Genauigkeit wichtig ist. Ein Double-Wert zum Beispiel ist auf 15 Stellen genau und wenn man ihn in eine Zeichenfolge umwandelt, wird alles jenseits der 15. Stelle gestrichen. Der Schleifenformatbezeichner sorgt dafr, dass diese Werte nicht wegfallen. Hexadezimalformat. Die Zahl "12345" wrde man hexadezimal als "3036" ausdrcken. Kurze Datumsform: "28/1/02" Lange Datumsform: "Montag, 28. Januar 2002" Kurze Zeitform: "12:31"

e f

p r

x d D t

Tabelle 8.4: Codes fr das Formatieren von Zeichenfolgen und DateTime-Angaben

303

Datenbindung in Windows Forms

Zeichenfolgen
T f F

Beschreibung Lange Zeitform: "3:51:24 PM" oder "17:54:22" Kombination aus Langdatum- und Kurzzeitformat: "Montag, 28. Januar 2002, 12:31" Kombination aus Langdatum- und Langzeitformat: "Montag, 28. Januar 2002,
17:54:22"

g G m r s u U y

Kurzdatum und Kurzzeit: "28/1/02 3:51" Kurzdatum und Langzeit: "28/1/02 17:54:22" Tag und Monat: "28. Januar" Das RFC1123-Muster: "Die, 10 Apr 2001 15:51:24 MESZ" Das ISO8601-Muster fr Datum und Zeit: "2001-04-10T15:51:24" Universelles Muster fr Datum und Zeit: "2001-04-10 15:51:24Z" Universelles Muster fr Datum und Zeit: "2001-04-10 15:51:24Z" Jahr und Monat: "Januar 2002"

Tabelle 8.4: Codes fr das Formatieren von Zeichenfolgen und DateTime-Angaben (Forts.)

Die Zeichenfolgenwerte in Tabelle 8.4 sind nicht von der Gro-/Kleinschreibung abhngig. Daher ist "c" das Gleiche wie "C". Beachten Sie auch, dass die Ausgaben der Formatzeichenfolgen fr Datumswerte von den Lndereinstellungen Ihres jeweiligen PCs abhngen. Sie knnen jeder Formatzeichenfolge eine Zahl anhngen, die angibt, fr wie viele Stellen (digits) die Zeichenfolge ausgefhrt werden soll. Wenn Sie die Zahl "0,12345" htten, wrde das "p"-Format die Zeichenfolge als "12,35 %" ausgeben, wobei die Dezimalstellen nach dem Komma zu zwei Stellen auf- oder abgerundet werden. Wollen Sie aber alle ursprnglichen Werte erhalten, hngen Sie die Zahl 3 an: "p3". Es wrde die Zeichenfolge nun als "12,345 %" anzeigen. Beim "d"-Format werden je nach Bedarf Nullen angehngt, ohne den Wert der Zahl zu ndern: Bei der Zahl "12345" wrde "d6" also "012345" ausgeben. Doch "f6" wrde mit demselben Ausgangswert 12345.000000 ausgeben.

304

Das Steuerelement DataGrid

Das Testen von Treffern


Das letzte Thema in dieser Lektion ist die Fhigkeit des DataGrid festzustellen, was der Benutzer angeklickt hat, so etwa welche Zelle, welche Beschriftung usw. Dies wird als Treffertest oder Hit-Test bezeichnet. Die HitTest-Methode macht die Ermittlung dieser Information zu einem Kinderspiel. Beim Treffertesten mssen Sie ein Ereignis behandeln, bei dem der Benutzer einen Teil des DataGrid-Steuerelements anklickt. Idealerweise wre dies das MouseDown-Ereignis, das einen besonderen Delegaten fr die Rckgabe der x- und y-Koordinaten des Klicks verwendet. Um diesem Ereignis einen Delegaten zuzuweisen, verwenden Sie eine ungefhr so aussehende Zeile:
dgData.MouseDown += new MouseEventHandler(this.HandleClick);

Die Signatur Ihres HandleClick-Ereignisses wrde dann so aussehen:


private void HandleClick(Object Sender, MouseEventArgs e) {}

Verwenden Sie die X- und Y-Eigenschaften des MouseEventArgs-Objekts, um festzustellen, wo genau der Benutzer geklickt hat. Rufen Sie dann die HitTest-Methode auf, um herauszufinden, mit welchem Teil des DataGrid-Steuerelements diese Koordinaten bereinstimmen. Die HitTest-Methode gibt ein DataGrid.HitTestInfo-Objekt zurck, das die gewnschten Informationen enthlt. Das folgende Stck Code zeigt die HandleClickMethode beim Gebrauch der HitTest-Methode:
private void HandleClick(Object Sender, MouseEventArgs e) { DataGrid.HitTestInfo myInfo; myInfo = dgData.HitTest(e.X, e.Y); MessageBox.Show("Spalte: " + myInfo.Column.ToString()); MessageBox.Show("Zeile: " + myInfo.Row.ToString()); MessageBox.Show("Typ: " + myInfo.Type.ToString()); }

Die Type-Eigenschaft gibt an, welcher Teil des DataGrid-Steuerelements angeklickt wurde. Sie kann einen der folgenden Werte der Aufzhlung DataGrid.HitTestType haben:

Caption Cell ColumnHeader ColumnResize None ParentRows RowHeader RowResize

305

Datenbindung in Windows Forms

Diese Aufzhlungswerte geben die damit verbundenen Objekte nicht tatschlich zurck (beispielsweise liefert DataGrid.HitTestType.Cell kein DataGridCell-Objekt). Diese Werte geben nur den angeklickten Objekttyp an.

8.4

Zusammenfassung

Heute haben Sie etwas ber Datenbindung in Windows Forms gelernt. Dabei haben Sie auch eine Reihe von Datenbindungs-Steuerelementen kennen gelernt, etwa das DataGrid. Datenbindung ist eine leistungsfhige Methode, Ihre Benutzeroberflche mit Ihrem Datenmodell zu verknpfen. Um ein Steuerelement an Daten zu binden, fgen Sie einfach seiner DataBindings-Eigenschaft eine Datenquelle hinzu und geben die zu bindenden Daten an:
MyControl.DataBindings.Add("Text ", DataSource, "optionaler Name der Datenquelle");

Sie knnen daraufhin die Steuerelementeigenschaft BindingContext.Position verwenden, um durch die verknpften Daten zu navigieren, einen Datensatz nach dem anderen. Ein Groteil der heutigen Lektion beschftigte sich mit dem DataGrid-Steuerelement, weil es so ntzlich und leistungsstark ist. Damit knnen Sie Daten leicht in einem Datenblattformat anzeigen, und die Bestandteile knnen editierbar oder schreibgeschtzt (read-only) sein. Setzen Sie einfach die DataSource-Eigenschaft des Steuerelements auf die zu bindenden Daten. Sie knnen das Aussehen des DataGrid-Steuerelements anpassen, indem Sie die DataGridTableStyle- und DataGridColumnStyle-Objekte verwenden. Ersteres steuert die Anzeige des gesamten DataGrid-Steuerelements, Letzteres die Anzeige pro Spalte. Es muss zuerst ein DataGridTableStyle-Objekt vorliegen, bevor Sie DataGridColumnStyle-Objekte hinzufgen knnen. Verwenden Sie die Objekteigenschaft MappingName, um festzulegen, welche Mitglieder der Daten anzuzeigen sind. Morgen lernen Sie Datenbanken in Ihre Anwendungen zu integrieren.

306

Fragen und Antworten

8.5
F A

Fragen und Antworten


Falls Sie ein Array verwenden, knnen Sie nicht viel dagegen tun. Falls Sie auf ein Array angewiesen sind, wird es das Beste sein, ein Array nur derjenigen Eigenschaften zu erstellen, die Sie angezeigt haben wollen. Im Falle einer DataTable erstellen Sie DataGridColumnStyles-Stile fr jede anzuzeigende Spalte; diejenigen ohne zugewiesenen Stile werden einfach nicht angezeigt. Morgen erfahren Sie Neuigkeiten ber DataSets und DataViews Teile von ADO.NET so dass Sie damit auch angezeigte Spalten anpassen knnen.

Was, wenn ich nicht alle Eigenschaften eines Objekts in DataGrid anzeigen mchte?

Gibt es eine Mglichkeit, eine einzelne Zelle in DataGrid anzupassen? A Leider nein. Wenn Sie diese Art von Kontrolle ber die Anzeige brauchen, dann sollten Sie wahrscheinlich Ihre eigene Benutzeroberflche erstellen und sich nicht auf das DataGrid sttzen.

8.6

Workshop

Dieser Workshop soll Ihnen helfen, sich besser an die heute behandelten Begriffe zu erinnern. Es hilft Ihnen sehr, die Antworten vollstndig zu verstehen, bevor Sie weiterlesen. Die Antworten zu den Quizfragen und den bungen finden Sie in Anhang A.

Quiz
1. Schreiben Sie ein Statement, das einem Textfeld-Steuerelement namens tbOne eine neue Datenbindung hinzufgt. Binden Sie an die Text-Eigenschaft das Array arrOne. 2. Ist das folgende Statement fr das Textfeld aus Frage 1 korrekt?
tbOne.BindingContext.Position += 1;

3. Wahr oder falsch? Man kann Daten an ein Form-Objekt binden. 4. Welches Ereignis wird oft verwendet, um die HitTest-Methode aufzurufen, und aus welchem Grund? 5. Wie wrde die Anzeige der Zahl "458.34e+04" aussehen, wenn man die Formatzeichenfolge "d3" darauf anwendet?

307

Datenbindung in Windows Forms

6. Welcher Eigenschaft des DataGrid-Steuerelements fgen Sie DataGridTableStyleObjekte hinzu? 7. Welcher Eigenschaft des DataGrid-Steuerelements fgen Sie DataGridColumnStyleObjekte hinzu? 8. Angenommen, Sie haben eine DataTable namens dtData mit zwei Spalten namens ColA und ColB. Erzeugen Sie eine neue, leere Zeile. 9. Erzeugen Sie die ColA-Spalte der in Frage 8 erwhnten DataTable als einen IntegerTyp.

bung
Erstellen Sie eine DataGrid und DataTable verwendende Anwendung, in der der Benutzer seine Kontofhrungsinformationen wie in Quicken oder Microsoft Money eingeben kann. Gestatten Sie nicht, dass das DataGrid manuell editiert wird, sondern stellen Sie vielmehr dem Benutzer andere Steuerelemente fr die Eingabe der Informationen bereit, die dann im DataGrid beim Besttigen bzw. Absenden angezeigt werden. Um dieses Beispiel einfach zu halten, gestatten Sie momentan nur Abbuchungen.

308

ADO.NET einsetzen

ADO.NET einsetzen

Der Einsatz von Datenbanken ist heutzutage hufig eine wichtige Komponente in fast jeder modernen Anwendung. Datenbanken knnen eine Flle an Informationen speichern, von Kundendaten fr einen multinationalen Konzern bis hin zu den Gstebuchbesuchern auf einer Website. Daraus folgt, dass das Zusammenspiel zwischen einer Anwendung und einer Datenbank oftmals von hchster Bedeutung ist, da entscheidende Daten, die ausgetauscht werden, auf dem Spiel stehen knnen. ADO.NET ist eine Technologie, mit der Ihre Anwendungen mit praktisch jeder Art von Datenbank oder Speichertechnik kommunizieren knnen, von Oracle ber XML bis zu einfachem Text. Mit diesem leistungsfhigen Mechanismus knnen Sie Daten anzeigen, abrufen, bearbeiten, aktualisieren oder lschen, wobei Sie nur minimale Mittel bentigen. Dieses Kapitel zeigt, wie Sie ADO.NET verwenden und inwiefern es einen immensen Fortschritt gegenber frheren Techniken darstellt. Heute lernen Sie,

wie Sie Datenbanken in Access und SQL Server 2000 erstellen, aus welchen Komponenten ADO.NET besteht, wie Sie Daten aus einer Datenbank an Ihre Anwendungen binden, wie man eine Datenbank aktualisiert, wie XML-Daten gelesen und geschrieben werden.

9.1

Was ist ADO.NET?

Vor der Einfhrung von .NET verwendeten Windows-Anwendungen hufig ActiveX Data Objects (ADO) fr den Zugang zu und die Bearbeitung von Datenbanken. ADO ist eine fr den Datenzugriff eingesetzte Programmierschnittstelle. Diese Methode war effizient und Entwickler konnten sie recht leicht erlernen und einsetzen. ADO litt jedoch unter einem veralteten Datenzugriffsmodell, das zahlreiche Einschrnkungen aufwies, darunter die Unfhigkeit, Daten zu bermitteln. Zusammen mit dem bergang von standardmigen SQL- (Structured Query Language-) Datenbanken zu strker verteilten Datentypen (wie etwa der Extensible Markup Language, kurz XML) stellte Microsoft ADO.NET vor, die nchste Evolutionsstufe von ADO. ADO.NET stellt eine bedeutende berarbeitung von ADO dar. Damit knnen Windows Forms-Anwendungen Daten auf weitaus effizientere und vielfltigere Weise darstellen. Es umfasst den vollen Funktionsumfang von XML und vermag daher leicht mit jeder XMLfhigen Anwendung zu kommunizieren. ADO.NET weist verschiedene neue Merkmale auf, die Ihr Leben als Entwickler erleichtern werden. Sie sollten sich mit ADO.NET vertraut machen, da es wichtig fr dynamische Anwendungen ist. Seine Komplexitt zu kennen, wird Ihnen spter viele Kopfschmerzen ersparen.

310

Was ist ADO.NET?

Abbildung 9.1 zeigt das Datenzugriffsmodell in ADO.NET. Zu OLE DB-konformen Datenquellen wie etwa SQL oder JET (die Microsoft Access Datenbank-Engine) ist ADO.NET vollstndig kompatibel.
Windows Forms Application

ADO.NET

OLE DB

ODBC

SQL

Jet

etc.

Abbildung 9.1: Die Beziehung zwischen ADO.NET und Windows FormsAnwendungen

ADO.NET gegenber ADO


Obwohl ADO und ADO.NET einige Objekte gemeinsam haben, berwiegen doch die Unterschiede. Whrend nmlich ADO verbindungsbasiert war, fut ADO.NET auf kurzen XML-Nachrichtenbasierten Interaktionen mit Datenquellen. Diese Fhigkeit verleiht ADO.NET eine weitaus hhere Effizienz im Zusammenhang mit webbasierten Anwendungen. Einen grundlegenden Wandel stellt in ADO.NET die Integration von XML fr den Datenaustausch dar. ADO.NET verwendet XML in allen Transaktionen, wodurch ADO.NET Datenspeicher viel einfacher als ADO erreichen, persistent speichern und mit ihnen Daten austauschen kann. Somit kann ADO.NET auch eine hhere Performanz erzielen, denn XML-Daten lassen sich leicht in alle mglichen Datentypen umwandeln. Ein weiterer Paradigmenwechsel besteht in der Art und Weise, wie ADO.NET mit Datenbanken zusammenspielt. ADO erfordert zwingend das Sperren von Datenbankressourcen und permanente Verbindungen fr seine Anwendungen, ADO.NET jedoch nicht mehr. Letzteres ist daher weitaus besser skalierbar, denn die Benutzer balgen sich quasi nicht mehr um die Datenbankverbindungen. Tabelle 9.1 fasst die zwischen ADO und ADO.NET vorgenommenen nderungen zusammen.

311

ADO.NET einsetzen

In ADO Daten werden dargestellt durch: Recordset, das einer einzelnen Tabelle oder einem Abfrageergebnis hnelt Datenzugriff: Greift sequenziell auf Zeilen in einem Recordset zu Beziehungen zwischen mehreren Tabellen: Erfordert SQL-JOINs -UNIONs, um Daten aus mehreren Tabellen zu einem Recordset zusammenzufhren Gemeinsame Datenverwaltung:

In ADO.NET

DataSet, das mehrere Tabellen aus jeder beliebigen

Datenquelle enthalten kann

Erlaubt vollstndig nichtsequenziellen Datenzugriff im DataSet durch eine auflistungsbasierte Hierarchie

Verwendet DataRelation-Objekte, die sich zur Navigation ber verbundene Tabellen hinweg eignen

Erfordert die Konvertierung der Datenquellen Verwendet XML, so dass keine Konvertierung ntig ist Programmierbarkeit Verwendet ein Connection-Objekt, um mit den einer Datenquelle zu Grunde liegenden Konstrukten zu kommunizieren Skalierbarkeit Datenbanksperren und -verbindungen fhrten zu Konkurrenzkampf um die Datenressourcen Firewalls Voller Probleme, denn Firewalls verbieten viele Arten von Anfragen Kein Problem, denn XML ist vollstndig Firewallsicher Keine Sperren oder langwierigen aktiven Verbindungen; der Konkurrenzkampf wurde eliminiert Verwendet die streng typisierten Charakteristika von XML; erfordert keine Verwendung von Datenkonstrukten; kann alles nach dem Namen referenzieren

Tabelle 9.1: Zwischen ADO und ADO.NET vorgenommene nderungen

312

Was ist ADO.NET?

ADO.NET und XML


XML ist auch fr die Datenbereitstellung ein sehr ntzliches Hilfsmittel. Da es vollstndig textbasiert ist, knnen Menschen diese Sprache leicht lesen und schreiben und es lsst sich einfach durch die Sicherheitsmanahmen des Internets schleusen. XML speichert Daten, indem es dafr eine hierarchische Darstellung von Feldern und ihren Daten bereitstellt. Wenn Sie eine Datenbank namens Users mit den Feldern Name, UserID und Geburtsdatum htten, so wrde sie in Textform so dargestellt werden:
<Users> <User> <Name /> <UserID /> <Geburtsdatum /> <User> </Users>

Diese Grundstruktur wird als XML-Schema bezeichnet. (Tatschlich liegen die Dinge etwas komplizierter als hier, die detaillierte Darstellung ginge aber ber den Rahmen dieses Buches hinaus.) Mit Hilfe dieses Schemas knnen Sie daraufhin alle Daten in Ihren Tabellen darstellen:
<Users> <User> <Name>Chris Payne</Name> <UserID>1</UserID> <Geburtsdatum>27. Juni</Geburtsdatum> </User> <User> <Name>Eva Payne</Name> <UserID>2</UserID> <Geburtsdatum>15. Juli</Geburtsdatum> </User> </Users> ... ...

Dieser Code lsst sich von jeder Person mit einer Textverarbeitung (wie etwa NotePad) lesen, wohingegen die entsprechende Datenbanktabelle nur von jemandem gelesen werden kann, der diese bestimmte Datenbankanwendung benutzt oder die Tabelle fr eine weitere Datenbankanwendung konvertiert. XML ist eine wirkungsvolle, implementierungsunabhngige Mglichkeit, Daten zu speichern und zu senden sicherlich einer der Grnde, warum es im Web ein so groer Erfolg geworden ist. Daher erscheint es nur folgerichtig, dass auch Datenbanken und ihre Schnittstellen diese Kommunikationsmethode bernehmen.

313

ADO.NET einsetzen

ADO.NET setzt XML bei jedem Datenaustausch und fr interne Darstellungen ein. Sobald Daten aus einer Datenbank geholt werden, werden sie in XML dargestellt und an jeden gewnschten Bestimmungsort geschickt. Da jede Anwendung XML leicht umwandeln kann, sorgt diese Vorgehensweise fr eine breitgefcherte Kompatibilitt Ihrer Daten.

9.2

Eine Datenbank anlegen

Fr die Zwecke dieser Lektion werden Sie es hilfreich finden, eine Datenbank zu haben, die Sie fr das Testen der Beispiele verwenden knnen. Daher beschreibt dieses Unterkapitel, wie man eine Datenbank sowohl in Microsoft Access als auch in SQL Server 2000 erstellt, zwei der meistverbreiteten und am besten erhltlichen Datenbanksysteme.

Eine Access-Datenbank
Sie werden die Datenbank, die Sie hier erstellen, in spteren Lektionen wiederverwenden, daher wollen wir etwas Brauchbares entwerfen. Zuerst erzeugen Sie eine Benutzer-Tabelle zur Speicherung folgender Informationen:

VorName NachName Adresse Stadt Bundesland PLZ Telefonnummer

Diese typische Benutzer-Datenbank lsst sich auf viele verschiedene Anwendungen bertragen. Um die Datenbank in Access zu starten, rufen Sie Access auf und whlen NEU aus dem DATEI-Men. Wenn Sie das Dialogfeld in Abbildung 9.2sehen, whlen Sie darin die Option LEERE ACCESS-DATENBANK aus, nennen sie TYWinForms.mdb und speichern sie im Ordner C:\Winforms\daten. (Der Ordner ist beliebig whlbar; doch die Beispiele in diesem Buch verweisen auf diesen Ordner.) Nach einem Klick auf OK sollten Sie die folgenden Optionen sehen knnen: Erstellt eine Tabelle in der Entwurfsansicht, Erstellt eine Tabelle unter Verwendung des Assistenten und Erstellt eine Tabelle in der Datenblattansicht. Whlen Sie Entwurfsansicht und fangen Sie an, Ihre Spalten einzutragen. Tragen Sie VorName in die Spalte FELDNAME ein und whlen

314

Eine Datenbank anlegen

Sie TEXT in der Spalten DATENTYP. Optional knnen Sie fr dieses Feld eine Beschreibung eingeben wie etwa Benutzervorname. Fllen Sie die brigen Felder der obigen Liste aus, dann sollten Sie eine Tabelle hnlich der in Abbildung 9.3 erhalten.

Abbildung 9.2: Ausgehend von diesem Dialogfeld knnen Sie eine neue, leere Datenbank erstellen.

Abbildung 9.3: Hier geben Sie die Feldinformationen der Tabelle ein.

Fgen Sie schlielich eine weitere Spalte namens UserID hinzu, die Ihre Identittsspalte ist (jeder Wert hier ist eindeutig). Weisen Sie ihr den Datentyp AutoWert zu und rechtsklicken Sie auf den schwarzen Pfeil neben dem Namen. Whlen Sie aus dem Men PRIMRSCHLSSEL aus und beachten Sie, wie das Schlsselsymbol neben dem Namen erscheint. Als Nchstes speichern Sie die Tabelle mit dem Befehl DATEI/SPEICHERN. Der Dateiname sei tblUsers. Whlen Sie zum Schluss DATEI/SCHLIEEN, um die Tabelle tblUsers zu schlieen.

315

ADO.NET einsetzen

Wenn Sie einen Primrschlssel fr mehr als eine Spalte definieren wollen, dann markieren Sie jede Spalte bei gedrckter (Strg)-Taste und rechtsklicken dann, um den Primrschlssel auszuwhlen. Sie knnen nun Benutzer leicht hinzufgen, indem Sie auf die tblUsers-Option doppelklicken, die im Hauptfenster erscheint. Wenn Sie Daten eingeben, brauchen Sie keinen Wert fr die Spalte UserID einzugeben. Eine Zahl wird automatisch eingefgt und bei jedem neuen Datensatz inkrementiert, wie in Abbildung 9.4 zu sehen. Tabelle 9.2 fhrt die verschiedenen in Access 2000 angebotenen Datentypen auf. hnliche Datentypen sind in smtlichen Datenbanksystemen zu finden, wenn auch die konkreten Namen variieren knnen.
Datentyp
Autonumber (AutoWert)

Beschreibung Eine automatisch inkrementierte Ganzzahl (Integer), um Zeilen in einer Tabelle eindeutig zu identifizieren. Wird zur Speicherung von Whrungsdaten verwendet. Wird fr die Speicherung von Datums- und Zeitwerten verwendet. Access 2000 ist in der Lage, Jahreswerte zwischen 100 und 9999 zu speichern. Speichert bis zu 65535 alphanumerische Zeichen. Speichert numerische Werte. Speichert bis zu 255 alphanumerische Zeichen. Wird fr Spalten verwendet, die nur einen von zwei Werten haben knnen (hnlich zu Bit- oder booleschen Typen)

Currency (Whrung) Date/Time (Datum/Uhrzeit)

Memo Number (Zahl) Text Yes/No (Ja/Nein)

Tabelle 9.2: Die Datentypen in Access 2000

Abbildung 9.4: Sie knnen jetzt in Ihre neue Tabelle Daten eingeben.

316

Eine Datenbank anlegen

Eine Datenbank in SQL Server 2000 anlegen


Das Anlegen einer Datenbank in SQL Server 2000 ist ein wenig komplizierter als in Access, doch der Ablauf ist im Wesentlichen der gleiche. Access ist Ihren individuellen Ansprchen angemessen, doch SQL Server 2000 wird in der Regel fr umfangreichere Datenbanken verwendet, die mehr Sicherheit und Stabilitt erfordern. Wir wollen nun die tblUser-Datenbank in SQL Server 2000 erstellen. In Ihrem Startmen finden Sie die Mengruppe MICROSOFT SQL SERVER: Whlen Sie die Menoption ENTERPRISE MANAGER. Sofern alles korrekt eingerichtet wurde, sollten Sie ein dem Windows Explorer hnliches Navigationssystem sehen knnen, das oben MICROSOFT SQL SERVERS zeigt und den Namen Ihres Servers ein paar Hierarchieebenen darunter. Erweitern Sie den DATENBANKEN-Ordner (siehe Abbildung 9.5) und whlen Sie die Option NEUE DATENBANK aus dem Men VORGANG bzw. aus dem Men AKTION, wenn Sie den Enterprise Manager auf dem Client ausfhren. (Sie knnen auch das Kontextmen nutzen).

Abbildung 9.5: Um Ihre Datenbank zu erstellen, erweitern Sie den Ordner DATENBANKEN und whlen die Option NEUE DATENBANK aus dem Kontextmen fr Vorgnge bzw. Aktionen aus.

Geben Sie als Namen TYWinforms in das Dialogfeld ein, das erscheint. Im Moment akzeptieren Sie einfach die Standardoptionen der Registerkarte ALLGEMEIN und klicken auf OK. SQL Server 2000 wird eine neue Datenbank und ein Transaktionsprotokoll fr Sie anlegen. Sie sollten nun TYWINFORMS im Ordner DATENBANKEN sehen knnen. Als Nchstes erweitern Sie diesen Eintrag in der Strukturansicht und whlen dann den TABELLEN-Knoten.

317

ADO.NET einsetzen

Sie bemerken, das SQL Server 2000 bereits einige Tabellen angelegt hat. Es handelt sich um Systemtabellen, die SQL Server benutzt, um die Elemente, die Sie in die Tabelle stellen, zu registrieren. Ignorieren Sie diese Tabellen fr den Moment und whlen Sie NEUE TABELLE aus dem VORGANG-Men aus. Sie sehen ein Gitter, das dem in Access gezeigten hnelt. Geben Sie die Informationen wie in Access ein (siehe Abbildung 9.6), nur dass Sie diesmal den varchar-Datentyp anstelle von Text verwenden, denn Text hat in SQL Server 2000 eine andere Bedeutung. Beachten Sie, dass SQL Server 2000 weitaus mehr Datentypen bereitstellt als Access. Dieser Server ist eine leistungsfhigere Anwendung, bei der Entwickler die Datenbankoptionen mit viel grerer Feinheit einstellen knnen. Markieren Sie im SPALTEN-Bereich in der unteren Dialogfeldhlfte (siehe Abbildung 9.6) das UserID-Feld und whlen Sie JA fr die Eigenschaft Identitt. Dann klicken Sie auf dieses Feld mit der rechten Maustaste und whlen PRIMRSCHLSSEL FESTLEGEN aus. Speichern Sie als Nchstes diese Tabelle als tblUsers, indem Sie die Entwurfsansicht mit einem Klick auf die Steuerschaltflche X rechts oben schlieen, die Frage nach dem Speichern der Tabelle mit JA beantworten und dann den gewnschten angeben. Um in Ihre Tabelle Daten eingeben zu knnen, klicken Sie mit der rechten Maustaste auf die Tabelle und whlen TABELLE FFNEN/ALLE ZEILEN ZURCKGEBEN aus, um einen tabellarischen Dateneingabemechanismus hnlich dem in Access zu ffnen. Tragen Sie ein paar Namen und Adressen ein. (Tragen Sie jedoch nichts im UserID-Feld ein.) Klicken Sie auf das Ausrufezeichen (= Ausfhren) im Men, um die UserIDs automatisch zu erhhen. Schlieen Sie zu guter Letzt Ihre neue Tabelle durch einen Klick auf die SCHLIEENSteuerschaltflche.

Abbildung 9.6: Um eine Tabelle zu erstellen, geben Sie Feldnamen und Datentypen ein.

318

SQL fr die Datenbankabfrage

Ich empfehle

Bitte beachten Sie

Verwenden Sie SQL Server, wenn Sie eine Setzen Sie SQL Server nicht fr sehr einfache umfangreiche Datenbanklsung entwickeln, fr Datenbanken mit nur wenigen Tabellen ein. die Performance wichtig ist und die fortgeschrit- Das fhrt zu Komplikationen, die unntig sind. tene Datenbankfunktionen erfordert.

9.3

SQL fr die Datenbankabfrage

Die Structured Query Language (SQL) ist ein Industriestandard, um an eine Datenbank Befehle auszugeben. Anwendungen wie SQL Server, Access und Oracle verwenden gleichermaen SQL, um Daten zu manipulieren (wiewohl jede ihre eigene Methode hat, SQL einzusetzen). Die grundlegenden Fhigkeiten von SQL bestehen im Abrufen, Bearbeiten und Lschen von Daten, doch in Wirklichkeit handelt es sich um eine leistungsfhige und komplizierte Sprache, von der wir aber einen Groteil nicht in diesem Buch besprechen werden. Um aber mit ADO.NET arbeiten zu knnen, bentigen Sie eine gewisse Vertrautheit mit SQL, und die folgenden Abschnitte bilden eine kurze Einfhrung. Wie jede Programmiersprache verfgt auch SQL ber Statements, die ausgefhrt werden (doch anders als etwa C# muss SQL nicht kompiliert werden). Die vier wichtigen Statements, die Sie zwingend fr die Zwecke dieser Lektion kennen mssen, sind SELECT, DELETE, INSERT und UPDATE; zum Glck folgen sie alle einer hnlichen Syntax.

Das SELECT-Statement
SELECT ist wohl das gebruchlichste SQL-Statement. Sein Zweck ist im Grunde, eine Reihe von Daten aus einer Datenbanktabelle zu holen. Die Syntax dafr sieht so aus: SELECT Auswahlliste FROM Tabellenname [WHERE Suchbedingung] ORDER BY Sortierausdruck [ASC | DESC ]}

Die Struktur folgt grundlegenden Mustern des Englischen. Auswahlliste ist im Allgemeinen eine durch Kommas getrennte Liste der Spalten, die Sie abrufen wollen. VorName, NachName wrde so etwa die Werte in diesen beiden Spalten zurckgeben. Zudem knnen Sie ein Sternchen verwenden, um alle Spalten einer bestimmten Tabelle (siehe Tipp) zurckzugeben. Tabellenname ist der Name derjenigen Tabelle, aus der die Daten geholt werden sollen.

319

ADO.NET einsetzen

Hier ist ein einfaches SELECT-Statement, das Sie fr die von Ihnen angelegten Datenbanken verwenden knnten:
SELECT * FROM tblUsers

Dieses Statement gibt alle Zeilen aller Spalten in der tblUsers-Tabelle zurck. Sie knnen damit tun, was Sie wollen. Das folgende Statement ist in der Funktion identisch:
SELECT UserID, VorName, NachName, Adresse, Stadt, Bundesland, PLZ, Telefonnummer FROM tblUsers

Sie sollten SELECT * nicht verwenden, weil es oft mehr Daten liefert als Sie brauchen, und das kann zu reduzierter Performanz fhren. Sie sollten statt dessen ausdrcklich nur diejenigen Spalten angeben, die Sie brauchen. Das ist zwar mehr Mhe beim Codeschreiben, doch Ihr Code wird dafr schneller ausgefhrt.
SELECT-Statements geben stets alle Zeilen in der Datenbank zurck, es sei denn, man schrnkt dies durch eine WHERE-Bedingung ein. Sie knnen WHERE verwenden, um zustzliche Kriterien fr die zurckgegebenen Daten anzugeben. Eine WHERE-Bedingung kann im Grunde jedes logische Statement sein, hnlich wie das if-Statement in normalen Programmiersprachen. Hier ein Beispiel: SELECT FROM tblUsers WHERE VorName = 'Chris' SELECT FROM tblUsers WHERE Stadt = 'ASP-Stadt' und NachName = 'Mitchell'

Das erste Statement gibt nur Datenstze zurck, in denen das Feld VorName den Wert Chris aufweist. Das zweite Statement gibt nur diejenigen Datenstze zurck, in denen Stadt gleich ASP-Stadt ist und das NachName-Feld Mitchell enthlt. Um Jokerzeichen fr Ihre WHERE-Bedingung zu verwenden, gibt es das LIKE-Schlsselwort gefolgt von (einem oder mehreren) Jokerzeichen. Die Tabelle 9.3 fhrt die Operatoren auf, die das LIKE-Schlsselwort akzeptiert.
Operator * _ (ein Unterstrich) [] bersetzung Eine Zeichenfolge von null oder mehr Zeichen (in SQL Server wird dieses Zeichen durch % dargestellt). Genau ein Zeichen. Ein bestimmtes Zeichen in vorgegebener Menge.

Tabelle 9.3: Jokerzeichen fr den LIKE-Operator in Access

320

SQL fr die Datenbankabfrage

Die folgenden Statements demonstrieren den Gebrauch von LIKE:


1 2 3 SELECT * FROM tblUsers WHERE VorName LIKE '*s*' SELECT * FROM tblUsers WHERE VorName LIKE '_hris' SELECT * FROM tblUsers WHERE VorName LIKE '[abcd]hris'

Das SQL-Statement in Zeile 1 gibt jede Zeile zurck, in der der Vorname im Wert ein s enthlt. Der Sternchen-Operator * funktioniert in SQL auf gleiche Weise wie in Windows und bedeutet null oder mehr Zeichen. Das Statement in Zeile 2 gibt alle Datenstze zurck, in denen der Vorname ein Zeichen enthlt, das von der Zeichenfolge hris gefolgt wird. Auf Grund der vorliegenden Daten ergibt dieses Statement nur einen Datensatz, aber Sie sehen den Sinn. Zeile 3 gibt jeden Datensatz zurck, in dem der Vorname eine Zeichenfolge enthlt, die mit einem der Buchstaben a, b, c oder d beginnt, gefolgt von der Zeichenfolge hris. Die ORDER BY-Klausel gibt die Reihenfolge an, in der die Daten zurckgegeben werden, so etwa in alphabetischer Reihenfolge. Verwendet man diese Bedingung nicht, kann man nie wissen, in welcher Reihenfolge man die Daten aus der Datenbank erhlt. Mgen sie auch in alphabetischer Reihenfolge eingegeben worden sein, so heit dies noch lange nicht, dass man sie auch in dieser Reihenfolge wieder herausbekommt. Mit der ORDER BY-Klausel knnen Sie festlegen, wie die Spalten sortiert werden, etwa so:
SELECT * FROM tblUsers ORDER BY VorName ASC

Dieses Statement gibt alle Datenstze in alphabetisch nach Vornamen sortierter Reihenfolge zurck. Die Sortieroption DESC dreht diese Reihenfolge um (ASC = ascending, aufsteigend; DESC = descending, absteigend). Sie knnen mehrere, durch Komma abgegrenzte Spaltennamen in der ORDER BY-Klausel angeben, und zwar fr jede Spalte mit ihrer eigenen Sortierreihenfolge. SQL verwendet die zustzlichen Spalten, falls die erste duplizierte Daten (Verdopplungen) enthlt. Geben Sie aber keinesfalls Spalten an, die nicht in der Tabelle vorhanden sind. Sie knnen SELECT-Statements einsetzen, um Daten aus mehr als einer Tabelle auf einmal zurckzugeben. Fr das Tabellenname-Argument verwenden Sie dann einfach eine durch Kommas getrennte Liste von Tabellennamen. Normalerweise bestehen zwischen mehreren Tabellen Beziehungen, aber dieses Thema ginge ber das Anliegen dieses Kapitels hinaus. Alle anderen SQL-Statements, die wir heute betrachten, folgen derselben grundlegenden Form wie die SELECT-Abfrage. Daher verwenden wir weniger Zeit darauf.

321

ADO.NET einsetzen

Das INSERT-Statement
Ein weiteres gebruchliches SQL-Statement ist INSERT, das neue Zeilen (also neue Daten) in eine Datenbanktabelle einfgt. Seine grundlegende Syntax sieht wie folgt aus:
INSERT [INTO] Tabellenname [(Spaltenliste)] VALUES (DEFAULT | NULL | Ausdruck)

Dieses problemlose Statement hnelt der Syntax von SELECT. Wir wollen ein Beispiel auf der Basis unserer Benutzer-Datenbank erstellen:
INSERT INTO tblUsers (VorName, NachName, Adresse, Stadt, Bundesland, PLZ, Telefonnummer) VALUES ('Chris', 'Payne', 'ASP-Strae 135', ASP-Stadt', 'Florida', '36844', '8006596598')

Dieses Statement fgt eine neue Zeile mit den in der VALUES-Klausel angegebenen Feldwerten ein. Der in VALUES gelieferte Datentyp muss mit dem Datentyp in der entsprechenden Spalte bereinstimmen, oder man erhlt eine Fehlermeldung. Wenn Sie eine Spalte angeben, mssen Sie auch einen Wert angeben, oder Sie bekommen noch eine Fehlermeldung.

Das UPDATE-Statement
Das UPDATE-Statement aktualisiert vorhandene Zeilen in einer Datenbanktabelle. Sie knnen hier eine WHERE-Bedingung einbauen, um nur eine Untermenge der Tabellenwerte zu aktualisieren. Die Syntax sieht wie folgt aus:
UPDATE Tabellenname SET Spaltenname = (DEFAULT | NULL | Ausdruck) [WHERE Suchbedingung]

Lassen Sie die WHERE-Bedingung weg, werden die angegebenen Spalten in jeder Zeile der Datenbank aktualisiert. Setzen Sie dieses Statement nur mit grter Vorsicht ein, denn damit knnen Sie Ihre Daten komplett ruinieren! Indem wir wieder unsere Datenbanktabelle tblUser verwenden, lsst sich UPDATE im Beispiel so verwenden:
UPDATE tblUsers SET Adresse = 'ASP-Strae 136', 'Stadt = ASP-Weiler' WHERE VorName = 'Chris' AND NachName = 'Payne'

Dieses Statement ndert die Werte Adresse und Stadt fr diejenigen Datenstze, in denen das Feld VorName den Wert Chris und das Feld NachName den Wert Payne enthlt. Das ist momentan nur ein einziger Datensatz. Sie knnen nur die in der Spaltenname-Liste bentigten Spalten angeben. Die WHERE-Bedingung ist identisch mit der fr das SELECT-Statement; Sie knnen auch hier wieder Jokerzeichen verwenden.

322

Einfhrung in DataSet

Das DELETE-Statement
Das letzte hier behandelte Statement ist DELETE, welches Zeilen aus einer Datenbank lscht. Die Syntax sieht wie folgt aus:
DELETE FROM Tabellenname WHERE (Suchbedingung)

In diesem einfachen Statement gibt es lediglich die sehr wichtige WHERE-Bedingung zu beachten. Denn wird sie nicht angegeben, lscht DELETE alle Zeilen aus der gesamten Datenbanktabelle ein recht katastrophales Ereignis (auer wenn man eine Sicherheitskopie hat). Wieder ist die WHERE-Bedingung die gleiche wie fr SELECT ein Beispiel:
DELETE FROM tblUsers WHERE VorName = 'Chris'

Dieses Statement lscht nur Datenstze, in denen der VorName auf Chris lautet.

9.4

Einfhrung in DataSet

In ADO.NET dreht sich alles um das DataSet. Dieses Objekt ist ein brandneues Konzept, das das althergebrachten Recordset aus ADO ersetzt. Das DataSet ist ein einfacher speicherresidenter Datenspeicher, der ein einheitliches Programmiermodell fr den Datenzugriff bereitstellt, unabhngig vom jeweiligen Datentyp. Stellen Sie sich DataSet als eine Art Miniaturdatenbank vor. Anders als das Recordset enthlt das DataSet vollstndige Datenmengen, darunter Bedingungen, Beziehungen und sogar mehrere Tabellen auf einmal. Das DataSet-Objektmodell ist in Abbildung 9.7 zu sehen. Wenn Sie eine Verbindung zur Datenbank aufbauen, geben Sie ihr quasi eine Schachtel und befehlen dem Datenspeicher, diese mit etwas zu fllen. Sie knnen sie mit Datentabellen fllen, mit eigenen Daten von sonst wo oder mit anderen Daten. Ein DataSet ist eine solche Schachtel; die Datenbank bergibt Ihnen Kopien aller Daten, um die Sie bitten und diese knnen Sie in der Schachtel herumtragen, ohne zugleich eine Verbindung zur Datenbank aufrechterhalten zu mssen. Abbildung 9.7 enthlt auch ein DataTable-Objekt, das eine einzelne Datenbanktabelle darstellt. (Das DataSet verwaltet eine Auflistung dieser Tabellen im TablesCollection-Objekt.) Die DataTable ist eine vollstndige Reprsentation der entsprechenden Tabelle inklusive aller Beziehungen und Schlsselbedingungen. Sie enthlt zwei weitere Auflistungen namens Rows und Columns, die jeweils die Daten und das Tabellenschema darstellen. Mit dem Objekt RelationsCollection knnen Sie ber Tabellen je nach deren Beziehungen untereinander navigieren. Sie brauchen in Ihren SQL-Abfragen keine Joins und Unions mehr (die alte Methode zur Verknpfung von Tabellen), denn ADO.NET macht das

323

ADO.NET einsetzen

DataSet ExtendedProperties RelationsCollection TablesCollection DataTable ChildRelations Columns Constraints DefaultView ExtendedProperties ParentRelations PrimaryKeys Rows DataRows DataColumns

Abbildung 9.7: Das DataSet-Objektmodell enthlt vollstndige Mengen von Daten.

Navigieren viel einfacher. Die eigentlichen Beziehungen werden durch DataRelationObjekte dargestellt, die Informationen ber die beiden zu verknpfenden Tabellen enthalten, so etwa ber die Primr- und Fremdschlsselbeziehungen sowie die Namen der Beziehungen. Mit Hilfe des DataRelation-Objekts knnen Sie sogar Beziehungen hinzufgen. ADO.NET erzwingt zudem Schlsseleinschrnkungen; damit knnen Sie keine Tabelle derartig ndern, dass die Beziehung zu einer anderen Tabelle verletzt wrde. Das ExtendedProperties-Objekt umfasst zustzliche Informationen wie etwa Benutzernamen und Kennwrter. Kurzum, das DataSet ist ein vielseitiges Hilfsmittel. Es ist vllig gleich, wo die Daten herkommen oder wohin sie gehen; das DataSet ist vllig von jedem Datenspeicher abgetrennt. Daher knnen Sie DataSet als eine eigenstndige Entitt behandeln. Wie gesagt, kann DataSet mehrere DataTables enthalten. Da Sie bereits wissen, wie man DataTables erstellt, beherrschen Sie bereits die Methode, DataSets zu erstellen. Angenommen, Sie haben bereits eine Tabelle erzeugt und mit Zeilen und Spalten gefllt, dann ist alles, was Sie noch tun mssen, sie dem DataSet hinzuzufgen:
DataSet MyData = new DataSet("Meine Daten"); MyData.Tables.Add(Tabelle)

Diese Fhigkeit mag ja interessant sein, hat aber noch wenig praktischen Nutzen. Warum sollten Sie schlielich von Hand mehrere DataTables erzeugen, um sie dann in ein DataSet zu stecken? Diese Fhigkeit wird aber ntzlicher, sobald Sie mit Datenbanken zu tun haben.

324

Das Objektmodell von ADO.NET

9.5

Das Objektmodell von ADO.NET

Nun ist es an der Zeit, mit einer Datenbank zu kommunizieren. In den folgenden Abschnitten betrachten Sie die Objekte im ADO.NET-Gerst. Um mit Daten in einer Windows Forms-Anwendung zu interagieren, befolgen Sie diese fnf allgemeinen Schritte: 1. Erstellen Sie ein Objekt fr eine Datenbankverbindung. 2. ffnen Sie die Datenbankverbindung. 3. Fllen Sie ein DataSet mit den gewnschten Daten. 4. Richten Sie (optional) ein DataView-Objekt fr die Datenanzeige ein. 5. Binden Sie ein Steuerelement an das DataSet oder die DataView. Da Sie schon gestern den fnften Schritt grndlich untersucht haben, konzentrieren Sie sich heute auf die ersten vier. Das erfolgt in den nchsten Abschnitten. Sie werden zwei verschiedene Objekttypen verwenden. Eine Menge ist ausschlielich fr SQL Server reserviert (Sie knnen das den SQL-Provider nennen), und die andere ist fr alles andere bestimmt (Sie knnen das den OLE DB-Provider nennen). Der OLE DB-Provider funktioniert auch mit dem SQL Server, doch der SQL-Provider nutzt einige spezielle Charakteristika von SQL Server, die ihn effizienter machen. Die zwei verschiedenen Objektmengen werden jeweils durch die Prfixe Sql oder OleDB gekennzeichnet. Meist knnen Sie zwischen den beiden wechseln, indem Sie einfach das Prfix austauschen. Alle Eigenschaften und Methoden sind fast identisch. In diesem Buch konzentrieren wir uns auf den SQL-Provider. Bevor wir Code schreiben, wollen wir einen Blick auf die ADO.NET-Steuerelemente werfen, die wir einsetzen:

SqlConnection-Objekt: Dieses Steuerelement stellt eine Datenbankverbindung dar. SqlDataAdapter-Objekt: Dieses Mehrzweckwerkzeug dient der Kommunikation mit

einer Datenbank nach dem Aufbau einer Verbindung (etwa um Daten abzurufen, einzufgen oder zu aktualisieren).

SqlCommand-Objekt: Dieses Steuerelement stellt einen einzelnen von SqlAdapter

erzeugten Befehl dar. Das Objekt enthlt Informationen ber das SQL-Statement, das Sie auszufhren versuchen, und ist eng mit dem SqlAdapter verknpft.

DataSet: Das kennen Sie schon: die unverbundene Darstellung einer Datenbank.

325

ADO.NET einsetzen

Die Schritte 1 und 2 sind mit dem SqlConnection-Objekt verbunden, whrend Schritt 3 mit den Objekten SqlDataAdapter und SqlCommand arbeitet. Sie werden alle diese Objekte gleich einsetzen.

Verbindung zur Datenbank aufnehmen und Daten abrufen


Die Datenbankverbindung wird mit Hilfe des SqlConnection-Objekts gehandhabt (oder auch OleDBConnection, je nach Prfix). Alles was Sie dazu brauchen ist eine Verbindungszeichenfolge (Connection String) eine Zeichenfolge, die .NET mitteilt, wo sich die Datenbank befindet, um welchen Datenbanktyp es sich handelt, welche Version das ist usw. Dieser Verbindungszeichenfolge ist jedoch allgemein gehalten, so dass Sie sie nach dem ersten Erstellen nicht noch einmal schreiben mssen. Je nach der verwendeten Datenbank ndert sich die Zeichenfolge. Das folgende Stck Code zeigt Beispielzeichenfolgen fr SQL Server und Access:
//SQL Server "Initial Catalog=TYWinforms;Data Source=localhost;" //Access, C# "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + "C:\\winforms\\data\\TYWinforms.mdb;User ID=blah;Password=blah"; //Access, VB .NET "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & _ "C:\\winforms\\data\\TYWinforms.mdb;User ID=blah;Password=blah";

In C# besitzt das Backslash-Zeichen (\) eine besondere Bedeutung, daher muss man ihm einen weiteren Backslash voranstellen, um diese besondere Bedeutung zu unterdrcken und literales Zeichen darzustellen. VB .NET hingegen kennt solche Einschrnkungen nicht. Als Nchstes mssen Sie mit der Verbindungszeichenfolge ein SqlConnection-Objekt erzeugen:
String strConn = "Initial Catalog=TYWinforms;Data Source=localhost;"; SqlConnection objConn = new SqlConnection(strConn);

Der nchste Schritt besteht im Fllen des DataSets mit den gewnschten Daten. Dafr mssen Sie ein paar SQL-Statements schreiben. Listing 9.1 zeigt eine einfache Anwendung, die diese Statements schreibt und die Daten daraufhin in einem DataGrid anzeigt.

326

Das Objektmodell von ADO.NET

Listing 9.1: Auf eine Datenbank (tblUsers) zugreifen


1: using System; 2: using System.Windows.Forms; 3: using System.Drawing; 4: using System.Data; 5: using System.Data.SqlClient; 6: 7: namespace TYWinforms.Day9 { 8: public class Listing91 : Form { 9: DataGrid dgData = new DataGrid(); 10: DataSet objDS = new DataSet(); 11: 12: public Listing91() { 13: String strConn = "Initial  Catalog=TYWinforms;DataSource=localhost;User ID=sa"; 14: SqlConnection objConn = new SqlConnection(strConn); 15: 16: SqlDataAdapter objCmd = new SqlDataAdapter("SELECT * FROM  tblUsers", objConn); 17: objCmd.Fill(objDS, "tblUsers"); 18: 19: dgData.DataSource = objDS; 20: dgData.DataMember = "tblUsers"; 21: dgData.Dock = DockStyle.Fill; 22: 23: this.Controls.Add(dgData); 24: } 25: 26: public static void Main() { 27: Application.Run(new Listing91()); 28: } 29: } 30: }

Sie sollten die Angabe localhost in Zeile 13 Ihrer eigenen Installation von SQL Server anpassen. Die Standardeinstellung localhost knnte zwar funktionieren, doch Sie sollten dies auf jeden Fall noch einmal im SQL Enterprise Manager prfen. SQL Server lsst sich fr folgende Arten von Anmeldekonten konfigurieren: Windows NT/2000-Konten (Windows-Authentifizierung), SQL Server-Konten (SQL Server-Authentifizierung) oder beide Kontenarten (gemischter Modus). "sa" ist das SQL Server-Konto fr den Systemadministrator. Ist Ihr SQL Server jedoch auf Windows-Authentifizierung eingestellt, sollten Sie eine Verbin-

327

ADO.NET einsetzen

dungszeichenfolge wie die folgende verwenden, die dann Ihr Windows NT/ 2000-Konto (statt Ihr SQL Server-Konto) verwendet, um Sie zu authentifizieren:
"Initial Catalog=TYWinforms;Data Source=localhost;Trusted_Connection=true;";

Beachten Sie zunchst die Integration der Namensrume System.Data und System.Data.SqlClient (Letzterer hiee System.Data.OleDBClient, falls Sie mit dem OLE DB-Provider arbeiten). Sie sind fr die verschiedenen Datenverbindungsobjekte notwendig. In den Zeilen 13 und 14 erstellen Sie die Verbindungszeichenfolge und das SqlConnection-Objekt. In Zeile 16 wird Ihnen das SqlDataAdapter-Objekt vorgestellt. Es ist ein Mehrzweckwerkzeug fr Datenbanken; es kann SQL-Statements ausfhren, Daten zurckgeben und DataSets fllen. Sie verwenden es hier, um ein SELECT-Statement auszufhren und das DataGrid-Steuerelement zu fllen. Der Konstruktor fr dieses Objekt bernimmt zwei Parameter: Der erste besteht aus dem auszufhrenden SELECT-Statement (hier holen Sie einfach alle Datenstze aus der Tabelle tblUsers) und der zweite aus dem SqlConnection-Objekt, das den SqlDataAdapter darber informiert, wo er nach den Daten suchen soll. (Mehr dazu gleich.) In Zeile 17 rufen Sie die Fill-Methode auf. Sie ist recht interessant, fhrt sie doch im Grunde das ihr zugeleitete SELECT-Statement aus, erstellt ein DataTable-Objekt, dessen Zeilen das Ergebnis des SELECT-Statements sind, und steckt diese DataTable in ein DataSet. Die zwei Parameter dieser Methode sind das DataSet, um dort die gelieferten Ergebnisse zu platzieren, und der Name der erstellten DataTable.

Abbildung 9.8: Ihr gebundenes DataGrid zeigt die Informationen aus der Datenbank an.

328

Das Objektmodell von ADO.NET

Der Rest des Codes bindet Daten an das DataGrid; es drfte Ihnen noch von gestern bekannt sein. Sie setzen die Eigenschaft DataSource auf das neu gefllte DataSet und die DataMember-Eigenschaft auf die im DataSet enthaltene DataTable. Wenn Sie das DataMember nicht nher bestimmen, zeigt das DataGrid die Objekte im DataSet und nicht die Daten an (anders ausgedrckt erscheint in der Tabelle tblUsers anstelle der Benutzer). Ihrer Anwendung Datenbankfunktionen hinzuzufgen, ist also recht einfach und erfordert lediglich fnf bis sechs Zeilen Code. Listing 9.1 produziert das in Abbildung 9.8 gezeigte Ergebnis.

Daten bearbeiten
Mit dem SqlDataAdapter knnen Sie zudem Daten bearbeiten, wobei die Statements UPDATE, INSERT und DELETE verwendet werden knnen. Es stehen verschiedene Eigenschaften extra fr diese Befehle bereit. Der SqlDataAdapter verfgt ber die Eigenschaften UpdateCommand, InsertCommand, DeleteCommand und SelectCommand. SelectCommand wird im Konstruktor gefllt (Zeile 16 von Listing 9.1) und ausgefhrt, sobald die Fill-Methode aufgerufen wird. Die anderen Eigenschaften werden ausgefhrt, wenn die Update-Methode des SqlDataAdapters aufgerufen wird, doch dabei gibt es einen Haken. Diese Statements kommen nur dann zur Ausfhrung, wenn sich bereits Daten im zugrunde liegenden DataSet gendert haben. Nehmen Sie an, Sie wollen smtliche Vorkommen des Namens Christopher in der Benutzer-Datenbank in Chris ndern. Theoretisch knnten Sie eine UpdateCommand-Eigenschaft wie die folgende verwenden:
objCmd.UpdateCommand = new SqlCommand("Update tblUsers SET VorName = 'Chris' WHERE VorName = 'Christopher'); objCmd.Update();

Sie erstellen ein neues SqlCommand-Objekt, bergeben ihm das passende UPDATE-Statement und rufen dann die Update-Methode zwecks Ausfhrung des Statements auf. Theoretisch sollte dieses Vorgehen zuerst das DataSet aktualisieren und dann die nderungen zurck in die Datenbank schreiben. Es funktioniert aber nicht wie erwartet. Im Wesentlichen diktiert dieses Statement, auf welche Weise nderungen im DataSet in die Datenbank zurckgeschrieben werden sollen. Die Update-Methode prft das DataSet, um festzustellen, ob sich Werte gendert haben. Ist das nicht der Fall, passiert nichts. Andernfalls nimmt der SqlDataAdapter nur die genderten Datenstze, fhrt das SQLStatement an ihnen aus und schreibt die Ergebnisse zurck in die Datenbank. Im obigen Codestck wrde der vom Benutzer genderte Christopher-Datensatz in der Datenbank zu Chris aktualisiert werden, ungeachtet des Wertes, den der Benutzer wirklich

329

ADO.NET einsetzen

gendert hat (weil es das UPDATE-Statement eben so festlegt). Wrde der Benutzer einen anderen Datensatz ndern, der Christopher nicht enthielte, wrde nichts passieren (weil die WHERE-Bedingung im UPDATE-Statement diese Datenstze ausschliet). Dieses Vorgehen ist mhselig. Will man Daten ndern, muss man sie erst von Hand im
DataSet ndern und dann ein passendes SQL-Statement schreiben, das die nderungen

zurck in die Datenbank schreibt. Das ist zeitraubend und obendrein fehlertrchtig. Zum Glck stellt ADO.NET ein Objekt namens SqlCommandBuilder zur Verfgung, das all diese SQL-Befehle automatisch fr Sie erstellt. Wenn also der Benutzer im DataGrid ein Feld ndert, brauchen Sie sich nicht um die auszufhrenden SQL-Befehle zu kmmern. Sie rufen nur die Update-Methode auf, fertig. Um dieses Objekt zu verwenden, fgen Sie die folgende Zeile nach Zeile 17 in Listing 9.1 ein:
SqlCommandBuilder objBuilder = new SqlCommandBuilder(objCmd);

Diese Zeile erzeugt ein SqlCommandBuilder-Objekt fr den bereits erstellten SqlDataAdapter. Das Objekt fungiert als Wachhund: Es beobachtet alles, was bei den Daten vor sich geht und erzeugt rasch ohne Ihr Zutun die passenden SQL-Befehle. Wir wollen die Daten in Listing 9.1 editierbar machen. Fgen Sie zuerst dem CurrentCellChanged-Ereignis des DataGrid-Steuerelements einen Delegaten hinzu, damit Sie wissen, wann der Benutzer mit dem Bearbeiten einer Zelle fertig ist:
dgData.CurrentCellChanged += new EventHandler (this.UpdateData);

Dann fgen Sie den Ereignishandler hinzu, der einfach die Update-Methode des SqlDataAdapters aufruft: public void Update(Object Sender, EventArgs e) { objCmd.Update(objDS, "tblUsers"); }

Sofern Sie bereits den SqlCommandBuilder erstellt haben, schreibt dieses Statement alle nderungen am DataSet (via DataGrid) in die Datenbank zurck, so dass sie zu dauerhaften nderungen werden. Der erste Parameter ist das betreffende DataSet und der zweite die DataTable im DataSet, in der nderungen erfolgen. Kompilieren Sie diese Anwendung neu und fhren Sie sie aus. Jede von Ihnen vorgenommene nderung wird nun automatisch in die Datenbank zurckgeschrieben. Um dies zu testen, ndern Sie ein paar Eintrge und schlieen dann die Anwendung. Sie knnen sie wieder ffnen, um zu sehen, ob die nderungen permanent sind, oder Sie knnen die Datenbankanwendung (SQL Server oder Access) direkt ffnen, um die nderungen zu sehen. Diese Einsatzweise des SqlCommandBuilder-Objekts funktioniert nur dann, wenn die zurckgegebenen Daten einen Primrschlssel enthalten. Beim Aufbau der Tabelle tblUsers haben Sie den Primrschlssel auf das Feld UserID gesetzt. Dementsprechend knnen Sie den Primrschlssel per Programmcode unter Verwendung Ihres DataTable-Objekts setzen:

330

Das Objektmodell von ADO.NET

DataColumn[] arrKeys = {myTable.Columns["Spalte A"]}; myTable.PrimaryKey = arrKeys;

Die PrimaryKey-Eigenschaft der DataTable bernimmt ein Array von DataColumn-Objekten, weshalb Sie in diesem Codestck ein Array erstellen. Auerdem muss das SelectCommand zuerst mit einem SELECT-Statement gefllt werden. Ihre Datenbankanwendung ist nunmehr voll funktionsfhig. Sie knnen Daten anzeigen, Benutzer knnen diese ndern und die Datenbank wird aktualisiert. Wir wollen uns ein weiteres Beispiel ansehen, das in Listing 9.2 abgedruckt ist. Diesmal verwenden Sie kein DataGrid-Steuerelement zur Datenanzeige, sondern vielmehr einige Textfelder, die nur einen Datensatz auf einmal anzeigen. Sie stellen Schaltflchen bereit, damit Benutzer durch die Datenstze navigieren knnen, sowie eine SPEICHERN-Schaltflche, mit der die Benutzer nderungen in die Datenbank schreiben knnen. Listing 9.2: Eine etwas komplexere Datenbankanwendung
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: using using using using using System; System.Windows.Forms; System.Drawing; System.Data; System.Data.SqlClient;

namespace TYWinforms.Day9 { public class Listing92 : Form { DataSet objDS = new DataSet("tblUsers"); SqlDataAdapter objCmd; TextBox tbFirstName = new TextBox(); TextBox tbLastName = new TextBox(); Label lblFirstName = new Label(); Label lblLastName = new Label(); Button btNext = new Button(); Button btPrev = new Button(); Button btSave = new Button(); public Listing92() { lblFirstName.Location = new Point(10,10); lblFirstName.Text = "VorName: "; lblLastName.Location = new Point(10,35); lblLastName.Text = "NachName: "; tbFirstName.Location = new Point(75,10); tbLastName.Location = new Point(75,30); btNext.Text = "Nchster";

331

ADO.NET einsetzen

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: 72: 73:

btNext.Location = new Point(150,75); btNext.Click += new EventHandler(this.MoveNext); btPrev.Text = "Voriger"; btPrev.Location = new Point(10,75); btPrev.Click += new EventHandler(this.MovePrev); btSave.Text = "Speichern"; btSave.Location = new Point(75,125); btSave.Click += new EventHandler(this.Save); String strConn = "Initial Catalog=TYWinforms;DataSource=localhost;User ID=sa"; SqlConnection objConn = new SqlConnection(strConn); objCmd = new SqlDataAdapter("SELECT * FROM tblUsers", objConn); objCmd.Fill(objDS, "tblUsers"); SqlCommandBuilder objBuilder = new SqlCommandBuilder(objCmd); tbFirstName.DataBindings.Add("Text", objDS, "tblUsers.VorName"); tbLastName.DataBindings.Add("Text", objDS, "tblUsers.NachName"); this.Controls.Add(tbFirstName); this.Controls.Add(tbLastName); this.Controls.Add(lblFirstName); this.Controls.Add(lblLastName); this.Controls.Add(btNext); this.Controls.Add(btPrev); this.Controls.Add(btSave); } private void Save(Object Sender, EventArgs e) { this.BindingContext[objDS, "tblUsers"].EndCurrentEdit(); objCmd.Update(objDS, "tblUsers"); } private void MoveNext(Object Sender, EventArgs e) { this.BindingContext[objDS, "tblUsers"].Position += 1; } private void MovePrev(Object Sender, EventArgs e) { this.BindingContext[objDS, "tblUsers"].Position -= 1; } public static void Main() {

332

Das Objektmodell von ADO.NET

74: 75: 76: 77:

Application.Run(new Listing92()); } } }

Listing 9.2 weist eine Menge vielschichtiger Funktionen auf, daher wollen wir es lieber Zeile fr Zeile durchgehen. In den Zeilen 1 bis 5 importieren Sie Namensrume. Die Zeilen 9 bis 18 richten die verschiedenen Objekte und Steuerelemente ein, die Sie in der Anwendung verwenden werden. Die Zeilen 21 bis 38 richten diverse Eigenschaften der Formularsteuerelemente ein und weisen ihnen ein paar Delegaten zu. Ab Zeile 40 erstellen Sie eine Datenbankverbindung mit Hilfe der Standardverbindungszeichenfolge. In den Zeilen 43 und 44 setzen Sie ein SqlDataAdapterObjekt ein, um Datenstze aus der Datenbank zu holen und damit ein DataSet zu fllen. In Zeile 45 erzeugen Sie Ihr SqlCommandBuilder-Objekt, um sicherzustellen, dass SQL-Befehle automatisch erzeugt werden, sobald nderungen in der Tabelle erfolgen. In den Zeilen 47 und 48 binden Sie als Nchstes Elemente der gelieferten Daten mit Hilfe ihrer DataBindings-Eigenschaften an die TextBox-Steuerelemente. Denken Sie daran, dass die drei Parameter fr die Add-Methode nacheinander die Eigenschaft der anzubindenden TextBox, das Datenquellenobjekt und das anzubindende Feld in der Datenquelle sind. Die Werte tblUsers.VorName und tblUsers.NachName geben die Spalten fr Vor- und Nachnamen in tblUsers an. Wrden Sie sie an ein DataGrid binden, knnten Sie einfach tblUsers als Feldname verwenden, doch bei TextBox-Steuerelementen mssen Sie etwas genauer sein. Schlielich wollen Sie nicht mehr als eine Spalte an eine TextBox binden. Gehen wir zu den Ereignishandlern ber. Sowohl die MoveNext- und MovePrevMethoden ndern die Position-Eigenschaft des BindingContext-Objekts, damit man in den Datenstzen vor- und zurckblttern kann (Zeilen 66 und 70). Beachten Sie, dass Sie den BindingContext der Form bearbeiten und nicht jede gebundene TextBox einzeln. Wenn Sie das Formular manipulieren, werden alle gebundenen Steuerelemente im Formular ebenfalls modifiziert. Bevor wir die Save-Methode in Zeile 59 untersuchen, schauen wir uns an, was mit der Anwendung passiert. Wenn Sie sie ausfhren, sehen Sie Vor- und Nachnamen des ersten Datensatzes in der Datenbank, wie in Abbildung 9.9 zu sehen. Sie knnen auf die Schaltflche NCHSTER klicken, um zum nchsten Datensatz zu blttern, so dass sich der Text im Textfeld ndert. Die Schaltflche VORIGER blttert einen Datensatz zurck.

333

ADO.NET einsetzen

Abbildung 9.9: Sie sehen den Vor- und Nachnamen, wenn Sie Daten an die TextBox-Steuerelemente binden.

Wenn Sie Elemente bearbeiten, geht etwas Seltsames im Hintergrund vor sich. ndert man den Wert in einem der Textfelder, aktualisiert es theoretisch sofort das DataSet; dann knnen Sie die Update-Methode von SqlDataAdapter aufrufen, um die nderungen in die Datenbank zurckzuschreiben. Tatschlich werden die nderungen am DataSet erst dann vorgenommen, wenn sie BindingContext besttigt hat. Dies kann an mehreren Stellen erfolgen (dazu gehrt das ndern der Position-Eigenschaft). Jetzt haben wir ein Problem: Angenommen, der Benutzer ndert den Wert des Vornamens im Textfeld und klickt dann auf die Schaltflche SPEICHERN. Weil der BindingContext noch keine Gelegenheit hatte, die nderungen abzusegnen, hat sich das DataSet noch nicht gendert auch die Datenbank wird sich nicht ndern. Sie brauchen eine Mglichkeit, wie Sie BindingContext zwingen knnen, die nderungen zu besttigen, damit das Speichern der nderungen stattfinden kann. Die EndCurrentEdit-Methode aus Zeile 60 macht genau dies. Sie schreibt alle anstehenden nderungen in das DataSet, ohne die Position-Eigenschaft ndern zu mssen. Danach knnen Sie einfach die Update-Methode aufrufen, wie in Zeile 62 zu sehen. Jetzt ist Ihre Anwendung vollstndig.

9.6

Andere ADO.NET-Objekte

Bis hierher haben Sie die Objekte SqlDataAdapter und DataSet verwendet, um Datenquellen zu verarbeiten. Obwohl diese Objekte leistungsfhig und notwendig sind, erweisen sie sich fr manche Zwecke als weniger geeignet. Das DataSet beispielsweise kann unter Umstnden ein recht sperriges Objekt sein, da es ja eine komplette Mini-Datenbank an Informationen zu pflegen hat, inklusive Tabellenstruk-

334

Andere ADO.NET-Objekte

turen, Einschrnkungen, Indizes, Beziehungen usw. Zuweilen bentigt man diese Extrafunktionen gar nicht. Um diesen Missstand zu beheben, fhrt ADO.NET die Objekte SqlDataReader und OleDbDataReader ein. Diese Objekte erhalten nicht ihre eigene Kopie der Datenbank aufrecht, sondern holen vielmehr einen Datensatz nach dem anderen deshalb erfordern sie eine stndige Verbindung zur Datenbank (anders als der unverbundene Datenspeicher des DataSets). Die DataReader-Objekte weisen daher einen eingeschrnkten Funktionsumfang auf; man kann zum Beispiel nicht laufend zwischen Datenstzen hin und her springen, ohne fr das stndige Neuholen von Daten einen Preis an Performanz zu zahlen. Um ein DataReader-Objekt nutzen zu knnen, mssen Sie zuerst die Objekte SqlCommand und OleDbCommand einsetzen. Beide stellen einen an einer Datenbank ausgefhrten SQLBefehl dar und knnen ein Ergebnis liefern oder auch nicht. Sie haben sie bereits eingesetzt. Wenn Sie den SqlDataAdapter-Eigenschaften fr das Aktualisieren, Einfgen, Lschen oder Auswhlen etwas zuweisen, dann erzeugt .NET fr Sie verschiedene SqlCommand-Objekte dafr. Es sind diese hinter den Kulissen agierenden Objekte, die eigentlich die Datenbank abfragen und Ergebnisse liefern; der SqlDataAdapter ist lediglich der Nutznieer ihrer harten Arbeit. Ein SqlCommand-Objekt lsst sich auf verschiedene Art und Weise erstellen. Sie knnen ihm je nach Bedarf ein SQL-Statement zur Ausfhrung bergeben, ein SQL-Statement und ein SqlConnection-Objekt oder auch rein gar nichts. Hier ist ein Beispiel:
SqlCommand objCmd = new SqlCommand("SELECT *FROM tblUsers"); //oder SqlCommand objCmd = new SqlCommand("SELECT *FROM tblUsers", objConn); //oder SqlCommand objCmd = new SqlCommand();

Stellen Sie nicht einen oder mehrere dieser Werte whrend der Instantiierung des Objekts bereit, so knnen Sie dies mit Hilfe der Eigenschaften CommandText und Connection durchfhren. Sobald Sie Ihr SqlCommand-Objekt haben, knnen Sie einen SQL-Befehl auf unterschiedliche Weise ausfhren. Wenn Ihre Abfrage keine Daten liefert (etwa bei einem DELETE-Statement), dann knnen Sie die Methode ExecuteNonQuery verwenden:
SqlCommand objCmd = new SqlCommand("DELETE FROM tblUsers ", objConn); objCmd.ExecuteNonQuery();

Liefert Ihre Abfrage einen Einzelwert, lsst sich die ExecuteScalar-Methode einsetzen. Sie holt die erste Spalte der ersten Zeile in den gelieferten Ergebnissen so wie in diesem Beispiel:
SqlCommand objCmd = new SqlCommand("SELECT COUNT(*)FROM tblUsers", objConn); int intCount = (int)objCmd.ExecuteScalar();

Wollen Sie Ihre Daten als XML geliefert bekommen, knnen Sie die ExecuteXmlReaderMethode einsetzen, die ein XmlReader-Objekt zurckgibt.

335

ADO.NET einsetzen

Wenn Sie schlielich Ihre Daten mit Hilfe eines SqlDataReader-Objekts verarbeiten wollen, steht dafr die ExecuteReader-Methode bereit:
SqlDataReader objReader; SqlCommand objCmd = new SqlCommand("SELECT *FROM tblUsers ", objConn); objReader = objCmd.ExecuteReader();

Die einfache Anwendung in Listing 9.3 verwendet SqlDataReader, um die Ergebnisse aus einer Datenbank anzuzeigen. Listing 9.3: Die Verwendung eines SqlDataReaders
1: using System; 2: using System.Windows.Forms; 3: using System.Drawing; 4: using System.Data; 5: using System.Data.SqlClient; 6: 7: namespace TYWinforms.Day9 { 8: public class Listing93 : Form { 9: Label lblData = new Label(); 10: 11: public Listing93() { 12: lblData.Dock = DockStyle.Fill; 13: 14: String strConn = "Initial  Catalog=TYWinforms;DataSource=localhost;User ID=sa"; 15: SqlConnection objConn = new SqlConnection(strConn); 16: objConn.Open(); 17: 18: SqlDataReader objReader; 19: SqlCommand objCmd = new SqlCommand("SELECT UserID, FirstName FROM  tblUsers", objConn); 20: objReader = objCmd.ExecuteReader(); 21: 22: while (objReader.Read()) { 23: lblData.Text += objReader.GetInt32(0) + ": " +  objReader.GetString(1) + "\n"; 24: } 25: 26: objReader.Close(); 27: objConn.Close(); 28: 29: this.Controls.Add(lblData); 30: } 31:

336

Andere ADO.NET-Objekte

32: 33: 34: 35: 36:

public static void Main() { Application.Run(new Listing93()); } } }

In den Zeilen 14 und 15 legen Sie ganz normal mit der Erzeugung eines SqlConnection-Objekts los. In Zeile 16 mssen Sie die Verbindung mit Hilfe der Open-Methode ffnen. Verwenden Sie den SqlDataAdapter, ist dieser Schritt nicht erforderlich, doch der SqlDataReader bentigt ja eine offene Verbindung, um arbeiten zu knnen. Die Zeilen 18 bis 20 erstellen ein SqlDataReaderObjekt sowie ein SqlCommand-Objekt mit einer SQL-Abfrage, bevor sie die ExecuteReader-Methode aufrufen, um die Abfrageergebnisse im Reader abzulegen. Die Read-Methode von SqlDataReader springt so lange zum nchsten Datensatz in der Ergebnismenge, bis deren Ende erreicht ist, woraufhin sie dann false zurckgibt. Daher verwendet man in Zeile 22 eine while-Schleife, um so alle Datenstze im Reader zu durchlaufen. Sobald das Ende der Datenstze erreicht ist, endet die while-Schleife. Das SqlDataReader-Objekt verfgt ber eine Reihe von Get-Methoden, die die Werte aus der Ergebnismenge holen. Welche Methode sie genau benutzen, hngt jeweils vom zu erwartenden Datentyp ab. So liefert beispielsweise die GetString-Methode einen Zeichenfolgenwert und GetInt32 einen Integerwert. Diese Methoden bernehmen alle einen einzelnen Parameter, der angibt, welches Feld (bzw. welche Spalte) des aktuellen Datensatzes geliefert werden soll. In Zeile 23 holen Sie die ersten und zweiten Spaltenwerte jedes Datensatzes, wobei es sich um UserID und VorName aus der Tabelle tblUsers handelt. Nachdem alle Datenstze durchlaufen wurden, sorgen Sie fr das Schlieen des Readers und der SqlConnection-Objekte, indem Sie deren jeweilige Close-Methode verwenden, wie in den Zeilen 26 und 27 zu sehen. Listing 9.3 produziert das in Abbildung 9.10 gezeigte Ergebnis.

Abbildung 9.10: Sie knnen mit dem SqlReader-Objekt durch Datenstze blttern.

337

ADO.NET einsetzen

Ich empfehle

Bitte beachten Sie

Verwenden Sie einen SqlDataReader, wenn Sie Setzen Sie den SqlDataReader nicht ein, wenn Daten nur anzeigen, aber nicht bearbeiten wollen. Sie dem Benutzer gestatten wollen Daten zu bearbeiten oder wenn Sie komplexe Operationen mit diesen Daten ausfhren mssen (wie etwa das Bearbeiten der Tabellenbeziehungen). Verwenden Sie dann statt dessen SqlDataAdapter oder DataSet.

Parametrisierte Abfragen und gespeicherte Prozeduren


Eine gespeicherte Prozedur ist eine Reihe von einem oder mehreren SQL-Statements, die auerhalb einer Anwendung vordefiniert wird. In der Datenbank hat sie ihren Platz neben den Tabellen. Sie werden fr Ihre Datenbankinteraktionen hufig gespeicherte Prozeduren einsetzen. Sie sind schneller als normale SQL-Statements, denn sie werden kompiliert. Sie tragen zudem dazu bei, dass Ihre Anwendung leichter zu pflegen ist: Wenn sich Ihre Datenanforderungen ndern sollten, gestaltet sich das Aktualisieren der gespeicherten Prozeduren weitaus einfacher, als die Abfragen in Ihrer Anwendung umzuschreiben und sie neu zu kompilieren. In Access werden gespeicherte Prozeduren als Abfragen bezeichnet; sie verwenden eine etwas andere Syntax als die in SQL Server. In diesem Buch beschrnken wir uns auf gespeicherte Prozeduren in SQL Server. Um in SQL Server eine gespeicherte Prozedur zu erstellen, ffnen Sie den Enterprise Manager und expandieren die Liste der Datenbanken so weit, bis Sie den Abschnitt GESPEICHERTE PROZEDUREN unterhalb der TYWinforms-Datenbank sehen. Rechtsklicken Sie auf diesen Abschnitt und whlen Sie NEUE GESPEICHERTE PROZEDUR aus. Fr unsere Zwecke wollen wir alles etwas einfacher halten. Tippen Sie den folgenden Code in das nun erscheinende Fenster:
CREATE PROCEDURE qryGetUser (@intUserID int ) AS SELECT *FROM tblUsers WHERE UserID =@intUserID

Ihr Fenster sollte so aussehen wie das in Abbildung 9.11. Diese Abfrage liefert die kompletten Informationen ber einen bestimmten Benutzer, wie durch die Verwendung der Benutzer-ID deutlich gemacht wird. Elegant an dieser Abfrage ist, dass die Benutzer-ID variieren kann: Die Abfrage akzeptiert einen Parameter namens @intUserID, den Ihre Anwendung bereitstellen muss, um die richtigen Ergebnisse zu erhalten. Sehen wir uns an, wie man diesen Parameter bereitstellen kann.

338

Andere ADO.NET-Objekte

Abbildung 9.11: Benutzen Sie dieses Fenster, um in SQL Server eine gespeicherte Prozedur zu erstellen.

Alle Parameter mssen Teil eines SqlCommand-Objekts sein (das bedeutet, dass Sie entweder einen SqlDataAdapter und ein DataSet oder einen SqlDataReader einsetzen, um Ihre Daten abzurufen). Mit Hilfe der oben erstellten gespeicherten Prozedur qryGetUser erzeugt das folgende Stck Code ein SqlCommand-Objekt mit den notwendigen Parametern:
SqlCommand objQuery = new SqlCommand("qryGetUser ", objConn); objQuery.CommandType = CommandType.StoredProcedure; objQuery.Parameters.Add("@intUserID ", SqlDbType.Int, 4).Value = 1;

Die Deklaration des SqlCommand-Objekts beginnt wie blich, mit einer Ausnahme: Statt der auszufhrenden SQL-Anweisung bergeben Sie dem Objekt den Namen der gespeicherten Prozedur. In der zweiten Zeile mssen Sie die CommandType-Eigenschaft auf den StoredProcedure-Wert der CommandType-Aufzhlung setzen. Andere Werte in dieser Aufzhlung sind Text (der Standardwert, der bei bergabe einer SQL-Anweisung verwendet wird) und TableDirect, welcher einfach die Struktur einer Tabelle liefert (dieser letzte Wert funktioniert nur im Zusammenspiel mit einem OLE DB-Provider). Zum Schluss fgen Sie der Parameters-Eigenschaft des SqlCommand-Objekts einen neuen Parameter hinzu. Der erste Parameter fr die Add-Methode ist der Name des erwarteten Parameters in der gespeicherten Prozedur; er muss den gleichen Namen tragen wie in der gespeicherten Prozedur. Der zweite Parameter gibt den Datentyp des Parameters an; Tabelle 9.4 fhrt die mglichen Werte der SqlDbType-Aufzhlung auf. Der dritte Wert gibt die Gre an (dieser Wert wurde beim Erstellen der Tabelle gesetzt; er braucht nicht exakt genau zu sein). Mit Hilfe der Value-Eigenschaft setzen Sie dann den Wert fest, den der Parameter der gespeicherten Prozedur verwenden soll. In unserem Fall luft es darauf hinaus, dass Sie die Informationen fr den Benutzer mit der Benutzer-ID 1 zurckerhalten.

339

ADO.NET einsetzen

SqlDbType-Wert BigInt Binary Bit Char DateTime Decimal Float Image Int Money NChar NText NVarChar Real SmallDateTime SmallInt SmallMoney Text Timestamp TinyInt UniqueIdentifier VarBinary VarChar Variant

Entsprechender .NET-Typ
Int64

Array des Typs Byte


Boolean String DateTime Decimal Double

Array des Typs Byte


Int32 Decimal String String String Single DateTime Int16 Decimal String DateTime Byte Guid

Array des Typs Byte


String Object

Tabelle 9.4: Werte der SqlDbType-Aufzhlung

340

Andere ADO.NET-Objekte

Da Sie nun ein SqlCommand-Objekt inklusive Parametern haben, knnen Sie damit tun, was Sie wollen. Um beispielsweise Daten fr einen SqlDataReader zurckzuerhalten, verwenden Sie folgenden Code:
SqlDataReader objReader; objReader = objQuery.ExecuteReader();

Wenn Sie hingegen ein DataSet fllen wollen, setzen Sie folgenden Code ein:
DataSet objDS = new DataSet(); SqlDataAdapter objCmd = new SqlDataAdapter(); objCmd.SelectCommand = objQuery; objCmd.Fill(objDS, "tblUsers ");

Im zweiten Codestck weisen Sie das SqlCommand-Objekt der SelectCommand-Eigenschaft des SqlDataAdapters zu. Je nachdem, was die gespeicherte Prozedur eigentlich bewirkt, htten Sie statt dessen auch die UpdateCommand-, InsertCommand- oder DeleteCommand-Eigenschaften angeben knnen. Es gibt eine Alternative: Bei Verwendung des SqlDataAdapters brauchen Sie nicht zwingend ein separates SqlCommand-Objekt anzulegen. Sie knnen auf den notwendigen Befehl auch aus der SelectCommand-Eigenschaft heraus verweisen:
SqlDataAdapter objCmd = new SqlDataAdapter("qryGetUser ",objConn); objCmd.SelectCommand.CommandType = CommandType.StoredProcedure; objCmd.SelectCommand.Parameters.Add("@intUserID ", SqlDbType.Int, 4).Value = 1;

Das Objekt DataView


Eine DataView ist die benutzerdefinierte Ansicht einer DataTable. Sie knnen also eine vorhandene mit Daten gefllte DataTable auswhlen und sie mit Filtern Ihren Wnschen anpassen. Dafr brauchen Sie also weder Ihre DataTable zu ndern noch Ihre Datenbank laufend neu abzufragen: Es ist einfach eine gefilterte Ansicht. Sie knnen die DataView an die Steuerelemente in Windows Forms binden. Eine DataView erstellen Sie ganz einfach, indem Sie ihr die anzupassende Tabelle mitteilen:
DataView myView = new DataView (objDS.Tables["tblUsers"]);

Sie knnen die RowFilters-Eigenschaft fr das Filtern der Ergebnisse einsetzen; im Grunde funktioniert diese Eigenschaft wie die WHERE-Klausel in einem SQL-Statement. Das folgende Statement wrde zum Beispiel nur diejenigen Datenstze zur Anzeige bringen, in denen das VorName-Feld das Wort Christopher enthlt.
myView.RowFilter = "VorName = 'Christopher'";

341

ADO.NET einsetzen

Die Sort-Eigenschaft der DataView, mit der Sie die Sortierreihenfolge der Daten ndern knnen, folgt der gleichen Syntax wie ORDER BY in SQL-Befehlen:
myView.Sort = "VorName ASC, NachName DESC";

Die RowStateFilter-Eigenschaft filtert Datenstze gem ihrem aktuellen Status, beispielsweise, ob sie bearbeitet wurden, unverndert geblieben sind oder hinzugefgt wurden:
myView.RowStateFilter = DataViewRowState.Unchanged;

Diese Werte lassen sich mit Hilfe des Operators | verknpfen (OR in VB .NET). Die mglichen Werte in der DataViewRowState-Aufzhlung lauten wie folgt:

Added: Gibt an, dass die Zeile neu ist. CurrentRows: Schliet unvernderte, neue und bearbeitete Zeilen mit ein. Deleted: Gibt eine gelschte Zeile an. ModifiedCurrent: Gibt den aktuellen Wert aller Zeilen an, nachdem eine beliebige

Zeile gendert wurde.

ModifiedOriginal: Gibt den ursprnglichen Wert aller Zeilen an, nachdem eine beliebige Zeile gendert wurde. None: Gibt Zeilen an, die keinen Status haben. OriginalRows: Gibt alle ursprnglichen Zeilen an, darunter auch die unvernderten

und gelschten Zeilen.

Unchanged: Gibt eine unvernderte Zeile an.

Sie knnen die Art und Weise steuern, wie der Benutzer mit der DataView interagiert, indem Sie die Eigenschaften AllowDelete, AllowEdit und AllowNew verwenden. Da die DataView als Operationsbasis ein DataTable-Objekt bentigt, knnen Sie eine DataView nicht zusammen mit einem SqlDataReader-Objekt einsetzen. Nutzen Sie statt dessen die Objekte DataSet und SqlDataAdapter.

9.7

XML im Zusammenhang mit ADO.NET

ADO.NET ist eng mit XML verzahnt. ADO.NET verwendet XML in smtlichen Transaktionen, so dass es vielseitiger verwendbar und effizienter wird. Sie knnen folglich leicht von ADO.NETs Kernobjekt, dem DataSet, zu XML gelangen und umgekehrt. Die meisten der XML-bezogenen Funktionen, die Sie mit ADO.NET ausfhren wollen, lassen sich mit nur zwei Methoden bewerkstelligen: ReadXml und GetXml. Die erste ldt

342

XML im Zusammenhang mit ADO.NET

XML aus jedem beliebigen XML-basierten Objekt (etwa einer Textdatei, aus einem Datenstrom, einem XML-Dokument usw.) und legt die Ergebnisse in einem DataSet ab. Die zweite Methode nimmt umgekehrt Daten aus einem DataSet und liefert eine Zeichenfolge, die den XML-Code enthlt, der die Daten darstellt. Wir wollen uns ein konkretes Beispiel ansehen. Listing 9.4 zeigt das XML-Codestck vom Kapitelanfang: Listing 9.4: XML-Beispieldaten
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: <Users> <User> <Name>Chris Payne</Name> <UserID>1</UserID> <Geburtsdatum>27. Juni</Geburtsdatum> </User> <User> <Name>Eva Payne</Name> <UserID>2</UserID> <Geburtsdatum>15. Juli</Geburtsdatum> </User> </Users>

Speichern Sie Listing 9.4 als Sample.xml im Ordner C:\winforms\data. Dieser XML-Code prsentiert eine Datenbank namens Users mit einer Tabelle namens User. Diese Tabelle umfasst drei Spalten: Name, UserID und Geburtsdatum. Als Nchstes zeigt Listing 9.5 eine einfache Anwendung, die die XML-Daten ldt und bindet. Listing 9.5: XML-Daten mit ADO.NET binden
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: using using using using using System; System.Windows.Forms; System.Drawing; System.Data; System.Data.SqlClient;

namespace TYWinforms.Day9 { public class Listing95 : Form { DataSet objDS = new DataSet(); TextBox tbName = new TextBox(); TextBox tbGeburtsdatum = new TextBox(); Label lblName = new Label(); Label lblGeburtsdatum = new Label();9

343

ADO.NET einsetzen

16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39:

public Listing95(){ lblName.Location = new Point(10,10); lblName.Text = "Name: "; lblGeburtsdatum.Location = new Point(10,35); lblGeburtsdatum.Text = " Geburtsdatum: "; tbName.Location = new Point(75,10); tbGeburtsdatum.Location = new Point(75,30); objDS.ReadXml("c:\\winforms\\data\\sample.xml"); tbName.DataBindings.Add("Text ", objDS, "User.Name "); tbGeburtsdatum.DataBindings.Add("Text ", objDS, "User.Geburtsdatum "); this.Controls.Add(tbName); this.Controls.Add(tbGeburtsdatum); this.Controls.Add(lblName); this.Controls.Add(lblGeburtsdatum); } public static void Main(){ Application.Run(new Listing95()); } } }

Der Groteil dieses Codes initialisiert die Benutzeroberflche. Interessant wird es erst ab Zeile 24, in der Sie die ReadXml-Methode zum Laden einer Datei einsetzen. Der Parameter zeigt den Pfad zur fraglichen Datei an. In den Zeilen 26 und 27 binden Sie ganz normal die zwei TextBox-Steuerelemente. Sorgen Sie jedoch dafr, dass der dritte Parameter die Struktur des XML-Dokuments richtig widerspiegelt. Listing 9.5 fhrt zu dem in Abbildung 9.12 gezeigten Ergebnis. Was passiert nun, wenn die Daten aktualisiert werden? Kann man die Update-Methode aufrufen, um die nderungen ins XML-Dokument zurckzuschreiben? Leider nicht. Die einzige Mglichkeit, die XML-Quelle zu aktualisieren, besteht darin, explizit eine XML-Datei zu schreiben, doch dieser Vorgang ist zum Glck einfach. Zuerst erstellen Sie ein Objekt XmlDataDocument, um den XML-Code aus dem DataSet zu holen. Dann setzen Sie dessen Save-Methode ein, um die Datei zu schreiben:
using System.Xml; ... XmlDataDocument objXml = new XmlDataDocument(objDS); objXml.Save("c:\\winforms \\data \\sample2.xml ");

344

Zusammenfassung

Abbildung 9.12: Sie knnen die Anwendung in Listing 9.5 verwenden, um XML-Daten anzubinden.

Ein XmlDataDocument-Objekt ist in der XML-Welt das quivalent zum SqlDataAdapter. Dieses Objekt ist mit dem DataSet verknpft, so dass Sie auf einfache Weise vom einen zum anderen gelangen. Die erste Zeile im obigen Codestck instantiiert ein neues XMLDataDocument-Objekt und bergibt ihm das fragliche DataSet. Die Save-Methode schreibt die XML-Daten zum angegebenen Pfad. Das XMLDataDocument weist noch einige weitere ntzliche Mitglieder auf, die wir hier aber nicht errtern werden. Eine komplette Referenz dazu finden Sie in der Dokumentation des .NET Framework SDK.

9.8

Zusammenfassung

Heute haben Sie eine Menge ber Datenbanken gelernt: von ihrer Erstellung in Access und SQL Server ber den Einsatz parametrisierter gespeicherter Prozeduren bis hin zum Einsatz von XML. Mit ADO.NET kann man auf fast jedes Datenbanksystem mit Hilfe einer einfachen Verbindungszeichenfolge zugreifen, die angibt, wo sich die Daten befinden, welche Erlaubnis fr den Zugriff ntig ist usw. Sie haben von SQL-Statements wie SELECT, INSERT, DELETE und UPDATE erfahren. Mit Hilfe dieser Statements knnen Sie Daten aus jedem Datenbanksystem, das sie untersttzt, holen und bearbeiten. Jedes Statement befolgt eine hnliche Syntax, so dass man sie leicht alle erlernen kann, sobald man eines kennt.
DataSet ist das zentrale Objekt in ADO.NET. Es stellt eine vollstndige Mini-Datenbank dar. Wird das DataSet mit den Objekten SqlDataAdapter und OleDbDataAdapter sowie mit

Datenbindung gekoppelt, lassen sich dem Benutzer Daten auf einfache Weise prsentieren. Sobald man die Update-Methode des SqlDataAdapters mit dem SqlCommandBuilderObjekt verknpft, kann man nderungen in die Datenbank zurckschreiben, um sie dauerhaft zu speichern.

345

ADO.NET einsetzen

Zustzlich zum DataSet gibt es die DataReader-Objekte, die zwar effizienter einzusetzen sind, aber einen geringeren Leistungsumfang besitzen. Diese Objekte lassen sich mit Hilfe der ExecuteReader-Methode des SqlCommand-Objekts fllen. Das DataView-Objekt erlaubt Ihnen, die Anzeige Ihrer DataTable-Objekte wirkungsvoll anzupassen, wodurch Ihnen erspart bleibt, mhevoll immer und immer wieder SQL-Statements ausfhren zu mssen, sobald Sie Daten filtern wollen. Zum Schluss lernten Sie, wie ADO.NET und das DataSet mit Hilfe der Methoden ReadXml und GetXml auch XML-Daten laden und bearbeiten knnen. Diese Daten lassen sich genau wie andere Daten binden, nderungen lassen sich mit Hilfe der Save-Methode des XMLDataDocument-Objekts in die XML-Dateien zurckschreiben.

9.9
F A

Fragen und Antworten


Ja. Mit Hilfe der Fill-Methode knnen Sie ein DataSet mit Daten aus einem Recordset fllen, doch auf Grund von Beschrnkungen in ADO geht das nicht auch umgekehrt. Dieser Vorgang erfordert, die ADO-Typbibliotheken in das .NET Framework zu importieren. (Mehr dazu an Tag 14, wenn es um den Einsatz von ActiveX geht.)

Kann ich das ADO-Recordset weiterhin mit ADO.NET und dem DataSet verwenden?

Warum sind gespeicherte Prozeduren effizienter als normale SQL-Statements? Sollte ich also statt dessen immer gespeicherte Prozeduren verwenden? A Gespeicherte Prozeduren sind aus zwei Grnden effizienter. Zum einen sind sie kompiliert und kompilierte Anwendungen sind stets schneller als unkompilierte. Zweitens hat SQL Server einen so genannten Ausfhrungsplan. Wurde eine gespeicherte Prozedur bereits einmal ausgefhrt, wei SQL Server genau, wo er die Daten suchen soll und wie er sie am besten holt. Dieser Ausfhrungsplan steigert die Ausfhrungsgeschwindigkeit einer Abfrage enorm. Der Haken dabei ist nur, dass dieser Ausfhrungsplan bei normalen Abfragen nicht gespeichert wird und SQL Server ihn bei der nchsten Ausfhrung der Abfrage neu erstellen muss. Die Antwort auf die Frage, ob man gespeicherte Prozeduren stets normalen Abfragen vorziehen sollte, ist umstritten. Die Wahl bleibt Ihnen berlassen. Doch zum Glck gibt es ein paar Richtlinien, um Ihnen dabei zu helfen. Im Allgemeinen verwenden Sie eine gespeicherte Prozedur, wenn Ihre Abfrage ber ein einfaches SELECT-Statement hinausgeht. (Je komplizierter ein SQL-Statement, desto hher sein Ressourcenbedarf, besonders dann, wenn kein Ausfhrungsplan zur Verfgung steht.)

346

Workshop

Sie wissen, dass sich die Abfrage knftig ndern knnte, da sich das Datenmodell ndert. die Abfrage hufig ausgefhrt wird, also hufiger als ein- oder zweimal pro Anwendungsnutzung. Sie mit einer umfangreichen Datenmenge hantieren mssen. F Kann ich die Daten in einem SqlDataReader binden? A Leider nicht. Weil der SqlDataReader nur jeweils einen Datensatz aus einer Datenquelle holen kann, knnen Sie ihn nicht binden. Aus diesem Grund sollten Sie beim DataSet bleiben, wenn Sie den Benutzer Daten bearbeiten lassen wollen.

9.10 Workshop
Dieser Workshop soll Ihnen helfen, sich besser an die heute behandelten Begriffe zu erinnern. Es hilft Ihnen sehr, die Antworten vollstndig zu verstehen, bevor Sie weiterlesen. Die Antworten zu den Quizfragen und den bungen finden Sie in Anhang A.

Quiz
1. Schreiben Sie ein SELECT-Statement, das aus der tblUsers-Tabelle nur solche Datenstze holt, in denen der Wert fr das Feld UserID zwischen 5 und 10 liegt. 2. Welche Parameter fordert das SqlCommandBuilder-Objekt fr seinen Konstruktor? 3. Ein SqlCommandBuilder erzeugt SQL-Befehle nur dann automatisch, wenn ein Primrschlssel existiert. Warum? 4. Wahr oder falsch? Meist knnen Sie die Vorsilbe Sql durch OleDb ersetzen, um die Objekte im OLE DB-Provider nutzen zu knnen. 5. Ist es ausreichend, die DataSource-Eigenschaft eines DataGrid-Steuerelements auf ein geflltes DataSet zu setzen? 6. Schreiben Sie fr ein gegebenes SqlCommand-Objekt namens objCmd ein Statement, das einen Parameter namens @Geburtsdatum mit einem Wert von 1/7/01 hinzufgt. 7. Nennen Sie die Methode, die veranlasst, dass nderungen sofort in ein DataSet geschrieben werden.

347

ADO.NET einsetzen

bung
Erstellen Sie eine Anwendung, mit der der Benutzer SQL-Statements in ein Textfeld eingeben kann. Sollte das Statement Ergebnisse liefern, legen Sie sie in einem DataSet ab und gestatten Sie dessen Bearbeitung. Kmmern Sie sich nicht darum, ob die Abfrage im richtigen Format eingegeben wird; Fehlerprfungen werden erst an Tag 21 durchgenommen.

348

MDI-Anwendungen erstellen

0 1

MDI-Anwendungen erstellen

Bis jetzt haben Sie lediglich Anwendungen mit einer Einzeldokumentschnittstelle (Single Document Interface, SDI) erstellt: Sie verfgen nur ber eine Schnittstelle, mit der der Benutzer zusammenarbeitet. Bei einer Mehrfachdokumentschnittstelle (Multiple Document Interface, MDI) kann der Benutzer mit mehreren Ausgaben der gleichen Schnittstelle gleichzeitig arbeiten, so dass sich die Produktivitt erhht. Heutzutage sind viele Anwendungen MDI-Applikationen. In der heutigen Lektion lernen Sie, wie Sie MDI-Anwendungen erstellen. Die Grundlagen zu erwerben ist einfach, doch die Handhabung all dieser Schnittstellen zugleich erfordert ein wenig Feingefhl. Heute lernen Sie,

was MDI ist und wie Ihnen dieses Konzept hilft, wie Sie eine Excel-hnliche Benutzeroberflche erstellen, wie man gleichzeitig Mens und Ereignisse in mehreren Dokumenten verarbeitet, wie Sie Ihre MDI-Dokumente miteinander kommunizieren lassen.

10.1 Einfhrung in die Mehrfachdokumentschnittstelle


Was eine MDI-Anwendung bedeutet und darstellt, lernen Sie am besten am praktischen Beispiel. Abbildung 10.1 zeigt die MDI-Merkmale von Microsoft Excel in Aktion und Abbildung 10.2 zeigt Adobe Framemaker. Wre Excel eine SDI-Anwendung, knnte man nur mit einem Dokument auf einmal arbeiten und nicht drei Dokumente gleichzeitig geffnet halten, wie in Abbildung 10.1 zu sehen. Jede Schnittstelle oder jedes Dokument ist selbststndig und unabhngig von den anderen. Was man in der einen tut, betrifft die anderen nicht. Jedes Dokument kann seine jeweils eigene Menge an Daten, Objekten, Parametern usw. verwalten. Gegenber SDI-Anwendungen bietet eine MDI-Anwendung mehrere Vorteile. Der erste ist offensichtlich: ein Zuwachs an Produktivitt fr den Benutzer. Theoretisch knnte er in zwei offenen Fenstern mehr Arbeit erledigen als in nur einem; das ist schon die ganze Idee, die sich hinter einem Multitasking-Betriebssystem verbirgt. Wre es produktiver, nur eine Anwendung oder ein Dokument auf einmal zu benutzen, htte sich das Windows-Betriebssystem wohl niemals so erfolgreich entwickelt. MDI-Anwendungen sind ntzlich fr Datenanalyseprogramme wie etwa Excel. Ist mehr als ein Dokument geffnet und folglich auch mehr als eine Datenmenge zu bearbeiten, kann der Benutzer Daten leichter analysieren und vergleichen.

350

Einfhrung in die Mehrfachdokumentschnittstelle

Abbildung 10.1: Mit einer MDIAnwendung knnen Sie mehrere Schnittstellen auf einmal nutzen.

Abbildung 10.2: Adobe Framemaker stellt einen anderen Typ von MDI bereit.

Mit MDI-Anwendungen knnen Sie einem Benutzer ein Mehr an Informationen zur Verfgung stellen. Denken Sie z.B. an Visual Studio. Es verfgt ber mehrere Fenster, um verschiedenartige Informationen anzuzeigen: Eines zeigt die Eigenschaften des aktuellen Objekts, eines die Hierarchie der Windows Forms-Steuerelemente in einem Formular und

351

MDI-Anwendungen erstellen

ein drittes den Formularcode. Werden in mehreren Fenstern unterschiedliche Facetten einer Anwendung angeboten, kann der Benutzer mehr Arbeit mit weniger Aufwand bewltigen. Ein Einzeldokument in einer MDI-Anwendung lsst sich maximal vergrern, um alle anderen Dokumente zu verdecken. Auf diese Weise verhlt sie sich fast wie eine SDI-Applikation; sogar die Mens verschmelzen, um ein einzelnes, geffnetes Fenster zu reflektieren. Durch diese Flexibilitt kann der Benutzer die Art der Umgebung whlen, in der er gerne arbeitet. Doch MDI-Anwendungen haben auch ihre Nachteile. Als bedeutendster stellt sich oft ihr Hunger auf Systemressourcen heraus. Je mehr Dokumente man auf einmal geffnet hat, desto mehr Rechenkapazitt wird bentigt, was sich wiederum nachteilig auf die entsprechende Anwendung auswirkt, wenn nicht sogar auf Windows selbst. Dies bekommt eine erhhte Bedeutung in Anwendungen wie Adobe Photoshop, in denen jedes Dokument groe Grafiken beinhalten kann, manche bis zu mehreren Megabyte gro. Manche Anwendungen eignen sich nicht fr ein Dasein als MDI. Die weiter vorn in diesem Buch entwickelte CD-Katalog-Anwendung ist ein gutes Beispiel dafr. Es gibt einfach keinen Grund, warum sie MDI-fhig sein sollte. Schlielich arbeiten Sie darin mit nur einer CD-Sammlung; MDI knnte in diesem Fall sogar zu einer gewissen Verwirrung des Benutzers beitragen. MDI ist sowohl fr den Benutzer als auch den Entwickler komplizierter, was eigentlich gar nicht sein muss.

MDI-Mens
Ein interessantes Merkmal von MDI-Anwendungen ist ihr Umgang mit Mens. Herkmmliche Mens sind ja meist statisch: Die verfgbaren Mens und Optionen ndern sich nicht (obwohl Microsoft Word 2000 Sie vom Gegenteil zu berzeugen versucht). Ganz gleich, was Sie tun, so haben Sie doch stets die gleichen Auswahlmglichkeiten. Doch im Fall von MDI kann jedes Fenster seinen eigenen Satz an Mens haben. Dieses Feature erlaubt Ihnen, bestimmte Ttigkeiten nur im aktuellen Dokument auszufhren. Je nachdem, was Sie in der Anwendung tun, knnen sich die Mens ndern. Auch die Anwendung in ihrer Gesamtheit kann ihre eigenen Mens haben, die alle Dokumente betreffen. Sie haben wahrscheinlich in den meisten MDI-Anwendungen ein FENSTER-Men bemerkt. Es bietet Mglichkeiten, wie man die geffneten Fenster organisieren kann, etwa durch Nebeneinanderlegen oder bereinanderstapeln. Werden die verschiedenen Dokumente ausgewhlt, so mag es vorkommen, dass Menelemente miteinander verschmelzen, damit sie nicht mehr als einmal angezeigt werden. Dieses Verhalten lsst sich vollstndig steuern.

352

Eine MDI-Anwendung erstellen

10.2 Eine MDI-Anwendung erstellen


Um eine MDI-Anwendung auf die Beine zu stellen, sind mindestens zwei Dinge ntig: Erstens braucht man ein Haupt- oder bergeordnetes (Parent-) Fenster, um alle anderen offenen Dokumente zu beherbergen. Dieses bergeordnete Fenster sollte nur sehr wenige Benutzeroberflchenelemente aufweisen, denn die meisten davon sind in den anderen Dokumenten enthalten. Das bergeordnete Fenster ist eine Klasse, die von der Klasse System.Windows.Forms.Form erbt (genau wie alle anderen Windows Forms-Anwendungen). Als Zweites brauchen Sie eine Klasse, die die untergeordneten (Child-) Dokumente definiert. Sie erbt vom gleichen Namensraum wie das bergeordnete Fenster, doch sie sollte alle fr die Anwendung ntigen Oberflchenelemente umfassen. Ebenso wie bei benutzerdefinierten Dialogfeldern (siehe Tag 7) brauchen Sie auch hier keine Main-Methode im untergeordneten Dokument. Wir wollen uns ein einfaches Beispiel fr Parent-Child-Formulare ansehen. Als Erstes zeigt Listing 10.1 die Klasse des untergeordneten Dokuments. Listing 10.1: Die Klasse des untergeordneten MDI-Dokuments
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: using System; using System.Windows.Forms; using System.Drawing; namespace TYWinforms.Day10 { public class Document : Form { private Button btClick = new Button(); public Document() { btClick.Text = "Close Me!"; btClick.Location = new Point(100,100); btClick.Click += new EventHandler(this.Close); this.Controls.Add(btClick); } private void Close(Object Sender, EventArgs e) { this.Close(); } } }

Die Document-Klasse in Zeile 10 weist keine Besonderheiten auf; sie sieht genau wie jede andere Windows-Anwendung aus, nur dass sie keine Main-Methode besitzt. Beachten Sie das Steuerelement im Formular (Zeile 7), Button, das das Formular auf einen Klick hin

353

MDI-Anwendungen erstellen

schliet (Zeile 18). Beachten Sie auch den Namen der Klasse: Document. Diese Information wird im bergeordneten MDI-Fenster wichtig, das in Listing 10.2 zu sehen ist. Listing 10.2: Das bergeordnete MDI-Fenster
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: using System; using System.Windows.Forms; using System.Drawing; namespace TYWinforms.Day10 { public class Listing102 : Form { public Listing102() { this.IsMdiContainer = true; Document doc = new Document(); doc.MdiParent = this; } public static void Main() { Application.Run(new Listing102()); } } }

Die Klasse Listing102 in Listing 10.2 ist hinsichtlich der Anzahl der Codezeilen sogar noch einfacher. Drei Zeilen betreffen den MDI-Aspekt: 8, 10 und 11. Zeile 8 verwendet die Eigenschaft IsMdiContainer, um anzugeben, dass dieses Formular andere, nmlich untergeordnete Dokumente aufzunehmen vermag. Diese Eigenschaft ist vom Objekt System.Windows.Forms.Form geerbt, so dass Sie nun jede Windows Forms-Anwendung MDI-fhig machen knnen, indem Sie einfach diese Eigenschaft auf true setzen. Als Nchstes erzeugen Sie eine neue Instanz der in Listing 10.1 erstellten Document-Klasse. In Zeile 11 teilen Sie diesem neuen Objekt mit, wem es sich sozusagen unterzuordnen hat. Sie setzen die MdiParent-Eigenschaft auf das aktuelle Formular, welches durch this angegeben wird (Me in VB .NET). Auch diese Eigenschaft ist geerbt, so dass auch jede Windows Forms-Anwendung den untergeordneten Part in einer MDI-Anwendung einnehmen kann. Sobald Sie diese Anwendung kompiliert und ausgefhrt haben, sehen Sie das Formular wie in Abbildung 10.3 . Sie knnen feststellen, dass Sie nun ein bergeordnetes Formular haben, welches einfach den Behlter fr das untergeordnete Dokument darstellt. Sie knnen sodann das untergeordnete Dokument auf jede gewnschte Weise bearbeiten es maximieren oder minimie-

354

Eine MDI-Anwendung erstellen

ren usw. Wenn Sie die in Listing 10.1 angelegte Schaltflche anklicken, wird das untergeordnete Dokument geschlossen und das leere bergeordnete Formular bleibt zurck.

Abbildung 10.3: Ihre erste MDI-Anwendung sollte ungefhr so aussehen.

Sie knnen die Zeilen 10 und 11 wiederholt verwenden, um neue untergeordnete Dokumente zu erzeugen. Hufig bringt man diesen Code in einem Ereignishandler fr Menelemente unter, damit der Benutzer steuern kann, wann er neue Dokumente anlegt. Um dies zu erreichen, modifizieren Sie den Konstruktor von Listing 10.1 entsprechend dem folgenden Codefragment:
public Document(String strName) { this.Text = strName; ...

Dieser neue Konstruktor bernimmt eine Zeichenfolge, die der Titelzeile des Formulars zugewiesen ist. In Listing 10.2 fgen Sie dann eine Variable hinzu, die die Anzahl der Dokumente und Menobjekte verfolgt. ndern Sie Listing 10.2 wie folgt ab:
public class Listing102 : Form { private int intCounter = 0; private MainMenu mnuMain = new MainMenu(); public Listing102() { MenuItem miFile = mnuMain.MenuItems.Add("&Datei"); MenuItem miNew = miFile.MenuItems.Add("&Neu"); miNew.Click += new EventHandler(this.HandleMenu); this.Menu = mnuMain; ...

Die intCounter-Variable wird jedes Mal erhht, wenn der Benutzer ein neues Dokument ffnet; mit Hilfe dieser Zahl lsst sich jedes neue untergeordnete Dokument leicht referen-

355

MDI-Anwendungen erstellen

zieren. Das DATEI-Men ist ein Standardmen mit einem NEU-Menelement, das veranlasst, dass ein neues Dokument angelegt wird. Im nchsten Schritt verschieben Sie die Zeilen 10 und 11 aus Listing 10.2 in eine neue Methode, nmlich den Ereignishandler fr das NEU-Menelement, wie im folgenden Codeabschnitt zu sehen:
public void handleMenu(Object Sender, EventArgs e) { intCounter++; Document doc = new Document("Dokument " + intCounter.ToString()); doc.mdiParent = this; doc.Show(); }

Dieser Code nutzt den modifizierten Konstruktor der zuvor erstellten Document-Klasse. Er bergibt der neuen Klasse eine Zeichenfolge, die ihre Beschriftung wird. Vergessen Sie nicht die Show-Methode aufzurufen, um das neue untergeordnete Dokument anzuzeigen. Dieser Schritt war nicht notwendig, als sich dieser Code noch im Klassenkonstruktor befand, doch das hat sich nun gendert. Nun kompilieren Sie diese Anwendung neu und probieren das NEU-Menelement aus. Abbildung 10.4 zeigt das Ergebnis, das mehrere untergeordnete Dokumente enthlt.

Abbildung 10.4: Der Benutzer kann steuern, wie viele Fenster geffnet werden.

356

Eine MDI-Anwendung erstellen

Theoretisch knnte man unbegrenzt viele Fenster ffnen. Wollten Sie diese Anzahl begrenzen, brauchen Sie einfach nur die intCounter-Variable zu prfen, bevor ein neues Dokument angelegt wird. Liegt die Zahlenangabe ber Ihrem Schwellenwert, erzeugen Sie kein neues Document-Objekt. Beachten Sie auch, dass jedes neue Dokument eine benutzerfreundliche Titelzeile aufweist: Dokument 1, Dokument 2 usw. Wenn Sie eines der untergeordneten Fenster maximieren, verschwindet diese Titelzeile jedoch. Um diesen Makel zu beheben, legen Sie eine berschrift fr das bergeordnete MDI-Formular fest:
this.Text = "Listing 10.2";

Wenn Sie nun die Fenster maximieren, ndert sich die Titelzeile von Listing 10.2 auf eine Weise, dass sie das passende untergeordnete Fenster widerspiegelt: Listing 10.2 Dokument 1, Listing 10.2 Dokument 2 usw. .NET handhabt einen groen Teil der Probleme, wenn man mit mehreren untergeordneten Fenstern umgeht, das ist aber noch nicht alles. Auch Mens werden fr Sie automatisch gehandhabt. Wenn ein untergeordnetes Dokument ber ein verknpftes Men verfgt, ndert sich die Menleiste des bergeordneten Formulars so, dass sie das aktuelle bzw. aktive Dokument widerspiegelt. Wir wollen ein einfaches Men zur Document-Klasse hinzufgen, damit der Benutzer die Hintergrundfarbe des Formulars ndern kann; fr diese Anwendung verwenden wir das gngige ColorDialogDialogfeld. Als Erstes fgen Sie das Men ein wie im folgenden Code zu sehen:
private MainMenu mnuMain = new MainMenu(); public Document(String strName){ MenuItem miFormat = mnuMain.MenuItems.Add("Format " + strName); MenuItem miColor = miFormat.MenuItems.Add("&Farbe..."); miColor.Click += new EventHandler(this.HandleMenu); this.Menu = mnuMain; ...

Beachten Sie, dass der Menname so festgelegt wurde, dass er den Namen des untergeordneten Dokuments reflektiert. Diese Namenskonvention wird Ihnen beim berblick ber das aktive Fenster helfen. Als Nchstes fgen Sie den Ereignishandler fr das Men hinzu:
private void HandleMenu(Object Sender,EventArgs e) { ColorDialog dlgColor = new ColorDialog(); if (dlgColor.ShowDialog()== DialogResult.OK) { this.BackColor = dlgColor.Color; } }

Schlagen Sie bei Bedarf noch einmal Tag 7 nach, um Ihr Gedchtnis aufzufrischen, was Standarddialogsteuerelemente betrifft. Im Wesentlichen zeigt die Methode das ColorDialog-Steuerelement an, und wenn der Benutzer eine Farbe auswhlt und auf die OK-Schaltflche klickt, ndert sich die Hintergrundfarbe des jeweiligen untergeordneten Formulars.

357

MDI-Anwendungen erstellen

Wenn Sie jetzt die Anwendung ausfhren, bemerken Sie als Erstes, dass das dem untergeordneten Dokument hinzugefgte Men in der Menleiste des bergeordneten Formulars angezeigt wird. Dass das Men hier platziert wird, hilft, ein Durcheinander zu vermeiden. Beachten Sie auerdem, dass sich die Menbeschriftung ndert, wenn man auf unterschiedliche untergeordnete Dokumente klickt dies spiegelt das aktive untergeordnete Dokument wider. Sobald alle untergeordneten Fenster geschlossen wurden, verschwindet das neue FORMAT-Men. Wir wollen noch ein weiteres verbreitetes Merkmal einer MDI-Anwendung betrachten: die Fensteranordnung. Fast jede MDI-Anwendung weist standardmig ein FENSTER-Men auf, mit dem der Benutzer die untergeordneten Fenster innerhalb des bergeordneten Fensters bersichtlicher anordnen kann. Diese Fenster enthalten in der Regel eine Liste aller offenen untergeordneten Fenster, so dass man leicht jenes auswhlen kann, zu dem man wechseln mchte das erspart das Durchsuchen aller Fenster, bis man auf das gewnschte stt. Wir wollen die verschiedenen Mglichkeiten betrachten, wie man Fenster anordnen kann:

BERLAPPEND: Fenster werden bei dieser Einstellung bereinander gestapelt, mit nur gerade mal so viel Verschiebung, dass noch die Titelzeile des darunter liegenden Fensters zu sehen ist. NEBENEINANDER: Fenster werden so angeordnet, dass sie sich nicht berschneiden. Jedes Fenster ist genau so hoch wie das bergeordnete Formular, doch die Fenster teilen sich die Breite des Hauptformulars gleichmig untereinander auf. UNTEREINANDER: Der gleiche Fall, nur dass diesmal die Aufteilung senkrecht erfolgt, die Breite aber jeweils der des bergeordneten Formulars entspricht.

Diese Layouts sind in der Aufzhlung MdiLayout definiert: MdiLayout.Cascade, MdiLayout.TileHorizontal und MdiLayout.TileVertical. Die LayoutMdi-Methode des FormObjekts kann diese Werte dazu verwenden, Fenster entsprechend anzuordnen. Wir wollen die Anwendung noch einmal modifizieren, um diese Layouts zu nutzen. Listing 10.3 zeigt die neue Version des Listings 10.2 und enthlt alle bislang gemachten nderungen. Listing 10.3: Das vollendete bergeordnete MDI-Formular
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: using System; using System.Windows.Forms; using System.Drawing; namespace TYWinforms.Day10 { public class Listing102 : Form { private int intCounter = 0; private MainMenu mnuMain = new MainMenu(); public Listing102() {

358

Eine MDI-Anwendung erstellen

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: 53: 54: 55:

MenuItem miFile = mnuMain.MenuItems.Add("&Datei"); MenuItem miNew = miFile.MenuItems.Add("&Neu"); MenuItem miWindow = mnuMain.MenuItems.Add("&Fenster"); miWindow.MenuItems.Add("berlappend", new EventHandler(this.ArrangeWindows)); miWindow.MenuItems.Add("Untereinander", new EventHandler(this.ArrangeWindows)); miWindow.MenuItems.Add("Nebeneinander", new EventHandler(this.ArrangeWindows)); miNew.Click += new EventHandler(this.HandleMenu); miWindow.MdiList = true; this.IsMdiContainer = true; this.Menu = mnuMain; this.Size = new Size(800,600); this.Text = "Listing 10.2"; } private void ArrangeWindows(Object Sender, EventArgs e) { MenuItem miTemp = (MenuItem)Sender; switch (miTemp.Text) { case "berlappend": this.LayoutMdi(MdiLayout.Cascade); break; case "Untereinander": this.LayoutMdi(MdiLayout.TileHorizontal); break; case "Nebeneinander": this.LayoutMdi(MdiLayout.TileVertical); break; } } private void HandleMenu(Object Sender, EventArgs e) { intCounter++; Document doc = new Document("Dokument " + intCounter.ToString()); doc.MdiParent = this; doc.Show(); } public static void Main() { Application.Run(new Listing102()); } } }

359

MDI-Anwendungen erstellen

Der neue Code fr das FENSTER-Men findet sich in den Zeilen 13 bis 16. Sie erzeugen zunchst das FENSTER-Men und dann drei Untermens fr jede der drei Fensteranordnungen. Beachten Sie, dass sie alle auf denselben Ereignishandler namens ArrangeWindows zeigen. In Zeile 19 setzen Sie die MdiList-Eigenschaft des neuen FENSTER-Mens auf true, um Ihrer Anwendung mitzuteilen, dass dieses Men fr die Steuerung von untergeordneten MDI-Fenstern vorgesehen ist. Im Wesentlichen wird dieses Men Menelemente automatisch hinzufgen und entfernen, die die gerade geffneten untergeordneten Dokumente widerspiegeln. Werden diese dynamischen Menelemente angeklickt, erhlt das passende untergeordnete Fenster den Fokus, um Benutzereingaben zuzulassen. Springen wir zur ArrangeWindows-Methode in Zeile 27. Sie wollen feststellen, welches Menelement fr die Fensteranordnung angeklickt worden ist und die untergeordneten Fenster entsprechend arrangieren. Zuerst konvertieren Sie die Sender-Variable (welche das Objekt darstellt, das das Ereignis auslste) in ein MenuItem, damit Sie die ntigen Eigenschaften und Methoden verwenden knnen. Das switch-Statement wertet die Beschriftung des angeklickten MenuItems aus. Der entsprechende case-Zweig ruft die LayoutMdi-Funktion auf und bergibt ihr einen der Werte der MdiLayout-Aufzhlung.

Abbildung 10.5: Das Nebeneinanderlegen von Fenstern ist nur eine Ihrer LayoutOptionen.

360

Eine MDI-Anwendung erstellen

Nun kompilieren Sie diese Anwendung und fhren sie aus. Sie werden bemerken, dass sich das FENSTER-Men beim Hinzufgen neuer Dokumente dynamisch ndert, um die neuen Fenster zu reflektieren. Klicken Sie auf die verschiedenen Layout-Optionen, um sie zu testen. Abbildung 10.5 zeigt ein Beispiel.

Verschiedene Arten von untergeordneten Fenstern erstellen


Bis jetzt haben Sie MDI-Anwendungen erstellt, die mehrere Instanzen desselben untergeordneten Dokuments aufweisen. Es ist jedoch nicht zwingend, dass Sie Ihre Anwendungen in dieser Weise erstellen: Jedes untergeordnete Dokument kann von einem vllig verschiedenen Objekttyp sein, solange nur alle von der Klasse System.Windows.Forms.Form erben. Wir wollen einmal ein brauchbares Beispiel erstellen. Viele Anwendungen (wie etwa Visual Studio) verfgen ber Symbolleisten und Eigenschaftenfenster, die man auf dem Bildschirm bewegen kann. Diese Fenster werden zustzlich zur zentralen Benutzeroberflche bereitgestellt, welche meist fr die Dateneingabe vorgesehen ist, und bieten weitere hilfreiche Funktionen an, die sonst nicht direkt zugnglich sind. Bedenken Sie jedoch, dass sich fr das, was Sie mit einem untergeordneten MDI-Dokument vorhaben, mglicherweise ein benutzerdefiniertes nichtmodales Dialogfeld besser eignet. Jede Vorgehensweise hat ihre Vor- und Nachteile, daher sollten Sie Ihre Wahlmglichkeiten genau gegeneinander abwgen, bevor Sie sich fr die vielschichtige Welt von MDI entscheiden.
Ich empfehle Verwenden Sie MDI und untergeordnete Dokumente, wenn Sie den Desktop des Benutzers nicht berladen mchten. und der Benutzer leicht einen Bildlauf durchfhren kann, um verborgene Bereiche von untergeordneten Dokumenten einzusehen (wie zum Beispiel in Microsoft Excel). Bitte beachten Sie Setzen Sie MDI nicht ein, wenn Sie ein Fenster brauchen, das mehrere untergeordnete Dokumente auf einmal beeinflussen kann. Ein Beispiel dafr wre ein Fenster mit Schaltflchen, die automatisch Text im aktiven untergeordneten Fenster einfgen knnen. Wrden Sie in diesem Fall ein untergeordnetes MDI-Dokument verwenden, knnten Sie den Text nicht im aktiven untergeordneten Fenster einfgen, weil dieses ja das Fenster mit den Schaltflchen wre. Sie wrden Ihre Referenz auf das beabsichtigte Ziel dieser Operation verlieren.

361

MDI-Anwendungen erstellen

10.3 Die Besonderheiten von MDI-Formularen steuern


Es bereitet keine besondere Mhe, eine MDI-Anwendung zu erstellen. Um das absolute Minimum an Funktionsumfang zu erzeugen, muss man nur ein paar Codezeilen einfgen (vorausgesetzt, man hat bereits seine untergeordnete Dokumentklasse). Unter der Oberflche erfordert das Steuern von MDI-Anwendungen jedoch wesentlich mehr.

Mens
Wenn sowohl den ber- als auch untergeordneten Dokumenten Mens zugeordnet sind, ndert sich die Menleiste des bergeordneten Dokuments, wenn das jeweilige untergeordnete Dokument aktiv ist. Dies wird als Zusammenfhren von Mens (Menu Merging) bezeichnet; die Mens des untergeordneten und bergeordneten Fensters verschmelzen zu einer Menleiste. Sind die unterschiedlichen Mens eindeutig, erfolgt die Verschmelzung anstandslos, wie Sie heute bereits bemerken konnten. Der Vorgang wird jedoch etwas interessanter, sobald man auf doppelte Menelemente stt. Lassen Sie uns sehen, was in einer solchen Lage passiert. Fgen Sie dem Konstruktor der Document-Klasse in Listing 10.1 folgenden Code hinzu:
MenuItem miFile = mnuMain.MenuItems.Add("Datei");

Abbildung 10.6: Die Anwendung verfgt jetzt ber zwei DATEI-Mens.

362

Die Besonderheiten von MDI-Formularen steuern

Nun verfgen sowohl das bergeordnete als auch das untergeordnete Formular ber ein DATEI-Men. Kompilieren Sie diese Anwendung und fhren Sie sie aus. Wenn Sie ein neues untergeordnetes Dokument ffnen, erhalten Sie die Fenster, die Sie in Abbildung 10.6 sehen. Die Mens des bergeordneten und des untergeordneten Fensters sind jetzt miteinander verschmolzen und Sie haben zwei DATEI-Mens! Sicherlich ist das nicht gerade das, was Sie wollen. Irgendwie mssen Sie die Anwendung wissen lassen, dass die zwei DateiMens eigentlich dasselbe Men sind und dass ihre Inhalte kombiniert werden sollen. Zum Glck kann Ihnen die MenuMerge-Aufzhlung helfen. Tabelle 10.1 beschreibt die Werte dieser Aufzhlung.
Wert
Add

Beschreibung Das Standardverhalten. Ein untergeordnetes Menelement wird der bergeordneten Menleiste hinzugefgt, ganz gleich, ob bereits eines existiert (dies fhrt zum Problem des doppelten DATEI-Mens). Alle Untermens dieses Menelements werden mit denen des bergeordneten Mens, die den gleichen Platz im Men innehaben, zusammengefhrt. Dieses MenuItem wird beim Zusammenfhren nicht verwendet; es wird vielmehr weggelassen und nicht in der kombinierten Menleiste auftauchen. Dieses MenuItem wird jedes vorhandene MenuItem, das den gleichen Platz in dem Men innehat, ersetzen. Das untergeordnete Menelement hat stets Vorrang vor dem des bergeordneten. Sie verwenden diesen Wert, wenn Sie die Ereignishandler fr ein bestimmtes Menelement berschreiben wollen.

MergeItems

Remove

Replace

Tabelle 10.1: Die Werte der MenuMerge-Aufzhlung

Damit alle Werte (auer Remove) funktionieren, muss allen MenuItems ein MenuMerge-Wert zugewiesen werden d.h. sowohl im bergeordneten als auch im untergeordneten Formular. Um den Wert zuzuweisen, fgen Sie in die Listings 10.1 und 10.2 folgende Codezeile ein:
miFile .MergeType = MenuMerge.MergeItems;

Wenn Sie nun die Anwendung ausfhren und ein untergeordnetes Dokument anlegen, erscheint nur ein DATEI-Men. Versuchen Sie nun dem Listing 10.1 folgende Zeile hinzuzufgen; sie fgt einfach ein Untermen in das DATEI-Men des untergeordneten Dokuments ein:
MenuItem miNew = miFile.MenuItems.Add("Neu");

363

MDI-Anwendungen erstellen

Fhren Sie die Anwendung erneut aus. Was passiert, wenn Sie ein untergeordnetes Dokument anlegen? Die DATEI-Mens verschmelzen, aber nun haben Sie zwei NEU-Untermens. Um dieses Problem zu beheben, knnen Sie entweder in beiden Listings MergeType = MenuMerge.Replace festlegen (was dazu fhren wrde, dass das Menelement des untergeordneten Dokuments das des bergeordneten Formulars ersetzen wrde) oder Sie geben in beiden Listings MergeType = MenuMerge.Remove an. Das brig bleibende Menelement entspricht der entgegengesetzten Klasse, in der Sie MenuMerge.Remove angeben. Wenn Sie z.B. Remove in der bergeordneten Klasse angeben, dann ist das untergeordnete Menelement dasjenige, das in der Menleiste angezeigt wird; nur seine Funktion wird ausgefhrt, sobald man es anklickt. Beachten Sie, dass das ganze Thema der Zusammenfhrung nur Elemente betrifft, die im Men den gleichen Platz innehaben. Angenommen, Sie haben ein Men des bergeordneten Dokuments mit folgenden Elementen (von oben nach unten): DATEI, NEU, SPEICHERN, SCHLIEEN. Ein korrespondierendes untergeordnetes Dokumentenmen besitzt folgende Elemente (wiederum von oben nach unten): DATEI, NEU, SCHLIEEN. Die jeweiligen Mens DATEI und NEU sollten gem den soeben genannten MenuMerge-Regeln gehandhabt werden. Mit den Menelementen danach gibt es jedoch ein kleines Problem. Stellen Sie sich vor, Sie wollen das SCHLIEEN-Men des untergeordneten Dokuments das SCHLIEEN-Men des bergeordneten Dokuments berschreiben lassen. Daher setzen Sie MergeType = MenuMerge.Remove in der bergeordneten Klasse und MergeType = MenuMerge.Replace in der untergeordneten Klasse. Dieses Vorgehen sollte funktionieren, oder? Leider klappt das nicht ganz. Das SCHLIEEN-Men des bergeordneten Dokuments verschwindet zwar wie erwartet, doch das bergeordnete SPEICHERN-Men wird nun durch das untergeordnete SCHLIEEN-Men ersetzt, so dass das Mensystem durcheinander gert und der Benutzer ernsthaft verwirrt ist. Obwohl wir wissen, dass das SCHLIEEN-Men des bergeordneten Dokuments dem gleichnamigen Men des untergeordneten Dokuments entsprechen sollte, wei es die Anwendung nicht. Vielmehr kann sie nur auf der Grundlage der Position des Menelements im Men operieren. SCHLIEEN befindet sich im bergeordneten Men an Position 3 (die Zhlung beginnt bei Null) und im untergeordneten Men an Position 2. Die Wege dieser beiden Menelemente werden sich also nie kreuzen, weshalb sie auch nie verschmelzen knnen. Die Position 2 im bergeordneten Men wird nmlich vom SPEICHERN-Men eingenommen, so dass es vom MergeType des untergeordneten SCHLIEENMens betroffen wird. Doch zum Glck gibt es auch fr dieses Problem eine Lsung. Die MergeOrder-Eigenschaft von MenuItems lsst sich dazu benutzen, eine Reihenfolge zu diktieren und somit auch die Parent-Child-Menbeziehung, in der die Menelemente auftauchen. Im obigen Beispiel knnen Sie beide SCHLIEEN-Menelemente auf denselben MergeOrder-Wert setzen:

364

Die Besonderheiten von MDI-Formularen steuern

miClose.MergeOrder = 3

Nun wei die Anwendung nicht nur, dass diese beiden Menelemente einander entsprechen, sondern sie wird sie auch in der aufsteigenden Reihenfolge der MergeOrder-Werte anordnen. Mit anderen Worten: Ein Menelement mit MergeOrder gleich 0 wird an der Spitze des Mens platziert, das Element mit MergeOrder gleich 1 folgt unmittelbar darunter usw. Beachten Sie, dass die Menelemente nicht an die durch MergeOrder vorgeschriebene Position verschoben werden. Daher ist die Anfangsposition weiterhin durch die Reihenfolge diktiert, in der Sie MenuItems zum Men hinzufgen. Welchen MergeOrder-Wert Sie nun tatschlich setzen, ist eigentlich nicht wichtig; Sie knnen ohne weiteres die MergeOrder des SCHLIEEN-Menelements auf 1000 setzen. Windows Forms ist intelligent genug, um keine 999 leeren Menelemente vor das fragliche Menelement zu setzen (es sei denn, Sie befehlen, 999 leere Stellen einzufgen). Die MergeOrder ist nur eine willkrliche Zahl, die die Parent-Child-Beziehungen und die Anordnung von zusammengefhrten Elementen regelt. Elemente mit hherer MergeOrderZahl werden in einem Men stets tiefer einsortiert als Elemente mit einer niedrigeren MergeOrder. Daher mag es nicht in jedem Fall von Vorteil sein, die MergeOrder-Werte fortlaufend zu nummerieren, also 0 fr das erste Element, 1 fr das zweite und so weiter. Was, wenn Ihre untergeordnete Anwendung ein Menelement hat, das Sie zwischen zwei Menelementen im bergeordneten Men einschieben wollen? In diesem Fall wrde es helfen, zwischen den MergeOrder-Werten einen gewissen Abstand vorzusehen, so dass man spter noch expandieren kann. Abbildung 10.7 illustriert das geschilderte Konzept.

Untergeordnete Fenster
In vielen Fllen mssen Sie wissen, welches untergeordnete MDI-Fenster das aktive ist. Angenommen, Sie fhren eine Suche in einem RichTextBox-Steuerelement durch oder fgen Text aus einer anderen Anwendung ein. Die Eigenschaft ActiveMdiChild des bergeordneten Formulars liefert ein Form-Objekt, das dem untergeordneten Fenster entspricht. Wir wollen Ihrem bergeordneten Formular ein FARBE-Men hinzufgen, das ein ColorDialog-Dialogfeld anzeigt und die Farbe des aktiven untergeordneten Fensters ndert. Zu diesem Zweck fgen Sie dem Listing 10.2 folgenden Code hinzu:
MenuItem miFormat = mnuMain.MenuItems.Add("Farbe "); MenuItem miColor = miFormat.MenuItems.Add("&Farbe..."); miColor.Click += new EventHandler(this.ChangeColor);

365

MDI-Anwendungen erstellen

Elternmen Neu, MergeOrder = 0 ffnen, MergeOrder = 5 Drucken, MergeOrder = 10 Beenden, MergeOrder = 100

Kindmen Neu, MergeOrder = 0 Speichern, MergeOrder = 2 Speichern als, MergeOrder = 3 Rckgngig, MergeOrder = 9 Schlieen, MergeOrder = 11

Merged Menu Neu, MergeOrder = 0 Speichern, MergeOrder = 2 Speichern als, MergeOrder = 3 ffnen, MergeOrder = 5 Rckgngig, MergeOrder = 9 Drucken, MergeOrder = 10 Schlieen, MergeOrder = 11 Beenden, MergeOrder = 100

Abbildung 10.7: Die genauen MergeOrder-Werte sind unwesentlich; nur auf die relativen Werte kommt es an.

Nun fgen Sie den ChangeColor-Ereignishandler hinzu:


private void ChangeColor(Object Sender,EventArgs e){ ColorDialog dlgColor = new ColorDialog(); if (dlgColor.ShowDialog()== DialogResult.OK){ if (ActiveMdiChild != null){ ActiveMdiChild.BackColor = dlgColor.Color; } } }

Diese Methode erstellt zunchst ein Dialogfeld ColorDialog und zeigt es an, damit der Benutzer eine Farbe auswhlen kann. Bevor Sie fortfahren, mssen Sie prfen, ob der Benutzer in der Anwendung schon ein untergeordnetes Fenster erzeugt hat. Ist das nicht der Fall und versuchen Sie auf die Eigenschaft ActiveMdiChild zuzugreifen, erhalten Sie eine Fehlermeldung. Zum Schluss legen Sie die Eigenschaften des ActiveMdiChild-Formulars genau wie fr jedes andere Form-Objekt fest. Sie knnen sich auch benachrichtigen lassen, sobald ein untergeordnetes Fenster aktiviert wird oder vom Benutzer den Fokus erhlt. Dafr steht das Ereignis MdiChildActivate zur Verfgung. Fgen Sie den folgenden Code Ihrem bergeordneten Formular hinzu:
//auerhalb des Konstruktors private StatusBar sbarMain = new StatusBar(); //innerhalb des Konstruktors this.Controls.Add(sbarMain); this.MdiChildActivate += new EventHandler(this.UpdateStatus);

366

Die Besonderheiten von MDI-Formularen steuern

Als Nchstes fgen Sie die UpdateStatus-Methode ein:


private void UpdateStatus(Object Sender,EventArgs e) { sbarMain.Text = ActiveMdiChild.Text +": " + ActiveMdiChild.BackColor.ToString(); }

So einfach ist das! Abbildung 10.8 zeigt ein Beispiel.

Abbildung 10.8: Abbildung 10.8: Das Ereignis MdiChildActivate

kann Sie darber unterrichten, sobald ein untergeordnetes Fenster den Fokus erhlt.

Sollten Sie jemals auf ein untergeordnetes Dokument zugreifen mssen, bei dem es sich nicht um das aktive untergeordnete Dokument handelt, knnen Sie die MdiChildrenEigenschaft einsetzen, die ein Array aller untergeordneten Formulare liefert, seien sie nun aktiv oder nicht.

Auf untergeordnete Steuerelemente zugreifen


Der Zugriff auf untergeordnete Formulare ist also nicht besonders schwierig. Das ist jedoch beim Zugriff auf ihre Steuerelemente ganz anders. Da Sie schlielich praktisch alle Steuerelemente mit Hilfe des private-Modifizierers erstellt haben, drften sie ber das bergeordnete Formular nicht zugnglich sein, oder? Zum Glck lautet die Antwort nein. Die in einem untergeordneten Formular enthaltenen Steuerelemente knnen Sie ber die Controls-Eigenschaft des untergeordneten Dokuments erreichen. Betrachten Sie bitte den in einem bergeordneten Konstruktor enthaltenen Code:

367

MDI-Anwendungen erstellen

string StrText; Document doc = new Document("Neues Dokument"); doc.MdiParent = this; strText = doc.Controls[0].Text;

Dieser Befehl ruft die Text-Eigenschaft dessen ab, was auch immer das erste Steuerelement im untergeordneten Formular ist. Wir wollen ein realistischeres Beispiel erstellen. Listing 10.4 zeigt ein neues bergeordnetes Anwendungsformular, das ein neues untergeordnetes Formular erzeugt, zu dem wir gleich noch kommen. (Der Einfachheit halber wurden die standardmigen Windows-Mens und Ereignishandler in diesem Code weggelassen.) Listing 10.4: Ein eindringendes bergeordnetes Steuerelement
1: using System; 2: using System.Windows.Forms; 3: using System.Drawing; 4: 5: namespace TYWinforms.Day10 { 6: public class Listing104 : Form { 7: private int intCounter = 0; 8: private MainMenu mnuMain = new MainMenu(); 9: 10: public Listing104() { 11: MenuItem miFile = mnuMain.MenuItems.Add("&Datei"); 12: MenuItem miNew = miFile.MenuItems.Add("&Neu"); 13: MenuItem miOpenIn = miFile.MenuItems.Add("&ffnen in neuem  Fenster"); 14: miOpenIn.Enabled = false; 15: 16: miFile.Popup += new EventHandler (this.EnableOpenIn); 17: miNew.Click += new EventHandler(this.HandleMenu); 18: miOpenIn.Click += new EventHandler(this.OpenIn); 19: miWindow.MdiList = true; 20: 21: this.IsMdiContainer = true; 22: this.Menu = mnuMain; 23: this.Size = new Size(800,600); 24: this.Text = "Listing 10.3"; 25: } 26: 27: private void EnableOpenIn(Object Sender, EventArgs e) { 28: if (this.ActiveMdiChild != null) { 29: if (((TextBox)ActiveMdiChild.Controls[0]).SelectedText != "") { 30: mnuMain.MenuItems[0].MenuItems[1].Enabled = true;

368

Die Besonderheiten von MDI-Formularen steuern

31: } else { 32: mnuMain.MenuItems[0].MenuItems[1].Enabled = false; 33: } 34: } 35: } 36: 37: private void OpenIn(Object Sender, EventArgs e) { 38: if (ActiveMdiChild != null) { 39: string strText =  ((TextBox)ActiveMdiChild.Controls[0]).SelectedText; 40: 41: intCounter++; 42: Document2 doc = new Document2("Document " +  intCounter.ToString()); 43: doc.MdiParent = this; 44: doc.Controls[0].Text = strText; 45: doc.Show(); 46: } 47: } 48: 49: private void HandleMenu(Object Sender, EventArgs e) { 50: intCounter++; 51: 52: Document2 doc = new Document2("Dokument " + intCounter.ToString()); 53: doc.MdiParent = this; 54: doc.Show(); 55: } 56: 57: public static void Main() { 58: Application.Run(new Listing104()); 59: } 60: } 61: }

Listing 10.4 beginnt mit einem normalen bergeordneten MDI-Formular. Sie haben ein DATEI-Men, mit dem der Benutzer neue untergeordnete Dokumente anlegen kann. Sein Ereignishandler ist in Zeile 49 zu finden. Diese HandleMenu-Methode ist praktisch die gleiche wie in Listing 10.2, doch erzeugt sie diesmal die Instanz eines untergeordneten Dokuments Document2 statt nur Document. Sie werden diese neue Klasse gleich sehen; sie ist im Grunde sehr einfach. Wir haben auerdem in Zeile 13 ein neues Menelement namens IN NEUEM FENSTER FFNEN. Dieses Menelement bernimmt jeden Text, den der Benutzer in einem untergeordneten Fenster ausgewhlt hat, erzeugt ein neues untergeordnetes Dokument und legt nur den ausgewhlten Text im neuen

369

MDI-Anwendungen erstellen

Dokument ab. (Sie merken schon, dass die neue untergeordnete Klasse ber wenigstens eine TextBox-Steuerelement verfgt.) Ist jedoch kein untergeordnetes Dokument vorhanden, soll der Benutzer dieses Menelement nicht benutzen knnen. Um diesen Aspekt zu steuern, weisen Sie dem Popup-Ereignis des DATEI-Mens einen Delegaten zu (Zeile 16). Das Ereignis wird immer dann ausgelst, wenn der Benutzer das Men ffnet. Zeile 27 enthlt den Ereignishandler fr das Popup-Ereignis. Sie wollen das Menelement FFNEN IN nur dann einschalten, sofern a) ein untergeordnetes Dokument aktiv ist und b) dieses untergeordnete Dokument markierten Text enthlt. Das erste if-Statement in Zeile 28 prft, ob ein untergeordnetes Dokument aktiv ist. Das zweite ist ein wenig komplizierter. Denken Sie daran, dass Sie auf die Steuerelemente in einem untergeordneten Formular mit Hilfe der Controls-Eigenschaft zugreifen knnen. Zwar trifft dies zu, doch diese Eigenschaft liefert nur ein Control-Objekt, so dass Sie es in ein passendes Steuerelement konvertieren mssen. In diesem Fall erwarten Sie ja ein TextBox-Steuerelement. Das if-Statement in Zeile 29 holt das erste Steuerelement im aktiven untergeordneten Dokument, konvertiert es in den Typ TextBox und wertet dann die SelectedText-Eigenschaft daraufhin aus, ob ein Text markiert worden ist. Ist dies nicht der Fall, soll das Menelement abgeschaltet werden, wie in Zeile 32 angegeben. Ist jedoch wirklich etwas Text markiert, dann soll das Menelement FFNEN IN aktiviert werden. Dies ist der erste Teil des Zugriffs auf die Steuerelemente im untergeordneten Dokument. Der nchste Schritt besteht im Bereitstellen der Funktionalitt von FFNEN IN. Diese wird von der OpenIn-Methode in Zeile 37 gehandhabt. Wiederum prfen Sie in Zeile 38 zuerst, ob ein untergeordnetes Dokument aktiv ist. In der nchsten Zeile rufen Sie den markierten Text (SelectedText) aus dem ersten Steuerelement des untergeordneten Dokuments, wobei Sie wieder daran zu denken haben, das Objekt per Typumwandlung passend zu konvertieren. Dann erzeugen Sie ein neues untergeordnetes Dokument auf die gleiche Weise, wie wenn ein Benutzer das NEU-Menelement anklicken wrde: Sie inkrementieren den Zhler, erstellen ein neues Document2-Objekt, legen seine MdiParent-Eigenschaft auf das aktuelle Formular fest und rufen schlielich zwecks Anzeige die Show-Methode auf. In Zeile 44 ist ein gesonderter Arbeitsschritt zu sehen: Sie setzen die Text-Eigenschaft des ersten Steuerelements im neuen untergeordneten Dokument auf den Text, der aus dem letzten untergeordneten Dokument geholt wurde. Wir wollen zum Schluss noch einen Blick auf die neue Document2-Klasse in Listing 10.5 werfen.

370

Die Besonderheiten von MDI-Formularen steuern

Listing 10.5: Die Klasse Document2


1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: using System; using System.Windows.Forms; using System.Drawing; namespace TYWinforms.Day10 { public class Document2 : Form { private TextBox tbText = new TextBox(); public Document2(String strName) { tbText.Dock = DockStyle.Fill; tbText.Multiline = true; tbText.HideSelection = false; this.Text = strName; this.Controls.Add(tbText); } } }

Listing 10.5 zeigt eine einfache Klasse, die lediglich ein TextBox-Steuerelement enthlt. Die TextBox wird in Zeile 10 so eingestellt, dass sie das gesamte Formular ausfllt, so dass der Benutzer umfangreiche Textmengen eingeben kann.

Abbildung 10.9: Nachdem Sie etwas Text markiert und auf das Menelement FFNEN IN geklickt haben, erscheint Ihr ausgewhlter Text in dem neuen Dokumentfenster.

371

MDI-Anwendungen erstellen

Kompilieren Sie diese Anwendung und fhren Sie sie aus. Prfen Sie die Funktionsfhigkeit, indem Sie etwas Text markieren und auf das Menelement FFNEN IN klicken. Abbildung 10.9 zeigt, wie Ihre Anwendung aussehen sollte.

10.4 Zusammenfassung
MDI-Anwendungen nutzen sinnvoll das Multitasking-Konzept. Wenn mehr als ein Dokument auf einmal in einer Anwendung geffnet ist, knnte ein Benutzer theoretisch doppelt so viel Arbeit verrichten. Das Erstellen einer MDI-Anwendung erweist sich hufig als wirtschaftlicher, als den Benutzer zu zwingen, eine fragliche Anwendung zweimal zu starten, nur um ihn in zwei Dokumenten arbeiten lassen zu knnen. Um eine MDI-Anwendung erstellen zu knnen, braucht man eine Klasse fr das bergeordnete Formular und eine fr das untergeordnete Formular. Dabei kann es sich um beliebige Klassen handeln, solange sie vom Form-Objekt erben. Beliebige Windows Forms knnen entweder bergeordnete oder untergeordnete MDI-Formulare sein; hier gibt es keine Einschrnkung. Die meisten MDI-Anwendungen verfgen ber ein FENSTER-Men, mit dem der Benutzer die untergeordneten Fenster in einer bersichtlicheren Weise anordnen kann. Ein Men knnen Sie leicht wie folgt erstellen: Erzeugen Sie einfach die ntigen Menelemente und rufen Sie die LayoutMdi-Methode mit dem passenden Wert der MdiLayout-Aufzhlung auf. Sie knnen zudem die MdiList-Eigenschaft dieses Mens auf true setzen, um automatisch die untergeordneten Dokumente als Menelemente auflisten zu lassen, so dass der Benutzer damit leichter ein Dokument zur Arbeit auswhlen kann. Mens bilden in MDI-Anwendungen ein recht interessantes Thema. Sowohl bergeordnete als auch untergeordnete Dokumente knnen ber verknpfte Mens verfgen, doch die Probleme beginnen, wenn die beiden miteinander verschmelzen mssen. Doch die Eigenschaften MergeType und MergeOrder lsen diese Probleme, weil Sie damit die Anwendung instruieren knnen, welche Mens auf welche Weise angezeigt werden sollen. Um das aktuelle bzw. aktive untergeordnete Fenster in einer MDI-Anwendung zu entdecken, rufen Sie die ActiveMdiChild-Eigenschaft des bergeordneten Formulars auf. Das MdiChildActivate-Ereignis wird ausgelst, wenn ein untergeordnetes Fenster aktiviert wird. Sie knnen auf die in den untergeordneten Dokumenten enthaltenen Steuerelemente zugreifen, indem Sie die Controls-Eigenschaft des untergeordneten Objekts verwenden; denken Sie jedenfalls daran, den zurckgelieferten Wert bei Bedarf zu konvertieren.

372

Fragen und Antworten

10.5 Fragen und Antworten


F Wenn ich eine bestimmte Anzahl von Fenstern geffnet habe (etwa vier oder fnf), dann ordnen die Befehle zum Neben- und Untereinanderlegen die Fenster auf gleiche Weise an. Was ist der Grund dafr? A Die Anwendung versucht, die Fenster so anzuordnen, dass der vorhandene Platz am wirtschaftlichsten ausgenutzt wird. Sie versucht jedes untergeordnete Fenster grtmglich anzuzeigen. Je nach der Bildschirmgre erzeugt das Nebeneinanderlegen eine zweite Fensterspalte, wenn mehrere Fenster geffnet sind; doch ab einem bestimmten Punkt des Aufteilens der jeweiligen Hhe kann man auer der Titelzeile jedes untergeordneten Fensters nichts mehr sehen. Der Punkt, an dem eine zweite Spalte (bzw. eine Zeile beim Untereinanderlegen) erzeugt wird, kann sowohl fr Neben- als auch Untereinanderlegen der gleiche sein. In bestimmten Situationen bleibt nur noch eine Mglichkeit brig, um Fenster anzuordnen, und sowohl Neben- als auch Untereinanderlegen nutzen diese eine Mglichkeit. F Wie veranlasse ich meine untergeordneten Fenster dazu, in einer Ecke oder an einer Seite des bergeordneten Dokuments anzudocken? A Leider ist das Andocken von untergeordneten Fenstern nicht so einfach, wie es aussieht. Die Dock-Eigenschaft wirkt sich auf Form-Objekte nicht so aus wie auf andere Steuerelemente, so dass die Verwendung eines DockStyle-Wertes nichts bewirkt und die in vielen Anwendungen bliche Einrastfunktion nicht verfgbar ist. Eine Methode besteht darin, einen Ereignishandler fr das LocationChangedEreignis des untergeordneten Fensters zu erstellen. Wird dieses Ereignis ausgelst, knnen Sie die Position des fraglichen untergeordneten Fensters auswerten, und falls es sich in einem bestimmten Bereich (sagen wir, 10 Pixel vom linken Rand des bergeordneten Fensters entfernt) befindet, knnen Sie die Position und Gre anpassen, um den Eindruck des Andockens und Einrastens zu vermitteln. Ein typischer Ereignishandler wrde wie folgt aussehen:
private void SnapIt(Object Sender, EventArgs e) { Form frmTemp = (Form)Sender; if (frmTemp.Location.X < 10 & frmTemp.Location.Y < 10) { frmTemp.Location = new Point(0,0); frmTemp.Size = new Size(100,550); } }

Beachten Sie, dass diese Methode das untergeordnete Fenster nicht direkt andockt, aber zumindest eine gute Alternative dazu bietet.

373

MDI-Anwendungen erstellen

Ich habe ein untergeordnetes Fenster vom Symbolleistentyp erstellt, doch ich kann es nicht ber die Grenzen des bergeordneten Fensters hinaus bewegen. In Photoshop und Visual Studio geht das jedoch. Was ist los? A In diesen anderen Anwendungen haben Sie es nicht wirklich mit untergeordneten MDI-Fenstern zu tun. Vielmehr handelt es sich bei diesen Symbolleisten um angepasste Dialogfeldklassen, die sich an jede beliebige Position bewegen lassen und daher nicht einmal an die Grenzen des bergeordneten Fensters gebunden sind. Sollten Sie also eine Methode der anderen vorziehen? Die Antwort hngt von der zu erstellenden Anwendung ab. Wenn Sie zudem die Arbeitsflche des Benutzers nicht berladen wollen, wre es eine gute Idee, statt benutzerdefinierten Dialogfeldern untergeordnete MDI-Fenster anzulegen.

10.6 Workshop
Dieser Workshop soll Ihnen helfen, sich besser an die heute behandelten Begriffe zu erinnern. Es hilft Ihnen sehr, die Antworten vollstndig zu verstehen, bevor Sie weiterlesen. Die Antworten zu den Quizfragen und den bungen finden Sie in Anhang A.

Quiz
1. Welche Eigenschaft mssen Sie im bergeordneten MDI-Formular einstellen, um es zu einer MDI-Anwendung zu machen? 2. Welche Eigenschaft mssen Sie im untergeordneten MDI-Dokument einstellen, um es zum Bestandteil einer MDI-Anwendung zu machen? 3. ber welche drei Werte verfgt die Aufzhlung MdiLayout? 4. Wahr oder falsch? Sie mssen erst die Show-Methode aufrufen, bevor Sie ein untergeordnetes MDI-Formular anzeigen knnen, wenn es im Konstruktor seines bergeordneten Formulars erzeugt wird. 5. Der folgende Code veranlasst das TextBox-Steuerelement tbText nicht dazu, das gesamte Formular auszufllen:
TextBox tbText = new TextBox(); tbText.Dock = DockStyle.Fill;

Warum nicht?

374

Workshop

6. Wie findet man heraus, ob ein untergeordnetes MDI-Fenster aktiv ist? 7. Sie haben drei Menelemente in einem untergeordneten Dokument erzeugt, aber sie werden im bergeordneten Dokument nicht richtig angezeigt. Nennen Sie drei Dinge, die man zur Behebung dieses Problems prfen sollte. 8. Wahr oder falsch? Der MergeOrder-Wert muss sich schrittweise erhhen lassen; Sie knnen keine Werte in der Reihenfolge berspringen.

bung
Erstellen Sie eine MDI-Version der gestrigen Anwendung fr die Ausfhrung von SQLStatements. Erzeugen Sie ein benutzerdefiniertes Dialogfeld, das ein Textfeld anzeigt, in dem der Benutzer seine Abfrage eingeben kann.

375

Arbeiten mit der Windows-Ein-/ Ausgabe

1 1

Arbeiten mit der Windows-Ein-/Ausgabe

Das Arbeiten mit Ein- und Ausgabe (E/A) ist ein wichtiger Bestandteil des Umgangs mit jedem Betriebssystem. Dazu gehrt mehr als nur das Lesen und Schreiben von Dateien. E/A umfasst auch Drucken, Netzwerkzugang, berwachung von nderungen im Dateisystem und so weiter. Die heutige Lektion konzentriert sich auf verschiedene Aspekte der E/A in .NET Windows Forms. Sie werden erkennen, dass .NET den einst komplizierten Vorgang einfach gemacht hat, so dass Sie in krzester Zeit Dokumente speichern werden! Heute lernen Sie,

was Streams sind und wie man sie verwendet, wie man Streams liest und in sie schreibt, wie man eine Oberflche im Explorer-Stil erstellt, wie man Textdateien bearbeitet, die am Drucken eines Dokuments beteiligten Arbeitsschritte.

11.1 Einfhrung in Streams


Was ist eine Datei? Vom Standpunkt des Benutzer aus stellt eine Datei eine Information auf der Festplatte eines Computers dar. Sie hat einen Namen, einen Verzeichnispfad und eine Namenserweiterung; ihre Eigenschaften wie etwa Gre, Erstellungsdatum usw. lassen sich leicht ablesen. Wenn ein Benutzer eine Datei erstellt, geht er davon aus, dass sie nicht verschwinden, sondern auf die Festplatte geschrieben wird, es sei denn, eine Katastrophe trte ein. Fr den Entwickler hingegen sieht eine Datei ein wenig anders aus. Man muss in Begriffen von Bits und Bytes denken, so dass eine Datei im Grunde eine Sammlung von Einsen und Nullen ist, die auf der Festplatte gespeichert wird. Damit eine bestimmte Ansammlung von Bits einen Sinn ergibt, mssen sie auf eine bestimmte Art und Weise angeordnet sein, und unterschiedliche Dateitypen weisen jeweils ihre gesonderten Schemas auf. Eine Datei ist stets mit einer Position oder Verzeichnisstruktur auf dem Computer verknpft. Dieser Mechanismus eignet sich hervorragend fr den normalen Gebrauch und ist perfekt, wenn es um vordefinierte Speichermechanismen geht. Wenn man wei, welche Dateien man verwenden wird, erweist sich dieses Verfahren der sequentiellen Speicherung von Bits als ideal. Es hat jedoch auch seine Unzulnglichkeiten. Die herkmmliche Ein-/Ausgabe befasst sich nur mit der E/A von Dateien und kann keinen anderen Speichermechanismus handhaben. Man muss die richtige Bitsequenz und hufig sogar den Verzeichnispfad liefern, damit sie richtig funktioniert. Auf Grund der Natur eines Betriebssystems wie Win-

378

Einfhrung in Streams

dows und der Neigung der Benutzer, stets das Unerwartete zu tun, ist dieser Systemtyp anfllig fr Fehler. Das .NET Framework fhrt erstmals den Begriff Stream (Strom) ein. Zwischen einem Stream und einer Datei gibt es einen Hauptunterschied: Ein Stream kann von berallher stammen und er muss nicht auf der Festplatte gespeichert werden wie eine Datei. Er kann aus dem Speicher kommen, aus einem Netzwerk, von der Sicherheitskopie auf einem Bandlaufwerk usw. Die Position ist willkrlich, genau wie die Datenquelle fr ein DataSet. Diese Position wird als der Sicherungsspeicher eines Streams bezeichnet. Der wichtigste Aspekt an einem Stream, den man sich merken sollte, ist der, dass es sich lediglich um einige Daten handelt, die von einer Quelle unabhngig sind. Auf diese Weise muss sich ein Entwickler nicht mit den Schwierigkeiten beim Umgang mit einer bestimmten Datenquelle herumschlagen. Die Art und Weise, wie ein Stream genau funktioniert, mag Sie ein wenig verwirren. Er ldt nicht die gesamte Datenquelle in den Speicher und er lsst die Datenquelle auch nicht im bekannten Internetsinne strmen (wie etwa ein Streaming-Video). Stellen Sie sich einen Stream vielmehr als Ihren persnlichen Butler vor. Er ist der Vermittler zwischen Ihnen und den verknpften Daten und umgekehrt. Somit kann Ihr Butler Daten zur Datenquelle bringen und von dort welche holen. Er kann Ihnen Elemente der Quelle entweder in der Reihenfolge bringen, wie er sie antrifft oder in jeder anderen Weise, die Sie whlen; Sie knnten ihm etwa befehlen, ein Mittelstck aus einer Quelle zu besorgen. Abbildung 11.1 illustriert dieses Verteilungssystem.

nDate e quell

Strom

nDate e uell q

Abbildung 11.1: Ein Stream fungiert als eine Art Butler, um Informationen von Ihnen an eine Datenquelle auszuliefern oder umgekehrt.

Man kann vier verschiedene Arten von Streams verwenden, je nach deren Quelltyp: FileStream, MemoryStream, NetworkStream und CryptoStream (Letzterer ist an kryptographische Daten, die der Sicherheit dienen, geknpft). Darber hinaus stellt das Objekt BufferedStream lediglich eine Verbindung zu einem anderen Stream her. Es stellt diesen Streams Pufferfunktionen zur Verfgung. Zudem lassen sich die Streams miteinander verknpfen, falls einer fr eine Aufgabe nicht ausreichen sollte. So knnen Sie z.B. ein FileStream-Objekt einsetzen, um Daten aus einer Datei zu lesen und dann ein NetworkStream-Objekt, um diese Daten ber ein Netzwerk zu bertragen, ohne dabei mittendrin abbrechen zu mssen. Auch hier ist die ursprngliche Datenquelle gleichgltig; ein Stream lsst sich mit fast allem verbinden.

379

Arbeiten mit der Windows-Ein-/Ausgabe

Diesem Puzzle der Streams fehlt noch ein Stckchen: Streamreader und -writer. Sie helfen Ihnen bei der Interaktion mit den Streaminhalten. Wie ihre Namen andeuten, lesen sie aus oder schreiben in einen Stream. Um auf die Butler-Analogie zurckzukommen: Es kann vorkommen, dass Ihr Butler nicht die gleiche Sprache wie Sie spricht. Daher bentigen Sie einen bersetzer, der Ihre Befehle an den Butler weitergibt und Sie informiert, was der fremdsprachige Butler zu sagen hat. Stellen wir uns eine konkrete .NET-Situation vor. Angenommen, Sie haben eine XMLDatei. Um die Datei zu ffnen und auf deren Inhalt zuzugreifen, knnen Sie einen FileStream einsetzen. Als Nchstes bentigen Sie jedoch ein XmlReader-Objekt, das die Informationen aus dem Stream holen kann, wobei es Nullen und Einsen bei diesem Vorgang in XML-Inhalte bersetzt. Alternativ knnen Sie einen StringReader einsetzen, der den Stream lesen und eine umfangreiche Zeichenfolge liefern knnte. Offensichtlich ist der XmlReader in diesem Fall besser fr die Aufgabe geeignet, denn damit knnen Sie spezifische XML-Funktionen anwenden, die der StringReader nicht anbietet. Abbildung 11.2 illustriert die Kette der Objekte, mit der Sie interagieren mssen, um mit Ein-/Ausgabe unter Windows umzugehen.
Leser oder Schreiber

beliebiger Datenspeicher

Strom

Abbildung 11.2: Ihr Stream kann Daten aus einer Datenquelle liefern und ein Reader oder Writer kann Ihnen die Daten in verwendbarem Format liefern.

11.2 Dateien und Verzeichnisse


Bevor Sie anfangen, Dateien zu lesen und zu schreiben, mssen Sie wissen, wie Sie mit diesen generischen Objekten umgehen. Mit Hilfe von vier .NET-Objekten knnen Sie Dateien und Ordner in einem Dateisystem manipulieren: File, FileInfo, Directory und DirectoryInfo. Die Objekte File und FileInfo hneln einander; sie enthalten zu einem groen Teil die gleichen Mitglieder. Der Unterschied besteht darin, dass das File-Objekt rein statisch ist. Wie Sie wissen, brauchen Sie bei statischen Mitgliedern keine Instanz einer Klasse, um sie verwenden zu knnen. Sie knnen daher die File.Copy-Methode benutzen, um eine beliebige Datei von einem Standort zu einem anderen zu kopieren. Das FileInfo-Objekt betrifft nur die aktuelle Instanz einer Datei. Die Methode FileInfo.Copy kopiert die aktuelle Datei. Die gleichen Regeln gelten fr die Objekte Directory und DirectoryInfo. Meist werden Sie die Info-Objekte verwenden, da Sie mit spezifischen Dateien und Verzeichnissen umgehen.

380

Dateien und Verzeichnisse

Wir wollen uns ein Beispiel ansehen. Listing 11.1 erzeugt eine Verzeichnisliste im Explorer-Stil, indem es ein Verzeichnis durchluft und die Elemente einem TreeView-Steuerelement hinzufgt. Listing 11.1: Eine Verzeichnisliste im Explorer-Stil erstellen
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: 42: using using using using System; System.Windows.Forms; System.Drawing; System.IO;

namespace TYWinforms.Day11 { public class Listing111 : Form { private TreeView tvFiles = new TreeView(); private Label lblInfo = new Label(); public Listing111() { tvFiles.Dock = DockStyle.Left; tvFiles.Width = 150; lblInfo.Size = new Size(100,100); lblInfo.Text = " Select a file or\ndirectory"; PopulateList("c: \\", tvFiles.Nodes.Add("c: \\")); this.Controls.Add(tvFiles); this.Controls.Add(lblInfo); } private void PopulateList(String strPath, TreeNode currNode) { DirectoryInfo dir = new DirectoryInfo(strPath); TreeNode nodeSubDir; foreach (DirectoryInfo d in dir.GetDirectories()) { nodeSubDir = currNode.Nodes.Add(d.Name); PopulateList(d.FullName, nodeSubDir); } foreach (FileInfo f in dir.GetFiles("*.*")) { currNode.Nodes.Add(f.Name); } } public static void Main() { Application.Run(new Listing111()); } } }

381

Arbeiten mit der Windows-Ein-/Ausgabe

Listing 11.1 beginnt normal: Ein Steuerelement ListView wird angelegt, mehrere Eigenschaften werden eingestellt und das Formular wird angezeigt. Doch die PopulateList-Methode ist etwas Neues. Die PopulateList-Methode wird als rekursive Methode bezeichnet, da sie immer wieder sich selbst aufruft. Sie beschafft Informationen ber das im ersten Parameter angegebene Verzeichnis. Wird sie das erste Mal aufgerufen (wie in Zeile 18), ist das Verzeichnis C: bzw. das Stammverzeichnis. Zeile 26 legt ein temporres Objekt an, das wir gleich verwenden. Das DirectoryInfo-Objekt in Zeile 25 enthlt einige Informationen ber das aktuelle Verzeichnis. Mit Hilfe der GetDirectories-Methode knnen Sie alle Unterverzeichnisse herausfinden und mit der GetFiles-Methode auch alle im Verzeichnis enthaltenen Dateien. In Zeile 28 iterieren Sie mit der foreachSchleife durch alle Unterverzeichnisse im aktuellen Verzeichnis. Zeile 29 fgt dem TreeView-Steuerelement einen neuen Knoten hinzu, wobei der Name jedes Unterverzeichnisses verwendet wird. Zeile 30 berspringen wir vorerst. Die FullName-Eigenschaft der Objekte DirectoryInfo und FileInfo liefert den Namen des Objektes einschlielich der kompletten Pfadangabe. Hier ein Beispiel:
c:\inetpub\wwwroot\myfile.aspx

Die Name-Eigenschaft liefert hingegen nur den Namen ohne die Pfadangabe:
myfile.aspx.

Zeile 33 hnelt dem bereits Untersuchten. Zu jeder Datei im Verzeichnis fgen Sie der Strukturansicht einfach einen neuen Knoten mit dem jeweiligen Dateinamen hinzu. Nun ist das Dateisystem jedoch hierarchisch aufgebaut; man knnte Unterverzeichnisse bis zum Abwinken einrichten. Ohne Zeile 30 wrde diese Methode nur die Unterverzeichnisse und Dateien des Stammverzeichnisses C:\ auflisten; sie geht nicht tiefer. Man braucht also eine Mglichkeit, immer tiefer durch die Unterverzeichnisse zu graben, bis man am Ende anlangt. Da man nicht wissen kann, wie tief die Verzeichnisstruktur auf der Festplatte eines Benutzers reicht, sollte man diese Unterverzeichnisse automatisch ermitteln knnen. Die PopulateList-Methode erledigt das fr Sie. Enthlt das aktuelle Verzeichnis keine Unterverzeichnisse, dann werden die Zeilen 28 bis 31 einfach nicht ausgefhrt, und es geht weiter. Das Gleiche gilt fr die Zeilen 33 bis 35. Es ist also sinnvoll, diese Methode auch fr die nchste Ebene von Unterverzeichnissen einzusetzen. In Zeile 30 rufen Sie wieder die PopulateList-Methode auf, wobei Sie ihr die Pfadangabe des aktuellen Unterverzeichnisses und den Knoten im Hierarchiebaum bergeben. Wird die Methode diesmal ausgefhrt, holt sie sich die Informationen ber das Unterverzeich-

382

Dateien und Verzeichnisse

nis, fgt es unter dem aktuellen Knoten ein und durchluft seine Unterverzeichnisse und Dateien wie sonst auch. Stt der Vorgang auf ein weiteres Unterverzeichnis, wird dafr die PopulateList-Methode erneut aufgerufen und so weiter. Auf diese Weise knnen Sie alle Verzeichnisse und Dateien auf der Festplatte durchlaufen, ohne im Voraus wissen zu mssen, wie tief die Struktur reicht. Rekursion ist also fr Ein- und Ausgabe von Dateien von hoher Bedeutung. Tabelle 11.1 erklrt den Ablauf ein bisschen detaillierter.
Aktuelles Verzeichnis und aktueller Knoten
C:\ C:\ASPNET

brige Unterverzeichnisse
ASPNET, Windows

Aktion Durchlaufe das erste Unterverzeichnis in C:\ Liste die Dateien in C:\ASPNET auf, falls sie verfgbar sind; gehe zurck zum bergeordneten Verzeichnis. Durchlaufe das zweite Unterverzeichnis in C:\ Durchlaufe das erste Unterverzeichnis in
C:\Windows

Keine

C:\ C:\Windows

Windows Wallpaper, System32

C:\Windows\Wallpaper

Keine

Liste die Dateien in C:\Windows\Wallpaper auf, falls sie verfgbar sind; gehe zurck zum bergeordneten Verzeichnis. Durchlaufe das zweite Unterverzeichnis in
C:\Windows

C:\Windows

System32

C:\Windows\System32

Keine

Liste die Dateien in C:\Windows\System32 auf, falls sie verfgbar sind; gehe zurck zum bergeordneten Verzeichnis. Liste die Dateien in C:\Windows auf, falls sie verfgbar sind; gehe zurck zum bergeordneten Verzeichnis. Liste die Dateien in C:\ auf, falls sie verfgbar sind; brich ab.

C:\Windows

Keine

C:\

Keine

Tabelle 11.1: Der Ablauf der Rekursion

Abbildung 11.3 zeigt das Ergebnis dieser Anwendung. Beachten Sie, dass das Durchlaufen aller Verzeichnisse auf Ihrer Festplatte eine Weile dauern kann, so dass sich die Anwendung nicht sofort ffnet. Um diesen Vorgang zu beschleunigen, knnen Sie zur Laufzeit diese Baumstruktur erstellen lassen statt whrend der Initialisierung. Mit anderen Worten: Iterieren Sie durch ein Unterverzeichnis und seine Dateien nur dann, wenn der Benutzer

383

Arbeiten mit der Windows-Ein-/Ausgabe

diesen Knoten expandiert; so mssen Sie nur eine Ebene auf einmal bewltigen, und die Ausfhrung wird erheblich beschleunigt. Diese Aufgabe ist eine bung fr Sie.

Abbildung 11.3: Listing 11.1 erzeugt diese Explorer-hnliche Anwendung.

Wenn Sie den Code in Listing 11.1 ausfhren, knnten Sie eine Fehlermeldung erhalten, weil Sie nicht die Berechtigung haben, auf die Informationen aller Verzeichnisse und Dateien zuzugreifen. Sie knnen den Suchbereich auf ein bestimmtes Verzeichnis eingrenzen, indem Sie die GetDirectoriesMethode in Zeile 28 entsprechend ndern:
.GetDirectories("windows*")

Diese Methode bringt die Anwendung dazu, nur solche Verzeichnisse aufzulisten, die das Wort windows im Namen aufweisen. Hchstwahrscheinlich lsst sich dadurch Ihr Berechtigungsproblem umgehen. An Tag 21 lernen Sie eine bessere Mglichkeit kennen. Wir wollen diese Anwendung erweitern, um den Umgang mit Datei- und Verzeichnisobjekten besser in den Griff zu bekommen. Fgen Sie in den Konstruktor folgenden Code ein:
tvFiles.AfterSelect += new TreeViewEventHandler(this.DisplayInfo);

Whlt nun ein Benutzer einen Knoten in der Strukturansicht aus, knnen Sie ihm im Bezeichnungsfeld gem Zeile 9 von Listing 11.1 zustzliche Informationen anzeigen. Listing 11.2 zeigt die DisplayInfo-Methode. Listing 11.2: Dem Benutzer Informationen anzeigen
1: 2: 3: 4: 5: 6: private void DisplayInfo(Object Sender, TreeViewEventArgs e) { TreeNode tempNode = e.Node; String strFullName = tempNode.Text; while (tempNode.Parent != null) { strFullName = tempNode.Parent.Text + "\\" + strFullName;

384

Dateien und Verzeichnisse

7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21:

tempNode = tempNode.Parent; } if (File.Exists(strFullName)) { FileInfo obj = new FileInfo(strFullName); lblInfo.Text = "Name: " + obj.Name; lblInfo.Text += "\nGre: " + obj.Length; lblInfo.Text += "\nZugriff: " + obj.LastAccessTime.ToString(); } else { DirectoryInfo obj = new DirectoryInfo(strFullName); lblInfo.Text = "Name: " + obj.Name; lblInfo.Text += "\nAttribute: " + obj.Attributes.ToString(); lblInfo.Text += "\nZugriff: " + obj.LastAccessTime.ToString(); } }

Das TreeViewNodeEventArgs-Objekt liefert dasjenige TreeNode-Objekt, das angeklickt wurde. In Zeile 2 speichern Sie dieses Objekt in einer Variablen zur spteren Verwendung. Dann holen Sie die Beschriftung in Zeile 3. Als Nchstes mssen Sie den vollen Pfadnamen desjenigen Knotens suchen, der angeklickt wurde, so dass Sie die Datei oder das Verzeichnis korrekt ermitteln knnen, um die entsprechenden Informationen hierzu anzuzeigen. Um den Pfadnamen zu suchen, verwenden Sie eine while-Schleife, die die ParentEigenschaft des aktuellen Knotens prft. Gibt es keine bergeordneten Knoten (das wre im Stammverzeichnis der Fall), wird die Parent-Eigenschaft den Wert null liefern und Sie wissen, dass Sie den vollen Pfadnamen haben. In der Schleife hngen Sie einfach den Namen jedes bergeordneten Knotens in die Zeichenfolge ein und bewegen sich eine Stufe hher. Der nchste Codeabschnitt bestimmt das zu behandelnde Objekt, sei es nun eine Datei oder ein Verzeichnis. Die File.Exists-Methode bestimmt, ob der angegebene Pfad auf eine Datei zeigt. Ist dies der Fall, zeigen Sie diverse Informationen ber die Datei im Bezeichnungsfeld an, wie in den Zeilen 11 bis 14 zu sehen. Existiert die Datei jedoch nicht, wissen Sie, dass es sich statt dessen um ein Verzeichnis handelt, so dass Sie entsprechend andere Informationen anzeigen (Zeilen 16 bis 19). Werfen Sie nun einen weiteren Blick auf die Applikation, wie sie in Abbildung 11.4 zu sehen ist. Tabelle 11.2 fhrt die Mitglieder des FileInfo-Objekts auf.

385

Arbeiten mit der Windows-Ein-/Ausgabe

Abbildung 11.4: In Ihrem Explorer knnen Sie auch Rckmeldungen anbieten. Eigenschaft
Attributes

Beschreibung Liefert die Dateiattribute. Kann einen der Werte der FileAttributes-Aufzhlung annehmen: Archive, Compressed, Device, Directory, Encrypted, Hidden, Normal, NonContentIndexed, Offline, ReadOnly, ReparsePoint, SparseFile, System, Temporary. Gibt Erstellungsdatum und zeit einer Datei an. Gibt ein DirectoryInfo-Objekt an, das das bergeordnete Verzeichnis dieser Datei darstellt. Enthlt eine Zeichenfolge, die den vollstndigen Pfad des bergeordneten Verzeichnisses angibt. Gibt an, ob die Datei gltig ist. Legt die Dateierweiterung fest. Gibt den Dateinamen inklusive Pfadangabe an. Gibt Datum und Zeit des letzten Dateizugriffs an. Gibt Datum und Zeit des letzten Schreibzugriffs an. Gibt die Dateigre an. Gibt den Dateinamen exklusive Pfadangabe an. Beschreibung Erzeugt einen StreamWriter, mit dem man Text an die Datei anhngen kann.

CreationTime Directory

DirectoryName

Exists Extension FullName LastAccessTime LastWriteTime Length Name

Methode
AppendText

Tabelle 11.2: FileInfo-Eigenschaften

386

Dateien und Verzeichnisse

Eigenschaft
CopyTo Create CreateText Delete MoveTo Open OpenRead OpenText

Beschreibung Kopiert die Datei an einen neuen Speicherplatz. Legt die Datei an. Legt die Datei und einen StreamWriter an, der in die Datei schreiben kann. Lscht die Datei. Verschiebt die Datei an einen neuen Speicherort. ffnet die Datei mit diversen Berechtigungen. Erzeugt fr diese Datei ein schreibgeschtztes FileStream-Objekt. Erzeugt fr diese Datei ein FileStream-Objekt mit Lese-/Schreibzugriff.

Tabelle 11.2: FileInfo-Eigenschaften (Forts.)

Tabelle 11.3 fhrt die verschiedenen Mitglieder des DirectoryInfo-Objekts auf. Ihnen fllt sicher auf, dass es einige Eigenschaften mit dem FileInfo-Objekt gemeinsam hat.
Eigenschaft
Attributes

Beschreibung Liefert die Verzeichnisattribute. Kann einen der Werte der FileAttributesAufzhlung annehmen: Archive, Compressed, Device, Directory, Encrypted, Hidden, Normal, NonContentIndexed, Offline, ReadOnly, ReparsePoint, SparseFile, System, Temporary. Gibt Erstellungszeit und -datum fr das Verzeichnis an. Gibt an, ob dieses Verzeichnis gltig ist. Gibt die Namenserweiterung des Verzeichnisses an. Gibt den Verzeichnisnamen inklusive Pfadangabe an. Gibt Datum und Zeit des letzten Verzeichniszugriffs an. Gibt Datum und Zeit des letzten Schreibzugriffs auf das Verzeichnis an. Gibt den Verzeichnisnamen exklusive Pfadangabe an. Liefert ein DirectoryInfo-Objekt, das das bergeordnete Verzeichnisses dieses Verzeichnisses darstellt. Liefert ein DirectoryInfo-Objekt, das die Wurzel dieses Verzeichnisses darstellt.

CreationTime Exists Extension FullName LastAccessTime LastWriteTime Name Parent

Root

Tabelle 11.3: Die Mitglieder von DirectoryInfo

387

Arbeiten mit der Windows-Ein-/Ausgabe

Eigenschaft Methode
Create CreateSubDirectory

Beschreibung Beschreibung Erstellt das Verzeichnis. Erstellt das Verzeichnis und liefert ein DirectoryInfo-Objekt, das das neue Verzeichnis darstellt. Lscht das Verzeichnis. Holt ein Array von DirectoryInfo-Objekten, die alle Unterverzeichnisse des aktuellen Verzeichnisses darstellen. Holt ein Array von FileInfo-Objekten, die alle Dateien des aktuellen Verzeichnisses darstellen. Holt ein Array von FileSystemInfo-Objekten, die alle Dateien und Verzeichnisse darstellen. Verschiebt die Datei an einen neuen Speicherplatz.

Delete GetDirectories

GetFiles

GetFileSystemInfos

MoveTo

Tabelle 11.3: Die Mitglieder von DirectoryInfo (Forts.)

Dateien lesen und schreiben


Gleichgltig, ob die Datei, die Sie verarbeiten wollen, nun binr ist oder aus reinem Text besteht, so bleibt doch die Vorgehensweise beim Lesen und Schreiben die gleiche: Man hngt ein Streamobjekt des erforderlichen Typs an und hngt diesem wiederum einen Reader oder Writer zwecks Dateizugriff an. Darber hinaus haben alle Reader und Writer die gleichen allgemeinen Mitglieder wenn Sie wissen, wie Sie eines verwenden, knnen Sie alle verwenden. Wir wollen uns ein Beispiel fr das Schreiben einer Textdatei ansehen. Listing 11.3 zeigt eine einfache Anwendung, in die der Benutzer einen Dateinamen zwecks Erstellung eingeben kann. Diese Textdatei wird mit dem Text "Hello World!" darin erstellt. Listing 11.3: Eine Datei erzeugen
1: 2: 3: 4: 5: 6: 7: using using using using System; System.Windows.Forms; System.Drawing; System.IO;

namespace TYWinforms.Day11 { public class Listing112 : Form {

388

Dateien und Verzeichnisse

8: private Button btCreate = new Button(); 9: private TextBox tbFileName = new TextBox(); 10: 11: public Listing112() { 12: tbFileName.Location = new Point(25,100); 13: tbFileName.Width = 150; 14: 15: btCreate.Text = "Erstellen!"; 16: btCreate.Location = new Point(200,100); 17: btCreate.Click += new EventHandler(this.CreateFile); 18: 19: this.Text = "Listing 11.2"; 20: this.Controls.Add(btCreate); 21: this.Controls.Add(tbFileName); 22: } 23: 24: public void CreateFile(Object Sender, EventArgs e) { 25: String strFilename = tbFileName.Text; 26: 27: if (!File.Exists(strFilename)) { 28: FileStream objFile = new FileStream(strFilename,  FileMode.CreateNew); 29: StreamWriter objWriter = new StreamWriter(objFile); 30: 31: objWriter.WriteLine("Hello World!"); 32: 33: objWriter.Close(); 34: objFile.Close(); 35: 36: MessageBox.Show("Fertig!"); 37: } 38: } 39: 40: public static void Main() { 41: Application.Run(new Listing112()); 42: } 43: } 44: }

Wir wollen den Konstruktor in Listing 11.3 berspringen; das einzige Interessante daran ist die Zuweisung des Delegaten in Zeile 17. Alle Aktionen beginnen in der CreateFile-Methode ab Zeile 24. Dieser Code setzt voraus, dass der Benutzer etwas Text in die TextBox eingegeben hat. In Zeile 27 prfen Sie, ob die angegebene Datei bereits existiert. Ist das der Fall, sollten Sie sie in Ruhe lassen. Gibt es sie noch nicht, drfen Sie fortfahren.

389

Arbeiten mit der Windows-Ein-/Ausgabe

Zeile 28 legt ein FileStream-Objekt an, um mit der Datei zu interagieren. Der Konstruktor dafr bernimmt einige Parameter. Der erste ist natrlich der Pfad zu der fraglichen Datei. Der zweite ist optional und spezifiziert den Modus, mit dem die Datei geffnet wird. Er kann einen der Werte der FileMode-Aufzhlung annehmen:
Append: ffnet die Datei, falls sie existiert, und bewegt sich bis zum Ende der Datei. Ist sie jedoch nicht vorhanden, wird eine neue Datei angelegt. Create: Legt eine neue Datei an. Ist sie bereits vorhanden, wird sie berschrieben. CreateNew: Legt eine neue Datei an. Ist sie bereits vorhanden, wird eine

Fehlermeldung ausgegeben.
Open: ffnet eine vorhandene Datei. OpenOrCreate: ffnet die Datei, falls sie vorhanden ist. Falls nicht, wird eine

neue Datei angelegt.


Truncate: ffnet eine vorhandene Datei und lscht jeden vorhandenen

Inhalt. Im Code legen Sie FileMode.CreateNew so fest, dass eine neue Datei unter Verwendung des in der TextBox angegebenen Pfadnamens angelegt wird. Nun da Sie fr Ihre Datei einen Stream haben, mssen Sie einen Writer erstellen, der Daten in sie schreibt. Da wir mit Informationen in reinem Text umgehen, erstellen wir in Zeile 29 einen StreamWriter. Der Konstruktor dieses Objekts bernimmt nur einen Parameter, nmlich den Stream, dem der Writer zuzuordnen ist. Die WriteLine-Methode in Zeile 31 schreibt den angegebenen Text in den Stream (und daher in die Datei) und hngt ein Zeilenabschlusszeichen an das Ende der Zeichenfolge an. Die Write-Methode hingegen hngt kein Zeichen fr den Zeilenabschluss an. Schlielich mssen Sie daran denken, die Close-Methoden sowohl fr den Writer als auch den Stream aufzurufen, damit die Anwendung wei, dass Sie damit fertig sind, und sie daher wertvolle Systemressourcen freigeben kann. In Zeile 36 wird ein Meldungsfeld angezeigt, um dem Benutzer mitzuteilen, dass die Aufgabe erledigt ist. Kompilieren Sie nun die Anwendung und fhren Sie sie aus. Tragen Sie einen gltigen Pfadnamen ein (z.B. C:\temp\blubb.txt) und klicken Sie auf die Schaltflche ERSTELLEN. Wenn Sie in das angegebene Verzeichnis wechseln, sehen Sie eine neue Textdatei, die die Wrter "Hello World" enthlt.

390

Dateien und Verzeichnisse

Lassen Sie uns fr einen Moment zurckgehen und uns den Konstruktor fr das Streamobjekt ansehen. Dieser flexible Konstruktor kann alle mglichen Parameter bernehmen. So sind z.B. alle folgenden Zeilen gltig:
new FileStream(strFilename, new FileStream(strFilename, new FileStream(strFilename, new FileStream(strFilename, FileShare.None,1024,true); FileMode.CreateNew); FileMode.Create, FileAccess.Read); FileMode.Create, FileAccess.Read, FileShare.None); FileMode.Create, FileAccess.Read,

Den ersten Konstruktor kennen Sie bereits. Der zweite erlaubt Ihnen die Funktionen festzulegen, die der Stream an der Datei ausfhren kann. Dabei knnen Sie die Werte der Aufzhlung FileAccess nutzen, die Read, ReadWrite und Write lauten. Der dritte Konstruktor fgt dieser Liste einen weiteren Aufzhlungswert aus FileShare hinzu. Dieser Wert bestimmt, wie andere Streamobjekte zur gleichen Zeit wie Sie auf diese Datei zugreifen knnen. Die Werte knnen None, Read, ReadWrite oder Write sein. Ein FileShare.None-Wert bedeutet, dass anderen Streams der Zugriff auf diese Datei verwehrt wird, whrend Sie mit ihr arbeiten. Der letzte Konstruktor fgt zwei weitere Parameter hinzu. Der erste zeigt die Puffergre in Byte an, die fr die Datei zu verwenden ist. Das Setzen eines Pufferspeichers kann die Performanz verbessern. Der zweite Parameter gibt an, ob die Arbeitsschritte asynchron ausgefhrt werden sollen; dazu mehr im Unterkapitel Asynchrone Ein-/Ausgabe. Die Erstellung des Streamobjekts ist der komplizierteste Teil des Listings 11.3. Doch die Verwendung des Writers ist einfach, denn man kann nur zwischen zwei Methoden whlen: Write oder WriteLine. Wir wollen diesem Listing einen Reader hinzufgen. Zu diesem Zweck modifizieren wir den if-Block in Zeile 27 des Listings 11.3 so, dass er ein else-Statement umfasst:
} else { FileStream objFile = new FileStream(strFilename, FileMode.Open,  FileAccess.Read); StreamReader objReader = new StreamReader(objFile);

String strContents = objReader.ReadToEnd(); objReader.Close(); objFile.Close(); MessageBox.Show(strContents); }

391

Arbeiten mit der Windows-Ein-/Ausgabe

In diesem Fall ist bekannt, dass die Datei existiert. Sie erzeugen wie zuvor ein FileStreamObjekt und benutzen die gleiche Pfadangabe. Statt eines StreamWriters erzeugen Sie jedoch diesmal einen StreamReader. Die Syntax ist identisch. Als Nchstes rufen Sie die Methode ReadToEnd des Readers auf, um den gesamten Dateiinhalt zurckzugeben. Dann schlieen Sie Reader und Stream und zeigen den Inhalt der Datei in einem Meldungsfeld an. Fhren Sie nun die Anwendung erneut aus, geben Sie den zuvor benutzten Dateinamen an und klicken Sie auf die Schaltflche ERSTELLEN. Statt einer Erledigt!-Meldung erscheint nun ein Meldungsfeld mit dem entsprechenden Dateiinhalt. Bearbeiten Sie diese Datei manuell und versuchen Sie es erneut. Im Meldungsfeld erscheint nun der neue Inhalt. Abbildung 11.5 zeigt ein Beispiel fr ein mgliches Ergebnis.

Abbildung 11.5: Bei Verwendung von Streams knnen Sie Textdateien leicht lesen und schreiben.

Das StreamReader-Objekt verfgt ber eine grere Anzahl Methoden fr das Lesen von Streams, als der StreamWriter fr das Schreiben von Streams. Ihnen ist bereits die Methode ReadToEnd begegnet, die den Gesamtinhalt einer Datei liefert. ReadLine liefert jeweils nur eine Zeile dieser Datei, ReadBlock liest eine festgelegte Menge von Zeichen, die an einem bestimmten Punkt beginnt, und legt die zurckgegebenen Daten in einem Array ab. Der Ausdruck ReadBlock(buffer, 0, 100) etwa liest vom ersten bis zum 100. Zeichen und legt diese Menge im char-Array buffer() ab. Erreichen Sie das Dateiende vor dem 100. Zeichen, liefert die Methode einfach nur das, was verfgbar ist. Die einfache Read-Methode liest aus dem Stream nur jeweils ein Zeichen. Ein Zeiger wird zum nchsten Zeichen bewegt, sobald Sie diese Methode aufrufen, so dass das nchste Zeichen beim nchsten Read-Aufruf geliefert wird. Read kann sich ebenfalls wie die ReadBlockMethode verhalten, wenn Sie ihr ein char-Array sowie Anfangs- und Endwerte bergeben. Die Peek-Methode hnelt wiederum Read: Sie holt das nchste Zeichen aus dem Stream, verschiebt aber den Zeiger nicht. Diese Methode wird am hufigsten dazu benutzt, um festzustellen, ob der fragliche Stream noch weiteren Inhalt aufweist. Ist das nicht der Fall, liefert Peek den Wert 1.

392

Dateien und Verzeichnisse

Es stehen Ihnen zahlreiche Typen von Readern und Writern zur Verfgung. Bislang haben Sie nur StreamReader und StreamWriter in Aktion gesehen, die Text lesen/schreiben knnen. In gleicher Weise knnen StringReader und StringWriter Zeichenfolgen lesen/ schreiben. HttpWriter kann Ausgaben an einen Web-Browser schreiben. In ASP.NET verwenden Sie HtmlTextWriter, um HTML auch in Browsern wiederzugeben. BinaryReader und BinaryWriter lesen und schreiben Binrdaten. Schlagen Sie in der Dokumentation zum .NET Framework nach, um mehr Informationen ber diese Reader und Writer zu finden. Da Sie aber bereits StreamReader und StreamWriter kennen, wissen Sie, wie die meisten funktionieren.

Binrdaten lesen und schreiben


Die zu lesenden Dateien kommen nicht immer im Textformat daher; vielmehr enthalten sie hufig Binrdaten. Diese knnen Sie nicht ohne die Hilfe eines Computers lesen. Beispielswiese verwenden Datenbanken hufig das Binrformat, um Platz zu sparen und ihre Inhalte vor neugierigen Blicken zu schtzen. Doch keine Sorge: .NET ist vollstndig in der Lage, selbst Binrdaten zu lesen und zu schreiben. Und Sie wissen bereits, wie das geht. Wir wollen eine Beispielanwendung erstellen, die Binrdaten lesen und schreiben kann. Sie ist in Listing 11.4 zu sehen. Listing 11.4: Im Binrformat arbeiten
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: Imports Imports Imports Imports System System.Windows.Forms System.Drawing System.IO

Namespace TYWinForms.Day11 Class Listing114 : Inherits Form private btWriteIt As New Button() private btReadIt As New Button() private nudHigh As New NumericUpDown() private nudLo As New NumericUpDown() private dtpDate As New DateTimePicker() private lblHigh As New Label() private lblLo As New Label() Public Sub New() lblLo.Text = "Tiefe Temp" lblLo.Location = new Point(10,10) nudLo.Location = new Point(110,10)

393

Arbeiten mit der Windows-Ein-/Ausgabe

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:

nudLo.Minimum = -100 nudLo.Maximum = 130 lblHigh.Text = "Hohe Temp" lblHigh.Location = new Point(10,40) nudHigh.Location = new Point(110,40) nudHigh.Minimum = -100 nudHigh.Maximum = 130 dtpDate.Location = new Point (10,80) btWriteIt.Text = "Schreiben!" btWriteIt.Location = new Point(30,200) AddHandler btWriteIt.Click, new EventHandler(AddressOf Me.WriteIt) btReadIt.Text = "Lesen!" btReadIt.Location = new Point(120,200) AddHandler btReadIt.Click, new EventHandler(AddressOf Me.ReadIt) Me.Controls.Add(lblLo) Me.Controls.Add(nudLo) Me.Controls.Add(lblHigh) Me.Controls.Add(nudHigh) Me.Controls.Add(dtpDate) Me.Controls.Add(btWriteIt) Me.Controls.Add(btReadIt) End Sub private sub WriteIt(Sender As Object, e As EventArgs) Dim objStream As New FileStream("c: \winforms\day11\records.dat", FileMode.Create) Dim objWriter As New BinaryWriter(objStream) objWriter.Write(CType(nudLo.Value, Short)) objWriter.Write(CType(nudHigh.Value, Short)) objWriter.Write(dtpDate.Value.ToString("d")) objWriter.Close() objStream.Close() end sub private sub ReadIt(Sender As Object, e As EventArgs) Dim strTemp As String Dim lo, hi as Short if not File.Exists("c:\winforms\day11\records.dat") then

394

Dateien und Verzeichnisse

66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87:

MessageBox.Show("Die Datei existiert nicht!") Return End If Dim objStream = New FileStream("c:\winforms\day11\records.dat", FileMode.Open, FileAccess.Read) Dim objReader As New BinaryReader(objStream) lo = objReader.ReadInt16() hi = objReader.ReadInt16() strTemp = objReader.ReadString() MessageBox.Show("Hchst- und Tiefsttemperaturen am " & strTemp & " waren " & hi.ToString() & " und " & lo.ToString() ) objReader.Close() objStream.Close() end sub Public Shared Sub Main() Application.Run(new Listing114()) End Sub End Class End Namespace

Dieses Programm holt die Hchst- und Tiefsttemperaturen eines Tages ein und schreibt diese Werte zusammen mit dem Datum in eine Datei. Neben den Schaltflchen fr das Schreiben und Lesen einer Datei deklarieren wir weitere Steuerelemente fr die Eingabe von Temperaturen und des Datums (Zeilen 9 bis 15). Die Steuerelemente werden initialisiert und im Konstruktor unserem Formular hinzugefgt (Zeilen 18 bis 46). Die WriteIt- und ReadIt-Methoden in den Zeilen 49 bzw. 61 bilden die Ereignishandler fr unsere zwei Schaltflchen, die eine neue Binrdatei erzeugen und aus derselben Datei lesen. Mit der WriteIt-Methode beginnt das Anlegen eines FileStream-Objekts in Zeile 50 nichts Neues fr Sie. Auch Zeile 51 ist hnlich, doch Sie erzeugen statt eines StreamWriters einen BinaryWriter. In den Zeilen 53 bis 55 schreiben Sie im Binrformat die Werte Ihrer Steuerelemente in Ihre Datei. Wir verwenden CType, um die Temperaturen in einen Short-Datentyp zu konvertieren, der weniger Platz beansprucht. Dann benutzen wir einen der DateTime-Formatierungscodes (vgl. Tag 8), um unser Datum in eine Zeichenfolge mit kurzem Datumsmuster (mm/dd/yyyy) umzuwandeln. Die BinaryWriter.Write-Methode wird fr mehrere Datentypen berladen, so dass die Aufrufe in den Zeilen 53

395

Arbeiten mit der Windows-Ein-/Ausgabe

und 54 diejenige Version der Methode aufrufen, die einen Short-Datentyp bernimmt, wohingegen der Aufruf in Zeile 55 die String-Version von Write aufruft. In den Zeilen 57 und 58 schlieen Sie Writer- und Streamobjekte. Um ein Zwischenergebnis zu erhalten, wollen wir einmal diese Anwendung ausfhren und auf die Schaltflche SCHREIBEN! klicken. Gehen Sie nach der Programmausfhrung ins Verzeichnis c:\winforms\day11, wo Sie eine neue Datei namens records.dat vorfinden sollten. ffnen Sie die Datei mit dem Windows-Editor, um folgende Ausgabe vorzufinden:
??????????????

Der Ausgabevorgang weist durchaus keinen Fehler auf. Der Editor kann nur reinen Text verarbeiten und wenn er auf diese Ausgabe im Binrformat stt, die Sie erstellt haben, wei er sich nicht anders als mit der Darstellung von Fragezeichen zu helfen. Um Ihnen zu beweisen, dass dies kein Trick ist, kehren Sie zu Listing 11.4 zurck. In Zeile 65 prfen Sie in der Methode ReadIt, ob diese Datei existiert (sie sollte), doch ist dies nicht der Fall, hrt die Subroutine (in Zeile 67) mit der Verarbeitung auf. In Zeile 70 erzeugen Sie ein weiteres FileStream-Objekt fr die Datei records.dat und in Zeile 71 einen BinaryReader. Da wir wissen, dass wir gerade zwei Shorts und einen nachfolgenden String geschrieben haben, um die Daten auszugeben, werden wir in den Zeilen 73 bis 75 zwei Shorts und einen String lesen, um die Daten wieder einzulesen. Doch beim Lesen von Binrdaten mssen wir eine andere Methode aufrufen. Da ein Short in VB .NET ein 16-Bit-Integer ist, verwenden wir die Methode ReadInt16 fr das Short-Lesen und die Methode ReadString fr das String-Lesen. Den sich daraus ergebenden Text zeigen wir in Zeile 77 an und schlieen dann (Zeilen 80/81) den Reader und den Stream. Wenn Sie diese Anwendung ausfhren und auf die Schaltflche LESEN! klicken, erscheint ein Meldungsfenster mit etwa folgendem Text:
Hchst- und Tiefsttemperaturen am 28.8.2002 waren 44 und 22.

Ihr Binrreader hat also erfolgreich das Binrformat in reinen Text bersetzt. Wenn Sie mit Binrdateien umgehen, ist es von hchster Bedeutung zu wissen, wie deren Inhalt jeweils aufgebaut ist, damit Sie sie richtig auslesen knnen. Sonst knnten die Ergebnisse unvorsehbar sein.

Serialisierung
.NET stellt auch eine Methode bereit, um Objekte zu serialisieren. Serialisierung ist der Vorgang, bei dem der Zustand einer Objektinstanz auf ein Speichermedium bertragen wird. Wenn Sie also etwas serialisieren, dann wandeln Sie es in ein Format um, das sich leicht auf Festplatte speichern oder ber das Internet bertragen lsst. Wird ein Objekt serialisiert, speichert man nicht nur

396

Dateien und Verzeichnisse

die public- und private-Mitglieder des Objekts, sondern auch die Metadaten der Klasse selbst. Auf diese Weise kann ein anderes Programm, eventuell auf einem anderen Rechner, diese Daten bei der Deserialisierung zurcklesen und eine exakte Kopie des Originalobjekts erzeugen. Doch die Probleme werden Ihnen auch diesmal in .NET von der CLR abgenommen und daher lsst sich Serialisierung leicht implementieren. Das folgende Beispiel bernimmt die gleichen Daten, die in Listing 11.4 geschrieben und gelesen wurden, doch diesmal wandeln wir diese Daten in eine Klasse um und berlassen der Serialisierung von .NET das Schreiben und Lesen. Listing 11.5: Serialisierung von Binrdaten
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: Imports Imports Imports Imports Imports Imports System System.Windows.Forms System.Drawing System.IO System.Runtime.Serialization System.Runtime.Serialization.Formatters.Binary

Namespace TYWinForms.Day11 <Serializable()> Class TempRec public loTemp as Short public hiTemp as Short public dateTemp as DateTime End Class Class Listing115 : Inherits Form private btWriteIt As New Button() private btReadIt As New Button() private nudHigh As New NumericUpDown() private nudLo As New NumericUpDown() private dtpDate As New DateTimePicker() private lblHigh As New Label() private lblLo As New Label() Public Sub New() lblLo.Text = "Tiefe Temp" lblLo.Location = new Point(10,10) nudLo.Location = new Point(110,10) nudLo.Minimum = -100 nudLo.Maximum = 130 lblHigh.Text = "Hohe Temp"

397

Arbeiten mit der Windows-Ein-/Ausgabe

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: 76:

lblHigh.Location = new Point(10,40) nudHigh.Location = new Point(110,40) nudHigh.Minimum = -100 nudHigh.Maximum = 130 dtpDate.Location = new Point (10,80) btWriteIt.Text = "Schreiben!" btWriteIt.Location = new Point(30,200) AddHandler btWriteIt.Click, new EventHandler(AddressOf Me.WriteIt) btReadIt.Text = "Lesen!" btReadIt.Location = new Point(120,200) AddHandler btReadIt.Click, new EventHandler(AddressOf Me.ReadIt) Me.Controls.Add(lblLo) Me.Controls.Add(nudLo) Me.Controls.Add(lblHigh) Me.Controls.Add(nudHigh) Me.Controls.Add(dtpDate) Me.Controls.Add(btWriteIt) Me.Controls.Add(btReadIt) End Sub private sub WriteIt(Sender As Object, e As EventArgs) Dim objStream As New FileStream("c:\winforms\day11\records.dat", FileMode.Create) Dim objFormatter As New BinaryFormatter() Dim objTempREc As New TempRec() objTempRec.loTemp = nudLo.Value objTempRec.hiTemp = nudHigh.Value objTempRec.dateTemp = dtpDate.Value objFormatter.Serialize(objStream,objTempRec) objStream.Close() end sub private sub ReadIt(Sender As Object, e As EventArgs) if not File.Exists("c:\winforms\day11\records.dat") then MessageBox.Show("Diese Datei existiert nicht!") Return End If

398

Dateien und Verzeichnisse

77: 78: 79: 80: 81: 82: 83:

Dim objStream = New FileStream("c:\winforms\day11\records.dat", FileMode.Open, FileAccess.Read) Dim objFormatter As New BinaryFormatter() Dim objTempREc As New TempRec() objTempRec = objFormatter.Deserialize(objStream) MessageBox.Show("Hchst- und Tiefsttemperaturen am " & objTempRec.dateTemp.ToString("d") & " waren " & objTempRec.hiTemp. ToString() & " und " & objTempRec.loTemp.ToString() ) objStream.Close() end sub Public Shared Sub Main() Application.Run(new Listing115()) End Sub End Class End Namespace

84: 85: 86: 87: 88: 89: 90: 91: 92:

In den Zeilen 5 und 6 importieren wir die Bibliotheken, die wir fr die Implementierung der Serialisierung bentigen. In Zeile 10 erzeugen wir eine neue Klasse namens TempRec und geben ihr das Attribut Serializable. Dieses Attribut bringt .NET dazu, unserer Klasse automatisch die Serialisierungsfunktionalitt bereitzustellen. In C# wrde das Attribut wie folgt aussehen:
[Serializable] class TempRec

Wir halten die Klasse sehr einfach, so dass sie nur drei ffentliche Felder enthlt, die unsere Daten aufnehmen sollen. In Zeile 59 deklarieren wir in unserer WriteIt-Methode ein Objekt vom Typ BinaryFormatter, wo wir zuvor BinaryWriter verwendet hatten. In Zeile 60 deklarieren wir ein Objekt unseres TempRec-Typs und initialisieren es in den Zeilen 62 und 63 mit Werten aus unseren Steuerelementen. In Zeile 66 rufen wir die Serialize-Methode unseres BinaryFormatter-Objekts auf und bergeben ihr als Parameter unser FileStream-Objekt und unser TempRec-Objekt. Das BinaryFormatter-Objekt wei, wie es den Zustand unseres Objekts im Stream speichern soll. Das beweisen wir uns selbst in unserer ReadIt-Methode, in der wir in den Zeilen 78 und 79 wieder ein BinaryFormatter-Objekt und ein TempRec-Objekt deklarieren. In Zeile 81 lesen wir unser Objekt aus der Datei zurck, wobei wir die Deserialize-Methode des BinaryFormatter-Objekts verwenden. In Zeile 83 geben wir die Ergebnisse aus.

399

Arbeiten mit der Windows-Ein-/Ausgabe

Worin besteht also der Sinn der bung? Sie soll zeigen, dass in .NET nicht nur Daten bertragbar sind, sondern auch Objekte und Klassen. Nun knnen Sie die Datei records.dat an jeden Adressaten schicken: Sie werden nicht nur die gespeicherten Temperaturdaten erhalten, sondern auch die Klasse, die die Daten enthlt. An Tag 15 erfahren Sie etwas ber XML-Serialisierung: Statt Objekte in Binrdaten umzuwandeln, werden sie in XML transformiert. Mehr ber Serialisierung finden Sie in der .NET Framework Dokumentation.

Das Steuerelement RichTextBox


Nun gibt es ein Wiedersehen mit dem Steuerelement RichTextBox (vgl. Tag 6). Damit kann der Benutzer Text wie in einem Textverarbeitungsprogramm eingeben. Sie haben aber noch nicht seine Fhigkeit gesehen, Dateien zu lesen und zu schreiben. Listing 11.6 ist eine vereinfachte Form von Listing 6.9, die das Steuerelement RichTextBox verwendet. Listing 11.6: Wieder mal das Steuerelement RichTextBox
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: Imports System Imports System.Windows.Forms Imports System.Drawing Namespace TYWinForms.Day11 Public Class Listing116 : Inherits Form private rtbText as New RichTextBox public sub New() rtbText.Dock = DockStyle.Fill rtbText.ScrollBars = RichTextBoxScrollBars.Both Me.Text = "Listing 11.6" Me.Font = New Font("Times New Roman", 10) Me.Controls.Add(rtbText) end sub public shared sub Main() Application.Run(new Listing116) end sub End Class End Namespace

400

Dateien und Verzeichnisse

Mittlerweile drfte Ihnen der Code in Listing 11.6 vertraut sein, so dass wir diesmal die Analyse berspringen knnen. Die zwei Methoden, die Ihnen noch nicht begegnet sind, heien LoadFile und SaveFile. Die Methode LoadFile bernimmt den Inhalt entweder einer Datei oder eines Stroms und legt ihn in einem RichTextBox-Steuerelement ab, damit ihn der Benutzer bearbeiten kann. So knnen Sie etwa folgenden Code in den Konstruktor von Listing 11.6 einfgen (der Pfad muss auf eine existente Datei Ihres Computers verweisen):
rtbText.LoadFile("c:\temp\happy.text", RichTextBoxStreamType.PlainText)

Der erste Parameter ist die zuvor erstellte Datei (entweder eine Pfadangabe oder ein Stream), und der zweite teilt dem RichTextBox-Steuerelement mit, welchen Typ von Datei es erwarten soll. Die mglichen Werte der RichTextBoxStreamType-Aufzhlung sind PlainText, RichNoOleObjs (ein RTF-Stream mit Leerzeichen anstelle von OLE-Objekten), RichText, TextTextOleObjs (ein Stream aus einfachem Text mit einer Textdarstellung von OLE-Objekten) und UnicodePlainText. (OLE-Objekte besprechen wir an Tag 14.) Abbildung 11.6 zeigt die neue Ausgabe der Anwendung, der die LoadFile-Methode hinzugefgt wurde.

Abbildung 11.6: Die LoadFile-Methode kann jede textbasierte Datei in ein RichTextBox-Steuerelement laden.

Die SaveFile-Methode bernimmt exakt die gleichen Parameter und ermglicht Ihnen das Schreiben oder Erzeugen einer Datei. Jetzt wissen Sie, dass das Steuerelement RichTextBox ber E/A-Fhigkeiten verfgt.

401

Arbeiten mit der Windows-Ein-/Ausgabe

11.3 Das Objekt FileSystemWatcher


Falls Sie Ihren Computer hinsichtlich nderungen an Dateien oder Verzeichnissen (Ordnern) berwachen mssen, dann ist FileSystemWatcher das Objekt Ihrer Wahl. Es steht sozusagen Wache und beobachtet alles, was in Ihrem System vor sich geht. Passiert etwas, dann lst das Objekt ein Ereignis aus, dem Sie einen Delegaten zuweisen knnen, damit die notwendigen Aktionen erfolgen. Angenommen, Sie haben eine Anwendung entwickelt, die eine Verzeichnisliste anlegt (vgl. Listing 11.1). Fgt ein Benutzer neue Dateien hinzu oder lscht Ordner, dann wird die Strukturansicht (TreeView) nicht aktualisiert, um die nderungen widerzuspiegeln. Mit dem FileSystemWatcher-Objekt knnen Sie jedoch das TreeView-Steuerelement benachrichtigen, dass solche nderungen vorgenommen wurden, und je nach Bedarf Knoten hinzufgen oder entfernen. Um einen FileSystemWatcher einzurichten, mssen Sie die zu berwachenden Dateien und Ordner sowie die zu verfolgenden nderungen angeben und dann das Objekt aktivieren. Das folgende Codestck erledigt alle drei Aufgaben:
FileSystemWatcher objWatcher = new FileSystemWatcher(); //geben Sie die zu berwachenden Dateien und Verzeichnisse an objWatcher.Path = "c:\"; objWatcher.Filter = "*.txt "; objWatcher.IncludeSubdirectories =true; //geben Sie die zu berwachenden nderungen an objWatcher.NotifyFilter = NotifyFilters.LastWrite; //schalten Sie den Beobachter ein objWatcher.EnableRaisingEvents = true;

Die ersten drei Eigenschaften sind praktisch selbsterklrend. Path ist das zu beobachtende Verzeichnis, Filter der zu beobachtende Dateityp in diesem Verzeichnis (also alle Dateien mit der Erweiterung *.txt) und IncludeSubDirectories gibt an, ob auch Unterverzeichnisse berwacht werden sollen. Die nchste Eigenschaft, NotifyFilter, bernimmt Werte aus der NotifyFilter-Aufzhlung. Diese lassen sich mit dem Operator | (OR in VB .NET) kombinieren. Dies knnen folgende Werte sein:

Attributes: Zeigt an, dass sich eine nderung an den Datei- oder Verzeichnisattributen ereignet hat. CreationTime: Zeigt an, dass die Erstellungszeit einer Datei oder eines Ordners gen-

dert wurde (das jeweilige Objekt wurde also erstellt).

402

Das Objekt FileSystemWatcher

DirectoryName: Zeigt, dass sich der Ordnername gendert hat. FileName: Zeigt, dass sich der Dateiname gendert hat. LastAccess: Zeigt an, dass auf eine Datei oder einen Ordner zugegriffen wurde. LastWrite: Zeigt an, dass in eine Datei geschrieben wurde. Security: Zeigt an, dass man die Sicherheitseigenschaften einer Datei oder eines Ordners gendert hat. Size: Gibt an, dass sich die Gre einer Datei oder eines Ordners gendert hat.

Zum Schluss setzen Sie einfach EnableRaisingEvents auf true, um den Beobachter zu aktivieren. Der FileSystemWatcher verfgt ber vier Ereignisse: Changed, Created, Deleted und Renamed. Diesen knnen Sie jeweils einen Delegaten zuweisen, der eine Art der Bearbeitung darstellt. Wir wollen ein einfaches Beispiel erstellen. Listing 11.7 erzeugt eine Anwendung, die auf Ihrem Windows-Desktop erscheinen soll. Sobald sich etwas in Ihrem Dateisystem ndert, lst diese Anwendung einen Alarm aus.

Listing 11.7: Ihre Festplatte berwachen


1: using System; 2: using System.Windows.Forms; 3: using System.Drawing; 4: using System.IO; 5: 6: namespace TYWinforms.Day11 { 7: public class Listing117 : Form { 8: FileSystemWatcher objWatcher = new FileSystemWatcher(); 9: 10: public Listing117() { 11: objWatcher.Path = "c:\\temp"; 12: objWatcher.Filter = "*.*"; 13: objWatcher.IncludeSubdirectories = true; 14: objWatcher.NotifyFilter = NotifyFilters.CreationTime |  NotifyFilters.LastWrite | NotifyFilters.Size; 15: objWatcher.EnableRaisingEvents = true; 16: 17: objWatcher.Changed += new FileSystemEventHandler(this.RaiseAlert); 18: objWatcher.Created += new FileSystemEventHandler(this.RaiseAlert); 19: objWatcher.Deleted += new FileSystemEventHandler(this.RaiseAlert); 20: 21: this.Text = "Listing 11.7"; 22: } 23: 24: private void RaiseAlert(Object Sender, FileSystemEventArgs e) {

403

Arbeiten mit der Windows-Ein-/Ausgabe

25: MessageBox.Show("Ich sehe dich!! Du " + e.ChangeType.ToString() + "  " + e.Name); 26: } 27: 28: public static void Main() { 29: Application.Run(new Listing117()); 30: } 31: } 32: }

Die Zeilen 11 bis 15 richten den FileSystemWatcher ein. In diesem Fall berwacht er die Eigenschaften LastWrite, CreationTime und Size auf Vernderungen. In den Zeilen 17 bis 19 richten Sie die passenden Ereignisse ein, die ausgelst werden sollen, wenn sich eine der erwhnten Eigenschaften ndert. Beachten Sie, dass diese Ereignisse jeweils einen Delegaten vom Typ FileSystemWatcherEventHandler verwenden, so dass Sie im Ereignishandler einen Ereignisparameter des Typs FileSystemEventArgs verwenden. Zeile 24 enthlt diesen Ereignishandler. Er zeigt dem Benutzer eine Meldung an, wenn eine nderung entdeckt wurde, wobei er eine Reihe von Eigenschaften des Objekts FileSystemEventArgs nutzt. ChangeType gibt die Art der erfolgten nderung an. Folgende Werte der Aufzhlung WatcherChangeTypes sind gltig: All (welcher alle nderungsarten umfasst), Changed, Created, Deleted und Renamed. Die Name-Eigenschaft gibt den Verzeichnis- bzw. Dateinamen an, der gendert wurde (alternativ knnten Sie auch FullName verwenden, um den kompletten Verzeichnispfad zu liefern). Die MessageBox-Methode in Zeile 25 liefert eine generische Meldung an den Benutzer. Kompilieren Sie diese Anwendung, fhren Sie sie aus und ndern Sie die Datei happy.txt (bzw. die entsprechende von Ihnen erstellte Datei). Die Anwendung sollte Sie dann alarmieren, wie etwa in Abbildung 11.7 zu sehen. Wenn Sie diese Datei bearbeiten, erhalten Sie von der Anwendung zwei Meldungen, die beide Changed lauten, da Sie ja zwei Eigenschaften der Datei nderten: Size und LastWrite. Denken Sie daran, Ihre NotifyFilter-Typen mit den Ereignissen, die ausgelst werden, zu korrelieren. So ntzt es Ihnen etwa nichts, wenn Sie die NotifyFilter-Eigenschaft auf NotifyFilters.FileName setzen, wenn Sie nicht das Renamed-Ereignis berwachen.

404

Asynchrone Ein-/Ausgabe

Abbildung 11.7: Ertappt!

11.4 Asynchrone Ein-/Ausgabe


Standardmig waren alle E/A-Operationen, die Sie bislang vorgenommen haben, synchron. Das bedeutet, dass Ihre Anwendung nichts anderes tun kann, bis die fragliche E/A-Operation beendet wurde. Sie knnen also dem Benutzer keine Meldung anzeigen, whrend eine Datei gelesen oder geschrieben wird. Bis jetzt war das synchrone Verfahren kein Problem. Alle Arbeitsschritte wurden relativ rasch erledigt, so dass Sie sich nicht den Kopf darber zerbrechen mussten, dass die Anwendung darauf wartet, mit anderen Arbeitsschritten fortzufahren, whrend die jeweilige E/A-Operation zu Ende gefhrt wird. In manchen Fllen kann man jedoch nicht darauf warten, bis die Ein-/Ausgabe fertig ist. Das trifft zum Beispiel zu, wenn man den Inhalt einer sehr umfangreichen Datei einliest oder darauf wartet, dass ein sehr langsames Netzwerk einen Stream bereitstellt. Diese Ablufe knnen mitunter sehr lange dauern. Bei synchronen Ablufen bleibt Ihnen keine andere Wahl, als den Benutzer Ihrer Anwendung warten zu lassen, bis die jeweiligen Vorgnge abgeschlossen sind. Um diesen Konflikten entgegentreten zu knnen, gibt Ihnen .NET die Mglichkeit zu asynchronen Operationen. Damit erfolgen Ihre E/A-Ablufe abgetrennt vom Rest der Anwendungsausfhrung, so dass Ihre Anwendung nicht auf den Abschluss langwieriger Vorgnge warten muss. Stellen Sie sich vor, dass alle Arbeitsschritte, die in Ihrer Anwendung ablaufen mssen (Initialisierung, Ereignisverarbeitung, Erstellen von Steuerelementen usw.) sich in einer Warteschlange anstellen, um ausgefhrt zu werden. Bei asynchroner Arbeitsweise treten die wichtigsten Prozesse aus der Warteschlange heraus, erledigen die ihnen zugeteilten Aufgaben und reihen sich dann wieder ein.

405

Arbeiten mit der Windows-Ein-/Ausgabe

Asynchrone Operationen sind ein komplexes Thema, das gewhnlich erfahrenen Benutzern vorbehalten bleibt, so dass wir nicht im Detail darauf eingehen. Das Erstellen einer asynchronen Anwendung erfordert hufig eine radikale Abkehr von den Entwurfsmustern, die Sie gewhnt sind, und wird in der Realitt auch nicht sehr hufig eingesetzt. Momentan gengt es zu wissen, dass Streams zustzlich zu ihren normalen Lese-/Schreibmethoden auch ber asynchrone Versionen dieser Methoden verfgen.

11.5 Drucken unter .NET


Das letzte E/A-Thema fr heute ist das Drucken. Um etwas drucken zu knnen, muss man durch ein paar mitunter seltsame Schleifen springen. Doch der grundlegende Funktionsumfang besteht aus dem PrintDocument-Objekt und dem PrintPage-Ereignis. Anders als sein Name suggeriert, ist PrintDocument keineswegs ein zu druckendes Dokument. Vielmehr ist es so etwas wie der Verkehrspolizist, der steuert, wie ein Dokument gedruckt wird. Es existiert, um den Vorgang anzustoen und fr einen reibungslosen Ablauf zu sorgen. Sobald etwas zu drucken ist, lst das PrintDocument das PrintPage-Ereignis aus und berlsst es Ihnen, dieses Ereignis abzufangen und das Dokument tatschlich zu drucken. Bevor Sie dies lernen, schauen wir uns die Grundlagen an. Angenommen, Sie wollen dem Benutzer gestatten, den Inhalt der RichTextBox in Listing 11.6 zu drucken. (In den folgenden Beispielen verwenden wir C#-Code, so dass Sie gegebenenfalls von VB .NET in C# bzw. umgekehrt konvertieren mssen, damit die angegebenen Codefragmente funktionieren.) Zuerst sorgen Sie dafr, dass die ntigen Namensrume importiert werden:
using System.Drawing.Printing;

Dann fgen Sie ein Menelement hinzu, so dass der Benutzer das Dokument drucken kann:
MainMenu mnuMain = new MainMenu(); MenuItem miFile = new MenuItem("Datei "); MenuItem miPrint = new MenuItem("Drucken "); miPrint.Click += new EventHandler(this.PrintClicked); mnuMain.MenuItems.Add(miFile); miFile.MenuItems.Add(miPrint);

Im Ereignishandler PrintClicked knnen Sie das Dialogfeld PrintDialog anzeigen, wenn der Benutzer seine Druckeinstellungen festlegen knnen soll, bevor er tatschlich druckt. (Informationen zum Konfigurieren der Druck- und Seiteneinstellungen finden Sie an Tag 7.) Im Moment berspringen wir diesen Schritt und drucken einfach das Dokument. Der Ereignishandler sollte wie im folgenden Codestck aussehen:

406

Drucken unter .NET

private void PrintClicked(Object Sender,EventArgs e){ PrintDocument pd = new PrintDocument(); pd.PrintPage += new PrintPageEventHandler(this.pd_PrintPage); pd.Print(); }

Beachten Sie diese drei Codezeilen: das Anlegen eines PrintDocuments, die Zuweisung des Delegaten zum PrintPage-Ereignis und den Aufruf des Print-Befehls. Sie mssen noch nicht einmal angeben, was zu drucken ist. Sobald Sie eine pd_PrintPage-Methode erstellt haben, knnen Sie diese Anwendung kompilieren und ausfhren. Ihr Drucker wird reagieren, wenn Sie auf das DRUCKEN-Men klicken. Doch momentan wird nichts auer einer leeren Seite ausgegeben es gibt nichts zu drucken. Fr jede zu druckende Seite wird das PrintPage-Ereignis ausgelst, so dass Sie das Dokument letztendlich in diesem Ereignishandler drucken. Wir schauen uns einmal den Ereignishandler in Listing 11.8 an. Listing 11.8: Der PrintPage-Ereignishandler
1: private void pd_PrintPage(Object Sender, PrintPageEventArgs e) { 2: StringReader objReader = new StringReader(rtbText.Text); 3: 4: Font fntPrint = this.Font; 5: int count = 0; 6: float yPos = 0; 7: float lpp = e.MarginBounds.Height / fntPrint.GetHeight(e.Graphics); 8: float fltTopMargin = e.MarginBounds.Top; 9: float fltLeftMargin = e.MarginBounds.Left; 10: String strLine = null; 11: 12: while (count < lpp && ((strLine = objReader.ReadLine()) != null)) { 13: yPos = fltTopMargin + (count * fntPrint.GetHeight(e.Graphics)); 14: 15: e.Graphics.DrawString(strLine, fntPrint, Brushes.Black, fltLeftMargin,  yPos, new StringFormat()); 16: 17: count++; 18: } 19: 20: if (strLine != null) { 21: e.HasMorePages = true; 22: } else { 23: e.HasMorePages = false; 24: } 25: }

407

Arbeiten mit der Windows-Ein-/Ausgabe

Beachten Sie, dass dieser Ereignishandler ein PrintPageEventArgs-Objekt erfordert. Es enthlt die fr das Drucken notwendigen Eigenschaften. Zeile 2 holt den Text aus der frher erstellten RichTextBox. Sie mssen die zu druckende Information in einen Stream bringen oder in einen Reader, weil dies die einzigen Dinge sind, die .NET drucken kann. Darauf folgt einiges an Initialisierung. Sie mssen einige Daten ber die zu bedruckende Seite kennen, bevor es losgehen kann. Sie mssen wissen, wo der Druckvorgang beginnen kann, wo die Rnder liegen, wie viel auf eine einzelne Seite passt, wie hoch die Druckqualitt sein soll usw. Die Druckfunktionen von .NET sind alle im Namensraum System.Drawing abgelegt. Doch im Gegensatz zum Malen (Drawing) mssen Sie beim Drucken stets in der ersten Zeile anfangen (selbst wenn es nichts zu drucken gibt) und dann zeilenweise weitermachen. Wenn Sie mit der Arbeitsweise eines Druckers vertraut sind, wird Ihnen dieser Ablauf einleuchten. Der Druckkopf bewegt sich horizontal ber eine Seite, druckt Zeile fr Zeile und kehrt dann an den Anfang zurck, um die nchste Zeile zu drucken, hnlich wie eine Schreibmaschine. Deshalb mssen Sie den Druckvorgang in der gleichen Weise angehen. Zeile 4 ermittelt die Schriftart, mit der zu drucken ist; in diesem Fall verwenden Sie die gleiche Schriftart, die die RichTextBox verwendet, so dass das gedruckte Dokument genauso aussehen wird wie das Dokument am Bildschirm. Zeile 5 initialisiert eine Variable, die Sie verwenden, um sich per Schleife durch die Zeilen des Dokuments zu bewegen (gleich mehr dazu). Zeile 6 verrt uns, wo Sie zu drucken anfangen wollen: die y-Koordinate. Dieser Wert ist anfangs null und bercksichtigt nicht die Hhe der Rnder (gleich mehr zu den Rndern). Mit dem Ereignishandler in Listing 11.8 lsst sich nur eine Seite bedrucken. Ist Ihr Dokument lnger, dann wird der Ereignishandler erneut ausgefhrt und holt das Dokument noch einmal. Der Rest folgt automatisch und Ihre Anwendung tritt in eine endlose Druck-Schleife ein. Daher empfiehlt es sich, den Code in Zeile 2 auerhalb der PrintPage-Ereignishandlermethode unterzubringen. Zeile 7 bestimmt die Anzahl der Zeilen pro Seite. Diese Zahl wird bestimmt, indem man die Seitenhhe durch die Hhe der verwendeten Schrift dividiert. Die Angabe der Seitenhhe wird vom PrintPageEventArgs-Objekt geliefert. Denken Sie daran, die Hhe der Rnder in Ihrer Berechnung zu bercksichtigen, was der Grund dafr ist, dass Zeile 7 die Hhe der MarginBounds-Eigenschaft bestimmt. Das Font-Objekt verfgt ber eine GetHeight-Methode, welche die Hhe der aktuell verwendeten Schrift bestimmt. Um den Parameter fr GetHeight brauchen Sie sich noch nicht zu kmmern.

408

Drucken unter .NET

Die Zeilen 8 und 9 bestimmen jeweils die linken und oberen Rnder. Zeile 10 schlielich initialisiert eine Variable, die Sie gleich verwenden werden. Denken Sie daran, dass das Drucken zeilenweise erfolgt. Daher mssen Sie eine Schleife einrichten, die durch die Zeilen im von Ihnen eingerichteten Reader iteriert. Dies tut die while-Schleife (Zeile 12). Wenn sie das Seitenende erreicht (welches durch die Angabe der Zeilen pro Seite definiert ist) oder wenn es keinen Inhalt mehr zu drucken gibt, endet die Schleife. Beachten Sie die Shortcut-Syntax in Zeile 12; Sie knnen direkt im whileStatement einer Variablen einen Wert zuweisen. Sie verwenden ReadLine im Reader, um die aktuell zu druckende Zeile zu liefern. Die Zeile 13 bestimmt, wo genau die aktuelle Zeile auf der Seite gedruckt werden soll. Sie addieren die Randbreite und rechnen dann aus, wie viele Zeilen Sie bereits gedruckt haben, indem Sie Ihren Zeilenzuwachs mit der Schrifthhe multiplizieren. Zum Schluss sind Sie endlich bereit, die Seite zu drucken (Zeile 15). .NET behandelt die zu druckende Seite wie eine Leinwand. Um eine Zeile zu drucken, rufen Sie die Methode DrawString des Graphics-Objekts auf, das mit Ihrem Drucker verknpft ist. (Diese Begriffe ergeben ab Tag 13 etwas mehr Sinn). DrawString bernimmt sechs Parameter in der folgenden Reihenfolge:

String: Die auf der Seite zu zeichnende Zeichenfolge. Font: Die Schriftart fr das Drucken der Zeichenfolge. Brush: Ein Objekt, das dem Drucker mitteilt, wie die Zeichenfolge zu drucken ist (in welcher Farbe, ob ausgefllt oder mit Verlauf usw.) Mehr zu diesem Objekt erfahren Sie an Tag 13. X: Die x-Koordinate fr den Druckbeginn. Y: Die y-Koordinate fr den Druckbeginn. StringFormat: Ein Objekt, das die zu druckende Zeichenfolge formatiert (vertikale

oder horizontale Ausrichtung bzw. Hoch- oder Querformat etc.). Sie kennen bereits die meisten dieser Parameter, so etwa String, X und Y. Fr Brush und StringFormat knnen Sie generische Werte benutzen, wie in Zeile 15 gezeigt. In Zeile 17 inkrementieren Sie den Zhler, der Ihnen verrt, welche Zeile gerade gedruckt wird. Tabelle 11.4 fhrt Sie durch die komplette while-Schleife in den Zeilen 12 bis 18.

409

Arbeiten mit der Windows-Ein-/Ausgabe

Ausgabedatei, Zeilennummer 1 2 3 4 5 6

x- und y-Koordinaten der Zeile 0, oberer Rand

Zeileninhalte (aus der weiter vorn in dieser Lektion erzeugten Datei happy.txt)
Hallo Welt!

0, oberer Rand + Hhe von Zeile 1 Hier ist noch mehr 0, oberer Rand + Hhe von Zeile 1 Text, den ich hier eingefgt habe + Hhe von Zeile 2 0, oberer Rand + Hhe der Zeilen 1, 2, 3 0, oberer Rand + Hhe der Zeilen 1, 2, 3, 4 0, oberer Rand + Hhe der Zeilen 1, 2, 3, 4, 5
um die Applikation

in Listing 11.3 zu testen.

Null

Tabelle 11.4: Der Druckvorgang

Wir wollen uns den Rest des Codes in Listing 11.8 ansehen:
20: 21: 22: 23: 24: if (strLine != null){ e.HasMorePages = true; } else { e.HasMorePages = false; }

Das Objekt PrintPageEventArgs hat eine HasMorePages-Eigenschaft, die angibt, ob es noch mehr Seiten zu drucken gibt. Ist dies der Fall, wird ein weiteres PrintPage-Ereignis ausgelst und der gesamte Druckvorgang beginnt von neuem. Leider wei die Anwendung selbst nicht, ob es mehr Seiten zu drucken gibt; das mssen Sie selbst ausrechnen. Daher bestimmen Sie in Zeile 20, ob die letzte Zeichenfolge, die aus der RichTextBox gelesen wurde, null ist. Ist das der Fall, heit das, dass es keine Zeilen mehr zu drucken gibt und man fertig ist. An diesem Punkt setzen Sie HasMorePages auf false. Wenn Sie aber mehr Text auf einer weiteren Seite zu drucken haben, dann lsen Sie wieder PrintPage aus. In diesem Fall setzen Sie HasMorePages auf true (Zeile 21). Beachten Sie, dass dieser Codeabschnitt nach der while-Schleife folgt, so dass die aktuelle Seite bereits vollstndig gedruckt worden ist (gem dem Zeilen/Seite-Wert bzw. dann, wenn der Inhalt der RichTextBox komplett gedruckt ist). Dieser Ablauf ist recht kompliziert, daher wollen wir ihn zusammenfassen: 1. Erstellen Sie eine Mglichkeit, wie der Benutzer das Dokument drucken kann (also das Menelement).

410

Zusammenfassung

2. Erzeugen Sie das PrintDocument-Objekt, weisen Sie dem PrintPage-Ereignis einen Ereignishandler zu und rufen Sie PrintDocument.Print auf. 3. Bestimmen Sie im PrintPage-Ereignishandler die Eigenschaften der Seite, darunter die Zeilen/Seite, die Randbreite und die zu verwendende Schriftart. Erstellen Sie ein Stream- oder Readerobjekt, das den zu druckenden Inhalt enthlt. 4. Durchlaufen Sie in einer Schleife die Zeilen des Streams oder Readers, bis Sie den Zeilen/Seite-Wert oder das Ende des Readers erreicht haben. Legen Sie die y-Koordinate fr den Druckbeginn in jeder Zeile fest. Drucken Sie die Zeile, indem Sie Graphics.DrawString im PrintPageEventArgs-Objekt aufrufen und ihm die notwendigen Parameter bergeben. 5. Bestimmen Sie, ob Sie noch mehr Inhalt zu drucken haben, und legen Sie HasMorePages entsprechend fest. Wenn Sie dem Benutzer erlaubt haben, die Seiteneinstellungen zu ndern, knnen Sie die genderten Werte dem PrintDocument zuweisen, wie im folgenden Beispiel:
PageSettings objSettings = new PageSettings(); PrinterSettings objPrinter = new PrinterSettings(); //fgen Sie hier Code ein, um die Einstellungen anzupassen //(mglichweise mit Standarddialogfeldern) PrintDocument pd = new PrintDocument(); pd.DefaultPageSettings = objSettings; pd.PrinterSettings = objPrinter;

Sobald das PrintPage-Ereignis ausgelst wird, enthlt das PrintPageEventArgs-Objekt die neuen Einstellungen (wie etwa Randnderungen usw.). Sie knnen einen Druckauftrag auch lschen, indem Sie die Cancel-Eigenschaft des PrintPageEventArgs-Objekts auf true setzen.

11.6 Zusammenfassung
Sie haben nun von Streams und ihrer Rolle bei E/A erfahren. Jedes Mal, wenn Sie es mit E/A unter .NET zu tun haben, gehen Sie mit Streams um, die quellenunabhngige Informationsspeicher darstellen, denen Sie Writer und Reader zuweisen knnen, um deren Inhalte zu bearbeiten. Obwohl es zahlreiche verschiedene Arten von Streams, Readern und Writern gibt, ist ihre Arbeitsweise doch stets hnlich. Um eine Datei zu ffnen, erstellen Sie fr sie einfach einen Stream mit den erforderlichen
FileMode-, FileAccess- und FileShare-Aufzhlungswerten. Die Anwendung kann alle

Funktionen handhaben, um festzustellen, ob die Datei existiert, ob sie berschrieben oder

411

Arbeiten mit der Windows-Ein-/Ausgabe

erstellt werden soll usw. Als Nchstes weisen Sie einen Reader oder Writer des passenden Typs zu (StreamReader und StreamWriter fr die meisten Textdateien) und rufen eine der folgenden E/A-Methoden auf: Write, WriteLine, ReadLine, Read, ReadBlock, ReadToEnd usw. Das RichTextBox-Objekt verfgt ber eingebaute E/A-Funktionen. Sie mssen lediglich die LoadFile- und SaveFile-Methoden aufrufen, um ein vorhandenes Dokument bearbeiten zu knnen. Das FileSystemWatcher-Objekt erweist sich als ntzlich, wenn es darum geht, die vor sich gehenden nderungen in Ihrem Dateisystem zu berwachen. Sie knnen sich jedes Mal, wenn eine Datei erstellt, gendert, umbenannt oder sonst wie verndert wird, benachrichtigen lassen und dann entsprechend darauf reagieren. Dieses Objekt sollte man nicht fr Sicherheitszwecke missbrauchen; dafr gibt es viel besser geeignete Objekte. Zum Schluss haben Sie das Drucken von Inhalten erlernt. Drucken ist keineswegs ein intuitiv verstndlicher Prozess. Sie erstellen ein PrintDocument-Objekt und rufen eine Print-Methode auf, die wiederum Ereignisse auslst, in welche Sie eine Schleife einflechten mssen, die durch den zu druckenden Inhalt fhrt. Schlielich zeichnen Sie den Inhalt auf das Papier. Nachdem Sie das gelernt haben, knnen Sie den gleichen Code zum Glck fast berall einsetzen, denn die Drucklogik ndert sich recht selten.

11.7 Fragen und Antworten


F Kann ich Streams, Writer und Reader oder auch eine RichTextBox dazu verwenden, Word-Dokumente zu bearbeiten? A Nicht ganz. Word-Dokumente sind kein reiner Text, sondern verwenden vielmehr ein Binrformat, das exklusiv von Microsoft entwickelt wurde. Dieses Format erlaubt Microsoft nicht nur, eigene Funktionen in Word-Dokumente einzubauen, sondern stellt auch sicher, dass nur Benutzer, die Word (oder die Lizenz dafr) besitzen, Word-Dokumente bearbeiten knnen. Sie knnen binre Reader und Writer dazu einsetzen, Word-Dokumente zu bearbeiten. Doch alle Dateien weisen ein spezifisch geordnetes Format auf. Wenn Sie also nicht ganz genau wissen, wie ein Word-Dokument intern aufgebaut ist, drfte Ihre Mhe umsonst sein. Microsoft gibt die Informationen ber das binre Dateiformat an Unternehmen weiter, die Word lizenzieren mchten. Wenn Sie zu einer solchen Firma gehren, dann viel Erfolg! F Ich habe von isolierter Speicherung in .NET gehrt. Was versteht man darunter? A Isolierte Speicherung ist ein interessantes Konzept. Damit ist gemeint, dass jeder Benutzer Ihrer Anwendung ber sein eigenes dediziertes Repository (Speichersystem) fr solche Dokumente verfgt, die whrend der Anwendungsausfhrung

412

Workshop

bearbeitet werden. Dieses Repository ist sicher und vom Betriebssystem beschtzt, so dass sich vertrauliche Daten an diesem Speicherplatz ablegen lassen. Sie knnen zudem Kontingente fr die dort speicherbare Datenmenge einrichten. Isolierte Speicherung wird nur selten in Desktop-Anwendungen eingesetzt. Sie ist gebruchlicher in Internet-Applikationen, bei denen viele Benutzer auf einen Rechner zugreifen.

11.8 Workshop
Dieser Workshop soll Ihnen helfen, sich besser an die heute behandelten Begriffe zu erinnern. Es hilft Ihnen sehr, die Antworten vollstndig zu verstehen, bevor Sie weiterlesen. Die Antworten zu den Quizfragen und den bungen finden Sie in Anhang A.

Quiz
1. Worin besteht der Unterschied zwischen den Read- und Peek-Methoden? 2. Was ist eine rekursive Funktion und wann erweist sie sich als hilfreich? 3. Worin besteht der Unterschied zwischen synchroner und asynchroner Arbeitsweise? 4. Zu welchem Namensraum gehrt die Druckfunktionalitt? 5. Welche sind die vier Ereignisse des FileSystemWatcher-Objekts? 6. Welche sind die Werte der FileMode-Aufzhlung ?

bung
Modifizieren Sie Listing 11.1 so, dass die TreeView-Knoten jedes Mal dynamisch erstellt werden, wenn ein Benutzer einen Knoten expandiert, anstatt alle whrend der Initialisierungsphase der Anwendung erstellen zu lassen. Fgen Sie auerdem eine benutzerdefinierte Druckfunktion hinzu, um die sichtbaren Inhalte des TreeView-Steuerelements drucken zu knnen. (Tipp: Sie mssen die Inhalte der Strukturansicht erst in einer passenden Variablen sammeln, um sie drucken zu knnen.)

413

Formulare Internetfhig machen

2 1

Formulare Internet-fhig machen

Das Internet ist mittlerweile ein zentraler Aspekt der Computerverwendung. Daher wird es nur den Tatsachen gerecht, wenn Sie das Internet entsprechend in Ihren Anwendungen bercksichtigen. Dank .NET ist die Integration von Internetfunktionen ein Kinderspiel. Heute werfen Sie einen Blick auf verschiedene Internetprotokolle und darauf, wie man sie unter .NET einsetzt. Sie werden erkennen, dass Sie damit verschiedene Aufgaben leicht erledigen knnen, von einfachen Webanfragen bis zum Zugriff auf sichere Webserver. In krzester Zeit werden Sie webfhige Anwendungen auf die Beine stellen knnen. Heute lernen Sie,

was Internet- und austauschbare Protokolle sind, wie das WebRequest-Objekt die Arbeit erleichtert, was ASP.NET ist, auf welche Weise .NET mit dem Internet Explorer integriert ist, wie man auf sichere Websites zugreift.

12.1 Internetprotokolle
Wenn Sie schon einmal das Internet benutzt haben, dann haben Sie wohl auch bereits von den verschiedenen Protokollen gehrt, darunter IP, FTP, HTTP, UDP und TCP. Sie werden alle eingesetzt, damit das Internet funktioniert. Das bekannteste Protokoll ist das Hypertext Transfer Protocol (HTTP). Es wird am hufigsten in Browsern wie dem Internet Explorer oder dem Netscape Communicator eingesetzt. Man benutzt es, um Hypertext Markup Language- (HTML-) Seiten zur Anzeige auf den Browser zu holen. Ohne HTTP wre das Internet wahrscheinlich nicht das, was Sie heute kennen. Das File Transfer Protocol (FTP) ist ebenfalls recht gebruchlich. FTP wird hauptschlich fr die Dateibertragung zwischen Computern eingesetzt. Es wurde so entworfen, dass es fr die bertragung groer Dateien effizienter ist als HTTP. Es gab einmal eine Zeit, zu der man alle Tricks dieser Protokolle kennen musste, um einer Anwendung Internetfhigkeiten hinzuzufgen. Jedes unterscheidet sich von den anderen, so dass sich das Problem vergrert, wenn man mehrere zusammen einsetzen will. .NET vereinfacht diese Aufgabe fr den Entwickler. Wir wollen uns zunchst noch ansehen, wie das Internet arbeitet. Ein Groteil der Internetanwendungen fut auf dem Client/Server-Modell (auch unter der Bezeichnung Request/Response- bzw. Anforderung/Antwort-Modell bekannt). In diesem

416

Internetprotokolle

Paradigma dient ein Rechner, der so genannte Server, als Informationsspeicher und andere Rechner dienen als Verbraucher dieser Informationen und werden als Clients bezeichnet. Der Client schickt eine Anforderung (Request; via HTTP) an den Server und dieser antwortet (Response), indem er die angeforderten Daten zurck an den Client schickt (wiederum via HTTP; vgl. Abbildung 12.1). Konkret ausgedrckt: (1.) Sie besuchen eine Website, wo Sie als Client auftreten, (2.) Ihr Browser schickt eine HTTP-Anforderung an den Server und (3.) diese Website schickt die angeforderte Seite. Das Gesamtkonzept ist einfach, aber effizient in seiner Arbeitsweise. Beachten Sie, dass sowohl der Client als auch der Server beliebige Rechner sein knnen.
SERVER BROWSER Anforderung
Verarbeitung der Anforderung

Antwort

Abbildung 12.1: Im Client/Server-Modell speichert ein Computer (Server) Informationen und ein anderer (Client) ruft sie ab.

Wenn Millionen von Clients und Servern mit dem Internet verbunden sind, kann sich die Suche nach einem bestimmten Server recht schwierig gestalten. Daher wurde das System der Universal Resource Identifier (URI; manchmal wird auch URL verwendet, siehe den Hinweis unten) entwickelt; dieses System bildet den Schlssel zu den Internetfunktionen in .NET. Sie haben sicher schon einmal URIs wie den folgenden gesehen:
http://www.clpayne.com/default.asp?blah=bloh

Ein Uniform Resource Locator (URL), mit dem viele Menschen vertrauter sind, ist eine Form des URI, die bei Netzwerkprotokollen arbeitet, um bestimmte Adressen zu finden. Der URI besteht aus vier Teilen. Der erste, nmlich http://, gibt das Protokoll an, das verwendet werden soll, wenn Ressourcen vom Server angefordert werden. Dieser Teil wird auch Schemabezeichner (Schema Identifier) genannt. (Sie brauchen sich diesen Begriff nicht zu merken.) Der nchste Teil, www.clpayne.com, ist der Name des Servers (im Web). Das kann ein benutzerfreundlicher Name wie der hier gezeigte sein (dank des Domain Naming Systems oder DNS) oder einfach eine IP-Adresse wie etwa 127.0.0.1. Dieser Servername ist im Wesentlichen die Adresse des Servers, hnlich wie Ihre Hausadresse. Der Unterschied: Dieser Servername soll im gesamten Internet nur ein einziges Mal (unique) vorkommen, also eindeutig sein (leider kommen Konflikte dennoch vor).

417

Formulare Internet-fhig machen

Der folgende Teil, nmlich /default.asp, ist der Pfadbezeichner (Path Identifier), der dem Server verrt, was genau der Client nun eigentlich anfordert. In diesem Fall ist es eine Active Server Page namens default.asp. Vielfach handelt es sich auch um eine HTMLSeite wie etwa index.html. Der vierte Teil ist eine optionale Abfragezeichenfolge. Diese Zeichenfolge, nmlich
?blah=bloh, wird dem Ende des Pfadbezeichners durch ein Fragezeichen angehngt und

gibt alle zustzlichen Parameter an, die der Server fr die Verarbeitung der Anfrage bentigen knnte. Wie Sie wahrscheinlich erwartet haben, sind alle Teile durch Klassen im .NET Framework reprsentiert.

Austauschbare Protokolle
Die wichtigsten Internetklassen fr Windows Forms-Anwendungen unter .NET sind WebRequest und WebResponse. WebRequest schickt Anforderungen mit einem URI an einen Server und WebResponse sendet Informationen zurck an den anfragenden Client. Das Interessante an diesen Klassen (und anderen .NET-Internetklassen) ist, dass sie mit jedem Protokoll zusammenarbeiten, so dass Sie Ihre Anwendung nicht ndern oder neuen Code schreiben mssen, nur um ein anderes oder neues Protokoll verwenden zu knnen.
WebRequest und WebResponse dienen als eine Art Verkehrspolizisten, hnlich der SqlDataAdapter-Klasse von Tag 9. Bitte erinnern Sie sich, dass SqlDataAdapter verschiedene SqlCommand-Objekte erzeugte, je nachdem, welche Art von SQL-Abfrage ausgefhrt wurde. Wenn Sie WebRequest verwenden, um eine Information vom Server anzufordern, registriert

das Objekt automatisch das Protokoll, das Sie verwenden wollen, und erzeugt das notwendige Helferobjekt, um die Anforderung zu verarbeiten (so etwa ein HttpWebRequest-Objekt fr HTTP-Anforderungen). Weil diese Helferobjekte dynamisch von einem Verkehrsobjekt abgeschaltet werden knnen, sind sie als austauschbare Protokolle (Pluggable Protocols, wrtlich: einsteckbare Protokolle) bekannt. .NET steckt bei Bedarf die jeweils ntige Klasse ein. Auf diese Weise knnen Sie eine Anwendung vom BrowserTyp allein mit WebRequest aufbauen. Sie brauchen sich auch nicht darum zu kmmern, welche Adressart der Benutzer in Ihren Browser eingibt, denn WebRequest passt sich dem dynamisch an. Dies ist die Grundlage fr Internetanwendungen unter .NET: Einfachheit und Leichtigkeit fr den Entwickler, whrend fr den Benutzer die Leistungsfhigkeit beibehalten wird.

418

Clients und Server

12.2 Clients und Server


Wie bereits besprochen, dient ein Server als Repository fr Informationen und ein Client als Benutzer dieser Information. Weil ein Groteil des Internets aus Servern und Clients besteht, werden Sie meist diese beiden Anwendungstypen entwickeln. In den nchsten Abschnitten betrachten wir, wie man Clients erstellt, die mit Servern zusammenspielen; Serveranwendungen sind hufig ASP.NET vorbehalten. Fr einige der folgenden Beispiele brauchen Sie einen Webserver, der Ihre Anforderungen empfangen kann. In manchen Fllen ist ein ffentlich zugnglicher Server wie www.clpayne.com oder www.microsoft.com ausreichend, doch manchmal brauchen Sie einen, mit dem Sie experimentieren knnen. Es ist wahrscheinlich, dass auf Ihrem Computer bereits ein Webserver luft, nmlich Internet Information Server (IIS) von Microsoft. Um dies herauszufinden, geben Sie in der Adresszeile des Browsers einfach http://localhost ein. Erscheint eine Fehlerseite, luft noch kein Server, so dass Sie IIS zunchst installieren mssen. Sollte eine Begrungsseite erscheinen oder gar eine Webseite, dann sind Sie schon startbereit. Um den IIS einzurichten, bentigen Sie Ihre Windows-Installations-CD. IIS ist eine optionale Komponente, die Sie ber die SYSTEMSTEUERUNG und die Seite SOFTWARE/HINZUFGEN/ENTFERNEN installieren knnen. Klicken Sie auf die Schaltflche HINZUFGEN/ENTFERNEN, whlen Sie die Optionen fr den IIS, klicken Sie auf WEITER und folgen Sie dann den Anweisungen, um mit dem IIS-Einsatz anfangen zu knnen.

Anforderungen mit WebRequest senden


Das Senden einer Anforderung ist nicht schwer. In der einfachsten Form braucht man nur ein paar neue Zeilen Code, um diese Funktion zu verwenden. Wir beginnen mit Listing 12.1. Listing 12.1: Ihre erste Internet-Applikation
1: 2: 3: 4: 5: 6: 7: using using using using using System; System.Windows.Forms; System.Drawing; System.Net; System.IO;

namespace TYWinforms.Day12 {

419

Formulare Internet-fhig machen

8: public class Listing121 : Form { 9: private RichTextBox rtbWeb = new RichTextBox(); 10: 11: public Listing121() { 12: CallServer(); 13: 14: rtbWeb.Multiline = true; 15: rtbWeb.Dock = DockStyle.Fill; 16: 17: this.Text = "Listing 12.1"; 18: this.Size = new Size(800,600); 19: this.Controls.Add(rtbWeb); 20: } 21: 22: private void CallServer() { 23: WebRequest objRequest = WebRequest.Create("http://  www.clpayne.com"); 24: WebResponse objResponse = objRequest.GetResponse(); 25: 26: StreamReader objReader = new  StreamReader(objResponse.GetResponseStream()); 27: 28: rtbWeb.Text = objReader.ReadToEnd(); 29: 30: objReader.Close(); 31: objResponse.Close(); 32: } 33: 34: public static void Main() { 35: Application.Run(new Listing121()); 36: } 37: } 38: }

Die ersten 20 Zeilen sind im Grunde Standard. In Zeile 4 achten Sie bitte auf die Verwendung des zustzlichen Namensraums System.Net, der alle ntigen Internetklassen enthlt. Die eigentliche Internetanforderung erfolgt in dem Aufruf der angepassten CallServer-Methode. Der erste Schritt zur Erstellung einer Internetverbindung ist die Erzeugung eines WebRequest-Objekts, wie in Zeile 23 gezeigt. Die einfachste Mglichkeit dazu besteht im Aufruf einer statischen Create-Methode, wobei man den URI der Internetressource bergibt, auf die man zugreifen mchte. (Dieser Wert ist auch ber die RequestUri-Eigenschaft der WebRequest-Klasse verfgbar ndern Sie einfach die URI-Zeichenfolge in die gewnschte Adresse und sorgen Sie

420

Clients und Server

dafr, dass Sie eine Verbindung zum Internet haben.) Beachten Sie, dass Sie noch keine Verbindung hergestellt, sondern nur die Vorbereitungen dafr getroffen haben. In Zeile 24 stellen Sie die Verbindung her. Mit dem eben erstellten WebRequestObjekt rufen Sie GetResponse auf und speichern das Ergebnis in einem WebResponse-Objekt. Als Nchstes holen Sie die Antwort des Servers aus dem WebResponse-Objekt, indem Sie GetResponseStream aufrufen, welcher ein Streamobjekt liefert. Zeile 26 ordnet diesem Stream einen Reader zu, so dass Sie auf die Inhalte zugreifen knnen. In Zeile 28 lesen Sie den Inhalt des Streams mit Hilfe des Readers und legen den Inhalt im Steuerelement RichTextBox ab. Vergessen Sie nicht, sowohl den Reader als auch das WebRequest-Objekt zu schlieen. Abbildung 12.2 zeigt die Anwendung, nachdem sie die Webanforderung verarbeitet hat.

Abbildung 12.2: Nach der Ausfhrung einer Webanforderung erhalten Sie diese Ergebnisse.

Was haben wir vorliegen? Abbildung 12.2 zeigt den Inhalt, der von der Website www.clpayne.com geholt wurde. Das ist keine bunte Webseite, sondern vielmehr ihr HTML-Code. Sie sehen den HTML-Inhalt, weil jede Anwendung dies sieht, wenn sie eine Anforderung an einen Server abschickt. Der eigentliche Darstellungsteil ist eine separate Funktion.

421

Formulare Internet-fhig machen

Lassen Sie uns die beteiligten Schritte rekapitulieren: 1. Erstellen Sie ein WebRequest-Objekt mit WebRequest.Create. 2. Rufen Sie GetResponse auf, um die Verbindung zu ffnen. 3. Rufen Sie GetResponseStream auf, um den Inhalt zu holen, wobei Sie bei Bedarf einen Reader anhngen. 4. Schlieen Sie die Webobjekte (und gegebenenfalls den Reader). Web-basierte Interaktionen sttzen sich ebenso wie E/A-Operationen auf Streams. Sie eignen sich aus mehreren Grnden hervorragend frs Web. Bei Verwendung eines Streams brauchen Sie nicht die gesamte Ressource herunterzuladen, bevor Sie etwas damit tun knnen. Sie knnen den Stream lesen, whrend er noch hereinkommt (hnlich wie das Betrachten eines Streaming-Videos, bevor es ganz heruntergeladen ist). Bedenken Sie auch, dass Streams auf jegliche Art von Datenquelle zugreifen knnen, von HTML bis XML. Das erleichtert das Abrufen von Webinhalten erheblich. Mit Hilfe von WebRequest knnen Sie dem Webserver auch Nachrichten in Form eines Streams schicken. Dieses Posting ist ein Vorgang, bei dem der Client zustzliche eigene Informationen schicken kann, die sich normalerweise nicht in der Anforderungsnachricht befinden. Diese Information kann auch Postings von Formularen umfassen. Wenn Sie ein Online-Formular ausfllen und auf die Schaltflche OK klicken, schicken Sie eigentlich eine Webanforderung an den Server, so dass dieser Ihre Daten verarbeiten kann. (Dementsprechend wird der zuvor benutzte Vorgang des Abholens als Get-Anforderung (GetRequest) bezeichnet.) Um diese Anwendung zu testen, wollen wir zunchst eine Webseite erstellen, die Formular-Postings akzeptiert. Listing 12.2 zeigt eine Beispielseite. Listing 12.2: formpost.aspx
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: <%@ Page Language="CS" %> <script runat="server"> public void Page_Load(Object if (Request.Form.Count == lblMessage.Text = "You } else { lblMessage.Text = "You } } </script>

Sender, EventArgs e) { 0) { didnt post anything!"; posted: " + Request.Form.ToString();

<html><body> <ASP:Label id="lblMessage" runat="server"/> </body></html>

422

Clients und Server

Speichern Sie diese Datei unter dem Namen formpost.aspx in Ihrem StammWebverzeichnis (blicherweise c:\inetpub\wwwroot, wenn Sie die Standardeinstellungen des IIS bernommen haben). Die Datei ist eine einfache ASP.NETSeite, doch wie Sie sehen, hnelt sie einer Windows Forms-Datei. Damit die Seite formpost.aspx funktioniert, mssen Sie das ASP.NET-Modul installiert haben. Das Modul ist eine optionale Komponente des .NET Frameworks. Wir wollen Listing 12.2 kurz analysieren. In ASP.NET haben Sie es mit zwei Arten von Code zu tun: mit Server- und mit HTML-Code. Der HTML-Abschnitt obigen Listings ist in den Zeilen 13 bis 15 zu finden. Der Servercode sieht wie Code aus VB .NET oder C# aus und ist in den <script>-Tags enthalten, wie es die Zeilen 3 bis 11 zeigen. Dieser Code wird kompiliert und funktioniert fast genau wie Windows Forms-Anwendungen. ASP.NET-Seiten knnen eine Methode namens Page_Load aufweisen, die beim Aufruf der Seite durch den Browser ausgefhrt wird, hnlich wie ein Konstruktor fr ein Windows Forms-Programm, wenn die Anwendung startet. Die Page_Load-Methode bernimmt die gleiche Parameterliste wie viele Windows Forms-Ereignisse, weil sie ein Ereignishandler fr das Page_Load-Ereignis in ASP.NET ist, das beim Laden der Seite ausgelst wird. ASP.NET verfgt ber das Request-Objekt, das hnlich wie das bekannte WebRequestObjekt funktioniert. Die Form-Eigenschaft ist eine Auflistung aller Werte, die dem Server gepostet wurden; htten Sie ein Formular online ausgefllt, fnden sich Ihre Informationen hier wieder. Wurde nichts gesendet, ist die Auflistung leer (die Count-Eigenschaft ist 0), und Sie knnen eine Meldung (also die schlechte Nachricht) im Label-Steuerelement auf Zeile 14 anzeigen. Das Label-Steuerelement entspricht einem Windows FormsLabel, so dass dieser Code vertraut aussehen drfte. Befindet sich jedoch etwas im Posting des Formulars, zeigen Sie eine gute Nachricht an, die aus You posted: und dem gesendeten Wert besteht (Zeile 8). Request.Form.ToString schreibt lediglich alles Gesendete in Klartext. Testen Sie die Seite, indem Sie in Ihrem Browser auf die Adresse http://localhost/formpost.aspx gehen. Sie sollten die Meldung You didn't post anything! erhalten, da Sie ja noch keine Daten gesendet haben. Wir kommen gleich dazu. Dieser Abschnitt hat einen kurzen berblick ber ASP.NET geboten, doch wie Sie sehen knnen, ist dieses Thema nicht sonderlich kompliziert, sobald Sie einmal Windows Forms kennen gelernt haben. Wir wollen nun die Windows Forms-Anwendung erstellen, die dieses Formular postet. Listing 12.3 zeigt den entsprechenden Code.

423

Formulare Internet-fhig machen

Listing 12.3: Daten an Ihre ASP.NET-Seite senden


1: using System; 2: using System.Windows.Forms; 3: using System.Drawing; 4: using System.Net; 5: using System.IO; 6: 7: namespace TYWinforms.Day12 { 8: public class Listing123 : Form { 9: private RichTextBox rtbWeb = new RichTextBox(); 10: private WebRequest objRequest; 11: public Listing123() { 12: rtbWeb.Dock = DockStyle.Fill; 13: 14: objRequest = WebRequest.Create("http://localhost/formpost.aspx"); 15: 16: PostData(); 17: GetData(); 18: 19: this.Text = "Listing 12.3"; 20: this.Controls.Add(rtbWeb); 21: } 22: 23: private void PostData() { 24: objRequest.Method = "POST"; 25: objRequest.ContentLength = 23; 26: objRequest.ContentType = "application/x-www-form-urlencoded"; 27: 28: StreamWriter objWriter = new StreamWriter  (objRequest.GetRequestStream()); 29: objWriter.Write("Hi this is a form post!"); 30: objWriter.Close(); 31: } 32: 33: private void GetData() { 34: WebResponse objResponse = objRequest.GetResponse(); 35: StreamReader objReader = new StreamReader  (objResponse.GetResponseStream()); 36: 37: rtbWeb.Text = objReader.ReadToEnd(); 38: 39: objReader.Close(); 40: objResponse.Close(); 41: } 42:

424

Clients und Server

43: 44: 45: 46: 47:

public static void Main() { Application.Run(new Listing123()); } } }

Die Anwendung in Listing 12.3 sendet einige Daten an die Datei formpost.aspx (Listing 12.2) und zeigt die Ergebnisse in einem in Zeile 9 deklarierten RichTextBox-Steuerelement an. Der Konstruktor ist recht einfach; er ruft eine PostData-Methode auf, um die Daten an den Server zu senden, und GetData, um die Antwort zu empfangen. Beachten Sie die Erstellung des WebRequest-Objekts in Zeile 14; es zeigt auf die eben erzeugte Datei formpost.aspx. Wir wollen einen Blick auf PostData in Zeile 23 werfen. Um dem Server Daten zu senden, mssen Sie ihm erst dieses Vorhaben mitteilen, indem Sie die Method-Eigenschaft des WebRequest-Objekts in Zeile 24 auf POST setzen. Nachdem der Server diese Information erhalten hat, wei er, dass er auf Daten vom Client warten soll. In Zeile 25 legen Sie die Lnge des zu sendenden Inhalts fest; in diesem Fall ist es die Lnge einer Zeichenfolge. Die Lngenangabe ist optional, im Allgemeinen empfiehlt es sich aber, diesen Wert bereitzustellen. In Zeile 26 setzen Sie die ContentType-Eigenschaft des WebRequest-Objekts, was sehr wichtig ist. Der Standardwert lautet auf "text/html", so dass reiner Text, als HTML formatiert, bertragen wird. Gepostete Daten jedoch werden nicht als reiner Text, sondern in einem speziell verschlsselten Format bertragen. In Zeile 26 wird dieses Format als application/x-www-form-urlencoded festgelegt. Legen Sie dieses Format nicht fest, wird der Server die gesendeten Daten nicht erkennen und sie ignorieren. Nach dem Prparieren des Servers ist es Zeit fr den Datenversand. Da alle E/AOperationen in .NET ber Streams erfolgt, bilden Postings keine Ausnahme. In Zeile 28 rufen Sie daher die GetRequestStream-Funktion auf, um einen schreibbaren Stream zurckzugeben, der an den Server gesendet wird, und hllen ihn dann in einen Writer ein. In Zeile 29 rufen Sie die Write-Methode auf (wie fr jedes Writer-Objekt), um Ihre Daten schlielich an den Server zu schicken. Beachten Sie, dass die ContentLength-Eigenschaft in Zeile 25 der Lnge des Zeichenfolge in Zeile 29 entspricht. Statt die Anzahl der Zeichen manuell zu zhlen, knnen Sie folgenden Code benutzen:
objRequest.ContentLength = "Hi, this is a form post!".Length;

Alternativ knnen Sie die zu sendenden Daten in einer Zeichenfolgenvariablen speichern und ihre Length-Eigenschaft aufrufen.

425

Formulare Internet-fhig machen

Abschlieend mssen Sie genau wie beim Lesen von Anforderungsinformationen Ihren Writer schlieen (siehe Zeile 30). Die GetData-Methode in den Zeilen 33 bis 41 ist praktisch identisch mit der CallServerMethode aus Listing 12.1. Sie rufen GetResponse auf, um ein WebResponse-Objekt zu erzeugen, rufen dann die Methode GetResponseStream auf und erstellen einen Reader; zum Schluss rufen Sie ReadToEnd auf. Sowohl der Reader als auch das WebResponse-Objekt werden dann beendet (in den Zeilen 39 und 40). Der einzige Unterschied zwischen diesen Methoden und dem frheren CallServer besteht darin, dass Sie hier gerade Daten an den Server gesendet haben, so dass Sie eine entsprechend zugeschnittene Antwort erwarten. Wenn Sie diese Anwendung kompilieren und ausfhren, erhalten Sie Ergebnisse, die den in Abbildung 12.3 gezeigten hneln.

Abbildung 12.3: Nach dem Posten an den Server erhalten Sie diese Ergebnisse.

In diesem Bild knnen Sie erkennen, dass der zurckgelieferte Inhalt aus HTML besteht. Das erwartete Ergebnis wird geliefert:
You posted: Hi+this+is+a+form+post!

Weil die ASP.NET-Seite gepostete Formulardaten erhalten hat, zeigt sie die gute Nachricht mit den gesendeten Daten an. Beachten Sie jedoch das Pluszeichen zwischen jedem Wort. Da die gesendeten Daten in einem Spezialformat geschickt wurden, besteht einer der bei Postings notwendigen Formatierungstricks im Ersetzen von Leer- durch Pluszeichen. Das WebRequest-Objekt ist recht vielseitig, denn es erlaubt Ihnen, Inhalte von jedem Server zu holen bzw. an beliebige Server zu schicken. Vor der Einfhrung von .NET gestaltete sich das Holen und Posten von Daten weitaus schwieriger als nur das Lesen oder Schreiben eines Streams. Tabelle 12.1 zeigt die Eigenschaften und einige Methoden des WebRequest-Objekts.

426

Clients und Server

Eigenschaft

Beschreibung

ConnectionGroup- Diese Eigenschaft wird zum Gruppieren von Verbindungen (Connection Name Pools) benutzt. Diese werden zur Leistungssteigerung eingesetzt, wenn mehrere

Verbindungen ntig sind.


ContentLength ContentType Credentials

Die Lnge der bermittelten Daten. Typ und Format der bermittelten Daten. Anmeldeinformationen, die fr den Zugang zu Webressourcen benutzt werden. (Mehr dazu im Abschnitt ber Sicherheit.) Die Auflistung von HTTP-Headern, die mit jeder Anforderung geschickt werden. Die Protokollmethode fr diese Anforderung. Gibt an, ob dem Server Authentifizierungsinformationen gesendet werden sollen, bevor man dazu aufgefordert wird. Der fr den Webzugang zu verwendende Proxy. Der mit der Anforderung verknpfte URI der Internetressource. Der Zeitraum, den die Anforderung auf eine Serverantwort maximal warten darf. Beschreibung

Headers

Method PreAuthenticate

Proxy RequestUri Timeout

Methode

GetRequestStream Liefert einen Stream, der sich fr Datenlieferungen an den Server nutzen lsst. GetResponse

Liefert einen Stream, der die Serverantwort enthlt.

Tabelle 12.1: Die Mitglieder von WebRequest

Anfragen mit WebResponse verarbeiten


Die WebResponse-Klasse verarbeitet die Serverantwort. Diese einfache Klasse umfasst nicht viele Mitglieder. Tabelle 12.2 zeigt die Eigenschaften und Methoden von WebResponse.
Eigenschaft
ContentLength ContentType

Beschreibung Die Lnge der bermittelten Daten. Typ und Format der bermittelten Daten.

Tabelle 12.2: Mitglieder von WebResponse

427

Formulare Internet-fhig machen

Eigenschaft
Headers

Beschreibung Die Auflistung von HTTP-Headern, die mit einer Anforderung geschickt werden. Der URI der antwortenden Ressource. Beschreibung Schliet den Antwortstream, so dass mithin auch die Verbindung zum Server beendet wird. Liefert einen Stream, der die Antwort des Servers enthlt.

ResponseUri

Methode
Close

GetResponseStream

Tabelle 12.2: Mitglieder von WebResponse (Forts.)

Protokollspezifische Klassen
Bislang haben Sie gelernt, die Helferklassen WebRequest und WebResponse zu erstellen, wenn es um Web-Interaktionen geht. In den meisten Fllen gengen diese beiden Klassen. Die Helferklassen knnen aber auch zustzliche Funktionen bereitstellen. Angenommen, Sie mssen einmal den Typ der Anwendung angeben, die auf den Server zugreift wie zum Beispiel ein Internet Explorer-kompatibler Browser. Diese Informationen bezeichnet man als Benutzer-Agent (user-agent). Mit dem WebRequest-Objekt lsst sich der Typ nicht direkt bereitstellen, doch seine Helferklasse HttpWebRequest, die bei der HTTP-Verarbeitung hilft, kann dies:
((HttpWebRequest)objRequest).UserAgent = "Internet Explorer";

Um auf die HttpWebRequest-spezifischen Eigenschaften zuzugreifen, mssen Sie die WebRequest-Variable passend konvertieren, wie im obigen oder im folgenden Codefragment gezeigt:
HttpWebRequest objHttpRequest = (HttpWebRequest)objRequest;

Tabelle 12.3 zeigt die Mitglieder von HttpWebRequest, zustzlich zu denen in Tabelle 12.1.
Eigenschaft
Accept Address

Beschreibung Entspricht dem Accept-HTTP-Header. Gibt den URI des Servers an, der tatschlich auf die Anforderung antwortet (kann sich von Uri unterscheiden).

Tabelle 12.3: Die Mitglieder von HttpWebRequest (ergnzend zu Tabelle 12.1)

428

Clients und Server

Eigenschaft
AllowAutoRedirect

Beschreibung Gibt an, ob die Anforderung vom Server veranlasste Umleitungen zulassen soll. Gibt an, ob der Antwortstream gepuffert werden soll (verbessert oftmals die Leistung). Liefert Clientsicherheitszertifikate. Entspricht dem Connection-HTTP-Header. Kennzeichnet einen Delegaten, der auf eine HTTP-100-Meldung antworten kann. Kennzeichnet die Cookies, die der Anforderung zugeordnet sind. Entspricht dem Expect-HTTP-Header. Gibt an, ob vom Server eine Nachricht empfangen wurde. Entspricht dem If-Modified-Since-HTTP-Header. Gibt an, ob das Anforderungsobjekt eine dauerhafte Verbindung zum Server aufrechterhalten soll. Legt die maximale Anzahl von Umleitungen fest, die das Anforderungsobjekt zulsst. Gibt den Medientyp der Anforderung an. Gibt an, ob Anforderungen an den Server durch eine Pipeline erfolgen sollen (funktioniert nur, wenn KeepAlive auf true gesetzt wurde). Legt die zu verwendende HTTP-Version fest. Entspricht dem Referer-HTTP-Header. Gibt an, ob dem Server Daten in Segmenten geschickt werden sollen. Legt den Dienstpunkt fest, der fr den Server zu verwenden ist. Entspricht dem Transfer-encoding-HTTP-Header. Entspricht dem User-agent-HTTP-Header.

AllowWriteStreamBuffering ClientCertificates Connection ContinueDelegate

CookieContainer Expect HaveResponse IfModifiedSince KeepAlive

MaximumAutomaticRedirections MediaType Pipelined

ProtocolVersion Referer SendChunked ServicePoint TransferEncoding UserAgent

Tabelle 12.3: Die Mitglieder von HttpWebRequest (ergnzend zu Tabelle 12.1) (Forts.)

Um mehr Informationen ber die verschiedenen HTTP-Header zu erhalten, suchen Sie bitte die Website des WWW-Consortiums auf (www.w3c.org).

429

Formulare Internet-fhig machen

12.3 Arbeiten mit dem Internet Explorer


Unter dem .NET Framework knnen Sie Ihre Windows Forms-Anwendungen mit dem Internet Explorer zusammenarbeiten lassen. Dies erspart Ihnen eine Menge der Zeit, die Sie frher mit dem Umschreiben Ihrer Desktop-Anwendungen fr den Einsatz im Internet verwendet haben. Wenn Sie also zum Beispiel eine ntzliche Windows Forms-Anwendung erstellt haben, dann kann jedermann sie mit Hilfe des Internet Explorers ber das Internet erreichen (vorausgesetzt, man hat die korrekten Berechtigungen und zudem das .NET Framework installiert). Wir wollen die am 5. Tag erstellte Taschenrechner-Anwendung wiederverwenden. Verschieben Sie diese Anwendung in das Web-Stammverzeichnis Ihres Computers (c:\inetpub\wwwroot), ffnen Sie Ihren Browser und tippen Sie Folgendes in die Adresszeile ein:
http://localhost/Anwendungsname.exe

(Ersetzen Sie Anwendungsname.exe durch den tatschlichen Dateinamen der Anwendung.) Ihr Browser wird die Anwendung aufrufen, die nun genau wie eine normale Windows Forms-Anwendung luft. Abbildung 12.4 zeigt das Ergebnis.

Windows Forms-Steuerelemente in den Internet Explorer einbetten


Sie knnen in den Internet Explorer auch Windows Forms-Steuerelemente einbetten, wenn Sie das HTML-Tag <OBJECT> verwenden. Listing 12.4 zeigt eine einfache HTMLSeite, die das TextBox-Steuerelement in den Browser einbettet. Listing 12.4: Windows Forms-Steuerelemente in Internet Explorer einbetten
1: 2: <html><body> <OBJECT id="MyControl" classid="http:System.Windows.Forms.dll# System.Windows.Forms.TextBox" height=300 width=300 VIEWASTEXT> <param name="Text" value="Hi there!"> <param name="Width" value="100"> <param name="Height" value="100"> <param name="Multiline" value="true"> </OBJECT> </body></html>


3: 4: 5: 6: 7: 8:

Wenn Ihnen das <OBJECT>-Tag nicht vertraut ist, so ist es leicht zu erlernen. Der erste Parameter ist id (in Zeile 2), der aus einem benutzerfreundlichen Namen besteht, den Sie verwenden knnen, um spter in Ihrem Code auf die-

430

Arbeiten mit dem Internet Explorer

ses Steuerelement verweisen zu knnen. Im zweiten Parameter namens classid ist die Aktion spezifiziert. Um nun ein .NET-Steuerelement einzubetten, bentigen Sie zweierlei fr den Parameter classid: den Namen und Pfad der Assembly oder ausfhrbaren Datei, die das Steuerelement enthlt, das Sie einbetten wollen. Dann brauchen Sie (vom Pfadnamen durch ein Nummernzeichen # abgetrennt) den vollstndigen Namensraum-Namen des fraglichen Steuerelements. Weil Sie das TextBox-Steuerelement einbetten wollen, setzen Sie den Assembly-Pfad auf System.Windows.Forms.dll und den Namensraum-Namen auf System.Windows.Forms.TextBox.

Abbildung 12.4: Sie knnen Ihre Windows FormsAnwendungen vom Browser aus ausfhren.

Datei System.Windows.Forms.dll ist normalerweise im Ordner c:\winnt\Microsoft.NET\Framework\version abgelegt, doch fr etwas mehr Komfort knnen Sie eine Kopie im Ordner C:\inetpub\wwwroot ablegen. Auf Die diesen Ordner kann man ber das Web zugreifen. Legen Sie diese Kopie nicht dort ab, kann es sein, dass Sie den Text in der Anwendung nicht sehen knnen. Als Nchstes verraten die Eigenschaften Height und Width (in den Zeilen 4 und 5) dem Browser, wie viel Bildschirmflche das Steuerelement belegen darf. Dies betrifft nicht das tatschliche Ergebnis, sondern nur, wie viel Flche reserviert werden soll. VIEWASTEXT ist eine Helfereigenschaft, die dem Browser mitteilt, wie das Steuerelement dargestellt werden soll, falls es nicht kompatibel ist. Jede Eigenschaft des einzubettenden Steuerelements lsst sich mit Hilfe des PARAM-Tags offen legen, wie in den Zeilen 3 bis 6 zu sehen ist. Hier legen Sie die TextBox-Eigenschaf-

431

Formulare Internet-fhig machen

ten Text, Height, Width und Multiline offen und setzen Anfangswerte dafr. Beachten Sie, dass Sie das OBJECT-Tag auch wieder schlieen mssen, wie in Zeile 7 zu sehen. Speichern Sie das Listing als HTML-Datei und betrachten Sie es in Ihrem Browser (obigen Tipp beachten). Sie sollten ein Ergebnis wie das in Abbildung 12.5 zu sehen bekommen.

Abbildung 12.5: Ihr TextBox-Steuerelement wurde in Internet Explorer eingebettet.

Wenn Sie wollen, knnen Sie die offen gelegten Eigenschaften per JavaScript ndern, so wie im folgenden Beispiel:
function ChangeValues(){ MyControl.Text ="Ich habe soeben den Text gendert!"; MyControl.Width ="75 "; }

Sie verweisen auf das Steuerelement mit Hilfe des id-Wertes, den Sie oben in Listing 12.4 gesetzt haben. Natrlich wirkt dieses Beispiel etwas an den Haaren herbeigezogen. Statt ein solches Windows Forms-Steuerelement in Internet Explorer einzubetten, knnten Sie einfach ein ASP.NET-Steuerelement verwenden; das ist viel einfacher und effizienter. Doch wenn Sie ein eigenes Steuerelement entwickelt haben, dann wre die geschilderte Methode eine gute Mglichkeit, es ber das Web erreichbar zu machen. (Weitere Informationen zum Erstellen von benutzerdefinierten Steuerelementen erhalten Sie an Tag 18.)

432

Internetsicherheit

12.4 Internetsicherheit
Internetsicherheit ist ein vielschichtiges Thema. Dieses Unterkapitel wird Sicherheitsaspekte nicht en dtail abdecken knnen, denn dafr eignet sich ein (serverorientiertes) ASP.NET-Buch besser. Vielmehr befasst sich dieser Abschnitt damit, wie Windows FormsAnwendungen mit Sicherheit umgehen. Heutzutage befinden sich mehrere Formen von Internetsicherheit in Gebrauch, wobei jede einem anderen Zweck dient. Secure Sockets Layer (SSL) ist ein Protokoll (hnlich wie HTTP), das fr die sichere Datenbermittlung vom Client zum Server verwendet wird. Wahrscheinlich haben Sie bereits SSL im Zusammenhang mit Websites wie www.amazon.de benutzt, die Kreditkarteninformationen verarbeiten. Sie erkennen solche Sites daran, dass ihr URI stets mit dem Krzel HTTPS (s wie secure) beginnt. Die Objekte WebRequest und WebResponse sind vollstndig kompatibel zu SSL und Sie brauchen nichts Besonderes zu tun, um SSL nutzen zu knnen. Denken Sie daran, dass diese Objekte austauschbare Protokolle sind, so dass Ihre Anwendung bereits SSL handhaben kann. Ein weiterer Sicherheitstyp besteht in der Internetauthentifizierung. Anders als SSL, das sich auf die sichere Datenbertragung konzentriert, beruht Authentifizierung auf der Verifizierung der Berechtigungen des Clients, bevor berhaupt eine Datenbertragung erfolgt. Dieser Vorgang nimmt an, dass der Client nach der Besttigung der Benutzeridentitt auch das Recht hat, auf alle ntigen Ressourcen zuzugreifen die sichere bertragung wird dann durch andere Mechanismen (wie zum Beispiel SSL) gehandhabt. Sicherheit ist auch fr Websites ein bedeutendes Thema, denn sie sind Hackerangriffen strker ausgesetzt als Desktop-Anwendungen. Deshalb werden Websites oftmals mit besonderen Sicherheitsvorkehrungen geschtzt. Zum Glck ist Windows Forms vollstndig kompatibel mit solchen Manahmen, so dass Sie sichere Ressourcen mit Ihren Anwendungen erreichen knnen.

Authentifizierung
Windows verfgt ber drei Arten von Prozeduren der Internetauthentifizierung. Jede versucht, die Anmeldeinformationen des Benutzers zu verifizieren, bevor er in eine Website gelassen wird; die Site muss herausfinden, ob ein Benutzer der ist, der er zu sein vorgibt. Die gebruchlichste Prozedur ist die Basisauthentifizierung. Bei dieser Methode fordert eine besuchte Website den Benutzer auf, einen Benutzernamen und sein Kennwort anzugeben. Stimmen diese Anmeldeinformationen mit den Angaben in der Serverliste der sicheren Benutzer berein, wird der Zugang gewhrt. Abbildung 12.6 zeigt eine typische durch Basisauthentifizierung geschtzte Website, wie in Windows XP zu sehen.

433

Formulare Internet-fhig machen

Abbildung 12.6: Basisauthentifizierung fordert Sie auf, Ihren Benutzernamen und Ihr Kennwort einzugeben.

Die zweite Methode, die als Digestauthentifizierung bezeichnet wird, hnelt der ersten: Die Website fragt vom Benutzer Anmeldeinformationen ab. Der Unterschied besteht darin, dass bei der Basisauthentifizierung die Benutzereingaben unverschlsselt zum Server geschickt werden, bei der Digestauthentifizierung aber verschlsselt (im DigestModus). Die dritte Methode ist die Integrierte Windows-Authentifizierung (auch als NTLM bekannt). Dieses Verfahren ist recht raffiniert. Es findet nmlich keine Prfung von Anmeldeinformationen statt, sondern die Clientversion von Windows kommuniziert hinter den Kulissen mit der Serverversion, um Informationen zu liefern. Die verwendeten Anmeldeinformationen sind jene, die Sie verwenden, wenn Sie sich in Ihren Computer einloggen. Dieses Verfahren ist recht sicher, doch es setzt voraus, dass beide Seiten Windows einsetzen.

Berechtigungsklassen
Windows Forms und die WebRequest-Methode knnen mit allen drei Authentifizierungsverfahren zusammenarbeiten. Dafr ist nur eine Eigenschaft ntig, nmlich WebRequest.Credentials. Sie bernimmt als Parameter ein NetworkCredential-Objekt.
WebRequest objRequest = WebRequest.Create("http://localhost/secure"); objRequest.Credentials = new NetworkCredential("Christopher Payne", "mein Kennwort");

Der Konstruktor fr das NetworkCredential-Objekt bernimmt einen Benutzernamen, ein Kennwort und optional eine Domne. Dann fordern Sie ganz normal eine Ressource (im Internet) an. Fertig. Dieses Verfahren funktioniert mit Basis-, Digest- und NTLM-Methoden. Alternativ knnen Sie Folgendes verwenden:

434

Zusammenfassung

WebRequest objRequest = WebRequest.Create("http://localhost/secure"); NetworkCredential objCredentials = new NetworkCredential(); objCredentials.UserName = "Christopher Payne "; objCredentials.Password ="Mein Kennwort"; objCredentials.Domain ="Meine Domne "; objRequest.Credentials =objCredentials;

Sicherheit kann ein kompliziertes Thema sein, doch der Umgang damit fllt mit Windows Forms leicht.

12.5 Zusammenfassung
Heute haben Sie gelernt, dass Windows Forms fr seine Webanwendungen austauschbare Protokolle verwendet. Das sind Klassen, die die verschiedenen Internetprotokolle handhaben (HTTP, FTP etc.), die sich dynamisch je nach der angeforderten Internetressource ersetzen lassen. Auf diese Weise brauchen Sie nur einen Codesatz zu schreiben, um eine beliebige Zahl von Protokollen nutzen zu knnen. Den Kern der austauschbaren Protokolle bilden die Klassen WebRequest und WebResponse. Die beiden Klassen kapseln alle Funktionen, die man fr den Zugriff auf Webressourcen bentigt, und fhren die Protokollverarbeitung fr Sie durch. Die Vorgehensweise fr den Zugriff auf Internetressourcen besteht im Allgemeinen darin, mit Hilfe der WebRequest.Create-Methode und einem bereitgestellten URI ein WebRequest-Objekt zu erzeugen und dann die GetResponse-Methode aufzurufen. Darauf folgt GetResponseStream, um die Serverantwort abzuholen, und zum Schluss das Beenden der Verbindung. Optional knnen Sie GetRequestStream aufrufen, um Informationen auf den Server zu schreiben. Windows Forms-Anwendungen knnen auf verschiedene Weise mit Internet Explorer arbeiten. Sie knnen einfach eine Anwendung mit ihrem URL aufrufen oder sie mit Hilfe des OBJECT-Tags in eine Webseite einbetten. Zum Schluss haben Sie gelernt, dass alle Aspekte der Authentifizierungssicherheit ber die
Credentials-Eigenschaft des WebRequest-Objekts gehandhabt werden. Die Eigenschaft bernimmt ein NetworkCredential-Objekt, das Benutzername, Kennwort und Domne als

Informationen an einen Server schickt.

435

Formulare Internet-fhig machen

12.6 Fragen und Antworten


F In welcher Hinsicht unterscheidet sich eine ASP.NET-Anwendung von einer webfhigen .NET-Windows-Anwendung? A ASP.NET-Anwendungen sind speziell fr das Internet entworfen und lassen sich nur ber einen Server erreichen. Bei ASP.NET wurde die verteilte Struktur des Internet bercksichtigt, wodurch es in Sachen Internet weitaus effizienter als Windows Forms ist. Aus diesem Grund bevorzugt man fr Serveranwendungen ASP.NET gegenber Windows Forms. Das soll Sie aber nicht davon abhalten, Clientanwendungen in einer der beiden Technologien zu entwickeln.

Kann ich asynchrone Webanforderungen einsetzen? A Na klar! Genau wie bei normaler E/A haben die Webklassen von Windows Forms auch asynchronen Zugriff, der auch genauso verwendet wird. Die asynchrone Arbeitsweise liegt jedoch jenseits der Themen dieses Buches. Weiterfhrende Informationen finden Sie in der .NET Framework-Dokumentation.

Was versteht man unter einem Webdienst? A Webdienst (Web Service) ist die Bezeichnung fr eine breite Kategorie von Internetanwendungen. Der Zweck von Webdiensten ist die unsichtbare Bereitstellung von Anwendungen (oder Teilen davon) ber das Internet, und zwar auf dem Desktop des Endanwenders (nicht unbedingt in einer Browser-Oberflche). Webdienste haben sowohl Kritiker als auch Untersttzer, die sich darber streiten, ob in Webdiensten die Zukunft des Internet liegt.

12.7 Workshop
Dieser Workshop soll Ihnen helfen, sich besser an die heute behandelten Begriffe zu erinnern. Es hilft Ihnen sehr, die Antworten vollstndig zu verstehen, bevor Sie weiterlesen. Die Antworten zu den Quizfragen und den bungen finden Sie in Anhang A.

Quiz
1. Wahr oder falsch? Um dem Server POST-Informationen zu schicken, verwendet man das WebResponse-Objekt. 2. Nennen Sie die vier Bestandteile eines URI. 3. Welchen Typs muss der Inhalt sein, um Formularinformationen posten zu knnen?

436

Workshop

4. Was ist das HttpWebRequest-Objekt? 5. Welche sind die drei Typen der Windows-Authentifizierung?

bung
Erstellen Sie eine Anwendung, die eine Webseite (Ihrer Wahl) untersucht, deren Bilder parst (in HTML beginnen Grafiken mit dem Tag <img src=") und alle Bilder eines nach dem anderen in einem PictureBox-Steuerelement anzeigt. Verwenden Sie eine Schaltflche, um durch die Grafiken zu blttern. (Tipp: Verwenden Sie die Methode Image.FromStream, um eine Grafik aus einem Stream zu holen.)

437

Grafische Anwendungen mit GDI+

3 1

Grafische Anwendungen mit GDI+

Eines der interessantesten Leistungsmerkmale von Windows Forms sind die Grafikfhigkeiten. Mit dem Graphical Device Interface (GDI+) knnen Sie alle Arten von Schnittstellen fr das Zeichnen und Malen erstellen, von Bildeditoren bis zu Textmodellierungsanwendungen, oder sogar etwas so Simples wie Microsoft Paint. Die heutige Lektion befasst sich exklusiv mit GDI+: woher sie stammt, wie Sie sich darin zurechtfinden und welche Tricks sie fr Sie bereithlt. Grafikanwendungen sind hufig hoch spezialisiert, so dass Sie vielleicht die hier vorgestellten Kenntnisse nicht oft brauchen werden, doch es kann sich als ntzlich erweisen, sie zu kennen. Heute lernen Sie,

warum wir eine Schnittstelle fr Grafikgerte bentigen, welches die Basiskomponenten von GDI+ sind, wie Sie in Ihren Formularen Figuren und Text zeichnen, wie man Grafiken manipuliert und zeichnet, wie man unregelmig geformte Windows Forms erstellt.

13.1 Einfhrung in die Grafik und Bildbearbeitung unter Windows


Das Thema Grafikfhigkeiten in Windows umfasst eine breite Palette von Anwendungen. Fast alles, was Sie in Windows tun, betrifft einen grafischen Aspekt. Viele haben schon wenigstens einmal von Grafik- oder Videokarten in ihrem Computer gehrt. Typ, Gre, Farbtiefe und andere Eigenschaften der Anzeige hngen alle von der verwendeten Grafikkarte ab. Das uralte Problem mit den Grafikfhigkeiten eines Betriebssystem besteht darin, dass man als Entwickler nie vorher wei, welche Leistung die jeweilige Grafikkarte erbringt. In modernen PCs kann man ohne weiteres Komponenten austauschen, ohne das brige System zu verndern, so dass mglicherweise keine zwei Computer miteinander identisch sind. Die Verarbeitung von Grafiken wird dadurch etwas kompliziert, denn Ihre Anwendungen mssen mit allen mglichen Grafikkarten zurechtkommen knnen leider erledigen nur wenige Grafikkarten ihre Arbeit auf hnliche Weise. Man braucht also eine einfache Schnittstelle, die der Entwickler nutzen kann, um Bildprogramme unabhngig von der verwendeten Hardware zu erstellen. Ursprnglich entwickelte Microsoft die GDI (Graphical Device Interface, grafische Gerteschnittstelle), um dieses Problem zu lsen. GDI dient als eine Art bersetzer fr grafische Hardware. Man schickt Befehle an die GDI, um Grafikfunktionen auszufhren, und GDI wiederum spricht zur jeweiligen Hardware. Zum Glck ist GDI vielsprachig; sie wurde entworfen,

440

Einfhrung in die Grafik und Bildbearbeitung unter Windows

um mit praktisch jeder Art von Grafikkarte auf dem Markt zu kommunizieren. Daher mssen sich Entwickler nicht mit den Eigenheiten jeder einzelnen Grafikkarte befassen. Vielmehr kann man sich nun auf den Umgang mit GDI konzentrieren, die sich wiederum mit den jeweiligen Grafikkarten auseinandersetzt. GDI und GDI+ betreffen nur zweidimensionale Grafiken unter Windows. Fr dreidimensionale Grafik sind DirectX und OpenGL zustndig, die die gleiche bersetzerfunktion wie GDI(+) haben. GDI ffnete den Entwicklern das Tor zu einer neuen Welt. Das Entwickeln grafischer Anwendungen wurde wesentlich einfacher. Die Funktionen von Grafikkarten werden jedoch stndig weiterentwickelt. Neue Merkmale lieen 3D-Anwendungen viel fortschrittlicher aussehen als einfache Windows-2D-Grafik. Mit jeder neuen Windows-Version wurde auch eine neue Fassung von GDI ausgeliefert, die versuchte, den neuesten Merkmalen zu entsprechen. Die stndigen Erweiterungen machten GDI zudem schwerfllig. Dies veranlasste neben dem strategischen bergang zur .NET-Architektur Microsoft zur kompletten berarbeitung von GDI: Ein neues Paradigma wurde eingefhrt. Bei GDI+ handelt es sich also um eine neu entwickelte, erweiterte Version von GDI. Sie untersttzt nicht nur die neuesten Grafiktechniken, sondern ist vollstndig konform zu .NET. Wie Sie durch das Arbeiten mit .NET wissen, erleichtert dies einige Entwicklungsarbeiten. Gertekontext und Handles braucht man nicht mehr, denn man hat nun lediglich mit Grafikklassen und ihren Mitglieder zu tun, genau wie in jeder anderen Klasse in Windows Forms. GDI+ lsst sich in drei Bereiche aufteilen. Der erste, Vektorgrafik, dreht sich um Grundelemente Dinge wie Linien, Kurven und einfache Figuren. Vektorgrafik basiert auf einem Koordinatensystem; ndern sich die Koordinaten, so ndert sich auch die Grafik. Der zweite Bereich besteht aus (Bitmap-) Grafiken, dreht sich also um Dinge, die sich nicht mit einfachen Linien darstellen lassen (wie etwa die Hintergrundgrafik fr Ihren Desktop). Fr Grafiken ist ein Koordinatensystem ohne Bedeutung, denn Bilder werden ohne Rcksicht auf x- und y-Koordinaten gemalt. Weil Vektorgrafiken auf Koordinaten basieren, lassen sie sich einfacher und effizienter bearbeiten als Bilder. Wenn Sie etwa eine Linie verzerren wollen, ndern Sie einfach die entsprechenden Koordinaten. Wollen Sie eine Bitmap-Grafik verzerren, mssen Sie die Bits bearbeiten, aus denen die Grafik besteht. Vektorgrafiken lassen sich beliebig ohne Qualittsverlust verzerren, denn eine Koordinate ist eine Koordinate. Im Gegensatz dazu verliert eine Grafik beim Umformen oder Skalieren an Qualitt, weil es nur eine bestimmte Datenmenge pro Bild gibt; man kann einem Bild keine neuen Details hinzufgen, ohne es neu zu erfassen. Die dritte Komponente besteht aus Typografie bzw. Text. Hier geht es um unterschiedliche Schriftarten, Schriftgren, -stile und -farben.

441

Grafische Anwendungen mit GDI+

Alle drei Bereiche drehen sich um ein paar zentrale GDI+-Klassen und eine Reihe von grundlegenden Konzepten.

Pinsel und Stifte


Bevor Sie loslegen, sollten Sie ber ein paar Begriffe Bescheid wissen. In GDI+ stoen Sie laufend auf zwei gngige Begriffe, nmlich Stifte (pens) und Pinsel (brushes); sie hneln ihren Gegenstcken aus der Realitt. Ein Pen ist im Wesentlichen fr das Zeichnen einer Linie zustndig. Stellen Sie sich einen echten Stift vor. Da gibt es Stifte mit verschiedenen Farbtinten, verschiedenen Dicken (ein Fller zieht einen dickeren Strich als ein Kugelschreiber) und sogar verschiedenen Texturen. In GDI+ verhlt sich ein Pen genauso. Sie knnen einen Pen mit allen gewnschten Charakteristika erzeugen und damit Linien oder Text zeichnen. Ein Brush ist dafr zustndig, die von einem Pen gezeichneten Figuren mit einer vorgegebenen Farbe und Textur auszufllen. So wrden Sie etwa mit dem Pen einen Kreis zeichnen, ihn aber mit einem Brush mit der Farbe Orange ausfllen. Brushes lassen sich auf Dinge anwenden, die man nicht unbedingt als Figuren ansehen wrde, beispielsweise Schriften. In GDI+ knnen Brushes sogar Farbverlufe malen, etwa um Ihre Objekte interessanter zu machen. Das Graphics-Objekt bildet das Kernstck von GDI+. Es enthlt alle Methoden und Eigenschaften, die Sie zum Zeichnen von Linien und Text sowie zum Drucken (vgl. Tag 11) bentigen. Es wird Ihnen bald vertraut sein.

Rechtecke und Bereiche


Zwei weitere wichtige Begriffe sind Rechtecke (Rectangles) und Bereiche (Regions). Ein Rectangle ist in .NET einfach ein rechtwinkliges grafisches Objekt und eine Region stellt die Flche dieses Rechtecks dar. Diese zwei Objekte werden oft benutzt, um bestimmte Bereiche zu vereinbaren oder festzulegen, in die andere Objekte (etwa Linien oder Bilder) gezeichnet werden sollen. Man sieht diese Objekte praktisch nie ohne Referenz auf eine Graphics-Methode. Ein Rechteck lsst sich auf zweierlei Weise erstellen: durch das Angeben eines Startpunktes links oben und eines Size-Werts oder durch das Angeben der x- und y-Koordinaten fr die obere linke Ecke sowie einer Breite und Hhe:
Rectangle objRect = new Rectangle(new Point(0,0), new Size(100,100)); Rectangle objRect = new Rectangle(0,0,100,100);

442

Das Ereignis Paint

Im zweiten Konstruktor bestehen die ersten beiden Parameter aus den x- und y-Koordinaten des Startpunktes und die letzten beiden Koordinaten aus der Breite und Hhe des Rechtecks. Ein Region-Objekt wird erzeugt, indem man ihm ein Rectangle-Objekt bergibt:
Region objRegion = new Region(objRect);

Alternativ knnten Sie eine beliebige Form mit einem GraphicsPath-Objekt angeben, doch darauf kommen wir noch im Abschnitt Figuren zeichnen zu sprechen. Diese beiden Objekte verfgen selbst ber nicht besonders viele Methoden, doch sie erweisen sich als hilfreich bei der Bestimmung von Grafikumrissen, weshalb man sie stets im Zusammenhang mit anderen Grafikobjekten antrifft.

13.2 Das Ereignis Paint


Die wichtigste Aufgabe von GDI+ besteht nicht darin, komplizierte Zeichenprogramme wie etwa Photoshop oder Windows Paint zu erstellen. GDI+ ist dafr zustndig die Fenster jeder verwendeten Anwendung zu zeichnen. Jedes Mal wenn ein Fenster gezeichnet werden muss (etwa beim Start, beim Maximieren usw.), wird das Paint-Ereignis ausgelst. Wann immer Sie also steuern mssen, wie ein Windows-Formular gezeichnet wird, mssen Sie fr Paint einen Ereignishandler bereitstellen. Wir wollen einen Blick auf ein einfaches Beispiel, das in Listing 13.1 zu sehen ist, werfen. Dieses Listing zeichnet auf ein Windows-Formular etwas Text. Listing 13.1: Text mit dem Paint-Ereignis zeichnen
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: using System; using System.Windows.Forms; using System.Drawing; namespace TYWinforms.Day13 { public class Listing131 : Form { public Listing131() { this.Text = "Listing 13.1"; this.Paint += new PaintEventHandler(this.PaintMe); } private void PaintMe(Object Sender, PaintEventArgs e) { Graphics objGraphics = e.Graphics; Brush brushText = new SolidBrush(Color.Black); Font fntText = new Font("Times New Roman", 24, FontStyle.Regular);

443

Grafische Anwendungen mit GDI+

16: 17: objGraphics.DrawString("Ich male gerade!", fntText, brushText,  new Point(50,100)); 18: } 19: 20: public static void Main() { 21: Application.Run(new Listing131()); 22: } 23: } 24: }

Der Konstruktor fr diese Klasse (Zeile 7) ist sehr einfach. Er legt nur die Titelzeile fr das Formular fest und weist der Paint-Methode einen Delegaten zu. Wir wollen uns den PaintMe-Ereignishandler, der in Zeile 12 beginnt, genauer ansehen. Beachten Sie bitte, dass der zweite Parameter fr diese Methode ein PaintEventArgs-Objekt ist. Dieses Objekt enthlt die wichtige Eigenschaft Graphics, die ein Graphics-Objekt liefert, das alle Methoden enthlt, um Linien zu zeichnen usw. Jedes Mal wenn Sie etwas zu zeichnen haben, brauchen Sie ein GraphicsObjekt, ohne das die GDI+ ihre Arbeit nicht tun kann. In Zeile 13 rufen Sie also dieses Objekt ab und weisen es zwecks einfacherer Verwendung einer Variablen zu. In Zeile 14 richten Sie ein Brush-Objekt ein, das fr das Zeichnen von Text bentigt wird. Beachten Sie, dass Sie dafr kein Pen-Objekt verwenden knnen. In Zeile 15 legen Sie ein Font-Objekt an, das die Schriftart enthlt, die Sie beim Zeichnen von Text verwenden wollen. In Zeile 17 rufen Sie schlielich die DrawString-Methode des Graphics-Objekts auf, um den Text Ich male gerade! in das Formular mit dem angegebenen Pinsel und der spezifizierten Schriftart zu zeichnen. Der vierte Parameter von DrawString gibt die Position an, wo der Text zu zeichnen ist. Abbildung 13.1 zeigt das Ergebnis. Alternativ knnen Sie statt des Bereitstellens eines Delegaten fr das Paint-Ereignis auch die OnPaint-Methode berschreiben. Von Tag 5 wissen Sie noch, dass jedes Ereignis eine entsprechende OnEreignisname-Methode besitzt. In unserem Fall knnen Sie die standardmige OnPaint-Methode des Formulars berschreiben, indem Sie eine neue Methode OnPaint erstellen. Diese berschreibt die Standardmethode des Formulars, um Ihre benutzerdefinierten Zeichenfunktionen bereitzustellen. Listing 13.2 entspricht funktionell Listing 13.1, benutzt aber die berschriebene OnPaint-Methode.

444

Das Ereignis Paint

Abbildung 13.1: Die Zeichenfolge Ich male gerade! wird jedes Mal dann gezeichnet, wenn das Paint-Ereignis ausgelst wird.

Listing 13.2: Eine Zeichenfolge mit einer berschriebenen OnPaint-Methode zeichnen


1: using System; 2: using System.Windows.Forms; 3: using System.Drawing; 4: using System.Drawing.Drawing2D; 5: 6: namespace TYWinforms.Day13 { 7: public class Listing132 : Form { 8: public Listing132() { 9: this.Text = "Listing 13.2"; 10: } 11: 12: protected override void OnPaint(PaintEventArgs e) { 13: Graphics objGraphics = e.Graphics; 14: Brush brushText = new SolidBrush(Color.Black); 15: Font fntText = new Font("Times New Roman", 24, FontStyle.Regular); 16: 17: objGraphics.DrawString("Ich male gerade!", fntText, brushText,  new Point(50,100)); 18: } 19: 20: public static void Main() { 21: Application.Run(new Listing132()); 22: } 23: } 24: }

Der Code ist zu Listing 13.1 identisch, nur dass die Zuweisung eines Delegaten in Zeile 10 verschwunden ist und die Deklaration der Malmethode sich in Zeile 12 gendert hat. Beachten Sie das Schlsselwort Override, das angibt, dass die standardmige OnPaint-Methode nicht verwendet werden soll. berschrie-

445

Grafische Anwendungen mit GDI+

bene Mitglieder knnen nicht private sein, daher markieren wir dieses als protected.

Dieses Vorgehen, die OnPaint-Methode zu berschreiben, wird meist dem Zuweisen eines Delegaten vorgezogen, da sie noch aus den alten Tagen von GDI stammt und somit mehr Entwicklern vertraut ist. So oder so bieten sich keine Leistungsvorteile. Obwohl es sich um eine der gebruchlichsten Methoden handelt, stellt die Handhabung des Paint-Ereignisses nicht die einzige Mglichkeit dar, ein Graphics-Objekt fr das Zeichnen zu holen. Auch das Form-Objekt kann eines fr Sie erzeugen, und zwar mit Hilfe der CreateGraphics-Methode:
Graphics objGraphics = this.CreateGraphics();

Sie knnen dann objGraphics verwenden, um jede gewnschte Zeichenaktion auszufhren, und das muss nicht einmal innerhalb der OnPaint-Methode erfolgen.

13.3 Text zeichnen


Worin besteht der Unterschied zwischen dem Textzeichnen mit Hilfe von DrawString und der Erstellung eines einfachen Label-Steuerelements? Das Label-Steuerelement erweist sich als ideale Lsung, wenn man schon vorher wei, wo der Text platziert wird und wie er lautet. Im Gegensatz dazu lsst sich DrawString dazu verwenden, Text dynamisch an jedem beliebigen Ort zu zeichnen. Werfen wir einen Blick auf ein entsprechendes Beispiel. Mit dem Code in Listing 13.3 kann der Benutzer Text immer dort eintippen, wo sich der Mauszeiger befindet, wenn er mit der Maus klickt. Listing 13.3: Text zeichnen
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: using using using using System; System.Windows.Forms; System.Drawing; System.Drawing.Text;

namespace TYWinforms.Day13 { public class Listing133 : Form { private bool blnIsWriting = false; private Point ptClick; private string strKey;

446

Text zeichnen

12: public Listing133() { 13: this.Text = "Listing 13.3"; 14: this.MouseDown += new MouseEventHandler(FormClicked); 15: this.KeyPress += new KeyPressEventHandler(this.Type); 16: } 17: 18: private void Type(Object Sender, KeyPressEventArgs e) { 19: if (blnIsWriting) { 20: strKey += e.KeyChar.ToString(); 21: this.Invalidate(); 22: } 23: } 24: 25: private void FormClicked(Object Sender, MouseEventArgs e) { 26: blnIsWriting = !blnIsWriting; 27: 28: if (blnIsWriting) { 29: this.Cursor = Cursors.IBeam; strKey = ""; 30: } else { 31: this.ResetCursor(); 32: } 33: ptClick = new Point(e.X, e.Y); 34: } 35: 36: protected override void OnPaint(PaintEventArgs e) { 37: Graphics objGraphics = e.Graphics; 38: Brush brushText = new SolidBrush(Color.Black); 39: Font fntText = new Font("Times New Roman", 1004,  FontStyle.Regular); 40: 41: objGraphics.DrawString(strKey, fntText, brushText, ptClick); 42: } 43: 44: public static void Main() { 45: Application.Run(new Listing133()); 46: } 47: } 48: }

In den Zeilen 8 bis 10 deklarieren Sie einige Variablen, die an anderer Stelle verwendet werden. blnIsWriting bestimmt, ob die Anwendung gerade im Textzeichenmodus ist (also auf Tastatureingaben des Benutzers wartet). ptClick wird den Punkt darstellen, an dem der Benutzer mit der Maus geklickt hat: Hier wird der Text gezeichnet. strKey schlielich wird die zu schreibende Zeichenfolge enthalten.

447

Grafische Anwendungen mit GDI+

In den Zeilen 14 und 15 deklarieren Sie Ereignishandler fr zwei Ereignisse: MousePress, so dass Sie feststellen knnen, wo der Benutzer seinen Text schreiben will, und KeyPress, damit Sie feststellen knnen, welche Taste der Benutzer gedrckt hat, um das entsprechende Zeichen auf dem Bildschirm zu zeichnen. Wir wollen uns zunchst das FormClicked-Ereignis in Zeile 25 ansehen. Als Erstes setzt es die blnIsWriting-Variable auf ihren entgegengesetzten Wert. Falls also blnIsWriting gerade auf false gesetzt ist (und die Anwendung somit nicht auf Tastatureingaben wartet), dann setzen wir sie auf true (so dass sich Text eingeben lsst), und umgekehrt. Steht blnIsWriting auf true, dann verwandeln Sie den Mauszeiger in den I-Balken und setzen strKey auf nothing, damit kein vorher eingegebener Text kopiert wird. Ist blnIsWirting jedoch gleich false, dann setzen Sie den Mauszeiger mit Hilfe der ResetCursor-Methode auf den Standardwert zurck. In Zeile 33 holen Sie den Punkt im Formular, an dem der Benutzer die Maus geklickt hat. Jedes Mal wenn der Benutzer einen Buchstaben auf der Tastatur eingibt, wird die Type-Methode in Zeile 18 ausgefhrt. Diese Methode prft, ob der Schreibmodus vorherrscht; ist das der Fall, hngt sie den getippten Buchstaben an die strKey-Variable an. Um nun wirklich diese Zeichenfolge zu zeichnen, brauchen wir natrlich ein Graphics-Objekt. Bislang kannten wir nur das Auslsen der Paint- oder PrintPage-Ereignisse als Methoden, um ein Graphics-Objekt zu beschaffen. Doch die Methode Form.Refresh ist eine einfache Mglichkeit, Paint aufzurufen: Es fhrt die Formularaktualisierung selbst durch. Springen wir nun zur berschriebenen OnPaint-Methode in Zeile 36. Diese Methode hnelt den bereits gesehenen OnPaint-Methoden. Sie erstellt ein Brush- und ein FontObjekt und ruft dann DrawString auf. Die neuen Parameter hier sind strKey, der die eingetippten Zeichen enthlt, und ptClick, der die Eingabeposition enthlt. Der Ablauf stellt sich folgendermaen dar: 1. Die Anwendung startet. 2. Der Benutzer klickt mit der Maus und tritt in den Schreibmodus ein. Die Position des Mausklicks wird in ptClick gespeichert. 3. Der Benutzer tippt ein Zeichen. Es wird strKey angehngt und Refresh wird aufgerufen. 4. Dank Refresh wird Paint ausgelst und OnPaint ausgefhrt, so dass strKey an der von ptClick angegebenen Position gezeichnet wird.

448

Text zeichnen

5. Klickt der Benutzer die Maus erneut, verlsst er den Schreibmodus. Beim Drcken einer Taste passiert nichts mehr. Wenn Sie die Anwendung ausfhren, knnen Sie in das Formular beliebigen Text eingeben. Es tritt aber ein verblffender Effekt auf: Wenn Sie mehr als ein Zeichen auf einmal eingeben, verschwindet der zuvor eingegebene Text. Woran liegt das? Man hat sein Augenmerk auf die OnPaint-Methode zu richten. Diese Methode bestimmt alles, was auf dem Bildschirm gezeichnet wird, sobald Paint aufgerufen wurde (vom Formular natrlich mal abgesehen). Zu jedem Zeitpunkt kennt diese Methode nur die gerade zu zeichnende Zeichenfolge. Alle zuvor gezeichneten Zeichenfolgen sind vergessen oder gelscht. Wollen Sie diese Zeichenfolgen behalten, mssen Sie sie entweder in einer Art von Array speichern oder aus ihnen Label-Steuerelemente bilden, die persistent sind. Wenn Sie schon einmal die Grafiken und Schriftzeichen auf Ihrem Bildschirm ganz genau angesehen haben, drften Sie einen Effekt namens Aliasing bemerkt haben. An Bildern mit gekrmmten oder rauen Kanten knnen Sie Treppchen erhalten, also Randbereiche, in denen es sichtbare Brche oder scharfe Ecken in einer Linie gibt, obwohl der Rand glatt sein soll. Das ist auf die Arbeitsweise von Monitoren zurckzufhren: Jeder Monitor verfgt ber eine feste Anzahl von Bildpunkten (Pixeln), die er zur Anzeige einer Grafik benutzen kann. Da Pixel auf einem Monitor rechteckig aussehen, knnen sie glatte gekrmmte Kanten nicht besonders genau darstellen das fhrt zum berchtigten Treppcheneffekt. Die Abbildung 13.2 zeigt eine extreme Nahaufnahme eines Textes in Windows, wo Aliasing augenfllig ist.

Abbildung 13.2: Aliasing im Text fhrt zu scharfen Kanten und Treppchen.

Antialiasing (auch als Grauabstufung bezeichnet) ist daher der Versuch einer Grafikkarte, die Treppchen zu gltten, indem sie angrenzende Bildpunkte aus der Nachbarschaft der Treppchen in einer helleren Farbe darstellt. Dies vermittelt den Eindruck glatterer Kanten, kann aber auch zu einem verschwommenen Eindruck im Bild fhren. Abbildung 13.3 zeigt eine Antialiasing-Version des Textes aus Abbildung 13.2.

Abbildung 13.3: Antialiasing glttet Treppchen durch Hinzufgen angrenzender Bildpunkte.

449

Grafische Anwendungen mit GDI+

Antialiasing kann sicherlich Grafiken und Schriften besser aussehen lassen, aber auch zu einer Leistungsminderung fhren, da ja der Computer erhhte Mhe darauf verwenden muss, all die angrenzenden Bildpunkte zu berechnen. Die Schriftarten (.fnt-Dateien) enthalten oft selbst Hinweise darauf, wie man sie optimal einem Antialiasing unterzieht und darstellt. Unter .NET knnen Sie diese Wiedergabehinweise nutzen. Sie knnen den Grad des Antialiasings, den Sie fr Ihre Fonts nutzen wollen, mit Hilfe der Eigenschaft Graphics.TextRenderingHint einstellen. TextRenderingHint bernimmt Werte der TextRenderingHint-Aufzhlung:

AntiAlias: Den traditionellen Antialiasing-Mechanismus ohne Hinweise aus den

Schriftartdateien verwenden.

AntiAliasGridFit: Den traditionellen Antialiasing-Mechanismus mit Hinweisen aus

den Schriftartdateien verwenden.

ClearTypeGridFit: Einen speziell fr LCD-Bildschirme ausgetftelten AntialiasingMechanismus verwenden (mit Hinweisen). SingleBitPerPixel: Keine Antialiasing-Hinweise verwenden. SingleBitPerPixelGridFit: Kein Antialiasing verwenden, wohl aber Hinweise. SystemDefault: Das Graphics-Objekt wird den Standard verwenden, den das Betriebs-

system fr Antialiasing empfiehlt. Um beispielsweise Antialiasing ohne Bercksichtigung der Wiedergabehinweise aus der Datei einzusetzen, verwenden Sie den folgenden Code:
Graphics objGraphics = e.Graphics; objGraphics.TextRenderingHint = TextRenderingHint.AntiAlias;

Beachten Sie, dass die TextRenderingHint-Aufzhlung im Namensraum System.Drawing.Text enthalten ist. Sorgen Sie also fr dessen Import, wenn Sie Antialiasing nutzen wollen.

13.4 Grafiken anzeigen


Sie haben ja bereits einige Beispiele fr die Grafikanzeige gesehen. .NET setzt hauptschlich zwei Klassen fr Bilder ein: Image, die mit einer Vielzahl von Bildformaten zusammenarbeitet, und Bitmap, die Image erweitert und Ihnen erlaubt, einzelne Bildpunkte in einer Grafik zu erreichen.

450

Grafiken anzeigen

Die Image-Klasse hat keinen Konstruktor. Um eine neue Grafik zu erzeugen, mssen Sie eine ihrer statischen Methoden verwenden, zum Beispiel FromFile, FromStream oder FromHBitmap:
Image objImage =Image.FromFile("c:\\My Pictures \\Smile.jpg"); Bitmap hingegen verfgt ber Konstruktoren, so dass Sie einen auf verschiedene Weise

instantiieren knnen, etwa so:


Bitmap objBM = new Bitmap("c:\\My Pictures \\Smile.jpg"); Bitmap objBM = new Bitmap(Image.FromFile("c:\\My Pictures\\Smile.jpg")); Bitmap objBM = new Bitmap(Image.FromStream(objResponse.GetResponseStream()));

Das Bildermalen ist in .NET recht leicht, denn Sie verwenden einfach die DrawImageMethode:
Image objImage =Image.FromFile("c:\\My Pictures \\Smile.jpg"); e.Graphics.DrawImage(objImage,0,0);

Diese Codezeilen zeichnen die Grafik smile.jpg in das Formular auf der Position der Koordinaten 0, 0. Die DrawImage-Methode bernimmt eine ganze Reihe von Parametern zu viele, um sie hier aufzuzhlen, so dass Sie am besten die .NET Framework Dokumentation nachschlagen, wenn Sie eine bestimmte Methode brauchen. Mit der DrawImage-Methode knnen Sie Grafiken verzerren, indem Sie ein Array von Point-Objekten verwenden (oft ntzlich fr Grafikknstler). Die Syntax fr den entsprechenden Befehl sieht so aus:
DrawImage(image, points[], rectangle, GraphicsUnit);

Der erste Parameter, image, ist natrlich das zu malende Bild. Der zweite Parameter ist ein Array von drei Point-Objekten, die jeweils die Bildecken links oben, rechts oben und links unten bestimmen. Der dritte Parameter ist ein Rechteckobjekt, das die ursprnglichen Abmessungen der Grafik angibt. Dies ist notwendig, damit die GDI+ wei, wie sie Transformationen anwenden soll. Der vierte Parameter bestimmt die Einheit, in der alles gemessen wird. Meistens verwendet man GraphicsUnit.Pixel. Andere Werte umfassen GraphicsUnit.Inch, GraphicsUnit.Millimeter und GraphicsUnit.Point. Listing 13.4 zeigt etwas Beispielcode, der eine Grafik ldt, deren Rnder bestimmt und sie skaliert.

451

Grafische Anwendungen mit GDI+

Listing 13.4: Grafiken bearbeiten


1: private Image objImg; 2: private Rectangle rectOriginal; 3: private Point[] arrPoints = new Point[3]; 4: ... 5: //im Konstruktor 6: objImg = Image.FromFile("c: \\winforms\\day13\\sample.jpg"); 7: rectOriginal = new Rectangle(0, 0, objImg.Width, objImg.Height); 8: 9: arrPoints[0].X = rectOriginal.X; 10: arrPoints[0].Y = rectOriginal.Y; 11: arrPoints[1].X = rectOriginal.Width; 12: arrPoints[1].Y = rectOriginal.Y; 13: arrPoints[2].X = rectOriginal.X; 14: arrPoints[2].Y = rectOriginal.Height; 15: 16: btClick.Click += new EventHandler(this.DistortImage); 17: ... 18: private void DistortImage(Object Sender, EventArgs e) { 19: arrPoints[0].X = Convert.ToInt32(tbUpperLeftX.Text); 20: arrPoints[0].Y = Convert.ToInt32(tbUpperLeftY.Text); 21: arrPoints[1].X = Convert.ToInt32(tbUpperRightX.Text); 22: arrPoints[1].Y = Convert.ToInt32(tbUpperRightY.Text); 23: arrPoints[2].X = Convert.ToInt32(tbLowerLeftX.Text); 24: arrPoints[2].Y = Convert.ToInt32(tbLowerLeftY.Text); 25: 26: this.Refresh(); 27: } 28: 29: protected override void OnPaint(PaintEventArgs e) { 30: Graphics objGraphics = e.Graphics; 31: 32: objGraphics.DrawImage(objImg, arrPoints, rectOriginal,  GraphicsUnit.Pixel); 33: } 34: ... 35: }

Diese Anwendung nimmt an, dass Sie sechs TextBox-Objekte erzeugt haben, in die ein Benutzer Koordinaten fr das Point-Array eintragen kann, und eine Schaltflche, die der Benutzer anklicken kann, um die Transformation zu veranlassen.

452

Grafiken anzeigen

In den Zeilen 1 bis 3 deklarieren Sie einige Variablen, die spter benutzt werden. In Zeile 6 laden Sie dann die Grafik mit Hilfe der Methode Image.FromFile (dies sollte auf eine vorhandene Datei zeigen). In Zeile 7 laden Sie anhand der Grafikeigenschaften ein Rectangle-Objekt. Dieses hilft GDI+ dabei zu bestimmen, wie das Bild zu transformieren ist. In den Zeilen 9 bis 14 initialisieren Sie die Point-Objekte im arrPoints-Array. Diese Punkte werden dazu verwendet herauszufinden, wo jeweils die linke obere, rechte obere und linke untere Ecke der Grafik gemalt werden soll. Anfangs sind sie auf die Ecken des ursprnglichen Rechtecks eingestellt. In der DistortImage-Funktion rufen Sie die Werte aus verschiedenen Textfeldern ab, platzieren sie im Transformationsarray und rufen dann die Methode Refresh auf, die das Paint-Ereignis auslst. In Zeile 32 wird DrawImage aufgerufen, wobei ihm die Grafik, das Transformationsarray, das ursprngliche Rechteck und die Maeinheit bergeben werden. Abbildung 13.4 zeigt eine Musteranwendung, die diesen Funktionsumfang nutzt, nachdem eine Transformation angewendet wurde.

Abbildung 13.4: Ein Transformationsarray lsst sich zur Transformation von Figuren verwenden.

453

Grafische Anwendungen mit GDI+

13.5 Figuren zeichnen


Der Schlssel zum Zeichnen von Figuren und Linien in GDI+ liegt in der Kenntnis der Fhigkeiten des Pen-Objekts. Dieses Objekt verfgt ber zahlreiche Eigenschaften, die bestimmen knnen, wie Ihre Figuren gezeichnet werden. Danach brauchen Sie nur die Koordinaten zu kennen, auf denen Sie die Figuren einzeichnen wollen. Werfen wir zunchst einen Blick auf die Eigenschaften des Pen-Objekts, die in Tabelle 13.1 aufgefhrt sind.
Eigenschaft
Alignment

Beschreibung Die Ausrichtung der mit diesem Pen gezeichneten Objekte. Ist also die Linie des Stifts dick, bestimmt diese Eigenschaft, an welchem Rand der Linie die Zeichnung ausgerichtet sein soll. Der Brush, der fr das Fllen des Innenbereichs verwendet wird. Die Farbe des Pen. Ein Array mit Werten fr einen Verbundstift, zum Zeichnen einer Verbundlinie, die aus parallelen Linien und Zwischenrumen besteht. Eine benutzerdefinierte Figur, die am Ende einer Linie gezeichnet wird (etwa eine Pfeilspitze). Eine benutzerdefinierte Figur, die am Anfang einer Linie gezeichnet wird. Die Figur, die an den Strichenden in einer gestrichelten Linie gezeichnet wird. Die Distanz vom Linienanfang bis zum Anfang der Striche. Ein Array von benutzerdefinierten Strichen. Der Stil der zu verwendenden Striche. Eine Figur, die am Ende einer Linie gezeichnet wird. Der Verknpfungsstil fr zwei sich berlappende Linien. Die Dicke einer Verknpfung in einer Gehrung. Stil einer mit dem Pen gezogenen Linie Eine Figur, die am Anfang der Linie gezeichnet wird. Eine geometrische Transformation fr den Stift. Die Breite der Linie, die der Stift zieht.

Brush Color CompoundArray

CustomEndCap

CustomStartCap DashCap DashOffset DashPattern DashStyle EndCap LineJoin MiterLimit PenType StartCap Transform Width

Tabelle 13.1: Die Eigenschaften der Klasse Pen

454

Figuren zeichnen

Um die Verwendungsweise des Stifts kennen zu lernen, schauen wir uns eine einfache Methode an, nmlich Graphics.DrawLine. Sie bernimmt drei Parameter: ein Pen-Objekt und zwei Point-Objekte, die Start- und Endpunkt einer Linie festlegen. Listing 13.5 zeigt ein Beispiel fr verschiedene zu zeichnende Linien. Listing 13.5: Linien zeichnen
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: protected override void OnPaint(PaintEventArgs e) { Graphics objGraphics = e.Graphics; Pen objPen = new Pen(Color.Black); //einfache schwarze Linie objGraphics.DrawLine(objPen, new Point(10,700), new Point(500,150)); //dickere Linie objPen.Color = Color.Red; objPen.Width = 10; objPen.Alignment = PenAlignment.Inset; objGraphics.DrawLine(objPen, new Point(50,200), new Point(500,430)); //Pfeil objPen.Color = Color.Blue; objPen.EndCap = LineCap.DiamondAnchor; objPen.StartCap = LineCap.ArrowAnchor; objGraphics.DrawLine(objPen, new Point(600,10), new Point(500,430)); //gestrichelte Linie objPen.Color = Color.Green; objPen.EndCap = LineCap.NoAnchor; objPen.StartCap = LineCap.NoAnchor; objPen.DashStyle = DashStyle.DashDotDot; objGraphics.DrawLine(objPen, new Point(20,60), new Point(300,600)); }

Zeile 4 beginnt mit der Erstellung unseres neuen Pen-Objekts. Der Rest des Codes zeichnet eine Reihe von Linien: Zeile 7 zeichnet eine einfache schwarze Linie, die Zeilen 11 bis 13 eine dickere rote Linie, die Zeilen 16 bis 19 eine dicke blaue Linie mit Pfeilen an den Enden und die Zeilen 22 bis 26 einen gestrichelten grnen Pfeil. Abbildung 13.5 zeigt das Ergebnis des Listings 13.5. Es gibt eine ganze Reihe von Aufzhlungen, die beim Pen-Objekt verwendet werden. Tabelle 13.2 fasst sie zusammen.

455

Grafische Anwendungen mit GDI+

Abbildung 13.5: Die Bildschirmausgabe zeigt eine Reihe von Linien unterschiedlicher Farbe und Dicke sowie anderen Eigenschaften. Aufzhlung
PenAlignment LineCap

Pen-Eigenschaft
Alignment DashCap, EndCap, StartCap

Werte
Center, Inset, Left, Outset, Right AnchorMask, ArrowAnchor, Custom, DiamondAnchor, Flat, NoAnchor, Round, RoundAnchor, Square, SquareAnchor, Triangle Custom, Dash, DashDot, DashDotDot, Dot, Solid Bevel, Miter, MiterClipped, Round HatchFill, LinearGradient, PathGradient, SolidColor, TextureFill

DashStyle LineJoin PenType

DashStyle LineJoin PenType

Tabelle 13.2: Pen-Aufzhlungen und ihre Eigenschaften und Werte

Um komplexere geometrische Figuren zu zeichnen als Linien, mssen Sie ber Pfade Bescheid wissen insbesondere ber das GraphicsPath-Objekt. GraphicsPath definiert eine Reihe von Linien oder Punkten, die zusammen eine einzelne Figur bilden, etwa ein Parallelogramm. Ein Pfad lsst sich auf zweierlei Art definieren. Sie knnen entweder StartFigure mit einer nachfolgenden Sequenz von Add-Methoden (gleich mehr dazu) und der DrawPathMethode aufrufen. Oder Sie knnen die zu zeichnenden Punkte im Konstruktor von GraphicsPath selbst deklarieren. Bei dieser Methode mssen Sie auch die zu zeichnende Linienart angeben.

456

Figuren zeichnen

Wir wollen einmal untersuchen, was der GraphicsPath erzielen kann. ber einfache Linien hinaus kann GDI+ auch Ellipsen, Kurven, Bgen, Bzierkurven (komplexe Pfade, die mathematisch berechnet werden), Kreisformen, Polygone, Rechtecke und Text zeichnen. Fr jede dieser Figuren gibt es eine entsprechende Add-Methode: AddPolygon, AddLine, AddBezier und so weiter. Somit knnten Sie mit folgendem Code eine recht interessante Figur zeichnen:
protected override void OnPaint(PaintEventArgs e) { Graphics objGraphics = e.Graphics; GraphicsPath objPath = new GraphicsPath(); Pen objPen = new Pen(Color.Blue, 5); objPath.StartFigure(); objPath.AddLine(new Point(10, 10), new Point(150, 10)); objPath.AddArc(150, 10, 150, 50, 0, 10); objPath.AddCurve(new Point[] {new Point(150,150), new Point(200,350), new Point(300,450)}); objPath.AddBezier(new Point(300,450), new Point(500,100), new Point(400,50), new Point(350,10)); objPath.AddPie(350, 10, 150, 50, 0, 50); objPath.AddString("Hello World", new FontFamily("Times New Roman"), Convert.ToInt32 (FontStyle.Italic), 50, new Point(500,100), new StringFormat()); objPen.LineJoin = LineJoin.Round; objGraphics.DrawPath(objPen, objPath); }

   

Am Anfang rufen Sie die StartFigure-Methode auf, um der GDI+ mitzuteilen, dass nachfolgende Befehle in das Formular zeichnen werden. Darauf folgt eine Sequenz von AddMethoden, die jeweils eine Linie, eine Kurve, einen Bogen, eine Bzierkurve, eine Kreisform und eine Zeichenfolge hinzufgen. Jede Add-Methode nimmt eine unterschiedliche Menge von Argumenten entgegen (gleich mehr dazu). Zum Schluss rufen Sie die DrawPath-Methode auf, um das angegebene GraphicsPath-Objekt mit dem spezifizierten Pen-Objekt zu zeichnen. Was Sie als Ergebnis erhalten, sehen Sie in Abbildung 13.6. Die Add-Methoden knnen durchaus kompliziert werden und so ist es schwierig, sich alle Parameter zu merken. Tabelle 13.3 fasst sie fr Sie zusammen. Beachten Sie jedoch, dass alle Add-Methoden ber Listen von mehreren Parametern verfgen und zudem eine Reihe optionaler Parameter entgegennehmen, so dass die folgende Liste keineswegs vollstndig ist. Sie fhrt nur die gngigsten Methoden auf. Alle Parameter sind Integertypen, falls nicht anders angegeben.

457

Grafische Anwendungen mit GDI+

Abbildung 13.6: Verwenden Sie ein GraphicsPath-Objekt, um kompliziertere Figuren zu zeichnen. Methode
AddArc

Parametertypen x-Position, y-Position, Breite, Hhe, Anfangswinkel (Single-Datentyp), Steigungswinkel (Single-Datentyp) Startpunkt, erster Kontrollpunkt, zweiter Kontrollpunkt, Endpunkt (Kontrollpunkte gehen in Berechnungen ein, um den Kurvenverlauf zu bestimmen) ein Array von Bezier-Objekten

AddBezier

AddBeziers

AddClosedCurve ein Array von Point-Objekten, die eine geschlossene Kurve definieren AddCurve AddEllipse AddLine AddLines AddPath AddPie

Ein Array von Point-Objekten, die eine Kurve definieren x-Position, y-Position, Breite, Hhe Startpunkt (Point-Objekt), Endpunkt (Point-Objekt) ein Array von Line-Objekten ein GraphicsPath-Objekt x-Position, y-Position, Breite, Hhe, Anfangswinkel (Single-Datentyp), Steigungswinkel (Single-Datentyp) ein Array von Point-Objekten, die das Polygon definieren

AddPolygon

Tabelle 13.3: Die Add-Methoden und ihre Parameter

458

Figuren zeichnen

Methode
AddRectangle AddRectangles AddString

Parametertypen ein Rectangle-Objekt ein Array von Rectangle-Objekten Eine Zeichenfolge, ein FontFamily-Objekt, das das Schriftbild definiert, ein FontStyle-Aufzhlungswert, der in einen Integer umgewandelt werden muss, die Schrifthhe (Single-Datentyp), ein Point-Objekt, das den Ursprung angibt, ein StringFormat-Objekt, das die Charakteristika der Zeichenfolge angibt

Tabelle 13.3: Die Add-Methoden und ihre Parameter (Forts.)

Wenn Sie die diversen Add-Methoden nacheinander aufrufen und Linien miteinander verbinden wollen, mssen Sie den Endpunkt des einen Segments an den Anfangspunkt des nchsten Segments anpassen. Das kann manchmal eine Herausforderung sein, besonders bei gekrmmten Linien (bei drei Dimensionen wird es noch etwas kniffliger). Statt dessen knnen Sie alle zu zeichnenden Linien im Konstruktor des GraphicsPathObjekts deklarieren:
GraphicsPath objPath = new GraphicsPath( new Point[] { new Point(40, 140), new Point(275, 200), new Point(105, 225), new Point(190, 300), new Point(50, 350), new Point(20, 180) }, new byte[] { (byte)PathPointType.Start, (byte)PathPointType.Bezier, (byte)PathPointType.Bezier, (byte)PathPointType.Bezier, (byte)PathPointType.Line, (byte)PathPointType.Line } ); objGraphics.DrawPath(objPen, objPath);

In dieser Methode ist der erste Parameter des Konstruktor (obwohl er wie die ersten sechs Parameter aussieht) ein Array von Point-Objekten, die jeden Punkt im Pfad angeben:
new Point [] {new Point(40,140),new Point(275,200), new Point(105,225),new Point(190,300),new Point(50,350),new Point(20,180)}

Der zweite Parameter (der wie die nchsten sechs Parameter aussieht) gibt vor, wie die Linien zwischen diesen Punkten gezeichnet werden sollen. Alle diese Werte stammen aus der Aufzhlung PathPointType und mssen zu Byte-Datentypen umgewandelt werden (nur GDI+ wei warum).
new byte [] {{(byte)PathPointType.Start, (byte)PathPointType.Bezier,(byte)PathPointType.Bezier,

459

Grafische Anwendungen mit GDI+

(byte)PathPointType.Bezier,(byte)PathPointType.Line, (byte)PathPointType.Line }

Die PathPointType-Aufzhlung ist nicht so vielseitig wie die Add-Methoden. Sie enthlt nur wenige Werte: Start gibt an, dass der Punkt der Anfang einer Linie ist und dass keine Linie zu ihm hin gezeichnet werden soll; Bezier gibt an, dass die Linie zwischen zwei Punkten gekrmmt sein soll; Line gibt eine Gerade an und DashMode, dass die Linie gestrichelt sein soll. Abbildung 13.7 zeigt das Ergebnis unseres neuen GraphicsPath-Objekts. Welche Methode Sie whlen, um Ihre komplexen Figuren zu zeichnen, steht Ihnen frei, denn jede hat ihre Vorteile. Die Serie von Add-Methoden mag ein wenig leichter zu handhaben und zudem leichter zu lesen sein und ist vielseitiger. Doch Sie mssen an die Syntax fr jeden Typ von Add-Methode denken. Andererseits ist die Konstruktormethode nicht so vielseitig, aber Sie mssen sich nicht 1000 verschiedene Parameter merken und nicht im Geiste Linien und Punkte miteinander verbinden, denn jeder Punkt fhrt zum nchsten.

Abbildung 13.7: Werte der PathPointType-Aufzhlung ergeben komplexe Figuren.

Figuren ausfllen
Sie kennen jetzt die Verwendungsweise von Pen beim Zeichnen. Um jedoch die entsprechenden Figuren mit Farben, Grafiken und Farbverlufen zu fllen, verwendet man diverse Brush-Objekte.

460

Figuren zeichnen

Diese Objekte verfgen ber keinerlei Methoden, so dass sie nicht selbst Funktionen ausfhren knnen Pinsel sind lediglich Werkzeuge, mit denen andere Objekte zeichnen. Es gibt fnf Hauptarten von Pinseln. Die erste und grundlegende ist SolidBrush. Sie fllt die angegebene Region mit einer durchgehenden Farbe Ihrer Wahl:
SolidBrush objBrush =new SolidBrush(Color.Black);

Ein HatchBrush hingegen fllt eine Region mit Linien, um dem Hintergrund ein Schraffurmuster zu verleihen. Fr diese Schraffur gibt es eine ganze Reihe von Stilen (56 insgesamt), die alle zur Aufzhlung HatchStyle gehren. HatchStyle.Cross etwa zeichnet ein Muster aus sich kreuzenden waagrechten und senkrechten Linien, whrend HatchStyle.Horizontal die Flche mit waagrechten Linien fllt.
HatchBrush verfgt ber zwei verschiedene Farbeigenschaften. Die erste gibt an, in welcher Farbe die Linie zu zeichnen ist, und die zweite legt die Hintergrundfarbe der Region fest. Der folgende Code erzeugt eine blaue Region mit schwarzen waagrechten Streifen: HatchBrush objBrush = new HatchBrush(HatchStyle.Horizontal, Color.Black, Color.Blue);

Die Pinsel LinearGradientBrush und PathGradientBrush steuern Farbverlufe, also Regionen, in denen eine Farbe allmhlich in eine andere bergeht (auch in Schwarzwei natrlich). LinearGradientBrush legt einen linearen Verlauf von einer Farben zur nchsten fest, so dass man keine Berechnungen anstellen muss. Diesen Verlauf knnen Sie leicht durch die Angabe von zwei Punkten (den Start- und Endpunkt des Farbverlaufs) und zwei Farben (die Start- und Endfarben des Verlaufs) erzeugen:
LinearGradientBrush objBrush = new LinearGradientBrush(new Point(0,0), new Point(100,100), Color.White, Color.Black);

Der PathGradientBrush ist ein wenig vielschichtiger. Er bildet einen Verlauf in einem strahlenfrmigen Muster: Die Farbe A beginnt im Mittelpunkt und verndert sich mit steigendem Radius hin zur Farbe B. Ein PathGradientBrush lsst sich auf der Grundlage eines GraphicsPath-Objekts aufbauen oder, wie hier gezeigt, mit einem Array von Punkten:
PathGradientBrush objBrush =PathGradientBrush(new Point [] {new Point(0,0),new Point(100,50),...});

Dann legen Sie die zentrale Farbe fest:


objBrush.CenterColor = Color.White;

Und schlielich ein Array von Farben, eine fr jeden angegebenen Punkt:
ObjBrush.SurroundColors = new Color [] {{Color.Blue, Color.Red, Color.Orange,...});

Abbildung 13.8 zeigt Beispiele fr beide Pinsel, LinearGradientBrush und PathGradientBrush.

461

Grafische Anwendungen mit GDI+

Abbildung 13.8:
LinearGradientBrush (links) zeich-

net einen einfachen Farbverlauf; PathGradientBrush

(rechts) zeichnet einen kreis- oder sternfrmigen Farbverlauf.

Der letzte Pinseltyp ist TextureBrush, der eine Grafik fr das Fllen einer Region verwendet. Sie erzeugen diesen Pinsel durch das bergeben einer Grafik:
TextureBrush objBrush = new TextureBrush(Image.FromFile("c:\\temp\\sample.jpg"));

Da Sie nun Ihre Pinsel haben, knnen Sie eine Flche mit Hilfe einer Reihe von FillMethoden ausfllen: FillClosedCurve, FillEllipse, FillPath, FillPie, FillPolygon, FillRectangle und FillRegion. Jede Fill-Methode hat eine Liste von Parametern, doch mit ihnen kann man zum Glck leichter umgehen als mit Add-Methoden. Tabelle 13.4 zeigt diese Parameterlisten.
Methode
FillClosedCurve FillEllipse

Parametertyp Ein Brush und ein Array von Punkten, die die Kurve definieren. Ein Brush und ein Rectangle-Objekt, das die vier Begrenzungspunkte der Ellipse definiert (die Auenpunkte links, oben, rechts und unten) Ein Brush und ein GraphicsPath-Objekt Ein Brush, ein Rectangle, das die vier Begrenzungspunkte der Torte definiert (wie bei der Ellipse), ein Anfangswinkel und ein Steigungswinkel. (Die Torte muss nicht vollstndig sein; es kann sich auch um ein Segment handeln.) Ein Brush und ein Array von Punkten, die das Polygon definieren Ein Brush und entweder ein Rectangle-Objekt oder vier Integer, die ein Rechteck definieren (x-Koordinate oben links, dito eine y-Koordinate, Breite und Hhe) Ein Brush und ein Array von Rectangle-Objekten Ein Brush und ein Region-Objekt

FillPath FillPie

FillPolygon FillRectangle

FillRectangles FillRegion

Tabelle 13.4: Die Fill-Methoden und ihre Parameter

462

Figuren zeichnen

Listing 13.6 zeigt ein Beispiel fr die Verwendung der Fill-Methoden. Wenn der Benutzer die Maus klickt und zieht, zeichnet er ein Rechteck, das die Auswahl anzeigt. Wird die Maustaste losgelassen, fllt sich der Auswahlbereich mit einem Farbverlauf. Listing 13.6: Auswahlflchen fllen
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: using using using using System; System.Windows.Forms; System.Drawing; System.Drawing.Drawing2D;

namespace TYWinforms.Day13 { public class Listing136 : Form { private Point ptStartClick; private Point ptCurrentPos; private bool blnDrag = false; private bool blnFill = false; private GraphicsPath objPath; private PathGradientBrush objBrush; private Pen objPen = new Pen(Color.Black); private Rectangle rectSelect; public Listing136() { this.Text = "Listing 13.6"; this.Size = new Size(800,600); } protected override void OnMouseDown(MouseEventArgs e) { blnDrag = true; ptStartClick = new Point(e.X, e.Y); ptCurrentPos = ptStartClick; } protected override void OnMouseUp(MouseEventArgs e) { blnDrag = false; blnFill = true; } protected override void OnMouseMove(MouseEventArgs e) { ptCurrentPos = new Point(e.X, e.Y); if (blnDrag | blnFill) { this.Refresh(); } } protected override void OnPaint(PaintEventArgs e) {

463

Grafische Anwendungen mit GDI+

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:

if (blnDrag) { objPen.DashStyle = DashStyle.Dash; if (ptCurrentPos.X < ptStartClick.X & ptCurrentPos.Y < ptStartClick.Y) { rectSelect = new Rectangle(ptCurrentPos.X, ptCurrentPos.Y, ptStartClick.X - ptCurrentPos.X, ptStartClick.Y - ptCurrentPos.Y); } else if (ptCurrentPos.X < ptStartClick.X & ptCurrentPos.Y > ptStartClick.Y) { rectSelect = new Rectangle(ptCurrentPos.X, ptStartClick.Y, ptStartClick.X - ptCurrentPos.X, ptCurrentPos.Y - ptStartClick.Y); } else if (ptCurrentPos.X > ptStartClick.X & ptCurrentPos.Y < ptStartClick.Y) { rectSelect = new Rectangle(ptStartClick.X, ptCurrentPos.Y, ptCurrentPos.X - ptStartClick.X, ptStartClick.Y - ptCurrentPos.Y); } else { rectSelect = new Rectangle(ptStartClick.X, ptStartClick.Y, ptCurrentPos.X - ptStartClick.X, ptCurrentPos.Y - ptStartClick.Y); } e.Graphics.DrawRectangle(objPen, rectSelect); } if (blnFill && rectSelect.Width != 0 && rectSelect.Height != 0) { objPath = new GraphicsPath(); objPath.AddRectangle(rectSelect); objBrush = new PathGradientBrush(objPath); objBrush.CenterColor = Color.White; objBrush.SurroundColors = new Color[] {Color.Red, Color.Blue, Color.Orange, Color.Green}; e.Graphics.FillRectangle(objBrush, rectSelect); blnFill = false; } } public static void Main() { Application.Run(new Listing136()); } } }

Obwohl diese Anwendung kompliziert aussehen mag, ist sie es eigentlich nicht; es gibt nur eine Reihe von Bedingungen, die erfllt sein mssen. In Zeile 22 berschreiben wir das OnMouseDown-Ereignis der Code dafr ist identisch mit jenem, der fr das Zuweisen eines Delegaten zum MouseDown-Ereignis des For-

464

Figuren zeichnen

mulars zustndig ist (wir haben das Gleiche schon fr das OnPaint-Ereignis gemacht). Die Ereignisse OnMouseUp und OnMouseMove in den Zeilen 28 und 33 sind hnlich. Sobald OnMouseDown ausgefhrt wird, wissen Sie, dass der Benutzer die Maustaste gedrckt hat und so anzeigt, dass er bereit ist, eine Auswahl zu treffen (wir bezeichnen dies als den Auswahlmodus). Setzen Sie die blnDrag-Variable auf true, damit Ihre anderen Methoden wissen, dass Sie sich gerade im Auswahlmodus befinden, und holen Sie dann den Startpunkt der Auswahl vom MouseEventArgs-Objekt. ptCurrentPos in Zeile 25 ist ein weiteres Point-Objekt, das wir gleich benutzen werden. In OnMouseUp hat der Benutzer gerade die Maustaste losgelassen und zeigt so an, dass er mit seiner Auswahl fertig ist: Der Auswahlmodus ist somit beendet. Setzen Sie blnDrag auf false. Sobald die Auswahl getroffen ist, mssen Sie sie mit einem Farbverlauf fllen; Sie treten in den Fllmodus ein. Die OnMouseMove-Methode wird bei jeder Mausbewegung ausgefhrt. Dabei mssen Sie stets die neue Position des Mauszeigers erfassen, damit Sie die ausgewhlte Region genau bestimmen knnen. Die OnPaint-Methode in Zeile 39 ist ebenso fr das Markieren der vorgenommenen Auswahl zustndig wie fr das Fllen der Auswahl mit einem Farbverlauf, sobald der Benutzer den Auswahlmodus verlassen hat. Befinden wir uns jedoch noch im Auswahlmodus, dann ergibt Zeile 40 den Wert true und die Zeilen 41 bis 52 werden ausgefhrt. Sehen Sie sich Abbildung 13.9 an. Der erste Mausklick des Benutzers (also der Punkt, an dem die OnMouseDown-Methode ausgefhrt wird) findet am Achsenursprung statt. Von hier aus kann der Benutzer die Maus in vier Regionen (die Quadranten) bewegen. Denken Sie daran, dass sich in Windows Forms y-Werte erhhen, wenn Sie sich im Formular nach unten bewegen; das ist der Grund, warum sich die y-Achse entgegen der normalen Geometrie zu verhalten scheint. Aus der Benutzerauswahl mssen wir eine Region (in unserem Fall ein Rectangle) erstellen. Daher mssen wir die Ecken des Auswahlbereichs bestimmen. Die gngigste Situation (besonders bei Rechtshndern) sieht vor, sich in den Quadranten 2 zu bewegen. Hier ist der x-Wert positiv und der y-Wert positiv. Die obere linke Ecke ist der Ursprung. Die obere rechte Ecke entspricht dem y-Anfangswert, doch einem greren, neuen x-Wert. Die untere rechte Ecke ist ein grerer x- und y-Wert, und die untere linke Ecke hat den gleichen x-Wert wie der Ursprung, doch einen greren y-Wert. Es hilft dem Verstndnis, ein kleines Rechteck zu zeichnen, wie es in Abbildung 13.10 zu sehen ist.

465

Grafische Anwendungen mit GDI+

y- Achse

Quadrant 4
(negatives x, negatives y)

Quadrant 1
(positives x, negatives y)

Ausgangspunkt
x-Achse

Quadrant 3
(negatives x, positives y)

Quadrant 2
(positives x, positives y)

Abbildung 13.9: Vom Ausgangspunkt knnen Sie sich in vier Richtungen bewegen.
y- Achse

Quadrant 4
(negatives x, negatives y)

Quadrant 1
(positives x, negatives y)

Ausgangspunkt
x-Achse

Quadrant 2
(positives x, positives y)

Quadrant 3
(negatives x, positives y) Der Benutzer trifft seine Auswahl hier

Abbildung 13.10: Der Benutzer zieht die Maus hinunter und nach rechts, um eine Auswahl zu markieren.

Wir haben jedoch nur zwei Punkte, von denen wir ausgehen knnen: den startenden Mausklick (den in Zeile 24 erzeugten ptStartClick-Punkt) und die aktuelle Mausposition, die bei jeder Bewegung aktualisiert wird (Zeile 34). Um die von der Auswahlregion umschlossene Flche (ein Rectangle) zu bestimmen, haben Sie die

466

Figuren zeichnen

obere linke Ecke: x-Anfangswert, y-Anfangswert, Breite des Rectangle (festgelegt durch die obere rechte Ecke): aktueller x-Wert minus x-Anfangswert, Hhe des Rectangle: aktueller y-Wert minus y-Anfangswert.

Dieses Vorgehen klappt ausgezeichnet, wenn wir uns im Quadranten 2 befinden, in dem die aktuellen x- und y-Werte immer grer sind als die jeweiligen Anfangswerte. Sie knnen diese Gleichung sogar in Zeile 50 sehen. Wenn wir uns jedoch in einen der anderen Quadranten bewegen, besteht die Mglichkeit, dass die Gleichungen fr Hhe und Breite in der vorhergehenden Liste zu einem negativen Wert fhren und Rectangles lassen sich nicht mit negativen Werten erzeugen. Um also ein nicht-negatives Rectangle zu erstellen, mssen Sie Ihre Gleichungen dem jeweiligen Quadranten ein wenig anpassen. Die if-Statements in den Zeilen 43, 45 und 47 bestimmen, in welchem Quadranten in Bezug auf den Ausgangspunkt sich die Maus befindet (das letzte else in Zeile 49 setzt voraus, dass man sich in Quadrant 2 befindet). Die Statements in den Zeilen 44, 46 und 48 enthalten die verschiedenen Gleichungen, die sich auf die Quadranten anwenden lassen. Fr Quadrant 4 etwa (Zeilen 43 bis 44) sehen die Gleichungen folgendermaen aus:

obere linke Ecke: aktueller x-Wert, aktueller y-Wert Breite des Rectangle: x-Anfangswert minus aktueller x-Wert Hhe des Rectangle: y-Anfangswert minus aktueller y-Wert

Die Gleichungen fr Hhe und Breite sind denen fr Quadrant 2 entgegengesetzt. Diejenigen fr die anderen beiden Quadranten knnen Sie sich selbst ausrechnen, daher wollen wir gleich mit dem Code fortfahren. Nachdem Sie das Rectangle, das die Auswahlregion darstellt, korrekt bestimmt haben, rufen Sie in Zeile 52 die DrawRectangle-Methode auf, um einen Pen fr eine gestrichelte Linie zu verwenden. Da dies bei jedem Aufruf an OnPaint (also bei jeder Mausbewegung) erfolgt, wird berall dort, wohin der Benutzer den Mauszeiger bewegt, eine Umrisslinie gezogen; dies vermittelt den Eindruck, dass die fragliche Flche ausgewhlt wurde. Sobald der Benutzer die Maustaste losgelassen hat, wird der Auswahlmodus durch den Fllmodus abgelst, der in den Codezeilen 55 bis 64 umgesetzt ist. Um die Flche mit einem Farbverlauf zu fllen, verwenden Sie den Pinsel PathGradientBrush. Um ihn zu erzeugen, bentigen Sie die vier Eckpunkte Ihrer Auswahlregion. Statt zu versuchen, mit dem Rectangle direkt umzugehen, erstellen Sie auf der Grundlage des Rectangle ein GraphicsPath-Objekt. Zeile 56 erzeugt das GraphicsPath-Objekt und Zeile 57 fgt ihm das Rectangle hinzu. Nun knnen Sie den PathGradientBrush leicht in Zeile 58 erzeugen und die entsprechenden Farben einstellen. Zeile 62 ruft die FillRectangle-Methode auf, um die Auswahlregion zu fllen, und Zeile 63 beendet den Fllmodus.

467

Grafische Anwendungen mit GDI+

Um die Funktionsweise dieser Anwendung vollstndig zu verstehen, mssen Sie sie erstellen und ausprobieren. Abbildung 13.11 zeigt die Grundzge der Anwendung.

Abbildung 13.11: Wenn der Benutzer die Maustaste loslsst, wird die Auswahlflche mit einem Farbverlauf gefllt.

Die Anwendung demonstriert die gngigsten Aufgaben im Umgang mit GDI+: das Koordinieren der OnPaint-Methode mit den verschiedenen Benutzeraktionen. Da OnPaint so hufig auftritt, muss man dafr sorgen, dass man diese Methode richtig erstellt, so dass sie wei, was sie in einem fraglichen Augenblick zu tun hat. Dies wird blicherweise mit booleschen Variablen gehandhabt im Listing 13.6 geben sie Auswahl- und Fllmodi an. Ein unkonomischer Umgang mit Variablen unter GDI+ kann zu Performanzeinbuen fhren. Deklarieren Sie daher Ihre Variablen frhzeitig, instantiieren Sie sie aber nur dann, wenn Sie sie bentigen, und verwenden Sie sie wieder, sooft Sie knnen.

Figuren dauerhaft machen


Beachten Sie, wenn Sie die Anwendung aus Listing 13.6 ausfhren, dass alles, was Sie zuvor gezeichnet haben, verschwindet, sobald Sie ein neues Rechteck zu zeichnen anfangen. Es gibt nmlich eine groe Einschrnkung bei der Verwendung des Graphics-Objekts: Alles, was es zeichnet, bleibt nur so lange auf dem Bildschirm, bis OnPaint erneut aufgerufen wird.

468

Figuren zeichnen

Stellen Sie sich vergleichsweise vor, Sie trgen ein groes Gemlde. Es ist so gro, dass Sie nur eines auf einmal tragen knnen. Sie halten das Gemlde, bis Ihnen jemand ein anderes gibt, woraufhin Sie das erste weglegen mssen, um das zweite annehmen zu knnen. Die Methode OnPaint arbeitet auf die gleiche Weise: Sie kann nur den Gegenwert einer Zeichnung zu einem Methodenaufruf halten und wenn OnPaint das nchste Mal aufgerufen wird, verwirft sie, was immer sie zuvor gezeichnet hat, um erneut mit dem Zeichnen zu beginnen. Was auch immer Sie in einem Formular mit Hilfe des Graphics-Objekts zeichnen, wird also nicht Teil des Formulars. Das Objekt sitzt einfach auf dem Formular, bis OnPaint auftaucht, die Schiefertafel leer wischt und von vorn anfngt. Man kann die OnPaint-Einschrnkung umgehen. Indem man in eine Grafik zeichnet, kann man dafr sorgen, dass nderungen nicht verloren gehen, ganz unabhngig von der Zahl der OnPaint-Aufrufe. Dafr brauchen Sie kein bestimmtes Bild, sondern in Ihrer Anwendung nur eine brandneue Grafik zu entwerfen, die zunchst einfach leer ist. Ein Beispiel:
Bitmap imgWorking = new Bitmap(800, 600, PixelFormat.Format32bppArgb);

Die ersten beiden Parameter fr den Bitmap-Konstruktor sind die Breite und die Hhe. Der dritte Parameter besteht aus einem Wert der PixelFormat-Aufzhlung, der den Typ der zu erstellenden Grafik bestimmt. Hier erzeugen Sie eine 32-Bit-Grafik mit Alpha-Transparenz (mehr dazu in der Dokumentation zum .NET Framework). Wenn Sie etwas auf den Bildschirm zeichnen mssen, das dauerhaft sein soll, zeichnen Sie es auf diese Bitmap-Grafik. Mit folgendem Code knnen Sie von dieser Grafik ein Graphics-Objekt erhalten:
Graphics objG = Graphics.FromImage(imgWorking); FromImage ist eine statische Methode des Graphics-Objekts, die Sie berall verwenden knnen. Zum Beispiel knnen Sie das Bild mit einem weien Rechteck fllen: objG.FillRectangle(new SolidBrush(Color.White), new Rectangle(0,0,800,600));

Sie mssen lediglich dafr sorgen, dass die Grafik imgWorking auf den Bildschirm gezeichnet wird. Dafr gibt es verschiedene Mglichkeiten: Sie knnen die Grafik als Hintergrundbild des Formulars nehmen oder sie in der OnPaint-Methode anzeigen:
//das Hintergrundbild festlegen this.BackgroundImage = imgWorking; //OnPaint einsetzen protected override void OnPaint(PaintEventArgs e) { e.Graphics.DrawImage(imgWorking,new Point(0,0)); }

Mit diesen Techniken knnten Sie eine Anwendung mit mehr Grafikfunktionen erstellen.

469

Grafische Anwendungen mit GDI+

Figuren umwandeln
Das Graphics-Objekt verfgt ber drei Methoden, mit denen Sie alles, was Sie zeichnen, transformieren knnen, seien es Figuren oder Grafiken: Es handelt sich um RotateTransform, ScaleTransform und TranslateTransform. Die erste Methode dreht alle vom Graphics-Objekt gezeichneten Figuren, die zweite skaliert sie in der Gre und die dritte bewegt das Objekt in eine bestimmte Richtung. Ihre Einsatzweise ist sehr einfach:
e.Graphics.RotateTransform(angle) // Angle ist der Drehungswinkel // in Grad (Single-Typ) e.Graphics.ScaleTransform(x ,y) // x und y sind die Faktoren, um die die // Transformation in x- bzw. y// Richtung skaliert werden soll. e.Graphics.TranslateTransform(x ,y) //x und y sind die Werte der // Verschiebung (in Pixel) in // x- bzw. y-Richtung

Zum Beispiel zeichnet der folgende Code ein Rechteck, das um 30 Grad im Uhrzeigersinn gedreht wird:
e.Graphics.RotateTransform(30); e.Graphics.DrawRectangle(objPen, 0, 0, 200, 100);

Wenn Sie eine dieser Transformationsmethoden aufrufen, wird alles danach Gezeichnete ebenfalls entsprechend transformiert. Um in den Normalmodus zurckzukehren, rufen Sie die ResetTransform-Methode auf. (Fr Elemente, die bereits mit Hilfe der Transformationsmethoden gezeichnet wurden, bleibt die Transformation bestehen.) Wollen Sie nur bestimmte Objekte transformieren, so bietet das GraphicsPath-Objekt eine Transform-Methode an, mit der Sie alle erwhnten Transformationen ausfhren knnen doch es gibt einen Haken: Sie mssen das Matrix-Objekt verwenden, das eine Matrix aus drei mal drei Werten darstellt. Zwar verfgt Matrix ber ein paar benutzerfreundliche Methoden, doch um seine Leistungsfhigkeit wirklich nutzen zu knnen, mssten Sie sich an lineare Algebra erinnern, besonders an die Matrixtransformationsklassen. Daher werden wir uns auf die benutzerfreundlichen Methoden beschrnken (vgl. Matrix-Objekt in der Dokumentation). Um ein Objekt zu transformieren, mssen Sie zunchst ein GraphicsPath-Objekt aus dem umzuwandelnden Objekt erzeugen, daraufhin ein Matrix-Objekt erstellen, die passende Transformationsmethode aufrufen und schlielich die Transform-Methode des GraphicsPath-Objekts aufrufen. Beispielsweise so:
// Objekt erstellen, das als Pfad zu zeichnen ist GraphicsPath objPath = new GraphicsPath(); objPath.AddEllipse(0, 0, 140, 70);

470

Windows Forms beschneiden

// Matrix-Objekt erstellen und Transformationsmethoden aufrufen Matrix objMatrix = new Matrix(); objMatrix.Rotate(30); objPath.Transform(objMatrix); // die geeignete Draw-Methode aufrufen e.Graphics.DrawPath(objPen,objPath);

Tabelle 13.5 fhrt die benutzerfreundlichen Methoden von Matrix auf.


Methode
Reset Rotate RotateAt

Beschreibung Setzt alle Transformationen zurck. Dreht das Objekt um einen bestimmten Winkel (in Grad). Dreht das Objekt um den angegebenen Punkt mit dem angegebenen Winkel (in Grad). Zum Beispiel: RotateAt(30, new PointF(0.0, 0.0)); PointF ist einfach eine Gleitkommaversion von Point). Skaliert das Objekt in die x- und y-Richtungen (funktioniert wie Graphics.ScaleTransform).

Scale

Shear

Wendet einen Scherungsvektor auf das Objekt an (benutzt zwei Parameter vom Typ Single). Funktioniert wie Graphics.TranslateTransform.

Translate

Tabelle 13.5: Die Transformationsmethoden von Matrix

13.6 Windows Forms beschneiden


Unser letztes Thema heute beschftigt sich mit Regions (Bereichen). Das Haupteinsatzgebiet des Region-Objekts liegt im Beschneiden einer Zeichnung um bestimmte Teile. Wenn Sie ein Region-Objekt erstellen, werden alle Zeichnungsteile auerhalb des Bereichs abgeschnitten und nur der Innenbereich bleibt erhalten. Ein Region-Objekt wird gewhnlich entweder durch ein Rectangle erstellt oder mit einem GraphicsPath-Objekt, falls Sie komplizierter geformte Bereiche erstellen mssen. Weil das Beschneiden keine gewhnliche Situation in GDI+ ist, bergehen wir dieses Thema weitgehend. Doch ein sehr interessanter Nutzeffekt einer Region besteht darin, dass Sie damit Windows Forms erstellen knnen, die nicht rechteckig sind. Sie knnen dadurch dreieckige oder gar kreisfrmige Anwendungsfenster erhalten, wenn Sie wollen.

471

Grafische Anwendungen mit GDI+

Fgen Sie den folgenden Code in eine der heute erstellten Anwendungen hinzu:
Point[] polyPoints = { new Point(0, 0), new Point(300, 0), new Point(300, 75), new Point(150, 300), new Point(0, 75) }; GraphicsPath objPath = new GraphicsPath(); objPath.AddPolygon(polyPoints); this.Region =new Region(objPath);

Im GraphicsPath erzeugen Sie ein Vieleck mit der Form eines Baseballfeldes und setzen dann die Region-Eigenschaft des aktuellen Formulars auf das neue Polygon. Was Sie dann erhalten, sieht hnlich wie die Abbildung 13.12 aus.

Abbildung 13.12: Region lsst sich zur Erzeugung kreativ gestalteter Anwendungsfenster einsetzen.

Dieses ausgefallene Beispiel das Sie nicht unbedingt nachahmen mssen vermittelt Ihnen eine Vorstellung, wozu Region fhig ist: Alles auerhalb der Region wird nicht gezeichnet, doch Sie knnen mit der unsichtbaren Anwendung weiterhin interagieren. Sie mssen nur darauf achten, dass Sie nicht die Titelleiste beschneiden, oder Sie werden das Fenster nie bewegen oder die Anwendung nicht mit der Maus schlieen knnen.

472

Zusammenfassung

13.7 Zusammenfassung
Eines der Hauptziele von GDI+ besteht in der Vereinfachung grafischer Entwicklungsarbeit, indem es dem Entwickler eine abstrakte Schnittstelle fr den Umgang mit unterschiedlicher Grafik-Hardware bereitstellt, ohne die Leistung oder den Funktionsumfang zu beschrnken. Unter GDI+ knnen Sie die neuesten Grafiktrends wie etwa Antialiasing oder Texturabbildungen ausntzen. Heute haben Sie etwas ber die Klassen Pen (Stift) und Brush (Pinsel) erfahren. Pen wird fr das Ziehen von Linien, Brush fr das Fllen eingeschlossener Bereiche mit Farbe oder Textur verwendet. Der gebruchlichste Bereich, mit dem Sie zu tun haben, ist das Rectangle-Objekt, das praktisch alles darstellen kann, vom Windows Forms-Objekt bis zu einer markierten Auswahl. Als Nchstes erfuhren Sie vom Paint-Ereignis, das jedes Mal ausgelst wird, wenn das Formular neu gezeichnet werden muss. Weil es sich so oft ereignet, bietet es sich ideal an, gebruchliche Zeichenroutinen aufzunehmen. Doch je nach dem Aufbau Ihrer Anwendung bleiben Elemente, die whrend dieses Ereignisses gezeichnet werden, nicht bis zum nchsten Paint-Aufruf bestehen. Die einzige Mglichkeit, Text zu zeichnen, ist die Verwendung der DrawString-Methode (von dem Einsatz wie Text aussehender Figuren einmal abgesehen). Mit Hilfe der TextRenderingHint-Eigenschaft des Graphics-Objekts knnen Sie bestimmen, wie die Antialiasing-Qualitt des gezeichneten Textes aussehen wird. Das Zeichnen von Grafiken erfordert nur die DrawImage-Methode, die entweder ein Imageoder Bitmap-Objekt verwendet. Um Grafiken zu transformieren, knnen Sie ein Array aufbauen, das aus drei Point-Objekten besteht, die jeweils die transformierten Koordinaten oben links, oben rechts und unten links definieren. Zu den kompliziertesten Bereichen in GDI+ gehrt wohl das Zeichnen von Figuren, einfach aus dem Grund, dass es so viele Optionen dafr gibt es kann schwierig sein, sich an die jeweilige Parameterliste fr eine bestimmte Zeichenmethode zu erinnern. Zustzlich zu den Draw-Methoden stehen noch die Add- und Fill-Methoden zur Verfgung. Die Sequenz von Add-Methoden wird verwendet, um einem GraphicsPath-Objekt diverse Linien und Figuren hinzuzufgen. Die Fill-Methoden hingegen dienen dazu, begrenzte Bereiche mit Farben oder Texturen zu fllen. Figuren zu transformieren lsst sich in GDI+ einfach mit den Methoden RotateTransform, ScaleTransform und TranslateTransform bewerkstelligen. Diese drei Methoden betreffen das Graphics-Objekt und alles, was es im Nachhinein zeichnet. Wollen Sie aber nur ein bestimmtes Objekt transformieren, sollten Sie das Matrix-Objekt verwenden und eine seiner Transformationsmethoden.

473

Grafische Anwendungen mit GDI+

13.8 Fragen und Antworten


F Gibt es noch fortgeschrittenere GDI+-Fhigkeiten? A Sie haben richtig vermutet! Neben den hherwertigen Transformationsfhigkeiten des Matrix-Objekts gibt es noch zahlreiche Funktionen, die man meist nur errtert, wenn man viel mit Bildbearbeitungssoftware zu tun hat. Ein interessantes Leistungsmerkmal ist die Mglichkeit, Metadateien zu erzeugen und zu bearbeiten. Eine durch das Metafile-Objekt dargestellte Metadatei erlaubt Ihnen, Grafikbefehle aufzuzeichnen (das Verfahren hnelt Aktionen in Photoshop oder Makros in Word). Wenn Sie Aktionen zum wiederholten Mal gleichfrmig ausfhren, so knnen Sie diese Aktionen in einer Metadatei speichern und spter einfach die Datei aufrufen, statt den ganzen Code neu zu schreiben. Ein weiteres Merkmal ist die Fhigkeit, Grafiken umzufrben. Genau wie Sie eine
Matrix zur Transformation von Gre und Form eines Objekts einsetzen, so kn-

nen Sie auch Farbmatrizen benutzen, um die Farben einer Grafik zu beeinflussen. So knnen Sie etwa ein Farbbild in ein Schwarzweifoto verwandeln oder umgekehrt. GDI+ verfgt ber gengend Funktionen, um diesem Thema ein ganzes Buch zu widmen. Mehr Informationen finden Sie in der Dokumentation. F Wie erzeuge ich transparente Figuren und Grafiken? A Das ist sehr einfach: Legen Sie einfach einen Alpha-Wert fr eine Farbe fest, wenn Sie Ihren Pen oder Brush erzeugen. Sie knnen dies mit Hilfe der FromArgbMethode etwa so ausfhren:
Pen objPen = new Pen(Color.FromArgb(128,0,0,0)); // ein zu 50% schwarzer Strift (50% transparent)

Dann zeichnen Sie wie gewohnt Ihre Figuren. FromArgb bernimmt vier IntegerParameter, wovon jeder zwischen 0 und 255 liegt: einen Alpha- (Transparenz-) Wert sowie Werte fr Rot, Grn und Blau. Eine Grafik transparent zu machen, ist schon etwas schwieriger. Zu diesem Zweck mssten Sie etwas ber Farbmatrizen lernen, welches ein komplettes Kapitel beanspruchen kann. Sie knnen aber zumindest Teile einer Grafik durchsichtig machen, und zwar mit dem ColorMatrix-Objekt. Leider geht das ber den Rahmen dieses Buches hinaus.

474

Workshop

13.9 Workshop
Dieser Workshop soll Ihnen helfen, sich besser an die heute behandelten Begriffe zu erinnern. Es hilft Ihnen sehr, die Antworten vollstndig zu verstehen, bevor Sie weiterlesen. Die Antworten zu den Quizfragen und den bungen finden Sie in Anhang A.

Quiz
1. Wie heit das Hauptobjekt in GDI+? 2. Welche sind die zwei Mglichkeiten, um das Paint-Ereignis auszunutzen? Nennen Sie fr jede ein einfaches Beispiel. 3. Nennen Sie fnf Eigenschaften des Pen-Objekts. 4. Wahr oder falsch? Um eine Zeichenfolge zu zeichnen, verwendet man einen Pen. 5. Erlutern Sie den Begriff der Matrix. 6. Welche sind die fnf Brush-Typen? 7. Welche Methode muss man zuerst aufrufen, bevor man die Add-Methoden nutzen kann, um einem GraphicsPath-Objekt Figuren hinzuzufgen? 8. Handelt es sich bei Treppchen um das Ergebnis von Aliasing oder von Antialiasing?

bung
Erstellen Sie eine Zeichenapplikation, in der der Benutzer mehrere Zeichenfunktionen nutzen kann, darunter das Zeichnen mit einem Bleistift-artigen Mauszeiger, das Fllen von Rechtecken und das Radieren. Sorgen Sie fr die Dauerhaftigkeit der Zeichnungen. Setzen Sie das Standarddialogfeld FARBE ein, damit der Benutzer die Farbe des Stiftes oder des gefllten Rechtecks whlen kann. (Tipp: Verwenden Sie die Methode Graphics.FromImage.)

475

ActiveX

4 1

ActiveX

Das .NET Framework ist immer noch eine recht junge Technologie, so dass es noch nicht viele brauchbare Anwendungen gibt, die damit erstellt wurden. Mit .NET knnen Sie jedoch auf die Flle von Applikationen zugreifen, die vor .NET geschrieben wurden. Sie knnen deren Funktionsumfang in Ihre eigenen Anwendungen integrieren. Dies ist Ihnen mit der Technologie namens ActiveX mglich ein Hilfsmittel, mit dem sowohl .NET- als auch Nicht-.Net-Anwendungen miteinander kommunizieren und Daten gemeinsam nutzen knnen. ActiveX kann auf eine lange Entstehungsgeschichte zurckblicken, so dass bereits viele Entwickler damit vertraut sind. Heute lernen Sie,

wie ActiveX und COM zusammenarbeiten, wo OLE-Automatisierung ihren Platz hat, wie man einen Webbrowser erstellt, wie Sie Microsoft Word und Excel aus Ihrer eigenen Anwendung heraus ffnen, wie man ActiveX richtig einsetzt.

14.1 Einfhrung in ActiveX


Um die Funktionsweise von ActiveX zu verstehen, mssen Sie ein wenig in die Geschichte zurckgehen. Als Microsoft eine Technologie namens Object Linking and Embedding (OLE) einfhrte, ermglichte dies dem Benutzer, einen Dokumenttyp in einen anderen Typ einzubetten (und zu verknpfen). Abbildung 14.1 illustriert dieses Konzept. Mit Hilfe von OLE konnten Sie ein Word-Dokument in Ihre Anwendung einbetten, ohne irgendwelche zustzliche Funktionalitt schreiben zu mssen. In diesem Beispiel wrde man Word als Hosted-Anwendung (wrtlich: bewirtete Anwendung) bezeichnen und Ihre eigene als die Host-Anwendung (wrtlich: als Gastgeber). Durch ActiveX lsst sich jede Anwendung zu einem Host oder einer HostedApplikation machen. Wenn Sie einmal mit lteren Visual Basic-Versionen programmiert haben, ist Ihnen vermutlich OLE bekannt. Es erlaubte Ihnen, andere Anwendungen in Ihre eigene VB-Applikation einzubinden. Sie konnten dann die Fremdanwendung genau wie jedes normale Objekt nutzen. Dem Host stand jede ffentliche Funktion der gehosteten Anwendung zur Verfgung. Die Funktionalitt lie sich genau wie in .NET offen legen, nmlich mit Schlsselwrtern wie Public oder Protected. Eine gehostete Anwendung musste nicht alles offen legen, sondern nur den Funktionsumfang, der ihr notwendig erschien.

478

Einfhrung in ActiveX

Abbildung 14.1: Mit Hilfe von OLE knnten Sie WordFunktionen in einer anderen Anwendung nutzen.

Um das Jahr 1993 entwickelte Microsoft eine Technologie namens Component Object Model (COM). COM ist eine durchstrukturierte Menge von Regeln, an die sich Anwendungen halten knnen, um mit anderen Anwendungen ber das Betriebssystem zu kommunizieren. Zahlreiche Applikationen setzen COM ein, um sich mit Datenbanken zu verstndigen. Solange sich Ihre Anwendung an die COM-Regeln hlt, ist garantiert, dass sie mit jeder anderen COM-Anwendung kommunizieren kann. COM ist in der heutigen EDV bereits vorherrschend. COM baute auf den Fundamenten auf, die mit OLE gelegt worden waren. Dessen einfaches Verknpfen und Einbetten wurde dahingehend erweitert, dass OLE-Automatisierung realisierbar wurde (hnlich wie normales OLE, nur dass sich mit OLE-Automatisierung eine andere Anwendung aus der eigenen heraus steuern lie dies geht weit ber die Einbettung von Dokumenten hinaus). Mit der Zeit vernderte sich die Technik und das Internet wurde fr ein Massenpublikum zugnglich. OLE-Automatisierung erhielt einen anderen Namen (neben einigen technischen nderungen), nmlich ActiveX. ActiveX funktioniert also genau wie OLE-Automatisierung. Es war und ist noch immer sehr verbreitet, denn damit konnten sich selbst sehr unterschiedliche Anwendungen miteinander verstndigen. Die eine konnte in Visual C++ geschrieben sein, die andere etwa in Visual Basic, doch wenn beide die COM-Regeln befolgten, konnten sie zusammenarbeiten. Vor der Einfhrung von .NET erforderten unterschiedliche Programmiersprachen vllig verschiedene Programmiertechniken, was die Verstndigungsschwierigkeiten weiter erhhte und so wiederum die Bedeutung von ActiveX. Diese Situation ndert .NET. Da alle .NET-Anwendungen das gleiche Framework (Gerst) nutzen und mit der CLR arbeiten, sind sie automatisch in der Lage, sich miteinander zu verstndigen. ActiveX-Steuerelemente werden in .NET nicht mehr eingesetzt, denn sie sind von den Windows Forms-Steuerelementen ersetzt worden.

479

ActiveX

Das heit aber nicht, dass die Millionen von ActiveX-Anwendungen zu .NET kompatibel sind. Vielmehr musste .NET einen Weg finden, wie es ActiveX untersttzen kann, um solche Anwendungen zu hosten und selbst gehostet zu werden. Im nchsten Abschnitt betrachten wir die technischen Anforderungen an die Erstellung einer .NET-ActiveX-HostAnwendung sowie einige gngige ActiveX-Applikationen.

14.2 ActiveX-Steuerelemente
Windows Forms sind nicht gnzlich kompatibel zu ActiveX. Das einzige, was man in ein Windows-Formular einbauen kann, sind Windows Forms-Steuerelemente, also Objekte, die von der Klasse System.Windows.Forms.Control abgeleitet sind. Daher lsst sich eine ActiveX-Anwendung nicht direkt in ein Windows-Formular einbetten und umgekehrt. Doch es gibt einen Ausweg. Die Klasse System.Windows.Forms.AxHost ist eine spezielle Klasse, die als Brcke zwischen beiden Welten dient. AxHost selbst ist ein Windows FormsSteuerelement und lsst sich folglich in ein Windows Form einbetten. Doch einer ActiveXAnwendung erscheint AxHost wie ein ActiveX-Host. Es handelt sich im Wesentlichen um einen Behlter fr ActiveX-Objekte. Abbildung 14.2 zeigt das neue Programmiermodell.
Abbildung 14.2: AxHost hllt ActiveX-Anwendungen in der Verkleidung als Windows FormsSteuerelement ein, damit sie in Windows Forms eingesetzt werden knnen.

Das AxHost-Steuerelement ndert sich je nach der ActiveX-Anwendung, die es enthlt. Wenn Letztere beispielsweise eine Eigenschaft Text enthlt, so verfgt auch AxHost ber Text. Jede Funktion der gehosteten Anwendung wird AxHost hinzugefgt, damit sie sich in der .NET-Anwendung nutzen lsst. Bevor man AxHost einsetzen kann, ist jedoch ein weiterer Schritt zu bewltigen, der die kompilierten Quellcodedateien der gehosteten Anwendung betrifft. .NET-Anwendungen werden ja nicht zu ausfhrbarem Code kompiliert, sondern zu MSIL. MSIL enthlt Metadaten, die der CLR beschreibende Daten bereitstellen, die dann erst den Code ausfhrt (und weitere Steuerungsfunktionen bernimmt). Anwendungen vor .NET haben aber weder Metadaten noch MSIL, was sie fr die CLR quasi unverdaulich macht; nicht einmal das AxHost-Steuerelement kann da noch aushelfen.

480

ActiveX-Steuerelemente

Daher ist jede ActiveX-Anwendung, die Sie hosten wollen, erst einmal zu .NET kompatibel zu machen. Zum Glck mssen Sie dafr aber keinen Code umschreiben. Denn .NET stellt mit ActiveX Control Importer (Aximp.exe) ein spezielles Werkzeug bereit, das die ntigen Anpassungen automatisch vornimmt. Dieses Hilfsmittel verndert den Quellcode einer ActiveX-Anwendung in keiner Weise. Vielmehr untersucht es den Code, sprt den ganzen Funktionsumfang auf, der der ActiveX-Anwendung zur Verfgung steht, und erzeugt daraus MSIL und Metadaten. Die offen gelegte Funktionalitt der ActiveX-Anwendung wird auf Metadaten abgebildet, welche sich wiederum selbst gegenber der .NET-Anwendung offen legen. Diese Abbildung bzw. dieses Mapping wird als Wrapper eines ActiveX-Steuerelements bezeichnet (von engl. to wrap: einwickeln). Der Wrapper liegt als Schicht ber den vorhandenen Dateien der ActiveX-Anwendung und legt die darunter liegende Funktionalitt gegenber .NET mit Hilfe kompatibler Metadaten offen. (Der Wrapper hllt also den Funktionsumfang in der Anwendung ein.) Der Einsatz von Aximp.exe ist einfach. Geben Sie folgenden Code auf der Befehlszeile ein:
aximp ActiveXApplikation

Dieser Befehl erzeugt die Wrapperdatei gleichen Namens wie das Original. Aximp.exe hat wenige optionale Parameter, doch wir werden sie hier nicht behandeln, da sie sich meist um Sicherheitsaspekte drehen, zu denen wir spter noch kommen. Weil Aximp.exe Dateien erzeugt, die den gleichen Namen wie das Original tragen, knnte es vorkommen, dass Sie aus Versehen das Original berschreiben. Rufen Sie daher Aximp.exe niemals aus dem selben Ordner auf wie die Originaldatei! Rufen Sie es vielmehr aus einem anderen Ordner auf und geben Sie einen Wert fr den /out-Parameter an, mit dem Sie einen anderen Ausgabenamen festlegen knnen:
aximp SHDowVw.dll /out:Webbrowser.dll

Das Endergebnis besteht in einem neuen .NET-kompatiblen Windows Forms-Steuerelement, das von AxHost erbt und die Funktionalitt der gehosteten Anwendung kapselt. Sie knnen nun dieses Steuerelement genau wie jedes andere in Ihren Windows FormsAnwendungen einsetzen. Wir sehen uns dazu ein paar Beispiele an.

481

ActiveX

Das ActiveX-Steuerelement Webbrowser


Das ActiveX-Steuerelement Webbrowser bietet praktisch den gleichen Funktionsumfang wie Internet Explorer. Tatschlich nutzt Internet Explorer dieses ActiveX-Steuerelement selbst, um mit dem Web zu interagieren. Mit dem Steuerelement knnen Sie ber das Internet auf eine Webseite zugreifen und die Ergebnisse in Ihrem Formular anzeigen. Doch worin besteht der Unterschied zum WebRequest-Objekt von Tag 12? Das Steuerelement parst den gelieferten HTML-Code und zeigt ihn als Webseite an, wohingegen WebRequest lediglich den HTML-Code liefert, aber nicht als Webseite anzeigt. Das ActiveXSteuerelement Webbrowser ist also eine Mglichkeit, einfach Webseiten wie im Browser anzuzeigen. Zunchst aber mssen Sie die kompilierte Datei suchen, die die Funktionalitt des ActiveX-Steuerelements enthlt. Diese Datei heit shdocvw.dll und findet sich normalerweise im Ordner C:\winnt\system32. Fhren Sie aus dem (hoffentlich wie empfohlen angelegten) Ordner c:\winforms\day14 von der Befehlszeile aus folgendes Kommando aus:
aximp c:\winnt\system32\shdocvw.dll

Sie sollten das in Abbildung 14.3 gezeigte Ergebnis sehen.

Abbildung 14.3: Aximp.exe erzeugt aus shdocvw.dll zwei Dateien.

Es werden zwei Dateien erzeugt SHDocVw.dll und AxSHDocVw.dll , weil es zwei exponierte Klassen in der ursprnglichen Datei shdocvw.dll gab. Fr unser Beispiel bentigen Sie nur die zweite Datei. Aximp.exe legt jede dieser Klassen in einem Namensraum gleichen Namens ab, so dass Sie in Ihrem Code leicht auf sie verweisen knnen. Als Nchstes setzen wir das neue Steuerelement in einer Anwendung ein. Um .dllDateien zu importieren, verwenden Sie das Schlsselwort using (Imports in VB .NET). Listing 14.1 zeigt den Code, der einen einfachen Webbrowser erzeugt.

482

ActiveX-Steuerelemente

Listing 14.1: Ein einfacher Webbrowser, in dem ActiveX eingesetzt wird


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: 42: } public static void Main() { Application.Run(new Listing141()); } } } using using using using System; System.Windows.Forms; System.Drawing; AxSHDocVw;

namespace TYWinforms.Day14 { public class Listing141 : Form { private TextBox tbAddress = new TextBox(); private Button btGo = new Button(); private AxSHDocVw.AxWebBrowser objBrowser = new AxSHDocVw.AxWebBrowser(); public Listing141() { tbAddress.Width = 650; tbAddress.Location = new Point(50,0); tbAddress.Text = "http://www.clpayne.com"; btGo.Size = new Size(50,20); btGo.Location = new Point(0,0); btGo.Text = "Go!"; btGo.Click += new EventHandler(this.Go); objBrowser.Size = new Size(800,550); objBrowser.Dock = DockStyle.Bottom; this.Text = "Listing 14.1"; this.Size = new Size(800,600); this.Controls.Add(objBrowser); this.Controls.Add(tbAddress); this.Controls.Add(btGo); } private void Go(Object Sender, EventArgs e) { Object arg1 = 0; Object arg2 = ""; Object arg3 = ""; Object arg4 = ""; objBrowser.Navigate(tbAddress.Text, ref arg1, ref arg2, ref arg3, ref arg4);

483

ActiveX

In Zeile 4 importieren wir den neu erzeugten Namensraum AxSHDocVw, der das Webbrowser-ActiveX-Steuerelement enthlt. Der Namensraum enthlt alle Elemente, die man zum Websurfen bentigt. In Zeile 10 erzeugen wir das AxWebBrowser-Objekt, mit dem wir Webseiten abrufen und anzeigen. Die Zeilen 8 und 9 erstellen ein Textfeld und eine Schaltflche, mit denen der Benutzer einen URL eingeben kann, um an diese Adresse zu surfen. Vorerst setzen Sie in Zeile 15 diesen URL auf http://www.clpayne.com. In diesem Listing interessiert vor allem noch der Abschnitt mit der Methode Go in den Zeilen 32 bis 36. Zeile 33 erzeugt eine Reihe von Object-Variablen (gleich mehr dazu). Zeile 35 ruft die Navigate-Methode des AxWebBrowserObjekts auf, das die eigentliche Anforderung, das Abrufen und die Anzeige einer Webseite ausfhrt. Der erste Parameter dieser Methode ist der anzusteuernde URL, der vom Textfeld tbAddress geliefert wird. Die nchsten vier Parameter sind Referenzen auf die vier in Zeile 33 erzeugten Variablen. Dieser Code sieht ein wenig seltsam aus, weil es sich nicht um normale .NET-Syntax handelt; er wird vom Webbrowser ActiveX-Steuerelement gefordert, um ordnungsgem zu funktionieren. (Merken Sie sich einfach die Syntax, wenn Sie dieses ActiveX-Steuerelement erneut verwenden wollen.) In VB .NET sind die vier letzten Parameter nicht erforderlich. Also knnen Sie einfach schreiben:
objBrowser.Navigate(tbAddress.Text)

Das ist nur eine der Eigenheiten von ActiveX. Wenn Sie diese Anwendung kompilieren, denken Sie bitte daran, einen Verweis auf die Datei AxSHDocVw.dll einzufgen:
csc /t:winexe /r:system.dll /r:system.windows.forms.dll /r:system.drawing.dll / r:axshdocvw.dll listing14.1.cs

Abbildung 14.4 zeigt das Ergebnis, nachdem die GO-Schaltflche angeklickt wurde. Der schwierigste Teil hierbei ist es gewesen, die richtige .dll-Datei zu lokalisieren und die Methoden des ActiveX-Steuerelements zu entdecken. Danach sieht der Code doch recht vertraut aus. Obwohl es keinen wirklich neuen Code gibt, gewinnen Sie durch das ActiveX-Steuerelement doch eine Menge zustzlicher Funktionen hinzu. Um herauszufinden, welche Funktionen ein ActiveX-Steuerelement bereithlt, knnen Sie folgende Methoden einsetzen. Sie knnen in Visual Studio .NET den Objektkatalog benutzen oder die zum ActiveX-Steuerelement gehrende Dokumentation lesen. Kommen die Steuerelemente von Microsoft, probieren Sie es mit Msdn.Microsoft.com. Schlielich knnen Sie es mit dem Intermediate Language Disassembler-Tool (ildasm.exe) versuchen. Dieses Hilfsmittel sieht

484

ActiveX-Steuerelemente

sich die Metadaten einer .NET-Datei an und liefert Informationen ber verfgbare Methoden, Eigenschaften, Ereignisse und andere Mitglieder. So liefert etwa der Befehl
ildasm listing14.1.exe

Informationen ber die Main- und Go-Methoden sowie die Parameter, die sie erfordern, und ber die Variablen btGo, objBrowser und tbAddress. Dieses Tool lsst sich nur bei .NET-Dateien einsetzen.

Abbildung 14.4: Die Webseite wird im ActiveX-Steuerelement angezeigt.

Die Typbibliotheken von Microsoft Office


Mit ActiveX knnen Sie auch Anwendungen wie Microsoft Word oder Excel steuern, obwohl sich das Vorgehen von dem bisher beschriebenen unterscheidet. Insbesondere legen diese Anwendungen ihre Funktionalitt nicht in der gleichen Weise wie herkmmliche ActiveX-Anwendungen frei, was Sie aber nicht davon abhlt, sie nach den gleichen Mechanismen zu verwenden. In der Programmierung gibt es einen speziellen Dateityp namens Typbibliothek. Eine solche Datei wird nur dazu erstellt, um von einer Anwendung benutzt zu werden und ihr Definitionen fr bestimmte Funktionen bereitzustellen (hnlich wie Klassen in .NET). Jede Anwendung in Microsoft Office verfgt ber (mindestens) eine solche mit ihr verknpfte Typbibliothek. Sie definiert die Klassen und Funktionen, die von der Anwendung benutzt werden.

485

ActiveX

Obwohl die Typbibliothek-Dateien jeweils zu einer bestimmten Anwendung gehren, knnen Sie doch die ActiveX-Technik benutzen, um sie in anderen Anwendungen zu verwenden. Das bedeutet, dass Sie dann Office-Dokumente in Ihrer eigenen Anwendung ffnen oder erstellen knnen. Hherwertige Funktionen wie die MicrosoftRechtschreibprfung und die Grammatikprfung lassen sich ebenfalls so nutzen. Wie alte .dll-Dateien sind auch Typbibliothek-Dateien nicht mit dem .NET Framework kompatibel, so dass Sie sie anpassen mssen. Das erledigt das Type Library Importer-Tool (tblimp.exe), das auf hnliche Weise arbeitet wie der ActiveX Control Importer (aximp.exe).
tlbimp TypbibliothekName

beispielsweise erzeugt einen Wrapper um die betreffende Typbibliothek, der die erforderlichen Metadaten enthlt, um mit der CLR zusammenzuarbeiten. Die folgenden Abschnitte setzen voraus, dass Sie Microsoft Office 2000 installiert haben. Frhere Office-Versionen knnen bei der Verwendung dieses Codes abweichende Ergebnisse liefern. In jedem Fall mssen Sie eine Microsoft Office-Installation haben, damit Sie die Beispiele nutzen knnen.

Word via ActiveX nutzen


Wir experimentieren ein wenig mit Word, um herauszufinden, welche Funktionen wir borgen knnen. Die mit Word verknpfte Typbibliothek heit msword9.olb und findet sich blicherweise im Ordner c:\Programme\Microsoft Office\Office. Vom Ordner c:\winforms\day14 aus geben Sie in der Befehlszeile folgendes Kommando ein und drcken ():
tlbimp "c:\Programme\Microsoft Office\Office\msword9.olb"

Sie sollten folgendes Ergebnis sehen:


TlbImp -Type Library to .NET Assembly Converter Version 1.0.2914.16 Copyright (C)Microsoft Corp.2001.All rights reserved. Type library imported to Word.dll

Die neu erstellte Datei Word.dll enthlt jetzt die ganze Funktionalitt, die Sie zum Arbeiten mit Word-Dokumenten bentigen. Listing 14.2 zeigt eine Beispielanwendung. Listing 14.2: Microsoft Word via ActiveX nutzen
1: 2: 3: 4: using using using using System; System.Windows.Forms; System.Drawing; Word;

486

ActiveX-Steuerelemente

5: 6: 7: namespace TYWinforms.Day14 { 8: public class Listing142 : Form { 9: private Button btView = new Button(); 10: private Button btViewInWord = new Button(); 11: private RichTextBox rtbContent = new RichTextBox(); 12: 13: private Word.Application appWord; 14: private Word.Document docWord; 15: private Word._Document docWorking; 16: private Word.Documents colDocs; 17: private Word.Range objRange; 18: 19: 20: private object missing = System.Reflection.Missing.Value; 21: 22: public Listing142() { 23: btView.Text = "Klicken Sie bitte hier, um ein Word-Dokument zu  ffnen"; 24: btView.Location = new Point(150,50); 25: btView.Size = new Size(150,50); 26: btView.Click += new EventHandler(this.OpenDoc); 27: 28: btViewInWord.Text = "Klicken Sie bitte hier, um ein Word-Dokument  in Word zu ffnen"; 29: btViewInWord.Location = new Point(350,50); 30: btViewInWord.Size = new Size(150,50); 31: btViewInWord.Click += new EventHandler(this.OpenDocInWord); 32: 33: rtbContent.Dock = DockStyle.Bottom; 34: rtbContent.Size = new Size(800,400); 35: 36: this.Text = "Listing 14.2"; 37: this.Size = new Size(800,600); 38: this.Controls.Add(btView); 39: this.Controls.Add(btViewInWord); 40: this.Controls.Add(rtbContent); 41: } 42: 43: private void OpenDoc(Object Sender, EventArgs e) { 44: OpenFileDialog dlgOpen = new OpenFileDialog(); 45: 46: if (dlgOpen.ShowDialog() == DialogResult.OK) { 47: InitializeWord(dlgOpen.FileName); 48: rtbContent.Text = objRange.Text;

487

ActiveX

49: CloseWord(); 50: } 51: } 52: 53: private void OpenDocInWord(Object Sender, EventArgs e) { 54: OpenFileDialog dlgOpen = new OpenFileDialog(); 55: 56: if (dlgOpen.ShowDialog() == DialogResult.OK) { 57: InitializeWord(dlgOpen.FileName); 58: appWord.Visible = true; 59: } 60: } 61: 62: private void InitializeWord(string filename) { 63: appWord = new Word.Application(); 64: colDocs = appWord.Documents; 65: 66: object strFilename = (object) filename; 67: 68: docWorking = (_Document)docWord; 69: docWorking = colDocs.Open(ref strFilename, ref missing, ref  missing, ref missing, ref missing, ref missing, ref missing,  ref missing, ref missing, ref missing, ref missing, ref missing); 70: objRange = docWorking.Range(ref missing, ref missing); 71: } 72: 73: private void CloseWord() { 74: docWorking.Close(ref missing, ref missing, ref missing); 75: appWord.Quit(ref missing, ref missing, ref missing); 76: } 77: 78: public static void Main() { 79: System.Windows.Forms.Application.Run (new Listing142()); 80: } 81: } 82: }

Da dieser Code recht kompliziert ist, sollten wir uns nur schrittweise durcharbeiten. Zunchst ist der Konstruktor in den Zeilen 22 bis 41 doch recht standardmig. Er richtet einige Schaltflchen und ein RichTextBox-Steuerelement ein, das die Inhalte derjenigen Word-Dokumente enthlt, die wir ffnen. Beachten Sie die Ereignishandler fr die verschiedenen Schaltflchen: Einer ist dafr gedacht, ein Word-Dokument zu ffnen und dessen Inhalt im RichTextBox-Steuerelement abzulegen, und die andere soll ein Word-Dokument in Word selbst ffnen. Gleich mehr zur Implementierung dieser Ereignishandler.

488

ActiveX-Steuerelemente

In Zeile 4 sehen Sie einen neuen Namensraum: Word. Er wurde beim Einsatz des Type Library Importers erzeugt und enthlt alle Word-Objekte, auf die Sie in der Anwendung zurckgreifen. Die Zeilen 13 bis 17 deklarieren mehrere wichtige Variablen. Die erste, namens appWord, ist vom Typ Word.Application (aus dem Word-Namensraum) und stellt die eigentliche Microsoft Word-Anwendung dar. Bevor Sie Dokumente ffnen knnen, muss natrlich die Word-Anwendung gestartet werden, daher ist die Variable wichtig. Die zweite, namens docWord, stellt ein beliebiges Word-Dokument dar. (Beachten Sie, dass eine Word-Anwendung und ein Word-Dokument zwei verschiedene Dinge sind; eine Anwendung kann geffnet sein, ohne dass Dokumente geffnet sind.) Zeile 15 deklariert eine spezielle Variable namens docWorking, die vom Typ _Document ist. Der Unterstrich vor dem Namen gehorcht einer Konvention aus C und C++, die besagt, dass dieser Datentyp eine Referenz auf eine Speicherstelle ist. Dieser Datentyp wird aber in VB .NET und in C# nicht verwendet, auer ltere Technologien (vor .NET) zu referenzieren. Fr den Moment gengt es zu wissen, dass diese Variable ein bestimmtes Word-Dokument im Unterschied zu einem generischen Dokument wie bei der docWord-Variablen reprsentiert. Zeile 16 deklariert die colDocs-Variable, die eine Auflistung von Word-Dokumenten reprsentiert. Das ist wichtig, denn in Word muss jedes Dokument einer Auflistung angehren, selbst wenn diese nur aus einem Dokument besteht. Die Zeile 17 deklariert ein Word.Range-Objekt, das einen Textbereich in einem Dokument reprsentiert (hnlich den Bereichen im RichTextBoxSteuerelement). Ein weiterer Spezialtyp von Variablen wird in Zeile 20 deklariert: ein Objekt mit dem Wert Missing. Es stammt aus dem Namensraum System.Reflection und wird allgemein zu Darstellung optionaler Methodenparameter benutzt. Solche optionalen Methodenparameter sind Ihnen hier schon oft begegnet; wenn man den Parameter nicht festlegen will, lsst man ihn frei. Solche Parameter gab es auch schon vor .NET. Das Problem entsteht daraus, dass sie in .NET nicht gut mit jenen aus Alt-Anwendungen zusammenarbeiten. Optionale Methodenparameter aus Zeiten vor.NET werden zwingend in .NET erforderlich. Wenn man sich aber trotzdem nicht auf einen Wert dafr festlegen will, benutzt man einfach den Missing-Wert. Dieser bezeichnet also Parameter, die weiterhin optional bleiben sollen. Beispiele dafr finden Sie im Code weiter unten. Auch diesem Variablentyp begegnen Sie nur, wenn Sie mit lteren Technologien arbeiten. Wir berspringen die Ereignishandler zunchst und kommen zur Methode
InitializeWord in Zeile 62. Sie ist zustndig fr das Einrichten, Starten und

489

ActiveX

Laden eines Word-Dokuments. Sie bernimmt einen Zeichenfolgenparameter, der die zu ffnende Word-Datei angibt. In den Zeilen 63 und 64 instantiieren Sie die Word-Objekte Application und Documents. Zeile 66 scheint etwas Komisches zu bewirken: Sie wandelt den Dateinamen, der an InitializeWord bergeben wurde, in einen Object-Datentyp um. Das ist im Interesse der Kompatibilitt mit lterer Technik notwendig. Zeile 68 instantiiert das docWorking-Dokument. Dies ist, wie erwhnt, ein bestimmtes Word-Dokument. Nun wird es interessant: Man kann ein bestimmtes Word-Dokument nicht direkt erstellen (und folglich docWorking nicht direkt instantiieren). Man muss zunchst ein generisches Word-Dokument erzeugen und erst daraus ein bestimmtes erzeugen. Das ist der Grund, warum Zeile 68 das generische Dokument in ein spezifisches umwandelt und es daraufhin der docWorking-Variablen zuweist. Keine Angst: Diese verwirrende Situation tritt uerst selten auf. Es liegt am Design von Word. Zeile 69 ist fr das eigentliche ffnen eines Word-Dokuments zustndig. Sie ruft die Open-Methode aus dem Documents-Auflistungsobjekt auf. Der erste Parameter ist die Zeichenfolge fr die zu ffnende Datei. Die folgenden elf Parameter waren alle (zuvor) optional, doch auf Grund der Umwandlung in .NET mssen Sie sie mit dem Missing-Wert versehen. Beachten Sie das Schlsselwort ref (vgl. auch Listing 14.1), das von lteren ActiveX-Technologien wegen der Umwandlung zu .NET gefordert wird. Jeder Aufruf an eine Methode, die nicht zu .NET gehrt, muss das ref-Schlsselwort fr jeden ihrer Parameter verwenden. Zeile 70 instantiiert die objRange-Variable, die den gesamten Textbereich im Dokument enthlt. Die CloseWord-Methode in den Zeilen 73 bis 76 schliet alle geffneten Word-Dokumente und beendet die Word-Anwendung mit zwei relativ einfachen Methoden. Sie rufen zuerst die Close-Methode des docWorking-Objekts auf, um so alle offenen Dokumente zu schlieen. Die drei optionalen Parameter werden wieder durch Missing ersetzt. Zeile 75 schliet die Word-Anwendung, indem sie die Quit-Methode aufruft und die optionalen Parameter bereitstellt. Zum Schluss kommen wir auf die Ereignishandler in den Zeilen 43 bis 51 und 53 bis 60 zurck. Sie hneln einander hinsichtlich ihrer Funktion. Beide ffnen ein OpenFileDialog-Dialogfeld, um dem Benutzer die Auswahl einer Datei zu ermglichen. Danach rufen sie die benutzerdefinierte Funktion InitializeWord auf (die die Word-Anwendung und das Dokument initialisiert und ffnet). Da die OpenDoc-Methode den Text des Word-Dokuments im RichTextBox-Steuerelement anzeigen will, holt sie den Inhalt mit Hilfe der Text-Eigenschaft des Range-Objekts, das zuvor mit InitializeWord angelegt wurde. OpenWord ruft dann die CloseWord-Methode auf, um Word zu schlieen (da es ja jetzt den

490

ActiveX-Steuerelemente

Text hat). OpenDocInWord hingegen kmmert sich nicht um diese letzten beiden Schritte, sondern setzt einfach die Visible-Eigenschaft auf true, um so die Word-Anwendung sichtbar zu machen. Kompilieren Sie diese Anwendung und fhren Sie sie aus. (Vergessen Sie nicht den Verweis auf die Assembly Word.dll.) ffnen Sie ein Dokument mit Hilfe der Schaltflche KLICKEN SIE BITTE HIER, UM EIN WORD-DOKUMENT ZU FFNEN. Nach der Auswahl eines Dokuments sollten Sie ein hnliches Ergebnis wie in Abbildung 14.5 sehen, je nach dem ausgewhlten Dokument. (Beachten Sie, dass Sie bei dem geschilderten Vorgehen die Word-Formatierung im RichTextBox-Steuerelement verlieren. Der Grund: Das Steuerelement hat weniger Fhigkeiten als Word.)

Abbildung 14.5: Ihr Word-Dokument sollte im RichTextBoxSteuerelement angezeigt werden.

Probieren Sie die andere Schaltflche (KLICKEN SIE BITTE HIER, UM EIN WORD-DOKUWORD ZU FFNEN) aus, um ein Word-Dokument in Word selbst zu ffnen. Word startet, um ein von Ihnen ausgewhltes Dokument anzuzeigen, so dass Sie es ganz normal bearbeiten knnen. Abbildung 14.6 zeigt dafr ein Beispiel.
MENT IN

Excel via ActiveX verwenden


Excel arbeitet mit einem hnlichen Prozess wie Word. Die Typbibliothek fr Excel ist excel9.olb und befindet sich im selben Ordner wie die fr Word: c:\Programme\Microsoft Office\Office. Importieren Sie diese Bibliothek mit dem folgenden Befehl:
tlbimp "c:\Programme\Microsoft Office\Office\excel9.olb"

491

ActiveX

Abbildung 14.6: Mit ActiveX knnen Sie Word aus Ihrer eigenen Anwendung heraus ffnen und steuern.

Listing 14.3 zeigt ein Beispiel, wie man Excel nutzen kann, um die Elemente in einem ComboBox-Steuerelement zu sortieren. Beachten Sie, dass dies lediglich ein Beispiel fr die Verwendung von Excel via ActiveX ist: Eine vergleichbare Funktionalitt lsst sich auch mit Hilfe der Sorted-Eigenschaft des ListBox-Steuerelements erzielen. Listing 14.3: Sortieren mit Excel und ActiveX
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: using using using using using System; System.Windows.Forms; System.Drawing; System.Reflection; Excel;

namespace TYWinforms.Day14 { public class Listing143 : Form { private System.Windows.Forms.Button btAdd = new System.Windows.Forms.Button(); private System.Windows.Forms.Button btSort = new System.Windows.Forms.Button(); private ComboBox cboUnsorted = new ComboBox(); private System.Windows.Forms.ListBox lbSorted = new System.Windows.Forms.ListBox(); private Excel.Application objApp; private Workbooks colWBooks;

492

ActiveX-Steuerelemente

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: 61:

private _Workbook objWBook; private Sheets colSheets; private _Worksheet objSheet; private object missing = Missing.Value; public Listing143() { btAdd.Text = "Hinzufgen"; btAdd.Location = new System.Drawing.Point(210,50); btAdd.Click += new EventHandler(this.Add); btSort.Text = "-> Sortieren ->"; btSort.Location = new System.Drawing.Point(300,250); btSort.Size = new Size(150,50); btSort.Click += new EventHandler(this.Sort); cboUnsorted.Location = new System.Drawing.Point(50,50); cboUnsorted.Size = new Size(150,500); cboUnsorted.DropDownStyle = ComboBoxStyle.Simple; lbSorted.Location = new System.Drawing.Point(550,50); lbSorted.Size = new Size(150,500); this.Text = "Listing 14.3"; this.Size = new Size(800,600); this.Controls.Add(btSort); this.Controls.Add(btAdd); this.Controls.Add(cboUnsorted); this.Controls.Add(lbSorted); } private void Add(Object Sender, EventArgs e) { cboUnsorted.Items.Add(cboUnsorted.Text); cboUnsorted.Text = ""; } private void Sort(Object Sender, EventArgs e) { lbSorted.Items.Clear(); int intCount = cboUnsorted.Items.Count; objApp = new Excel.Application(); colWBooks = objApp.Workbooks; objWBook = colWBooks.Add(XlWBATemplate.xlWBATWorksheet); colSheets = objWBook.Worksheets; objSheet = (_Worksheet) colSheets.get_Item(1);

493

ActiveX

62: 63: 64: 65: 66: 67: 68: 69: 70:

Range objRange = objSheet.get_Range("A1", Convert.ToChar(intCount+64).ToString() + "1"); String[] arrValues = new String[intCount]; for (int i = 0; i < intCount; i++) { arrValues[i] = cboUnsorted.Items[i].ToString(); } objRange.Value2 = arrValues; objRange.Sort(objSheet.Rows[1, Missing.Value], XlSortOrder.xlAscending, Missing.Value, Missing.Value, XlSortOrder.xlAscending, Missing.Value, XlSortOrder.xlAscending, XlYesNoGuess.xlNo, Missing.Value, Missing.Value, XlSortOrientation.xlSortRows, XlSortMethod.xlStroke); Object[,] arrSorted; arrSorted = (Object[,])objRange.Value2; for (int i=arrSorted.GetLowerBound(0); i <= arrSorted.GetUpperBound(0); i++) { for (int j=arrSorted.GetLowerBound(1); j <= arrSorted.GetUpperBound(1); j++) { lbSorted.Items.Add(arrSorted[i,j]); } } cboUnsorted.Items.Clear(); objWBook.Saved = true; objApp.Quit(); } public static void Main() { System.Windows.Forms.Application.Run (new Listing143()); } } }

71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90:

Listing 14.3 hnelt stark dem Listing 14.2, da ja hnliche Funktionen abgedeckt werden. Der Konstruktor in den Zeilen 22 bis 45 ist recht standardmig: Er initialisiert ein Kombinationsfeld, ein Listenfeld und einige Schaltflchen. Beachten Sie, dass wir die vollen Namen der Steuerelemente einschlielich der Namensrume angeben mssen, denn der Excel-Namensraum verfgt ber Steuerelemente mit dem gleichen Namen durch den vollstndig qualifizierten Namen des Namensraumes knnen wir die gewnschten Steuerelement unterscheiden. Auch die Zeilen 1 bis 12 sollten recht vertraut aussehen (denken

494

ActiveX-Steuerelemente

Sie daran, den Excel-Namensraum zu referenzieren). Die Add-Methode in den Zeilen 47-50 ist der Ereignishandler fr die HINZUFGEN-Schaltflche: Sie fgt dem ComboBox-Steuerelement neue Eintrge hinzu. Wir sehen uns gleich noch den Ereignishandler fr die SORTIEREN-Schaltflche an; er ruft die SortMethode auf, die den Groteil der Verarbeitung erledigt. Untersuchen wir als Nchstes die verwendeten Variablen. In den Zeilen 14 bis 18 sehen Sie diverse Excel-Typen, die den Word-Typen hneln. Das Programmiermodell fr Excel sehen Sie in Abbildung 14.7: eine verhltnismig tief gestaffelte Hierarchie. Um also Daten in Excel eingeben zu knnen, muss man weit hinuntergehen, bis man ein Range-Objekt findet. Die Zeilen 14 bis 18 deklarieren alle Hierarchieobjekte mit Ausnahme von Range, zu dem wir gleich kommen. Beachten Sie das _WorkBook-Objekt in Zeile 16: Es stellt ein bestimmtes Objekt dar (hnlich wie _Document in Word). Zeile 20 erzeugt das Missing-Objekt, das dazu verwendet wird, optionale Parameter in Methodenaufrufen an Excel-Objekte darzustellen.
Anwendung

Arbeitsmappen-Sammlung

Arbeitsmappe Arbeitsmappe

Das Excel-Programmiermodell

Arbeitsblatt-Sammlung

Arbeitsblatt Arbeitsblatt Bereich

Abbildung 14.7: Das Excel-Programmiermodell fut auf verschiedenen Auflistungen und Objekten.

Die Sort-Methode ab Zeile 52 ist diejenige Methode, die die Excel-Objekte tatschlich erstellt und die in das ComboBox-Steuerelement eingegebenen Werte sortiert. Da die Methode ein wenig kompliziert ist, sollten wir die einzelnen Schritte durchgehen: 1. Erstellt eine neue Excel-Anwendung. 2. Holt eine leere Arbeitsmappen-Auflistung von der Anwendung. 3. Fgt dieser Auflistung eine neue Arbeitsmappe hinzu. 4. Holt die leere Arbeitsblatt-Auflistung aus der neuen Arbeitsmappe. 5. Holt das erste Arbeitsblatt aus der Arbeitsblatt-Sammlung. 6. Ruft einen Bereich von Zellen ab.

495

ActiveX

7. Erstellt aus den Eintrgen im Kombinationsfeld ein Array von Werten und platziert es im ausgewhlten Bereich. 8. Sortiert diese Werte. 9. Holt die eben sortierten Werte und fgt sie in das Listenfeld hinzu. 10. Schliet die Excel-Anwendung. Es handelt sich zwar um einen recht langwierigen Prozess, die Werte zu sortieren, doch der meiste Aufwand betrifft die Initialisierung von Excel. Die ersten fnf Schritte werden in den Zeilen 56 bis 60 erledigt; da sie praktisch selbsterklrend sind, fahren wir fort. In Zeile 62 wird der Schritt Nr. 6 erledigt. Genau wie man erst einen Textbereich beschaffen muss, um damit in einem RichTextBox-Steuerelement oder in Word arbeiten zu knnen, so bentigen Sie einen Zellbereich in Excel. Dieser Bereich wird mit Hilfe der get_Range-Methode geholt, aber wir mssen mit ein paar Tricks dafr sorgen, dass sie richtig funktioniert. get_Range bernimmt zwei Parameter: die Anfangszelle des Bereichs und dessen letzte Zelle. Der Einfachheit halber ist die Anfangszelle stets A1, wie in Zeile 62 zu sehen. Die letzte Zelle variiert je nach der Zahl der Eintrge im Kombinationsfeld und da man nie wissen kann, wie viele Eintrge der Benutzer in diese Liste einfgt, kann man nicht im Voraus wissen, wo die letzte Zelle liegt. Vergessen Sie nicht: Jede Zelle muss in Excel durch einen Buchstaben und eine Ziffer bezeichnet sein, so etwa A1 oder C7 oder Q33. Da wir nur Spalten brauchen, ist die Ziffer fr die letzte Zelle stets 1. Abbildung 14.8 veranschaulicht dieses Konzept.

Abbildung 14.8: Die letzte Zelle befindet sich stets in Zeile 1, doch in einer unbekannten Spalte.

Wir mssen also die Anzahl der Elemente im Kombinationsfeld in einen Buchstaben umwandeln, der sich als Spaltenbezeichnung nutzen lsst. Hierfr mssen Sie ein wenig ber ASCII-Zeichenbezeichnungen Bescheid wissen. So entspricht die ASCII-Nummer 65 dem groen A, 66 dem groen B usw. Wir nehmen daher die Anzahl der Eintrge im Kombinationsfeld und addieren 64, um die richtige ASCII-Zeichennummer zu erhalten (1 Element entspricht also nach dem Addieren von 64 der ASCII-Nummer 65, dem groen A). Daraufhin setzen wir die Convert.ToChar-Methode ein, um die Zahl in den richtigen Buchstaben umzuwandeln. So erhalten wir stets die richtige Schlusszelle fr den Bereich. Um nun die Werte im Kombinationsfeld in den Zellbereich einzufgen, muss man sie zunchst in einem Array ablegen (Zeilen 64 bis 67). Das erledigt eine einfache for-

496

ActiveX-Steuerelemente

Schleife, die diese Elemente dem Array arrValues hinzufgt. Zeile 68 platziert sie dann mit Hilfe der Value2-Eigenschaft in den Zellbereich. Zeile 70 (Schritt 7) ist zwar lang, aber einfach. Die Sort-Methode bernimmt zwlf Parameter, doch wir wollen nur drei davon angeben. Der erste betrifft die zu sortierenden Daten. In unserem Fall ist dies die erste Zeile im Arbeitsblatt. Die Rows-Methode bernimmt zwei Parameter, nmlich eine Zeilen- und Spaltennummer, doch da uns Spalten in diesem Beispiel nicht interessieren, lautet der zweite Wert auf Missing. Der zweite Parameter gibt an, wie die Sortierung durchzufhren ist, und kann entweder XlSortOrder.xlAscending (aufsteigende Reihenfolge) oder XlSortOrder.xlDescending (absteigend) lauten. Die nchsten fnf Parameter werden benutzt, wenn man anhand zustzlicher Werte sortieren mchte. Da wir aber nur eine Zeile von Daten haben, verwenden wir Standardwerte als Parameter. Der achte Parameter gibt an, ob die Spalten berschriften tragen. Die neunten und zehnten Parameter sind nicht relevant. Doch der elfte ist wichtig, denn er gibt an, ob Excel Spalten oder Zeilen sortieren soll. Normalerweise spielt das nur eine Rolle, wenn man mehr als eine Spalte bzw. Zeile zu sortieren hat, doch wir geben trotzdem xlSortRows an, um nach Zeilen sortieren zu lassen. Auch der zwlfte Parameter ist unwichtig. Nach dem Sortieren der Werte mssen diese aus Excel geholt werden. Das ist komplizierter, als es klingt. Weil die Value2-Eigenschaft (die Werte in die Zellen einfgte) ein eindimensionales Array akzeptiert hat, knnte man annehmen, dass sie auch ein eindimensionales Array zurckgibt. Dem ist nicht so. Wenn sie dazu benutzt wird, Werte aus Zellen zu holen, liefert Value2 ein zweidimensionales Array, das Sie dem zweidimensionalen Array arrSorted in Zeile 73 zuweisen. Um die sortierten Werte aus dem Array in das Listenfeld zu verschieben, mssen Sie eine Doppelschleife einrichten, wie in den Zeilen 75 bis 79 zu sehen. Zum Schluss ist die Anwendung zu schlieen (Schritt 10). Dafr knnen Sie einfach die Quit-Methode benutzen (Zeile 83), doch Excel wrde den Benutzer auffordern anzugeben, ob er das vorhandene Arbeitsblatt speichern wolle (da der Benutzer nicht wei, dass Excel unsichtbar im Hintergrund arbeitet, knnte ihn dies ein wenig irritieren). Um dies zu umgehen, setzen Sie die Saved-Eigenschaft auf true, die Excel mitteilt, dass das Arbeitsblatt in der Tat bereits gespeichert wurde (selbst wenn dies nicht der Fall ist). Kompilieren Sie diese Anwendung (Referenzen auf Excel-Namensraum und -Assembly nicht vergessen) und testen Sie, ob Excel wirklich sortiert. Abbildung 14.9 zeigt ein entsprechendes Beispiel. Da Sie mit dem Innenleben der Microsoft Office-Anwendungen nicht vertraut sind, mag es Sie zunchst verwirren, mit ihnen via ActiveX zu arbeiten. Als sehr hilfreich erweist sich dabei jedoch der Objektkatalog des Visual Basic-Editors, der zum Lieferumfang von Microsoft Office gehrt. Um dieses Werkzeug zu benutzen, gehen Sie in das EXTRASMen, das in jeder Office-Anwendung zu finden ist, whlen MAKRO aus und dann Visual Basic-Editor (taucht dieser Menbefehl nicht auf, bedeutet dies, dass Sie den Visual BasicEditor zu Anfang nicht installiert haben; legen Sie dann die Office-CD ein und whlen Sie

497

ActiveX

diese Option aus dem Men aus). Befinden Sie sich erst einmal im Editor, whlen Sie OBJEKTKATALOG aus dem ANSICHT-Men aus oder drcken die Taste (F2). Der Objektkatalog (vgl. Abbildung 14.10) fhrt alle Objekte, Methoden, Eigenschaften und Ereignisse auf, die in jeder Office-Anwendung zur Verfgung stehen. Fr das Arbeiten mit ActiveXAnwendungen ist dies ein wertvolles Hilfsmittel.

Abbildung 14.9: Die in das Kombinationsfeld ungeordnet eingetragenen Namen von Figuren aus der Sesamstrae erscheinen sortiert im Listenfeld.

Abbildung 14.10: Der Objektkatalog von Microsoft Office fhrt alle Funktionen auf, die Sie ber ActiveX nutzen knnen.

498

.NET-ActiveX-Steuerelemente erstellen

14.3 .NET-ActiveX-Steuerelemente erstellen


In technischer Hinsicht sind ActiveX-Steuerelemente in .NET durch Windows FormsSteuerelemente ersetzt worden, so dass Sie eigentlich kein .NET-ActiveX-Steuerelement erstellen knnen. Allerdings ist es mglich, .NET-Anwendungen so aufbauen, dass sie sich als ActiveX-Steuerelemente von Nicht-.NET-Anwendungen verwenden lassen. Das ist ein relativ einfacher Vorgang und es sind nur ein paar zustzliche Aspekte zu beachten. Der erste Schritt besteht natrlich darin, Ihre Anwendung entsprechend zu entwerfen. Jede Funktion oder Variable, zu der Ihre ActiveX-Anwendung spter Zugang haben soll, muss als public deklariert werden. Das ist nicht gerade neu. Das betrifft auch .NET-Klassen. Haben Sie beispielsweise eine Taschenrechner-Anwendung erstellt, die von einer anderen Anwendung via ActiveX benutzt werden soll, wollen Sie wahrscheinlich die Hauptrechenfunktionen der Host-Anwendung zur Verfgung stellen. Diese mssen also public deklariert werden:
public int Add(int intA,int intB){ ... }

Zum anderen mag es Ihnen vielleicht nicht recht sein, wenn der Benutzer die Schaltflchen des Taschenrechners ndern knnte, daher markieren Sie diese als private:
private Button btnAdd = new Button();

Als Nchstes mssen Sie Ihre .NET-Anwendung bei der Windows-Registrierung anmelden. Alle ActiveX-Steuerelemente mssen registriert sein, um korrekt zu funktionieren. Obwohl Ihre .NET-Anwendung technisch gesehen kein ActiveX-Steuerelement ist, so behandeln doch Host-Anwendungen sie als solches und daher muss sich Ihre Anwendung als solches verhalten. Dieser Registriervorgang wird vom Assembly Registration-Tool (regasm.exe) erledigt. Es liest die Metadaten aus Ihrer Anwendung und fgt die erforderlichen Eintrge der Registrierung hinzu, damit sich Ihre Anwendung als ActiveX-Steuerelement verwenden lsst. Die Syntax dafr ist simpel:
regasm AssemblyFilename

So knnen Sie beispielsweise Listing 14.2 als ActiveX-Steuerelement mit dem folgenden Kommando von der Befehlszeile aus registrieren:
regasm listing14.2.exe

Sie sollten das folgende Ergebnis sehen:


RegAsm -.NET Assembly Registration Utility Version 1.0.2914.16 Copyright (C)Microsoft Corp.2001.All rights reserved. Types registered successfully

499

ActiveX

Das war's schon. Nicht-.NET-Anwendungen knnen nun auf Ihre Anwendung via ActiveX wie auf jedes andere ActiveX-Steuerelement zugreifen; die Host-Anwendungen mssen nicht angepasst werden.

14.4 Richtlinien fr das Arbeiten mit ActiveX


Weil ActiveX eine etwas betagte Technologie ist, sind mit ihrem Einsatz auch einige Einbuen verbunden. So mag mitunter ein bestimmter Datentyp, der in .NET verfgbar ist, nicht in einer Pr-.NET-Umgebung zur Verfgung stehen oder umgekehrt (zum Beispiel gibt es den Datentyp variant aus VB 6 nicht mehr in VB .NET). Tritt dieser Fall ein, muss die CLR einen Typ in den anderen bersetzen ein Vorgang, der als Marshalling bezeichnet wird. Es erfordert zustzliche Verarbeitungszeit und beeintrchtigt Ihre Anwendungen mehr, als Sie annehmen. Auf Grund der Beschrnkungen in technischer Hinsicht verlieren Sie zudem ein hohes Ma an Flexibilitt, wenn Sie ActiveX einsetzen. Parameter, die zuvor optional waren, mssen jetzt immer bereitgestellt werden, ob sie nun bentigt werden oder nicht. Hierbei spielt das Missing-Objekt eine Rolle: Sie mssen es fr jene Parameter benutzen, fr die Sie keine Werte liefern wollen. In diesem Zusammenhang ist es manchmal schwierig, genau herauszufinden, welche Typen man fr ActiveX-Methodenparameter bentigt, selbst dann, wenn man ber eine Dokumentation verfgt. Wenn Sie eine Anwendung via ActiveX benutzen wollen, dann knnen Sie hufig auf hnliche Funktionen stoen, die bereits in .NET eingebaut sind. Zum Beispiel haben Sie vor .NET mglicherweise das ActiveX-Steuerelement FileSystemObject verwendet, um E/AOperationen auszufhren. Sie knnen jetzt die Klassen, von denen Sie an Tag 11 erfahren haben, fr den gleichen Zweck einsetzen. Wegen dieser und anderer Details sollten Sie doppelt berprfen, ob Sie nicht die gleiche Funktion verwenden knnen, die bereits im .NET Framework bereitsteht, oder ob Sie sie selbst erstellen knnen, falls es nicht zuviel Mhe ist. Wenn Sie die Einbuen beim Einsatz von ActiveX vermeiden knnen, dann sollten Sie dies unter allen Umstnden tun.

500

Richtlinien fr das Arbeiten mit ActiveX

Ich empfehle Setzen Sie ActiveX nur ein, wenn es absolut unumgnglich ist, so etwa, wenn die gewnschte Funktionalitt sehr kompliziert ist oder wenn Sie sich auf eine proprietre Technologie sttzen mssen, damit Ihre Anwendung luft (etwa der Einsatz von Word, um Word-Dokumente ffnen zu knnen).

Bitte beachten Sie Verwenden Sie ActiveX nicht, wenn die gewnschte Funktionalitt einfach ist oder bereits in einer .NET Framework-Klasse zur Verfgung steht. Schlagen Sie zuerst in der Dokumentation zu .NET nach!

Ein weiteres Problem, das die ganze Sache noch komplizierter macht, sind die Richtlinien fr die Lizenzierung von ActiveX-Steuerelementen. Wenn man ein ActiveX-Steuerelement lizenziert, heit das, dass Sie es nur einsetzen knnen, wenn Sie die entsprechenden Lizenzen dafr besitzen (was normalerweise deren Kauf bedeutet). Angenommen, Sie haben Microsoft Office auf Ihrem Entwicklungscomputer und erstellen die Anwendungen aus den Listings 14.2 und 14.3. Dann verkaufen Sie Ihre Produkte an einen Heimanwender. Verfgt dieser nicht ebenfalls ber Office, kann er sie nicht einsetzen. Sie knnen manchmal die ntigen ActiveX-Steuerelemente mit Ihrer Anwendung bndeln, doch dies erfordert weitere Lizenzvereinbarungen und kostet im Allgemeinen viel Geld. Darber hinaus gibt es verschiedene Arten von Lizenzvereinbarungen, so etwa Produktions- und Entwicklungslizenzen, Einzelplatzlizenzen, Einzelprozessorlizenzen usw. Sie mssen dafr sorgen, dass jeder, der Ihre Anwendung nutzt, ber die gleiche Lizenz verfgt. Manche Lizenzarten lassen sich nicht gemeinsam nutzen oder bertragen, was die ganze Sache noch etwas kniffliger macht. Als Entwickler knnen Sie Ihre eigenen Steuerelemente lizenzieren, so dass man Lizenzen von Ihnen kaufen muss. Etwas komplizierter wird es, wenn Sie die Lizenz fr das Steuerelement eines anderen kaufen und es in Ihrem eigenen Produkt nutzen wollen. Unterschiedliche Unternehmen haben unterschiedliche Anforderungen und Preismodelle, wenn Sie deren Lizenzen vertreiben wollen. In diesen Fllen mssen Sie sich beim Verkufer des Steuerelements erkundigen. Wenn Sie unbedingt wollen, knnen Sie in der .NET Framework Dokumentation mehr ber Lizenzierung erfahren. Sie erlutert, wie man ActiveX-Lizenzen fr seine .NETAnwendung erwirbt und einsetzt, und sogar, wie man Lizenzen fr die eigenen .NETAnwendungen erstellt (fr den Fall, dass man ein eigenes Produkt verkaufen will).

501

ActiveX

14.5 Zusammenfassung
Heute haben Sie gelernt, was man unter ActiveX versteht, wie Sie es nutzen knnen und welche Probleme bei seiner Verwendung auftreten knnen. ActiveX kann einen groen Beitrag zu den Fhigkeiten Ihrer Anwendungen leisten. ActiveX, das frher als OLE-Automatisierung bekannt war, ist eine auf dem Component Object Model (COM) basierende Technologie. COM ist eine Menge von Regeln und Spezifikationen, die festlegen, auf welche Weise Anwendungen sich miteinander verstndigen, unabhngig von der Sprache oder dem Entwicklungsschema, mit der oder nach dem sie entwickelt wurden. Windows Forms sind nicht vollstndig zu ActiveX kompatibel; das einzige, was man in ein Windows-Formular einbauen kann, ist eben ein Windows Forms-Steuerelement. Um ActiveX-Anwendungen oder -Steuerelemente in Ihren .NET-Anwendungen einsetzen zu knnen, mssen Sie das jeweilige ActiveX-Steuerelement in die .NET-Klasse AxHost hllen, die fr .NET wie ein Windows Forms-Steuerelement aussieht, doch wie ein ActiveX-Host fr eine ActiveX-Anwendung. Sie mssen diese Klasse nicht selbst erstellen, sondern dies erledigt das ActiveX Control Importer-Werkzeug fr Sie, indem es Metadaten fr ActiveXSteuerelemente erzeugt. Zustzlich gibt es das Werkzeug Type Library Importer, mit dem man Anwendungen wie Microsoft Word oder Excel als ActiveX-Steuerelemente nutzen kann. Um ActiveX-Anwendungen den Zugriff auf .NET-Anwendungen zu gestatten, brauchen Sie nur das Assembly Registration-Werkzeug einzusetzen, das aus den jeweiligen Metadaten Registrierungseintrge erzeugt und dafr sorgt, dass die jeweiligen Methoden und Eigenschaften richtig deklariert werden (unter Verwendung von public und private). Morgen beginnt eine neue Woche und der Schwerpunkt liegt auf einem anderen Aspekt von Windows Forms. Sie erfahren etwas ber Webdienste, eine wichtige Triebkraft hinter der .NET-Revolution. bersehen Sie die Bonusanwendungen nicht.

14.6 Fragen und Antworten


F Ist der Einsatz von ActiveX-Steuerelementen in einem Windows-Formular sicher? A Da ActiveX eine ltere Technik ist und nicht vllig kompatibel zu .NET, gibt es bestimmte Annahmen, wenn Sie es in Ihrer Anwendung nutzen. Erstens knnen ActiveX-Steuerelemente nicht die eingebauten Sicherheitsmerkmale der CLR nutzen. Auf Grund dessen kann es vorkommen, dass bestimmte Funktionen in ActiveX-Steuerelementen nicht in einer .NET-Anwendung zur Verfgung stehen. Sie knnen auch nicht mit dem Internet Explorer darauf zugreifen (vgl. Tag 12).

502

Workshop

Damit es richtig funktioniert, muss man ein ActiveX-Steuerelement auch in einem zentralen Repository registrieren (genauer gesagt, in der Windows-Registrierung). Zu diesem Schritt gehren auch zustzliche Sicherheitsvorkehrungen. Schlielich lassen sich ActiveX-Steuerelemente einfach nicht in das .NET Framework importieren. Der Versuch dazu fhrt zu unvorhersehbaren Ergebnissen, die von Speicherlecks bis hin zu abgestrzten Systemen reichen. F Kann ich mit ActiveX jede .NET-Anwendung nutzen? A Leider nicht. Doch viele Anwendungen lassen sich via ActiveX nutzen, ohne dass eine Dokumentation ein Wrtchen darber verliert. Es ist hufig so, dass das einzige Erfordernis dafr, dass eine Anwendung sich via ActiveX nutzen lsst, im Vorhandensein von public-Mitgliedern besteht. Einige Anwendungen, die zuvor via ActiveX nutzbar waren, lassen sich unter .NET berhaupt nicht mehr nutzen, weil diesbezglich Beschrnkungen in der Abwrtskompatibilitt bestehen. Am besten versucht man es einfach einmal. Sie werden berrascht sein, wie viele Ihrer bevorzugten Anwendungen sich mit ActiveX nutzen lassen, solange Sie die entsprechenden .dll-Dateien finden knnen.

14.7 Workshop
Dieser Workshop soll Ihnen helfen, sich besser an die heute behandelten Begriffe zu erinnern. Es hilft Ihnen sehr, die Antworten vollstndig zu verstehen, bevor Sie weiterlesen. Die Antworten zu den Quizfragen und den bungen finden Sie in Anhang A.

Quiz
1. Wie heien jeweils die ausfhrbaren Dateien fr die Werkzeuge ActiveX Control Importer, Type Library Importer und Assembly Registration? 2. Wahr oder falsch? Der ActiveX Control Importer modifiziert den Quellcode einer .dll, um sie zu .NET konform zu machen. 3. Wahr oder falsch? Es ist besser, vorhandene .NET-Technik zu nutzen als ein hnliches ActiveX-Steuerelement. 4. Was versteht man unter Marshalling?

503

ActiveX

5. Was wrde sich aus dem folgenden Code ergeben?


MessageBox.Show(Convert.ToChar(88).ToString());

6. Wie machen Sie ein Office-Anwendungsobjekt, das Sie gerade erstellt haben, fr den Benutzer sichtbar?

bung
Setzen Sie das Werkzeug Intermediate Language Disassembler ein, um das ActiveX-Steuerelement Webbrowser (AxSHDocVw.dll) zu untersuchen. Erweitern Sie dann das Listing 14.1, damit sich die Anwendung mehr wie ein richtiger Browser verhlt. Integrieren Sie Schaltflchen namens STARTSEITE, STOP und AKTUALISIEREN und ndern Sie die Text-Eigenschaft des Formulars so ab, dass sie zum Titel der gerade geladenen Webseite passt.

504

Woche 2 Rckblick
Projekt 2: Die Textverarbeitung erweitern
In der vergangenen Woche haben Sie von einigen Technologien erfahren, die Windows Forms ergnzen und Anwendungen leistungsfhiger machen. Dazu gehren Datenbanken, Ein-/Ausgabe, ActiveX und das Internet. Jetzt knnen Sie bereits voll funktionsfhige, um nicht zu sagen ntzliche Anwendungen auf mittlerer Stufe erstellen. In diesem Abschnitt werden die in der letzten Woche erlernten Technologien und Techniken zusammengefasst und auf das Textverarbeitungsprogramm NetWord angewendet, das Ende der ersten Woche angefangen wurde. Sie werden insbesondere E/A-Funktionen (damit das Textverarbeitungsprogramm Dokumente ffnen und speichern kann), eine Mehrfachdokumentschnittstelle sowie Druckfunktionen hinzufgen.

MDI hinzufgen
Zunchst soll die Textverarbeitung NetWord in eine MDI-Anwendung umgewandelt werden, damit der Benutzer mehrere Dokumente gleichzeitig bearbeiten kann. Dieser Abschnitt umfasst den Groteil aller ntigen nderungen. Aus Projektstufe 1 wissen Sie noch, dass wir eine Hauptklasse namens NetWord haben, die ein RichTextBox-Steuerelement fr die Bearbeitung sowie mehrere Mens enthlt. Weil wir die Anwendung nun in ihre MDI-Version umwandeln, mssen wir ein paar Vorbereitungen treffen. Legen Sie von NetWord eine Kopie an, die Sie Document nennen. Diese neue Klasse reprsentiert jedes untergeordnete Fenster in der MDI-Version, whrend NetWord weiterhin das Hauptanwendungsfenster bleibt. Entfernen Sie aus der Document-Klasse die Main-Methode: Sie brauchen sie hier nicht. Entfernen Sie sodann alle Verweise auf die FFNEN- und BEENDEN-Mens (die mniOpen- und mniExit-Objekte), die in der NetWord-Klasse platziert werden. Die anderen Menelemente, das Kontextmen, die Statusleiste und praktisch alle anderen Variablen und Methoden knnen Sie beibehalten. Fr die verbleibenden Menelemente mssen Sie MergeOrder-Werte angeben, damit sie richtig mit den Mens in der NetWord-Klasse verschmelzen knnen. Fgen Sie dem Konstruktor folgenden Code hinzu:

505

Woche 2 Rckblick

mniSave.MergeOrder = 30; mniFile.MenuItems [1].MergeOrder = 35; mniPageSetup.MergeOrder = 40; mniPrintPreview.MergeOrder = 45; mniPrint.MergeOrder = 50;

Weitere Modifikationen sind in NetWord selbst auszufhren. Zuerst entfernen Sie alle Verweise auf Objekte oder Variablen, die fr jedes Dokument spezifisch sind. Entfernen Sie alle Verweise auf die Mens SPEICHERN, SEITENEINRICHTUNG, DRUCKVORSCHAU, DRUCKEN, BEARBEITEN, KOPIEREN, EINFGEN, AUSSCHNEIDEN, SUCHEN, FORMAT und SCHRIFTART. Belassen Sie jedoch die beiden Menelemente FFNEN und BEENDEN. Entfernen Sie alle Verweise auf das RichTextBox-Steuerelement (rtbDocument). Lschen Sie die Variablen fntCurrent, fntColor, objPageSettings und objPrinterSettings. Da sich all diese Elemente in der Document-Klasse befinden, bentigen Sie sie nicht in NetWord. Weil die Benutzer mit mehreren Fenstern zugleich umgehen werden, sollte ein FENSTERMen eingefgt werden, mit dem sie die Fenster anordnen und ein bestimmtes auswhlen knnen. Fgen Sie im Deklarationsabschnitt von NetWord folgenden Code ein:
private private private private MenuItem MenuItem MenuItem MenuItem mniWindow = new MenuItem("Fenster"); mniWinC = new MenuItem("berlappend"); mniWinTH = new MenuItem("Nebeneinander"); mniWinTV = new MenuItem("Untereinander");

Im Konstruktor fgen Sie folgenden Code ein:


mniWinC.Click += new EventHandler(this.WindowClicked); mniWinTH.Click += new EventHandler(this.WindowClicked); mniWinTV.Click += new EventHandler(this.WindowClicked); mniWindow.MdiList = true; mniWindow.MenuItems.Add(mniWinC); mniWindow.MenuItems.Add(mniWinTH); mniWindow.MenuItems.Add(mniWinTV); mnuMain.MenuItems.Add(mniWindow);

Als Nchstes fgen Sie der Klasse folgende Methode hinzu:


private void WindowClicked(Object Sender, EventArgs e) { MenuItem miTemp = (MenuItem)Sender; switch (miTemp.Text) { case "berlappend": this.LayoutMdi(MdiLayout.Cascade); break; case "Nebeneinander": this.LayoutMdi(MdiLayout.TileHorizontal); break; case "Untereinander":

506

Woche 2 Rckblick

this.LayoutMdi(MdiLayout.TileVertical); break; } }

Geben Sie im Konstruktor die geeigneten MergeOrder-Werte an:


mniNew.MergeOrder = 10; mniOpen.MergeOrder = 20; mniExit.MergeOrder = 99; mniFile.MenuItems [2 ].MergeOrder = 98;

Erinnern Sie sich, dass MergeOrder-Werte nicht aufeinander folgen mssen, denn die Mens werden je nach ihrem relativen Wert zusammengefhrt. Man braucht einen Zhler, um die bersicht ber die untergeordneten Fenster zu behalten. Fgen Sie daher folgenden Code in den Deklarationsabschnitt ein:
private int intCounter = 0;

Zum Schluss mssen Sie NetWord mitteilen, dass es nun ein bergeordnetes MDI-Formular ist. Fgen Sie also in den Konstruktor folgenden Code ein:
this.IsMdiContainer = true;

Wenn alles gut geht, knnen Sie die Anwendung mit dieser Anweisung erneut kompilieren:
csc /t:winexe /r:system.dll /r:system.drawing.dll /r:system.windows.forms.dll Netword.cs document.cs FindDialog.cs

Im Augenblick besteht noch keine Mglichkeit, der Anwendung untergeordnete Fenster hinzuzufgen (weil das FFNEN-Men noch nichts bewirkt), doch diesen Mangel beheben wir im nchsten Abschnitt.

E/A hinzufgen
In Projektstufe 1 hatten wir eine rudimentre Textverarbeitung, mit der der Benutzer Schriftarten ndern sowie bestimmte Textteile ausschneiden, einfgen, kopieren und suchen konnte. Es hatte Mens fr das ffnen und Speichern von Dateien, doch besaen diese keine Ereignishandler, taten also nichts. Sie erinnern sich von Tag 11 noch, dass das RichTextBox-Steuerelement ber eigene eingebaute Funktionen fr das ffnen und Speichern von Dateien verfgt. Diese nutzen wir jetzt. Doch weil das RichTextBox-Steuerelement nur reinen Text und RTF-Dateien ffnen kann, mssen wir achtsam vorgehen.

507

Woche 2 Rckblick

Die NetWord-Klasse verfgt immer noch ber die FileClicked-Methode, um das FFNENMenelement zu bedienen. Diese ndern wir ein wenig ab, wie Sie aus Listing R2.1 ersehen. Listing R2.1: Dem FFNEN-Men E/A-Funktionen hinzufgen
1: private void FileClicked(Object Sender, EventArgs e) { 2: MenuItem mniTemp = (MenuItem)Sender; 3: Document doc; 4: 5: switch (mniTemp.Text) { 6: case "Neu": 7: intCounter++; 8: 9: doc = new Document("Dokument " + intCounter); 10: doc.MdiParent = this; 11: doc.Show(); 12: break; 13: case "ffnen...": 14: 15: dlgOpen.Filter = "Alle Dateien (*.*)|*.* "; 16: 17: if (dlgOpen.ShowDialog() == DialogResult.OK) { 18: intCounter++; 19: 20: doc = new Document(dlgOpen.FileName); 21: doc.MdiParent = this; 22: 23: FileInfo filTemp = new FileInfo (dlgOpen.FileName); 24: if (filTemp.Extension == ".rtf") { 25: ((RichTextBox)doc.Controls[0]).LoadFile(dlgOpen.FileName,  RichTextBoxStreamType.RichText); 26: } else { 27: ((RichTextBox)doc.Controls[0]).LoadFile(dlgOpen.FileName,  RichTextBoxStreamType.PlainText); 28: } 29: 30: doc.Show(); 31: } 32: break; 33: case "Beenden": 33: this.Close(); 35: break; 36: } 37: }

508

Woche 2 Rckblick

Ein Groteil des Codes drfte vertraut aussehen. Die erste Neuheit folgt in Zeile 3: Hier wird eine Variable des Typs Document deklariert (die weiter vorn neu erstellte untergeordnete MDI-Klasse), die spter in dieser Methode eingesetzt werden soll. Das switch-Statement bewertet das angeklickte Menelement, und weil fast alle Mens entfernt worden sind, betrifft dies nur die FFNEN- und BEENDEN-Mens. Zeile 15 filtert die Auswahlmglichkeiten fr unser OpenFileDialog-Dialogfeld (siehe das Projekt im Anschluss an Woche 1, um herauszufinden, wo dieses erzeugt wird), um jeden Dateityp zuzulassen. Whlt der Benutzer eine Datei aus, mssen wir den Zhler fr die untergeordneten Fenster inkrementieren (Zeile 18). Zeile 20 instantiiert das neue untergeordnete Document-Objekt, wobei der Dateiname an den Konstruktor bergeben wird. Das bedeutet, dass wir den Konstruktor der Klasse Document modifizieren mssen, damit er den Dateinamen akzeptiert. Die Deklaration des Konstruktors in Document ist demnach wie folgt zu ndern:
public Document(string strName){

Um den Dateinamen in der Titelzeile anzuzeigen, wird folgende Anweisung benutzt:


this.Text = strName;

Als Nchstes mssen wir in der NetWord-Klasse bestimmen, ob die vom Benutzer ausgewhlte Datei ein RTF- oder ein Nur-Text-Dokument ist oder gar etwas anderes darstellt. Zu diesem Zweck ist die Dateierweiterung auszuwerten. Basierend auf der ausgewhlten Datei erzeugt Zeile 23 ein neues FileInfoObjekt (vergessen Sie nicht, den Namensraum System.IO zu importieren), um diese Aufgabe zu erfllen. Lautet die Erweiterung .rtf, handelt es sich um eine RTF-Datei. Zeile 25 ruft die LoadFile-Methode des RichTextBox-Steuerelements im untergeordneten Dokument auf (vgl. Tag 10). Dabei wird der ausgewhlte Dateiname und ein Wert aus der RichTextBoxStreamType-Aufzhlung bergeben. Hier bergibt die Methode den Wert RichText, der das RichTextBoxSteuerelement anweist, eine RTF-Datei (.rtf) zu erwarten. Sollte die Dateierweiterung jedoch anderweitig lauten, bergeben wir der LoadFile-Methode RichTextBoxStreamType.PlainText. Schlielich wird in Zeile 30 das untergeordnete Dokument angezeigt. Nun knnen Sie so viele untergeordnete Dokumente ffnen, wie Sie wollen, gleichgltig, ob es sich um Nur-Textoder RTF-Dateien handelt.

509

Woche 2 Rckblick

Um Dokumente zu speichern, gehen Sie zur Document-Klasse und ndern die FileClickedMethode. Der case-Zweig fr das Speichern sollte nun wie folgt aussehen:
case "Speichern": FileInfo filTemp = new FileInfo(this.Text); if (filTemp.Extension == ".txt") { rtbDocument.SaveFile(this.Text, RichTextBoxStreamType.PlainText); } else { rtbDocument.SaveFile(this.Text, RichTextBoxStreamType.RichText); } rtbDocument.Modified = false; break;

hnlich wie der Befehl fr das FFNEN-Men muss das SPEICHERN-Men den Dateityp auswerten, mit dem man es zu tun hat, und daraufhin den passenden RichTextBoxStreamType-Aufzhlungswert an die SaveFile-Methode bergeben. Setzen Sie die ModifiedEigenschaft des RichTextBox-Steuerelements auf true, um der Anwendung mitzuteilen, dass alle nderungen gespeichert worden sind. Der Benutzer kann die Datei aber noch von einer anderen Stelle aus speichern. Wie Sie wissen, wird der Benutzer beim Schlieen des Dokuments gefragt, ob er seine eventuell vorgenommenen nderungen speichern mchte. Dies erfolgt in der DocumentClosingMethode, so dass wir entsprechenden Code hier einfgen mssen. Statt irgendwelcher nderungen des Vorhergehenden mssen wir aber nur die FileClicked-Methode aufrufen, die die passenden Parameter bergibt. Die DocumentClosing-Methode sollte wie in Listing R2.2 aussehen. Listing R2.2: Dem Benutzer gestatten, nderungen vor dem Schlieen des Dokuments zu speichern
1: 2: 3: private void DocumentClosing(Object Sender, CancelEventArgs e) { if (rtbDocument.Modified) { DialogResult dr = MessageBox.Show("Wollen Sie die nderungen im  Dokument speichern?", this.Name, MessageBoxButtons.YesNoCancel); 4: 5: if (dr == DialogResult.Yes) { 6: FileClicked(mniSave, new EventArgs()); 7: } else if (dr == DialogResult.Cancel) { 8: e.Cancel = true; 9: } 10: } 11: }

Die einzige nderung erfolgt in Zeile 6. Jetzt rufen Sie die FileClicked-Methode auf und bergeben das mniSave-Men und ein neues EventArgs-Objekt. Das war's schon.

510

Woche 2 Rckblick

Druckfunktionen hinzufgen
Drucken unter .NET ist wie erwhnt nicht gerade eine unkomplizierte Angelegenheit: Man verwendet ein PrintDocument-Objekt, das jedes Mal ein PrintPage-Ereignis auslst, wenn etwas zu drucken ist. Fr die PrintPage-Methode mssen Sie einen Ereignishandler erstellen, der GDI+ nutzt, um dann den eigentlichen Druckvorgang zu veranlassen. Der Code, um das PrintDocument-Objekt anzulegen, sollte in der Methode Document.FileClicked im case-Zweig fr Drucken untergebracht werden:
case "Drucken": PrintDialog dlgPrint = new PrintDialog(); dlgPrint.PrinterSettings = new PrinterSettings(); if (dlgPrint.ShowDialog() == DialogResult.OK) { objReader = new StringReader(rtbDocument.Text); PrintDocument pd = new PrintDocument(); pd.DefaultPageSettings = objPageSettings; pd.PrintPage += new PrintPageEventHandler(this.PrintIt); pd.Print(); } break;

Hier rufen Sie den zu druckenden Text aus dem RichTextBox-Steuerelement mit Hilfe eines StringReader-Objekts ab. Dieses Objekt wird in der eigentlichen Routine fr das Drucken des Dokuments verwendet. Denken Sie daran, dem PrintDocument das objPageSettings-Objekt und einen Delegaten zuzuweisen sowie die Print-Methode aufzurufen. Zudem ist es ntig, fr das neue StringReader-Objekt im Deklarationsabschnitt Ihres Codes eine neue Variable namens objReader zu definieren. Die PrintIt-Methode (der Ereignishandler fr das Print-Ereignis) sollte wie in Listing R2.3 aussehen. Listing R2.3: Eine Seite drucken
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: private void PrintIt(Object Sender, PrintPageEventArgs e) {

Font fntPrint = this.Font; int count = 0; float yPos = 0; float lpp = e.MarginBounds.Height fntPrint.GetHeight(e.Graphics); float fltTopMargin = e.MarginBounds.Top; float fltLeftMargin = e.MarginBounds.Left; String strLine = null;

511

Woche 2 Rckblick

12: while (count < lpp && ((strLine = objReader.ReadLine()) != null)) { 13: yPos = fltTopMargin + (count * fntPrint.GetHeight (e.Graphics)); 14: 15: e.Graphics.DrawString(strLine, fntPrint, Brushes.Black, fltLeftMargin,  yPos, new StringFormat()); 16: 17: count++; 18: } 19: 20: if (strLine != null) { 21: e.HasMorePages = true; 22: } else { 23: e.HasMorePages = false; 24: } 25: }

Dieser Code ist beinahe eine exakte Kopie von Listing 11.6, weshalb wir ihn nicht nochmals komplett analysieren. Der einzige Unterschied liegt darin, dass das objReader-Objekt nicht mehr in diesem Listing erzeugt wird; vielmehr wurde es in den case-Zweig fr Drucken verschoben (siehe oben). Auf diese Weise vermeiden Sie den Fall der unendlichen Druckschleife, den Tag 11 beschrieben hat. Nun verfgt Ihre Anwendung ber Druckfhigkeiten. Abbildung R2.1 zeigt die frisch erweiterte Anwendung bei der Arbeit. In Projektstufe 3 fgen Sie unter anderem benutzerdefinierte Windows FormsSteuerelemente hinzu. Wenn das Projekt fertig gestellt ist, haben Sie eine Textverarbeitung, die es mit einigen kommerziellen Programmen aufnehmen kann.

Abbildung R2.1: Das NetWord-Textverarbeitungsprogramm bei der Arbeit.

512

Tag 1 Tag 2 Tag 3 Tag 4 Tag 5 Tag 6 Tag 7

Mit Windows Forms beginnen Windows Forms-Anwendungen erstellen Mit Windows Forms arbeiten Mens und Symbolleisten Ereignisse in Windows Forms Windows Forms mit Steuerelementen erweitern Mit Dialogfeldern arbeiten

25 45 77 111 151 177 231

W O C H E

Tag 8 Tag 9 Tag 10 Tag 11 Tag 12 Tag 13 Tag 14

Datenbindung in Windows Forms ADO.NET einsetzen MDI-Anwendungen erstellen Arbeiten mit der Windows-Ein-/Ausgabe Formulare Internet-fhig machen Grafische Anwendungen mit GDI+ ActiveX

281 309 349 377 415 439 477

W O C H E

Tag 15 Tag 16 Tag 17 Tag 18 Tag 19 Tag 20 Tag 21

Webdienste Windows-Dienste Fortgeschrittene Techniken fr Steuerelemente in Windows Forms Benutzerdefinierte Steuerelemente in Windows Forms Multithreading-Anwendungen Windows Forms konfigurieren und bereitstellen Debugging und Profiling

515 539 565 589 621 655 681

W O C H E

Das Innenleben von Windows Forms


Herzlichen Glckwunsch, dass Sie es bis zur dritten Woche auf Ihrer Reise durch Windows Forms geschafft haben! Mittlerweile werden Sie in dieser Welt schon zum Experten und sind bereit, beinahe jede Herausforderung anzunehmen. Die zwei ersten Wochen wurden darauf verwendet, Ihnen die Grundlagen zu vermitteln und darauf aufzubauen. Woche 1 stellte Ihnen Windows Forms vor und lehrte Sie, damit zu arbeiten. In Woche 2 machten Sie Bekanntschaft mit einigen .NET-Technologien, durch die Ihre Windows Forms-Anwendung ntzlicher und funktionsreicher wird. Whrend der kommenden Woche blicken wir Windows Forms sozusagen unter die Motorhaube und sehen uns die Technologien an, die den Treibstoff fr Windows Forms liefern, und wie Sie sie am besten fr Ihre Anwendungen nutzbar machen. An Tag 15 lernen Sie etwas ber Webdienste, die wohl in erster Linie den Ansto dazu geben, sich mit .NET zu befassen und es einzusetzen. Tag 16 informiert Sie ber Windows-Dienste, die sich grundlegend von Webdiensten unterscheiden. Damit knnen Sie nmlich Ihre Anwendungen eng mit dem Betriebssystem verzahnen. Sie haben bereits gelernt, die Mehrzahl der im .NET Framework verfgbaren Windows FormsSteuerelemente wie zum Beispiel TextBox, ListBox und Label einzusetzen. An Tag 17, der unter anderem auch GDI+ behandelt, lernen Sie einige fortgeschrittene Merkmale bestimmter Steuerelemente kennen, so etwa wie Sie deren Erscheinungsbild ndern und sie zu Aktionen veranlassen, die davor nicht mglich waren. An Tag 18 lernen Sie, wie Sie eigene Windows FormsSteuerelemente erzeugen, mit denen sich Ihre Anwendungen betrchtlich erweitern lassen. An Tag 19 mit dem Thema Multithreaded-Anwendungen erfahren Sie, wie man asynchrone Methodenaufrufe ausfhrt und hinsichtlich ihrer Leistung das Optimum aus Anwendungen herausholt. Tag 20 steht unter dem Motto Windows Forms konfigurieren und einrichten. Dieses Kapitel zeigt Ihnen, wie Sie Ihre Anwendungen konfigurieren und bereitstellen, damit sie fr ihren groen Auftritt vor echten Kunden und Benutzern bereitstehen. Das letzte Kapitel, Tag 21, kmmert sich um das Debuggen und die Leistungsoptimierung durch Profiling. Fehlerbeseitigung in der Entwicklersprache als Debugging bezeichnet stellt eine entscheidende Phase in der Anwendungsentwicklung dar. An Tag 21 erlernen Sie einige der unterschiedlichen Methoden. Das Profiling hingegen fhrt Sie in die Geheimnisse der Performanzoptimierung ein, damit Ihre Anwendungen auch mit optimaler Leistung laufen knnen.

Webdienste

5 1

Webdienste

Ein Web Service bzw. Webdienst ist eine recht interessante Sache, erlaubt er es Ihnen doch, auf Anwendungen im Internet zuzugreifen und sie auszufhren Sie brauchen also keine Anwendung auf Ihrem Computer installiert zu haben, um den Webdienst nutzen zu knnen. Webdienste stellen einen neue Denkweise hinsichtlich herkmmlicher Anwendungen dar, und da sie auf erprobten Technologien und Protokollen basieren, knnen Sie davon ausgehen, dass es sie noch eine Weile geben wird. Heute lernen Sie,

was genau ein Webdienst ist, wie man SOAP spricht, wie sich Webdienste leicht erreichen und integrieren lassen, wo man diese Webdienste findet.

15.1 Was sind Webdienste?


Fr Menschen, die gerade mit dem .NET Framework angefangen haben, sind Webdienste meist recht verwirrend. Sie scheinen so etwas wie die Kreuzung einer Website mit einer Desktop-Anwendung zu sein, gleichen aber im Grunde keinem von beiden. Um Webdienste vllig zu verstehen, mssen Sie die dahinter stehende Technik begreifen und wozu man sie berhaupt entwickelt hat. Bevor sich das Internet durchgesetzt hatte, gab es zahlreiche einsame Computer und Unternehmen. Kein Computer (jedenfalls kein normaler) konnte je mit anderen kommunizieren. Ein Unternehmen entwickelte fr gewhnlich seine hauseigenen Anwendungen, whrend die Firma nebenan genau das Gleiche fr sich tat, und keine der beiden ahnte etwas von den Errungenschaften der anderen. Hinzu kam, dass sich die Systeme der beiden Firmen so stark voneinander unterschieden, dass sie keinerlei Daten gemeinsam nutzen konnten. Erst einmal mussten Technologien wie ActiveX entwickelt werden, damit unterschiedliche Systeme sich miteinander verstndigen konnten, doch selbst deren Wirkungskreis war begrenzt. Die Anwendungen mussten lokal vorhanden sein, d.h. man konnte sie nicht ber grere Entfernungen verteilen, und sie waren plattformabhngig. COM und ActiveX stellten ntzliche Ideen dar, wie man einen grundlegenden Entwurfsmangel beheben konnte. Schlielich entstand das Internet, das die Denkweise vieler Menschen umkrempelte. Mit einem Mal konnten zwei Computer, die an entgegengesetzten Enden der Welt standen, miteinander sprechen: ber das Internet und mit Hilfe standardisierter Protokolle. Daten

516

Was sind Webdienste?

lieen sich wie nie zuvor gemeinsam nutzen. Mit den Umwlzungen in der Telekommunikation erffneten sich neue Horizonte. Insbesondere die Wirtschaft sprang auf den neuen Zug auf mit etwas Mhe. Entwicklerteams hatten nun ber Globalisierung nachzudenken und das bedeutete, die verfgbaren Mittel ber das Internet miteinander zu integrieren; auerdem sollten die Desktop-Anwendungen ber das Internet erreichbar sein (etwa fr Auendienstmitarbeiter und Heimarbeiter). Komplett neue Wirtschaftszweige entstanden, um sich dieser Probleme anzunehmen, und Unternehmen, die sich bisher auf eine Art Tante-Emma-Laden (etwa Handwerksbetriebe) verlassen hatten, wagten sich auf Neuland vor. Die wichtigste Antriebskraft fr die Internetrevolution bestand in der Entwicklung von Kommunikationsstandards, insbesondere von HTTP (ca. 1992), FTP, TCP usw. Gleichgltig, welchen Computertyp man benutzte, Hauptsache, man verfgte ber eine Telefonleitung. Dann konnte man diese Standards nutzen und am Spa teilhaben. Die Benutzer konnten die Grenzen ihrer Desktop-Computer berschreiten und mit ein paar Mausklicks in den Cyberspace abdsen. Das Internet bot also die Lsung, nach der groe wie kleine Unternehmen gesucht hatten, nmlich standardisierte und plattformunabhngige Kommunikationsprotokolle. Mit diesen Protokollen lie sich eine bedeutende Kommunikationshrde nehmen, doch die Wirtschaft hatte immer noch keine Mglichkeit, die Informationen in ihren selbstgestrickten Datenbanken auszutauschen. Den nchsten Schritt Richtung Webdienste stellt die Entwicklung von XML dar. Die eXtensible Markup Language (XML) erlaubte die bertragung umfangreicher Datenmengen via Internet mit Hilfe der gleichen Standardprotokolle, die bei anderen Kommunikationsformen funktionierten. Weil es auf erprobten Techniken beruhte, setzte sich XML in kurzer Zeit auf breiter Front durch. Nun verfgte man ber die Datenbertragungstechnik (via XML) und ber die darunter liegenden Kommunikationsprotokolle (via HTTP). Es gab jedoch ein Komponentenproblem: Die Anwendungen, die diese Daten und bertragungen steuerten, waren immer noch an den Desktop-Computer gefesselt. Zwar konnten sie XML ber das Internet verschicken, aber mehr auch nicht. Schlielich kommen wir auf Webdienste zu sprechen. Die Webdienste schlieen die Lcke, indem sie es Anwendungen ermglichen, ber das Internet miteinander zu kommunizieren. Dies erfolgt ber die gleichen erprobten Protokolle, die man im Internet seit jeher benutzt hat. Mit SOAP, einer Sonderform vom XML (gleich mehr dazu), ermglichen Webdienste jeder Anwendung, Befehle ber das Internet zu schicken, wo eine wartende Applikation sie ausfhrt und die Resultate zurckschickt. Mit anderen Worten knnte also mein Rechner Ihrem Rechner befehlen, eine Anwendung zu starten und mir die Ergebnisse zu liefern.

517

Webdienste

Was bedeutet nun die Erstellung eines solchen Befehls? Wenn Ihr Rechner eine CD abspielen soll, geben Sie ihm den entsprechenden Befehl. Das Betriebssystem wiederum erlsst einen Befehl (in Bits und Bytes) an das CD-Laufwerk, welches ihn ausfhrt. Das Abschicken von Befehlen via Internet an einen Webdienst erfolgt auf hnliche Weise, obwohl die Befehle nun, statt in Einsen und Nullen, in SOAP- und XML-Code dargestellt werden. Angenommen, Sie brauchen eine Word-Funktion, verfgen aber ber keine Installation von Word auf Ihrem Desktop-PC. Microsoft bietet jedoch eventuell einen Word-Webdienst an, allerdings auf einem weit entfernten Computer. Ihr Rechner knnte eine Verbindung mit Microsofts Word-Dienst herstellen, damit Sie die ntige Word-Funktion via Webdienst nutzen knnten, gerade so, als ob sich Word auf Ihrem Desktop befnde. Sie bruchten also Word nie zu kaufen, sondern knnten es vielmehr als Webdienst gegen eine geringe Monatsgebhr mieten, so wie Sie etwa AOL oder andere Internet-Dienstanbieter bezahlen. Dieses Modell bietet mehrere Vorteile. Erstens brauchen Sie nie wieder etwas auf Ihrem Rechner zu installieren (auer Ihrem Internetprogramm, versteht sich). Das fhrt zu geringerem Ressourcenbedarf, weniger Wartung und Problembehebung sowie zu der Sicherheit, dass man stets mit der neuesten Version arbeitet. Auerdem kostet es Sie weniger (zumindest kurzfristig), die Anwendung zu nutzen. Auf der Seite von Microsoft erspart dieses Modell dem Unternehmen die Mhe, sich mit den Installationsproblemen auf Tausenden oder gar Millionen von unterschiedlichen Rechnern zu befassen, weil es nur eine Ausfhrung des Webdienstes zu pflegen braucht statt eine Million Word-Exemplare. Da Webdienste auf Standardprotokollen fuen, kann jeder sie einsetzen, vom Heimanwender bis zum Grounternehmen und ganzen Websites. Zudem kann eine Anwendung natrlich mehrere Webdienste zugleich nutzen. Angenommen, Sie setzen eine Hausplanungssoftware ein, mit der Sie die Stockwerke Ihres Traumhauses entwerfen. Bei dessen Errichtung ndern sich jedoch laufend Preisangaben, Materialvorrte und Anforderungen. Damit nicht die Entwurfsanwendung alle diese Regeln selbst bereitstellen muss (was ihre stndige Aktualisierung erfordern wrde), kann sie statt dessen diverse Webdienste kontaktieren, so etwa einen Berechnungsdienst, der die bald bentigte Materialmenge austftelt, sowie einen Einkaufsdienst, der Preisangaben prft und Bestellungen aufgibt. Diese Webdienste sind direkt mit der Entwurfsanwendung integriert, so dass dem Benutzer nie der Unterschied auffllt ihm erscheint es, als ob die Entwurfsanwendung alles von selbst erledigen wrde. Abbildung 15.1 veranschaulicht dieses Beispiel. Ein weiteres Beispiel wre ein Brsentickerdienst. Sie starten von Ihrem bevorzugten Online-Brsenmakler aus eine Kursabfrage. Unter Verwendung von Webdiensten muss Ihr Makler seine Datenbank der Brsenkurse nicht mehr selbst pflegen und aktualisieren. Er knnte einfach eine Anfrage an einen Webdienst schicken, der auf dem Web-Rechner der Brse selbst vorliegt und der den angefragten Kurs liefern wrde. Als Endanwender wss-

518

Was sind Webdienste?

Internet Berechnungsdienst Einkaufsdienst

Abmessungen berechnen eine Bestellung aufgeben

Hausplanungsanwendung

Abbildung 15.1: Eine (Desktop-basierte) Hausplanungsanwendung knnte dem Benutzer unsichtbar Webdienste zur Verfgung stellen.

ten Sie nicht, wie Ihr Makler zu seinen Kursangaben kam. Abbildung 15.2 zeigt diesen Ablauf als Schaubild. Whrend Sie wissen, dass Webdienste auf XML und HTTP beruhen, gibt es ein neues Protokoll, ber das Sie Bescheid wissen sollten: SOAP.
Website 1 Website 1 erhlt . Anforderung. Fordert ACME von Site 2 an. Website 1 erhlt Daten. Zeigt sie dem Benutzer an. Website 2 (Aktienbrse) Website 2 erhlt Anforderung. Holt ACME-Information aus der Datenbank. Schickt sie zurck an Website 1.

Benutzeranfrage bzgl. ACME

Abbildung 15.2: Ein Online-Brsenmakler kann sich auf Kursticker-Webdienste sttzen, um seinen Kunden Leistungen anzubieten.

Das Simple Object Access Protocol (SOAP)


Das Simple Object Access Protocol (kurz SOAP genannt) gestattet im Wesentlichen den Zugriff auf Objekte ber das Internet. (Das Wort simple in SOAP bezieht sich auf das Protokoll, nicht auf die Objekte.) Das Protokoll legt die Art und Weise fest, in der eine Anwendung einer anderen via Internet Nachrichten schicken kann. Obwohl es selbst ein Protokoll ist, ist SOAP in seiner Existenz von anderen Protokollen abhngig.

519

Webdienste

Die von SOAP gesprochene Sprache ist XML. Anders ausgedrckt, ist jede SOAP-Meldung (also ein Befehl an eine andere Anwendung) eigentlich in XML geschrieben; SOAP stellt dafr nur einen bestimmten Formatierungsstil bereit. Kann eine Anwendung also XML verstehen, kann sie daher auch SOAP verstehen. Fr die bertragung verlsst sich SOAP auf HTTP. Mit dessen Hilfe knnen SOAP-Meldungen berall dorthin gelangen, wohin auch reiner Text und HTML gelangen knnen, d.h. sie knnen auch Firewalls und andere Sicherheitsmanahmen umgehen. Weiter unten finden Sie ein paar Beispiele fr SOAP-Meldungen. Vorerst sollten wir uns noch weitere Protokolle ansehen, die eine Rolle spielen.

Andere Protokolle fr Webdienste


SOAP lsst sich nicht als einziges Protokoll von Webdiensten verwenden. HTTP bietet eine Reihe verschiedener Mittel fr die Datenbertragung: HTTP-Get und HTTP-Post. Einen HTTP-Get-Befehl verwenden Sie, wenn Sie eine Webseite abrufen. HTTP-Post wird benutzt, um Daten an Server zu schicken (meistens Formulardaten). Beide Mechanismen lassen sich fr die bertragung von Webdienst-Befehlen und -Daten einsetzen, doch sie haben auch ein paar Nachteile. Insbesondere knnen sie nur sehr einfache Daten oder Kommandos bermitteln. Der einzige Datentyp, der mit HTTP-Get und HTTP-Post zusammenarbeitet, ist der String, weshalb auch Befehle an einen Webdienst oder die gelieferten Daten sehr einfach gehalten sein mssen. Andererseits wurde XML fr die Aufgabe entworfen, auch viele komplexe Datentypen darzustellen, und da SOAP auf XML basiert, ist es weitaus vielseitiger als eine der beiden HTTP-Methoden. Obwohl wir heute alle drei Methoden (SOAP, HTTP-Post und HTTP-Get) besprechen, konzentrieren wir uns auf Grund seiner Flexibilitt auf SOAP.

15.2 Mit ASP.NET Webdienste erstellen


Da der Zugriff auf Webdienste zu einem Groteil genauso abluft wie der Abruf einer Webseite, erscheint es angemessen, dass man ASP.NET einsetzen kann, um Webdienste zu erstellen. Bei Active Server Pages.NET handelt es sich um die neueste Version von Active Server Pages, die vollstndig kompatibel zum .NET Framework ist. Es wird zur

520

Mit ASP.NET Webdienste erstellen

Erstellung von dynamischen Websites und Internet-fhigen Anwendungen eingesetzt. Ohne zu sehr ins Detail gehen zu wollen, werden Sie doch schnell erkennen, dass die Erstellung von ASP.NET-Seiten viel mit dem Schreiben von Windows Forms-Anwendungen gemein hat. Ein Blick auf einfache ASP.NET-Seiten hilft Ihnen besser zu verstehen, wie ein Webdienst funktioniert. Listing 15.1 zeigt eine typische ASP.NET-Seite. Sie bittet den Benutzer um Informationen und heit ihn dann willkommen. Fr die Ausfhrung der folgenden Beispiele bentigen Sie einen Webserver und ASP.NET. Den Internet Information Server erhalten Sie mit Windows 2000 oder XP, whrend ASP eine Installationsoption des kostenlosen .NET Frameworks ist (http://www.microsoft.com/net). Weitere Informationen finden Sie in der .NET Framework-Dokumentation. Listing 15.1: Eine einfache ASP.NET-Seite
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: <%@ Page Language="CS" %> <script runat="server"> public void Submit(Object Sender, EventArgs e) { lblMessage.Text = "Willkommen " + tbName.Text + "!<br>Es " +"ist " + System.DateTime.Now.ToString(); } </script> <html><body> <form runat="server"> Bitte geben Sie Ihren Namen ein: <p> <ASP: TextBox id="tbName" runat="server" /> <ASP: Button id="btSubmit" text="Abschicken" OnClick=" Abschicken" runat="server" /><p> <ASP: Label id="lblMessage" runat="server"/> </form> </body></html>

Speichern Sie die Datei unter dem Namen Listing15.1.aspx im Ordner c:\inetpub\wwwroot und betrachten Sie sie in Ihrem Browser mit Hilfe der Adresse http://localhost/ listing15.1.aspx. Nachdem Sie Ihren Namen eingegeben und die ABSCHICKEN-Schaltflche angeklickt haben, sollten Sie einen Bildschirm sehen knnen, der Abbildung 15.3 hnelt.

521

Webdienste

Abbildung 15.3: Ausgabebeispiel einer einfachen ASP.NET-Seite

Eine ASP.NET-Seite ist in zwei Hauptabschnitte eingeteilt: den Codedeklarationsblock (Zeilen 3 bis 9) und HTML (Zeilen 11 bis 20). Der Codedeklarationsblock ist in script-Tags eingehllt. Das sieht aus wie typischer Windows Forms-Code in C#. Die Submit-Methode ist ein einfacher Ereignishandler (inklusive der blichen Parameterliste), der den Text eines Label-Steuerelements auf einen Willkommensgru setzt. Beachten Sie runat="server" in Zeile 3. Dies ist fr eine ASP.NET-Seite sehr wichtig ohne diese Angabe wrde die Seite nicht richtig funktionieren. Der HTML-Abschnitt besteht aus unkompliziertem HTML sowie einigen fr ASP.NET spezifischen Tags, insbesondere ASP:TextBox in Zeile 15, ASP:Button in Zeile 16 und ASP:Label in Zeile 18. Es handelt sich um ASP.NET-Steuerelemente, die den entsprechenden Steuerelementen in Windows Forms sehr hnlich sind. Sie verfgen ber die gleichen Eigenschaften und Ereignisse. (Beachten Sie, dass jedes das Attribut runat="server" aufweist.) Zeile 17 weist die ASP.NET-Seite an, die Submit-Methode auszufhren, sobald das ButtonSteuerelement angeklickt wurde. (Sie mssen die OnClick-Methode berschreiben, statt dem Ereignis einen Delegaten zuzuweisen.) Diese Seite wird im Browser als normale Webseite angezeigt, wenn sie betrachtet oder abgerufen wird. Klickt der Benutzer auf die ABSCHICKEN-Schaltflche, verwendet die Seite jedoch eine HTTP-Post-Funktion, um Informationen an den Server zu schicken, wo sie wie

522

Mit ASP.NET Webdienste erstellen

eine Windows Forms-Anwendung ausgefhrt wird. Ist dies beendet, schickt sie das Ergebnis in Form von HTML zurck an den Benutzer. Ein Webdienst arbeitet sehr hnlich wie diese Seite: Ein Benutzer (Website, Anwendung oder reale Person) fordert den Webdienst von einem Browser oder einer Anwendung aus an, doch statt HTML zu liefern, gibt der Webdienst XML zurck. Wenn dann der Benutzer beschliet, eine Methode auszufhren (hnlich wie der Klick auf die ABSCHICKENSchaltflche), wird die Information via SOAP (oder HTTP-Get bzw. HTTP-Post) an den Server geschickt und die Ergebnisse kommen wieder als XML zurck.

Webmethoden
Schauen wir uns einen sehr einfachen Webdienst an, nmlich einen Addier-Taschenrechner, der in Listing 15.2 zu sehen ist. Listing 15.2: Ein einfacher Webdienst
1: 2: 3: 4: 5: 6: 7: 8: 9: <%@ WebService Language="CS" Class="Calculator" %> using System.Web.Services; public class Calculator : WebService { [WebMethod] public int Add(int intA, int intB) { return intA + intB; } }

Speichern Sie diese Datei als Listing15.2.asmx (alle Webdienstdateien enden auf .asmx) im Ordner c:\inetpub\wwwroot. Diese Datei hnelt einer Kreuzung aus einer ASP.NET-Seite und einer Windows Forms-Anwendung. Die erste Zeile einer Webdienstdatei deklariert, dass es sich um einen Webdienst handelt, welche Sprache er verwendet und die Hauptklasse des Dienstes. Zeile 3 importiert den Namensraum System.Web.Services ein weiteres Erfordernis. Die Calculator-Klasse ist in Zeile 5 zu sehen. (Sie ist von der WebService-Klasse abgeleitet statt von der Form-Klasse wie normale Windows Forms-Anwendungen.) Als Nchstes kommt der wichtigste Teil eines Webdienstes: die Webmethode. Eine Webmethode (genauer: Webdienstmethode) verhlt sich wie jede andere Methode, nur dass sie sich eben ber das Internet ausfhren lsst. Eine Methode lsst sich zur Webmethode umwandeln, indem man ihrer Deklaration das Attribut [WebMethod] anhngt, wie in Zeile 6 gezeigt (verwenden Sie in

523

Webdienste

VB .NET die Syntax <WebMethod()>). Man kann in einem Webdienst so viele Webmethoden einsetzen, wie man will, doch sie mssen alle das Attribut [WebMethod] aufweisen und als public deklariert sein. Soll eine Methode nicht ber das Internet erreichbar sein, wird sie ganz normal deklariert. Beachten Sie besonders die Klasse und ihre Methode: Calculator und Add. Wenn Sie spter Webdienste in Anwendungen integrieren, werden Sie wieder auf sie stoen. Zum Glck hlt .NET Werkzeuge fr den Fall bereit, dass Sie diese Mitgliedernamen vergessen sollten (spter mehr dazu). Die Funktionsweise dieses Webdienstes ist recht einfach. Wenn die AddMethode aufgerufen wird, bernimmt sie zwei Parameter, addiert sie und liefert das Ergebnis genau wie jede regulre Methode. Hier handelt es sich jedoch um eine Webmethode, die ber das Internet von einem beliebigen Ort aus erreichbar ist. Htte Microsoft diesen Addierdienst entwickelt, knnte man auf die Anwendung ber die Website von Microsoft zugreifen, falls man zwei Zahlen addieren msste (was zwar etwas praxisfremd ist, aber zumindest das Wesen der Methoden umreit, die ber das Internet zugnglich sind). Die .asmx-Datei lsst sich in einem Browser darstellen, genau wie eine ASP.NET-Seite. Der Grund dafr ist, dass eigentlich ASP.NET die Maschine ist, die Webdienste und .asmx-Dateien verarbeitet. In ASP.NET sind alle ntigen Funktionen eingebaut, um Webanforderungen anzustoen und zu beantworten, so dass es als durchaus sinnvoll erscheint, Webdienste mit ASP.NET zu bndeln. Webdienstdateien enthalten keine Elemente fr die Benutzeroberflche. Die Implementierung der Benutzeroberflche bleibt vllig dem Client berlassen; Sie mssen also lediglich die Funktionen erstellen, die hinter den Kulissen arbeiten. Obwohl dies eine sehr einfache Methode ist, knnen Sie schon erahnen, welche Mglichkeiten sich hier auftun. Angenommen, diese Klasse wre in Wirklichkeit die Klasse Word.Application oder Word.Document, die Sie an Tag 14 nutzten. Mit diesem Webdienst knnten Sie jede verfgbare Webmethode von Word ber das Internet nutzen!

Die Dienstbeschreibung
Betrachten Sie die Datei Listing15.2.asmx in Ihrem Browser nach der Eingabe der Adresse http://localhost/listing15.2.asmx; Sie sollten dann die Seite sehen, die in Abbildung 15.4 wiedergegeben ist.

524

Mit ASP.NET Webdienste erstellen

Abbildung 15.4: Unsere Anwendung stellt die Beschreibung des Webdienstes zur Verfgung.

Dieser Text befand sich nicht in der .asmx-Datei, aber ASP.NET versteht, dass es sich um einen Webdienst handelt, und erzeugt eine zustzliche Ausgabe fr Sie. Die Seite in Abbildung 15.4 bietet eine ntzliche Beschreibung fr denjenigen, der den Webdienst nutzen mchte. Klicken Sie auf den Hyperlink Dienstbeschreibung oben auf der Seite, erhalten Sie den XML-Inhalt, den dieser Webdienst an potenzielle Kunden schickt. Er teilt mit, was dieser Webdienst kann, welche Daten er erwartet und welche er liefert. Dieser Code wird als Dienstbeschreibung (Service Description) bezeichnet und ist in der Web Services Description Language (WSDL) geschrieben wobei es sich lediglich um eine einfallsreiche Methode handelt, XML zu formatieren. Wenn Sie XML schon kennen, werden Ihnen einige interessante Aspekte an diesem Code auffallen. Jede der Webdienst-Methoden (obwohl es momentan nur eine gibt) wird aufgelistet, nebst den Eingabeparametern, die sie erwartet. Es gibt zudem einen Abschnitt namens AddResponse, der die Antwort beschreibt, die der Webdienst schickt, sobald die AddMethode ausgefhrt wird. Informationen zu den weiteren Abschnitten finden Sie auf der Website www.w3c.org/TR/wsdl. Die WSDL-Dienstbeschreibung spielt spter noch eine Rolle, wenn Sie diesen Webdienst von einer Windows Forms-Anwendung aus nutzen wollen.

525

Webdienste

Webdienste unter ASP.NET


Gehen Sie zurck auf die Beschreibungsseite, die in Abbildung 15.4 zu sehen ist, und klicken Sie auf die ADD-Verknpfung am Kopf der Seite, die Sie auf eine neue Seite fhrt. Hier knnen Sie den Webdienst tatschlich mit HTTP-Post ausprobieren. Geben Sie zwei Werte in die Textfelder ein, die mit intA und intB bezeichnet sind, und klicken Sie auf die INVOKE-Schaltflche. Sie erhalten die folgende Ausgabe:
<?xml version="1.0" encoding="utf-8" ?> <int xmlns="http://tempuri.org/">8</int>

Hier handelt es sich um den XML-Inhalt, der zum Client des Webdienstes zurckgeliefert wrde. Das Beispiel ist sehr einfach, doch die Ausgabe kann durchaus vielschichtig werden, da XML ja viele verschiedene Datentypen handhaben kann, darunter auch ADO.NET-Datasets. Wenn Sie XML nicht kennen, ist auch das kein Problem, weil Sie nmlich nie direkt mit XML zu tun bekommen, es sei denn, Sie wnschen es. Wie Sie in den nchsten Abschnitten sehen, kann das .NET Framework alles fr Sie handhaben. Sie mssen lediglich entscheiden, welchen Webdienst Sie nutzen wollen, und ihn dann einsetzen. Alle Meldungen werden unsichtbar verarbeitet. Es erscheint Ihnen, als wrden Sie eine weitere lokale Klasse oder ein solches Objekt in Ihrer Anwendung verwenden. Das ist einer der groartigen Nutzeffekte von Webdiensten. Beachten Sie, dass Datenbertragungen bis jetzt (die Ausfhrung der Add-Methode, das Betrachten der WSDL-Beschreibung, der Empfang der XML-Ausgabe) mit Hilfe eines Standardprotokolls ausgefhrt worden sind: HTTP. Selbst die SOAP-Methoden (zu denen wir gleich kommen) reisen via HTTP. Da HTTP ein berall verfgbarer Standard ist, eignet sich dieses Protokoll ideal fr Webdienst-bertragungen.

15.3 Webdienste in Anwendungen


Die Nutzung eines Webdienstes durch eine Windows Forms-Clientanwendung umfasst drei Schritte: Ermitteln (Discovery), Proxy und Implementierung. Ermitteln ist ein Vorgang, bei dem man versucht herauszufinden, ber welche Fhigkeiten ein Webdienst verfgt ansatzweise haben Sie das schon getan, als Sie die WSDL-Beschreibung untersucht haben. Der zweite Schritt besteht in der Erzeugung einer Proxy-Klasse, um den Dienst zu nutzen, und der dritte Schritt besteht darin, den Dienst tatschlich zu implementieren, indem Sie von Ihrer Anwendung aus Befehle an ihn senden. Abbildung 15.5 veranschaulicht diese drei Interaktionen zwischen einem Webdienst und seinem Client. Die zwei letzten Schritte werden weiter unten beschrieben.

526

Webdienste in Anwendungen

1. Entdeckung Anwendung Was tust du? Beschreibung 2. Dienstbeschreibung Was erwartest du? Beschreibung 3. Befehle Tu dies Hier sind die Ergebnisse Webdienst

Abbildung 15.5: Die Interaktion mit einem Webdienst umfasst das Ermitteln, das Untersuchen der Dienstbeschreibung und das Senden von Befehlen.

Ermitteln ist die Gelegenheit fr den Client, den Webdienst kennen zu lernen. Man findet heraus, wozu der Dienst in der Lage ist, also welche Webmethoden er besitzt, und wie man diese Methoden ausfhrt. Ein Weg zur Ermittlung fhrt ber die Dienstbeschreibung, welche Sie finden knnen, indem Sie die Zeichenfolge ?WSDL an das Ende des URLs des Dienstes anhngen, etwa so:
http://localhost/listing15.2.asmx?WSDL

Das sieht zwar toll aus, hilft Ihrer Windows Forms-Anwendung aber nicht viel. Was wir brauchen, ist eine echte XML-Datei, die sich von einer potenziellen Clientanwendung nutzen lsst. Diese Datei kann man mit Hilfe des Web Service Discovery-Werkzeugs (disco.exe) erzeugen. Diesem Werkzeug muss man nur den URL des Webdienstes bereitstellen, dann erledigt es den Rest. Geben Sie in der Befehlszeile folgendes Kommando ein:
disco http://localhost/listing15.2.asmx

Sie sollten folgende Ausgabe sehen knnen:


Microsoft (R)Web Services Discovery Utility [Microsoft (R).NET Framework,Version 1.0.3705.0] Copyright (C)Microsoft Corporation 1998-2001.All rights reserved. Bei folgenden URLs wurden Dokumente gefunden: http://localhost/listing15.2.asmx?wsdl http://localhost/listing15.2.asmx?disco Folgende Dateien enthalten den Inhalt, der bei den entsprechenden URLs gefunden wurde: .\listing15.wsdl <-http://localhost/listing15.2.asmx?wsdl .\listing15.disco <-http://localhost/listing15.2.asmx?disco Die Datei .\results.discomap enthlt Verknpfungen zu diesen Dateien.

527

Webdienste

Das Discovery-Werkzeug hat drei Dateien erzeugt: listing15.disco, ein XML-Dokument, das Verknpfungen (URLs) zum Webdienst und seiner WSDL-Beschreibung enthlt; listing15.wsdl, eine Kopie der WSDL-Dienstbeschreibung; und schlielich results.discomap, eine weitere XML-Datei, die nur Verknpfungen zu den anderen beiden Dateien enthlt. Sie verfgen nun ber Kopien von allem, was der Webdienst leisten kann, und Ihre Anwendungen knnen diese Kopien benutzen, um den Dienst richtig zu verwenden. Die Ermittlungsphase ist optional, doch in den meisten Fllen will man wissen, was ein Webdienst kann, bevor man ihn benutzt. Lassen Sie diesen Prozess aus, so wird er im nchsten Schritt, der Erstellung einer Proxy-Klasse, automatisch fr Sie erledigt. Da Sie nun im Besitz der Dienstbeschreibung sind, knnen Sie mit dem Konsumieren (ein hochtrabendes Wort fr Nutzung) des Webdienstes beginnen. Wir werfen zunchst einen Blick auf die technischen Bedingungen. Um dem Webdienst Nachrichten schicken zu knnen, mssen Sie auf ihn via Internet zugreifen und HTTP-Botschaften senden und empfangen knnen. Auerdem muss es mglich sein, XML- und SOAP-Meldungen zu lesen und zu schreiben. Der Internetzugang ist nicht schwer, doch Sie knnen bislang weder XML noch SOAP schreiben. .NET hilft Ihnen mit einem Werkzeug aus der Klemme, das die ntigen Funktionen fr Sie erzeugt: das WSDL-Hilfsprogramm (wsdl.exe). Es untersucht die Dienstbeschreibung (siehe oben) und erzeugt eine neue Klasse, die als Proxy-Klasse bezeichnet wird. Wir werfen einen nheren Blick darauf, da der Begriff zunchst verwirren knnte. Im Idealfall wrde die Anwendung direkt auf den Webdienst zugreifen. XML-Botschaften zu senden und zu empfangen sowie sie zu dekodieren wre kein Problem und die Integration folglich ein Kinderspiel. Weil es das aber nicht so einfach ist wie es klingt, springt die Proxy-Klasse fr Sie ein. Sie enthlt alle Funktionen, die ntig sind, um HTTP-Nachrichten zu senden und SOAP und XML in Eigenschaften zu bersetzen, mit denen man leichter umgehen kann. Die Proxy-Klasse enthlt auch etwas, das aussieht wie Kopien der Webmethoden des Webdienstes. Die Namen der Methoden sind sogar identisch, doch wenn man sich die Implementierung der Methoden ansieht, lassen sich Unterschiede erkennen. Dieses Bezeichnungsschema wird so gehandhabt, damit die Arbeit mit der Proxy-Klasse reibungslos verluft. Um das Ganze verstndlicher zu machen, erstellen wir einmal eine Proxy-Klasse. Sie begeben sich in das gleiche Verzeichnis, in dem sich die zuvor bei der Ermittlung erzeugte .wsdl-Datei befindet. Auf der Befehlszeile benutzen Sie das WSDL-Hilfsprogramm, um mit folgendem Befehl eine Proxy-Klasse zu erzeugen:
wsdl /l:CS /n:TYWinforms.Day15 Listing15.wsdl

528

Webdienste in Anwendungen

Dieses Kommando erzeugt aus der beim Ermitteln angelegten .wsdl-Datei eine ProxyKlasse in C#. Die Klasse wird im Namensraum TYWinforms.Day15 angelegt. Tabelle 15.1 fhrt die gngigsten Parameter auf, die im WSDL-Hilfsprogramm benutzt werden. Um eine vollstndige Auflistung zu erhalten, tippen Sie wsdl /? auf der Befehlszeile ein.
Parameter
/language

Beschreibung Legt die Sprache fest, in der die Proxy-Klasse anzulegen ist. Lsst sich mit /l abkrzen. Legt den Namensraum fest, in dem die Proxy-Klasse angelegt werden soll. Lsst sich mit /n abkrzen. Unterdrckt das Microsoft-Logo. Der Dateiname, den die Proxy-Klasse tragen soll. Das Protokoll, das die Klasse zur Kommunikation mit dem Webdienst nutzen soll. Das kann SOAP, HttpGet oder HttpPost sein.

/namespace

/nologo /out /protocol

Tabelle 15.1: Gebruchliche Parameter des WSDL-Hilfsprogramms

Sie knnen dem WSDL-Hilfsprogramm entweder die .wsdl-Datei oder die eigentliche Online-Dienstbeschreibung in WSDL bergeben. Der folgende Befehl funktioniert genau wie der vorige:
wsdl /l:CS /n:TYWinforms.Day15 http://localhost/listing15.2.asmx?WSDL

Wenn der Befehl erfolgreich ausgefhrt wurde, sehen Sie eine neue Datei namens Calculator.cs (die zuvor erzeugte Webdienst-Klasse hie Calculator), die in Listing 15.3 zu sehen ist. (War Ihnen kein Erfolg beschieden, prfen Sie, ob sich die .asmx-Datei im richtigen Verzeichnis befindet und ob Ihr Webserver luft.) Listing 15.3: Die Proxy-Klasse Calculator
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: //-------------------------------------------------------// <autogenerated> // This code was generated by a tool. // Runtime Version: 1.0.3705.0 // // Changes to this file may cause incorrect behavior // and will be lost if the code is regenerated. // </autogenerated> //-------------------------------------------------------//

529

Webdienste

12: // This source code was auto-generated by wsdl, 13: // Version=1.0.3705.0. 14: namespace TYWinforms.Day15 { 15: using System.Diagnostics; 16: using System.Xml.Serialization; 17: using System; 18: using System.Web.Services.Protocols; 19: using System.ComponentModel; 20: using System.Web.Services; 21: 22: 23: /// <remarks/> 24: [System.Diagnostics.DebuggerStepThroughAttribute()] 25: [System.ComponentModel.DesignerCategoryAttribute("code")] 26: [System.Web.Services.WebServiceBindingAttribute (Name="CalculatorSoap",  Namespace="http: //tempuri.org/")] 27: public class Calculator :  System.Web.Services.Protocols.SoapHttpClientProtocol { 28: 29: /// <remarks/> 30: public Calculator() { 31: this.Url = "http://localhost/listing15.2.asmx"; 32: } 33: 34: /// <remarks/> 35: [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http: //  tempuri.org/Add", RequestNamespace="http://tempuri.org/",  ResponseNamespace="http://tempuri.org/",  Use=System.Web.Services.Description.SoapBindingUse.Literal,  ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] 36: public int Add(int intA, int intB) { 37: object[] results = this.Invoke("Add", new object[] {intA,  intB}); 38: return ((int)(results[0])); 39: } 40: 41: /// <remarks/> 42: public System.IAsyncResult BeginAdd(int intA, int intB,  System.AsyncCallback callback, object asyncState) { 43: return this.BeginInvoke("Add", new object[] { 44: intA, intB}, callback, asyncState); 45: } 46: 47: /// <remarks/> 48: public int EndAdd(System.IAsyncResult asyncResult) { 49: object[] results = this.EndInvoke(asyncResult);

530

Webdienste in Anwendungen

50: 51: 52: 53:

return ((int)(results[0])); } } }

Obwohl sich hier viel Code findet, ber den Sie noch nichts wissen, werden wir hier nicht nher darauf eingehen alles zu seiner Zeit. Aber die Klasse sollten wir uns genauer ansehen. Der Konstruktor fr die Calculator-Klasse befindet sich in den Zeilen 29 bis 32. Er ist abgeleitet von der Klasse System.Web.Services.SoapHttpClientProtocol. Diese Basisklasse enthlt die Funktionen, um HTTP-Nachrichten zu senden und zu empfangen, und da Calculator von ihr erbt, erbt sie auch diese Funktionalitt. Der URL des Webdienstes wird ebenfalls hier als Eigenschaft festgelegt. Nach einer langen Attributliste sehen Sie in den Zeilen 35 bis 39 eine Add-Methode, die wie jene aussieht, die Sie im Webdienst erstellt haben. Doch diese Add-Methode fhrt nicht die eigentliche Addition aus, sondern ruft vielmehr die Add-Webmethode des Webdienstes via Internet auf, indem sie die Invoke-Methode benutzt, die sie von der Klasse SoapHttpClientProtocol geerbt hat. In den Zeilen 42 und 48 sehen Sie zwei Methoden, BeginAdd und EndAdd, die sich zum asynchronen Aufruf der Webmethode nutzen lassen. ber asynchrone Aufrufe erfahren Sie mehr an Tag 19, so dass wir diese Methoden frs Erste berspringen knnen. Kompilieren Sie die Klasse mit dem folgenden Befehl:
csc /t:library /r:system.dll /r:system.web.dll Calculator.cs

Die Klasse kompilieren Sie als Bibliothek statt als ausfhrbare Windows-Datei, denn Sie knnen die Proxy-Klasse nicht alleine einsetzen; sie muss von einer anderen Anwendung eingesetzt werden. Sobald Sie nun ber eine kompilierte Klasse verfgen, knnen Sie sie wie jedes andere Objekt verwenden. Listing 15.4 zeigt eine einfache Windows Forms-Anwendung, die diese Klasse und ber den Proxy den Webdienst verwendet. Listing 15.4: Die Konsumierung des Webdienstes
1: 2: 3: 4: 5: 6: 7: using System; using System.Windows.Forms; using System.Drawing; namespace TYWinforms.Day15 { public class Listing154 : Form { private Button btSubmit = new Button();

531

Webdienste

8: private TextBox tbNumberA = new TextBox(); 9: private TextBox tbNumberB = new TextBox(); 10: private Label lblAdd = new Label(); 11: 12: public Listing154() { 13: tbNumberA.Location = new Point(20,50); 14: tbNumberB.Location = new Point(140,50); 15: 16: lblAdd.Text = " + "; 17: lblAdd.Location = new Point(120,50); 18: 19: btSubmit.Text = "Add It!"; 20: btSubmit.Location = new Point(140,100); 21: btSubmit.Click += new EventHandler(this.CallService); 22: 23: this.Text = "Listing 15.4"; 24: this.Controls.Add(tbNumberA); 25: this.Controls.Add(tbNumberB); 26: this.Controls.Add(lblAdd); 27: this.Controls.Add(btSubmit); 28: } 29: 30: private void CallService(Object Sender, EventArgs e) { 31: Calculator objCalc = new Calculator(); 32: int intAnswer = objCalc.Add(Convert.ToInt32(tbNumberA.Text),  Convert.ToInt32(tbNumberB.Text)); 33: 34: MessageBox.Show(intAnswer.ToString()); 35: } 36: 37: public static void Main() { 38: Application.Run(new Listing154()); 39: } 40: } 41: }

Der Groteil des Listings entspricht dem Standardverfahren; die Benutzeroberflche wird im Konstruktor mit einigen Textfeldern, einem Bezeichnungsfeld und einer Schaltflche eingerichtet. Interessant wird es ab Zeile 30, nmlich im CallService-Ereignishandler. In Zeile 31 erzeugen Sie eine neue Instanz der Calculator-Proxy-Klasse. In der folgenden Zeile rufen Sie die Add-Methode auf, wobei Sie ihr Werte aus den zwei Textfeldern bergeben (vergessen Sie nicht, die Zeichenfolgenwerte in Integer umzuwandeln). In Zeile 34 wird die Ausgabe in einem Meldungsfeld angezeigt.

532

Webdienste in Anwendungen

Kompilieren Sie diese Anwendung mit dem folgenden Befehl:


csc /t:winexe /r:system.dll /r:system.windows.forms.dll /r:system.drawing.dll / r:Calculator.dll Listing15.4.cs

Abbildung 15.6 zeigt das Ergebnis.

Abbildung 15.6: Das Ergebnis wurde wirklich ber das Internet geliefert.

Was ist also abgelaufen? Ihre Anwendung wurde normal ausgefhrt. Sie erzeugte eine Instanz der Proxy-Klasse, rief eine von deren Methoden auf und holte die Ergebnisse ab. Fr die Klasse in Listing 15.4 unterscheidet sich dieses Vorgehen in nichts von einem gewhnlichen Methodenaufruf. Sie wei nur, dass das Calculator-Objekt einen Wert liefert sie kmmert sich nicht darum, woher der Wert kommt oder wie er entstand. Die Proxy-Klasse behlt ihre Geheimnisse fr sich; sie kapselt alle Funktionen, die das Internet anzapfen, schickt eine SOAP-Nachricht, holt die Antwort in XML ab und bersetzt diese in einen einfachen Wert, den die Anwendung verwenden kann. Die Proxy-Klasse nimmt uns also alle Mhe im Umgang mit Webdiensten ab. Weil sie ihren Namen und die Namen ihrer Methoden mit dem Webdienst teilt, ist sie praktisch unsichtbar. Wenn man es nicht besser wsste, knnte man glauben, man interagiere direkt mit dem Webdienst, also genau wie mit einem lokal vorhandenen Objekt. Der Aufwand fr dieses einfache Ergebnis mag ungerechtfertigt hoch erscheinen, doch man muss lediglich drei Schritte bewltigen: das Werkzeug wsdl.exe einsetzen, um die Proxy-Klasse zu erzeugen, sie kompilieren und die Windows Forms-Anwendung erstellen, um auf die Webmethoden zuzugreifen. (Erledigt jemand die ersten beiden Schritte fr Sie, dann brauchen Sie nur fr die Implementierung zu sorgen.) Sie mssen nicht das Ergebnis kennen, das jeweils von den Schritten der Ermittlung und der Proxy-Erzeugung generiert wird das ist irrelevant, falls Sie nicht Ihre eigenen Proxies von Hand erzeugen wollen. Da der Proxy alle komplizierteren Funktionen verbirgt, brauchen Sie nur ein paar Codezeilen in Ihrer Windows Forms-Anwendung, um eine Menge Leistung zu erzielen.

533

Webdienste

Wenn Sie dieses einfache Paradigma befolgen, knnen Sie von berall auf einen Webdienst zugreifen. Der Dienst kann Befehle empfangen und Ergebnisse schicken, weil diese in XML und via HTTP bertragen werden, also mit zwei allgegenwrtigen Standards.

15.4 Webdienste suchen


Es ist etwas witzlos, Webdienste zu erstellen und sie dann selbst zu konsumieren; da ist es schon besser, die Webdienste anderer Leute zu nutzen, denn schlielich will man ja Funktionen nutzen, die man selbst nicht hat (also nicht selbst erstellen kann oder will). Um Ihnen bei der Suche nach Webdiensten zu helfen, wurde die Universal Description, Discovery and Integration (UDDI) -Spezifikation entwickelt: ein Regelwerk, das Webdiensten und ihren Clients mitteilt, wie sie einander suchen sollen. Mit Hilfe der Spezifikation knnen Unternehmen oder Einzelpersonen aber auch Einzelheiten ber die Dienste, die sie anbieten, und sogar ber die Firmen selbst verffentlichen. Das UDDI-Regelwerk ist in XML verfasst, so dass sich UDDI-Informationen berall im Internet verbreiten lassen. Die eigentliche Spezifikation knnen Sie unter http:// www.uddi.org/schema/uddi_v2.xsd nachlesen. Die UDDI-Website (www.uddi.org), die in Abbildung 15.7 zu sehen ist, bietet eine Registratur von Diensten, sozusagen eine Suchmaschine fr Webdienste. Hier knnen Sie ihr Geschft und seine Dienste registrieren lassen, damit andere Organisationen Sie finden knnen.

Abbildung 15.7: UDDI ist eine durchsuchbare Registratur fr Webdienste.

534

Empfehlungen fr den Einsatz von Webdiensten

Eine Suche auf dieser Website ergibt eine Liste von Diensten, die den Abfragekriterien entsprechen. Jeder Dienst stellt eine Art Stckliste bereit: wer den Dienst anbietet bis hin zu den Kosten seiner Nutzung. Es sind bereits zahlreiche Webdienste erhltlich, so dass Sie hchstwahrscheinlich auch einen finden, der Ihren Anforderungen entspricht. Webdienste knnen sich in ihrer Implementierung stark unterscheiden. Daher ist es keine schlechte Idee, einmal durch die UDDI-Registratur zu blttern, um sich mit dem Angebot vertraut zu machen. Sie werden feststellen, dass einige Webdienste die Ermittlungsphase unterbinden; einige erfordern bei ihrem Einsatz spezielle SOAP-Mechanismen; wieder andere brauchen berhaupt kein SOAP. Suchen Sie nach ein paar kostenlosen Diensten und probieren Sie aus, ob Sie sie in ihre Windows Forms-Anwendungen einbauen knnen, um sie zu nutzen.

15.5 Empfehlungen fr den Einsatz von Webdiensten


Auf welche Weise man einen Webdienst nutzt, hngt von der jeweiligen Situation ab. Die heutigen Beispiele basierten alle auf SOAP, da es ja die vielseitigsten Mglichkeiten fr die Dienstnutzung bietet. Es gibt allerdings einen Nachteil beim Einsatz von SOAP. Wie Sie bereits gemerkt haben, ist die Kodierung der Befehle und Daten in SOAP-Meldungen ein komplizierter Vorgang. Die hin und her geschickten Nachrichten sind recht umfangreich, denn SOAP muss auch XML-Schemainformationen verschicken. Eine andere Mglichkeit, einen Webdienst zu nutzen, stellt HTTP-Get dar. Kennen Sie den URL des Dienstes sowie die Methode(n) und Parameter, die er erwartet, knnen Sie auch die Methoden des Dienstes aufrufen. Sie knnen auf den folgenden URL zugreifen, um eine Add-Methode des heute erstellten Taschenrechnerdienstes zu nutzen, indem Sie einen Schrgstrich und den Methodenamen hinter den URL stellen, gefolgt von den Parametern in einer Abfragezeichenfolge:
http://localhost/Listing15.2.asmx/Add?intA=1&intB=2

Auf die Abfrage hin gibt dieser URL den XML-Code aus, der das Ergebnis der Methode enthlt. In diesem Fall sehen Sie Folgendes:
<?xml version="1.0 "?> <int xmlns="http://tempuri.org/">3</int>

Sie knnen auch einen Dienst via HTTP-Post nutzen. Setzen Sie einfach das action-Attribut eines HTML-Formulars auf Add:
<form action="http://localhost/Listing15.2.asmx/Add">

535

Webdienste

Dies ist der URL, gefolgt von einem Schrgstrich und dem Methodennamen. Wenn Sie das Formular abschicken, wird jeder Parameter an den Dienst gesendet und die passende XML-Ausgabe generiert. Denken Sie jedoch daran, dass die Formularparameter die gleichen Namen haben mssen wie die Parameter, die der Dienst erwartet. Nach der Frage, welche Methode man einsetzen soll, ist die Wahl eigentlich simpel. Wenn man keine komplexen Daten zu verarbeiten hat, setzt man HTTP-Get oder HTTP-Post ein. Weil sie weniger Daten hin und her schicken, ist ihre Performanz hher. Sollen komplexe Daten vom Dienst geliefert werden, wie etwa ein DataSet oder eine Klasse, setzen Sie SOAP ein. Die anderen beiden Methoden knnen diese Art von Daten nicht senden.
Ich empfehle Verwenden Sie SOAP, wenn Sie der Ansicht sind, dass Sie auf Ihren Dienst Zugriff fr komplexe Daten oder vielseitige Programmfunktionen brauchen. Bitte beachten Sie Meiden Sie SOAP, wenn Netzwerkbandbreite ein entscheidender Faktor ist. Setzen Sie statt dessen HTTP-Get oder HTTP-Post ein.

15.6 Zusammenfassung
Webdienste sind eine interessante neue Technologie, und hat man einmal die grundlegenden Konzepte verstanden, sind sie sehr einfach umzusetzen. Webdienste bieten Ihnen den Schlssel zur Verteilung von Anwendungen ber das Internet. Im Wesentlichen sind Webdienste Anwendungen, die ber das Internet zur Verfgung stehen. Sie entwickelten sich, um die fundamentalen Lcken im Rechenwesen zu schlieen, die ltere Technologien wie ActiveX und COM offen gelassen hatten. Webdienste basieren auf HTTP und XML, so dass sie auf standardisierten, universell verfgbaren Protokollen fuen, die jeder einsetzen kann, unabhngig von der jeweiligen Plattform oder dem Betriebssystem. SOAP ist das gebruchlichste Protokoll, das im Zusammenhang mit Webdiensten benutzt wird. Es besteht aus einem Regelwerk fr die XML-Formatierung in einer standardisierten Art und Weise, die jeder Webdienst oder dessen Client verstehen kann. Webdienste lassen sich auch mit Hilfe von HTTP-Get und HTTP-Post nutzen. Webdienste kann man mit Hilfe von ASP.NET erstellen. Der Quellcode sieht dann hnlich aus wie der von ASP.NET-Seiten oder Windows Forms-Anwendungen, so dass man ihn leicht lesen kann. Die Dienste exponieren Funktionen, indem sie Webmethoden erstellen, die wie normale Methoden funktionieren, nur dass sie sich ber das Internet erreichen lassen.

536

Fragen und Antworten

Fr den Einsatz eines Webdienstes fhrt man drei Schritte aus: Ermittlung, Proxy-Erzeugung und Implementierung. Ermittlung ist ein optionaler Schritt, bei dem man Informationen ber einen Webdienst herausfinden kann. Zur Erzeugung eines Proxys gehrt die Erstellung einer Klasse, die den Funktionsumfang kapselt, den man braucht, um auf einen Webdienst bers Internet zuzugreifen. Die Implementierung schlielich erfordert nichts weiter als die Verwendung der Proxy-Klasse in der gewnschten Anwendung. Das letzte Protokoll, von dem Sie heute erfahren haben, ist UDDI. Mit dieser Spezifikation knnen Sie Ihren eigenen Webdienst definieren und ihn potenziellen Kunden beschreiben (in etwas endanwenderfreundlicheren Worten als es die WSDL-Dienstbeschreibung vermag). Die UDDI-Website www.uddi.org bietet Ihnen eine durchsuchbare UDDIDatenbank, bei der Sie Ihre eigenen Webdienste registrieren lassen knnen.

15.7 Fragen und Antworten


F Sind Webdienste sicher? A Da sie auf XML, also reinem Text, basieren, bieten sie an und fr sich keine Sicherheit. Man kann jedoch ein paar Mechanismen einsetzen, um sie sicher zu machen. ASP.NET verfgt ber eine Reihe eingebauter Sicherheitsfunktionen und Sie knnen zur Sicherung von Webdiensten die gleichen Techniken einsetzen, die man fr die Absicherung einer Website nutzen wrde. Zudem besitzt SOAP eine Funktionsuntermenge namens SOAP-Headers, die sich fr die Sicherung von Webdiensten einsetzen lsst.

Worum handelte es sich bei den Attributen in Listing 15.3? A So wie eine Webmethode das [WebMethod]-Attribut bentigt, um richtig funktionieren zu knnen, so brauchen auch die Methoden in einer Proxy-Klasse einige Attribute. Diese definieren zustzliche Informationen ber eine Methode, die bei deren Ausfhrung ntzlich ist, so etwa die Angabe, wo sich erforderliche Dateien befinden. Nhere Informationen erhalten Sie aus den Dokumentationen zu VB .NET und C#, die mit der Dokumentation zu .NET Framework gebndelt sind.

Ich habe keinen Webserver. Gibt es eine andere Mglichkeit, wie ich versuchen kann, Webdienste zu erstellen? A Sie bentigen lediglich einen Ort, wo es einen Webserver gibt und Untersttzung fr das .NET Framework. www.Brinkster.com bietet einen kostenlosen Dienst an, mit dem Sie Websites, die auf ASP.NET und Webdiensten basieren, hochladen und ausfhren knnen. Erstellen Sie die Webdienste auf dem eigenen Rechner und laden Sie sie auf Brinkster hoch, um sie zu testen.

537

Webdienste

Wenn Sie Ihren eigenen Webserver haben wollen, knnen Sie Microsoft Personal Web Server ausprobieren, der sich kostenlos von www.Microsoft.com herunterladen lsst.

15.8 Workshop
Dieser Workshop soll Ihnen helfen, sich besser an die heute behandelten Begriffe zu erinnern. Es hilft Ihnen sehr, die Antworten vollstndig zu verstehen, bevor Sie weiterlesen. Die Antworten zu den Quizfragen und den bungen finden Sie in Anhang A.

Quiz
1. Wofr steht die Abkrzung SOAP? 2. Wahr oder falsch? Das Werkzeug disco.exe erzeugt eine lokale Kopie der WSDLDienstbeschreibung. 3. Was ist eine Proxy-Klasse und warum setzt man eine ein? 4. Stimmt etwas nicht mit folgendem Code?
[WebMethod] private string HelloWorld() { return "Hello World!"; }

5. Welche Dateierweiterungen werden fr Dateien von .NET-Webdiensten benutzt? 6. Warum sehen sich ASP.NET-Dateien und Windows Forms-Dateien so hnlich?

bung
Erstellen Sie einen Webdienst, der Werte von einer Maeinheit in eine andere umrechnet (so etwa von Zoll in Zentimeter; um die genauen Werte brauchen Sie sich nicht zu kmmern). Erstellen Sie einen Windows Forms-Client, der es dem Benutzer gestattet, mit dem Webdienst zu interagieren.

538

Windows-Dienste

6 1

Windows-Dienste

Heute erfahren Sie etwas ber einen anderen Aspekt von .NET und Windows: WindowsDienste. Sie bieten Funktionen, die in das jeweilige Betriebssystem integriert und sozusagen hinter den Kulissen ausgefhrt werden. Diese Dienste hat es schon eine Weile gegeben, etwa unter Windows NT. Neu ist jedoch ihr Einsatz unter .NET. Mit diesen Diensten kann man Web- oder FTP-Server pflegen, Sicherheits- und Anwendungsprobleme berwachen und sogar Datenbanken kontrollieren. Solche Anwendungen laufen ohne Mitwirkung durch den Benutzer. Heute lernen Sie,

um was es sich bei Windows-Diensten handelt, wie man Anwendungen, die keine Benutzeroberflche haben, ausfhrt, wie man Anwendungen installiert, wie sich Dienste mit der Windows-Diensteverwaltung steuern lassen und wie man ber Anwendungen Protokoll fhrt.

16.1 Was sind Windows-Dienste?


Windows-Dienste gleichen keiner Anwendung, die Sie bislang entwickelt haben. Sie hneln Webdiensten insofern, dass man jederzeit auf sie zugreifen kann. Doch hier endet die hnlichkeit bereits die beiden Diensttypen sind nicht zu verwechseln. Bei einer normalen Windows Forms-Anwendung wird dem Benutzer eine Oberflche angeboten, mit der er interagieren kann. Die Anwendung muss ausdrcklich aufgerufen und wieder beendet werden. Bei einem Windows-Dienst ist nichts davon ntig. Er luft als Teil des Betriebssystems im Hintergrund, ist in Bereitschaft und wartet auf Benutzung. Windows-Dienste erfordern nicht unbedingt Benutzereingaben: Sie erfllen hufig ihre eigenen Aufgaben, whrend alles Mgliche um sie herum vor sich geht. Ein Windows-Dienst wird gebraucht, wenn etwas auf einer regelmigen Grundlage zu erledigen ist oder wenn jederzeit ein Ereignis stattfinden knnte, auf das zu reagieren ist hierfr ist eine normale Anwendung nicht die beste Lsung, denn ihre Benutzeroberflche soll ja nicht den Desktop des Benutzers blockieren. Mit Windows werden bereits viele vorinstallierte Dienste ausgeliefert, die zu jeder Zeit im Hintergrund laufen. Eine Menge der Programme, die Sie kennen, laufen als WindowsDienste, so etwa der Webserver IIS, der SQL Server und sogar der Druckspooler, der Ihrem Drucker Daten schickt. Es gibt noch viele andere, die weitaus unaufflliger agieren. Gehen Sie ins Startmen und dort in den EINSTELLUNGEN auf SYSTEMSTEUERUNG/VERWAL-

540

Was sind Windows-Dienste?

TUNG/DIENSTE, um die Liste der auf Ihrem Computer laufenden Dienste einzusehen. Abbildung 16.1 zeigt ein typisches Beispiel dafr in Windows 2000 Professional.

Abbildung 16.1: In einer typischen Installation von Windows 2000 sind jederzeit mehrere Dutzend WindowsDienste eingerichtet.

Die meisten Windows-Dienste laufen im Hintergrund, um Ereignisse zu verarbeiten, die stattfinden knnten. So wartet etwa IIS auf eine Webanfrage von einem Client, um sie zu verarbeiten. Gibt es danach keine weitere Anfrage abzuarbeiten, geht er wieder in den Leerlaufzustand ber. Alle Dienste sorgen dafr, dass das Betriebssystem wie gewnscht arbeitet. Dabei wird aber vermieden, den Benutzer durch unntige Informationen oder Aufforderungen zu Eingriffen zu stren. Ein Windows-Dienst lsst sich so konfigurieren, dass er beim Rechnerstart aufgerufen wird, wenn ihn eine Anwendung anfordert oder auch zu einem anderen Ihnen genehmen Zeitpunkt. Er lsst sich vorbergehend anhalten und wieder neu starten. Wenn Sie einen Windows-Dienst erstellen, knnen Sie ihn nicht einfach so ausfhren und beobachten. Sie mssen ihn im Betriebssystem installieren und als Windows-Dienst ausfhren lassen. Da man ihn nicht vor seiner Ausfhrung direkt testen kann, ist seine Erstellung ein wenig knifflig. Warum sollten wir uns also mit diesen Diensten befassen, wo sie sich doch von Windows Forms-Anwendungen so grundlegend unterscheiden? Der erste Grund dafr ist, dass beide das gleiche Framework nutzen, so dass die Diensterstellung der eines Windows Formulars hnelt. Sie erhalten dadurch einen anderen Blickwinkel auf die Technologie hinter Windows Forms nmlich .NET und das ist nie verkehrt. Zum Zweiten erweisen sich Windows-Dienste fr Entwickler herkmmlicher Desktop-Anwendungen meist als ntzlich.

541

Windows-Dienste

Man wird Sie hufig bitten, eine Anwendung zu schreiben, die man am besten in Form eines Dienstes entwickelt, was aber nur selten gemacht wird. Und schlielich werden Sie etwas ber Installer erfahren, also Windows-Dienste, die sich im Hinblick auf Windows Forms-Anwendungen als sehr wertvolle Hilfsmittel erweisen. Die heutige Lektion demonstriert Ihnen, wie Sie Ihre Fhigkeiten hinsichtlich Windows Forms leicht auf eine andere Technologie anwenden knnen.

16.2 Einen Windows-Dienst erzeugen


Im Hinblick auf die Architektur ist die Erstellung eines Windows-Dienstes doch recht vielschichtig, daher wollen wir es langsam angehen. Die erste neue Klasse, die Sie kennen sollten, ist System.ServiceProcess.ServiceBase: Dies ist die Basisklasse fr alle WindowsDienste unter .NET. Sie hnelt daher der Klasse System.Windows.Forms.Application, deren gebruchlichstes Mitglied die Run-Methode ist, die bei jeder bislang von Ihnen benutzten Main-Methode eingesetzt worden ist. Die Dienst-Basisklasse enthlt eine allgemein gltige Menge von Eigenschaften, mit denen sich ein Windows-Dienst definieren lsst, sowie eine Run-Methode, die den jeweiligen Dienst startet. Die von Ihnen erstellten Windows-Dienste erben von dieser Klasse genau wie Windows Forms-Klassen von System.Windows.Forms.Form erben:
public class MyFirstService : System.ServiceProcess.ServiceBase

Wir kommen gleich wieder auf die ServiceBase-Klasse zurck. Als Nchstes mssen Sie die drei Klassen System.ServiceProcess.ServiceProcessInstaller, System.ServiceProcess.ServiceInstaller und System.Configuration.Install .Installer kennen lernen. Diese Klassen sind erforderlich, um einen Windows-Dienst zu installieren und zu starten. ServiceProcessInstaller und ServiceInstaller sind dafr zustndig, dass Informationen ber den jeweiligen Dienst in die Registrierdatenbank von Windows geschrieben werden hier mssen alle Windows-Dienste registriert werden, um richtig zu funktionieren. Installer ist die Basisklasse, die zur Installation aller .NETAnwendungen benutzt wird. Heute werden Sie diese Klasse nur kurz verwenden, um Ihren Windows-Dienst einzurichten. Sobald der Windows-Dienst luft, verwendet man die Windows-Diensteverwaltung, engl. Service Control Manager (SCM), um ihn zu verwalten. Der SCM ist eine grafische Benutzeroberflche zur Manipulation von Windows-Diensten. Obwohl es eine gleichwertige .NET-Klasse namens System.ServiceProcess.ServiceController gibt, verwenden wir hier den SCM, weil er einfacher zu handhaben ist. Abbildung 16.2 zeigt ein Diagramm der bislang skizzierten Architektur.

542

Einen Windows-Dienst erzeugen

Service Control Manager

Start Stop Pause etc

Windows-Dienst ServiceBase Ihre angepasste Service-Klasse Ihr angepasster Installer Installer

erben

erben

ServiceProcessInstaller ServiceInstaller

Abbildung 16.2: Sie erzeugen Ihre Windows-Dienste aus mehreren unterschiedlichen Klassen und bedienen sich des Service Control Managers (SCM), um sie zu verwalten.

Wir wollen uns die ServiceBase-Klasse genauer ansehen. Sie verfgt ber einige Ereignisse, die man benutzt, um den Dienst zu steuern, darunter Start, Stop und Pause. (Der nchste Abschnitt zeigt, wie Sie den Dienst schreiben, so dass diese Ereignisse Ihnen die entsprechend ntige Funktionalitt bieten.) Tabelle 16.1 beschreibt alle Eigenschaften von ServiceBase und Tabelle 16.2 fhrt die Methoden auf.
Eigenschaft
AutoLog

Beschreibung Gibt an, ob die Ereignisse Starten, Beenden, Anhalten und Fortsetzen im integrierten Ereignisprotokoll aufgezeichnet werden sollen. Gibt an, ob der Dienst Benachrichtigungen ber nderungen im Energieversorgungsstatus des Computers handhabt, also das PowerEvent-Ereignis verarbeitet wird. Gibt an, ob sich der Dienst anhalten und fortsetzen lsst. Gibt an, ob der Dienst vom Abschalten des Rechners verstndigt werden soll. Gibt an, ob sich der Dienst beenden lsst. Ein Ereignisprotokoll, in das Sie Informationen eintragen knnen. Der Name des fraglichen Dienstes.

CanHandlePowerEvent

CanPauseAndContinue CanShutdown

CanStop EventLog ServiceName

Tabelle 16.1: Eigenschaften von ServiceBase

543

Windows-Dienste

Methode
OnContinue

Beschreibung Wird ausgefhrt, wenn der Dienst ber den SCM aus einem angehaltenen Zustand heraus gestartet wird. Wird ausgefhrt, wenn der SCM ein benutzerdefiniertes Ereignis an den Dienst abschickt. (Nhere Informationen finden Sie in der Dokumentation zum .NET Framework.) Wird ausgefhrt, wenn der Dienst angehalten wird. Wird ausgefhrt, wenn sich der Status der Energieversorgung ndert. Wird ausgefhrt, wenn der Computer heruntergefahren wird. Wird ausgefhrt, wenn der Dienst aus einem beendeten Zustand heraus gestartet wird. Wird ausgefhrt, wenn der Dienst beendet wird. Stellt den Hauptstartpunkt fr den Dienst bereit.

OnCustomCommand

OnPause OnPowerEvent OnShutdown OnStart

OnStop Run

Tabelle 16.2: Methoden von ServiceBase (Forts.)

Es gibt keine ffentlich zugnglichen nicht-geerbten Ereignisse fr die ServiceBase-Klasse. Um Aktionen durchzufhren, mssen Sie statt dessen die entsprechenden OnEreignisName-Methoden berschreiben. Mehr dazu im nchsten Abschnitt. Sobald Sie Ihren Dienst und den Installer erstellt haben und diese bereitstehen, installieren Sie sie im SCM. Dieser findet die Programmdatei fr Ihren Dienst und fhrt sie in passender Weise aus. Der SCM sollte die einzige Einheit sein, die auf Ihre DienstProgrammdatei direkt zugreift. Als nchsten Schritt erstellen wir einen Dienst, um zu sehen, wie das alles zusammenpasst. Dies ist heute nur ein einfacher Windows-Dienst namens Day16Service. Er berwacht den Computer, auf dem er luft. Wir setzen die noch von Tag 11 bekannte Klasse FileSystemWatcher ein, um zu bestimmen, ob nderungen am Dateisystem erfolgt sind. Sollte dies der Fall sein, wird ein Vermerk in das Ereignisprotokoll geschrieben. Denken Sie daran, dass Windows-Dienste keine Benutzeroberflche haben. Somit knnen sie nicht direkt mit dem Benutzer kommunizieren. Versucht man etwa, dementsprechende Methoden aufzurufen, kann das zu Problemen fhren. Wenn Sie beispielsweise die Methode MessageBox.Show in Ihrem WindowsDienst aufrufen, versucht der Dienst zwar, ein Meldungsfeld anzuzeigen, der Benutzer bekommt es jedoch nie zu Gesicht. Der Dienst wartet nun ewig darauf, dass der Benutzer auf das Meldungsfeld reagiert, was dieser aber gar nicht kann.

544

Einen Windows-Dienst erzeugen

Listing 16.1 zeigt den einfachen Windows-Dienst Day16Service. Listing 16.1: Ein berwachungsdienst fr das Dateisystem
1: Imports System.ServiceProcess 2: Imports System.IO 3: 4: namespace TYWinforms.Day16 5: 6: Public Class Day16Service : Inherits ServiceBase 7: private objWatcher As New FileSystemWatcher 8: 9: Public Sub New() 10: objWatcher.Path = "c:\" 11: objWatcher.IncludeSubdirectories = True 12: objWatcher.NotifyFilter = (NotifyFilters.FileName Or  NotifyFilters.DirectoryName Or NotifyFilters.LastWrite) 13: objWatcher.EnableRaisingEvents = True 14: 15: AddHandler objWatcher.Changed, new FileSystemEventHandler(AddressOf  Me.FileChanged) 16: AddHandler objWatcher.Deleted, new FileSystemEventHandler(AddressOf  Me.FileDeleted) 17: AddHandler objWatcher.Created, new FileSystemEventHandler(AddressOf  Me.FileCreated) 18: AddHandler objWatcher.Renamed, new RenamedEventHandler(AddressOf  Me.FileRenamed) 19: 20: Me.ServiceName = "TYWinforms Day 16 Service" 21: End Sub 22: 23: Shared Sub Main() 24: ServiceBase.Run(New Day16Service()) 25: End Sub 26: 27: Protected Overrides Sub OnStart(ByVal args() As String) 28: EventLog.WriteEntry("TYWinforms Day 16 Service startet") 29: End Sub 30: 31: Protected Sub FileChanged(Sender As Object, e As FileSystemEventArgs) 32: EventLog.WriteEntry(e.Name & " " & e.ChangeType.ToString()) 33: End Sub 34: 35: Public Sub FileCreated(Sender As Object, e As FileSystemEventArgs) 36: EventLog.WriteEntry(e.Name & " erstellt um: " & e.FullPath) 37: End Sub

545

Windows-Dienste

38: 39: 40: 41: 42: 43: 44: 45: 46: 47:

Public Sub FileDeleted(Sender As Object, e As FileSystemEventArgs) EventLog.WriteEntry(e.Name & " gelscht aus: " & e.FullPath) End Sub Public Sub FileRenamed(Sender As Object, e As RenamedEventArgs) EventLog.WriteEntry(e.OldName & " umbenannt in: " & e.Name) End Sub End Class end namespace

Wenn Sie an FileSystemWatcher zurckdenken, dann drfte Ihnen dieser Code vertraut sein. In Zeile 1 wird der Namensraum System.ServiceProcess importiert, damit die richtigen Klassen zur Verfgung stehen. Beachten Sie, dass Ihre Windows-Dienstklasse Day16Service von der ServiceBase-Klasse in Zeile 6 erbt. Der Konstruktor fr Day16Service (Zeilen 9 bis 21) setzt einige Eigenschaften des FileSystemWatcher-Objekts, darunter den zu berwachenden Verzeichnispfad (nmlich c:\), die zu beobachtenden Vernderungsarten und ob Unterverzeichnisse einbezogen werden sollen. Die Zeilen 15 bis 18 weisen den diversen Ereignissen des FileSystemWatcher-Objekts Delegaten zu und die Ereignishandler finden sich in den Zeilen 31 bis 45. In Zeile 20 legen Sie den Namen des Dienstes fest, der im SCM als benutzerfreundlicher Name angezeigt wird. In Zeile 23 findet sich die typische Main-Methode. Von hier aus rufen Sie die Run-Methode der ServiceBase-Klasse auf. Sie ldt den Dienst (wie sonst eine Anwendung) in den Arbeitsspeicher. Doch ein Windows-Dienst startet im Grunde erst, wenn er vom SCM dazu angewiesen wird, ansonsten dreht er nur Dumchen. Da es keine ffentlich zugnglichen Ereignisse fr die ServiceBase-Klasse gibt, muss man zunchst die OnEreignisName-Methoden erben, um Ereignisse zu verarbeiten. In Zeile 27 berschreiben Sie das OnStart-Ereignis, um das StartEreignis zu verarbeiten. Jeglicher Initialisierungscode fr den Dienst sollte weiterhin im Konstruktor platziert werden (wie in anderen Anwendungen) und nicht hier in der OnStart-Methode. Diese Methode wird vor allem benutzt, um das System vom Start des Dienstes zu unterrichten. Wir rufen die WriteEntry-Methode des EventLog-Objekts auf, um den angegebenen Text in das Systemereignisprotokoll zu schreiben. Dort kann man spter nachlesen, wie der Dienst ausgefhrt wird. Das Ereignisprotokoll sehen wir uns spter in diesem Kapitel an.

546

Einen Windows-Dienst erzeugen

Jede der vier folgenden Methoden tut fast das Gleiche wie OnStart. Die Methoden FileCreated, FileDeleted, FileChanged und FileRenamed behandeln die FileSystemWatcher-Ereignisse und schreiben entsprechende Eintrge in das Ereignisprotokoll. Kompilieren Sie diese Anwendung noch nicht. Sie mssen noch einen Installer erstellen. Listing 16.2 zeigt einen recht einfachen Installer, den man fr den Dienst nutzen kann: Listing 16.2: Ihr Installer fr den Windows-Dienst
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: Imports System.ServiceProcess Imports System.ComponentModel Imports System.Configuration.Install namespace TYWinforms.Day16 <RunInstaller(True)> Public Class MyInstaller : Inherits Installer Private objPInstaller As New ServiceProcessInstaller Private objInstaller As New ServiceInstaller Public Sub New() objPInstaller.Account = ServiceAccount.LocalSystem objPInstaller.Password = Nothing objPInstaller.Username = Nothing objInstaller.ServiceName = "TYWinforms Day 16 Service" Installers.AddRange(New Installer() {objPInstaller, objInstaller}) End Sub End Class End Namespace

Sobald man die beteiligten Klassen kennt, ist dieses Listing sogar noch einfacher zu lesen als das vorige. Wie in den Zeilen 1 bis 3 zu sehen, bentigen wir drei Namensrume fr diese Klasse. In Zeile 6 wird die Installer-Klasse erzeugt. Sie erbt von der Klasse System.Configuration.Install.Installer; dies ist ntig, damit die Klasse von der CLR als Installer angesehen wird. Zweitens hat sie ein Attribut vor dem Klassennamen. Auf true gesetzt, teilt das <RunInstaller>-Attribut ([RunInstaller] in C#) der CLR mit, dass diese Installer-Klasse whrend des Installationsvorgangs eingesetzt werden soll. Die Zeilen 7 und 8 erzeugen ServiceProcessInstaller- und ServiceInstallerKlassen. Erinnern Sie sich, dass diese Klassen dafr zustndig sind, Informationen ber den Windows-Dienst in die Windows-Registrierung zu schreiben,

547

Windows-Dienste

damit er richtig eingesetzt wird. Dem Entwickler stellen sie diverse Eigenschaften zur Verfgung, die fr den Dienst genutzt werden. Die Zeilen 11, 12 und 13 legen das Konto (den Account) fest, unter dem der Windows-Dienst ausgefhrt wird. Das ist sehr wichtig. Eine typische WindowsInstallation kann mehrere Benutzer haben und davon kann jeder unterschiedliche Berechtigungen besitzen. Zum Beispiel lassen sich mitunter die Dateien im Verzeichnis EIGENE DATEIEN des einen Benutzers nicht durch einen anderen Benutzer betrachten. Diese Zugriffsrechte werden auch dazu verwendet, die Kontrolle, die ein Benutzer ber das Betriebssystem hat, zu beschrnken, damit nicht ein unbekannter oder nicht vertrauenswrdiger Benutzer das System zerstren kann. Unserem Windows-Dienst erlauben wir, unter dem Systemkonto zu laufen. Diese Anmeldung bietet berhaupt keine Einschrnkungen, denn es handelt sich um das Konto, das der Computer selbst benutzt. Es gibt eine ganze Reihe von Kontotypen, die der Dienst nutzen kann. Das zu benutzende Konto wird mit den Eigenschaften Account, Username und Password der ServiceProcessInstaller-Klasse eingerichtet. Die Account-Eigenschaft verwendet einen Wert der Aufzhlung ServiceAccount:

LocalService: Ein Konto, das umfangreiche Berechtigungen besitzt und den Computer selbst reprsentiert. LocalSystem: Besitzt weniger Berechtigungen als LocalService und reprsentiert einen anonymen Benutzer. NetworkService: Besitzt noch weniger Berechtigungen als LocalSystem, reprsentiert aber den Computer in einem Netzwerk. User: Ein bestimmtes Benutzerkonto. Wird es benutzt, muss man Werte fr die Eigenschaften ServiceProcessInstaller.Username und Password bereitstellen.

Normalerweise benutzt man das LocalSystem-Konto fr einen Dienst, denn man will ihn ja als Systemprozess laufen lassen. Bei bestimmten Gelegenheiten will man aber die Kontoart wechseln. Wenn man beispielsweise einen Dienst nur ausfhren mchte, sobald sich ein bestimmter Benutzer mit passenden Berechtigungen am System anmeldet, kann man den Wert ServiceAccount.User benutzen.
LocalService und NetworkService sind nur auf Rechnern verfgbar, auf denen

Windows XP luft.

548

Dienste zum Zusammenspiel mit Windows bringen

Weil unser Dienst unter dem LocalSystem-Konto luft, setzen wir die Eigenschaften Username und Password auf Nothing (null in C#). In Zeile 15 wird der Name des Dienst-Installers festgelegt. Dies muss der gleiche Name sein wie jener, den man in der Dienstklasse festlegt (Zeile 20 von Listing 16.1). Schlielich sagen Sie dem Installer, welcher Installer ausgefhrt werden soll. Das mag zunchst etwas komisch klingen, doch eine einzelne Installer-Datei kann tatschlich mehrere Installer-Klassen haben. Die Methode Installers.AddRange kann zu diesem Zweck ein Array von Installer-Klassen bernehmen. In Zeile 17 legt man fest, welche dieser Installer-Klassen ausgefhrt werden soll. Man legt einfach ein neues Array von Installer-Objekten an, das die Objekte ServiceProcessInstaller und ServiceInstaller enthlt, die bereits initialisiert wurden. Kompilieren Sie nun diese Anwendung, wobei Sie darauf achten, alle Assemblies einzubeziehen:
vbc /t:winexe /r:system.dll /r:system.serviceprocess.dll / r:system.configuration.install.dll listing16.1.vb listing16.2.vb

Nun haben Sie zwar einen ausfhrbaren Dienst, aber Sie knnen ihn nicht direkt ausfhren. Versuchen Sie dies dennoch, erhalten Sie eine Fehlermeldung wie in Abbildung 16.3 zusammen mit der Wahlmglichkeit, den Dienst zu debuggen. Im nchsten Abschnitt untersuchen wir, welche Vorkehrungen zu treffen sind, um den Dienst zu installieren und unter Windows auszufhren.

Abbildung 16.3: Ihr Windows-Dienst lsst sich nicht direkt ausfhren.

16.3 Dienste zum Zusammenspiel mit Windows bringen


Der fertig kompilierte Dienst lsst sich nicht auf Funktionstchtigkeit testen, ohne ihn zuvor zu installieren. Doch .NET Framework stellt ein Befehlszeilen-gesteuertes Hilfsprogramm bereit das Installer-Werkzeug (installutil.exe) , das dies fr Sie erledigt. Gehen Sie zum Verzeichnis c:\winforms\day16 (oder dorthin, wo der kompilierte Dienst vorliegt) und geben Sie das folgende Kommando ein:
installutil listing16.1.exe

549

Windows-Dienste

Wenn alles gut geht, sehen Sie folgende Ausgabe:


Microsoft (R).NET Framework Installation utility Version 1.0.3705.0 Copyright (C)Microsoft Corporation 1998-2001. All rights reserved. Eine transaktive Installation wird ausgefhrt. Die Installationsphase wird gestartet. Die Protokolldatei enthlt den Status der Assembly c:\winforms\day16\listing16.1.exe. Die Datei befindet sich in c:\winforms\day16\listing16.1.InstallLog. Assembly 'c:\winforms\day16\listing16.1.exe' wird installiert. Betroffene Parameter: assemblypath = c:\winforms\day16\listing16.1.exe logfile = c:\winforms\day16\listing16.1.InstallLog Dienst 'TYWinforms Day 16 Service' wird installiert... Dienst 'TYWinforms Day 16 Service' wurde installiert. Die Ereignisprotokollquelle TYWinforms Day 16 Service im Protokoll Application wird erstellt... Die Installationsphase ist abgeschlossen und die Commit-Phase beginnt. Die Protokolldatei enthlt den Status der Assembly c:\winforms\day16\listing16.1.exe. Die Datei befindet sich in c:\winforms\day16\listing16.1.InstallLog. Assembly 'c:\winforms\day16\listing16.1.exe' wird ausgefhrt. Betroffene Parameter: assemblypath = c:\winforms\day16\listing16.1.exe logfile = c:\winforms\day16\listing16.1.InstallLog Die Commit-Phase wurde abgeschlossen. Die transaktive Installation ist abgeschlossen. C:\winforms\day16>

Das Installer-Hilfsprogramm ist sehr ntzlich. Es verrichtet seine Arbeit intelligent und erzeugt eine umfangreiche Ausgabe. Diese Bildschirmausgabe hier beschreibt eine Installationstransaktion. Das bedeutet, dass der Computer im Falle eines Scheiterns der Installation wieder zurck zum Ausgangspunkt gehen kann. Das heit, dass keine halbinstallierten Programme Ihr System ruinieren knnen. Dieses Leistungsmerkmal wird zunehmend bedeutender, je umfangreicher und komplizierter Ihre Anwendungen werden, etwa wenn Sie mehr als einen Dienst aus einer Einzeldatei installieren wollen.

550

Dienste zum Zusammenspiel mit Windows bringen

Wenn Ihre Installation jedoch fehlschlgt, sehen Sie folgende Ausgabe:


Microsoft (R).NET Framework Installation utility Version 1.0.3705.0 Copyright (C)Microsoft Corporation 1998-2001.All rights reserved. Eine transaktive Installation wird ausgefhrt. Die Installationsphase wird gestartet. Die Protokolldatei enthlt den Status der Assembly c:\winforms\day16\listing16.1.exe. Die Datei befindet sich in c:\winforms\day16\listing16.1.InstallLog. Assembly 'c:\winforms\day16\listing16.1.exe' wird installiert. Betroffene Parameter: assemblypath = c:\winforms\day16\listing16.1.exe logfile = c:\winforms\day16\listing16.1.InstallLog Dienst 'TYWinforms Day 16 Service' wird installiert... Die Ereignisprotokollquelle TYWinforms Day 16 Service im Protokoll Application wird erstellt... Whrend der Installationsphase ist eine Ausnahme aufgetreten. System.ComponentModel.Win32Exception: Der angegebene Dienst ist bereits vorhanden Die Rollback-Phase der Installation wird gestartet. Die Protokolldatei enthlt den Status der Assembly c:\winforms\day16\listing16.1.exe. Die Datei befindet sich in c:\winforms\day16\listing16.1.InstallLog. Assembly 'c:\winforms \day16 \listing16.1.exe' wird zurckgesetzt. Betroffene Parameter: assemblypath = c:\winforms\day16\listing16.1.exe logfile = c:\winforms\day16\listing16.1.InstallLog Der vorherige Status des Ereignisprotokolls fr die Quelle TYWinforms Day 16 Service wird wiederhergestellt. Die Rollback-Phase wurde abgeschlossen. Die transaktive Installation ist abgeschlossen. Die Installation ist fehlgeschlagen, und der Rollback wurde ausgefhrt.

Der Fehler wird auf halber Strecke durch die Ausgabe beschrieben. Der Installationsvorgang wird auf den Ausgangspunkt zurckgefahren, damit Ihr Rechner weiterhin richtig funktioniert. Beachten Sie, dass der Installer die Ausgabedateien installutil.InstallLog, listing16.1.InstallLog und listing16.1.InstallState erzeugt hat. Die ersten beiden bieten einen Groteil dessen, was das Installer-Hilfsprogramm in seiner Ausgabe erzeugte.

551

Windows-Dienste

Diese Dateien helfen Ihnen bei der Fehlersuche und -diagnose. Die letzte Datei ist eine SOAP-Meldung, die ein Webdienst nutzen kann, um zu bestimmen, ob der WindowsDienst richtig installiert worden ist. Wir besprechen sie hier nicht, aber Sie knnen gerne einmal durch den XML-Code dieser Datei blttern. Sie sollten auch wissen, wie man einen Dienst deinstalliert. Auch dies lsst sich mit dem Installer-Hilfsprogramm ausfhren:
installutil /u listing16.1.exe

Der /u-Schalter befiehlt dem Hilfsprogramm, den Windows-Dienst aus dem SCM zu entfernen und die mit ihm verknpften Registrierungsschlssel zu lschen, damit Ihr Rechner frei von schdlichen Einstellungen bleibt. Wenn Sie berhaupt einmal den Code Ihres Dienstes ndern mssen, sollten Sie ihn zuerst deinstallieren, bevor Sie ihn neu kompilieren. Erst danach wird er neu installiert, damit Ihr System nicht durch neue Einstellungen in einen instabilen Zustand versetzt wird.

Windows-Dienste starten und beenden


Sie sind nun so weit, den Windows-Dienst zu starten und dafr zu sorgen, dass er sauber luft. ffnen Sie die Systemsteuerung fr Dienste (START/SYSTEMSTEUERUNG/VERWALTUNG/DIENSTE). Blttern Sie die Seite hinunter, bis Sie Ihren Windows-Dienst sehen, der den Namen TYWinforms Day 16 Service trgt. Rechtsklicken Sie darauf und whlen Sie den Befehl STARTEN. Abbildung 16.4 zeigt diesen Vorgang. Sie knnen den Dienst auch von der Befehlszeile aus starten. Geben Sie dazu folgendes Kommando ein, wenn Sie sich im Verzeichnis c:\winforms\day16 befinden:
net start "TYWinforms Day 16 Service"

Abbildung 16.4: Der SCM startet Ihren Windows-Dienst.

552

Dienste zum Zusammenspiel mit Windows bringen

Das Dialogfenster, das nun erscheint, zeigt Ihnen einen Fortschrittsbalken an, der das Fortschreiten des Startvorgangs verdeutlicht. Einem Windows-Dienst stehen 30 Sekunden zur Verfgung, um richtig zu starten und anzulaufen, bevor der SCM annimmt, dass der Dienst defekt sei und den Vorgang beendet. Falls Sie eine Fehlermeldung erhalten sollten, mssen Sie den Dienst mit dem /u-Schalter des Installer-Hilfsprogramms deinstallieren, Ihren Quellcode neu untersuchen und alle gefundenen Fehler bereinigen. Dann kompilieren und installieren Sie den Dienst erneut. Geht alles gut, sollte Ihr Dienst relativ schnell anlaufen, da er ja nicht sonderlich kompliziert ist. Sie knnen ihn jetzt mit einem Rechtklick beenden (im Kontextmen BEENDEN whlen). Htten Sie CanPauseAndContinue auf true gesetzt, knnten Sie den Dienst auch anhalten und fortsetzen. Wie die Pause- und Play-Tasten an einem Videorecorder, werden diese beiden Funktionen benutzt, weil sie weniger Zeit kosten, als den Dienst vllig zu beenden und erneut zu starten. Sie knnen den Dienst auch von der Befehlszeile aus beenden. Geben Sie dazu folgendes Kommando ein, wenn Sie sich im Verzeichnis c:\winforms\day16 befinden:
net stop "TYWinforms Day 16 Service"

Ihre Dienste berwachen


Ihr Dienst soll ja das Dateisystem berwachen, doch woher wollen Sie wissen, ob er funktioniert? An diesem Punkt kommt das Ereignisprotokoll (engl. Event Log) ins Spiel. Sie knnen das Ereignisprotokoll mit der Ereignisanzeige ansehen, indem Sie auf START/SYSTEMSTEUERUNG/VERWALTUNG/EREIGNISANZEIGE gehen. Abbildung 16.5 zeigt die Ereignisanzeige mit einem typischen Protokoll. Das Ereignisprotokoll ist ein zentrales Ablagesystem fr Information ber die Vorgnge in einem Rechner. Fehlermeldungen und Benachrichtigungen, die Ihr Computer ausgibt, sind hufig hier wiederzufinden (manchmal wissen Sie nicht, was schief gelaufen ist, daher sollten Sie hier nachsehen). Im linken Fenster der Ereignisanzeige sehen Sie drei Eintrge: ANWENDUNGSPROTOKOLL, SICHERHEITSPROTOKOLL und SYSTEMPROTOKOLL. Klicken Sie auf ANWENDUNGSPROTOKOLL und betrachten Sie die sich daraus ergebende Liste im rechten Fenster. Die meisten Eintrge in dieser Liste werden vom Dateiberwachungsdienst erzeugt. Doppelklicken Sie auf einen beliebigen Eintrag, um Einzelheiten zu betrachten, wie sie in Abbildung 16.6 zu sehen sind.

553

Windows-Dienste

Abbildung 16.5: Das Ereignisprotokoll zeichnet Ihre System- und Anwendungsereignisse auf.

Abbildung 16.6: Die Beschreibung, die von Ihrem Windows-Dienst erzeugt wird.

Diese Details stellen die Ausgabe dar, die der Windows-Dienst erzeugt hat. Abbildung 16.6 zeigt, dass sich das Verzeichnis dokumente und einstellungen\localservice\anwendungsdaten\microsoft gendert (Changed) hat. Sie sehen zustzliche Informationen wie etwa die

554

Schnittstellen fr Windows-Dienste

Uhrzeit, zu der der Eintrag erzeugt wurde, welcher Windows-Dienst ihn erzeugte, unter welchem Benutzer der Rechner gerade lief usw. Schlieen Sie das EIGENSCHAFTEN-Fenster und kehren Sie zur Ereignisanzeige zurck. Sie knnen sehen, wie sich eine Reihe von Dateien und Verzeichnissen ndert etwas, was Sie sonst nie erfahren wrden, falls Sie sie nicht mit Vorbedacht berwachen wrden. Ihr Ereignisprotokoll wird sich rasch mit Meldungen vom Dateiberwachungsdienst fllen. Mein Rechner hatte in weniger als 60 Sekunden mehr als 60 Meldungen, nur indem ich den Dienst startete und ein Word-Dokument bearbeitete. Das Ereignisprotokoll verfgt nur ber begrenzten und festgelegten Platz. Sollte Ihr Ereignisprotokoll voll sein, generiert Ihr Windows-Dienst Fehlermeldungen und verhlt sich schlielich nicht mehr vorhersagbar. Sie knnen Ihr Protokoll aufrumen, indem Sie auf den Eintrag im linken Fenster rechtsklicken und die Kontextoption ALLE EREIGNISSE LSCHEN whlen. Klicken Sie auf die Registerkarte SYSTEMPROTOKOLL im linken Fenster. Das rechte Fenster zeigt dann von Ihrem Rechner erzeugte Fehlermeldungen und Benachrichtigungen an, die sich nicht auf Anwendungen beziehen. Sie sehen hier wahrscheinlich Vermerke, die besagen, dass Ihrem Windows-Dienst eine Startmeldung vom SCM geschickt wurde und dass er erfolgreich gestartet wurde. Fr alle Ihre Windows Forms-Anwendungen und besonders fr Ihre Windows-Dienste kann das Ereignisprotokoll von groem Vorteil sein. Wenn Sie mal auf einen Fehler stoen, dessen Ursache Sie nicht finden knnen, wird Ihnen das Ereignisprotokoll detaillierte Informationen liefern, die Ihnen bei der Problemlsung helfen. Bei Windows-Diensten, die ja keine Benutzeroberflche haben, bildet das Ereignisprotokoll die einzige Stelle, an der Sie Rckmeldungen erhalten. Zgern Sie nicht, dieses Hilfsmittel zu nutzen, wenn Sie Fehler in Ihren Anwendungen beseitigen (beim Debugging) oder auch nur, um zu sehen, wie Ihr Rechner arbeitet.

16.4 Schnittstellen fr Windows-Dienste


Windows-Dienste haben zwar keine Benutzeroberflche, doch man kann eine Schnittstelle erstellen, um mit ihnen zu interagieren. Zu diesem Zweck nutzt man die Klasse ServiceController. Diese Klasse steuert Dienste. Damit knnen Sie also Dienste starten, beenden, anhalten und fortsetzen sowie deren aktuellen Zustand abfragen. Auf diese Weise lsst sich ein benutzerdefinierter SCM erstellen.
ServiceController hat zwei Eigenschaften, die uns interessieren: ServiceName und MachineName. ServiceName ist offensichtlich der zu steuernde Windows-Dienst und bei Machine-

555

Windows-Dienste

Name handelt es sich um den Namen des Rechners, auf dem der angegebene Dienst luft.

Das bedeutet, dass man Dienste steuern kann, die auf verschiedenen Computern laufen, solange diese sich im selben Netzwerk befinden. Es gibt auch Methoden zur Dienststeuerung, darunter Start, Stop usw. Wir wollen eine einfache Windows Forms-Anwendung erstellen, um die Klasse ServiceController auszuprobieren. Listing 16.3 zeigt den entsprechenden Code. Listing 16.3: Ihr benutzerdefinierter SCM
1: 2: 3: 4: 5: 6: 7: 8: Imports Imports Imports Imports System System.Windows.Forms System.Drawing System.ServiceProcess

namespace TYWinforms.Day16 public class Listing163 : Inherits Form private objController As New ServiceController("TYWinforms Day 16  Service") 9: 10: private btStart As New Button() 11: private btStop As New Button() 12: private tmrService As New Timer() 13: 14: public sub New() 15: objController.MachineName = "." 16: 17: btStart.Location = new Point(20,75) 18: btStart.Text = "Start" 19: AddHandler btStart.Click, new EventHandler (AddressOf  Me.StartService) 20: 21: if objController.Status = ServiceControllerStatus.Running then 22: btStart.Enabled = false 23: end if 24: 25: btStop.Location = new Point(200,75) 26: btStop.Text = "Stop" 27: AddHandler btStop.Click, new EventHandler (AddressOf  Me.StopService) 28: 29: if objController.Status = ServiceControllerStatus.Stopped then 30: btStop.Enabled = false 31: end if 32: 33: tmrService.Interval = 1000

556

Schnittstellen fr Windows-Dienste

34: tmrService.Enabled = true 35: AddHandler tmrService.Tick, new EventHandler (AddressOf  Me.CheckStatus) 36: 37: Me.Text = objController.ServiceName & " " &  objController.Status.ToString() 38: 39: Me.Controls.Add(btStart) 40: Me.Controls.Add(btStop) 41: end sub 42: 43: private sub StartService(Sender As Object, e As EventArgs) 44: objController.Start() 45: btStart.Enabled = false 46: btStop.Enabled = true 47: 48: ResetController() 49: 50: Me.Text = objController.ServiceName & " " &  objController.Status.ToString() 51: end sub 52: 53: private sub StopService(Sender As Object, e As EventArgs) 54: objController.Stop() 55: btStart.Enabled = true 56: btStop.Enabled = false 57: 58: ResetController() 59: 60: Me.Text = objController.ServiceName & " " &  objController.Status.ToString() 61: end sub 62: 63: private sub CheckStatus(Sender As Object, e As EventArgs) 64: ResetController() 65: 66: Me.Text = objController.ServiceName & " " &  objController.Status.ToString() 67: end sub 68: 69: private sub ResetController() 70: objController = nothing 71: objController = New ServiceController ("TYWinforms Day 16 Service") 72: end sub 73: 74: public shared sub Main()

557

Windows-Dienste

75: 76: 77: 78:

Application.Run(new Listing163()) end sub end class end namespace

Eine Menge Holz sozusagen! Aber im Grunde ist all dies einfach. In Zeile 8 erzeugen Sie eine neue ServiceController-Klasse und bergeben ihr den Namen des zu steuernden Windows-Dienstes (TYWinforms Day 16 Service). Im Konstruktor setzen Sie in Zeile 15 die Eigenschaft ServiceController.MachineName auf ".", was den lokalen Rechner bezeichnet. Das ist zwar der Standardwert, aber es hilft, ihn trotzdem anzugeben. Die nchsten Zeilen initialisieren einige Schaltflchen, mit denen man den Windows-Dienst steuern kann. In Zeile 33 initialisieren Sie ein Timer-Steuerelement (vgl. Tag 6), das einen aktualisierten Status des fraglichen Dienstes protokolliert. Jede Sekunde wird der Status geprft und der Benutzer informiert das wird im Ereignishandler CheckStatus in Zeile 63 behandelt. Der Status des Windows-Dienstes wird durch einen Wert aus der Aufzhlung ServiceControllerStatus dargestellt, wie in den Zeilen 21 und 29 zu sehen. Wir prfen den Status in diesen zwei Zeilen, um zu bestimmen, welche (falls berhaupt eine) der Stopp- und Start-Schaltflchen aktiviert sein soll. ServiceControllerStatus kann folgende Werte annehmen:
ContinuePending Paused PausePending Running StartPending Stopped StopPending

In Zeile 37 wird die Titelzeile des Formulars auf den aktuellen Status des Dienstes gesetzt. Hier passiert etwas Interessantes. Wenn man auf die Eigenschaft ServiceController.Status zugreift, liefert sie nur einen Schnappschuss des Dienstzustands in dem Moment, zu dem Sie erstmals auf Status zugreifen. Anders ausgedrckt, wenn Ihr erster Aufruf an Status den Wert ServiceControllerStatus.Stopped liefert, dann geben alle nachfolgenden Aufrufe von Status den gleichen Wert zurck, gleichgltig, was mit dem Dienst in der Zwischenzeit geschehen sein mag. Um diesen Mangel zu umgehen, mssen Sie die ServiceController-Klasse jedes Mal, wenn Sie den Status abfragen wollen, neu instantiieren. (Dieses Problem betrifft ausschlielich Status.) Wir tun

558

Schnittstellen fr Windows-Dienste

dies in der ResetController-Methode in Zeile 69, die jeweils bei Bedarf aufgerufen wird. Werfen wir einen Blick auf die Methoden StartService und StopService in den Zeilen 43 und 53. Sie sind fast identisch. Sie rufen jeweils die Start- und Stop-Methoden auf, schalten die entsprechenden Schaltflchen ein oder aus, initialisieren den ServiceController neu und aktualisieren die Titelzeile des Formulars. Da die Status-Eigenschaft bereits im Konstruktor abgefragt wurde, muss der ServiceController vor einer erneuten Abfrage neu initialisiert werden. Schlielich setzt die CheckStatus-Methode in Zeile 63 den ServiceController zurck und aktualisiert die Titelzeile erneut. Kompilieren Sie diese Klasse (nicht vergessen, die Assembly System.ServiceProcess.dll zu referenzieren) und fhren Sie sie aus. ffnen Sie den System-SCM, um zu verifizieren, dass Ihre Anwendung tatschlich den Dienst beeinflusst. Verfolgen Sie, wie sich die Titelzeile jedes Mal ndert, wenn Sie auf die jeweils andere Schaltflche klicken. Abbildung 16.7 zeigt ein typisches Ergebnis.

Abbildung 16.7: Ihr benutzerdefinierter SCM kann jeden Windows-Dienst steuern.

Nach ein paar einfachen nderungen lsst sich der benutzerdefinierte SCM dafr einsetzen, jeden beliebigen Windows-Dienst zu verwalten. Sie knnen sogar alle Dienste von dieser einen Anwendung aus steuern. Die Methode ServiceController.GetServices beschafft ein Array von ServiceController-Objekten, das alle aktuell auf dem System installierten und laufenden Windows-Dienste reprsentiert. Etwas, das Ihr benutzerdefinierter SCM dem System-SCM voraus hat, ist die Fhigkeit, Ihrem Dienst benutzerdefinierte Befehle zu erteilen, die ber die normalen Start-, Beenden-, Pause- und Fortsetzen-Befehle hinausgehen. Die ServiceBase-Klasse hat wie erwhnt eine OnCustomCommand-Methode, die Sie berschreiben knnen. Die ServiceController-

559

Windows-Dienste

Klasse verfgt zu diesem Zweck ber eine korrespondierende ExecuteCommand-Methode. Leider ist ihr Aktionsradius etwas eingeschrnkt: Sie kann nur einen Integer als Parameter bernehmen. In Ihrem Dienst holen Sie diesen Integer und benutzen ein Select CaseStatement, um die gewnschte Funktion auszufhren. In Ihrem benutzerdefinierten SCM knnten Sie etwa Folgendes ausfhren:
objController.ExecuteCommand(128) objController.ExecuteCommand(145)

Im entsprechenden Dienst erzeugen Sie eine Methode, die der folgenden hnelt:
Protected Overrides Sub OnCustomCommand(intCommand as integer) Select Case intCommand case 128 ' einen Befehl ausfhren case 129 ' einen weiteren Befehl ausfhren ... ... case 145 ' den 18. Befehl ausfhren End Select End Sub

Mit dieser Methode knnen Sie fast alles, was Sie wollen, tun jedenfalls innerhalb der Fhigkeiten eines Windows-Dienstes. Die Werte fr benutzerdefinierte Befehlsparameter mssen zwischen den Ganzzahlen 128 und 256 liegen. Integer auerhalb dieses Wertebereichs sind fr das Betriebssystem reserviert und Versuche zu deren Verwendung fhren zum Absturz des jeweiligen Dienstes.

16.5 In Protokolldateien schreiben


Das letzte Thema, das wir heute anpacken, ist der Einsatz der EventLog-Klasse, um in benutzerdefinierte Ereignisprotokolle zu schreiben und nicht in die bereits vorgestellten Protokolle Anwendung, System und Sicherheit. In ein benutzerdefiniertes Ereignisprotokoll zu schreiben, ist leicht, denn man braucht nur ein paar Codezeilen im Dienst zu ndern. Zunchst ist ein neues EventLog-Objekt zu erzeugen:
private objLog as New System.Diagnostics.EventLog()

Wie bei der ServiceController-Klasse mssen Sie auch hier einen MachineName fr diese Klasse angeben (obwohl der Standardwert den lokalen Rechner bezeichnet). Sie mssen ebenfalls eine Quelle angeben, also normalerweise die Anwendung oder den Windows-

560

In Protokolldateien schreiben

Dienst, die oder der in das Protokoll schreiben soll. Dieser Quellenname taucht im Ereignisprotokoll als der Erzeuger der Ereignisse auf. Die Quelle ist auch noch in anderer Hinsicht von Bedeutung. Alle Ereignisprotokolle mssen ber eine zugeordnete Quelle verfgen; ohne diese liee sich nichts in sie eintragen. Jede Quelle hat daher ein einziges, damit verknpftes Ereignisprotokoll. Umgekehrt kann aber ein Ereignisprotokoll mehrere mit ihm verknpfte Quellen haben. Standardmig ist Ihr Windows-Dienst mit dem integrierten Anwendungsprotokoll verknpft. Da aber in eine neue Protokolldatei geschrieben werden soll, mssen Sie die AutoLog-Eigenschaft des Dienstes auf false setzen, um ihn vom Anwendungsprotokoll zu entkoppeln:
Me.AutoLog = false

Wenn Sie diese Eigenschaft auf true gesetzt lassen, bleibt Ihr neues Ereignisprotokoll leer, weil der Dienst weiterhin in das Anwendungsprotokoll schreibt. Bevor Sie die Quelle festlegen, mssen Sie ihr Vorhandensein prfen; ist dies der Fall, erhalten Sie eine Fehlermeldung. Die EventLog-Klasse hat eine SourceExists-Eigenschaft, um dies herauszufinden, sowie eine CreateEventSource-Methode, um eine neue Quelle zu erstellen, wenn noch keine existiert:
If Not EventLog.SourceExists("MySource") Then EventLog.CreateEventSource("MySource", "MyNewLog") End If

Der erste Parameter der CreateEventSource-Methode ist eine beliebige Quelle und der zweite das Ereignisprotokoll, in das zu schreiben ist. Dabei kann es sich um "Application", "Security" oder "System" handeln oder wie im Beispiel um eine benutzerdefinierte Protokolldatei, die einen beliebigen Namen tragen darf. Alternativ knnen Sie mit Hilfe der LogEigenschaft eine benutzerdefinierte Protokolldatei angeben:
objLog.Log = "MyNewLog"

Dann legen Sie einfach die Quelle Ihres Ereignisprotokolls fest:


objLog.Source = "MySource"

oder:
objLog.Source = "TYWinforms Day 16 Service"

Jedes Vorkommen der Methode EventLog.WriteEntry muss in Ihrem Code so gendert werden, dass nun das neue EventLog-Objekt benutzt wird: objLog.WriteEntry. Alles andere funktioniert weiterhin genauso. Sobald Ihr Windows-Dienst luft, erzeugt er eine neue Protokolldatei namens MyNewLog.evt im Verzeichnis c:\winnt\system32\config. In der Ereignisanzeige knnen Sie diese neue Datei hinzufgen (whlen Sie PROTOKOLLDATEI FFNEN aus dem VORGANG-Men), um deren Inhalt zu betrachten. Abbildung 16.8 zeigt die Ereignisanzeige mit der neuen Protokolldatei.

561

Windows-Dienste

Abbildung 16.8: Sie knnen so viele benutzerdefinierte Protokolldateien erzeugen, wie Sie wollen, aber bertreiben Sie es nicht!

16.6 Zusammenfassung
Sie haben heute etwas ber Windows-Dienste gelernt, einen weiteren Aspekt des .NET Frameworks. Dabei konnten Sie Ihre Kenntnisse aus dem Umgang mit Windows Forms einsetzen, um neue Probleme anzupacken. Windows-Dienste sind ein fester Bestandteil des Windows-Betriebssystems Sie knnen sie nun steuern, ihre Aktionen protokollieren und eigene Dienste erstellen. Ein Windows-Dienst ist eine Anwendung, die im Hintergrund auf Ihrem Computer luft. Sie verfgt ber keine Benutzeroberflche und kann deshalb nicht direkt mit dem Benutzer interagieren. Ihr Programmiermodell unterscheidet sich daher von normalen Windows Forms. Dienste erben von der Klasse System.ServiceProcess.ServiceBase, die ber alle notwendigen Ereignisse und Methoden verfgt. Um Ihrem Dienst Funktionen bereitzustellen, berschreiben Sie die Methoden OnStart, OnStop, OnPause und OnContinue. Die OnCustomCommand-Methode lsst sich ebenfalls berschreiben, um benutzerdefinierte Funktionen bereitzustellen. Um einen Dienst zu installieren, muss man mit den Klassen ServiceInstaller und ServiceProcessInstaller arbeiten. Diese Klassen schreiben Informationen ber Ihren Dienst in die Windows-Registrierung und gestatten dem SCM, den Dienst zu steuern.

562

Fragen und Antworten

Sie knnen die ServiceController-Klasse verwenden, um Ihren Dienst und jeden anderen Dienst auf dem Rechner zu steuern. Mit dieser Klasse lassen sich Dienste starten, beenden, anhalten und fortsetzen; man kann damit auch benutzerdefinierte Aktionen ausfhren und den Status eines Dienstes bestimmen. Verwenden Sie diese Klasse, um Ihren benutzerdefinierten SCM zu erstellen. Um ein benutzerdefiniertes Ereignisprotokoll zu schreiben, mssen Sie ein neues EventLogObjekt erzeugen und seine Quelle entsprechend einrichten. Die Methode CreateEventSource etabliert eine Anwendung oder einen Windows-Dienst als nutzbare Quelle fr Eintragungen in ein Protokoll. Diese Methode knnen Sie auch verwenden, um eine neue Ereignisprotokolldatei fr Ihre Quelle anzulegen. Mit der Ereignisanzeige lassen sich Ereignisse beobachten, die whrend der Ausfhrung in die Protokolldatei eingetragen werden.

16.7 Fragen und Antworten


F Kann ich mit den File- und Stream-Objekten benutzerdefinierte Ereignisprotokolle schreiben? A Selbstverstndlich, aber in vielen Fllen werden die heute beschriebenen benutzerdefinierten Ereignisprotokolle leichter einzusetzen sein und ausreichen. Sollten Ihnen diese Ereignisprotokolle nicht gengend Funktionen bereitstellen, so knnen Sie auch andere Methoden heranziehen, um Dateien zu erzeugen.

Wie kann ich einen Dienst debuggen? A Um einen Programmdebugger einsetzen zu knnen, mssen Sie ihn whrend der Initialisierungsphase mit Ihrem Dienst verbinden. Doch Dienste starten mitunter zu schnell, als dass Sie dies durchfhren knnten. Der Kniff besteht darin, in den Dienstcode eine Verzgerungsphase einzubauen, die den Dienst zu einem kurzen Moment des Innehaltens veranlasst, in dem Sie alles Ntige ausfhren knnen. Wenn Sie die folgende Codezeile irgendwo in den Code Ihres Dienstes einfgen, so wird Ihr Dienst 25 Sekunden warten, bevor er mit der Codeausfhrung fortfhrt:
System.Threading.Thread.Sleep(25000)

Sie sollten den Dienst jedoch nicht lnger als 30 Sekunden warten lassen, denn sonst nimmt der SCM an, dass der Dienst defekt ist, und bricht ihn ab. (Tag 21 bietet ausfhrliche Informationen zum Debugging.)

563

Windows-Dienste

16.8 Workshop
Dieser Workshop soll Ihnen helfen, sich besser an die heute behandelten Begriffe zu erinnern. Es hilft Ihnen sehr, die Antworten vollstndig zu verstehen, bevor Sie weiterlesen. Die Antworten zu den Quizfragen und den bungen finden Sie in Anhang A.

Quiz
1. Zu welchem Namensraum und zu welcher Assembly gehrt die ServiceBase-Klasse? 2. Zu welchen Namensrumen und Assemblies gehren die Klassen ServiceProcessInstaller, ServiceInstaller und Installer? 3. Welches Attribut muss ein Installer besitzen? 4. Wie heien die vier Sicherheitskonten, unter denen ein Dienst laufen kann? 5. Wo werden Ereignisprotokolldateien gespeichert? 6. Welches Werkzeug benutzt man, um einen Dienst zu installieren? Und welches, um einen zu deinstallieren? 7. Welcher Befehl wird verwendet, um einen Dienst von der Befehlszeile aus zu starten? 8. Ist der folgende Code gltig, wenn die Variable objController fr einen gltigen ServiceController initialisiert wird?
dim strHello as String = "RunHelloMethod" objController.ExecuteCommand(strHello)

9. Welche Zeichenfolge wird verwendet, um den lokalen Computer zu spezifizieren?

bung
Erstellen Sie einen Dienst fr das Sichern von Dateien und Verzeichnissen. Dieser Dienst soll tglich zu einem bestimmten Zeitpunkt den Inhalt des Ordners EIGENE DATEIEN in ein anderes, separates Verzeichnis sichern (so etwa um 20:00 Uhr). In diesem Beispiel kommt es nicht auf das Backup-Verzeichnis an, und Sie brauchen sich auch nicht um Dateikomprimierung zu kmmern; kopieren Sie die Dateien einfach. Schreiben Sie entsprechende Informationen in das Anwendungsereignisprotokoll, sobald die Sicherung ausgefhrt wurde.

564

Fortgeschrittene Techniken fr Steuerelemente in Windows Forms

7 1

Fortgeschrittene Techniken fr Steuerelemente in Windows Forms

Heute bewegen wir uns ber die Grundlagen der Windows Forms-Steuerelemente hinaus und untersuchen, wie man sie noch weiter anpassen kann. Dazu mssen Sie wissen, wer ein Steuerelement besitzt. Der Besitzer (per Vorgabe ist dieser der Entwickler) ist zustndig dafr, dass das Steuerelement gezeichnet wird und seine visuellen Anzeigeformen erzeugt werden. Wir sehen uns an, wie man von Steuerelementen Besitz ergreifen kann und eigene Anzeigeformen erstellt. Die Steuerelemente ListBox, ComboBox, TabControl und MenuItem sind alle dazu in der Lage, den Besitzer und das jeweilige Aussehen zu wechseln. Der Mechanismus dafr ist fr alle der gleiche, doch im Einzelnen unterscheiden sich die Techniken dafr ein wenig voneinander. Heute untersuchen wir diese vier Steuerelemente erneut daraufhin, wie weit man sie noch anpassen knnte. In dieser Lektion lernen Sie,

was es bedeutet, ein Steuerelement zu zeichnen, wie man GDI+ auf Steuerelemente anwendet, wie man Grafiken in die Steuerelemente ListBox und ComboBox hinzufgt, wie sich Werte bitweise miteinander vergleichen lassen, wie man Mens markiert erscheinen lassen kann.

17.1 Noch einmal: Mens und die Steuerelemente Listbox, ComboBox und TabControl
Wir haben uns an den Tagen 4 und 6 ausfhrlich mit diesen Steuerelementen beschftigt, doch wir wollen sie noch einmal Revue passieren lassen, um unser Gedchtnis aufzufrischen, bevor wir uns an fortgeschrittene Themen wagen. Mens werden mit Hilfe der Objekte MainMenu und MenuItem erstellt; MainMenu stellt die Menleiste einer Anwendung dar, und jedes Men und seine Untermens sind MenuItemObjekte. MenuItem steuert seine Funktion in einer unspektakulren Weise: Ein einzelnes MenuItem reprsentiert eine einzelne Menauswahl in der Anwendung. Diese Menoptionen werden stets als reiner Text angezeigt und knnen Abkrzungstasten (Tastenkombinationen, Shortcuts) oder Zusatztasten enthalten. Funktionell ist das Ganze zwar in Ordnung, optisch aber wenig attraktiv. Wenn Sie Mens in Ihrer Anwendung haben, so drften sie stets alle gleich aussehen, gleichgltig, zu welchem Zweck sie eingesetzt werden. Abbildung 17.1 zeigt ein typisches Men.

566

Noch einmal: Mens und die Steuerelemente Listbox, ComboBox und TabControl

Abbildung 17.1: Mens sind standardmig recht fade Steuerelemente.

Die Steuerelemente ListBox (Listenfeld) und ComboBox (Kombinationsfeld) weisen gewisse hnlichkeiten auf. Sie umfassen jeweils Elemente, die man auswhlen kann. Ein ausgewhltes Element wird in der standardmigen Windows-Farbe hervorgehoben, aber das war's auch schon. Die Elemente mssen wieder als reiner Text dargestellt werden. Wenn es nicht mehr Mglichkeiten gbe, wren diese Steuerelemente recht fade und eingeschrnkt. Zum Glck ist die Palette wesentlich umfangreicher. Dank GDI+ lsst sich das Standardverhalten dieser Elemente umgehen, so dass man sie an die eigenen Vorstellungen anpassen kann. Diese Steuerelemente knnen nmlich Grafiken anzeigen, ihre Farbe ndern usw. Fr das TabControl-Steuerelement stellt sich die Situation hnlich dar. Seine Standardverhaltensweise besteht darin, einfach eine hervorgehobene Registerkarte nach deren Auswahl anzuzeigen, wie in Abbildung 17.2 zu sehen ist. Wir knnen aber kreativer sein und TabControl-Steuerelemente ansprechender gestalten, wenn wir die Standardverhaltensweise berschreiben und interessantere Effekte einbauen.

Abbildung 17.2: Standardmige Registerkarten-Steuerelemente knnen recht langweilig aussehen.

567

Fortgeschrittene Techniken fr Steuerelemente in Windows Forms

Im Grunde knnen Sie hinsichtlich Design und Funktionsumfang der vier Steuerelemente recht flexibel sein. Die in den nchsten Abschnitten vorgestellten Verfahren zeigen, wie sich verschiedene Eigenschaften und Ereignisse mit etwas Liebe zum Detail so optimieren lassen, dass Sie erstaunlich gut aussehende Steuerelemente erhalten. Man wird sich fragen, wie Sie das gemacht haben. (Diese Methoden lassen sich zur Erzeugung dynamischer Mens wie in Microsoft Office 2000 verwenden.)

17.2 DrawMode festlegen


Der Schlssel zu diesen Erweiterungen besteht bei den Steuerelementen ListBox, ComboBox und TabControl in der Eigenschaft DrawMode (Mens verwenden ein etwas anderes Paradigma; mehr dazu im Abschnitt Mens: eine Frage des Besitzes). DrawMode definiert, wer fr das Zeichnen eines bestimmtes Steuerelements zustndig ist. Standardmig ist dies das Betriebssystem, also Windows. Nur durch Windows sehen Ihre Steuerelemente so aus, wie sie erscheinen, wenn sie ausgewhlt werden oder wenn die Maus ber ihnen schwebt usw. Um die Steuerelemente ListBox, ComboBox und TabControl anpassen zu knnen, muss man also Windows diese Kontrolle entreien.
DrawMode nimmt Werte aus der DrawMode-Aufzhlung an, die in Tabelle 17.1 aufgefhrt

sind.
Wert
Normal

Beschreibung Dies ist der Standardwert. Er zeigt an, dass das Betriebssystem fr das Zeichnen der Steuerelemente zustndig ist. Gibt an, dass Sie das Zeichnen steuern, doch dass alle Komponenten in einem Steuerelement (etwa die Eintrge in der ListBox) alle die gleiche Hhe haben. Im Grunde wie OwnerDrawFixed, doch jede Komponente im Steuerelement kann variable Hhe haben. Dies ist die flexibelste Methode, erfordert aber Ihrerseits die meiste Mhe.

OwnerDrawFixed

OwnerDrawVariable

Tabelle 17.1: Werte der Aufzhlung DrawMode

Angenommen, Sie haben ein ListBox-Steuerelement, dann knnen Sie die Eigenschaft DrawMode auf OwnerDrawFixed setzen und die Anzeigeweise anpassen. Steuerelemente, deren DrawMode-Eigenschaften auf einen anderen Wert als Normal gesetzt sind, werden als besitzergezeichnete Steuerelemente bezeichnet. Anders ausgedrckt, sind Sie, als Schpfer dieser Steuerelemente (zumindest in Ihrer Anwendung), auch der Besitzer der Steuerele-

568

DrawMode festlegen

mente und somit zustndig dafr, dass und wie sie gezeichnet werden. Prfen Sie dies in ein wenig Code nach:
dim lbxItems As New ListBox() ... 'hier Elemente hinzufgen ... lbxItems.DrawMode = DrawMode.OwnerDrawFixed

Wenn Sie eine Anwendung mit diesem Code kompilieren, erfahren Sie schnell, dass etwas fehlt: Die Elemente im Steuerelement werden nicht angezeigt, die ListBox ist nicht sichtbar. Windows hat nmlich auch die Darstellung des Steuerelements vllig Ihnen berlassen; nichts davon wird fr Sie gezeichnet. Fr das Betriebssystem ist es einfach ein Allesoder-nichts-Vorschlag. Da Sie bereits GDI+ kennen gelernt haben, ist das Zeichnen von Steuerelementen nicht schwer. Sie mssen dem DrawItem-Ereignis des Steuerelements einen Delegaten zuweisen und dann die notwendigen GDI+-Aufrufe nutzen, um das Steuerelement anzuzeigen. In C# sieht dies folgendermaen aus:
lbxItems.DrawItem += new DrawItemEventHandler(this.DrawMyBox);

Der Delegat DrawItemEventHandler bedeutet, dass Sie einen Ereignisparameter des Typs DrawItemEventArgs fr Ihren Ereignishandler benutzen:
private void DrawMyBox(Object Sender, DrawItemEventArgs e) DrawItemEventArgs ist ein sehr ntzliches Objekt: Es enthlt eine Reihe hilfreicher Mitglieder, die Ihnen beim Zeichnen des Steuerelements helfen, darunter das Graphics-Objekt (welches ja das Hauptobjekt von GDI+ ist). Tabelle 17.2 fasst die Eigenschaften von DrawItemEventArgs zusammen und Tabelle 17.3 fhrt seine Methoden auf. Eigenschaft
BackColor Bounds

Beschreibung Liefert die Hintergrundfarbe des darzustellenden Steuerelements. Liefert ein Rectangle-Objekt, das Gre und Form des darzustellenden Steuerelements angibt. Liefert die Schriftart, die im darzustellenden Steuerelement benutzt wird. Die Vordergrund- (also die Schrift-) Farbe des darzustellenden Steuerelements. Ein Graphics-Objekt, das sich zur Darstellung des Steuerelements verwenden lsst.

Font ForeColor Graphics

Tabelle 17.2: Eigenschaften von DrawItemEventArgs

569

Fortgeschrittene Techniken fr Steuerelemente in Windows Forms

Eigenschaft
Index State

Beschreibung Der Index des darzustellenden Steuerelements. Der aktuelle Zustand des darzustellenden Steuerelements.

Tabelle 17.2: Eigenschaften von DrawItemEventArgs (Forts.)

Methode
DrawBackground DrawFocusRectangle

Beschreibung Zeichnet den Hintergrund des Steuerelements, wenn es ausgewhlt ist. Zeichnet ein Fokusrechteck, das die Koordinaten der Flchengrenzen des Steuerelements angibt. Dieses Rechteck ist nicht sichtbar.

Tabelle 17.3: Methoden von DrawItemEventArgs

In den nchsten Abschnitten werden die in den Tabellen 17.1 bis 17.3 aufgefhrten Mitglieder eingesetzt.

Die Steuerelemente ListBox und ComboBox erweitern


Zu den hufigsten Anpassungswnschen eines Entwicklers gehrt die nderung der Elementfarben in ListBox-Steuerelementen und ComboBox-Steuerelementen. Mit den Ihnen bereits bekannten Methoden knnen Sie lediglich das gesamte Listenfeld ndern, jedoch nicht eine einzelne Zeile (vgl. Listing 6.5 als Beispiel). In besitzergezeichneten Steuerelementen knnen Sie jedoch jedes Element bearbeiten. Fr die Anpassung sind zwei Schritte erforderlich: 1. Setzen Sie die DrawMode-Eigenschaft auf einen anderen Wert als Normal. 2. Weisen Sie dem DrawItem-Ereignis des Steuerelements einen Ereignishandler zu und verwenden Sie ihn, um festzulegen, wie das Steuerelement gezeichnet werden soll. Whrend der erste Schritt leicht ist, erfordert der zweite ein wenig Nachdenken. Ein Steuerelement, das nicht besitzergezeichnet ist, kann verschiedene Erscheinungsformen haben (ausgewhlt oder nicht usw.). Dieses Aussehen oder dieser Zustand des Steuerelements wird von der Eigenschaft DrawItemEventArgs.State bestimmt. Sie nimmt einen der Werte aus der DrawItemState-Aufzhlung an, die Tabelle 17.4 auflistet.

570

DrawMode festlegen

Wert
Checked

Beschreibung Gibt an, dass das Element aktiviert ist. (Wird nur auf MenuItem-Steuerelemente angewendet.) Das zu zeichnende Element ist der Textfeldbereich eines ComboBox-Steuerelements. Der visuelle Standardzustand. Das Element ist deaktiviert. Das Element besitzt den aktuellen Fokus. Das Element ist grau dargestellt. (Trifft nur auf MenuItem-Steuerelemente zu.) Das Element wird vorausgewhlt, d.h. hervorgehoben dargestellt, wenn sich der Mauszeiger ber dem Element befindet. Das Element ist inaktiv. Das Element wird ohne Zugriffstaste angezeigt. (Trifft nur fr MenuItem-Steuerelemente zu.) Das Element wird ohne visuellen Hinweis darauf angezeigt, dass es den Fokus besitzt. Das ist meist der Fall, wenn die Methode DrawFocusRectangle nicht aufgerufen wurde. Das Element hat keinen visuellen Zustand. Das Element ist ausgewhlt.

ComboBoxEdit

Default Disabled Focus Grayed HotLight

Inactive NoAccelerator

NoFocusRect

None Selected

Tabelle 17.4: Werte der Aufzhlung DrawItemState

Die Werte aus DrawItemState lassen sich bitweise kombinieren, so dass ein Steuerelement mehr als einen Zustand haben kann. Es ist in Ihrem Code ein wenig Manipulation notwendig, um zu bestimmen, ob sich ein Steuerelement in einem bestimmten Zustand befindet. Listing 17.1 demonstriert, wie man diese Zustnde manipuliert. Listing 17.1: Die ListBox anpassen
1: 2: 3: 4: 5: 6: using System; using System.Windows.Forms; using System.Drawing; namespace TYWinForms.Day17 { public class Listing171 : Form {

571

Fortgeschrittene Techniken fr Steuerelemente in Windows Forms

7: private ListBox lbxItems = new ListBox(); 8: 9: public Listing171() { 10: lbxItems.Location = new Point(75,75); 11: 12: lbxItems.Items.Add("Item 1"); 13: lbxItems.Items.Add("Item 2"); 14: lbxItems.Items.Add("Item 3"); 15: lbxItems.Items.Add("Item 4"); 16: lbxItems.Items.Add("Item 5"); 17: 18: lbxItems.DrawMode = DrawMode.OwnerDrawFixed; 19: lbxItems.DrawItem += new DrawItemEventHandler(this.DrawMyBox); 20: 21: this.Text = "Listing 17.1"; 22: this.Controls.Add(lbxItems); 23: } 24: 25: public void DrawMyBox(Object Sender, DrawItemEventArgs e) { 26: e.DrawBackground(); 27: 28: if ((e.State & DrawItemState.Selected) == DrawItemState.Selected) { 29: e.Graphics.FillRectangle(Brushes.Orange, e.Bounds); 30: e.Graphics.DrawString("Item " + e.Index + "ausgewhlt!", new  Font("Arial",10), Brushes.Blue, new Point(e.Bounds.X, e.Bounds.Y)); 31: } else { 32: e.Graphics.DrawString("Item " + e.Index, new Font("Arial",10),  Brushes.Black, new Point (e.Bounds.X, e.Bounds.Y)); 33: } 34: } 35: 36: public static void Main() { 37: Application.Run(new Listing171()); 38: } 39: } 40: 41: }

In Zeile 7 deklarieren wir die ListBox, die wir anpassen werden. Der Konstruktor in den Zeilen 9 bis 23 initialisiert das Steuerelement mit einigen Standardwerten. Der wichtigste Abschnitt befindet sich in den Zeilen 18 und 19. DrawMode wird auf OwnerDrawFixed festgelegt und dem DrawItem-Ereignis ein Delegat zugewiesen, der auf die DrawMyBox-Methode in Zeile 25 zeigt. Bevor wir DrawMyBox untersuchen, sollten wir berlegen, wie der Ausfhrungsvorgang standardmig abluft. Sobald Ihre Anwendung angezeigt wird, zeich-

572

DrawMode festlegen

net Windows normalerweise das Formular und alle seine Objekte. Wenn Sie in den Zeilen 12 bis 16 der ListBox Elemente hinzufgen, wird die ListBox ungltig gemacht und muss neu gezeichnet werden (die OnPaint-Methode wird fr die ListBox aufgerufen). Windows untersucht jedes der Elemente in der ListBox, bestimmt seinen Zustand (ausgewhlt oder nicht usw.) und zeichnet das Element dementsprechend (ein ausgewhltes sieht anders aus als ein nicht ausgewhltes). Ist jedoch DrawMode etwas anderes als Normal, wird OnPaint zwar immer noch aufgerufen, doch in der ListBox wird natrlich kein Element gezeichnet. Daher wird der fr das DrawItem-Ereignis zugewiesene Ereignishandler benutzt, um jedes einzelne Element zu zeichnen. Auf diese Weise knnen Sie jedes Element einzeln kolorieren statt nur die ganze ListBox. Beachten Sie das DrawItemEventArgs-Objekt in Zeile 25; Sie werden es ausgiebig verwenden. In Zeile 26 rufen Sie zunchst die Methode DrawItemEventArgs.DrawBackground auf, um den Hintergrund des Steuerelements zu zeichnen (der Standardwert ist Wei). Achtung: Im Allgemeinen rufen Sie diese Methode frhzeitig in der Zeichen-Methode auf und immer bevor irgendwelche anderen Zeichenbefehle erteilt werden! Der Hintergrund wird stets ber alles, was bereits vorhanden ist, gezeichnet. Im nchsten Schritt mssen Sie den Zustand desjenigen Elements bestimmen, das Sie zeichnen wollen. Diese Bestimmung erfolgt mit Hilfe der Eigenschaft DrawItemEventArgs.State und einer Kombination von DrawItemState-Aufzhlungswerten. Zeile 28 bestimmt, ob das aktuelle Element ausgewhlt ist, indem bitweise ein DrawItemState-Wert und die State-Eigenschaft kombiniert werden. Dann wird dies mit dem Selected-Zustand verglichen. Sie mssen diesen merkwrdig aussehenden Code benutzen, weil die DrawItemState-Werte auf besondere Weise kombiniert werden. Der folgende Code etwa wrde nicht funktionieren, auer wenn sich das Steuerelement momentan einzig und allein im Zustand Selected befindet (was selten der Fall ist):
if (e.State == DrawItemState.Selected) {

Der Code in Zeile 28 erlaubt Ihnen herauszufinden, ob das Steuerelement einen bestimmten Zustand besitzt; so mssen Sie nicht nacheinander mehrere Zustnde abprfen. In VB .NET wrde das gleiche Statement folgendermaen aussehen:
if (e.State And DrawItemState.Selected) = DrawItemState.Selected

Auf hnliche Weise knnen Sie bestimmen, ob das aktuelle Steuerelement den Fokus hat:
if ((e.State & DrawItemState.Focus) == DrawItemState.Focus) {

573

Fortgeschrittene Techniken fr Steuerelemente in Windows Forms

Zeile 29 fllt das Rechteck, das mit dem aktuellen Element belegt ist, mit einem orangefarbenen Pinsel (Brush) aus. Die Bounds-Eigenschaft liefert, wie erwhnt, ein Rectangle, das den Oberflchenbereich des fraglichen Elements bedeckt. Zeile 30 benutzt die DrawString-Methode, um den Text zu zeichnen, der im Steuerelement angezeigt wird. In diesem Fall handelt es sich um den Index des Elements einschlielich des Wortes ausgewhlt!. Zeile 32 behandelt alle anderen Zustnde des Steuerelements. In diesen Fllen wird einfach der Elementtext angezeigt. Abbildung 17.3 zeigt das Ergebnis von Listing 17.1.

Abbildung 17.3: Ihre benutzerdefinierte ListBox sieht wirklich einzigartig aus.

Es gibt noch vieles, was Sie zustzlich tun knnen. Mit besitzergezeichneten Steuerelementen knnen Sie in Ihre Listenfelder und Kombinationsfelder Grafiken einfgen und die Hhe jedes Elements variieren. Jedes Mal wenn man in der ListBox ein Element erzeugt, wird ein MeasureItem-Ereignis ausgelst. Ist DrawMode nicht auf Normal gesetzt, knnen Sie dieses Ereignis dazu benutzen, die Elemente im Listenfeld in der Gre zu anzupassen. Ist DrawMode auf OwnerDrawVariable gesetzt, lsst sich die Hhe von einem Element zum nchsten variieren; mit den Modi Normal oder OwnerDrawFixed wre dies nicht mglich.
MeasureItem wird bei der Erstellung eines Listeneintrags ausgelst, doch DrawItem wird im Gegensatz dazu beim Zeichnen eines Elements ausgelst. Das heit, dass MeasureItem nur einmal im Lebenszyklus einer Anwendung auftritt, doch DrawItem sehr viele Male.

574

DrawMode festlegen

Manche Entwickler verstehen nicht, warum sich ihre Elemente nicht dynamisch skalieren lassen, wenn sie gezeichnet werden (z.B. wenn eines ausgewhlt wird). Der Grund liegt im obigen Statement: MeasureItem wird nur einmal ausgelst. Listing 17.2 verwendet ein ComboBox-Steuerelement, um variable Elementhhen zu demonstrieren. Listing 17.2: Eine ComboBox mit variabler Elementhhe
1: using System; 2: using System.Windows.Forms; 3: using System.Drawing; 4: 5: namespace TYWinForms.Day17 { 6: public class Listing172 : Form { 7: private ComboBox cboItems = new ComboBox(); 8: private Image[] arrImages; 9: 10: public Listing172() { 11: arrImages = new Image[]  {Image.FromFile("c:\\winforms\\day17\\icon1.bmp"),  Image.FromFile("c:\\winforms\\day17\\icon2.bmp"),  Image.FromFile("c:\\winforms\\day17\\icon3.bmp"),  Image.FromFile("c:\\winforms\\day17\\icon4.bmp"),  Image.FromFile("c:\\winforms\\day17\\icon5.bmp")}; 12: 13: cboItems.Location = new Point(50,25); 14: cboItems.Size = new Size(200,200); 15: cboItems.DropDownStyle = ComboBoxStyle.Simple; 16: 17: cboItems.Items.Add("Item 1"); 18: cboItems.Items.Add("Item 2"); 19: cboItems.Items.Add("Item 3"); 20: cboItems.Items.Add("Item 4"); 21: cboItems.Items.Add("Item 5"); 22: 23: cboItems.DrawMode = DrawMode.OwnerDrawVariable; 24: cboItems.DrawItem += new DrawItemEventHandler (this.DrawMyBox); 25: cboItems.MeasureItem += new  MeasureItemEventHandler(this.MeasureIt); 26: 27: this.Text = "Listing 17.2"; 28: this.Controls.Add(cboItems); 29: } 30:

575

Fortgeschrittene Techniken fr Steuerelemente in Windows Forms

31: public void MeasureIt(Object Sender, MeasureItemEventArgs e) { 32: e.ItemHeight = (int)(arrImages[e.Index].Height); 33: } 34: 35: public void DrawMyBox(Object Sender, DrawItemEventArgs e) { 36: e.DrawBackground(); 37: 38: if ((e.State & DrawItemState.Selected) == DrawItemState.Selected) { 39: e.Graphics.FillRectangle(Brushes.Orange, new  Rectangle(e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height)); 40: e.Graphics.DrawString("Element " + e.Index + "ausgewhlt!", new  Font("Arial",14), Brushes.White, new Point(e.Bounds.X, e.Bounds.Y)); 41: } else { 42: e.Graphics.DrawImage(arrImages[e.Index], new Point(e.Bounds.X,  e.Bounds.Y)); 43: e.Graphics.DrawString("Item " + e.Index, new Font("Arial",10),  Brushes.Black, new Point((int) (arrImages[e.Index].Width + 2), e.Bounds.Y)); 44: } 45: } 46: 47: public static void Main() { 48: Application.Run(new Listing172()); 49: } 50: } 51: 52: }

Hier wird zunchst ein Array von Grafiken erzeugt (Zeilen 8 und 11). Wir werden sie gleich nutzen (passen Sie die Dateinamen gegebenenfalls an die tatschlich bei Ihnen vorhandenen Grafiken an). Die Reihenfolge, in der wir diese Bilder dem arrImages-Array hinzufgen, ist die gleiche, in der sie spter im Kombinationsfeld erscheinen sollen (das ist nicht zwingend, wohl aber spter hilfreich).
DrawMode wird in Zeile 23 auf OwnerDrawVariable gesetzt und in den Zeilen 24 und 25 werden den Ereignissen MeasureItem und DrawItem Ereignishandler zugewiesen. Die Methode MeasureIt (Zeilen 31 bis33) tut nur eines: die Hhe des zu zeichnenden Elements festlegen. Das Objekt MeasureItemEventArgs hat vier einfache Mitglieder: Graphics: Liefert ein Graphics-Objekt, das zum Zeichnen eingesetzt wird Index: Der Index des gerade gezeichneten Elements ItemHeight: Die Hhe des zu zeichnenden Elements ItemWidth: Die Breite des zu zeichnenden Elements

576

DrawMode festlegen

In Zeile 32 legen Sie die ItemHeight-Eigenschaft so fest, dass sie die Hhe jedes einzelnen Elements angibt. Die Hhe hngt von der jeweils eingefgten Grafik ab. Sie mssen daher die Hhe derjenigen Grafik aus dem arrImages-Array holen, die die gleiche Indexnummer wie das zu zeichnende Element hat. Wir verwenden Bilder variabler Hhe, so dass jedes Element eine andere Hhe erhalten sollte. Die DrawMyBox-Methode wird aufgerufen, um die einzelnen Elemente zu zeichnen. Diese Methode ist fast mit der in Listing 17.1 identisch. Der Unterschied: Wenn in die Elemente Text zu zeichnen ist, muss man die Hhe der Grafik bercksichtigen, so dass der Text nun durch die Breite des Bildes verdrngt werden muss. Zeile 43 addiert deshalb 2 (Pixel), um fr ein wenig Abstand zwischen Text und Bild zu sorgen. Denken Sie daran, diese Werte in Integer umzuwandeln. Abbildung 17.4 zeigt das Ergebnis dieser Anwendung.

Abbildung 17.4: Nun knnen Ihre Kombinationsfelder Grafiken und Elemente unterschiedlicher Hhe enthalten.

Sobald Sie einmal wissen, wie man die standardmige Zeichenverhaltensweise eines Steuerelements berschreibt, ist es nicht schwierig, die eigene Funktionalitt zu erstellen, solange man Grundkenntnisse in GDI+ besitzt. Im nchsten Abschnitt wollen wir das DrawItemEventArgs-Objekt einsetzen, um TabControl-Steuerelemente etwas interaktiver zu gestalten.

577

Fortgeschrittene Techniken fr Steuerelemente in Windows Forms

Das Steuerelement TabControl erweitern


Die Anpassung des TabControl-Steuerelements verluft in hnlichen Bahnen, doch es gibt ein paar Unterschiede. Einerseits lassen sich keine Registerkarten unterschiedlicher Gre im TabControl zeichnen: Alle Registerkarten mssen gleich gro sein. Der Versuch, DrawMode auf OwnerDrawVariable zu setzen, wrde zu einem Fehler fhren. Zum Zweiten lassen sich TabControl-Steuerelemente im benutzerdefinierten Zeichenmodus nicht berwachen. Anders ausgedrckt, man kann nicht feststellen, wann sich der Mauszeiger ber einer Registerkarte befindet auf diesen Zustand kann man also nicht reagieren. Wegen dieser Einschrnkungen geben sich Entwickler oft mit der Standardimplementierung des TabControl-Steuerelements zufrieden. Bevor man resigniert, sollte man sich jedoch die Art und Weise ansehen, wie die Anpassung beim TabControl-Steuerelement funktioniert. Listing 17.3 zeigt ein Beispiel dafr. Listing 17.3: Ein TabControl anpassen
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: Imports System Imports System.Windows.Forms Imports System.Drawing namespace TYWinForms.Day17 public class Listing173 : Inherits Form private tcMain As New TabControl() private tpGeneral As New TabPage("Allgemein") private tpDisplay As New TabPage("Anzeige") private tpLocation As New TabPage("Position") private tpAdvanced As New TabPage("Extras") public sub New() Me.Text = "Listing 17.3" Me.Width = 600 Me.Controls.Add(tcMain) tcMain.Width = Me.Width tcMain.Height = Me.Height tcMain.TabPages.Add(tpGeneral) tcMain.TabPages.Add(tpDisplay) tcMain.TabPages.Add(tpLocation) tcMain.TabPages.Add(tpAdvanced) tcMain.ItemSize = new Size(75,20)

578

DrawMode festlegen

27: tcMain.SizeMode = TabSizeMode.Fixed 28: tcMain.DrawMode = TabDrawMode.OwnerDrawFixed 29: AddHandler tcMain.DrawItem, new DrawItemEventHandler(AddressOf  Me.DrawTab) 30: end sub 31: 32: private sub DrawTab(Sender As Object, e As DrawItemEventArgs) 33: if (e.State And DrawItemState.Selected) = DrawItemState.Selected  then 34: e.Graphics.DrawRectangle(Pens.Purple,  tcMain.GetTabRect(e.Index)) 35: e.Graphics.DrawString(tcMain.TabPages (e.Index).Text, new  Font("Arial",10), Brushes.Red,  RectangleF.op_implicit(tcMain.GetTabRect(e.Index))) 36: else 37: e.Graphics.DrawString(tcMain.TabPages (e.Index).Text, new  Font("Arial",10), Brushes.Black,  RectangleF.op_implicit(tcMain.GetTabRect(e.Index))) 38: end if 39: end sub 40: 41: public shared sub Main() 42: Application.Run(new Listing173()) 43: end sub 44: end class 45: end namespace

Wie an Tag 6 beschrieben, besteht ein TabControl aus einer oder mehreren Tabseiten (TabPages), die jeweils eine solche Registerkarte (Tab) darstellen. Das TabControl-Steuerelement und die Tabseiten werden in den Zeilen 8 bis 12 erstellt und in den Zeilen 21 bis 24 zusammengefgt. Zwar kann man ihre Gre nicht einzeln ndern, doch die Gre aller Registerkarten lsst sich auf einmal ndern. Dies erfolgt in Zeile 26. Standardmig kann eine Registerkarte nicht ihre Breite ndern. Um dies zu ermglichen und die vorgegebene Verhaltensweise zu berschreiben, muss man die Eigenschaft SizeMode auf TabSizeMode.Fixed setzen, wie in Zeile 27. Andere Werte der Aufzhlung TabSizeMode sind Normal (der Standardwert) und FillToRight. Der letztere Wert ist nur fr Steuerelemente mit mehreren Reihen von Registerkarten wirksam; er vergrert die jeweils ausgewhlte Registerkarte, damit sie die ganze Breite des TabControl-Steuerelements ausfllt. Der letzte Schritt im Konstruktor besteht darin, die DrawMode-Eigenschaft auf OwnerDrawFixed festzulegen und dem DrawItem-Ereignis einen Ereignishandler zuzuweisen. Beachten Sie, dass TabControl keineswegs die DrawMode-Aufzhlung bentzt, wie es alle anderen Steuerelemente heute taten. Vielmehr ver-

579

Fortgeschrittene Techniken fr Steuerelemente in Windows Forms

wendet es eine TabDrawMode-Aufzhlung, die zwar die gleichen Werte wie DrawMode enthlt, der aber der OwnerDrawVariable-Wert fehlt. Sobald die aktuelle Registerkarte (Zeile 33) ausgewhlt ist, zeichnet man mit der DrawTab-Methode (Zeile 32) ein farbiges Rechteck um das Register (Zeile 34) und zeigt den Text in Rot an (Zeile 35). Einige interessante Dinge passieren in den Zeilen 34 und 35. Zunchst ist festzuhalten, dass man die GetTabRect-Methode des TabControlSteuerelements benutzt. Diese Methode liefert den sichtbaren Teil eines bestimmten Registers: das aktuelle Register, wie es in diesem Fall von DrawItemEventArgs.Index angegeben wird. Normalerweise wrde man die Eigenschaft DrawItemEventArgs.Bounds einsetzen, um das richtige Rechteck zu erhalten. Doch die Bounds-Eigenschaft liefert die gesamte Oberflche eines Registers, einschlielich nicht sichtbarer Teile. Weil wir aber nicht auf unsichtbare Flchen zeichnen wollen, rufen wir stattdessen GetTabRect auf, welche das richtige Rechteck liefert. Wenn eine ausgewhlte Registerkarte so aussehen soll, als ob sie sich aus den anderen hervorschbe (die untere Registerlinie verschwindet dabei), zeichnen Sie ein Rechteck mit der Farbe LightGray:
e.Graphics.DrawRectangle(Pens.LightGray, tcmain.GetTabRect(e.Index))

Zeile 35 erledigt das Gleiche fr die DrawString-Methode. Hier rufen wir GetTabRect auf, die Grenzen festzulegen, auf die die Registerbeschriftung gezeichnet werden soll. Jeglicher Text auerhalb dieser Umrisse wird nicht gezeigt und so wird der Text nicht mitten im Buchstaben abgeschnitten. Wrde man hier DrawItemEventArgs.Bounds verwenden, liefe der Text ber den rechten Rand des Registers hinaus. Es gibt noch eine weitere interessante Methode: RectangleF.op_implicit. Die DrawStringMethode kann lediglich ein RectangleF-Objekt (das wie das Rectangle-Objekt funktioniert, jedoch auch Bruchteile von Gren enthalten kann) bernehmen. Weil GetTabRect ein normales Rectangle-Objekt liefert, mssen Sie die statische op_implicit-Methode des RectangleF-Objekts benutzen, um das eine in das andere umzuwandeln. Zeile 37 zeigt die standardmige Registerkartenansicht mit schwarzem Text in der Schriftart Arial ohne Umrisslinien an. Abbildung 17.5 zeigt das Ergebnis. Leider gibt es auer der nderung der Hintergrundfarbe, des Schriftbildes und der Textfarbe wenig, das man noch mit dem TabControl anstellen kann. Sie knnen jedoch bestimmt Ihre Steuerelemente mit dem Funktionsumfang anpassen, der zur Verfgung steht.

580

Mens: Eine Frage des Besitzes

Abbildung 17.5: In einem benutzerdefinierten TabControl-Steuerelement knnen Sie die Schrift- und Hintergrundfarbe ndern.

17.3 Mens: Eine Frage des Besitzes


Die grundlegende Vorgehensweise beim Zeichnen von benutzerdefinierten Mens ist im Wesentlichen die gleiche wie bei anderen Steuerelementen, doch es gibt einige Unterschiede bei den Eigenschaften. Da jedes Element in einem Men eigentlich ein separates MenuItem-Steuerelement ist, brauchen Sie eine ganze Schar von DrawItem-Ereignishandlern von MeasureItem-Ereignishandlern ganz zu schweigen , wenn Sie wnschen, dass sich jedes Menelement anders verhalten soll. Der Unterschied zwischen Mens und anderen Steuerelementen besteht hauptschlich in der Art und Weise, wie man das benutzerdefinierte Zeichnen einschaltet. Statt wie bei den anderen Steuerelementen die DrawMode-Eigenschaft auf OwnerDrawFixed oder OwnerDrawVariable zu setzen, setzen Sie die OwnerDraw-Eigenschaft des MenuItem-Steuerelements auf true:
private MenuItem miCustom = new MenuItem(); miCustom.OwnerDraw = true;

Die Wirkung ist die gleiche, das heit, dass Sie mit diesem Vorgehen den Zeichenvorgang fr jede Methode anpassen knnen; es lsst zu, dass der Besitzer des Steuerelements (= Sie) festlegt, wie das Steuerelement zu zeichnen ist. Diese Unterscheidung wird getroffen, weil MenuItem-Steuerelemente nicht von der gleichen Stelle wie andere Steuerelemente abgeleitet sind. Zum Beispiel ist auf der linken Seite von Abbildung 17.6 die Vererbungshierarchie eines ListBox-Steuerelements dargestellt. Insbesondere sind ListBox-Steuerelemente (direkt oder indirekt) von der Klasse System.Windows.Forms.Control abgeleitet. Das trifft auf MenuItem-Steuerelemente nicht zu, so dass sie streng genommen keine Windows Forms-Steuerelemente sind. Daher sieht der Mechanismus fr benutzerdefiniertes Zeichnen bei MenuItem-Steuerelementen etwas anders aus.

581

Fortgeschrittene Techniken fr Steuerelemente in Windows Forms

System.Object

System.MarshallByRefObject

System.ComponentModel.Component

System.Windows.Forms.Control

System.Windows.Forms.Menu

System.Windows.Forms.ListControl

System.Windows.Forms.MenuItem

Abbildung 17.6:
MenuItem-Steuerelemente erben nicht

System.Windows.Forms.ListBox

von der gleichen Klasse wie andere Steuerelemente.

Doch genug der grauen Theorie. Befassen wir uns mit einem Beispiel. Listing 17.4 zeigt den ersten Teil des Codes fr eine Anwendung mit benutzerdefinierten Menus (Teil 2 folgt in Listing 17.5). Listing 17.4: Ihre benutzerdefinierten Mens initialisieren
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: using System; using System.Windows.Forms; using System.Drawing; namespace TYWinForms.Day17 { public class Listing174 : Form { private MainMenu mnuCustom = new MainMenu(); private MenuItem miCustom = new MenuItem(); private MenuItem miRed = new MenuItem(); private MenuItem miBlue = new MenuItem(); private MenuItem miOrange = new MenuItem(); private MenuItem miGreen = new MenuItem(); public Listing174() { this.Text = "Listing 17.4"; this.Menu = mnuCustom; miCustom = mnuCustom.MenuItems.Add("Angepasstes Menu"); miRed = miCustom.MenuItems.Add("Rot"); miBlue = miCustom.MenuItems.Add("Blau"); miOrange = miCustom.MenuItems.Add("Orange"); miGreen = miCustom.MenuItems.Add("Grn"); miCustom.OwnerDraw = true;

582

Mens: Eine Frage des Besitzes

26: miCustom.DrawItem += new DrawItemEventHandler(this.DrawTopLevel); 27: miCustom.MeasureItem += new  MeasureItemEventHandler(this.MeasureItem); 28: 29: miRed.OwnerDraw = true; 30: miRed.DrawItem += new DrawItemEventHandler (this.DrawItem); 31: miRed.MeasureItem += new MeasureItemEventHandler  (this.MeasureItem); 32: 33: miBlue.OwnerDraw = true; 34: miBlue.DrawItem += new DrawItemEventHandler (this.DrawItem); 35: miBlue.MeasureItem += new MeasureItemEventHandler  (this.MeasureItem); 36: 37: miOrange.OwnerDraw = true; 38: miOrange.DrawItem += new DrawItemEventHandler (this.DrawItem); 39: miOrange.MeasureItem += new  MeasureItemEventHandler(this.MeasureItem); 40: 41: miGreen.OwnerDraw = true; 42: miGreen.DrawItem += new DrawItemEventHandler (this.DrawItem); 43: miGreen.MeasureItem += new  MeasureItemEventHandler(this.MeasureItem); 44: }

In den Zeilen 8 bis 13 legen Sie fnf MenuItem-Steuerelemente und ein MainMenu an. Die Zeilen 19 bis 23 fgen jedes Untermen seinem passenden bergeordneten Element hinzu in diesem Fall also mnuCustom oder miCustom. Die Zeilen 25 bis 43 bewerkstelligen jeweils das Gleiche fr die verschiedenen MenuItem-Steuerelemente: Die OwnerDraw-Eigenschaft wird auf true gesetzt und den Ereignissen DrawItem und MeasureItem werden Delegaten zugewiesen. Alle Delegaten zeigen auf den MeasureItem-Ereignishandler und alle Menelemente auer miCustom zeigen auf den DrawItem-Ereignishandler. Wir wollen das miCustom-Men ein wenig anders zeichnen, da es sich um ein Men der obersten Hierarchieebene handelt. Listing 17.5 zeigt den Code fr diese ereignisverarbeitenden Methoden. Listing 17.5: Die Methoden, die fr das Messen und Zeichnen von Mens verwendet werden
45: private void MeasureItem(Object Sender, MeasureItemEventArgs e) { 46: SizeF stringSize = e.Graphics.MeasureString  (((MenuItem)Sender).Text, this.Font); 47:

583

Fortgeschrittene Techniken fr Steuerelemente in Windows Forms

48: e.ItemHeight = (int)stringSize.Height + 6; 49: e.ItemWidth = (int)stringSize.Width + 10; 50: } 51: 52: private void DrawTopLevel(Object Sender, DrawItemEventArgs e) { 53: if (((e.State & DrawItemState.HotLight) == DrawItemState.HotLight)  | (e.State & DrawItemState.Selected) == DrawItemState.Selected) { 54: e.Graphics.DrawString(((MenuItem)Sender).Text, new Font("Times  New Roman",12), Brushes.Blue, new Point(e.Bounds.X, e.Bounds.Y)); 55: } else { 56: e.Graphics.DrawString(((MenuItem)Sender).Text, new Font("Times  New Roman",12), Brushes.Black, new Point(e.Bounds.X, e.Bounds.Y)); 57: } 58: } 59: 60: private void DrawItem(Object Sender, DrawItemEventArgs e) { 61: e.Graphics.FillRectangle(Brushes.WhiteSmoke, new  Rectangle(e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height)); 62: 63: if (((e.State & DrawItemState.HotLight) == DrawItemState.HotLight)  | (e.State & DrawItemState.Selected) == DrawItemState.Selected) { 64: e.Graphics.FillRectangle(Brushes.LightGray, new  Rectangle(e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height)); 65: e.Graphics.DrawString(((MenuItem)Sender).Text, new Font("Times  New Roman",12), Brushes.Blue, new Point(e.Bounds.X, e.Bounds.Y)); 66: } else { 67: e.Graphics.DrawString(((MenuItem)Sender).Text, new Font("Times  New Roman",12), Brushes.Black, new Point(e.Bounds.X, e.Bounds.Y)); 68: } 69: } 70: 71: public static void Main() { 72: Application.Run(new Listing174()); 73: } 74: } 75: 76: }

Hier finden wir eine Menge langer Codezeilen, doch im Grunde ist der Funktionsumfang einfach. Fangen wir mit der MeasureItem-Methode in Zeile 45 an. Wie im vorigen Listing bereits zu sehen, ist diese Methode zustndig fr das Messen jedes Menelements, um dafr zu sorgen, dass sie alle in der richtigen Gre gezeichnet werden. Diese Methode ist fr benutzerdefinierte Mens sehr wichtig, denn ohne sie wrden Ihre Mens alle die Standardgre beibehalten und dadurch wahrscheinlich alles abschneiden, das man darin darstellen mchte.

584

Mens: Eine Frage des Besitzes

Als Erstes wird die Graphics.MeasureString-Methode aufgerufen, um die Gre der zu zeichnenden Zeichenfolge zu messen. Wir bestimmen diese Zeichenfolge aus der Text-Eigenschaft des Elements, das diese Methode aufruft. So wrde etwa Zeile 46 die Gre der Zeichenfolge "Angepasstes Men" messen, sobald das miCustom-Objekt gemessen wrde. Hhe und Breite des Mens werden sodann auf die Werte gesetzt, die vom MeasureString geliefert werden, plus ein paar Pixel fr ein wenig Abstand auf jeder Seite des Menelements (Zeilen 48 und 49). Gehen wir weiter zur DrawTopLevel-Methode in Zeile 52. Wir sind an zwei Fllen interessiert: Wenn das Men berflogen (sich also der Mauszeiger ber dem Men befindet) und wenn es ausgewhlt wird (anders ausgedrckt, wenn das Men heruntergeklappt ist und seine Untermens anzeigt). Beide Flle sollten dazu fhren, dass das Menelement hervorgehoben wird. In allen anderen Fllen sollte man das MenuItem ganz normal zeichnen. In Zeile 53 bestimmen Sie die Flle. Wenn man die DrawItemState-Werte prft, muss man sie bitweise kombinieren (vgl. Analyse von Listing 17.1). Zeile 53 prft, ob der Zustand des Mens HotLight oder Selected ist (der Operator | bedeutet in C# oder). Trifft einer der beiden Flle zu, zeichnet Zeile 54 den Text im Men in der Schriftart Times New Roman mit einem blauen Pinsel. Trifft keiner der beiden Flle zu, so zeichnet Zeile 56 den Text mit einem schwarzen Pinsel. Die DrawItem-Methode in Zeile 60 tut das Gleiche wie DrawTopLevel, doch mit zwei zustzlichen Schritten. In Zeile 61 fllen Sie zunchst den Hintergrund des Menelements mit einem WhiteSmoke-Pinsel (um etwas Abwechslung zu haben). In Zeile 63 werden die gleichen zwei Flle wie zuvor ausgewertet. Dann fllt Zeile 64 den Hintergrund des Mens mit einem LightGray-Pinsel und Zeile 65 zeichnet den Text wieder in Blau. Diese zwei letzten Schritte verleihen Ihren Mens etwas mehr Pep. Trifft jedoch keiner der beiden Flle zu, dann zeichnet Zeile 67 die Mens ganz normal. Kombinieren Sie die Listings 17.4 und 17.5 miteinander und kompilieren Sie das Ganze. Abbildung 17.7 zeigt das Ergebnis der Anwendung mit benutzerdefinierten Mens. Beachten Sie, dass sich die Farbe des Hintergrunds ebenso ndert wie die Textfarbe, wenn Sie ein Men hervorheben. Dieses Verhalten beobachtet man auch bei normalen von Windows gezeichneten Mens, doch die Farben, die Sie hier gewhlt haben, verleihen Ihren Mens das gewisse Etwas. Htten Sie nicht den DrawItemState.Selected-Fall des miCustom-Mens ausgewertet, dann wrde seine Schrift nicht blau bleiben, sobald Sie darauf klicken, um seine Untermens zu ffnen.

585

Fortgeschrittene Techniken fr Steuerelemente in Windows Forms

Abbildung 17.7: Mens mit Pfiff

Sie knnen natrlich Ihre Phantasie noch weiter spielen lassen. So knnten Sie Tastenkombination bzw. Zugriffstasten in den Mens anzeigen oder neben dem Text Bildchen anzeigen, um weitere visuelle Informationen zu zeigen.

17.4 Zusammenfassung
Dieser Tag konzentrierte sich auf die Erstellung benutzerdefinierter Steuerelemente und Sie haben gelernt, wie man GDI+ einsetzt, um visuelle Effekte zu erzeugen und Interaktivitt hinzuzufgen. Das Vorgehen beim Erzeugen eigener visueller Effekte besteht darin, zunchst einmal die DrawMode-Eigenschaft auf einen Wert der DrawMode-Aufzhlung zu setzen und dann dem DrawItem-Ereignis einen Delegaten zuzuweisen. Im Ereignishandler fr DrawItem verwenden Sie dann GDI+-Methoden, um Zeichenfolgen, Grafiken, Hintergrundfarben und alles sonst Gewnschte zu zeichnen. Hier ist alles gltig, was Sie an Tag 13 ber GDI+ gelernt haben. Verwenden Sie die State-Eigenschaft des DrawItemEventArgs-Objekts und die DrawItemState-Aufzhlung, um den aktuellen visuellen Zustand zu bestimmen, in dem sich das Steuerelement befindet, so etwa die vorausgewhlten, ausgewhlten und Standardzustnde. Diese Werte mssen bitweise miteinander verknpft werden, um jeden Einzelfall prfen zu knnen. Mens unterscheiden sich davon ein wenig, denn man setzt die OwnerDraw-Eigenschaft statt der DrawMode-Eigenschaft ein, doch die Vorgehensweise bleibt die gleiche: Man weist dem DrawItem-Ereignis einen Delegaten zu.

586

Fragen und Antworten

Da Sie nun die Grundlagen des Zeichnens von Steuerelementen kennen, untersuchen wir morgen, wie man Steuerelemente aus dem Nichts hervorzaubert.

17.5 Fragen und Antworten


F Sind ListBox, MenuItem, ComboBox und TabControl die einzigen Steuerelemente, die ich anpassen kann? A F Abgesehen von benutzerdefinierten Steuerelementen, ja. (Mehr dazu morgen.)

Ich habe einen Ereignishandler fr das DrawItem-Ereignis eingerichtet, doch er funktioniert nicht. Woran kann es liegen? A Damit das DrawItem-Ereignis ausgelst wird, mssen Sie die DrawMode-Eigenschaft entweder auf OwnerDrawFixed oder OwnerDrawVariable gesetzt haben oder bei Mens OwnerDraw auf true. Werden jedoch die Standardwerte fr diese Eigenschaften verwendet, wird das DrawItem-Ereignis nie ausgelst und Ihr Ereignishandler ntzt Ihnen nichts.

Wie kann ich dynamische Mens wie in Microsoft Office 2000 erzeugen, wo selten benutzte Mens verschwinden und nur bei Bedarf wieder erscheinen? A Anhand der heute gelernten Methoden knnen Sie das noch nicht. Wie es geht, erfahren Sie morgen. Mglicherweise haben Sie das bereits versucht, indem Sie die Visible-Eigenschaft eines Mens zunchst auf false gesetzt haben, sie dann aber zu gegebener Zeit wieder auf true setzten, nachdem das Men erschienen war. Sollten Sie dies versucht haben, dann wissen Sie, dass es leider nicht funktioniert; stattdessen verschwinden smtliche Mens und hinterlassen nur eine merkwrdig aussehende Box. Der Grund: Die MeasureItem- und DrawItem-Ereignisse mssen ausgelst werden, bevor ein Men aufklappt. Verwenden Sie das obige, fehlerhafte Verfahren, gert das Men gewissermaen in Verwirrung, weil es nicht wei, was es mit den anderen Elementen im Men tun soll ein Fehler folgt. Sie mssen die Standardverhaltensweise fr das Aufklappen von Mens unterdrcken, damit das funktioniert. Und das lernen Sie morgen.

587

Fortgeschrittene Techniken fr Steuerelemente in Windows Forms

17.6 Workshop
Dieser Workshop soll Ihnen helfen, sich besser an die heute behandelten Begriffe zu erinnern. Es hilft Ihnen sehr, die Antworten vollstndig zu verstehen, bevor Sie weiterlesen. Die Antworten zu den Quizfragen und den bungen finden Sie in Anhang A.

Quiz
1. Nennen Sie vier der Werte aus der DrawItemState-Aufzhlung. 2. Worin besteht der Unterschied zwischen den Aufzhlungen DrawMode und TabDrawMode? 3. Wann wird das MeasureItem-Ereignis ausgelst? 4. Wahr oder falsch? Registerkarten in einem TabControl-Steuerelement knnen von unterschiedlicher Gre sein. 5. Aus welchem Grund wrde man die GetTabRect-Methode aufrufen? 6. In welcher Hinsicht unterscheiden sich MenuItem-Steuerelemente von anderen Steuerelementen? 7. Was mssen Sie tun, um die Gre der Registerkarten in einem TabControl-Steuerelement zu ndern?

bung
Schreiben Sie eine Anwendung, mit der der Benutzer das Erscheinungsbild eines Mens anpassen kann. Setzen Sie dazu angepasste Kombinationsfelder ein, damit er Farben fr die Mens whlen kann.

588

Benutzerdefinierte Steuerelemente in Windows Forms

8 1

Benutzerdefinierte Steuerelemente in Windows Forms

Bis jetzt mussten Sie sich mit den eingebauten Windows Forms-Steuerelementen zufrieden geben, die Sie in Woche 1 kennen gelernt haben. Obwohl diese Steuerelemente einen groen Funktionsumfang abdecken, knnen sie nicht alle Ihre Wnsche erfllen. Doch das .NET Framework lsst Sie auf einfache Weise Steuerelemente erstellen, die das tun. Weil alle Steuerelemente im .NET Framework auf einem gemeinsamen Gerst und einer gemeinsamen Basisklasse fuen, ist es nicht schwierig, sie zu erweitern oder neue zu erzeugen. Dieses Kapitel zeigt, worum es sich bei dieser Basis handelt und auf welche Weise Sie sie nutzen knnen, um benutzerdefinierte Funktionalitt zu schaffen. Sie lernen auch, vorhandene Steuerelemente so zu verndern, dass sie mehr Funktionen bieten, ohne dass Sie sie vllig umschreiben mssen. Heute lernen Sie,

worum es sich bei der Klasse Control handelt, wie Sie Ereignisse in Steuerelementen nutzen knnen, wie man Steuerelemente so schreibt, dass sie auf intelligente Weise mit dem Benutzer interagieren, in welcher Hinsicht die Klasse UserControl-Klasse einem Windows Form hnelt.

18.1 Wozu benutzerdefinierte Steuerelemente erzeugen?


Benutzerdefinierte Steuerelemente visieren zwei Punkte im .NET Framework an:

Erweiterbarkeit Kapselung und Wiederverwendbarkeit

Bei der Entwicklung von Windows Forms-Anwendungen mag es vorkommen, dass die vorhandenen Windows Forms-Steuerelemente fr Ihre Anforderungen nicht ausreichen. Wollten Sie schon einmal dynamische Mens wie in Microsoft Office 2000 erstellen? Wollten Sie schon einmal den Text auf einer Schaltflche direkt bearbeiten knnen? Oder einmal etwas erschaffen, das berhaupt nicht wie eines der Windows Forms-Steuerelemente aussieht, beispielsweise eine Uhr? Keine dieser Aufgaben liee sich mit den eingebauten Windows Forms-Steuerelementen bewltigen. Das soll nicht heien, dass die vorhandenen ihre Aufgabe nicht richtig erfllen wrden. Es bedeutet lediglich, dass die Entwickler bei Microsoft nicht jedes Projekt vorausahnen konnten, das Sie ausfhren mchten, und sich nicht jedes Steuerelement vorstellen

590

Benutzerdefinierte Steuerelemente

konnten, das Sie bentigen. Die Mglichkeiten sind endlos, daher muss es einen Weg geben, um die vorhandenen Steuerelemente zu erweitern. Wiederverwendbarkeit ist ein weiteres Problem bei jeder Anwendung. Einige Aufgaben lassen sich mit der klugen Manipulation einer Kombination von Steuerelementen bewltigen. Man knnte so etwa eine Uhr erzeugen, indem man ein Timer-Steuerelement mit einem Label-Steuerelementverbindet. Es ist jedoch ein wenig lstig, jedes Mal wenn man eine Uhr braucht, den entsprechenden Funktionsumfang zu programmieren, die Ereignisse zu verarbeiten, die Anzeigeeigenschaften festzulegen usw. Doch im .NET Framework ist Kapselung grundstzlich eine gute Idee. Kapselt man Funktionen als Klasse oder Objekt, muss man sich wegen der Komplexitt des Objekts keine Gedanken machen. Man kann es einfach implementieren (oder in ein Windows Form einbauen), ohne sich darum zu kmmern, wie seine Interna funktionieren. Wird etwas gekapselt, lsst es sich zudem leicht wieder und wieder in unterschiedlichen Anwendungen einsetzen. Da der Code fr dieses Objekt eingekapselt ist und nicht von einer bergeordneten Anwendung abhngt, lsst er sich ebenfalls berall verwenden. Warum also sollten wir benutzerdefinierte Steuerelemente erstellen knnen? Um das .NET Framework zu erweitern, wie es uns angemessen erscheint, und um eigenen Funktionsumfang in einem hbschen, kleinen Objekt zu kapseln, das sich immer wieder neu verwenden lsst. Diese Beschreibung passt auf ungefhr jede benutzerdefinierte Klasse, die man erzeugt. Warum sind also Windows Forms-Steuerelemente etwas Besonderes? Bedenken Sie, dass ein Windows Form ausschlielich Windows Forms-Steuerelemente (und ActiveX-Objekte, die in die AxHost-Klasse eingehllt sind) enthalten kann. Sie knnen so viele eigene Klassen schreiben, wie Sie wollen, doch wenn sie sich nicht als Steuerelement qualifizieren, werden Sie sie nicht in Ihren Windows Forms einsetzen knnen. In der heutigen Lektion lernen Sie, Steuerelemente fr jeden Zweck zu programmieren und vorhandene Steuerelemente so anzupassen, dass sie alle Wnsche hinsichtlich des Einsatzes in Windows Forms erfllen.

18.2 Benutzerdefinierte Steuerelemente


Ein Windows Forms-Objekt ist per definitionem jedes Objekt, das direkt oder indirekt von der Klasse System.Windows.Forms.Control erbt. Diese Klasse reprsentiert eine Komponente, die ber eine grafische Schnittstelle verfgt. Sie stellt eine umfangreiche Funktionalitt bereit, die man braucht, um Steuerelemente in einem Formular darzustellen; wir betrachten diese Klasse im nchsten Abschnitt etwas nher.

591

Benutzerdefinierte Steuerelemente in Windows Forms

Sie wissen, wie man von anderen Klassen ableitet die Control-Klasse macht da keine Ausnahme. Sie mssen nur wenige Schritte bewltigen, bevor Sie ein voll funktionsfhiges benutzerdefiniertes Steuerelement haben: 1. Erzeugen Sie eine Klasse, die von der Klasse Control erbt. 2. Definieren Sie Eigenschaften, Methoden und Ereignisse fr Ihre Klasse. 3. berschreiben Sie die OnPaint-Methode, um Ihr benutzerdefiniertes Steuerelement zu zeichnen. 4. Kompilieren Sie Ihre Klasse und setzen Sie sie ein. Die nchsten Abschnitte befassen sich mit den ersten drei Schritten und den Mglichkeiten, benutzerdefinierte Steuerelemente mit zustzlichen Funktionen zu versehen. Wir sparen uns Schritt 4 fr den Schluss der heutigen Lektion auf, denn Sie wissen bereits, wie das geht.

Rekapitulation der Architektur fr Steuerelemente


Die Klasse Control ist die Wurzel fr fast alle Windows Forms-Steuerelemente, die Sie benutzt haben. Viele der Mitglieder, wie etwa die Size- und Location-Eigenschaften, die Sie in Steuerelementen verwendet haben, sind von Control geerbt. Abbildung 18.1 zeigt die Klassenarchitektur, die Control umgibt. Beachten Sie, dass selbst die Form-Klasse von Control abgeleitet ist. Abbildung 18.1 stellt nicht alle Windows Forms-Steuerelemente dar, sondern nur jene, die von Control abgeleitet sind. Steuerelemente wie MenuItem und TreeNode teilen diese Architektur nicht, daher werden sie hier nicht gezeigt. Alle Steuerelemente mit Control als Basis teilen die gleichen Mitglieder. Diesen Umstand nutzen wir aus, wenn wir benutzerdefinierte Steuerelemente erstellen; Sie mssen diese geerbte Funktionalitt nicht neu schreiben, denn sie liegt ja bereits vor. Werfen wir einen Blick auf die Reihenfolge der Ausfhrung in einer typischen Anwendung. Als Erstes wird stets die Main-Methode ausgefhrt, die blicherweise nur eine Zeile hat:
Application.Run(new KlassenName());

Diese Zeile bewirkt zwei Vorgnge. Erstens wird der Konstruktor der Windows FormsKlasse aufgerufen. Hier initialisieren Sie Variablen, Windows Forms-Steuerelemente usw. Nach der Ausfhrung des Konstruktors veranlasst die Run-Methode, dass Ihr Form auf dem Bildschirm erscheint.

592

Benutzerdefinierte Steuerelemente

System.Object System.MarshallByRefObject System.ComponentModel.Component Control ButtonBase Button CheckBox RadioButton DataGrid DateTimePicker GroupBox Label LinkLabel ListControl ComboBox ListBox CheckedListBox ListView MonthCalendar PictureBox PrintReviewControl ProgessBar ScrollableControl ContainerControl Form PrintPreviewDialog ThreadExceptionDialog PropertyGrid UpDownBase DomainUpDown NumericUpDown UserControl Panel TrackBar TabPage TreeView ScrollBar HScrollBar VScrollBar Splitter StatusBar TabControl TextBoxBase RichTextBox TextBox ToolBar

Abbildung 18.1: Die Komponentenhierarchie fr Windows FormsSteuerelemente

Wenn Ihr Formular das erste Mal am Bildschirm angezeigt wird, erzeugt die CLR dafr einen Handle. Ein Handle ist lediglich eine Referenz auf eine Stelle im Grafikspeicher oder, anders ausgedrckt, eine Stelle auf Ihrem Bildschirm. Diese Stelle nutzt die CLR hinter den Kulissen, um das Formular richtig anzeigen zu knnen. Dies ist von hchster Bedeutung: Ihr Formular hat keinen Handle, bis es zum ersten Mal angezeigt wird. Sobald es dargestellt worden ist, nimmt es auf dem Bildschirm eine Flche ein, die man als Clientbereich bezeichnet (und die.NET als Rectangle-Objekt darstellt). Sehen wir uns ein Steuerelement im Formular an, z.B. eine TextBox. Der Konstruktor Ihres Formulars drfte der Ort sein, an dem Sie Ihre TextBox initialisieren. Als Erstes wird der Konstruktor des TextBox-Steuerelements ausgefhrt (zu diesem Zeitpunkt ist es noch nicht sichtbar). Auch Steuerelemente verfgen ber Handles und analog zum Formular wird der Handle erst erzeugt, wenn das Steuerelement erstmals angezeigt wird. Merke: Bevor das Steuerelement erstmals angezeigt wird, hat es keinen Handle. Wird Ihr TextBox-Steuerelement schlielich angezeigt und der Handle erzeugt, nimmt es den Clientbereich ein. Beachten Sie aber, dass sich dieser Clientbereich von dem des Formulars unterscheidet (und meist kleiner ist).Jedes Mal, wenn das TextBox-Steuerele-

593

Benutzerdefinierte Steuerelemente in Windows Forms

ment das Paint-Ereignis auslst, benutzt es seinen Clientbereich, um seine Umrisse zu markieren. Mit anderen Worten: Die TextBox wird und kann nicht auerhalb ihres Clientbereichs angezeigt werden. Das Paint-Ereignis und somit auch der Handle hngen vom Clientbereich ab, um richtig zu funktionieren. Abbildung 18.2 zeigt das Ablaufdiagramm.
Application.Run

Der Konstruktor des Formulars wird ausgefhrt Der Handle fr das Formular wird erzeugt Das HandleCreated-Ereignis des Formulars wird ausgelst Das Formular wird angezeigt

Konstruktoren des Steuerelements werden ausgefhrt Handle fr das Steuerelement wird erzeugt Das Handle-Created-Ereignis des Steuerelements wird ausgelst Das Steuerelement wird angezeigt

Optional. Wenn es ein Steuerelement im Formular gibt, dann werden diese Schritte ausgefhrt

Abbildung 18.2: Ein Formular lsst sich erst anzeigen, wenn ein Handle erzeugt wurde, und ein Handle wird erst unmittelbar vor der Anzeige des Formulars erzeugt.

Wenn man benutzerdefinierte Steuerelemente entwirft, muss man unbedingt den Clientbereich kennen. Dafr braucht man wiederum einen Handle. Auch wenn man mit dem Handle selbst meistens gar nicht direkt arbeitet, muss ein Handle vorhanden sein, damit sich Ihr Steuerelement richtig zeichnen lsst. Wann ein Handle erzeugt wird, kann man mit dem HandleCreated-Ereignis bestimmen (spter mehr dazu). Dies ist das grundlegende benutzerdefinierte Steuerelement:
public class MyControl : Control { ... }

Die einzige Bedingung bis jetzt ist nur, dass es von der Klasse Control erbt. Als Nchstes sollte das Steuerelement auch ber ein paar Mitglieder verfgen.

Eigenschaften, Methoden und Ereignisse


Fr das Lernziel dieser Lektion erstellen wir ein Steuerelement, das wie ein Auge aussieht. Das Steuerelement im Formular stellt im Wesentlichen ein menschliches Auge dar und wird ber Eigenschaften, Methoden und Ereignisse verfgen, die sich dazu verwenden lassen, dem Mauszeiger ber den Bildschirm zu folgen.

594

Benutzerdefinierte Steuerelemente

Welche Eigenschaften sind ntig? Zunchst einmal die Farbe der Iris, die wir Bloodshotedness (blutunterlaufen) nennen, sowie die aktuelle Position der Pupille. Beim ersten Versuch sieht unsere benutzerdefinierte Klasse aus wie in Listing 18.1. Listing 18.1: Der Rahmen fr das Auge-Steuerelement
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: using System; using System.Windows.Forms; using System.Drawing; namespace TYWinforms.Day18 { public class EyeControl : Control { public Point PupilPosition; public Color IrisColor; public int BloodShotedness; public EyeControl() { ... } } }

Dieser grundlegende Rahmen fr Eigenschaften funktioniert, bietet aber nur wenige Fhigkeiten. Wenn beispielsweise der Benutzer dieses Steuerelements die IrisColor-Eigenschaft ndert, muss er warten, bis sich das Steuerelement neu gezeichnet hat, bevor sich die Irisfarbe ndert und es lsst sich nicht feststellen, wann dies erfolgt. Man muss also den Farbenwechsel und das Aktualisieren der erzwingen, sobald die Eigenschaft gendert wird. Zu diesem Zweck verwenden Sie die Syntax der Eigenschaftendeklaration. Diese Syntax, die in den meisten Programmiersprachen zu finden ist, erlaubt es, zustzlich zum Setzen oder Lesen eines Eigenschaftswertes auch Code auszufhren. Falls Sie bereits einmal mit C# oder VB .NET gearbeitet haben, ist Ihnen wohl diese Syntax bereits bekannt. Die Syntax sieht in C# folgendermaen aus:
public Typ Name { get { // Code zum Ausfhren und Abrufen der Eigenschaft } set { // Code zum Ausfhren und Festlegen der Eigenschaft } }

595

Benutzerdefinierte Steuerelemente in Windows Forms

Und in VB .NET:
Public Property Name As Typ Get ' Code zum Ausfhren und Abrufen der Eigenschaft End Get Set ' Code zum Ausfhren und Festlegen der Eigenschaft End Set End Property

Gem dieser Syntax knnen Sie jedes Mal, wenn Ihre Eigenschaft mit der Syntax IhrSteuerelement.Eigenschaft festgelegt oder abgerufen wird, eine Methode ausfhren. Sie werden dies besser verstehen, wenn Sie es im Code betrachten, wie etwa in Listing 18.2. Listing 18.2: Bessere Auge-Eigenschaften
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: using System; using System.Windows.Forms; using System.Drawing; namespace TYWinforms.Day18 { public class EyeControl : Control { private Point ptPosition; private Color clrIris; private int intBlood; public Point PupilPosition { get { return ptPosition; } set { ptPosition = value; Invalidate(); } } public Color IrisColor { get { return clrIris; } set { clrIris = value; Invalidate(); } } public int BloodShotedness { get { return intBlood; } set {

596

Benutzerdefinierte Steuerelemente

30: 31: 32: 33: 34: 35: 36: 37: 38: 39:

intBlood = value; Invalidate(); } } public EyeControl() { ... } } }

In den Zeilen 7 bis 9 legen Sie drei private Variablen an, die die Eigenschaften fr Irisfarbe, Bloodshotedness und Pupillenstellung darstellen. Alle diese Variablen sind rein intern und der Benutzer kann nicht darauf zugreifen. Die ffentlichen Eigenschaften, die Sie mit der Syntax der Eigenschaftendeklaration deklarieren, verweisen auf diese internen Variablen. Die erste ffentliche Eigenschaft ist PupilPosition in Zeile 11. Sie befolgt die get/set-Syntax. Wird ein get aufgerufen (wenn also der Benutzer etwas hnliches wie Point mypoint = EyeControl.PupilPosition ausfhrt), wird der Wert geliefert, der in der internen Variablen ptPosition gespeichert ist (Zeile 12). Legt der Benutzer den Wert der Eigenschaft (mit etwas hnlichem wie EyeControl.PupilPosition = new Point(30,30)) fest, setzen Sie die interne Variable ptPosition auf value, was den bergebenen Wert darstellt. Da sich das Steuerelement sofort, nachdem dieser Wert gesetzt wurde, neu zeichnen soll, rufen Sie die Invalidate-Methode auf. Sie lst das Paint-Ereignis aus (gleich mehr dazu). Als Nchstes mssen wir die Position des Augapfels immer dann aktualisieren, wenn sich der Mauszeiger bewegt. Das sieht wie ein guter Kandidat fr das MouseMove-Ereignis aus, doch hierbei gilt eine Einschrnkung. Das MouseMove-Ereignis wird nur ausgelst, wenn sich der Mauszeiger direkt ber das Formulars bewegt. Daher wrde sich der Augapfel nur bewegen, wenn sich der Mauszeiger direkt darber bewegt. Das ist nicht erwnscht, sondern vielmehr wollen wir, dass das Auge dem Mauszeiger berallhin folgt. Zur Lsung des Problems verwenden wir ein Timer-Steuerelement. Wenn das Zeitintervall des Timers abgelaufen ist, wird die Stellung des Augapfels aktualisiert. Erzeugen Sie also im Konstruktor Ihres Steuerelements einen Timer, setzen Sie seine Interval-Eigenschaft auf einen sehr kleinen Wert (wie zum Beispiel 100 Millisekunden) und weisen Sie einen Ereignishandler zu:
objTimer.Interval = 100; objTimer.Elapsed += new System.Timers.ElapsedEventHandler(this.Update); objTimer.Enabled = true;

597

Benutzerdefinierte Steuerelemente in Windows Forms

... private void Update(Object Sender, System.Timers.ElapsedEventArgs e) { ptPosition = Cursor.Position; Invalidate(); }

Die Cursor-Klasse reprsentiert den aktuellen Mauszeiger und die statische PositionEigenschaft liefert einen Punkt, der die aktuelle Position beschreibt. Rufen Sie diesen Wert ab und speichern Sie ihn in der internen ptPosition-Variablen. Auch hier mssen Sie die Invalidate-Methode aufrufen, um dafr zu sorgen, dass der Augapfel am Bildschirm aktualisiert wird. Fr dieses Beispiel setzen wir statt der zuvor verwendeten Klasse System.Windows.Forms.Timer die Klasse System.Timers.Timer ein. Sie knnen im Grunde beide einsetzen, doch letztere ist etwas genauer. Statt eines Tick-Ereignisses und eines EventHandler-Delegaten mssen Sie hier das Elapsed-Ereignis beobachten und einen ElapsedEventHandler verwenden. Wir wollen den Benutzer auch jedes Mal, wenn sich der Augapfel bewegt, benachrichtigen. Das heit, dass unser Augen-Steuerelement auch ein benutzerdefiniertes Ereignis bentigt. An Tag 5 haben wir benutzerdefinierte Ereignisse erstellt nur fr den Fall, dass Sie hierzu einen kleinen Auffrischungskurs gebrauchen knnen.

Wir nennen unser Ereignis EyeMoved und deklarieren es folgendermaen:


private EventHandler onEyeMoved; ... public event EventHandler EyeMoved { add { onEyeMoved += value; } remove { onEyeMoved -= value; } }

Bevor man ein Ereignis erzeugt, muss man bekanntlich einen Delegaten mit Hilfe der Syntax onEreignisName erstellen. Das tut die erste Zeile. Dann erzeugen Sie das Ereignis mit einer Syntax, die der Eigenschaftendeklaration hnelt. Die add- und remove-Abschnitte funktionieren hnlich wie die get- und set-Methoden fr Eigenschaften. Wenn der Benutzer dem EyeMoved-Ereignis einen Ereignishandler wie etwa
objEye.EyeMoved += new EventHandler(this.Whatever)

598

Benutzerdefinierte Steuerelemente

zuweist, fgt der add-Abschnitt diese Methode dem Delegaten onEyeMoved hinzu. Beschliet ein Benutzer, diesen Ereignishandler aus dem EyeMoved-Ereignis zu entfernen, wird der remove-Abschnitt ausgefhrt. Als Nchstes brauchen wir eine Methode, die den Delegaten ausfhrt:
protected virtual void OnEyeMoved(EventArgs e) { if (onEyeMoved != null) { onEyeMoved(this, e); } }

Diese Methode prft, ob dem Delegaten onEyeMoved ein Ereignishandler zugewiesen ist. Ist das der Fall, fhrt sie ihn aus. (C# beachtet bekanntlich die Gro-/Kleinschreibung, so dass onEyeMoved etwas anderes als OnEyeMoved ist!) In VB .NET wrde der gleiche Code so aussehen:
Protected Overridable Sub OnEyeMoved(e As EventArgs) RaiseEvent EyeMoved(Me, e) End Sub

Ihr Steuerelement zeichnen


Das Auge zu zeichnen, ist komplizierter, als es klingt. Theoretisch knnte man einfach die FillEllipse-Methode dreimal aufrufen (einmal fr das Augenwei, einmal fr die Iris und einmal fr die Pupille), doch dieses Vorgehen ignoriert die Position des Mauszeigers. Wir mssen also die Koordinaten fr den Mauszeiger auf die Augapfelstellung bertragen. Unser Auge soll auerdem rund sein, doch beim Zeichnen in Windows Forms werden nur Rechtecke und Quadrate verwendet. Wenn wir die Iris und Pupille so zeichnen, dass sie dem Mauszeiger folgen, knnte es passieren, dass sie sich auerhalb der Augenweiellipse bewegen das wrde recht merkwrdig aussehen (vgl. Abbildung 18.3). Daher brauchen wir nur innerhalb der Augenweiellipse zu zeichnen.

Abbildung 18.3: Ein relativ unheimliches Auge

599

Benutzerdefinierte Steuerelemente in Windows Forms

Auerdem mssen wir irgendwie noch die Eigenschaft Bloodshotedness integrieren. Je hher der Wert von Bloodshotedness, desto rter soll das Auge erscheinen (bloodshot: blutunterlaufen). Alle drei Probleme fhren zu einer recht komplizierten Aufgabe, doch wir knnen sie eines nach dem anderen bewltigen.

Innerhalb von Fensterbereichen zeichnen


Wir gehen zuerst das Problem an, wie man innerhalb des ueren Auges zeichnet. Wie Sie aus Tag 13 wissen, kann man Region-Objekte (Fensterbereiche) benutzen, um Zeichnungen zu beschneiden. Alles, was Sie also auerhalb einer bestimmten Fensterbereichs zeichnen, wird nicht angezeigt. Wir bentigen ein Region-Objekt, das das uere Auge darstellt, und sorgen dann dafr, dass nur innerhalb der Region gezeichnet wird. Am einfachsten lsst sich unser Region-Objekt mit einem GraphicsPath-Objekt erzeugen: GraphicsPath verfgt ber eine AddEllipse-Methode, welche genau die Form anbietet, die wir ausschneiden wollen.
private GraphicsPath pthClip = new GraphicsPath(); private Region regClip;

Dann fgen Sie dem Pfad eine Ellipse hinzu:


pthClip.AddEllipse(ClientRectangle); regClip = new Region(pthClip); ClientRectangle (eine von der Klasse Control geerbte Eigenschaft) ist ein RectangleObjekt, das den oben erwhnten Clientbereich festlegt; es wird von der Size-Eigenschaft gesteuert.

Doch Moment mal! Wir haben doch oben gesagt, dass der Clientbereich erst erzeugt wird, wenn das Steuerelement angezeigt und der Handle erstellt wird. In diesem Fall mssen wir warten, bis der Handle erzeugt wurde, bevor wir dem Pfad eine Ellipse hinzufgen und unsere Region anlegen knnen. An dieser Stelle kommt das Ereignis HandleCreated ins Spiel. Im Konstruktor Ihres Steuerelements fgen Sie den folgenden Code ein:
this.HandleCreated += new EventHandler(this.SetBounds);

Dann erzeugen Sie die SetBounds-Methode und fgen hier den Code ein, der den Fensterbereich erzeugt:
private void SetBounds(Object Sender, EventArgs e) { pthClip.AddEllipse(ClientRectangle); regClip = new Region(pthClip); }

Ihre Region ist jetzt ordnungsgem initialisiert. Nun knnen wir mit der Erstellung der OnPaint-Methode beginnen:

600

Benutzerdefinierte Steuerelemente

protected override void OnPaint(PaintEventArgs e) { e.Graphics.Clip = regClip; ... }

Von jetzt ab wird alles, was Sie mit dem Graphics-Objekt zeichnen, auf die Innenflche des ueren Auges beschrnkt sein. Es gibt aber noch einen Haken, mit dem wir zurechtkommen mssen. Das ClientRectangle wird ja, wie erwhnt, von der Size-Eigenschaft gesteuert. Doch was passiert, wenn sie der Benutzer nicht in seinem Code festlegt? Dann ist das ClientRectangle quasi nicht vorhanden und das Steuerelement erscheint nicht auf Ihrem Bildschirm. Man muss also im Konstruktor die Size-Eigenschaft und somit das ClientRectangle initialisieren:
this.Size = new Size(100,100);

Pinselfarben ndern
Wie lsst man nun das Auge blutunterlaufen aussehen? Wir frben nur das uere Auge rot. Der Grad dieser Rte wird durch die Bloodshotedness-Eigenschaft bestimmt. Die interne Variable intBlood ist jedoch ein Integer, so dass sich keine rote (oder irgendeine andere) Farbe angeben lsst. Daher ist der Integer in eine Wertangabe umzuwandeln, wie viel Prozent Rot erreicht werden soll. Wenn der Benutzer zum Beispiel folgenden Code schreibt, soll das Auge zu 100 Prozent rot sein:
objEye.BloodShotedness = 100;

Analog wrde der folgende Code das Auge zu 50 Prozent rten:


objEye.BloodShotedness = 50;

Um diese Berechnung auszufhren, mssen Sie ein wenig ber Farben Bescheid wissen. Es drfte allgemein bekannt sein, dass sich Farben aus den drei Grundfarben Rot, Grn und Blau (= RGB) zusammensetzen. Die tatschliche Farbe eines Objekts wird vom Mischungsverhltnis dieser Bestandteile bestimmt. In einer typischen Palette rangieren diese Einzelwerte zwischen 0 und 255. Eine Farbe mit den RGB-Werten 255, 255 und 255 entspricht Wei; 0, 0 und 0 entspricht Schwarz; 0, 255, 0 ist reines Grn und so weiter. Um den gewnschten Prozentsatz an Rot zu erhalten, muss man also die Anteile von Grn und Blau gleichermaen reduzieren. Jetzt knnen wir endlich die Bloodshotedness-Eigenschaft erzeugen. Sie sieht folgendermaen aus:
public int BloodShotedness { get { return intBlood; } set { intBlood = value; clrBlood = Color.FromArgb(255, 255 - (int)(255 * (intBlood / 100.0)),

601

Benutzerdefinierte Steuerelemente in Windows Forms

255 - (int)(255 * (intBlood / 100.0))); Invalidate(); } }

Hier wird ein neues privates Color-Objekt namens clrBlood deklariert, das wir spter zum Zeichnen verwenden. Mit der FromArgb-Methode knnen wir eine Farbe basierend auf den RGB-Werten erzeugen. Da die Farben unseres Auges zwischen reinem Wei und reinem Rot liegen sollen, bleibt der Wert der R-Komponente in unserer Farbe immer gleich 255. Die Werte fr Grn und Blau sind in dem Mae zu verringern, wie sich der blutunterlaufene Eindruck verstrken soll. Eine Anmerkung zur Berechnung: Die intBlood-Eigenschaft soll ein Prozentzahl darstellen. Deshalb teilen Sie den bergebenen Wert durch 100, um den Prozentwert zu erhalten:
intBlood / 100.0

(Wir geben 100.0 statt 100 an, weil wir das Ergebnis als Gleitkommawert statt als abgerundete Ganzzahl bentigen.) Als Nchstes ist das prozentuale Ergebnis bezogen auf 255 (den maximalen Farbwert) zu bestimmen:
255 * (inBlood / 100.0)

Betrgt der intBlood-Wert beispielsweise 50, liefert die erste Formel 0,5 (d.h. 50%) und die zweite 127,5. Dies entspricht der Hlfte, und das ist es, was wir wollen. Ziehen Sie zum Schluss diesen Wert vom Farbhchstwert 255 ab. Als Ergebnis werden jetzt die Grn- und Blauwerte prozentual bezogen auf 255 verringert, wenn sich Bloodshotedness erhht. Abbildung 18.4 zeigt ein sehr blutunterlaufenes Auge (also mit hohem Rotanteil). Wir knnen nun der OnPaint-Methode eine neue Zeile hinzufgen, die unser neues ColorObjekt dazu verwendet, das Innere des Auges auszufllen:
protected override void OnPaint(PaintEventArgs e) { e.Graphics.Clip = regClip; e.Graphics.FillEllipse(new SolidBrush(clrBlood), ClientRectangle); ... }

Wir mssen die Mauskoordinaten in die Augapfelposition bersetzen. Im nchsten Abschnitt wird dies mit einigen mathematischen Kniffen bewerkstelligt.

602

Benutzerdefinierte Steuerelemente

Abbildung 18.4: Ein relativ wtendes Auge

Koordinaten bersetzen
Die Mauskoordinaten in die Augapfelposition zu bersetzen, ist ein komplizierter Vorgang. Wir verwenden die Update-Methode, die der Ereignishandler fr das Elapsed-Ereignis des Timers war. Vor ein paar Seiten war sie wie folgt anzusehen:
private void Update(Object Sender, System.Timers.ElapsedEventArgs e) { ptPosition = Cursor.Position; Invalidate(); }

Wenn wir diese Methode unverndert bernehmen und das Auge mit ptPosition zeichnen wollen, ist Folgendes zu beachten: Der Aktionsradius des Mauszeigers ist offensichtlich grer als die Abmessungen des Steuerelements. Bei einem typischen Bildschirm mit einer Auflsung von 800 x 600 Pixel kann sich der Mauszeiger von der Koordinate (0,0) bis zur Koordinate (799,599) bewegen. Unser Steuerelement ist per Vorgabe nur 100 x 100 Pixel gro. Wir mssen also die obere Bereichsgrenze auf die Gre unseres Steuerelements natrlich prozentual skalieren. Wir dividieren die Breite und Hhe des Bildschirms durch Breite bzw. Hhe des Steuerelements dividieren, was uns die richtigen Wertebereiche liefert. Dies erfolgt mit dem folgenden Code in der Update-Methode:
ptPosition = Cursor.Position; ptPosition.X /= (Screen.PrimaryScreen.Bounds.Width / ClientRectangle.Width);

Die Screen-Klasse reprsentiert einen Bildschirm und die PrimaryScreen-Eigenschaft den gerade benutzten Bildschirm (falls mehr als einer vorhanden sein sollte). Bounds liefert ein Rechteck, das den Monitor darstellt. Man dividiert dessen Breite durch die Breite des Steuerelements (welche durch ClientRectangle festgelegt ist).

603

Benutzerdefinierte Steuerelemente in Windows Forms

Befindet sich der Mauszeiger gerade am Punkt (800, 0), ergibt diese Formel 800 / (800 / 100) = 100. Analog wird eine Mausposition von (0, 0) in 0 / (800 / 100) = 0 bersetzt. Diese beiden Werte fr die x-Koordinate und alle dazwischenliegenden Werte passen genau in die Breite unseres Steuerelements, das die Mae von 100 x 100 Pixel hat. Diese Formel ist sicherlich nicht exakt, und das sich daraus ergebende Auge drfte sich wohl nicht genau mit dem Mauszeiger bewegen, doch die Annherung ist fr unsere Zwecke nah genug. Das Gleiche wird fr die y-Koordinate erledigt:
ptPosition.Y /= (Screen.PrimaryScreen.Bounds.Height / ClientRectangle.Height);

Natrlich gibt es noch einen Haken. Die obere linke Ecke einer Zeichnung ist auf den Point-Wert festgelegt. Sollte in dieser Situation die ptPosition.X gleich null sein, sind wir aus dem Schneider. Doch was, wenn sie bei 100 liegt, der Breite unseres Steuerelements? Das Auge wrde auerhalb des Auges gezeichnet (und da wir unseren Clipping-Bereich festgelegt haben, wrde es berhaupt nicht gezeichnet). Man muss also die Breite des Auges bercksichtigen und die x- und y-Koordinaten entsprechend zurckverlegen, um so dafr zu sorgen, dass es nicht auerhalb der Umrisse des Steuerelements gezeichnet wird. Zu diesem Zweck vergleichen wir die x- und y-Koordinaten mit dem ClientRectangle:
if (ptPosition.X > ClientRectangle.Width-30) { ptPosition.X = ClientRectangle.Width-30; } if (ptPosition.Y > ClientRectangle.Height-30) { ptPosition.Y = ClientRectangle.Height-30; }

Dieser Code besagt Folgendes: Wenn die Position grer als die Breite des Steuerelements minus der Breite des Auges ist, setzt man die Position auf die Breite des Steuerelements minus die Breite des Auges. So kann das Auge nie ber den Rand hinausrutschen. Schlielich wollten wir noch ein benutzerdefiniertes Ereignis jedes Mal auslsen, wenn sich die Augenstellung gendert hat. Der letzte Schritt besteht also im Aufruf der OnEyeMoved-Methode (man beachte das grogeschriebene O), um das Ereignis auszulsen, und danach rufen wir die Invalidate-Methode auf:
OnEyeMoved(EventArgs.Empty); Invalidate();

Unsere OnPaint-Methode sind nun so aus:


protected override void OnPaint(PaintEventArgs e) { e.Graphics.Clip = regClip; e.Graphics.FillEllipse(new SolidBrush(clrBlood), ClientRectangle);

604

Benutzerdefinierte Steuerelemente

e.Graphics.FillEllipse(new SolidBrush(clrIris), new Rectangle(ptPosition.X, ptPosition.Y, 30, 30)); e.Graphics.FillEllipse(Brushes.Black, new Rectangle(ptPosition.X+5, ptPosition.Y+5, 20, 20)); }

Nun haben wir zwei neue Ellipsen. Die erste stellt die Iris dar und wird mit der Farbe clrIrisColor (die der Benutzer festlegen kann) sowie mit modifizierten ptPosition-Punktwerten gezeichnet. Die zweite Ellipse stellt die Pupille dar. Sie wird innerhalb der Iris mit einem Black-Pinsel gezeichnet.

Alles zusammensetzen
Bislang produzierten wir nur eine Menge kleiner Stcke des benutzerdefinierten Steuerelements. Bevor wir sie zusammensetzen, sollten wir rekapitulieren. Zunchst haben Sie Ihre Eigenschaften mit der Syntax der Eigenschaftendeklaration erzeugt. Dann haben Sie einen Timer verwendet, um die Position des Augapfels jede Zehntelsekunde zu aktualisieren. Sie haben einen benutzerdefinierten Delegaten erzeugt und lsen ein Ereignis bei jeder Augenbewegung aus. Als Nchstes wurde die Clipping-Region angelegt (nach einer genauen Untersuchung der Handles). Die Eigenschaft Bloodshotedness wurde mit Hilfe einer benutzerdefinierten RGB-Farbe spezifiziert. Schlielich haben Sie die Mauskoordinaten in die geeigneten Koordinaten des Steuerelements bersetzt. Listing 18.3 zeigt den vollstndigen Code holen Sie tief Luft. Listing 18.3: Ihr benutzerdefinierter Augapfel
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: using using using using System; System.Windows.Forms; System.Drawing; System.Drawing.Drawing2D;

namespace TYWinforms.Day18.Controls { public class EyeControl : Control { private Point ptPosition; private Color clrIris; private int intBlood; private Color clrBlood; private System.Timers.Timer objTimer = new System.Timers.Timer(); private EventHandler onEyeMoved; private GraphicsPath pthClip = new GraphicsPath();

605

Benutzerdefinierte Steuerelemente in Windows Forms

15: private Region regClip; 16: 17: public Point PupilPosition { 18: get { return ptPosition; } 19: set { 20: ptPosition = value; 21: Invalidate(); 22: } 23: } 24: 25: public Color IrisColor { 26: get { return clrIris; } 27: set { 28: clrIris = value; 29: Invalidate(); 30: } 31: } 32: 33: public int BloodShotedness { 34: get { return intBlood; } 35: set { 36: intBlood = value; 37: clrBlood = Color.FromArgb(255, 255 - (int)(255*(intBlood/  100.0)), 255 - (int)(255*(intBlood/100.0))); 38: Invalidate(); 39: } 40: } 41: 42: public EyeControl() { 43: ptPosition = new Point(0,0); 44: intBlood = 0; 45: clrBlood = Color.White; 46: clrIris = Color.Blue; 47: 48: objTimer.Interval = 100; 49: objTimer.Elapsed += new  System.Timers.ElapsedEventHandler(this.Update); 50: objTimer.Enabled = true; 51: 52: this.Size = new Size(100,100); 53: this.HandleCreated += new EventHandler (this.SetBounds); 54: } 55: 56: public event EventHandler EyeMoved { 57: add { onEyeMoved += value; } 58: remove { onEyeMoved -= value; }

606

Benutzerdefinierte Steuerelemente

59: } 60: 61: private void SetBounds(Object Sender, EventArgs e) { 62: pthClip.AddEllipse(ClientRectangle); 63: regClip = new Region(pthClip); 64: } 65: 66: private void Update(Object Sender, System.Timers.ElapsedEventArgs e) { 67: ptPosition = Cursor.Position; 68: 69: ptPosition.X /= (Screen.PrimaryScreen.Bounds.Width /  ClientRectangle.Width); 70: ptPosition.Y /= (Screen.PrimaryScreen.Bounds.Height /  ClientRectangle.Height); 71: 72: if (ptPosition.X > ClientRectangle.Width-30) { 73: ptPosition.X = ClientRectangle.Width-30; 74: } 75: 76: if (ptPosition.Y > ClientRectangle.Height-30) { 77: ptPosition.Y = ClientRectangle.Height-30; 78: } 79: 80: OnEyeMoved(EventArgs.Empty); 81: Invalidate(); 82: } 83: 84: protected virtual void OnEyeMoved(EventArgs e) { 85: if (onEyeMoved != null) { 86: onEyeMoved(this, e); 87: } 88: } 89: 90: protected override void OnPaint(PaintEventArgs e) { 91: e.Graphics.Clip = regClip; 92: e.Graphics.FillEllipse(new SolidBrush(clrBlood), ClientRectangle); 93: 94: e.Graphics.FillEllipse(new SolidBrush(clrIris), new  Rectangle(ptPosition.X, ptPosition.Y, 30, 30)); 95: e.Graphics.FillEllipse(Brushes.Black, new Rectangle(ptPosition.X+5,  ptPosition.Y+5, 20, 20)); 96: } 97: } 98: }

607

Benutzerdefinierte Steuerelemente in Windows Forms

Alle internen Variablen werden in den Zeilen 8 bis 15 deklariert, darunter auch der Ausschneidebereich (Clip-Bereich), die fr Iris und Bloodshotedness verwendeten Farben und der Ereignisdelegat. Die Zeilen 17 bis 40 sind die ffentlichen Eigenschaften, also diejenigen, die Benutzer ber ihren Code setzen knnen. PupilPosition und IrisColor sind einfach, doch die Bloodshotedness-Eigenschaft setzt Formeln ein, um einen Prozentwert in eine richtige Farbe zu bersetzen. Jede dieser Eigenschaften ruft die Invalidate-Methode auf, um das Auge neu zeichnen zu lassen. In Zeile 42 beginnt der Konstruktor. Alle internen Variablen werden initialisiert (darunter die Size-Eigenschaft) und der Timer wird eingerichtet und gestartet. Fr die Timer- und HandleCreated-Ereignisse werden Ereignishandler angegeben. Das benutzerdefinierte EyeMoved-Ereignis wird in den Zeilen 56 bis 59 deklariert. Dieser Code gleicht der Syntax der Eigenschaftendeklaration und weist einen Ereignishandler zu oder entfernt ihn aus dem Delegaten. Der Ereignishandler fr das HandleCreated-Ereignis, SetBound, findet sich in den Zeilen 61 bis 64. Nachdem Ihr Steuerelement einen Handle erhalten hat, initialisieren Sie hier den Ausschneidebereich. Die Update-Methode in Zeile 66 ist der Ereignishandler fr das Timer-Steuerelement. Sie ruft zunchst die aktuelle Position des Mauszeigers ab und bersetzt sie in Koordinaten, die in das Steuerelement passen. Die Serie von if-Statements in den Zeilen 72 bis 78 stellt sicher, dass Iris und Pupille niemals auerhalb des Steuerelements gezeichnet werden, indem sie die Breite und Hhe der Iris bercksichtigen. Zeile 80 ruft die OnEyeMoved-Methode auf, die lediglich das EyeMoved-Ereignis auslst. In Zeile 84 deklariert man das OnEyeMoved-Ereignis, dessen einzige Verantwortung im Auslsen des EyeMoved-Ereignisses liegt. Die berschriebene OnPaint-Methode (Zeile 90) zeichnet das uere Auge, die Iris und die Pupille, wobei sie die diversen Eigenschaften bercksichtigt, die zuvor gesetzt wurden. Zu diesen zhlt die Rte des ueren Auges, die Irisfarbe und die bersetzte Position von Iris und Pupille. Das hier entwickelte Steuerelement ist recht vielschichtig. Im Gegensatz dazu kann das einfachste benutzerdefinierte Steuerelement lediglich aus einer berschriebenen OnPaintMethode bestehen. Um etwas wirklich Sinnvolles zu erhalten, muss man jedoch Eigenschaften und Ereignisse einbeziehen.

608

Benutzerdefinierte Steuerelemente in Windows Forms

Auch dieses Steuerelement lsst sich sinnvoller gestalten. Beispielsweise knnte man die Iris- und Pupillenform je nach der Position des Mauszeigers ndern, so dass sie perspektivisch verkrzt und eher dreidimensional erscheint. Man knnte auch eine Blinzel- oder Glotz-Methode hinzufgen, wobei das Auge dem Mauszeiger folgen wrde, bis man ihm aufzuhren befiehlt. Sobald Sie die Grundelemente der Erstellung eigener Steuerelemente verstanden haben, besteht das einzige Hindernis hinsichtlich des Funktionsumfangs in Ihrer Vorstellungskraft.

18.3 Benutzerdefinierte Steuerelemente in Windows Forms


Kompilieren Sie Ihr benutzerdefiniertes Steuerelement zu einer Assembly mit folgendem Befehl:
csc /t:library /r:system.dll /r:system.windows.forms.dll /r:system.drawing.dll listing18.3.cs

Der letzte Schritt besteht darin, eine normale Windows Forms-Anwendung zu erstellen, die unser Steuerelement verwendet. Sie knnen es in jeder beliebigen Anwendung ebenso einsetzen wie ein TextBox- oder ComboBox-Steuerelement. Listing 18.4 zeigt ein Beispiel. Listing 18.4: Das Auge einer sinnvollen Nutzung zufhren
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: using using using using System; System.Windows.Forms; System.Drawing; TYWinforms.Day18.Controls;

namespace TYWinforms.Day18 { public class Listing184 : Form { private EyeControl objEye = new EyeControl(); public Listing184() { objEye.EyeMoved += new EventHandler(this.Go); objEye.Location = new Point(75,75); objEye.BloodShotedness = 0; this.Controls.Add(objEye); this.Text = "Listing 18.4"; } private void Go(Object Sender, EventArgs e) {

609

Benutzerdefinierte Steuerelemente in Windows Forms

20: 21: 22: 23: 24: 25: 26: 27:

this.Text = objEye.PupilPosition.ToString(); } public static void Main() { Application.Run(new Listing184()); } } }

Das Auge-Steuerelement wird in Zeile 8 erstellt. Seine Eigenschaften werden in den Zeilen 12 und 13 initialisiert und in Zeile 11 wird ein Ereignishandler zugewiesen. Die Go-Methode in den Zeilen 19 bis 21 aktualisiert lediglich die Titelzeile der Anwendung mit Hilfe der PupilPosition-Eigenschaft des Steuerelements. Abbildung 18.5 zeigt ein Beispiel fr das Ergebnis.

Abbildung 18.5: Ein Spionauge, das Sie (bzw. den Mauszeiger) beobachtet

18.4 Benutzerdefinierte Steuerelemente aus vorhandenen erzeugen


Zuweilen bentigt man die Flexibilitt, ein Steuerelement ganz neu erstellen zu knnen, berhaupt nicht. Mglicherweise ist der dafr ntige Funktionsumfang bereits in einem anderen, bereits vorhandenen Steuerelement gekapselt und Ihnen fehlt nur noch das iTpfelchen. In solchen Fllen hat man zwei Wahlmglichkeiten: Man kann ein existentes

610

Benutzerdefinierte Steuerelemente aus vorhandenen erzeugen

Steuerelement erweitern oder ein neues durch die Kombination mehrerer vorhandener erstellen. Die erste Methode wird als Erweiterung bezeichnet, die zweite bezeichnet ein zusammengesetztes oder Benutzersteuerelement. Wir werfen einen Blick auf beide Methoden. Keine Angst: Keine der beiden ist so kompliziert wie das Erschaffen eines vllig neuen Steuerelements.

Vorhandene Steuerelemente erweitern


Eines der hufig gewnschten Steuerelemente ist eine Schaltflche, deren Beschriftung sich direkt von der Tastatur aus bearbeiten lsst. Ein Benutzer knnte also einfach Zeichen eintippen und der Text im Button-Steuerelement wrde sich entsprechend ndern. Dafr jedoch brauchen Sie kein neues Steuerelement, denn die Funktion, wie man eine Schaltflche zeichnet, gibt es ja bereits. Um die gewnschte Fhigkeit nun einzufgen, erstellen Sie eine neue Klasse, die von der vorhandenen Button-Klasse und nicht direkt von der Control-Klasse erbt. Listing 18.5 zeigt den ntigen Code. Listing 18.5: Eine benutzerdefinierte Schaltflche
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: using using using using System; System.Windows.Forms; System.Drawing; System.Drawing.Drawing2D;

namespace TYWinforms.Day18.Controls { public class CustomButton : Button { private String strText; private Boolean blnEdit = false; protected override void OnMouseDown (MouseEventArgs e) { if (e.Button == MouseButtons.Right) { blnEdit = !blnEdit; strText = ""; } else { base.OnMouseDown(e); } } protected override void OnKeyPress(KeyPressEventArgs e) { if (blnEdit) { strText += e.KeyChar; this.Text = strText;

611

Benutzerdefinierte Steuerelemente in Windows Forms

24: 25: 26: 27:

} } } }

Hier bentigen wir zweierlei: eine Variable, die uns sagen kann, ob wir uns im Bearbeiten-Modus befinden, und eine Zeichenfolge, die die Zeichen, die der Benutzer eintippt, enthlt. Die beiden Variablen werden in den Zeilen 8 und 9 deklariert. Wenn der Benutzer mit der rechten Maustaste auf die Schaltflche klickt, wollen wir die standardmige Verhaltensweise berschreiben und in den Bearbeitungsmodus gehen. Das ist einfach: Die Zeile 11 berschreibt die OnMouseDownMethode. Wir verwenden die MouseEventArgs-Parameter, um zu bestimmen, welche Maustaste gedrckt wurde. War es die rechte, wird der boolesche Wert blnEdit negiert (Zeile 13) und in Zeile 14 der aktuell in der Schaltflche befindliche Text gelscht. War es die linke Maustaste, fhren Sie lediglich die Standardverhaltensweise mit dem Aufruf von base.OnMouseDown(e) aus. Anfangs ist blnEdit auf false gesetzt und demzufolge der Bearbeitungsmodus abgeschaltet. Der Code in Zeile 13 setzt ihn auf true, sobald die Schaltflche erstmals angeklickt wird, und zurck auf false, sobald die Schaltflche erneut angeklickt wird. Wir mssen in der Lage sein, Tastatureingaben abzurufen. Gewhnlich erfolgt dies mit dem KeyPress-Ereignis so auch hier. Wir berschreiben die standardmige OnKeyPress-Methode in Zeile 20. Falls der Bearbeitungsmodus eingeschaltet ist, erfassen wir die Tastendrcke, sammeln sie in der Zeichenfolge strText und zeigen sie schlielich in der Schaltflche mit Hilfe der Text-Eigenschaft an. In diesem Fall brauchen wir nicht einmal die OnPaint-Methode zu berschreiben. Setzt man die Text-Eigenschaft auf einen Wert (wie in Zeile 23), lst das Steuerelement automatisch die Paint-Methode aus und zeigt an, was auch immer in Text angegeben ist genau das, was wir wollen. Abbildung 18.6 zeigt eine Beispielausgabe, nachdem der Benutzer etwas Text eingegeben hat.

612

Benutzerdefinierte Steuerelemente aus vorhandenen erzeugen

Abbildung 18.6: Der Benutzer kann Text direkt in die Schaltflche (Button) eingeben.

Benutzersteuerelemente
Ein neues Steuerelement durch die Kombination anderer Steuerelemente zu erzeugen, ist sogar noch einfacher, als ein vorhandenes zu erweitern: Wir brauchen keine OnPaintMethoden zu erzeugen oder zu berschreiben. Derartige Steuerelemente, die aus zwei oder mehreren anderen Steuerelementen bestehen, bezeichnet man als zusammengesetzte Steuerelemente oder (hufiger) als Benutzersteuerelemente . Ein Benutzersteuerelement stellt eine weitere Ebene der Kapselung im .NET Framework dar. Hat man eine Reihe von Steuerelementen, die man hufig gemeinsam einsetzt etwa ein Anmeldeformular , ist es sinnvoll, diese Steuerelemente miteinander zu einem zu kombinieren, damit man diese Komplexitt bei ihrer individuellen Erstellung verbergen kann. Weil diese Aufgabe (Anmeldung) stets das Gleiche tut, lsst sich die Funktionalitt auch leicht in ein benutzerdefiniertes Steuerelement hllen. Stellen Sie sich ein Benutzersteuerelement als ein Windows Form vor, das sich innerhalb anderer Windows Forms verwenden lsst. Das Benutzersteuerelement ist im Wesentlichen ein Bestandteil der Benutzeroberflche, der komplizierter als ein einzelnes Windows Forms-Steuerelement ist. Listing 18.6 zeigt ein einfaches Beispiel fr ein Benutzersteuerelement, das ein TextBox- und ein Button-Steuerelement kombiniert. Listing 18.6: Ein Benutzersteuerelement zur Anmeldung
1: 2: 3: 4: 5: 6: Imports System Imports System.Drawing Imports System.Windows.Forms namespace TYWinforms.Day18.Controls public class LoginForm : Inherits UserControl

613

Benutzerdefinierte Steuerelemente in Windows Forms

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:

private lblUsername As New Label() private lblPassword As New Label() private tbUsername As New TextBox() private tbPassword As New TextBox() private btSubmit As New Button() public sub New() lblUsername.Text = "Benutzername: " lblUsername.Width = 70 lblUsername.Location = new Point(10,10) lblPassword.Text = "Kennwort: " lblPassword.Width = 70 lblPassword.Location = new Point(10,40) tbUsername.Location = new Point(80,10) tbPassword.Location = new Point(80,40) btSubmit.Text = "Abschicken" btSubmit.Location = new Point(50,70) AddHandler btSubmit.Click, new EventHandler(AddressOf Me.Register) Me.BackColor = Color.Beige Me.Size = new Size(190,100) Me.Controls.Add(lblUsername) Me.Controls.Add(lblPassword) Me.Controls.Add(tbUsername) Me.Controls.Add(tbPassword) Me.Controls.Add(btSubmit) end sub private sub Register(Sender As Object, e As EventArgs) MessageBox.Show(tbUsername.Text & " wurde registriert!") end sub end class end namespace

Auf den ersten Blick sieht dieses Listing wie das fr eine normale Windows Forms-Anwendung aus, wenn auch ohne Main-Methode. Es legt in den Zeilen 7 bis 11 eine Reihe von Steuerelementen an, initialisiert sie im Konstruktor (Zeilen 14 bis 27), legt einige allgemeine Eigenschaften fr die Klasse fest (Zeilen 29 bis 36) und richtet in den Zeilen 38 bis 40 fr das Button.Click-Ereignis einen Ereignishandler ein. Also gleicht dieses Steuerelement einem Windows Forms-Steuerelement aufs Haar fast.

614

Benutzerdefinierte Steuerelemente aus vorhandenen erzeugen

Beachten Sie Zeile 6. Statt von der Form-Klasse zu erben, erben wir von UserControl. Die Klasse UserControl-Klasse verhlt sich sowohl wie ein Form als auch wie ein Control. Sie kann andere Steuerelemente enthalten wie etwa Form, ist aber selbst ein Steuerelement, das sich in ein Form einbauen lsst. Wenn man also wei, wie man Windows Forms-Klassen anlegt, kann man auch Benutzersteuerelemente anlegen. Kompilieren Sie diesen Code mit dem folgenden Befehl zu einer Assembly:
vbc /t:library /r:system.dll /r:system.drawing.dll /r:system.windows.forms.dll listing18.6.vb

Listing 18.7 zeigt eine sehr einfache Anwendung, die dieses Benutzersteuerelement einsetzt. Listing 18.7: Der Einsatz des Benutzersteuerelements
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: using using using using System; System.Windows.Forms; System.Drawing; TYWinforms.Day18.Controls;

namespace TYWinforms.Day18 { public class Listing187 : Form { private LoginForm objFrm = new LoginForm(); public Listing187() { objFrm.Location = new Point(75,75); this.Controls.Add(objFrm); } public static void Main() { Application.Run(new Listing187()); } } }

Hier findet sich nichts Ungewhnliches, denn Listing 18.7 ist lediglich eine ganz normale Windows Forms-Anwendung, die zufllig unser LoginForm-Benutzersteuerelement nutzt. Moment mal! Listing 18.7 ist in C# geschrieben, doch Listing 18.6 in VB .NET. Das bedeutet eine weitere groartige Neuheit hinsichtlich Benutzersteuerelementen und Kapselung. Die von Ihnen erstellten Steuerelemente (Benutzersteuerelemente oder sonstige) mssen nicht in der gleichen Programmiersprache geschrieben sein wie die sie aufnehmende Anwendung. Hier sehen wir sozusagen die CLR bei der Arbeit, wie sie sprachber-

615

Benutzerdefinierte Steuerelemente in Windows Forms

greifende Komponenten zur Zusammenarbeit bringt. Abbildung 18.7 zeigt das Ergebnis von Listing 18.7.

Abbildung 18.7: Ein Benutzersteuerelement kapselt die Komplexitt seiner Steuerelementkomponenten.

Alle in Listing 18.6 deklarierten Steuerelemente waren privat. Im Allgemeinen ist dies bei Benutzersteuerelementen keine schlechte Idee, denn schlielich mchte man verhindern, dass der Endanwender in die internen Ablufe der Steuerelemente eingreift. Immerhin war das ja der Grund, warum man sie berhaupt als Benutzersteuerelement gekapselt hat. Das bedeutet jedoch nicht, dass man keine benutzerdefinierten Eigenschaften, Methoden oder Ereignisse wie die heute erzeugten benutzerdefinierten Steuerelemente erstellen kann. Vielmehr ist dies hufig notwendig. Nehmen wir etwa Listing 18.6: Statt die ClickMethode intern zu handhaben, htte das Benutzersteuerelement ein benutzerdefiniertes Ereignis erzeugen knnen, das das Click-Ereignis an die umgebende Anwendung htte weiterleiten knnen. Dadurch knnte der Schpfer der Anwendung genau bestimmen, was geschehen soll, sobald der Endanwender auf die ABSCHICKEN-Schaltflche klickt beispielsweise die Daten in eine Datenbank speichern. Zu diesem Zweck msste man in Listing 18.6 nur folgenden Code hinzufgen:
public event Registered(Sender As Object, username as String, password as String) Protected Sub OnRegistered() RaiseEvent Registered(Me, tbUsername.Text, tbPassword.Text) End Sub

Deklarieren Sie Ihr Ereignis (in diesem Fall Registered) mit den Parametern, die Sie an die umgebende Anwendung weiterleiten wollen. Erstellen Sie dann die entsprechende OnEreignisName-Methode, die das Ereignis auslst. VB .NET erledigt fr Sie den Rest (in C# htten Sie eine benutzerdefinierte Ereignishandler-Klasse zu erstellen, die die drei hier

616

Zusammenfassung

benutzten Parameter bernimmt, doch VB .NET tut das fr Sie weitere Informationen hierzu siehe Tag 5). Um Ihr Ereignis zu nutzen, fgen Sie den folgenden Code in den Konstruktor von Listing 18.7 ein:
objFrm.Registered += new LoginForm.RegisteredEventHandler(this.RegisterMe);

Dann erstellen Sie die RegisterMe-Methode:


private void RegisterMe (Object Sender, String username, String password) { MessageBox.Show ("Registriert " + username); }

Mit Benutzersteuerelementen knnen Sie benutzerdefinierte Schnittstellen erstellen und diese anderen Entwicklern bergeben, ohne sich sorgen zu mssen, ob sie mit Ihrem Code herumspielen und die Frchte Ihrer harten Arbeit verndern besonders wenn Sie ein C#Programmierer sind und die anderen in VB .NET schreiben.

18.5 Zusammenfassung
Heute haben Sie von drei Mglichkeiten erfahren, wie Sie Ihre Anwendungen erweitern knnen: benutzerdefinierte Steuerelemente erstellen, vorhandene Steuerelemente erweitern und Benutzersteuerelemente anlegen. Die Basisklasse fr jedes Steuerelement ist die Klasse Control. Wenn Sie eigene Steuerelemente erstellen mssen, stellen Sie sicher, dass Sie von Control ableiten. Diese Klasse ist bereits mit zahlreichen Eigenschaften, Methoden und Ereignissen ausgestattet, die sich nutzen lassen, doch man kann leicht auch eigene erstellen, sollten die vorgefertigten die eigenen Anforderungen nicht erfllen knnen. Beim benutzerdefinierten Steuerelement, das wir heute erstellt haben, erfuhren wir, dass es zuweilen schwieriger als angenommen ist, benutzerdefinierte Funktionalitt zu erzeugen. Das Augapfel-Steuerelement wies eine Reihe kleinerer Probleme auf, die vorab auf dem Weg zu einem groen Ganzen gelst werden mussten. Nach sorgfltiger Planung sollte man jedoch nicht mehr auf unerwartete Probleme stoen. Um ein vorhandenes Steuerelement zu erweitern, muss man einfach von ihm statt direkt von der Klasse Control erben. Da alle Steuerelemente von Control abgeleitet sind, erbt man ebenfalls von Control, wenn auch indirekt. Will man die visuelle Erscheinungsweise des vorhandenen Steuerelements beibehalten, stellt man sicher, dass die base.OnPaintMethode aufgerufen wird.

617

Benutzerdefinierte Steuerelemente in Windows Forms

Benutzersteuerelemente schlielich verhalten sich genau wie Windows Forms-Klassen. Der einzige Unterschied besteht darin, dass man von UserControl erbt statt von der FormKlasse, was es ermglicht, Benutzersteuerelemente auf Formularen zu platzieren. Die heutige Lektion unterstrich einige Hauptkonzepte des .NET Frameworks, namentlich Kapselung, Erweiterbarkeit und Wiederverwendbarkeit. Mit der Fhigkeit, eigene Steuerelemente aus anderen Steuerelementen oder ganz neu zu entwickeln, befinden Sie sich in einer ausgezeichneten Position, einzigartige Anwendungen zu erstellen.

18.6 Fragen und Antworten


F Kann ich mich benachrichtigen lassen, wenn sich eine der Eigenschaften meines Steuerelements ndert, beispielsweise wenn die PupilPosition-Eigenschaft vom Benutzer aktualisiert wird? A Ja, das ist mglich. ndert sich eine Eigenschaft in Ihrem Steuerelement, wird das Ereignis PropertyChanged ausgelst. Sie knnen diesem Ereignis einen Ereignishandler zuordnen oder die OnPropertyChanged-Methode berschreiben, damit sie benutzerdefinierten Code ausfhrt, sobald sich eine Eigenschaft ndert. Das entsprechende Objekt PropertyChangedEventArgs verfgt seinerseits ber eine Eigenschaft namens PropertyName, die Ihnen mitteilt, welche Eigenschaft sich gendert hat.

18.7 Workshop
Dieser Workshop soll Ihnen helfen, sich besser an die heute behandelten Begriffe zu erinnern. Es hilft Ihnen sehr, die Antworten vollstndig zu verstehen, bevor Sie weiterlesen. Die Antworten zu den Quizfragen und den bungen finden Sie in Anhang A.

618

Workshop

Quiz
1. Worin bestehen die vier Schritte bei der Erstellung eines benutzerdefinierten Steuerelements? 2. Welche Methode mssen Sie aufrufen, um Ihr Steuerelement neu zeichnen zu lassen? 3. Schreiben Sie in C# ein Beispiel fr eine ffentliche Eigenschaft und verwenden Sie die Syntax der Eigenschaftendeklaration. 4. Schreiben Sie in VB.NET ein Beispiel fr eine ffentliche Eigenschaft und verwenden Sie die Syntax der Eigenschaftendeklaration. 5. Was ereignet sich zuerst: es wird ein Handle erzeugt oder Ihr Steuerelement wird am Bildschirm angezeigt? Warum? 6. Wahr oder falsch? Man kann von einem RichTextBox-Steuerelement erben. 7. Nennen Sie drei Nutzeffekte der Erstellung von Benutzersteuerelementen.

bung
Erstellen Sie einen Thermostaten (Wrmeregler) als benutzerdefiniertes Steuerelement, das die aktuelle Temperatur in der Art eines richtigen Quecksilberthermometers anzeigt. Stellen Sie sicher, dass die Temperatur nicht ber das Maximum hinausgeht oder unter Null fllt. Implementieren Sie das Steuerelement in einer Windows Forms-Anwendung und stellen Sie dem Benutzer Schaltflchen bereit, mit denen er die Temperatur steigern oder verringern kann. (Natrlich mssen Sie sich all dies ausdenken, denn Sie knnen ja nicht die tatschliche Temperatur messen.)

619

MultithreadingAnwendungen

9 1

Multithreading-Anwendungen

Threading ist ein vielschichtiges Thema, mit dem man sich deshalb hufig nur in Publikationen fr Fortgeschrittene beschftigt. Doch es handelt sich dabei um ein groartiges Hilfsmittel, das die Performanz und Bedienbarkeit von Windows Forms-Anwendungen deutlich erhhen kann und dem man daher angemessene Aufmerksamkeit zukommen lassen sollte. Ein Thread ist die Grundeinheit, in der ein Computer Befehle ausfhrt (also ein einzelner Verarbeitungsprozess; von engl. thread: Faden). Mehrere Threads zu haben, erlaubt es, die Geschwindigkeit des Hauptprozessors (CPU, Central Processing Unit) und den parallelen Aufbau des Betriebssystems zu nutzen, so dass Prozesse schneller und effizienter ausgefhrt werden. Grob gesagt lsst sich die Leistung verdoppeln, indem man die Anzahl der Threads verdoppelt. Heute lernen Sie, worum es sich bei Threads genau handelt und was es heit, mehr als einen gleichzeitig auszufhren. Am Ende des Tages werden Sie die Vorurteile ausgerumt haben, die Multithreading-Anwendungen immer noch anhaften. Heute lernen Sie,

was genau ein Thread ist und was er fr Ihre CPU bedeutet, wie Threads in Windows funktionieren, wie man neue Threads erstellt und sie verwaltet, worum es sich bei threadbergreifendem Aufruf-Marshalling handelt, auf welche Weise man Vorteile aus Threadpools ziehen kann, wie man durch Synchronisierung eine sichere Kommunikation zwischen mehreren Threads ermglicht, mit welchen Techniken man eine vollstndig multithreaded arbeitende Anwendung erstellt.

19.1 Einfhrung in Multithreading


Bevor Sie etwas ber Multithreading lernen, sollten Sie erst einmal das gute alte Threading kennen lernen und ber Prozesse Bescheid wissen sowie ber die Art und Weise, wie sie mit dem Betriebssystem zusammenarbeiten. Threads und Prozesse stellen die Methode des Betriebssystems dar, Aufgaben so aufzuteilen, dass sie effizienter erledigt werden knnen. Mit einem Prozess teilt das Betriebssystem Anwendungen voneinander ab; eine Einzelanwendung reprsentiert blicherweise einen Einzelprozess. Ein Thread ist ein einzelner Verarbeitungsablauf. Ein einzelner Thread ist zustndig fr die Verarbeitung aller Befehle in einer einzelnen Aufgabe. Technisch ausgedrckt, handelt es sich bei einem Thread um

622

Einfhrung in Multithreading

die elementare Ausfhrungseinheit, der das Betriebssystem Ressourcen zuweist. Normalerweise wird ein Thread pro Prozess erzeugt. Stellen Sie sich vor, Sie wren der Besitzer eines neuen Restaurants, das gerade erffnet hat. Sie sind aber auch der einzige Angestellte. Kommt ein Gast herein, so sind Sie der einzige Mitarbeiter, der zur Verfgung steht, um alle notwendigen Arbeiten zu erledigen. Sie mssen eine Aufgabe beenden, bevor Sie sich der nchsten zuwenden knnen. Dies ist sicherlich nicht die beste Mglichkeit, die anfallende Arbeit zu erledigen. Sowohl Sie als auch Ihre Gste leiden darunter, dass Sie sich sozusagen rar machen. In Begriffen des Computerwesens entsprche dies einer Singlethreaded-Umgebung. Es gibt nur einen Thread (Sie), um alle ntigen Aufgaben zu bewltigen. Gehen Sie einmal eine Dekade zurck, dann stoen Sie auf ein Singlethreaded-Betriebssystem: DOS (gleichgltig, ob von Microsoft oder anderen Herstellern). In DOS konnte man nur eine Anwendung auf einmal ausfhren, denn DOS besa nur einen Thread, um sich seinen Aufgaben zu widmen. Wenn ein Thread eine Aufgabe ausfhrt, muss das ganze System innehalten und warten, bis er damit fertig ist, bevor der Benutzer weitermachen kann. Kurz gesagt, war DOS ein Ein-Prozess-Betriebssystem mit nur einem Thread pro Prozess. Stellen wir uns die CPU in dieser Lage vor. Der Hauptprozessor kann nur eine Aufgabe auf einmal verarbeiten (das ist oft sogar heute noch zutreffend). Tut man etwas in einer Anwendung, etwa auf eine Schaltflche klicken, so empfngt das Betriebssystem die Benutzereingabe, bersetzt sie in etwas, das der Computer verstehen kann (Bits und Bytes) und schickt den Befehl an die CPU. Muss die CPU gerade andere Aufgaben zuerst erledigen, so hat der neue Befehl geflligst in einer Schlange zu warten. Hat die CPU einen Befehl verarbeitet, gibt sie das Ergebnis aus und geht zum nchsten Befehl ber, so lange bis es keine mehr gibt. Tut die CPU gerade nichts, wird dies als Leerlaufzustand (idle state) bezeichnet. Zurck in die Gegenwart. Prozessoren sind inzwischen derartig leistungsfhig, dass sie Befehle schneller verarbeiten als diese ankommen. CPUs, deren Taktfrequenz man frher in Megahertz oder darunter ma (MHz: 1 Million Schwingungen bzw. Taktzyklen pro Sekunde), werden nun in Gigahertz (1 Milliarde Schwingungen pro Sekunde) gemessen. Jeder Einzelbefehl kann zwischen 1 und 5 Zyklen in Anspruch nehmen (oder mehr, je nach der Kompliziertheit des Befehls), so dass dies ungefhr zwischen 200 Mio. bis 1 Mrd. Befehlen pro Sekunde entspricht. (Meine Darstellung vereinfacht CPUs in hohem Mae.) Natrlich kann man nicht 200 Mio. Befehle/Sekunde an die CPU schicken. Die Singlethreaded-Umgebung wurde entwickelt, als CPU-Geschwindigkeiten noch so eingeschrnkt waren, dass Befehle, die so erteilt wurden, die CPU voll auslasteten. Hier kommt Multithreading ins Spiel. Moderne Betriebssysteme verfgen ber mehr als einen Thread, um Aufgaben zu bewltigen. Windows etwa hat Hunderte von Threads gleichzeitig laufen und erzeugt je nach Bedarf weitere.

623

Multithreading-Anwendungen

Kommen wir auf unser Restaurant-Beispiel zurck. Da nun Ihr Gstekreis wchst und Sie nicht mehr alles alleine machen knnen, stellen Sie vier Mitarbeiter ein: einen Koch, einen Kellner, einen Kellnerlehrling und jemanden fr den Gsteempfang. Nun knnen Sie vier (oder fnf, wenn Sie sich mitzhlen) Dinge gleichzeitig erledigen, wodurch Sie mehr Gste zufrieden stellen. Noch mehr Mitarbeiter wrden die Sache fr Sie noch einfacher machen (siehe Abbildung 19.1).
Eine Single-threaded Umgebung
Befehle Befehle

Eine Multithreaded Umgebung

Befehle
Befehle Befehle

Befehle

Befehle Befehle Befehle Befehle Befehle Befehle Befehle

Abbildung 19.1: Eine Multithreading-Umgebung bietet zahlreiche Kellner an, die Ihnen Aufgaben abnehmen.

Startet man eine Anwendung wie etwa Microsoft Word, wird ein neuer Prozess erzeugt. Auch ein neuer Thread wird angelegt, um alle Ausfhrungsaufgaben fr diesen Prozess zu handhaben; er wird zugewiesen, um alle Word-Befehle zu verarbeiten. Whrenddessen luft das Betriebssystem unabhngig von diesem neuen Thread, damit der Rechner sauber weiterluft. Auf diese Weise haben Sie sozusagen die Arme frei, um alles Anstehende zu erledigen. Starten Sie eine weitere Anwendung, beispielsweise Photoshop, werden ein weiterer Prozess und Thread erzeugt und handhaben diese Anwendung. Jetzt laufen zwei Threads gleichzeitig: der fr Word und der fr Photoshop. Da sie unabhngig voneinander laufen, kann man leicht zwischen ihnen hin und her wechseln. Threads mischen sich also nicht bei anderen Threads ein. Sollten sie es dennoch tun, treten unerwartete Resultate auf (siehe den Abschnitt Probleme mit Threading). Ein weiterer Blick auf die CPU. Zuvor fhrte sie einen Befehl nach dem anderen aus, dass man kaum nachkam. Nun jedoch macht die CPU zwar das Gleiche, doch da wir mehrere Prozesse (und Threads) zugleich laufen haben, wird die CPU stndig auf Trab gehalten. Jeder Thread kann der CPU seine eigenen Befehle schicken. In der oben beschriebenen Zwei-Thread-Situation (Word und Photoshop) hat die CPU zwei Datenzufuhren, die die Warteschlange fr auszufhrende Befehle fllen. Doch da die CPU nun Befehle so rasch ausfhrt, sind selbst diese zwei Zufuhren nicht genug, um sie in die Knie zu zwingen. Sehr schnell werden Befehle ausgefhrt und Resultate ausgegeben. Aus diesem Grund sieht es

624

Einfhrung in Multithreading

so aus, als knne man mehrere Aufgaben gleichzeitig erledigen, obwohl die CPU weiterhin eine nach der anderen abarbeitet. Das Threading muss nicht mit nur einem Thread getan sein, denn so wie das Betriebssystem mehrere Prozesse haben kann, so kann auch jeder Prozess ber mehrere Threads verfgen. Der Schwerpunkt der heutigen Lektion liegt nicht auf MultithreadingBetriebssystemen, sondern auf Multithreading-Anwendungen. Einer Einzelanwendung knnen mehrere Threads zugewiesen sein, damit ihre Effizienz erhht wird. Wenn die CPU nmlich Zeit brig hat, warum sollte man ihr nicht noch mehr Befehle schicken? In Microsoft Word kann man einen Thread haben, der sich um Benutzereingaben ber die Tastatur und deren Bildschirmanzeige kmmert, whrend sich ein anderer im Hintergrund mit der Archivierung bzw. Indizierung von Dateien beschftigt. Normalerweise msste man mit dem Tippen innehalten und warten, bis das Archivieren beendet ist, doch auf Grund seiner Multithreading-Fhigkeiten kann Word jeder Aufgabe einen Thread zuweisen, so dass der Benutzer nicht mit dem Tippen warten muss. Abbildung 19.2 verdeutlicht dieses Konzept.

Abbildung 19.2: Mit mehreren Threads kann eine Anwendung mehrere Aufgaben gleichzeitig erledigen.

Ein alltglicheres Beispiel dreht sich um das Internet. Websites werden zunehmend umfangreicher, so dass man lnger fr das Herunterladen braucht. Leider kann der Webbrowser, der einen Einzelprozess darstellt, nur Dumchen drehen, whrend er wartet, bis alle Webseiten heruntergeladen sind; diese Situation ist leider unvermeidlich. Setzt man jedoch einen Multithreading-Browser ein, knnen Sie von mehreren Websites gleichzeitig herunterladen, so dass Sie bei ausreichender Netzwerkbandbreite zwei (oder mehr) Seiten fr den Preis von einer bekommen. Es dauert immer noch genauso lange, eine Seite herunterzuladen, doch in dieser Zeit knnen Sie zwei, drei oder mehr Seiten herunterladen. Ein letztes Mal zurck zum Vergleich mit dem Restaurant. Jeder Ihrer Mitarbeiter steuert nun seine eigene Anwendung. Der Kellner leitet ein Kellnerprogramm, der Pikkolo ein Putzprogramm usw. Angenommen, Sie stellen jeweils einen weiteren Kellner, Putzgehil-

625

Multithreading-Anwendungen

fen und Empfangskellner ein. Jetzt knnen zwei Kellner das Kellnerprogramm leiten. War es schon effizienter, mit mehreren Mitarbeitern ein Restaurant zu fhren als mit nur einem (Ihnen), so ist es nun effizienter, mehrere Kellner die Tische bedienen zu lassen etc., solange es gengend Platz fr sie gibt. Kurze Rekapitulation: Ein Thread ist ein Ausfhrungsvorgang, eine Einheit, die fr die Befehlsausfhrung in einer Einzelanwendung zustndig ist. Ein Multithreaded-Betriebssystem kann mehrere Threads zugleich laufen haben, wovon jeweils einer eine Anwendung oder einen Prozess, der gerade ausgefhrt wird, handhabt. Einer MultithreadedAnwendung sind mehrere Threads zugewiesen, um mehrere Aufgaben gleichzeitig in der Anwendung ausfhren zu knnen. Da Sie nun die Vorteile kennen gelernt haben, sollen Sie auch ber die Nachteile Bescheid wissen.

Probleme mit Threading


Zunchst fllt einem natrlich als Nachteil ein mglicher Mangel an Ressourcen ein. Je mehr Threads man laufen hat, desto mehr Ressourcen verschlingen sie. Leider ist Ihr Computer kein unerschpflicher Quell an Ressourcen; man ist gezwungen, innerhalb der Beschrnkungen zu arbeiten, die einem CPU und Arbeitsspeicher setzen. Leider bedeutet auch das Hinzufgen eines zweiten Threads zu einer bestimmten Aufgabe nicht unbedingt, dass sich damit auch die Leistung verdoppelt. Der Leistungszuwachs entspricht nicht eins zu eins der Ressourcennutzung. Der Grund dafr ist der Bedarf des Computers an Ressourcen, nur um jeden Thread aufrechtzuerhalten. Erzeugt man also einen Thread, bentigt man nicht nur Ressourcen, um dessen Aufgaben ausfhren zu knnen, sondern auch zustzliche Kapazitt fr den Verwaltungsaufwand, um dafr zu sorgen, dass der Thread richtig luft. Wenn die CPU auerdem zwischen Threads hin und her wechselt, muss sie zudem den Status jedes einzelnen Threads im Gedchtnis behalten, damit sie wei, wo sie fortfahren soll, wenn sie zu einem bestimmten Thread zurckkehrt. Diese ganzen Beanspruchungen knnen mehr an Ressourcen verschlingen, als Ihnen Ihr Thread an Leistung zurckgibt. Ein weiteres Problem rhrt von der Verstndigung zwischen Threads her. Hufig ist es ntig, dass ein Thread sozusagen mit einem anderen Thread spricht, insbesondere dann, wenn beide der gleichen Anwendung angehren. Da jedoch Threads voneinander isoliert sind und jeder ber seine eigenen Ressourcen verfgt, kann dies zum Problem werden. Threads mssen spezielle Prozeduren ausfhren, um dafr zu sorgen, dass ihre Daten mit ausreichender Sicherheit ber die Grenzen zwischen den Threads in fremdes ThreadTerritorium reisen knnen. Dies kann auf Kosten der Leistung gehen. Ein noch wichtigeres Problem besteht, wenn zwei oder mehr Threads die gleiche Datenmenge gemeinsam nutzen mssen. Angenommen, gleichzeitig laufen zwei Threads in

626

Einfhrung in Multithreading

Word, die auf dieselbe Datei zugreifen. Was passiert, wenn einer der Threads die Datei verndert? Dann sind die Daten, ber die der andere Thread verfgt, nicht mehr gltig und mssen aktualisiert werden. Wenn die beiden Threads versuchen, zu genau der gleichen Zeit auf die Datei zuzugreifen, treten fast zwangslufig Probleme auf und Ihre Anwendung strzt mglicherweise ab. Aus diesem Grund knnen Threads ihre Ressourcen sperren. Anders ausgedrckt, wenn ein Thread eine bestimmte Ressource nutzt, dann lsst er alle anderen Threads wissen, dass sie keinesfalls versuchen sollen, auf dieselbe Ressource zuzugreifen. Auch diese Sperrungen knabbern an den Ressourcen und mindern die Leistung, so notwendig sie auch sind. Die anderen Threads sollten die Sperren beachten, mssen das aber nicht unbedingt tun. Natrlich ist der Einsatz von Threads nicht grundstzlich falsch, wie Sie noch aus dem vorigen Abschnitt wissen. Doch die Erstellung von Multithreading-Anwendungen erfordert sorgfltige Planung und bietet oftmals mehr Vor- als Nachteile.
Ich empfehle Ziehen Sie Multithreading in Betracht, wenn Sie die notwendigen Vorsichtsmanahmen ergreifen knnen, um die oben beschriebenen Probleme zu vermeiden, wenn kurze Reaktionszeiten fr Ihre Anwendungen zu garantieren sind oder um Ihren Endanwendern mehr Kontrollmglichkeiten einzurumen, whrend andere Aufgaben erledigt werden. Bitte beachten Sie Setzen Sie kein Multithreading ein, wenn Sie nicht im Voraus wissen, wie ressourcenhungrig Ihre Anwendung ist oder wie eingeschrnkt das Zielsystem hinsichtlich seiner Verarbeitungskapazitt ist.

Threading-Objekte verwenden
Die wichtigste im .NET Framework verwendete Threading-Klasse ist System.Threading.Thread. Sie reprsentiert einen einzelnen Thread, der gerade im Betriebssystem luft, und verfgt ber eine Reihe von Eigenschaften und Methoden, die Ihnen Informationen darber liefern, was gerade mit diesem Thread passiert. (Gleich mehr dazu.) Wenn man eine Anwendung startet, wird ein neuer Thread erzeugt (oder einem Threadpool entnommen) und dieser Anwendung zugewiesen. Dieser Thread bleibt whrend der gesamten Lebenszeit (= Laufzeit) der Anwendung fr die Ausfhrung ihrer Aufgaben zustndig. Dieser Thread wird als Hauptthread bezeichnet, weil er in der Anwendung stets der zentrale Thread ist, gleichgltig, wie viele weitere noch erzeugt werden. Der Umgang mit einem Thread-Objekt gestaltet sich anders als alles, was wir bisher besprochen haben. Eine nhere Untersuchung erfolgt im nchsten Abschnitt, doch im Augenblick sollten wir festhalten, dass ein Thread stets etwas haben muss, das er ausfhren kann;

627

Multithreading-Anwendungen

es muss ein Stck Code geben, das sich ausfhren lsst, sonst tut der Thread berhaupt nichts. Dies gleicht dem Vorgang, bei dem man einem Windows Forms-Steuerelement einen Ereignishandler zuweist. Der Hauptthread einer Anwendung hat die Main-Methode, um ihn beschftigt zu halten (die dann wiederum den Konstruktor der Anwendung usw. aufruft).
Thread ist zwar die wichtigste Klasse, mit der wir uns heute befassen, doch sie hat eine ganze Reihe von Helfern. ThreadPool wird fr die Einrichtung von Threadpooling benutzt: der Vorgang der gemeinsamen Nutzung von Threads durch Anwendungen. Das TimerThreadobjekt gleicht der Klasse System.Windows.Forms.Timer, mit der Sie gearbeitet haben.

Damit knnen Sie eine bestimmte Aufgabe auf einem anderen Thread in einem festgelegten Intervall ausfhren lassen. Monitor erlaubt Ihnen, Ressourcen zu sperren, um sie gegen den Zugriff anderer Threads zu schtzen. In der heutigen Lektion befassen wir uns mit all diesen Klassen, doch zunchst betrachten wir, wie man eigentlich mit einem Thread arbeitet.

19.2 Eine Multithreading-Anwendung erstellen


Bevor Sie eine Anwendung erstellen, die vollstndig multithreaded ist, untersuchen wir zunchst einige Eigenschaften einer Singlethreaded-Anwendung. Insbesondere zeigen wir, wie ein normaler Thread aussieht. Ab Listing 19.2 fgen Sie dann weitere Threads hinzu. Wenn man eine Windows Forms-Anwendung startet, wird bekanntlich ein so genannter Hauptthread erzeugt und der Anwendung zugewiesen. Endet der Thread, wird die Anwendung beendet. Hlt er inne, pausiert auch die Anwendung. Es hilft also, wenn man sich den Thread eng mit der jeweiligen Anwendung verknpft vorstellt. Listing 19.1 zeigt eine einfache Anwendung, die den Hauptthread untersucht. Listing 19.1: Einen Thread untersuchen
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: using using using using System; System.Windows.Forms; System.Drawing; System.Threading;

namespace TYWinforms.Day19 { public class Listing191 : Form { private Label lblInfo = new Label(); public Listing191() { lblInfo.Size = new Size(300,300);

628

Eine Multithreading-Anwendung erstellen

12: lblInfo.Location = new Point(10,10); 13: 14: Thread thdCurrent = Thread.CurrentThread; 15: lblInfo.Text = "Ist aktiv: " +  thdCurrent.IsAlive.ToString() + "\n"; 16: lblInfo.Text += "Ist im Hintergrund: " +  thdCurrent.IsBackground.ToString() + "\n"; 17: lblInfo.Text += "Prioritt: " +  thdCurrent.Priority.ToString() + "\n"; 18: lblInfo.Text += "Threadzustand: " +  thdCurrent.ThreadState.ToString() + "\n"; 19: 20: this.Text = "Listing 19.1"; 21: this.Controls.Add(lblInfo); 22: } 23: 24: public static void Main() { 25: Application.Run(new Listing191()); 26: } 27: } 28: }

Diese Anwendung erzeugt ein Label-Steuerelement und zeigt eine Reihe von Eigenschaften des Hauptthreads an. In Zeile 14 holen Sie eine Referenz auf den Hauptthread. Ein neues Thread-Objekt namens thdCurrent wird erzeugt und der Hauptthread mit Hilfe der statischen CurrentThread-Methode der Thread-Klasse abgerufen. Die Zeilen 15 bis 18 zeigen daraufhin die Eigenschaften des Threads an.
IsAlive (Zeile 15) zeigt an, ob der Thread gerade ausgefhrt wird. Natrlich erwarten wir, dass der Hauptthread luft, sobald auch die Anwendung luft, also sollte diese Eigenschaft true liefern. IsBackground in Zeile 16 zeigt an, ob dieser Thread im Hintergrund des jeweili-

gen Betriebssystems luft. Der Unterschied zwischen einem Vorder- und einem Hintergrundthread besteht darin, das letzterer nicht zwingend ist, um eine Anwendung am Laufen zu halten. Anders ausgedrckt, fhrt ein Hintergrundthread Dinge aus, die nicht fr die Ausfhrung der Anwendung erforderlich sind (etwa die Rechtschreibprfung in Word). Andererseits braucht eine Anwendung unbedingt einen Vordergrundthread, um laufen zu knnen. Da Ihr Hauptthread die Anwendung selbst reprsentiert, muss er zwingend ein Vordergrundthread sein, und IsBackground gibt daher false zurck.

629

Multithreading-Anwendungen

Einem Thread ist auch eine Prioritt zugewiesen. Sie sagt dem Betriebssystem, wie viel CPU-Zeit es dem Thread im Verhltnis zu anderen Threads zuteilen soll. So sollte beispielsweise ein Thread, der seine Aufgabe schnell erledigen soll, eine mglichst hohe Prioritt besitzen, damit er die CPU dazu veranlasst, ihm die meiste Zeit zu widmen. Mit der Priority-Eigenschaft lsst sich dieser Wert festlegen oder herausfinden und sie verwendet einen Wert aus der Aufzhlung ThreadPriority:
Highest: Threads mit der hchsten Prioritt AboveNormal: Threads mit der zweithchsten Prioritt Normal: Typische Threads BelowNormal: Die vierthchste Prioritt, direkt unter Normal Lowest: Threads mit dieser Prioritt werden nach allen anderen ausgefhrt

Standardmig wird Threads die Normal-Prioritt zugewiesen; unser Hauptthread macht da keine Ausnahme. Die ThreadState-Eigenschaft in Zeile 18 zeigt den Zustand an, in dem sich ein Thread gerade befindet (hnlich wie die ServiceControllerStatus-Eigenschaft aus Tag 16). Diese Eigenschaft wird gesteuert von Werten aus der Aufzhlung ThreadState:

Aborted: Der Thread wurde beendet. AbortRequested: Der Thread luft noch, doch ein Befehl wurde erteilt, ihn zu been-

den.

Background: Bei dem Thread handelt es sich um einen Hintergrundthread. Running: Der Thread wird gerade ausgefhrt. Stopped: Der Thread wurde beendet (hnlich wie Aborted). StopRequested: Dem Thread wurde eine Anforderung zur Beendigung geschickt (dieser Wert kann nur intern vom .NET Framework benutzt werden). Suspended: Der Thread wurde unterbrochen. SuspendRequested: Dem Thread wurde eine Anforderung zur Unterbrechung

geschickt.

Unstarted: Der Thread wurde erzeugt, aber noch nicht gestartet. WaitSleepJoin: Der Thread ist blockiert und daher unzugnglich, bis eine bestimmte Bedingung erfllt ist (gleich mehr dazu).

630

Eine Multithreading-Anwendung erstellen

Sie knnen sich denken, dass sich Ihr Hauptthread wahrscheinlich im Running-Zustand befindet, sobald die entsprechende Anwendung gestartet ist. Abbildung 19.3 zeigt das Ergebnis des Listings 19.1.

Abbildung 19.3: Die Eigenschaft eines typischen Hauptthreads.

Zu seiner Erstellung mssen Sie dem Thread etwas zum Ausfhren geben, mit anderen Worten: eine Methode. Sie stellt so etwas wie den Ereignishandler fr den Thread dar. Sobald ein Thread startet, wird diese Methode ausgefhrt. Die Verknpfung zwischen Thread und Methode wird von einem ThreadStart-Delegaten gehandhabt. Beispielsweise so:
Thread thdNewThread = new Thread(new ThreadStart(MyMethod)); MyMethod ist die Methode, die unser neuer Thread ausfhrt, sobald er startet. Der ThreadStart-Delegat gleicht jedem anderen Delegaten, der ein Ereignis auf eine Methode verweist. Htte die Thread-Klasse ein Start-Ereignis, knnte man die obige Zeile wie folgt

schreiben:
Thread thdNewThread = new Thread(); thdNewThread.Start += new ThreadStart(this.MyMethod);

Doch dieses Start-Ereignis gibt es leider nicht, so dass die vorigen zwei Statements zu einem zusammengefasst werden mssen, wie in der obigen Einzelzeile zu sehen. Um den Thread zu starten, ruft man die Start-Methode auf:
thdNewThread.Start();

Dieser Thread tut nichts, bis man Start aufruft.

631

Multithreading-Anwendungen

Da Sie nun mit ein paar grundlegenden Eigenschaften von Threads vertraut sind, schauen wir uns eine einfache Multithreading-Anwendung an. Listing 19.2 zeigt eine Applikation, die einen neuen Thread erzeugt. Die Anwendung demonstriert die Eigenheiten beim Umgang mit mehreren Threads in Windows Forms. Listing 19.2: Die Reihenfolge der Threadausfhrung
1: using System; 2: using System.Windows.Forms; 3: using System.Drawing; 4: using System.Threading; 5: 6: namespace TYWinforms.Day19 { 7: public class Listing192 : Form { 8: private Label lblInfo = new Label(); 9: private Button btStart = new Button(); 10: 11: public Listing192() { 12: lblInfo.Size = new Size(300,100); 13: lblInfo.Location = new Point(10,75); 14: 15: btStart.Location = new Point(100,10); 16: btStart.Text = "Start!"; 17: btStart.Click += new EventHandler(this.StartIt); 18: 19: Thread.CurrentThread.Name = "Hauptthread"; 20: 21: this.Text = "Listing 19.2"; 22: this.Controls.Add(lblInfo); 23: this.Controls.Add(btStart); 24: } 25: 26: private void StartIt(Object Sender, EventArgs e) { 27: lblInfo.Text = "Thread 2 gestartet von " +  Thread.CurrentThread.Name + "...\n"; 28: 29: Thread thdTest = new Thread(new ThreadStart(DoIt)); 30: thdTest.Name = "Thread 2"; 31: 32: thdTest.Start(); 33: lblInfo.Text += Thread.CurrentThread.Name + " schlft...\n"; 34: Thread.Sleep(2000); 35: lblInfo.Text += Thread.CurrentThread.Name + " erwacht...\n"; 36: thdTest.Join(); 37:

632

Eine Multithreading-Anwendung erstellen

38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51:

lblInfo.Text += "Thread 2 endet...\n"; } private void DoIt() { Thread.Sleep(1000); MessageBox.Show(Thread.CurrentThread.Name); lblInfo.Text += "Hallo von " + Thread.CurrentThread.Name + "\n"; } public static void Main() { Application.Run(new Listing192()); } } }

Der Konstruktor in den Zeilen 11 bis 24 richtet zwei Steuerelemente ein: ein Bezeichnungsfeld (Label) und eine Schaltflche (Button). Die Schaltflche wird benutzt, um einen neuen Thread anzulegen. Die Name-Eigenschaft des Hauptthreads wird in Zeile 19 auf Hauptthread gesetzt. Dadurch knnen wir ihn spter leichter finden. Werfen wir einen Blick auf StartIt, den Ereignishandler des Button-Steuerelements, in Zeile 26. Diese Methode ist relativ kompliziert, so dass wir frs Erste nur die Syntax untersuchen; zur Funktionalitt kommen wir gleich. Zuerst schreibt diese Methode etwas Text in das Label-Steuerelement. In Zeile 29 wird ein neuer Thread erzeugt, der die DoIt-Methode ausfhren soll. Zeile 30 legt den Namen des Threads fest und in Zeile 32 wird der Thread gestartet. Im Label-Steuerelement wird etwas mehr Text angezeigt und die SleepMethode aufgerufen. Diese hlt den Thread fr den (in Millisekunden) angegebenen Zeitraum an. In Zeile 35 wird noch mehr Text in das Label-Steuerelement geschrieben, in Zeile 36 die geheimnisvolle Join-Methode aufgerufen und schlielich in Zeile 38 weiterer Text angezeigt. Join veranlasst den aktuellen Thread zu warten, bis ein anderer Thread mit seiner jeweiligen Aufgabe fertig ist. So wartet etwa in Zeile 36 der Hauptthread, bis Thread 2 seine Verarbeitungsaufgabe beendet hat, und fhrt dann mit seiner eigenen Ausfhrung fort. Weil in Zeile 36 Join aufgerufen wird, muss der Hauptthread warten, bis Thread 2 seine Pflicht erfllt hat. In diesem Fall muss er warten, bis eine MessageBox angezeigt und wieder geschlossen wird, bevor er fortfahren kann. Die DoIt-Methode in Zeile 41 ist recht einfach; sie veranlasst den aktuellen Thread, fr eine Sekunde zu schlafen, zeigt eine MessageBox an und stellt dann etwas Text dar.

633

Multithreading-Anwendungen

Wenn Sie diese Anwendung ausfhren und auf die Schaltflche klicken, erhalten Sie die folgende Ausgabe im Bezeichnungsfeld:
(1 Sekunde vergeht) (MessageBox wird mit dem Text "Thread 2" angezeigt) (Applikation wartet, bis MessageBox geschlossen ist) Thread 2 gestartet von Main Thread... Main thread schlft... Main thread erwacht... Hallo von Thread 2 Thread 2 endet...

Was geht hier vor sich? Abbildung 19.4 zeigt die theoretische Reihenfolge der Ausfhrung.
Schaltflche hier gedrckt Zeit (in Sekunden) 0 Schriftfeld zeigt an: Thread 2 vom Haupt-Thread gestartet Thread 2 gestartet Thread 2 schlft 1 Sekunde Schriftfeld zeigt an: Haupt-Thread schlft Haupt-Thread schlft 2 Sekunden Schriftfeld zeigt an: Thread 2 beendet 1 Meldungsfenster zeigt an: Thread 2 Schriftfeld zeigt an: Hello from Thread 2 2 Schriftfeld zeigt an: Haupt-Thread erwacht Die Join-Methode wird ausgefhrt

Abbildung 19.4: Wie wir uns den Ablauf der Anwendung vorstellen

Wre das Ablaufdiagramm in Abbildung 19.4 korrekt, wrden wir jedoch die folgende Bildschirmausgabe erwarten:
Thread 2 gestartet von Main Thread... Main thread schlft... (1 Sekunde vergeht) (MessageBox wird mit dem Text "Thread 2" angezeigt) Hallo von Thread 2 (1 Sekunde vergeht) Main thread erwacht... Thread 2 endet...

Doch dies trifft nicht auf die tatschlichen Ereignisse zu. Um herauszufinden, was los ist, muss man wissen, dass Windows Forms-Steuerelemente keineswegs threadsicher sind: Weil die Steuerelemente via Threads nicht sicher miteinander kommunizieren knnen, reagieren sie nur auf den Thread, der sie erzeugte, und auf keinen anderen. Hier kann nur der Hauptthread auf das Label-Steuerelement zugreifen. Will das ein anderer Thread tun, muss er einen Befehl an den Hauptthread schicken, was als Marshalling eines Befehls bezeichnet wird. Der Hauptthread fhrt daraufhin diese gemarshallten Befehle aus (sobald er

634

Eine Multithreading-Anwendung erstellen

dazu kommt). Das ist der Grund, warum der von der DoIt-Methode in das Bezeichnungsfeld geschriebene Text erst nach den Dingen angezeigt wird, die nach ihm im Zeitablauf an der Reihe waren. Moment mal. Das Meldungsfeld wurde zur richtigen Zeit angezeigt eine Sekunde nach dem Start der Ausfhrung. Das liegt daran, dass das Meldungsfeld nicht im Hauptthread erzeugt wurde. Sein Besitzer ist der Thread 2 und das Meldungsfeld muss daher keine gemarshallten Befehle an den Hauptthread absetzen und darauf warten, dass diese ausgefhrt werden. Dieses Thema bringt eines der kompliziertesten Probleme aufs Tapet, auf das man stt, wenn man mit mehreren Threads zu tun hat: der Zugriff mehrerer Threads auf Objekte, die nur von einem Thread erstellt wurden. Leider gibt es keine Mglichkeit, dieses Problem zu umgehen. Alle Windows Forms-Steuerelemente sind nicht threadsicher, so dass jedes Mal, wenn ein Thread (auer dem erstellenden Thread) Zugriff auf ein Steuerelement bentigt, der Befehl gemarshallt werden muss. Damit ist der Thread der Ausfhrungsgewalt des Hauptthreads unterworfen ist und zwar selbst dann noch, wenn man die Priority-Eigenschaft des zweiten Threads auf Highest setzen wrde. Es ist also keine gute Idee, seine Steuerelemente gleichzeitig von mehr als einem Thread manipulieren zu lassen. Ihren Threads bleibt dennoch ein groer Spielraum brig und ein klassisches Beispiel wre eine Anwendung fr die Durchsuchung des Dateisystems. Werfen Sie einen Blick auf die Suchfunktion in Windows (START/SUCHEN). Gibt man ein paar Suchbegriffe ein und klickt dann auf die Schaltflche STARTEN, durchsucht das Hilfsprogramm die Festplatte nach Dateien mit den gesuchten Begriffen. Die Benutzeroberflche bleibt jedoch empfnglich fr Benutzereingaben. Wre dies jedoch ein singlethreaded Hilfsprogramm, wrde die Benutzeroberflche erstarren, whrend sie darauf wartet, dass der Suchvorgang beendet ist. Doch dank mehrerer Threads kann man weitere Suchbegriffe eingeben oder gar die Suche abbrechen, whrend sie noch im Hintergrund abluft. Ein weiteres Beispiel ist die automatische Rechtschreibprfung in Microsoft Word. Beim Tippen prft sie Ihre Orthografie im Hintergrund, damit sie automatisch ein falsch geschriebenes Wort entdecken kann, ohne dass dabei die Anwendung langsamer wird oder gar einfriert. Auch dies ist auf mehrere Threads zurckzufhren.

Threads miteinander synchronisieren


Unter Synchronisierung versteht man den Vorgang, bei dem sichergestellt wird, dass alle Threads wissen, wer gerade das Vorfahrtsrecht hat. Synchronisiert man etwas im wirklichen Leben, sorgt man dafr, dass jeder auf dem gleichen Stand ist. Wenn Sie z.B. Ihre Uhrzeit mit der Ihres Freundes synchronisieren (von chronos = Zeit), stellt jeder die Uhr

635

Multithreading-Anwendungen

auf den gleichen Zeitwert. Das Gleiche gilt fr die Threadsynchronisierung. Whrend der Synchronisierung informieren sich alle Threads ber den aktuellen Stand der Dinge (mit besonderer Bercksichtigung von Code- und Datenzugriff). Doch was bedeutet die Synchronisierung von Code- oder Datenzugriff eigentlich? Stellen Sie sich vor, Sie und zwei Ihrer Freunde wrden einen Wagen kaufen, um ihn sich zu teilen. Doch normalerweise kann nur einer von Ihnen am Steuer sitzen, und die anderen mssen warten, bis sie an der Reihe sind. Daher mssen Sie und Ihre Freunde Ihre Fahrzeiten aufeinander abstimmen. Springen Sie zuerst auf den Fahrersitz, so teilen Sie Ihren Freunden auf diese Weise mit, dass Sie zuerst da waren und die anderen warten mssen. Wenn Sie mit Fahren fertig sind, dann teilen Sie ihnen dies mit, und die anderen knnen sich mit Fahren abwechseln. Diesen Vorgang kann man aus zwei Perspektiven betrachten. Im einen Fall bringen Sie die Methoden in Einklang, die zum Fahren des Wagens eingesetzt werden. Im anderen bringen Sie den Wagen selbst in bereinstimmung. Anders ausgedrckt: Codesynchronisierung gegenber Datensynchronisierung. Spter wird auch dies ein wenig mehr Sinn ergeben. Beim Threading besteht das Ziel darin, Code auszufhren oder auf Daten bzw. ein Objekt zuzugreifen. Drei Threads nmlich Sie und Ihre zwei Freunde konkurrieren miteinander um die Ausfhrung des gleichen Programmcodes (symbolisiert durch das Fahren des Wagens). Ohne Synchronisierung wrden alle drei Threads versuchen, gleichzeitig auf den Fahrersitz zu gelangen, mit unvorhersehbaren Folgen. Ein Drittel der Zeit wrde der erste Thread laufen, whrend die anderen beiden um den Rest der Zeit konkurrieren wrden. Sie knnten nicht sagen, welcher Thread gerade luft. (Sie und Ihre Freunde sitzen gleichzeitig hinterm Steuer.) Noch schlimmer wre es, wenn dieser bestimmte Code einige Variablen erreichen und modifizieren wrde, von denen Ihre Anwendung abhngt (im Beispiel wre dies der Wagen). Jeder Thread mchte diese Variablen fr sich selbst reklamieren, wird sie aber nicht behalten knnen, was zu einer gewissen Unordnung fhren drfte und wahrscheinlich zum Absturz Ihrer Anwendung (dem Unfall des Wagens; vgl. Abbildung 19.5). Wrde jeder Ihrer Freunde versuchen, den Wagen fr sich zu behalten, wrde dieser garantiert einen Unfall erleiden. Wenn Sie jedoch Ihre Threads aufeinander abstimmen, befehlen Sie allen Threads, darauf zu warten, bis jeder an die Reihe kommt. Der erste Thread, der den fraglichen Code erreicht, verhngt ber ihn eine Sperre, und alle anderen Threads haben diese Sperre zu respektieren. Bis zur Aufhebung der Sperre kann nur der sperrende Thread den Code ausfhren. Nun knnen Sie vorhersehbare Ergebnisse haben, denn man wei stets, wer der Boss ist. Der Verkehrsunfall in Abbildung 19.5 wird vermieden, statt dessen sieht Ihre Lage nun eher wie in Abbildung 19.6 aus.

636

Eine Multithreading-Anwendung erstellen

Thread 2: setze x = Eva Thread 1: setze x = Chris Thread 3: setze x = Walter

?
Sensibler Code: setze Meine Variable = x

Abbildung 19.5: Wrde man die Threads nicht synchronisieren, knnten sie versuchen, gleichzeitig auf dieselben Objekte zuzugreifen. Das wrde zu einem Chaos fhren.

Threading-Anfnger verwechseln hufig Synchronisierung und threadbergreifendes Marshalling von Befehlen. Bei der Synchronisierung geht es um ein Stck Code, das ausgefhrt wird, oder um ein Objekt, auf das ein Thread zugreift beides findet innerhalb eines einzelnen Threads statt. Beim Marshalling von Befehlen hingegen versucht man, auf ein Objekt oder eine Variable ber mehrere Threads hinweg zuzugreifen. Whrend der fragliche Code versuchen knnte, auf ein bestimmtes Objekt zuzugreifen, kann dieses Objekt trotzdem einem Marshalling von Befehlen unterworfen sein; Synchronisierung und Marshalling sind zwei unterschiedliche Vorgehensweisen. Gehen Sie nicht davon aus, dass Sie die Probleme, die hinsichtlich des Marshallings von Befehlen errtert wurden, lsen knnen, indem Sie eine Sperre ber ein Objekt verhngen das wird nicht funktionieren und ergibt im Grunde auch keinen Sinn.
Thread 2: setze x = Eva Thread 1: setze x = Chris Thread 3: setze x = Walter

Sensibler Code: setze Meine Variable = x

Abbildung 19.6: Mit Synchronisierung darf nur noch ein Thread auf einmal eintreten und alle anderen werden ausgesperrt.

Bevor wir untersuchen, wie man Threads synchronisiert (ab Listing 19.4), werfen wir kurz einen Blick auf ein Beispiel, das keinerlei Synchronisierung aufweist. Listing 19.3 erzeugt drei verschiedene Threads, die versuchen, etwas in einem Bezeichnungsfeld anzuzeigen, mit recht interessanten Ergebnissen.

637

Multithreading-Anwendungen

Listing 19.3: Threading ohne Synchronisierung


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: 42: 43: 44: using using using using System; System.Windows.Forms; System.Drawing; System.Threading;

namespace TYWinforms.Day19 { public class Listing193 : Form { private Label lblInfo = new Label(); private Button btStart = new Button(); public Listing193() { lblInfo.Size = new Size(300,100); lblInfo.Location = new Point(10,75); btStart.Location = new Point(100,10); btStart.Text = "Start!"; btStart.Click += new EventHandler(this.StartIt); this.Text = "Listing 19.3"; this.Controls.Add(lblInfo); this.Controls.Add(btStart); } private void StartIt(Object Sender, EventArgs e) { lblInfo.Text = ""; Thread thdTest1 = new Thread(new ThreadStart(DoIt)); thdTest1.Name = "Thread 1"; thdTest1.Start(); Thread thdTest2 = new Thread(new ThreadStart(DoIt)); thdTest2.Name = "Thread 2"; thdTest2.Start(); Thread thdTest3 = new Thread(new ThreadStart(DoIt)); thdTest3.Name = "Thread 3"; thdTest3.Start(); } private void DoIt() { lblInfo.Text += Thread.CurrentThread.Name + " started...\n"; Thread.Sleep(100); lblInfo.Text += Thread.CurrentThread.Name + " ending...\n"; }

638

Eine Multithreading-Anwendung erstellen

45: 46: 47: 48: 49:

public static void Main() { Application.Run(new Listing193()); } } }

Dieses Listing gleicht Listing 19.2. Ein Bezeichnungsfeld und eine Schaltflche werden im Konstruktor in den Zeilen 11 bis 22 angelegt, und der Ereignishandler der Schaltflche, StartIt, beginnt in Zeile 24. Zeile 26 erzeugt einen neuen Thread und verweist ihn auf die DoIt-Methode. In Zeile 27 wird der Name des Threads auf Thread 1 festgelegt, danach wird der Thread gestartet. Die Zeilen 30 bis 32 und 34 bis 36 wiederholen diesen Vorgang mit zwei weiteren Threads, die natrlich Thread 2 und Thread 3 heien. Die DoIt-Methode in Zeile 39 ist recht einfach: Sie zeigt etwas Text im Bezeichnungsfeld an, wartet eine Zehntelsekunde und zeigt noch etwas mehr Text an. Wird nun diese Anwendung gestartet, sind die Ergebnisse unvorhersehbar. Klickt man mehrmals auf die Schaltflche, bekommt man jedes Mal ein anderes Ergebnis. Man kann nie sagen, welcher Thread Text in das Bezeichnungsfeld schreiben wird. Abbildung 19.7 zeigt ein typisches Beispiel dafr. Eigentlich wollen wir wissen, wer beim nchsten Mal Text ausgibt, und noch lieber wre uns, wenn einer der Threads seine Verarbeitungsaufgabe beenden wrde, bevor der nchste Thread startet. Synchronisierung, bernehmen Sie! Die Lsung besteht darin, eine Sperre auf den Code in den Zeilen 40 bis 42 zu legen. Erreicht der erste Thread diesen Code, wird er eine Sperre einrichten, so dass die nchsten beiden Threads in der Warteschlange warten mssen (in der Reihenfolge ihrer Ankunft an der Sperre), bis die Sperre wieder aufgehoben wird. Auf diese Weise hat jeder Thread Zeit, den fraglichen Code vollstndig auszufhren, ohne sich darum kmmern zu mssen, was die anderen Threads gerade zu tun versuchen. In .NET gibt es eine Reihe von Mglichkeiten, Threads zu synchronisieren, doch wir besprechen hier nur die vorherrschende, welche den Einsatz der Monitor-Klasse vorsieht. Diese ist recht interessant, da sie weder ber Eigenschaften noch Ereignisse verfgt und man sie nicht ausdrcklich anlegen kann. Vielmehr enthlt sie sechs statische Methoden, die zur Synchronisierung von Threads benutzt werden. Tabelle 19.1 fhrt diese Methoden auf.

639

Multithreading-Anwendungen

Abbildung 19.7: Die Reihenfolge, in der Text ausgegeben wird, lsst sich nicht vorhersagen. Methode
Enter

Beschreibung Legt eine Sperre auf ein Objekt. Ist die Sperre bereits gesetzt, wartet der Thread, bis die Sperre verfgbar wird. Hebt die Sperre auf einem Objekt auf. Benachrichtigt einen wartenden Thread von einer nderung hinsichtlich des Sperrzustands. Pulse lsst also einen anderen Thread, der Wait aufgerufen hat, wissen, dass er nicht mehr lange warten muss. Benachrichtigt alle wartenden Threads von einer nderung hinsichtlich des Sperrzustands. Versucht, eine Sperre fr ein Objekt zu erhalten, doch wenn sie bereits gesetzt ist, wartet der Thread nicht und diese Methode liefert false. Veranlasst den Verwalter einer Sperre, die Sperre aufzuheben und in die Warteschlange zurckzukehren, um dort auf den Erhalt einer weiteren Sperre zu warten. Wird benutzt, wenn ein Thread von der Ausfhrung eines anderen abhngt.

Exit Pulse

PulseAll

TryEnter

Wait

Tabelle 19.1: Methoden von Monitor

Die einfachste Mglichkeit, eine Sperre ber Code zu verhngen, besteht im Einsatz der Enter- und Exit-Methoden auf den fraglichen Code. ndern Sie beispielsweise die DoItMethode in Listing 19.3, dass sie wie folgt aussieht:
private void DoIt() { Monitor.Enter(this);

640

Eine Multithreading-Anwendung erstellen

lblInfo.Text += Thread.CurrentThread.Name + " started...\n"; Thread.Sleep(100); lblInfo.Text += Thread.CurrentThread.Name + " ending...\n"; Monitor.Exit(this); }

Wir haben zwei neue Zeilen hinzugefgt: Monitor.Enter(this) und Monitor.Exit(this). Wenn Sie jetzt die Anwendung starten, erhalten Sie jedes Mal die gleichen Ergebnisse, wie in Abbildung 19.8 zu sehen (je nach Ihrem System knnen die Threads 2 und 3 in anderer Reihenfolge erscheinen, doch stets sollten ihre Ausfhrungszeilen gruppiert sein).

Abbildung 19.8: Die Ausfhrung jedes Threads wird nun zu einer Gruppe zusammengefasst.

Die Enter- und Exit-Methoden bernehmen als Parameter das Objekt, auf das Sie die Sperre legen wollen. Obwohl also die Sperre einen Codeabschnitt schtzt, muss sie weiterhin auf ein echtes Objekt gelegt werden. Meistens drfte die Variable this (oder Me in VB .NET) gengen. Man kann aber auch die Sperre nur auf das Objekt legen, das man erreichen oder modifizieren mchte. Zum Beispiel so:
Monitor.Enter(lblInfo);

Sowohl C# als auch VB .NET haben ihre eigene Syntax fr das Einrichten von Sperren. C# verfgt ber das Schlsselwort Lock:
lock(this) { ... }

Und VB .NET besitzt das Schlsselwort SyncLock:


SyncLock (Me) ... End SyncLock

641

Multithreading-Anwendungen

Beide Methoden arbeiten auf genau die gleiche Weise wie beim Einsatz der Monitor-Klasse. Tatschlich hngen sie sogar von dieser ab, um richtig zu funktionieren. Die unterschiedliche Syntax wird benutzt, um Benutzern frherer Versionen dieser Programmiersprachen die Migration zu erleichtern. Schauen wir uns ein etwas realistischeres Beispiel fr Multithreading und Synchronisierung an. Listing 19.4 zeigt die Grundlage fr eine Anwendung zum Durchsuchen von einer oder mehreren Website(s). Der Benutzer kann Suchbegriffe selbst dann eingeben, wenn der Suchvorgang gerade abluft. Die Ergebnisse jedes Suchvorgangs werden in einem normalen Label-Steuerelement angezeigt. Listing 19.4: Eine synchronisierte Multithreading-Suchanwendung in VB .NET
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: Imports Imports Imports Imports System System.Windows.Forms System.Drawing System.Threading

namespace TYWinforms.Day19 public class Listing194 : Inherits Form private lblInfo As new Label() private tbSearch As new TextBox() private btStart As new Button() private intCounter As integer public sub New() intCounter = 0 lblInfo.Size = new Size(300,100) lblInfo.Location = new Point(10,75) tbSearch.Location = new Point(10,10) btStart.Location = new Point(120,10) btStart.Text = "Suche!" AddHandler btStart.Click, new EventHandler(AddressOf StartIt) Me.Text = "Listing 19.4" Me.Controls.Add(lblInfo) Me.Controls.Add(btStart) Me.Controls.Add(tbSearch) End Sub private sub StartIt(Sender As Object, e As EventArgs) intCounter += 1

642

Eine Multithreading-Anwendung erstellen

33: 34: dim thdSearch As New Thread(new ThreadStart (AddressOf Search)) 35: thdSearch.Name = "Suche " & intCounter 36: thdSearch.Start() 37: End Sub 38: 39: private sub Search() 40: Suchvorgang durchfhren 41: Thread.Sleep(2000) 42: 43: Monitor.Enter(Me) 44: 45: dim rnd As new Random() 46: dim i as integer 47: for i = 0 to 4 48: lblInfo.Text += Thread.CurrentThread.Name & ": " & rnd.Next &  Microsoft.VisualBasic.vbCrLf 49: next i 50: 51: Monitor.Exit(Me) 52: End Sub 53: 54: public shared sub Main() 55: Application.Run(new Listing194()) 56: End Sub 57: End Class 58: End Namespace

Der Aufbau dieses Listings ist im Grunde der gleiche wie in den Listings 19.2 und 19.3. In der Benutzeroberflche liegen ein Bezeichnungsfeld, eine Schaltflche und ein Textfeld vor. Der Click-Ereignishandler der Schaltflche ist die StartIt-Methode in Zeile 31. In StartIt erhhen Sie einen Zhler, der dem Benutzer mitteilt, wie viele Suchvorgnge bereits durchgefhrt wurden (Zeile 32), und erzeugen einen neuen Thread, dessen Name auf diesem Zhlerstand basiert (Zeilen 34 bis 35). Sobald die SUCHEN-Schaltflche angeklickt wurde, wird jedes Mal ein neuer Thread erzeugt und zusammen mit der Search-Methode ab Zeile 39 gestartet. Den Suchmechanismus haben wir hier eigentlich nicht erstellt (das ist Ihre Hausaufgabe), aber in Zeile 41 legen wir den Thread fr zwei Sekunden schlafen, um die Suche zu simulieren. Beachten Sie, dass dieser Befehl sich auerhalb jeglichen gesperrten Codes befindet. Befnde sich dieser Code innerhalb eines Sperrbereichs, dann msste jeder Suchvorgang so lange warten, bis der vorhergehende Suchvorgang beendet ist, damit er selbst mit der Suche beginnen knnte. Dies widersprche aber dem Zweck einer Multithreaded-Suchan-

643

Multithreading-Anwendungen

wendung, denn wir wollen die Suchvorgnge ja parallel ausfhren lassen, so dass wir eine hhere Reaktionsgeschwindigkeit der Anwendung erzielen. Die Zeilen 49 bis 51 werden nach dem Abschluss der Suchvorgnge ausgefhrt und zeigen lediglich eine Meldung im Bezeichnungsfeld an, wie viele Ergebnisse geliefert wurden. Weil die Mglichkeit besteht, dass viele Threads das Label-Steuerelement auf einmal ndern wollen (und weil die Suchergebnisse gruppiert werden sollen), platzieren wir diesen Code mit Hilfe der Methode Monitor.Enter unter eine Sperre. Um die Anzahl der gelieferten Suchergebnisse zu simulieren, wird mit Hilfe der Random-Klasse eine Zufallszahl generiert und im Label-Steuerelement angezeigt. Zum Schluss wird in Zeile 51 die Exit-Methode aufgerufen. Htten wir nicht die Sperrmechanismen eingesetzt, knnten wir nicht sicher sein, dass die Ergebnisse jedes Suchvorgangs gruppiert wrden. Abbildung 19.9 zeigt ein Beispielergebnis.

Abbildung 19.9: Eine Multithreading-Suchfunktion muss das Bezeichnungsfeld, das die Suchergebnisse ausgibt, sperren knnen.

Es gibt auer Monitor zwei weitere gngige Methoden, Code zu sperren. Die erste basiert auf der Klasse Mutex, die hnlich wie die Monitor-Klasse arbeitet, aber weitaus weniger Funktionsumfang bereitstellt. Zur Synchronisierung mit Mutex gehrt das bergeben eines bestimmten Mutex-Objekts zwischen einzelnen Threads; der Thread, der gerade das MutexObjekt hat, ist der einzige, dem die Ausfhrung von Code gestattet ist. Die zweite Methode basiert auf dem Einsatz der ReaderWriterLock-Klasse. Je nachdem, was Ihre Anwendung tun muss, setzt diese Klasse zwei verschiedene Arten von Sperren ein. Sie bietet eine Sperre fr einen Writer und mehrere Reader, was bedeutet, dass nur ein einziger Thread gesperrte Objekte modifizieren kann, doch alle anderen Threads weiterhin

644

Eine Multithreading-Anwendung erstellen

darauf zugreifen knnen (solange sie nicht versuchen, die Objekte zu verndern). Je nach Lage der Dinge kann dieses Vorgehen die Leistung Ihrer Anwendung erhhen. (Mehr Informationen ber diese Sperrtypen finden Sie in der Dokumentation zum .NET Framework.)

Threadpooling einsetzen
Ein Threadpool ist einfach eine Menge von inaktiven Threads, und Threadpooling ist der Vorgang, diese Threads einzusetzen. In einem Multithreading-Betriebssystem ist es eine Tatsache, dass ein Thread die meiste Zeit mit Nichtstun verbringt; jeder Thread wartet entweder darauf, eine Sperre auf ein Objekt legen zu knnen oder dass ein Ereignis eintritt. Whrend dieses Miggangs ist auch der Prozessor meist zum Nichtstun verurteilt; dennoch muss das Betriebssystem Kapazitten fr die Aufrechterhaltung des Threads reservieren, doch da dieser nichts tut, erzielt dies auch keinen Leistungszuwachs fr Ihre Anwendung. Technisch ausgedrckt, erlaubt Threadpooling dem Betriebssystem, die Threads zu kontrollieren. Verfllt ein Thread in Miggang und der Prozessor hat nichts mehr zu tun, gestattet das Betriebssystem einem anderen Thread, an die Stelle des ersten zu rcken, um so die CPU auf Trab zu halten. Threadpooling ist ein ganz normaler Vorgang in Ihrem Betriebssystem. Man kann es fr seine Anwendungen ausnutzen, so dass man als Entwickler nicht mehr selbst alle Threads einzeln verwalten muss; Erstellung, Aufrechterhaltung und Zerstrung von Threads handhabt das Betriebssystem. Man teilt diesem einfach mit, welcher Code mit einem Thread ausgefhrt werden soll, und es handhabt den Prozess, einen bestimmten Thread zuzuweisen und sobald diese Aufgabe ausgefhrt ist, diesen Thread wieder loszuwerden. Darin ist das Betriebssystem ziemlich effizient. Um Threadpooling in Anwendungen auszunutzen, setzt man die ThreadPool-Klasse ein. Mit Hilfe der statischen QueueUserWorkItem-Methode knnen Sie dem Betriebssystem sagen, dass Sie fr eine bestimmte Aufgabe einen Thread bentigen. Das System handhabt alle Zuweisungs- und Ausfhrungsaufgaben bezglich Threads selbst. Wenn der Thread mit seiner Arbeit fertig ist, kann er Ihren Hauptthread entsprechend benachrichtigen. Der Unterschied zwischen dem Einsatz eines ThreadPool-Objekts und normalen ThreadObjekten besteht darin, dass die Thread-Klasse mehr Flexibilitt bietet. Man kann so etwa seine Threads benennen und sie mit Thread individuell identifizieren. Beim Einsatz von ThreadPool kann man eigentlich nichts direkt mit bestimmten Threads tun, doch es ist im Allgemeinen effizienter hinsichtlich der Aufrechterhaltung von Threads. Bevor wir uns ein Beispiel anschauen, mssen Sie ber den Delegaten WaitCallBack Bescheid wissen, einem Helfer der ThreadPool-Klasse. WaitCallBack gleicht dem ThreadStart-Delegaten: Er verweist einen Thread auf eine auszufhrende Methode. Der einzige

645

Multithreading-Anwendungen

Unterschied: Die so von WaitCallBack angezeigte Methode muss ein Objekt als Parameter bernehmen. Dieses Objekt lsst sich unter anderem dazu verwenden, einen bestimmten Thread darzustellen (mehr dazu spter). Listing 19.5 zeigt ein einfaches Beispiel, wie man die ThreadPool-Klasse einsetzt. Listing 19.5: ThreadPool verwenden
1: Imports System 2: Imports System.Windows.Forms 3: Imports System.Drawing 4: Imports System.Threading 5: 6: namespace TYWinforms.Day19 7: public class Listing195 : Inherits Form 8: private lblInfo As new Label() 9: private btStart As new Button() 10: 11: public sub New() 12: lblInfo.Size = new Size(300,200) 13: lblInfo.Location = new Point(10,75) 14: 15: btStart.Location = new Point(100,10) 16: btStart.Text = "Start!" 17: AddHandler btStart.Click, new EventHandler(AddressOf StartIt) 18: 19: Me.Text = "Listing 19.5" 20: Me.Controls.Add(lblInfo) 21: Me.Controls.Add(btStart) 22: End Sub 23: 24: private sub StartIt(Sender As Object, e As EventArgs) 25: dim i as integer 26: 27: for i = 0 to 10 28: ThreadPool.QueueUserWorkItem(new WaitCallBack (AddressOf DoIt)) 29: next i 30: End Sub 31: 32: private sub DoIt(state As Object) 33: lblInfo.Text += "Gruss aus dem Threadpool!" &  Microsoft.VisualBasic.vbCrLf 34: End Sub 35: 36: public shared sub Main() 37: Application.Run(new Listing195())

646

Eine Multithreading-Anwendung erstellen

38: 39: 40:

End Sub End Class End Namespace

Dieses Listing verwendet das gleiche Gerst wie alle anderen Listings heute. Nur durch die StartIt-Methode in Zeile 24 und die DoIt-Methode in Zeile 32 unterscheidet es sich etwas. In der StartIt-Methode erzeugen Sie eine einfache Schleife. Diese ruft die Methode QueueUserWorkItem elfmal auf, wobei sie jedes Mal einen WaitCallBack-Delegaten bergibt, der auf die DoIt-Methode weist. Als Ergebnis schnappt sich das ThreadPool-Objekt elf Threads, von denen jeder die jeweilige DoIt-Methode so effizient wie mglich ausfhrt. Diese schreibt lediglich eine Meldung in das Label-Steuerelement (Zeile 33). Abbildung 19.10 zeigt das Ergebnis dieses Listings.

Abbildung 19.10: ThreadPool reiht jeden Thread in eine Warteschlange ein und steuert ihn.

Jeder von der ThreadPool-Klasse gehandhabte Thread wird automatisch gestartet, aufrechterhalten, gestoppt und (notfalls) zerstrt. Mit Hilfe der ThreadPool-Klasse kann man die Zeilen 32 bis 36 des Listings 19.4 mit nur einer Zeile ersetzen:
ThreadPool.QueueUserWorkItem(new WaitCallBack(AddressOf Search))

Ihre Anwendung wird effizienter ausgefhrt. Auch die erwhnten Synchronisierungstechniken gelten noch, wenn man die ThreadPool-Klasse einsetzt, so dass man nichts zustzlich tun muss. Erinnern Sie sich an den Parameter fr die DoIt-Methode in Zeile 32 des Listings 19.5? Sie knnen ihn fr fast alles einsetzen, was Sie wollen: Es handelt sich um ein generisches

647

Multithreading-Anwendungen

Objekt, das bergeben wird, um Ihren Threads zu helfen. Um einen echten Wert dafr festzulegen, muss man den Aufruf von QueueUserWorkItem ein klein wenig ndern. Wir untersuchen zwei Einsatzmglichkeiten fr dieses Objekt. Erstens lsst sich das Objekt einsetzen, um einen bestimmten Thread zu identifizieren. ndern Sie z.B. Zeile 28 so, dass sie wie folgt lautet:
ThreadPool.QueueUserWorkItem(new WaitCallBack(AddressOf DoIt), i.ToString())

Wir bergeben der QueueUserWorkItem-Methode einen zustzlichen Parameter. Da die DoIt-Methode einen Object-Datentyp erwartet, knnen wir etwas Beliebiges bergeben (da alle Typen in .NET von Object abgeleitet sind). i ist die Schleifenzhlervariable, die wir verwendet haben, um elf Threads zu erzeugen. Diese neue Zeile bergibt eine Zeichenfolgendarstellung der aktuellen Threadanzahl in der Warteschlange. ndern Sie Zeile 33 ein wenig:
lblInfo.Text +="Hello from thread" &&state.ToString()&Microsoft.VisualBasic.vbCrLf

Der im Aufruf der QueueUserWorkItem-Methode bergebene Wert wird in den State-Parameter der DoIt-Methode geleitet. Hier rufen wir lediglich noch einmal die ToStringMethode auf, um das Object in eine Zeichenfolge umzuwandeln und eine fr jeden Thread individuelle Zeichenfolge auszugeben. Die Bildschirmausgabe sieht auf Grund dieser nderungen so aus:
Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello from from from from from from from from from from from thread thread thread thread thread thread thread thread thread thread thread 0 1 2 3 4 5 6 7 8 9 10

Sie verfgen nun ber eine etwas rudimentre Mglichkeit, jeden einzelnen Thread zu identifizieren. Die zweite Einsatzmglichkeit fr den state-Parameter besteht darin, andere Threads davon in Kenntnis zu setzen, dass ein Thread mit seiner Ausfhrung fertig ist. Dazu wird die Klasse AutoResetEvent eingesetzt. AutoResetEvent bietet Methoden, die einen Thread veranlassen, so lange auf einen anderen zu warten, bis ein Flag gesetzt ist. Stellen Sie sich AutoResetEvent wie eine Art Toaster vor. Der wartende Thread muss abwarten, dass der Toast in die Hhe schiet, bis er mit seiner Aufgabe fortfahren kann. Das Listing 19.6 zeigt ein Beispiel.

648

Eine Multithreading-Anwendung erstellen

Listing 19.6: Threads in einem Threadpool benachrichtigen


1: Imports System 2: Imports System.Windows.Forms 3: Imports System.Drawing 4: Imports System.Threading 5: 6: namespace TYWinforms.Day19 7: public class Listing196 : Inherits Form 8: private lblInfo As new Label() 9: private btStart As new Button() 10: private objAREvent As new AutoResetEvent(false) 11: private intCounter As Integer = 10 12: 13: public sub New() 14: lblInfo.Size = new Size(300,200) 15: lblInfo.Location = new Point(10,75) 16: 17: btStart.Location = new Point(100,10) 18: btStart.Text = "Start!" 19: AddHandler btStart.Click, new EventHandler (AddressOf StartIt) 20: 21: Me.Text = "Listing 19.6" 22: Me.Controls.Add(lblInfo) 23: Me.Controls.Add(btStart) 24: End Sub 25: 26: private sub StartIt(Sender As Object, e As EventArgs) 27: dim i as integer 28: 29: for i = 0 to 10 30: ThreadPool.QueueUserWorkItem(new WaitCallBack(AddressOf DoIt),  objAREvent) 31: next i 32: 33: objAREvent.WaitOne() 34: lblInfo.Text += "Fertig!" 35: End Sub 36: 37: private sub DoIt(state As Object) 38: Thread.Sleep(2000) 39: lblInfo.Text += "Arbeite..." & Microsoft.VisualBasic.vbCrLf 40: 41: if intCounter = 0 then 42: CType(state, AutoResetEvent).Set() 43: else

649

Multithreading-Anwendungen

44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54:

Monitor.Enter(Me) intCounter = intCounter - 1 Monitor.Exit(Me) end if End Sub public shared sub Main() Application.Run(new Listing196()) End Sub End Class End Namespace

Dieses Gerst sehen wir uns nun ein letztes Mal an. In den Zeilen 10 und 11 erzeugen wir zwei neue Variablen: ein AutoResetEvent-Objekt und einen Zhler, der angibt, wie viele Threads Sie in dieser Anwendung erzeugen. Der Parameter fr den AutoResetEvent-Konstruktor zeigt den Anfangszustand des Objekts an; wird false bergeben, so heit das, dass es noch nicht bereit ist (der Toast wurde noch nicht ausgeworfen). Die nchste Vernderung gegenber Listing 19.5 findet sich in Zeile 30. Nun bergeben wir das neu erzeugte AutoResetEvent-Objekt der QueueUserWorkItemMethode. Die DoIt-Methode in Zeile 37 kann auf das AutoResetEvent-Objekt mit Hilfe ihres state-Parameters zugreifen. In Zeile 33 wird die WaitOne-Methode des AutoResetEvent-Objekts aufgerufen. Dies bringt den Hauptthread dazu zu warten, bis das AutoResetEvent-Objekt ihm sagt, dass er weitermachen kann (wenn der Toast ausgeworfen wird). Das Signal dazu wird gegeben, indem die Set-Methode des AutoResetEvent-Objekts aufgerufen wird (siehe nchster Absatz). Nun wird gewartet, bis alle anderen Threads ihre Ausfhrung beendet haben, bevor der Hauptthread fortgesetzt wird. Die StartIt-Methode zeigt im Bezeichnungsfeld etwas Text an, der angibt, dass alle Ausfhrungen beendet wurden. Zeile 38 legt den Thread fr zwei Sekunden schlafen und etwas Text wird im Bezeichnungsfeld angezeigt (Zeile 39). Die Zeilen 41 bis 47 enthalten etwas Interessantes. Wenn alle Threads beendet wurden, wollen wir die Set-Methode des AutoResetEvent-Objekts aufrufen. Die Anzahl der aktuell laufenden Threads wird durch die intCounter-Variable angegeben. Gibt es noch laufende Threads (dann ist intCounter grer null), mssen wir intCounter jedes Mal um eins verringern, wenn ein Thread seine Arbeit beendet hat. Man sollte hier nicht den Einsatz von Synchronisierung vergessen; mehr als ein Thread greift auf die intCounter-Variable zu, so dass man einige Sperren bentigt (Zeile 44), um sicherzustellen, dass nur ein Thread nach dem anderen auf intCounter zugreift. Wird schlielich der letzte Thread erreicht, rufen wir die Set-Methode auf, und der Hauptthread aus Zeile 34 wird wieder aufgenommen.

650

Eine Multithreading-Anwendung erstellen

(Vergessen Sie bitte nicht, dass diese Anwendung immer noch dem Marshalling von Befehlen unterworfen ist, da das verwendete Label-Steuerelement nicht threadsicher ist. Daher wird im Bezeichnungsfeld erst eine Ausgabe zu sehen sein, wenn alle Threads ihre Ausfhrung beendet haben.) Abbildung 19.11 zeigt die Ausgabe aus Listing 19.6.

Abbildung 19.11: Die Klasse AutoResetEvent lsst sich dazu einsetzen, den Hauptthread zu kennzeichnen, nachdem alle anderen Threads ihre Ausfhrung beendet haben. ThreadPool bietet eine leichte und effiziente Mglichkeit, in einer Anwendung mehrere Threads einzusetzen, indem es unttige Threads nutzt. Mit der Methode ThreadPool.GetMaxThreads kann man herausfinden, wie viele Threads sich maximal gleichzeitig ausfhren lassen (wie viele Threads also fr sofortigen Einsatz zur Verfgung stehen). ThreadPool.GetAvailableThreads zeigt die Anzahl zustzlicher Threads an, die man in die Warte-

schlange stellen kann, bis einem die unttigen Threads ausgehen. Ist diese Anzahl erreicht, warten Schlange stehende Elemente, bis ein weiterer Thread verfgbar wird. Ich empfehle den Einsatz des ThreadPool-Objekts, es sei denn, man bentigt die Flexibilitt, die das Arbeiten mit individuellen Thread-Objekten erlaubt.
Ich empfehle Verwenden Sie das ThreadPool-Objekt, wenn Sie kleine Codeblcke effizient mit mehreren Threads ausfhren mssen. Bitte beachten Sie Setzen Sie ThreadPool nicht ein, wenn Sie einzelne Threads bearbeiten mssen (etwa um Prioritten oder Namen festzulegen) oder wenn Ihre Threads ein sehr umfangreiches und kompliziertes Stck Code ausfhren mssen. In solchen Fllen ist es effizienter, Threads selbst zu handhaben.

651

Multithreading-Anwendungen

Multithreaded Steuerelemente Ein multithreaded Steuerelement lsst sich ebenso leicht wie eine multithreaded Anwendung erstellen. Man legt eine neue Klasse an, die von der Klasse Control erbt, und folgt dann den Richtlinien fr den Einsatz von Threads. Sie knnen Ihre Steuerelemente weitaus reaktionsfreudiger machen, wenn Sie mehrere Threads einsetzen. So knnten Sie etwa die Multithreading-Suchanwendung als benutzerdefiniertes Steuerelement kapseln. Anwendungen, die solche Steuerelemente verwenden, brauchen sich um Multithreading-Probleme nicht zu kmmern, denn diese Funktionalitt ist ja im Steuerelement selbst gekapselt. Die Erstellung von multithreaded Steuerelementen heit jedoch nicht, dass man die Beschrnkungen, die einem threadbergreifendes Marshalling auferlegt, berwinden kann. Leider ist alles, das von Control abgeleitet ist, auf einen einzelnen Thread beschrnkt, wird es in einer Anwendung eingesetzt. Alle anderen Threads mssen weiterhin Aufrufe an den erzeugenden Thread marshallen. Kurz und gut: Wenn Sie wissen, wie Sie benutzerdefinierte Steuerelemente erstellen und mehrere Threads einsetzen, knnen Sie bereits auch multithreaded Steuerelemente anlegen.

19.3 Zusammenfassung
Wir haben heute eine Menge komplizierter Themen besprochen. blicherweise ist das Thema Multithreading nur fortgeschrittenen Benutzern vorbehalten, doch mit .NET und Ihrem Wissen ber Windows Forms lsst sich Multithreading leicht bewltigen. Ein Thread ist eine grundlegende Ausfhrungseinheit in einem Betriebssystem. Das Betriebssystem reserviert Kapazitten fr die Ausfhrung und Aufrechterhaltung von Threads, whrend die CPU ihre Zeit auf mehrere Threads, die in der Anwendung laufen, verteilt. Weil der Prozessor so schnell ist, sieht es so aus, als ob man mehrere Anwendungen gleichzeitig ausfhrte, doch in Wahrheit fhrt die CPU nur eine nach der anderen aus, wenn auch sehr rasch. In .NET dreht sich Threading vor allem um die Thread-Klasse. Damit knnen Sie neue Threads erzeugen, Prioritten und Namen zuweisen, Threads starten und beenden usw. Einem Thread muss ein Code zur Ausfhrung bergeben werden und dies erfolgt mit dem ThreadStart-Delegaten. Synchronisierung ist der Vorgang, mit dem sichergestellt wird, dass nur ein Thread ein bestimmtes Codeelement auf einmal ausfhrt. blicherweise setzt man Synchronisierung ein, wenn das fragliche Codeelement sensibel ist; das heit, wenn es auf solche Objekte

652

Fragen und Antworten

zugreift und sie verndert, von denen die Anwendung abhngt. Fr die Synchronisierung sperrt man seine Objekte mit Hilfe der Monitor-Klasse und ihrer Enter- und Exit-Methoden. Die ThreadPool-Klasse nutzt die Art und Weise aus, wie Threads arbeiten, um ihre Multithreading-Aufgaben effizienter zu erledigen. Die Hauptmethode dieser Klasse ist QueueUserWorkItem, welche einen WaitCallBack-Delegaten benutzt, um einen Thread zu starten. Sie knnen eine AutoResetEvent-Klasse verwenden, um Threads zu signalisieren, dass einer von ihnen seine Aufgabe beendet hat.

19.4 Fragen und Antworten


F Wie funktioniert Threading auf Multiprozessorsystemen? A Verfgt Ihr Rechner ber mehrere Prozessoren, so nutzen diese Threads nicht gemeinsam; vielmehr erhlt jeder Prozessor einen gewissen Prozentsatz der Gesamtzahl aller Threads und arbeitet nur mit diesem Kontingent. Wie Threading im Einzelnen implementiert wird, hngt von Ihrer jeweiligen Plattform ab.

Was versteht man unter einer statischen Threadvariablen? A Statische Threadvariablen gleichen normalen statischen Variablen. Letztere reprsentieren jeweils eine Variable oder Eigenschaft, die allgemein auf eine Klasse anwendbar ist, statt auf eine spezielle Instanz einer Klasse. Statische Threadvariablen lassen sich auf alle Threads anwenden, statt nur auf eine bestimmte Instanz eines Threads. Verwendet man eine statische Threadvariable, erhlt jeder Thread, der auf diese Variable zugreift, sein eigenes Exemplar davon. Das heit, wenn ein Thread die Variable verndert, beeinflusst dies nicht die Variable in anderen Threads. Statische Threadvariablen sind ntzlich, wenn man wei, dass eine bestimmte Variable in jedem einzelnen Thread einzigartig sein sollte. Der Einsatz statischer Threadvariablen ist effizienter als etwa neue lokale Variablen fr jeden Thread zu erstellen. Sie knnen eine statische Threadvariable einfach dadurch deklarieren, dass Sie der Deklaration das ThreadStatic-Attribut voranstellen:
[ThreadStatic] int intValue; <ThreadStatic()> dim intValue as integer // C# ' VB .NET

653

Multithreading-Anwendungen

19.5 Workshop
Dieser Workshop soll Ihnen helfen, sich besser an die heute behandelten Begriffe zu erinnern. Es hilft Ihnen sehr, die Antworten vollstndig zu verstehen, bevor Sie weiterlesen. Die Antworten zu den Quizfragen und den bungen finden Sie in Anhang A.

Quiz
1. Schlieen Sie auf drei Funktionen in Microsoft Word, die multithreaded sein knnten. 2. Was versteht man darunter, wenn man etwas als nicht threadsicher bezeichnet? 3. Aus welchem Grund kann die ThreadPool-Klasse Aufgaben effizienter ausfhren? 4. Wahr oder falsch? Der Monitor.Enter-Methode muss man die this-Variable bergeben. 5. Ist der folgende Code korrekt?
Monitor.Enter(this) for i = 0 to 10 lblInfo.Text += "hallo!" next i

6. Welches der folgenden Statements ist korrekt?


1. ThreadPool.QueueUserWorkItem(new ThreadStart(this.Go)); 2. ThreadPool.QueueWorkUserItem(new WaitCallBack(this.Go)); 3. ThreadPool.QueueUserWorkItem(new WaitCallBack(this.Go));

7. Was ist ThreadStart? 8. Wahr oder falsch? Ein Thread beginnt mit seiner Ausfhrung, sobald man einen ThreadStart-Delegaten spezifiziert hat. 9. Welches ist der standardmige ThreadState- und ThreadPriority-Wert fr einen neu gestarteten Thread?

bung
Erstellen Sie ein Multithreading-Dateisuchprogramm. In dieser Anwendung soll ein Benutzer einen Dateinamen als Suchbegriff eingeben und auf die SUCHEN-Schaltflche klicken knnen, so dass ein neuer Thread erzeugt wird. Dann wird rekursiv das Dateisystem nach dem angegebenen Dateinamen durchsucht. Zeigen Sie die Ergebnisse in einem ListBox-Steuerelement an. (Nhere Informationen zum Algorithmus fr das Durchsuchen von Verzeichnissen finden Sie an Tag 11).

654

Windows Forms konfigurieren und bereitstellen

0 2

Windows Forms konfigurieren und bereitstellen

Die letzten Schritte in der Anwendungsentwicklung bestehen in der Konfiguration und Bereitstellung. Zur Konfiguration gehrt sicherzustellen, dass die richtigen Einstellungen benutzt werden, dass die Versionsnummern zutreffen usw. Die Bereitstellung bzw. das Deployment besteht darin, Ihre neue Anwendung auf die Computer der Benutzer zu bringen. Die heutige Lektion wird sich intensiv mit Konfiguration und Bereitstellung befassen. Wie Sie erkennen werden, ist keines dieser Themen kompliziert, und Bereitstellung ist sogar recht einfach. Sie werden eine Reihe neuer Technologien kennen lernen, die Sie bei Ihren Vorhaben untersttzen. Darber hinaus beschftigt sich dieses Kapitel mit Lokalisierung und Eingabehilfen, zwei Schritten, die hufig bersehen werden. Die Lokalisierung einer Anwendung versetzt sie in die Lage, in verschiedenen Kulturen und Sprachen zu funktionieren. Eingabehilfen stellen behinderten Benutzern Hilfen zur Verfgung, mit denen auch sie Ihre Anwendung bedienen knnen. Heute lernen Sie,

was .config- und .resources-Dateien sind, in welcher Weise man die ResourceManager-Klasse einsetzt, wie man im Code die Applikationslogik von Aspekten der Benutzeroberflche trennt, die verschiedenen Einstellungen kennen, die Ihren Anwendungen im .NET Framework zur Verfgung stehen, wo Sie Ihre Anwendung auf dem Computer des Benutzers installieren.

20.1 Einfhrung in die .NET-Konfiguration


Denkt man an die Konfiguration einer Anwendung, so stellen sich die meisten Menschen Merkmale der Benutzeroberflche vor. Wenn man beispielsweise Microsoft Word einsetzt, verstehen viele Benutzer unter Konfiguration das Einstellen der Schriftart, Nummerierung, Tabellen usw. Auch das Einstellen der Seitenrnder und das Justieren der Druckereinstellungen mag dazu gehren. Da der Entwickler zuvor nicht wei, welchen Drucker der Endanwender einsetzt, bleiben Aufgaben wie diese dem Benutzer berlassen. Das soll aber nicht heien, dass sich der Entwickler keine Gedanken ber Konfigurationsprobleme machen msste. Vielmehr sollte er eine Reihe von Aspekten vorbereiten, damit die Anwendung schlielich sauber luft. Zwei Beispiele dieser vorzubereitenden Aspekte sind Sicherheit und Versionsinformation. Hufig ist es notwendig, eine Anwendung mit standardmigen Konfigurationsdaten zu bndeln.

656

Einfhrung in die .NET-Konfiguration

Vor der .NET-Einfhrung gab es zwei verbreitete Methoden, Konfigurationsdaten zu liefern. Die erste bestand darin, die Windows-Registrierung zu nutzen, ein zentrales Ablagesystem fr Daten, die das Betriebssystem anfordert. Diese Registrierungsdatenbank enthielt Informationen ber jedes Programm auf dem Rechner, zustzlich zu Benutzer- und Betriebssystemdaten. Installierte man eine Anwendung das erste Mal, schrieb sie Daten in die Registrierung, auf die diese dann spter bei der Ausfhrung des Programms zurckgriff (siehe Abbildung 20.1). Bei Bedarf konnten nderungen in die Registrierung zurckgeschrieben werden, um die Einstellungen fr die Anwendung zu modifizieren. Bei diesem Vorgehen traten mehrere Probleme auf. Zum einen lsst sich die Registrierung nur relativ schwer editieren. Sie enthlt einen berfluss an (sensiblen) Informationen, die zuweilen so aufgebaut sind, dass sie sich dem unmittelbaren Verstndnis entziehen. Das Ansinnen, der Benutzer solle seine eigenen Registrierungseinstellungen bearbeiten, kann man sofort vergessen. In der Mehrzahl der Flle, in der Benutzer dies taten, richteten sie ein Chaos auf ihrem Rechner an, weil sie kritische Informationen zusammen mit nicht-kritischen Anwendungsdaten speicherten. Waren einige Einstellungen zu ndern oder neue hinzuzufgen, so hatte man zudem die Anwendung zu verndern und neu zu kompilieren das ist nicht immer eine einfache Aufgabe, besonders dann, wenn man gerade keinen Entwickler zur Hand hat.

Abbildung 20.1: Der Registrierungs-Editor (regedit.exe) zeigt die Informationen in der WindowsRegistrierungsdatenbank vollstndig an (im Bild eine Konfiguration fr Word 2000).

Die zweite gngige Methode bestand im Einsatz von Konfigurationsdateien. In Windows haben diese meist die Erweiterung .ini. Diese Dateien sorgten fr eine Menge Schwierigkeiten, die mit der Registrierung zusammenhingen; in der Regel waren .ini-Dateien leicht zu lesen und zu ndern. Eine Anwendung wurde mit einer oder mehreren .ini-Dateien geliefert, welche die Einstellungen enthielten, die die Anwendung bentigte. Listing 20.1 zeigt eine typische .ini-Datei fr Adobe Photoshop.

657

Windows Forms konfigurieren und bereitstellen

Listing 20.1: Konfigurationsinformationen fr Photoshop


1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: [Startup] AppName=Adobe Photoshop 6.0 FreeDiskSpace=2441 EnableLangDlg=Y [ISUPDATE] UpdateURL=http:// [AdobePI] ProductName=Photoshop ProductType=Retail ProductVersion=6.0 ProductLanguage=Not Applicable PIVersion=1.0 PIValue=1869701

Informationen wie etwa der Anwendungsname, die Version und die Sprache sind in der .ini-Datei enthalten. Jedes Mal wenn Photoshop gestartet wird, lsst sich das Programm mit diesen Informationen richtig konfigurieren. .NET entwickelt das .ini-Konzept einen Schritt weiter und macht aus allen Konfigurationsdateien XML-Dateien. Jetzt sind nicht nur die Konfigurationseinstellungen leicht zu lesen, sondern liegen auch in einem Standardformat vor, das es Anwendungen erleichtert, solche Daten zu lesen und zu schreiben. Der Name der Konfigurationsdatei muss mit dem der zu konfigurierenden Anwendung bereinstimmen. Hiee die Anwendung etwa Listing20.1.exe, msste die zugehrige Konfigurationsdatei Listing20.1.exe.config heien. Wie Sie im Verlauf der letzten 19 Tage gelernt haben, mssen Anwendungen allerdings nicht unbedingt Konfigurationsdateien aufweisen (gleich mehr dazu). Listing 20.2 zeigt ein typisches Beispiel. Listing 20.2: Eine typische .config-Datei
1: 2: 3: 4: 5: 6: 7: 8: 9: <configuration> <configSections> <section name="MusterSektion" type="System.Configuration.SingleTagSectionHandler" /> </configSections> <sampleSection setting1="Wert1" setting2="Wert zwei" setting3="dritter Wert" /> </configuration>

658

So konfigurieren Sie Ihre Anwendungen

Im Augenblick brauchen Sie hierzu nur zu wissen, dass .NET mehrere XML-Elemente in der .config-Datei definiert. Sie knnen auerdem Ihre eigenen benutzerdefinierten Konfigurationsdateien erzeugen, damit Ihre Anwendung jede Einstellung findet, die sie braucht. Auch das .NET Framework hat eine Konfigurationsdatei. Sie ist normalerweise im Verzeichnis c:\winnt\Microsoft.NET\Framework\version\CONFIG abgelegt. Sie trgt den Namen machine.config, da sie die Einstellungen enthlt, die der gesamte Computer verwendet. Wenn man keine eigenen anwendungsspezifischen Einstellungen angibt, so erbt die Anwendung solche von machine.config (das ist auch der Grund, warum nicht jede Ihrer Anwendungen eine .config-Datei bentigt). Diese Datei umfasst Informationen darber, welche Klassen sich um Sicherheitsaspekte kmmern, welche Dateien mit der ASP.NETEngine verknpft sind, welche Sprache und Kultur der Benutzer auf seinem Computer als Standard eingetragen hat, wie Debugging zu handhaben ist usw. Die Datei machine.config ist recht umfangreich, da sie Einstellungen fr jeden Aspekt im .NET Framework umfasst. (Standardmig sind es mehr als 750 Zeilen.) Dieses Buch geht auf die Datei nicht nher ein, aber sehen Sie die Datei ruhig einmal durch.

20.2 So konfigurieren Sie Ihre Anwendungen


Bevor man mit der Erzeugung von Konfigurationsdateien beginnen kann, sollte man zuerst eine zu konfigurierende Anwendung dafr haben. Listing 20.3 zeigt die Grundstruktur fr eine Anwendung mit einem INFO-Men. Listing 20.3: Eine konfigurationsfertige Anwendung
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: using System; using System.Windows.Forms; using System.Drawing; namespace TYWinforms.Day20 { public class Listing203 : Form { private MainMenu mnuMain = new MainMenu(); private MenuItem miHelp = new MenuItem("Hilfe"); private MenuItem miAbout = new MenuItem("Info"); public Listing203() { mnuMain.MenuItems.Add(miHelp); miHelp.MenuItems.Add(miAbout); miAbout.Click += new EventHandler(this.ShowAbout);

659

Windows Forms konfigurieren und bereitstellen

17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29:

this.Text = "Meine konfigurierbare Anwendung"; this.Menu = mnuMain; } private void ShowAbout(Object Sender, EventArgs e) { MessageBox.Show("TYWinforms Listing 20.3 Beispiel"); } public static void Main() { Application.Run(new Listing203()); } } }

Die Anwendung weist nur drei Steuerelemente auf: zwei MenuItems und ein MainMenu. Das INFO-Menelement verfgt ber einen Ereignishandler, der nur eine MessageBox mit einer netten Beschreibung des Programms anzeigt. Speichern Sie dieses Listing als Listing20.3.cs und kompilieren Sie es. Sie erhalten eine Datei namens Listing20.3.exe. Die entsprechende Konfigurationsdatei sollte demnach Listing20.3.exe.config heien. Die Konfigurationsdatei muss im selben Ordner wie die ausfhrbare Datei liegen. War Ihre Anwendung im Verzeichnis c:\winforms\day20, so muss die Datei Listing20 .3.exe.config im selben Ordner liegen.

Konfigurationsabschnitte
Die einfachste .config-Datei sieht so aus:
<configuration> </configuration>

Das einzige erforderliche XML-Tag ist das <configuration>-Element. Alle anderen Einstellungen sind jeweils in ihren eigenen Elementen innerhalb von <configuration> platziert. So ist etwa <appSettings> ein weiteres gebruchliches Konfigurationselement. Fgt man es dem vorigen Code hinzu, sieht das so aus:
<configuration> <appSettings> <add key="AppName" value="Listing20.3"/> </appSettings> </configuration>

660

So konfigurieren Sie Ihre Anwendungen

Bei der Erstellung dieser .config-Dateien sind einige Vorsichtsmanahmen zu ergreifen. Da es sich um XML-Dateien handelt, erfordern sie strikt angewandte XML-Syntax. Jedes Anfangstag zieht ein Endtag nach sich, etwa so:
<beliebig> ... </beliebig> oder <beliebig/>

Zweitens sind alle Abschnitte in der .config-Datei in der Camel-Schreibweise (alternierende Gro-/Kleinschreibung) zu formulieren. Dabei wird der erste Buchstabe des ersten Wortes im Abschnittsnamen klein geschrieben und alle nachfolgenden Wrter gro (falls nicht durch einen Punkt abgetrennt). Ein Beispiel:
<appSettings> ... </appSettings> <webRequestModules /> <globalization> ... </globalization> <system.diagnostics />

Obwohl diese Regeln einfach sind, so bringen sie doch manchen Anfnger aus dem Konzept. Bitte schenken Sie ihnen Beachtung. Sollte einmal eine Konfigurationsdatei Ihre Anwendung zum Absturz gebracht haben, dann prfen Sie noch einmal die Gro-/Kleinschreibung sowie die Klammern. Tabelle 20.1 fhrt alle Konfigurationsabschnitte auf, die fr Windows Forms zur Verfgung stehen.
Abschnitt
<application>

Beschreibung Enthlt Informationen ber entfernte Objekte, die Ihre Anwendung in Anspruch nimmt und exponiert. Lsst sich fr die Unterbringung benutzerdefinierter Anwendungseinstellungen nutzen. Legt die Module fest, die zur Authentifizierung von Internet-Anfragen benutzt werden. Enthlt Kanle, ber die die Anwendung mit entfernten Objekten kommuniziert. Enthlt Vorlagen fr die Verarbeitung von Ereignissen aus Kanlen zu entfernten Objekten. Definiert die Abschnitte in einer Konfigurationsdatei wie auch die Module und Assemblies, die diese Abschnitte verarbeiten. Legt fest, wie viele Internetverbindungen hchstens zulssig sind.

<appSettings>

<authenticationModules>

<channels>

<channelSinkProviders>

<configSections>

<connectionManagement>

Tabelle 20.1: Konfigurationsabschnitte fr Windows Forms

661

Windows Forms konfigurieren und bereitstellen

Abschnitt
<debug>

Beschreibung Legt fest, ob alle Klassen, die in der .config-Datei enthalten sind, beim Anwendungsstart geladen werden sollen (um bei der Validierung der .config-Datei zu helfen). Legt den Proxyserver fest, der fr HTTP-Anforderungen an das Internet zu benutzen ist. Enthlt Kryptographie-Informationen. Enthlt Informationen ber Assemblies und Garbage Collection. Enthlt Informationen ber die Version des .NET Frameworks, die fr Ihre Anwendung benutzt werden soll. Legt fest, dass Informationen in Dateien geschrieben werden, sofern die Ablaufverfolgung aktiviert ist (mehr dazu morgen). Dient als Behlter fr die Abschnitte <authenticationModules>, <defaultProxy>, <connectionManagement> und <webRequestModules>. Dient als Behlter fr die Abschnitte <application>, <channels>, <channelSinkProviders> und <debug>. Legt fest, wie Windows Forms-Anwendungen zu debuggen sind. Legt Module fest, die benutzt werden, um Internetanforderungen auszufhren.

<defaultProxy>

<mscorlib> <runtime> <startup>

<system.diagnostics>

<system.net>

<system.runtime.remoting>

<system.windows.forms> <webRequestModules>

Tabelle 20.1: Konfigurationsabschnitte fr Windows Forms (Forts.)

Der Rest dieses Abschnitts errtert die gngigsten vordefinierten Konfigurationselemente. Jedes Mal, wenn Sie eine Konfigurationsdatei ndern, mssen Sie erst die Anwendung neu starten, bevor die nderungen wirksam werden.

Der Abschnitt <configSections>


Wir besprechen den <configSections>-Abschnitt zuerst, weil er einer der wichtigsten ist. Wie in Tabelle 20.1 erwhnt, listet dieser Abschnitt die verschiedenen anderen Abschnitte auf und sagt, wie sie zu behandeln sind. Daher findet man fr jeden nachfolgenden Abschnitt, der errtert wird, einen Eintrag dafr im <configSections>-Abschnitt in der machine.config-Datei. Zum Beispiel:

662

So konfigurieren Sie Ihre Anwendungen

<configuration> <configSections> <section name="runtime" type="System.Configuration.IgnoreSectionHandler, System, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowLocation="false"/> <section name="mscorlib" type="System.Configuration.IgnoreSectionHandler, System, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowLocation="false"/> </configSections> </configuration>

Das scheint eine Menge verwirrender Code zu sein, doch wir mssen ihn nicht vollstndig verstehen. Das name-Attribut jedes hier definierten Abschnitts bestimmt den Namen dieses Abschnitts. Das type-Attribut zeigt die Klasse an, die zur Behandlung aller Aspekte dieses Abschnitts benutzt wird (etwa Lesen/Schreiben), gefolgt von der Assembly, in der sich die Klasse befindet, der Version dieser Assembly und einigen ergnzenden Informationen. Mssen Sie jemals Werte aus einem bestimmten Abschnitt beschaffen, schauen Sie in das
<configSections>-Element, um herauszufinden, welcher Klassentyp zur Lieferung dieser Werte zu erwarten ist. Die IgnoreSectionHandler-Klasse beispielsweise liefert keinerlei

Informationen; Abschnitte, die Benutzer nicht modifizieren sollten, verwenden diese Klasse. In hnlicher Weise setzt der <appSettings>-Abschnitt eine Klasse des Typs NameValueFileSectionHandler ein, die eine Gruppe von Schlssel/Wert-Paaren liefert, die die Informationen ber die fragliche Anwendung darstellen.
<configSections> ist im Grunde nur bei zwei Gelegenheiten ntzlich: wenn Sie wissen

mssen, wie ein bestimmter Abschnitt verarbeitet wird, und wenn Sie eigene Abschnitte definieren mssen. Um Letzteres zu erledigen, brauchen Sie lediglich den Namen des eigenen Abschnitts und die Klasse, die ihn handhabt, anzugeben. So erzeugt etwa das folgende Stck Code einen neuen Abschnitt namens meineAnwendung, die Schlssel/Wert-Paare an Informationen aus Ihrer .config-Datei liefert:
<configuration> <configSections> <section name="meineAwendung" type="System.Configuration.SingleTagSectionHandler" /> </configSections>

Der neue Abschnitt sollte folgendermaen aussehen:


<myApplication setting1="Wert1" /> </configuration>

663

Windows Forms konfigurieren und bereitstellen

Kmmern Sie sich nicht um die Klassen wie etwa SingleTagSectionHandler, die hier angegeben wurden. Man benutzt sie niemals direkt und Ihre Wahl, welche nun bei der Erstellung von Abschnitten zu bevorzugen ist, drfte meist ohne Bedeutung sein. Das heutige Unterkapitel Konfigurationswerte abrufen errtert die Klassen, ber die Sie Bescheid wissen sollten. Sie knnen Ihre eigenen Klassen erstellen, um Konfigurationsabschnitte zu verarbeiten. Das erfordert das Anlegen einer Klasse, die die Schnittstelle IconfigurationSectionHandler implementiert und XML aus Ihren .config-Dateien lesen kann. Doch dies geht ber das Thema dieses Buches hinaus. Mehr Informationen finden Sie in der Dokumentation zum .NET Framework.

Der Abschnitt <appSettings>


Der <appSettings>-Abschnitt ist in Windows Forms-Anwendungen am hufigsten vorzufinden. Es handelt sich um einen benutzerdefinierten Konfigurationsabschnitt, den man nutzen kann, um beliebige Informationen zu speichern. Sie haben bereits ein Beispiel dafr gesehen:
<configuration> <appSettings> <add key="AppName" value="Listing 20.3"/> </appSettings> </configuration>

Die Konfigurationsdaten in <appSettings> werden in Schlssel/Wert-Paaren angeordnet. Mit Hilfe des add-Elements knnen Sie je nach Bedarf so viele dieser Paare erzeugen, wie Sie brauchen. Ihre Anwendung kann leicht darauf zugreifen (mehr dazu im Unterkapitel Konfigurationswerte abrufen). Das obige Codestck definiert einen neuen Schlssel namens AppName mit dem Wert Listing 20.3. Wie Sie sicher bereits vermuten, legt diese Einstellung den Namen unserer Anwendung fest. Leicht lsst sich ein weiteres Element hinzufgen, um die Versionsnummer anzugeben:
... <add key="Version" value="1.0"/> ...

Da sich nicht viel mehr ber diesen Abschnitt sagen lsst, gehen wir zum nchsten.

664

So konfigurieren Sie Ihre Anwendungen

Der Abschnitt <startup>


Wollen Sie angeben, dass Ihre Windows Forms-Anwendung eine bestimmte Version des .NET Frameworks benutzt, so knnen Sie dies im <startup>-Abschnitt tun. Weil aber die einzige verfgbare Framework-Version die Nummer 1.0 trgt, ist <startup> vorerst von keinem groen Nutzen. Erst wenn Microsoft weitere Versionen auf den Markt gebracht hat, erhlt dieser Abschnitt mehr Sinn. Angenommen, die Version 2.0 wrde verffentlicht und verfgte ber viele neue Funktionen. Ihre Anwendung erstellen Sie auf der Basis dieser Version und sie enthlt nun Merkmale, die in Version 1.0 nicht verfgbar waren. Im <startup>-Abschnitt knnen Sie nun festlegen, dass Ihre Anwendung nur ausgefhrt werden darf, wenn Version 2.0 vorliegt. Wenn der Benutzer versucht, die Anwendung auszufhren, ohne dass er Version 2.0 installiert hat, lsst sich die Anwendung einfach nicht laden und zeigt eine Fehlermeldung an. Die Syntax fr diesen Abschnitt lautet wie folgt:
<startup> <requiredRuntime version="version" safemode="safemode" </startup> />

Sie brauchen sich lediglich um die zwei Platzhalter version und safemode zu kmmern. version stellt natrlich die Version des zu verwendenden .NET Frameworks dar. Diese Angabe muss im Format vx.x.x.x vorliegen, wobei jeder x-Wert eine Zahl zwischen 0 und 65.535 ist. Safemode kann entweder true oder false sein und gibt an, ob die CLR die Windows-Registrierung nach der richtigen zu verwendenden Version durchsuchen soll. Ein Beispiel:
<configuration> <startup> <requiredRuntime version="v1.0.0.0" safemode="false" /> </startup> </configuration>

Beachten Sie, dass version und safemode nicht zwingend erforderlich sind. Gibt man dennoch eine version-Nummer und true fr safemode an, dann muss die angegebene Versionsnummer mit der in der Windows-Registrierung bereinstimmen, sonst wird ein Fehler erzeugt.

665

Windows Forms konfigurieren und bereitstellen

Der Abschnitt <system.windows.forms>


Dieser Abschnitt legt nur eines fest: welches Element Fehler behandeln soll, die in einer Anwendung auftreten. Ein kurzer Blick auf die Syntax:
<system.windows.forms jitDebugging="value" />

Das .NET Framework benutzt ja einen JIT-Compiler (vgl. Tag 1), um Anwendungen aus MSIL in Maschinencode zu kompilieren, wenn die Anwendung ausgefhrt wird. Die Anwendung wird also gerade noch rechtzeitig kompiliert, damit sie ausgefhrt werden kann. hnlich arbeitet auch ein JIT-Debugger. Er greift gerade noch rechtzeitig ein, um Ihnen dabei zu helfen, die Anwendung zu reparieren, sobald ein Fehler aufgetreten ist. Stoen Sie in einer Windows Forms-Anwendung auf Fehler, dann haben Sie zwei Alternativen. Die erste Option besteht darin, nichts zu tun. Standardmig erhlt man in Windows Forms folgende Fehlermeldung, wie in Abbildung 20.2 zu sehen.

Abbildung 20.2: Der standardmige Fehlerbehandlung in Windows Forms

Das in Abbildung 20.2 gezeigte Dialogfeld bietet ein wenig Aufklrung darber, was den Fehler verursachte, lsst aber keine Aktion Ihrerseits zu. Sie knnen entweder fortfahren, die Anwendung zu nutzen, oder sie beenden, wobei Sie praktisch den Fehler ignorieren. Die zweite Wahlmglichkeit besteht im Einsatz des JIT-Debuggers zur Fehlerbeseitigung. Ein JIT-Debugger erlaubt es Ihnen, einen Fehler in der Anwendung zu beheben (sofern Sie noch ber den Quellcode verfgen), so dass der Fehler nicht noch einmal auftreten kann. In der Standardeinstellung hat Windows Forms das Eingreifen eines JIT-Debuggers zur Fehlerbeseitigung deaktiviert. Hier kommt der Konfigurationsabschnitt <system.windows.forms> ins Spiel. Haben Sie im obigen Code value auf true gesetzt, dann erscheint die in Abbildung 20.2 zu sehende Meldung nicht, sondern vielmehr sehen Sie etwas wie in Abbildung 20.3. In diesem Fall wird Ihnen eine Auswahl an JIT-Debuggern geboten, um einen Fehler zu beheben.

666

So konfigurieren Sie Ihre Anwendungen

Abbildung 20.3: Wenn Sie JIT-Debuggen aktivieren, knnen Sie einen JIT-Debugger zur Fehlerbeseitigung einsetzen.

In der morgigen Lektion wird Debugging intensiv behandelt. Damit der Abschnitt <system.windows.forms> funktioniert, mssen Sie zunchst Ihre Anwendung mit aktiviertem Debugging kompiliert haben. Die Aktivierung wird mit der Option /debug vorgenommen:
vbc /t:winexe /r:system.dll /debug MeinListing.vb Dies erzeugt eine neue Datei mit der Endung .pdb. Die Datei enthlt spezielle

Informationen ber die fragliche Anwendung, die Debugger auswerten knnen.

Konfigurationswerte abrufen
Nachdem Sie Ihre .config-Datei erstellt und eingerichtet haben, kann die Anwendung auf diese Konfigurationswerte leicht mit der ConfigurationSettings-Klasse zugreifen. Deren zwei Mitglieder werden hier vorgestellt. Das erste ist die GetConfig-Methode. Man setzt sie ein, indem man einen abzurufenden Konfigurationsabschnitt angibt. Der folgende Code liefert die Informationen, die im Abschnitt <system.windows.forms> enthalten sind:
ConfigurationSettings.GetConfig("system.windows.forms")

667

Windows Forms konfigurieren und bereitstellen

Die Klasse ConfigurationSettings ist im Namensraum System.Configuration enthalten. Diesen mssen Sie unbedingt importieren, wollen Sie ConfigurationSettings verwenden. Das Code-Statement liefert ein Objekt vom Typ System.Windows.Forms.ConfigData, das aber leider nicht sonderlich ntzlich ist. Die meisten Konfigurationsabschnitte geben solche Objekte zurck. Das ist der Grund, warum der Konfigurationsabschnitt <appSettings> eingefhrt wurde. Dieser Abschnitt liefert ein benutzerfreundliches Objekt, was uns zum zweiten Mitglied der ConfigurationSettings-Klasse fhrt. Die AppSettings-Eigenschaft liefert passenderweise die Einstellungen, die in <appSettings> enthalten sind. Statt GetConfig zu benutzen und die gelieferten Objekte umwandeln zu mssen, prsentiert AppSettings alle Daten in leicht verwendbarem Format. Erstellen Sie eine .config-Datei fr die aus Listing 20.3 erzeugte Anwendung (listing20.3.exe) und taufen Sie sie listing20.3.exe.config. Fgen Sie sie dem Code hinzu, der in Listing 20.4 zu sehen ist. Listing 20.4: Die .config-Datei fr Listing 20.3
1: 2: 3: 4: 5: <configuration> <appSettings> <add key="AppName" value="Listing 20.3" /> <add key="Version" value="1.0" /> <add key="Beschreibung" value="Eine Musteranwendung fr das Testen von  Konfigurationsdateien." /> 6: </appSettings> 7: </configuration>

Nun ndern Sie Zeile 17 des Listings 20.3 wie folgt:


this.Text = ConfigurationSettings.AppSettings["AppName"] + " version " + ConfigurationSettings.AppSettings["Version"];

Dieser Code ruft die Informationen von AppName und Version aus der .config-Datei ab und zeigt sie in der Titelzeile der Anwendung an. ndern Sie Zeile 22 wie folgt:
MessageBox.Show(ConfigurationSettings.AppSettings["Beschreibung"]);

Dieser Code zeigt die Beschreibung-Einstellung im INFO-Fenster an. Kompilieren Sie die Anwendung neu (und vergessen Sie nicht, den Namensraum System.Configuration zu importieren). Klicken Sie auf das INFO-Men. Sie sollten etwas hnliches wie in Abbildung 20.4 sehen.

668

Lokalisieren Sie Ihre Anwendung

Abbildung 20.4: Sie knnen Ihre Konfigurationsdaten in Ihrer Anwendung anzeigen lassen.

Das war's schon. Verwenden Sie die AppSettings-Eigenschaft, um in eckigen Klammern (in VB .NET in runden Klammern) die Einstellung anzugeben, an der Sie interessiert sind und schon wird sie beschafft (und angezeigt, wenn Sie wollen).

20.3 Lokalisieren Sie Ihre Anwendung


Lokalisierung ist der Prozess, bei dem bestimmte Aspekte je nach Publikum einer Anwendung in kulturspezifische Versionen umgewandelt werden, so etwa in franzsische Sprache statt englische, wenn die Anwendung in Frankreich benutzt wird. Windows pflegt die jeweiligen Lndereinstellungen eines Benutzers und eine Anwendung kann diese Einstellungen ermitteln und sich ihnen automatisch anpassen. Lokalisierung klingt nach einem komplizierten Vorgang, ist es aber nicht. Das .NET Framework macht es Ihnen leicht, die Lndereinstellungen fr eine Anwendung zu ndern. Sie mssen lediglich noch die eigentlichen Informationen in die jeweils gewnschte Sprache bersetzen. Meistens sind Texte und Grafiken zu bersetzen. In Begriffen der Lokalisierung werden solche Elemente als Ressourcen bezeichnet. Sie lassen sich getrennt von der Anwendung speichern. Bei der Lokalisierung einer Anwendung brauchen Sie lediglich diese Ressourcen zu bersetzen und .NET bernimmt den Rest.

669

Windows Forms konfigurieren und bereitstellen

Lokalisierung ist ein vierstufiger Prozess: 1. Erstellen Sie in normalem Text die Ressourcendateien, die fr die verschiedenen Lnder gebraucht werden. 2. Konvertieren Sie die Ressourcendateien in .resource-Dateien. 3. Kompilieren Sie die Ressourcendateien zu einer Assembly und legen Sie die Assemblies in entsprechenden Verzeichnissen ab. 4. Greifen Sie aus Ihrer Anwendung auf die Ressourcendateien zu. Der erste Schritt ist der einfachste. ffnen Sie den Windows-Editor und geben Sie den Text des Listings 20.5 ein. Listing 20.5: Ressourcen fr Englisch
1: 2: 3: Caption = TYWinforms Day 20 Application WelcomeMessage = Welcome to localization techniques! GoodbyeMessage = Leaving so soon?

Speichern Sie das Listing als Day20.txt. Dann erzeugen Sie eine Eigenschaft und einen entsprechenden Wert in Ihrer Anwendung. In unserem Fall wurden die Eigenschaften Caption, WelcomeMessage und GoodbyeMessage mit angemessenem Text erzeugt, der an verschiedenen Stellen im Programm angezeigt wird. Nun erstellen Sie eine weitere Ressource, aber diesmal in Spanisch, wie in Listing 20.6 zu sehen. Listing 20.6: Ressourcen fr Spanisch
1: 2: 3: Caption = TYWinforms Applicacion Dia 20 WelcomeMessage = Bienvenidos a tecnicas de localizacion! GoodbyeMessage = Ya se va?

Speichern Sie diese Datei unter Day20.es-ES.txt. Sie sollten bei diesem Listing ein paar Aspekte beachten, zuerst den Dateinamen. Der Anfang ist identisch mit dem fr Listing 20.5: Day 20. Das es ist der Code fr die spanische Lndereinstellung. Einige dieser Codes zeigen Unterkulturen an. es-GT steht fr guatemaltekisches Spanisch, es-ES fr Spanisch, das in Spanien gesprochen wird. In Listing 20.5 wurde dieser Kulturcode weggelassen, da es sich um die Standardkultur handelte: in diesem Fall Englisch. Ist Englisch nicht der Standard fr die Lndereinstellungen, dann lautet sein Krzel en. Die richtige Bezeichnung dieser Dateien ist sehr wichtig. Sie werden gleich verstehen, warum. (Eine komplette Liste der Kulturcodes finden Sie in der Dokumentation des .NET Frameworks unter der CultureInfo-Klasse.)

670

Lokalisieren Sie Ihre Anwendung

Beachten Sie, dass die Eigenschaftsnamen die gleichen geblieben sind. Das erleichtert es Entwicklern, auf sie im Code zu verweisen. Lediglich der eigentliche Textwert muss gendert werden. Nun sind die Ressourcendateien in etwas zu kompilieren, das das .NET Framework gebrauchen kann. Das ist ein zweistufiger Vorgang. Der erste Schritt besteht darin, das Werkzeug Resource File Generator einzusetzen (resgen.exe), um die .txt-Dateien in .resource-Dateien umzuwandeln. Die Syntax ist einfach:
resgen txt_Datei resource_Datei

In diesem Fall haben Sie zwei Befehle:


resgen Day20.txt Day20.resources resgen Day20.es-ES.txt Day20.es-ES.resources

Das Ergebnis sollte wie folgt aussehen:


Read in Writing Read in Writing 3 resources from 'Day20.txt' resource file... Done. 3 resources from 'Day20.es-ES.txt' resource file... Done.

Wenn Sie Visual Studio .NET einsetzen, um Anwendungen zu lokalisieren, denken Sie daran, dass die von Visual Studio .NET erzeugten Ressourcendateien nicht automatisch zu den von resgen.exe erzeugten kompatibel sind. Haben Sie sich erst einmal fr eine Methode entschieden, knnen Sie also nicht wechseln, ohne wieder ganz von vorne anzufangen. Der nchste Schritt besteht darin, diese .resources-Dateien zu einer Assembly zu kompilieren. Die dafr empfohlene Methode sieht vor, die Standardressourcendateien (hier in Englisch) in die ausfhrbare Anwendung einzubetten und alle anderen Ressourcendateien zu separaten Assemblies zu kompilieren (hier fr Spanisch), die als Satellitenassemblies bezeichnet werden. Wenn man der Anwendung eine neue Ressource hinzufgen muss, muss man auf diese Weise nicht die ausfhrbare Datei anfassen, sondern lediglich eine neue Ressource anlegen und sie in einer Assembly im richtigen Verzeichnis zusammen mit der fraglichen Anwendung speichern. Um eine Ressource in die ausfhrbare Datei einzubetten, verwendet man einen CompilerBefehl von der Befehlszeile (csc fr C#, vbc fr VB .NET) zusammen mit dem /resourceParameter.

671

Windows Forms konfigurieren und bereitstellen

Um jedoch Satellitenassemblies aus den Ressourcendateien zu erstellen, die nicht zum Standard gehren, setzt man den Assembly Linker ein (al.exe). Die Syntax fr dieses Hilfsprogramm sieht wie folgt aus:
al /t:library /embed:filename /culture:culture /out:out_file

Der erste Parameter, filename, stellt die .resources-Datei dar, die zu kompilieren ist. In diesem Fall soll filename Day20.es-ES.resources sein. Der zweite Parameter, culture, gibt die Kultur fr eine bestimmte zu kompilierende Datei an. Das ist wichtig, denn man kann nur eine Ressourcendatei pro Assembly haben, und jede Assembly muss den gleichen Namen haben (gleich mehr dazu). Die einzige Mglichkeit, wie das .NET Framework herausfinden kann, welche Assembly zu welcher Kultur gehrt, besteht also im Einsatz der culture-Eigenschaft. Der letzte Parameter, out_file, ist der Dateiname der kompilierten Assembly (eine .dll-Datei). In unserem Fall sollten Sie den folgenden Befehl verwenden:
al /t:library /embed:Day20.es-ES.resources /culture:es-ES /out:Day20.resources.dll

Sie erhalten eine Datei namens Day20.resources.dll, die die Spanisch-Ressourcen enthlt. Die Englisch-Ressourcen sind ja in der Datei Day20.resources abzulegen. Ein kleiner Exkurs zu den Dateinamen: Es ist bei der Lokalisierung in .NET wichtig, dass alle Ihre Dateinamen bereinstimmen. So haben etwa Ihre zwei Ressourcendateien den gleichen Anfang: Day20. Auch die Satellitenassembly teilt diese Wurzel und gleich werden Sie sehen, dass dies auch die Anwendung tun muss. .NET verlsst sich auf diese Namenskonvention, um beim Aufruf die richtigen Ressourcen finden zu knnen. Zustzlich muss man auch die richtige Verzeichnisstruktur fr die Ressourcen erstellen. Fr jede vorhandene Satellitenassembly, die nicht dem Standard angehrt, sollte man ein eigenes Verzeichnis mit dem Kulturnamen erzeugen. In unserem Fall haben Sie eine Ressourcendatei fr die Kultur es-ES. Deshalb legen Sie im Ordner Ihrer Anwendung einen Unterordner namens es-ES an und legen hier Ihre kompilierte Ressourcenassembly ab. Abbildung 20.5 zeigt eine typische Verzeichnisstruktur.
c:\Winforms\Day20 Day20.exe Day20.resources es-ES Day20.resources.dll Day20.es-ES.resources fr Day20.resources.dll Day20.fr.resources

Abbildung 20.5: Ihre kompilierten Quelldateien sollten in den Ordnern ihrer jeweiligen Kultur abgelegt werden.

672

Lokalisieren Sie Ihre Anwendung

Soll eine Anwendung die Lndereinstellungen ndern, sucht sie zuerst in jedem KulturVerzeichnis nach der richtigen Ressourcenassembly. Steht keine zur Verfgung, werden die Standardressourcen verwendet. Gibt es einen Ordner, der zwar die Kultur, aber nicht die Unterkultur angibt, dann verwendet die Anwendung statt dessen die bergeordnete Kultur. Hat man beispielsweise eine Satellitenassembly und ein Verzeichnis nur fr allgemeines Spanisch (es) angelegt, doch die Kultur des Benutzers ist es-ES, dann greift die Anwendung auf die es-Ressourcen zurck. Dieser Vorgang wird als Ressourcenfallback bezeichnet. Nun bentigen Sie fr die Nutzung Ihrer Ressourcen nur noch eine Anwendung. Listing 20.7 zeigt eine einfache Anwendung, die Ihre kompilierte Ressourcenassembly-Datei ausliest. Listing 20.7: Ihre erste Anwendung, die die jeweilige Kultur bercksichtigt
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: using using using using using using using using System; System.Windows.Forms; System.Drawing; System.Resources; System.Reflection; System.Threading; System.Globalization; System.ComponentModel;

namespace TYWinforms.Day20 { public class Listing207 : Form { private ResourceManager rsmDay20; private Label lblMessage = new Label(); public Listing207() { rsmDay20 = new ResourceManager("Day20", this.GetType().Assembly); lblMessage.Text = rsmDay20.GetString("Willkommensgruss"); lblMessage.Size = new Size(300,100); this.Text = rsmDay20.GetString("Titelzeile"); this.Controls.Add(lblMessage); this.Closing += new CancelEventHandler (this.CloseMe); } private void CloseMe(Object Sender, CancelEventArgs e) { MessageBox.Show(rsmDay20.GetString ("Abschiedgruss")); } public static void Main() {

673

Windows Forms konfigurieren und bereitstellen

31: 32: 33: 34:

Application.Run(new Listing207()); } } }

Speichern Sie dieses Listing als Day20.cs. (Dieser Dateiname muss mit dem Ihrer Ressourcendateien bereinstimmen.) In den Zeilen 1 bis 8 werden eine ganze Reihe zustzlicher Namensrume importiert. Sie sind fr die Lokalisierung und einige andere Dinge im Code erforderlich. In Zeile 12 wird die ResourceManager-Klasse erstellt, die sozusagen die Schaltstelle fr die Interaktion mit Ressourcen bildet. ResourceManager verfgt ber diverse Methoden, die Ressourcendateien laden und die darin enthaltenen Daten holen knnen. In Zeile 16 wird eine Instanz des ResourceManagerObjekts angelegt. Der Konstruktor bernimmt zwei Parameter. Der erste legt die Namenswurzel der zu verwendenden Ressourcen fest. (Jetzt verstehen Sie, warum alle Dateien die gleiche Wurzel haben mssen.) Der zweite Parameter besteht aus der Hauptassembly fr die Ressourcen. Meist besteht diese Assembly einfach aus Ihrer zentralen ausfhrbaren Datei. GetType().Assembly liefert diejenige Assembly, die die Klasse Listing207 enthlt. In Zeile 18 sehen Sie, wie eine Ressource das erste Mal geholt wird. Statt den Text des Bezeichnungsfeldes auf einen statischen Wert zu setzen, benutzen Sie die GetString-Methode der ResourceManager-Klasse und geben die Eigenschaft der zu holenden Ressourcendatei an. Denken Sie noch einmal an die Listings 20.5 und 20.6, in denen die Eigenschaften Caption, WelcomeMessage und GoodbyeMessage zur Verfgung standen. Die zweite wird in Zeile 18 eingesetzt, um den im Label-Steuerelement enthaltenen Text zu bestimmen. Die Caption-Eigenschaft hingegen wird in Zeile 21 verwendet, wieder mit der GetString-Methode des ResourceManager-Objekts. Da Sie dem Benutzer bei Beendigung der Anwendung eine Meldung anzeigen wollen, hngen Sie an das Closing-Ereignis einen Ereignishandler an. Die CloseMe-Methode in Zeile 26 zeigt eine MessageBox mit der GoodbyeMessage-Eigenschaft an. Denken Sie daran, dass Sie immer noch die standardmige Ressourcendatei haben: Day20.resources. Sie mssen sie in die kompilierte Version von Listing 20.7 einbetten. Zu diesem Zweck benutzen Sie folgenden Befehl, um Ihre Anwendung zu kompilieren:
csc /t:winexe /r:system.dll /r:system.drawing.dll /r:system.windows.forms.dll / res:Day20.resources Day20.cs

Die einzige Neuheit hier ist der /res-Parameter (der fr /resource steht). Er gibt die standardmige Ressourcendatei an, die in die ausfhrbare Datei einzubetten ist. Angenommen, Ihre Kultur ist Englisch, dann sehen Sie das in Abbildung 20.6 gezeigte Ergebnis,

674

Lokalisieren Sie Ihre Anwendung

wenn Sie versuchen, Ihre Anwendung zu schlieen. Die in der Titelzeile, dem LabelSteuerelement und dem Meldungsfeld zu sehenden Werte wurden aus dieser Standardressourcendatei beschafft. Wollen Sie die Anwendung in einer anderen Kultur testen, stehen Ihnen zwei Optionen zur Wahl: Schicken Sie Ihre Anwendung in ein anderes Land und installieren Sie sie dort, oder verwenden Sie die CultureInfo-Klasse. Es knnte zwar erholsam sein, nach Spanien zu reisen, doch wesentlich schneller geht es mit der CultureInfo-Klasse.

Abbildung 20.6: Ihre lokalisierte Anwendung verhlt sich in ihrer jeweiligen Kultur ganz normal.

Der Hauptthread in Ihrer Anwendung (vgl. Tag 19) verfgt ber eine Eigenschaft CurrentUICulture, die bestimmt, welche Kultur und somit, welche Ressourcen die Anwendung verwenden soll. Die CultureInfo-Klasse lsst sich dazu benutzen, eine bestimmte Kultur vorzugeben. Um die Spanisch-Anwendung zu testen, fgen Sie folgenden Code zwischen die Zeilen 16 und 17 ein:
Thread.CurrentThread.CurrentUICulture = new CultureInfo("es-ES");

Dieser Code stellt die Eigenschaft CurrentUICulture des Hauptthreads mit Hilfe der CultureInfo-Klasse auf es-ES ein. Kompilieren Sie die Anwendung (mit dem /res-Parameter) neu, dann sollten Sie das in Abbildung 20.7 gezeigte Ergebnis sehen. Alle Ressourcen wurden in Spanisch umgewandelt. Wenn man die Kultur mit der CultureInfo-Klasse ndert, schaut die Anwendung nach, ob im Ordner der ausfhrbaren Datei ein Unterverzeichnis namens es-ES existiert. Ist es vorhanden, besorgt sich die Anwendung ihre Ressourcen von dort. Ist das aber nicht der Fall, greift die Anwendung auf die Standardkultur zurck, nmlich Englisch.

675

Windows Forms konfigurieren und bereitstellen

Abbildung 20.7: Ihre Anwendung beherrscht nun mehrere Sprachen!

Wenn Sie eine Anwendung in eine weitere Sprache bersetzen mssen, erzeugen Sie einfache eine neue .resources-Datei, kompilieren sie zu einer Satellitenassembly und legen sie im richtigen Unterverzeichnis der Anwendung ab. Sie wiederholen also die Schritte 1 bis 3 vom Anfang dieser Lektion. Sie brauchen die Anwendung nicht neu zu kompilieren (es sei denn, Sie wollen sie mit unterschiedlichen CultureInfo-Objekten testen), was es erleichtert, die Anwendung zu lokalisieren. Es wird hufig die Empfehlung gegeben, all jene Elemente einer Anwendung, die sich lokalisieren lassen, in Ressourcendateien abzulegen, selbst dann, wenn man nicht vorhat, die Anwendung in andere Lnder zu liefern. Wenn also eine Wortwahl oder ein Bild in der Anwendung zu ndern ist, kann man das tun, indem man nur die Ressourcendateien bearbeitet, statt die gesamte Anwendung neu kompilieren zu mssen.

20.4 Eingabehilfen
Hufig mssen Sie Ihre Anwendung an die Bedrfnisse von Menschen mit Behinderungen oder Bedienungsproblemen bei Computern anpassen. Microsoft Active Accessibility (MSAA) ist eine Technologie, die es dem Entwickler erleichtern soll, solche Leistungsmerkmale einzubinden, die dieser Zielgruppe helfen. Standardmig setzt bereits jedes Windows Forms-Steuerelement MSAA um; jedes Steuerelement verfgt ber mehrere Eigenschaften, die spezifisch die MSAA betreffen. Bestimmte Anwendungen, die als Clientanwendungen mit Eingabehilfen bezeichnet werden, benutzen diese Eigenschaften, um Behinderten zu helfen, hufig in Form von visuellen Hinweisen oder gesprochenen Beschreibungen. Tabelle 20.2 fhrt diese Eigenschaften auf.

676

Der einfache Teil der Arbeit: Bereitstellung

Eigenschaft
AccessibilityObject

Beschreibung Liefert ein AccessibleObject-Objekt, das die Eingabehilfen des betreffenden Steuerelements beschreibt. Beschreibt die Aktion, die ein betreffendes Steuerelement ausfhrt. Beschafft oder setzt eine Beschreibung, die von Eingabehilfe-Clientanwendungen benutzt wird. Gibt den Namen des Steuerelements an, den die Eingabehilfe-Clientanwendungen zu sehen bekommen. Gibt den Typ des betreffenden Steuerelements an, etwa ein Dialogfeld oder ein MenuItem. Gibt an, ob das betreffende Steuerelement zu Eingabehilfe-Clientanwendungen konform ist.

AccessibleDefaultActionDescription AccessibleDescription

AccessibleName

AccessibleRole

IsAccessible

Tabelle 20.2: Eigenschaften fr Eingabehilfen

Sie finden die verschiedenen Eingabehilfen-Clients, die in Windows eingebaut sind, wenn Sie auf START, PROGRAMME, ZUBEHR, EINGABEHILFEN oder in die SYSTEMSTEUERUNG gehen.

20.5 Der einfache Teil der Arbeit: Bereitstellung


Bereitstellung ist das bei weitem einfachste Thema in der heutigen Lektion. Bereitstellung (Deployment) meint die Installation einer Anwendung auf den Computern der Benutzer. Da im .NET Framework Anwendungen vollstndig autonom sind, besteht die einzige notwendige Manahme fr ihre Bereitstellung im Kopieren von Dateien. Jede kompilierte .NET-Anwendung enthlt ja Metadaten, die dem Computer alle ntigen Informationen liefern, um die betreffende Anwendung auszufhren. Eintragungen in der Registrierung vorzunehmen, Dateien ins Windows-Verzeichnis zu speichern oder etwas dergleichen aus der Zeit vor .NET ist nunmehr unntig. Die erste Technik zur Bereitstellung besteht im Kopieren der entsprechenden Dateien auf den Zielcomputer. Bei den heute geschriebenen Anwendungen wrde dies die .exeDateien betreffen. Hinzu kommen die Erstellung aller erforderlichen Unterverzeichnisse fr andere Kulturen und die Platzierung passender Satellitenassemblies in ihnen. Der Kopiervorgang kann mit den gngigen Methoden erfolgen, vom File Transfer Protocol (FTP) bis hin zum guten alten XCOPY-Befehl.

677

Windows Forms konfigurieren und bereitstellen

Eine zweite Methode sieht den Einsatz der Windows Installer-Anwendung vor. Dieses Programm verpackt alle ntigen Dateien und erzeugt eine Datei mit der Endung .msi. Windows kann die .msi-Datei nutzen, um die Anwendung zu installieren (hnlich wie das bekannte Programm Setup.exe, das sich in vielen lteren Anwendungen findet). Der Windows Installer nimmt auch Eintragungen im HINZUFGEN/ENTFERNEN-Applet der Systemsteuerung (SOFTWARE-Applet) vor, mit denen sich Anwendungen leicht von der Festplatte entfernen lassen. Eine dritte Methode besteht darin, die Anwendungsdateien zu einer .CAB-Datei zu komprimieren, die als Archivdatei bezeichnet wird. Solche Archivdateien sind lediglich gepackte Versionen der ursprnglichen Dateien, die sich bei Bedarf extrahieren lassen. Nhere Informationen ber den Windows Installer und den Einsatz von Archivdateien finden Sie auf Microsofts Website unter dem folgenden URL:
http://www.microsoft.com/msdownload/platformsdk/sdkupdate/psdkredist.htm

20.6 Zusammenfassung
Konfiguration und Bereitstellung sind meist die letzten auszufhrenden Schritte in der Anwendungsentwicklung. Ihre Behandlung am Ende des Buches ist daher angemessen. Die Konfiguration von Windows Forms-Anwendungen wird normalerweise mit .configXML-Dateien gehandhabt. Diese Dateien enthalten Einstellungen, die eine Anwendung benutzt, darunter Hinweise, welche Version des .NET Frameworks zu verwenden ist und wie man Untersttzung fr Debugging bietet. Um auf diese Einstellungen ber die Anwendung zuzugreifen, benutzt man die ConfigurationSettings-Klasse und entweder die Eigenschaft AppSettings, die Werte aus dem Konfigurationselement <appSettings> liefert, oder die GetConfig-Methode, um jeden anderen Abschnitt der .config-Datei zu liefern. Eine Anwendung zu lokalisieren, bedeutet, ihre Ressourcen in verschiedene Sprachen und Kulturen zu bertragen. Dazu gehrt die Erzeugung von .resources-Dateien und Satellitenassemblies fr jede weitere Kultur, die man untersttzen will. Das Bezeichnungsschema fr lokalisierte Anwendungen ist von groer Bedeutung: Alle Dateien mssen den gleichen Wurzelnamen tragen. Man kann die ResourceManager-Klasse einsetzen, um Werte zu holen, die in Ressourcendateien enthalten sind. Auf diese Weise lsst sich die Anwendungslogik vollstndig von der Benutzeroberflche getrennt halten. Microsoft Active Accessibility ist in alle Windows Forms-Steuerelemente eingebaut. Die Eingabehilfen helfen Benutzern, die Schwierigkeiten bei der Bedienung einer Anwendung haben, indem sie ihnen audiovisuelle Hinweise geben, wie sie sich auf dem Computer zurechtfinden knnen. Diese Funktion wird in jedem Steuerelement durch eine Reihe besonderer Eigenschaften untersttzt.

678

Fragen und Antworten

Bereitstellung ist der Vorgang, bei dem eine Anwendung an die Benutzer ausgeliefert wird. Das einzige Erfordernis besteht hier im Kopieren der ntigen Dateien. Das .NET Framework und die CLR handhaben alles Weitere fr Sie.

20.7 Fragen und Antworten


F Wie erzeuge ich Ressourcendateien aus Bildern? A Leider ist dies schwieriger als bei reinem Text. Sie mssen erst Ihre Bilder in angemessen formatiertes XML umwandeln, bevor Sie sie zu Assemblies kompilieren knnen. Das .NET Framework QuickStart-Paket enthlt ein Hilfsprogramm namens ResXGen.exe. Es benutzt folgende Syntax:
ResXGen /i:dateiname /o:ressourcen_name /n:name

wobei dateiname die Grafikdatei angibt, ressourcen_name die zu erzeugende Ressourcendatei (mit der Endung .resx) und name den Eigenschaftsnamen fr diese Ressource (hnlich wie heute WelcomeMessage). Nhere Informationen ber ResXGen finden Sie in der .NET Framework-Dokumentation. F In der Zeit vor .NET konnte man benutzerspezifische Informationen in der Registrierung ablegen. Fr eine einzelne Anwendung konnte ich so fr verschiedene Benutzer jeweils unterschiedliche Einstellungen realisieren. Kann ich das auch in .NET tun? A Mit .config-Dateien geht das nicht. Man kann aber weiterhin benutzerspezifische Informationen in der Windows-Registrierung mit Hilfe der Registry-Klasse pflegen. Diese Klasse liefert Informationen ber in der Registrierung abgelegte Daten und erlaubt auch den Schreibzugriff auf die Registrierung.

Ich bekomme nicht heraus, welche Eigenschaft ein bestimmter Handler fr einen Konfigurationsabschnitt hat, oder welchen Datentyp er liefert. Ich bitte um Hilfe. A Das Hilfsprogramm Intermediate Language Disassembler (ildasm.exe) erweist sich als ntzlich, um die Inhalte einer .NET Assembly zu untersuchen (.dll- oder .exe-Dateien). Das Werkzeug verfgt ber eine grafische Oberflche, die alle Mitglieder, die in einer Assembly enthalten sind, detailliert auflistet. So knnen Sie herausfinden, welche Typen das Objekt liefert und welche Methoden und Eigenschaften es untersttzt. Das Tool ist hufig ein guter Ersatz fr die Dokumentation des .NET Frameworks.

679

Windows Forms konfigurieren und bereitstellen

20.8 Workshop
Dieser Workshop soll Ihnen helfen, sich besser an die heute behandelten Begriffe zu erinnern. Es hilft Ihnen sehr, die Antworten vollstndig zu verstehen, bevor Sie weiterlesen. Die Antworten zu den Quizfragen und den bungen finden Sie in Anhang A.

Quiz
1. Welchen Vorteil bieten .config- gegenber .ini-Dateien? 2. Was macht das Werkzeug al.exe? 3. Welche zwei Parameter erwartet das Hilfsprogramm resgen? 4. Wahr oder falsch? Eine Konfigurationsdatei fr eine MyApp.exe genannte Anwendung sollte MyApp.config genannt werden. 5. Wahr oder falsch? Wenn man Ressourcendateien bersetzt, muss man auch die Eigenschaftsnamen wie auch deren Werte bersetzen. 6. Welches ist das Basiselement fr alle .config-Dateien? 7. Zu welchem Zweck wird das Konfigurationselement <system.windows.forms> verwendet? 8. Was versteht man unter Camel-Schreibweise und wo ist sie erforderlich? 9. Schreiben Sie eine einfache .config-Datei, die ber einen <appSettings>-Abschnitt verfgt. Dieser Abschnitt sollte ein Element namens Color mit dem Wert Black enthalten.

bungen
1. Nehmen Sie drei weitere Lndereinstellungen in das Beispiel aus Listing 20.6 auf: Franzsisch (aus Frankreich), Dnisch und Griechisch. Kmmern Sie sich nicht um das bersetzen der eigentlichen Ausdrcke, sondern ndern Sie sie nur so, dass sie die jeweils aktuelle Kultur widerspiegeln. (In der .NET Framework-Dokumentation zur CultureInfo-Klasse finden Sie Angaben zu den Kulturcodes fr diese Sprachen.) 2. Erstellen Sie eine .config-Datei, die die jeweils zu verwendende Kultur von bung 1 angibt. So mssen Sie nicht Ihren Quellcode verndern und die Anwendung jedes Mal kompilieren, wenn Sie eine neue Kultureinstellung testen wollen. (Tipp: Nutzen Sie den <appSettings>-Abschnitt.)

680

Debugging und Profiling

1 2

Debugging und Profiling

Die heutige Lektion dreht sich um Debugging und Profiling. Debugging ist der Vorgang, bei dem jegliche Fehler in einer Anwendung beseitigt werden, gleichgltig, ob sie Ihnen bekannt sind oder nicht. Profiling (oder Profilerstellung) ist eine Technik, um Anwendungen hinsichtlich ihrer Leistung zu optimieren, indem man beobachtet, welche Leistung sie unter normalen Bedingungen aufbringen. Diese zwei Themen wurden zusammengefasst, weil zu ihnen hnliche Prozesse gehren. Zu beiden Verfahren gehrt die Technik, eine laufende Anwendung zu berwachen, damit man herausfinden kann, was genau unter der Motorhaube vor sich geht, wenn ein Benutzer die Anwendung bedient. Heute lernen Sie

die Vorteile kennen, die der Einsatz von try-catch-Blcken im Code bietet, wie man Programmdatenbankdateien fr das Debugging erzeugt, auf welche Weise JIT-Debugging funktioniert, Aspekte des CLR-Debuggers und seines Befehlszeilenvetters CorDbg kennen, die Process-Klasse einzusetzen.

21.1 Debuggen in Windows Forms


Ohne Zweifel sind Sie bei der Erstellung von Windows Forms-Anwendungen auch ber Fehler gestolpert. Fehler gehren zum Alltag des Entwicklers, daher muss man lernen, damit umzugehen. Hufig ist der Vorgang der Fehlerbeseitigung (also Debugging) zeitraubend und lstig; man durchsucht den Code Zeile fr Zeile und zuweilen sogar Zeichen fr Zeichen. Das .NET Framework erleichtert jedoch das Beheben von Fehlern. Man kann seine Software auf verschiedene Weise debuggen. Dieser Abschnitt untersucht Mglichkeiten, wie Sie die Fehlerquelle einkreisen knnen, whrend Sie noch Code schreiben. Die folgenden Abschnitte stellen eine Reihe von Werkzeugen vor, die das .NET Framework bereitstellt, um Fehler nach der Anwendungsentwicklung zu beseitigen. Zunchst ist es sinnvoll, einen Blick auf die Debugging-Strukturen zu werfen, die .NET bereitstellt. Sobald ein Fehler nach dem Kompilieren einer Anwendung auftritt, lst die CLR eine Ausnahme (Exception) aus ein einfallsreicher Ausdruck fr einen Fehler. Tritt solch ein Bug auf, wirft (throws) die CLR eine Ausnahme. .NET verfgt ber eine Exception-Klasse (und viele davon abgeleitete Klassen), die zur Darstellung jedes auftretenden Fehlers einsetzbar ist.

682

Debuggen in Windows Forms

Das ist sinnvoll, denn nun sind Fehler wirkliche Objekte, mit denen man umgeht. Man kann daher ihre Eigenschaften herausfinden, Methoden dafr aufrufen und sogar eigene Fehler erzeugen. Zur Fehlerbehandlung in Windows Forms gehrt es, diese Exception-Klassen zu finden und sie angemessen zu behandeln. Der Rest dieses Abschnitts befasst sich mit der einfachsten Methode, Exception-Klassen zu handhaben: durch den Einsatz der try-catch-Syntax des .NET Frameworks.
Try-catch-Syntax tritt in Blcken auf. Der try-Teil bedeutet, dass man Code versucht bzw. versuchsweise ausfhrt. Setzt man einen try-Codeblock ein, wird die CLR vorsichtiger bei der Codeausfhrung. Sie wird dadurch nicht langsamer, doch wenn jetzt eine Ausnahme ausgelst wird, ist die CLR vorgewarnt und verhindert einen Programmabsturz. Dies fhrt uns zum catch-Block: Er fngt jede Ausnahme ab, die ausgelst wurde. Hier kann man jede Ausnahme bearbeiten und korrigieren. Da er mit dem try-Block kombiniert wird, lassen sich nun Fehlermeldungen besonnen behandeln, statt den Benutzer dadurch aufzuregen, dass seine Anwendung abstrzt.

Die Syntax fr einen try-catch-Block ist einfach:


try { //sensibler Code } catch (Exception e) { //Code fr die Ausnahmebehandlung }

Oder in VB .NET:
Try 'sensibler Code Catch e as Exception 'Code fr die Ausnahmebehandlung End Try

Unter sensiblem Code versteht man einen Codeabschnitt, der eine Ausnahme auslsen kann oder von dem man glaubt, dass er nicht richtig funktionieren wird. Solcher Code kann alles Mgliche umfassen: von der Initialisierung einer Variablen bis hin zum Einsatz der E/ A-Fhigkeiten von .NET, um eine mglicherweise nicht vorhandene Datei zu ffnen. Das schon erwhnte catch-Statement fngt eine bestimmte Exception ab. Die zwei Codeabschnitte oben haben ein Objekt vom Typ Exception abgefangen. Man kann dies durch jede andere Klasse ersetzen, die von Exception abgeleitet ist, etwa IOException. Einzelne Ausnahmeklassen stellen bestimmte Fehlertypen dar und knnen eine Reihe von Unterausnahmen haben, die es erlauben, die Fehlerquelle weiter einzukreisen. Will man etwa auf eine nicht vorhandene Datei zugreifen, wird eine IOException ausgelst, die von der Klasse SystemException abgeleitet ist, welche wiederum von Exception abgeleitet ist (vgl. Abbildung 21.1).

683

Debugging und Profiling

System. Exception System. ApplicationException* InvalidFilterCriteriaException TargetException TargetInvocationException TargetParameterCountException System.IO.IsolatedStorage.IsolatedStorageException System.Windows.Forms.AxHost.InvalidActiveXStateException
*All sub-exceptions belong to the System.Reflection namespace **All sub-exceptions belong to the System namespace, unless otherwise noted Belongs to the System. ComponentModel namespace + Belongs to the System.Runtime.InteropServices namespace +++ Belongs to the System.Runtime namespace Belongs to the System.Security namespace Belongs to the System.Threading namespace

System.Runtime.Remoting.MetadataServices.SUDSGeneratorException System.SystemException**

AppDomainUnloadedException ArgumentException ArithmeticException ArrayTypeMismatchException BadImageFormatException CannotUnloadAppDomainException Design.Serialization.CodeDOMSerializerException+ LicenseException+ WarningException+ Configuration.ConfigurationException Configuration.Install.InstallException ContextMarshallException Data.DataException Data.DBConcurrencyException Data.SqlClient.SqlException DataSqlTypes.SqlTypeException Drawing.Printing.InvalidPrinterException EnterpriseServices.RegistrationException EnterpriseServices.ServicedComponentException ExecutionEngineException FormatException IndexOutOfRangeException InvalidCastException

InvalidOperationException InvalidProgramException IO.InternalBufferOverflowException IO.IOException Management.ManagementException MemberAccessException MulticastNotSupportedException NotImplementedException NotSupportedException NullReferenceException OutOfMemoryException RankException Reflection.AmbiguousMatchException Reflection.ReflectionTypeLoadException Resources.MissingManifestResourceException ExternalException++ InvalidComObjectException++ InvalidOleVariantTypeException++ MarshalDirectiveException++ SafeArrayRankMismatchException++ SafeArrayTypeMismatchException++ Remoting.RemotingException+++ Remoting.ServerException+++

Serialization.SerializationException+++ Cryptography.CryptographicException Policy.PolicyException SecurityException VerificationException XmlSyntaxException ServiceProcess.TimeoutException StackOverflowException SynchronizationLockException ThreadAbortException ThreadInterruptedException ThreadStateException TypeInitializationException TypeLoadException TypeUnloadedException UnauthorizedAccessException Web.Services.Protocols.SoapException Xml.Schema.XmlSchemaException Xml.XmlException Xml.XPath.XPathException Xml.Xsl.XsltException

Abbildung 21.1: Die Ausnahmenhierarchie im .NET Framework ist recht kompliziert (System.SystemException verfgt sogar

ber zwei oder gar drei Unterebenen, die hier nicht angezeigt sind).

Weil das Ausnahmeklassen von .NET hierarchisch gegliedert sind, kann man einen catchBlock fr eine bergeordnete Ausnahme benutzen und zugleich alle Ausnahmen abfangen, die von der bergeordneten abgeleitet sind. In den zwei Codebeispielen oben fngt man eigentlich jede Art von Ausnahme ab, weil die Exception-Klasse deren Urvater ist. Das bedeutet, dass man mehrere catch-Blcke pro try-Block einsetzen kann, sollte man einmal mehrere verschiedene Ausnahmetypen behandeln mssen. Beispielsweise so:
try { //sensibler Code } catch (IOException e) { //eine E/A-Ausnahme abfangen } catch (SystemException e) { //jede System-Ausnahme abfangen, darunter auch IOException und ihre gleichgeordneten Ausnahmen } catch (Exception e) { //alle Ausnahmen abfangen, einschlielich System-Ausnahmen }

684

Debuggen in Windows Forms

Sorgen Sie dafr, dass Sie beim Einsatz mehrerer catch-Blcke vom fokussiertesten zum diffusesten vorgehen: Code, der einen ganz bestimmten Fehler behandelt, sollte also vor Code kommen, der einen allgemein gehaltenen Fehler wie etwa Exception behandelt. Da nur ein catch-Block ausgefhrt wird, knnte sonst die generische Fehlerbehandlung stets vor allen anderen ausgefhrt werden. Zeit fr ein Beispiel. Listing 21.1 zeigt eine einfache Anwendung, die versucht, eine Textdatei zu ffnen und ihren Inhalt in einem RichTextBox-Steuerelement anzuzeigen. Listing 21.1: Eine einfache E/A-Anwendung
1: Imports System 2: Imports System.Windows.Forms 3: Imports System.Drawing 4: 5: Namespace TYWinForms.Day21 6: 7: Public Class Listing211 : Inherits Form 8: private rtbText as New RichTextBox 9: private btPush as new Button() 10: 11: public sub New() 12: rtbText.Height = 225 13: rtbText.Dock = DockStyle.Bottom 14: rtbText.ScrollBars = RichTextBoxScrollBars.Both 15: 16: btPush.Location = new Point(100,10) 17: btPush.Text = "ffnen!" 18: AddHandler btPush.Click, new EventHandler(AddressOf Me.OpenFile) 19: 20: Me.Text = "Listing 21.1" 21: Me.Controls.Add(rtbText) 22: Me.Controls.Add(btPush) 23: end sub 24: 25: private sub OpenFile(Sender As Object, e As EventArgs) 26: rtbText.LoadFile("c:\temp\happy.txt",  RichTextBoxStreamType.PlainText) 27: end sub 28: 29: public shared sub Main() 30: Application.Run(new Listing211) 31: end sub 32: End Class 33: End Namespace

685

Debugging und Profiling

Nach Tag 11 drfte dieses Listing recht vertraut aussehen. In den Zeilen 8 und 9 werden ein RichTextBox- und ein Button-Steuerelement erzeugt. Sie werden in den Zeilen 12 bis 18 initialisiert; das Click-Ereignis der Schaltflche hat in Zeile 25 einen Ereignishandler, der die LoadFile-Methode des RichTextBoxSteuerelements benutzt, um die Datei c:\temp\happy.txt zu ffnen und sie dem Benutzer anzuzeigen. Ist diese jedoch nicht vorhanden und fhrt man die Anwendung so aus, erhlt man die in Abbildung 21.2 gezeigte Fehlermeldung, falls man keinen Debugger installiert hat, oder die Fehlermeldung in Abbildung 21.3, falls man doch einen Debugger hat.

Abbildung 21.2: Die Standardroutine fr die Ausnahmebehandlung lsst Ihnen die Mglichkeit, fortzufahren und den Fehler zu ignorieren oder die Anwendung zu beenden.

Abbildung 21.3: Falls Sie einen JIT-Debugger installiert haben, knnen Sie die Anwendung debuggen. Beachten Sie den Ausnahmetyp, der ausgelst wird.

Wren Sie der Benutzer dieser Anwendung, drften Sie angesichts der in Abbildung 21.2 bzw. 21.3 wiedergegebenen Meldung(en) ziemlich frustriert sein. Man muss den Fehler so abfangen, dass der Benutzer eine hilfreiche Fehlermeldung erhlt, falls berhaupt. ndern Sie die OpenFile-Methode in Zeile 25 des Listings 21.1 so ab, dass sie wie in folgendem Codestck aussieht:

686

Debuggen in Windows Forms

private sub OpenFile(Sender As Object, e As EventArgs) Try rtbText.LoadFile("c:\temp\happy.txt", RichTextBoxStreamType.PlainText) Catch ex As Exception MessageBox.Show("Diese Datei existiert nicht!") End Try end sub

Klickt der Benutzer nun auf die Schaltflche und ist die Datei nicht vorhanden, erhlt er eine Meldung wie in Abbildung 21.4.

Abbildung 21.4: Bei der Verwendung eines Try-Catch-Blocks knnen Sie eine weitaus benutzerfreundlichere Meldung erzeugen.

Klickt der Benutzer auf die OK-Schaltflche, fhrt die Anwendung fort, als wre die Fehlermeldung nie ausgelst worden fast so, als wre die OpenFile-Methode nie ausgefhrt worden. Eine Meldung wird innerhalb des Catch-Blocks angezeigt, aber im Grunde knnte man hier alles Mgliche tun. So knnte man beispielsweise den Text abndern, um den Benutzer zu fragen, ob er die Datei erstellen mchte, falls es sie nicht gibt, und dies mit Hilfe der E/A-Fhigkeiten von .NET ausfhren. Der try-catch-Block hat einen weiteren, zustzlichen Teil: das finally-Statement. Dessen Syntax folgt der des catch-Statements. finally wird benutzt, um Code auszufhren, der immer laufen muss, gleichgltig, ob der Code in einem try-Block funktioniert oder nicht. Das folgende Codebeispiel erzeugt ein Array von Zeichenfolgen und versucht, ein FileInfo-Objekt aus jeder Zeichenfolge zu erzeugen. Ist die Datei vorhanden, wird eine Meldung mit der Angabe ihrer Lnge angezeigt. Gibt es sie aber nicht, wird eine benutzerfreundliche Meldung gezeigt. Im Meldungsfeld befindet sich die Anzahl der verarbeiteten Zeichenfolgen, gleichgltig, ob ein FileInfo-Objekt erzeugt wurde oder nicht.
int i = 0; FileInfo tmpFile; String[] arrFileStrings = new String[] {"c:\\temp\\happy.txt", "c:\\winforms\\day21\\listing21.1.vb"};

687

Debugging und Profiling

foreach (String strName in arrFileStrings) { try { tmpFile = new FileInfo(strName); MessageBox.Show("Lnge von " + strName + ": " + tmpFile.Length.ToString()); } catch (Exception ex) { MessageBox.Show(strName + " ist keine gltige Datei!"); } finally { i++; MessageBox.Show(i.ToString() + " Dateien verarbeitet"); } }

Alle Ausnahmeklassen haben einige ntzliche Eigenschaften, die zustzliche Informationen ber den betreffenden Fehler liefern. Tabelle 21.1 beschreibt die Eigenschaften der Exception-Klasse, die ja allen Unterausnahmeklassen vererbt werden.
Eigenschaft
HelpLink

Beschreibung Liefert eine optionale Datei, die mit der betreffenden Ausnahme verknpft ist. Das Format muss ein URL sein. Zum Beispiel:
file://C://TYWinforms/Day21/help.html#ErrorNum42

InnerException

Wenn eine Ausnahme eine weitere auslst liefert diese Eigenschaft einen Hinweis auf die Ursprungsausnahme. Die benutzerfreundliche Meldung, die die Ausnahme beschreibt. Der Name der Anwendung oder des Objekts, das die Ausnahme erzeugte. Die Zeichenfolgendarstellung aller Stapelrahmen in der Aufrufliste zum Zeitpunkt der Ausnahmeauslsung. Diese Eigenschaft teilt Ihnen mit, welche Methoden gerade ausgefhrt wurden. (Eine Methode ruft oftmals eine weitere auf, so dass Sie nun der Spur zurck bis zur Wurzelmethode des Problems folgen knnen.) Liefert die Methode, die die Ausnahme verursachte.

Message Source StackTrace

TargetSite

Tabelle 21.1: Eigenschaften von Exception

Statt zu warten, bis eine Fehlermeldung auftritt, knnen Sie auch jederzeit Ihre eigenen Fehler erzeugen, indem Sie das throw-Statement verwenden. Erzeugen Sie einfach eine neue Ausnahme vom gewnschten Typ und schon lsst sich Ihre eigene Anwendung sabotieren:
throw(new Exception());

Warum sollte man so etwas tun, fragt man sich. Das tut man am ehesten, wenn man benutzerdefinierte Windows Forms-Steuerelemente erstellt hat und auch das Verhalten im Fehlerfall prfen mchte. Das Schlsselwort throw eignet sich ausgezeichnet fr diesen

688

JIT-Debugging

Zweck. Man kann die Message- und InnerException-Eigenschaften fr neu erstellte Ausnahmen festlegen, die die behandelnde Anwendung dann ebenso benutzen kann, wie Sie es bereits mit den eingebauten Ausnahmen getan haben. Im Allgemeinen sollte man einen try-catch-Block immer dann einsetzen, wenn man Code ausfhrt, von dem nicht sicher ist, dass er garantiert sicher ausgefhrt wird, wie etwa Datenbankabfragen, Dateioperationen, Netzwerkanforderungen und Operationen, die von bestimmten Benutzereingaben abhngen. Im ganzen Buch wurden try-catch-Blcke an den meisten Stellen, wo sie sich htten befinden sollen, weggelassen, doch da Sie nun wissen, wie man deren Syntax benutzt, sollten Sie nicht zgern, sie einzusetzen.
Ich empfehle Setzen Sie try-catch-Blcke immer dann ein, wenn Sie Code ausfhren, von dem Sie nicht sicher sind, dass er richtig ausgefhrt wird. Bitte beachten Sie Verlassen Sie sich nicht auf try-catch-Blcke, um Benutzereingaben auf Gltigkeit zu prfen. Wenn Sie dem Benutzer ein Textfenster anzeigen, in das er eine Zahl eintragen soll, ist zu prfen, ob die Eingabe wirklich eine Zahl ist, bevor man den Code ausfhrt, der von der Zahl abhngt. Versuchen Sie nicht, solchen Code auszufhren, in der Hoffnung, dass der trycatch-Block ihn schon richtig behandeln werde.

21.2 JIT-Debugging
Just-In-Time-Debugging ist, wie bereits erwhnt, der Vorgang, bei dem die Fehler in der Anwendung beseitigt werden, whrend diese gerade luft und die Fehler nacheinander auftreten. Whrend try-catch-Blcke dafr geeignet sind, Bugs zu beseitigen, bevor der Benutzer sie zu sehen bekommt, wurde JIT-Debugging dafr entworfen, den Benutzer auf die Fehler aufmerksam zu machen, damit sie beseitigt werden knnen. In diesem Fall ist der Benutzer natrlich der Entwickler (der einzige, der die Fehler beheben kann). Luft die Anwendung, fhrt der Computer kompilierten Code aus. Dieser Code besteht aus Anweisungen, erzeugt bei der Ausfhrung Variablen, weist diesen Werte zu und hebt sie auch wieder auf. Alle diese Variablen werden natrlich im Speicher abgelegt. Wenn man sagt, ein Debugger wird angehngt, so ist damit gemeint, dass ein JIT-Debugger den Speicher berwacht, den die betreffende Anwendung belegt. Der Debugger kann die Anweisung und den belegten Speicher auswerten und dem Entwickler in verstndlicher Form mitteilen, was los ist.

689

Debugging und Profiling

Weil der JIT-Debugger nicht das Innenleben der gerade laufenden Anwendung kennen kann (da er nicht auf deren Quellcode zugreifen kann), braucht er etwas Untersttzung, um Informationen liefern zu knnen. Diese Hilfe bekommt er von einer Programm-Datenbankdatei (.pdb). Sie enthlt Debugging-relevante Informationen wie etwa ber Methoden und Variablen im Programm. Eine .pdb-Datei zu erstellen, ist leicht: Fgen Sie den /debug-Parameter in die Befehle fr den Befehlszeilencompiler ein:
vbc /t:winexe /r:system.dll,... /debug temp.vb

Durch dieses Kommando wird eine neue Datei namens temp.vb erzeugt. Versucht ein JITDebugger Fehler in einer Anwendung zu beseitigen in diesem Fall in temp.exe , benutzt er den Inhalt der .pdb-Datei, um ihm zu helfen. Zur Aktivierung des JIT-Debuggings ist noch ein weiterer Schritt ntig. Erinnern Sie sich an .config-Dateien und das Konfigurationselement <system.windows.forms>. Hierin ist auch JIT-Debugging zu aktivieren. Beispielsweise knnte die .config-Datei fr temp.exe wie folgt aussehen:
<configuration> <system.windows.forms jitDebugging="true"/> </configuration>

Als Nchstes lernen Sie alle .NET-Werkzeuge kennen, die fr das Debugging zur Verfgung stehen.

Das Werkzeug DbgClr


Der Common Language Runtime Debugger (DbgClr), erlaubt das Anhngen an einen Prozess und so die berwachung und Kontrolle der Ausfhrung einer Anwendung. Dieses Programm ist blicherweise im Verzeichnis c:\Programme\Microsoft.NET\FrameworkSDK\GuiDebug abgelegt und heit DbgClr.exe. Sie knnen sowohl diesen Debugger an eine bereits laufende Anwendung anhngen als auch eine neue Anwendung aus dem Debugger heraus starten. Im zweiten Fall startet man ganz von vorn und ffnet den Debugger, indem man DbgClr.exe ausfhrt. Sie sollten etwas sehen, das wie das in Abbildung 21.5 Gezeigte aussieht. Nachdem Sie den Debugger geffnet haben, fhren Sie die drei Schritte des DebuggingProzesses aus: 1. ffnen Sie die zu bereinigende Anwendung oder hngen Sie den Debugger an die bereits laufende Anwendung an. 2. Setzen Sie Haltepunkte. 3. Setzen Sie den Debugger ein, um die Anwendung zu bearbeiten.

690

JIT-Debugging

Abbildung 21.5: Die Benutzeroberflche des CLR-Debuggers hnelt ein wenig der von Visual Studio .NET.

Vorausgesetzt, Sie haben Listing 21.1 als listing21.1.vb gespeichert, ffnen Sie die Datei bitte aus dem CLR-Debugger heraus, indem Sie in das Men DATEI/FFNEN/DATEINAME gehen und diese Datei auswhlen. Nach dem ffnen sollten Sie den Quellcode im Debugger sehen. Die Datei erscheint im PROJEKTMAPPEN EXPLORER auf der rechten Seite des Fensters. In dieser Datei knnen Sie den Debugging-Prozess verfolgen. Um mit der Fehlerbeseitigung zu beginnen, whlen Sie ZU DEBUGGENDES PROGRAMM aus dem DEBUGGEN-Men aus und geben den Namen der ausfhrbaren Datei ein (c:\winforms\day21\listing21.1.exe). Jetzt wei der Debugger, welche Anwendung mit dem geffneten Quellcode verknpft ist. Drcken Sie die Funktionstaste (F5), um die Anwendung zu starten, oder gehen Sie ins DEBUGGEN-Men und klicken Sie auf STARTEN. Die Anwendung wird ganz normal angezeigt und der CLR-Debugger steht bereit. Ist jedoch die Anwendung bereits gestartet, gehen Sie in das Men EXTRAS im CLRDebugger und whlen den Befehl DEBUGPROZESSE aus. Ein Listing wie in Abbildung 21.6 erscheint. Whlen Sie in der Liste Ihre Anwendung aus und klicken Sie auf ANFGEN. Daraufhin erscheint die Anwendung im unteren Teil des Dialogfeldes im Bereich GEDEBUGGTE PROZESSE in Abbildung 21.6. Klicken Sie auf die UNTERBRECHEN-Schaltflche, um die Ausfhrung anzuhalten und den Code anzusehen. Das Anfgen an eine bereits laufende Anwendung ist sinnvoll, wenn man nicht ber den entsprechenden Quellcode verfgt (wiewohl man nicht die Anwendung ohne den Quellcode ndern kann). Ein neues Fenster namens AUSGABE ist nun im CLR-Debugger verfgbar. Dieses Fenster zeigt, welche Module fr die Anwendung geladen wurden. Das kann dazu beitragen, die Abhngigkeiten der Anwendung festzustellen.

691

Debugging und Profiling

Abbildung 21.6: Hngen Sie Ihrer Anwendung den Debugger durch einen Klick auf ANHNGEN an.

Wenn Sie sich mit einem Debugger in einen Prozess einklinken, sollten Sie sicherstellen, dass keine wichtige Anwendung luft. Der Grund: Die Anwendung wird bei diesem Vorgang eingefroren und jede nicht gespeicherte Information kann verloren gehen. Ein Haltepunkt ist eine Stelle im Code, an der die Ausfhrung angehalten wird: Eine Pause tritt ein. Haltepunkte sind ntzlich fr die Fehlerbeseitigung in bestimmten Codezeilen. Wssten Sie etwa, dass eine solche Zeile Fehler erzeugt, knnten Sie direkt davor einen Haltepunkt setzen und daraufhin den Debugger einsetzen, um die fraglichen Anweisungen und Variablen zu untersuchen. Um einen Haltepunkt zu setzen, klicken Sie auf die linke Spalte neben dem Quellcode Ihrer Datei. Ein roter Punkt sollte erscheinen, der anzeigt, dass die Ausfhrung in dieser Zeile stoppt. Halten Sie den Mauszeiger direkt ber den Haltepunkt, um zu sehen, wo genau der Stopp passieren wird (vgl. Abbildung 21.7). Jetzt knnen Sie Ihre Anwendung wieder normal bedienen. Haben Sie keine Haltepunkte gesetzt und gibt es keine Fehlermeldungen, drfte der Code so rasch ausgefhrt werden, dass Sie nichts sehen knnen, wenn es passiert. Obwohl dies ein Hinweis ist, dass die Anwendung reibungslos funktioniert, hilft Ihnen das bei der Fehlerbeseitigung keineswegs. Um die Anwendung zu untersuchen, klicken Sie auf die DEBUGGEN BEENDEN-Schaltflche (oder drcken () + (F5)) und setzen am Anfang der OpenFile-Methode in Zeile 25 (Listing 21.1) einen Haltepunkt. Drcken Sie (F5) erneut.

692

JIT-Debugging

Abbildung 21.7: Wenn Sie in Ihrem Code einen Haltepunkt setzen, wird die Ausfhrung vorbergehend an der angegebenen Zeile gestoppt.

Die Anwendung startet ganz normal, doch sobald man auf die Schaltflche klickt, hlt die Anwendung an und der Debugger bernimmt die Kontrolle. Das Ausgabefenster rechts unten verndert sich, um die aktuell geladenen Dateien und Assemblies anzuzeigen. Dies verrt Ihnen alle Module, die von der Anwendung betroffen sind. Das LOKALFenster unten links (man muss eventuell auf die LOKAL-Registerkarte klicken, um es zu sehen) zeigt alle aktuellen Variablen sowie deren Werte an. Verwenden Sie das LOKALFenster, um festzustellen, ob die Variablen richtig eingestellt sind. Wenn Sie z.B. nicht herausfinden knnen, warum die Titelzeile nicht richtig angezeigt wird, schauen Sie im LOKALFenster unter this (Me in VB .NET), System.Windows.Forms.Form, WindowText nach und prfen dort den eingetragenen Wert. Widerspricht er Ihren Erwartungen, wissen Sie, dass etwas vor diesem Haltepunkt schief gelaufen ist. Im LOKALFenster kann man sogar die Werte ndern, obwohl diese nderungen nicht dauerhaft sind; man verliert sie, sobald die Fehlerbeseitigung beendet ist. Diese Information kann jedenfalls bei der Problemlsung hilfreich sein. Der CLR-Debugger lsst sich jedoch nicht als Editor missbrauchen: Man kann damit keinen Quellcode ndern. Zu diesem Zweck mssen Sie den WindowsEditor (NotePad) oder einen anderen bevorzugten Texteditor einsetzen, z.B. in Visual Studio .NET. Hat die Anwendung bei einem Haltepunkt angehalten, ffnen Sie das BEFEHLSFenster (unten rechts, oder whlen Sie FENSTER, DIREKT aus dem DEBUGGEN-Men). In diesem Fenster knnen Sie Befehle Ihrer Anwendung ausfhren, als ob sie sich im Quellcode

693

Debugging und Profiling

befnden. Tippen Sie in das BEFEHLSFenster MessageBox.Show("Gruss aus dem Befehlsfenster") und drcken Sie (). Der Debugger fhrt den Befehl aus und zeigt das Meldungsfeld an. Von diesem Fenster kann man praktisch jeden Befehl ausfhren lassen. Als Nchstes ffnen Sie das BERWACHEN-Fenster (aus dem DEBUGGEN/FENSTER-Men whrend des Debugprozesses) und geben den Namen einer Variablen ein, die in Ihrer Seite benutzt wird (etwa rtbText), dann drcken Sie (). Nun knnen Sie die Variable beobachten, whrend Sie sich durch Ihre Anwendung bewegen, und sehen, ob ihr stets ein richtiger Wert zugewiesen wird. Tippen Sie Me in das BERWACHEN-Fenster ein, knnen Sie alle Variablen sehen, die zu Me gehren. Diese beiden Fenster eignen sich gut dazu, jederzeit Informationen ber die Anwendung zu liefern. Doch manchmal muss man die Applikation in Aktion sehen und nicht im eingefrorenen Zustand. Am oberen Rand des CLR-Debugger-Fensters befinden sich Schaltflchen, mit denen Sie die Ausfhrung der Anwendung in Echtzeit steuern knnen, so etwa das Springen zur nchsten Codezeile und das vollstndige Beenden der Fehlerbeseitigung. Neben dem gelben Pfeil NCHSTE ANWEISUNG ANZEIGEN befinden sich drei Schaltflchen: EINZELSCHRITT, PROZEDURSCHRITT und AUSFHREN BIS RCKSPRUNG. Damit knnen Sie durch die Ausfhrung der Anwendung navigieren. Haben Sie etwa einen Haltepunkt auf eine Methode gesetzt, hlt ein Klick auf PROZEDURSCHRITT die Ausfhrung bei jedem Statement in der Methode an. Das ist hilfreich, wenn man jede auszufhrende Zeile sehen muss, doch es knnte etwas lstig werden, vor allem, wenn man so durch sehr viel Code waten muss. Wenn Sie auf einen Methodenaufruf stoen, schickt der Debugger Sie nicht etwa zur entsprechenden Methode, sondern springt darber. EINZELSCHRITT gleicht PROZEDURSCHRITT ein wenig, nur dass man in verschiedene Richtungen im Code verzweigen kann (etwa in andere Methoden), wenn es die Codelogik erfordert. Ein Klick auf die AUSFHREN BIS RCKSPRUNG -Schaltflche, und die Anwendung fhrt die aktuelle Methode ohne anzuhalten aus, bis sie zum Beginn der nchsten Methode gelangt. Mit AUSFHREN BIS RCKSPRUNG knnen Sie also ganze Methoden berspringen, wenn Sie sicher wissen, dass sie in Ordnung sind. Zur Linken der SCHRITT-Schaltflchen befinden sich Steuerelemente, die wie die Steuertasten eine Rekorders aussehen: WEITER, ALLE UNTERBRECHEN, DEBUGGEN BEENDEN und NEU STARTEN. Diese Schaltflchen tun genau das, was auf ihnen steht, so dass man damit den Debug-Vorgang als Ganzes steuern kann.

Das Werkzeug CorDbg


CorDbg.exe ist praktisch die Befehlszeilenversion des JIT-Debuggers DbgClr.exe. Er funkti-

oniert auf die gleiche Weise, doch statt eine grafische Benutzeroberflche zu bedienen, muss man Befehle ber die Eingabeaufforderung erteilen. Die Bedienung ist daher ein wenig unkomfortabler und das Tool wird hier nicht eingehend besprochen.

694

JIT-Debugging

Um das Programm CorDbg.exe zu starten, ohne zuerst eine Anwendung zu laden, geben Sie lediglich CorDbg in der Befehlszeile ein. Dann erteilen Sie den Befehl
run anwendung name

Stellen Sie sicher, dass sich die zu prfende Anwendung im selben Verzeichnis befindet, aus dem heraus Sie den Befehl erteilt haben. Verwendet man listing21.1.exe, sollte man etwas wie in Abbildung 21.8 zu sehen bekommen.

Abbildung 21.8: CorDbg ist ein befehlszeilengesttzter Debugger, mit dem Sie der Ursache eines Problems auf den Grund gehen knnen.

Die Anwendung wird nun an der Main-Methode gestartet und wieder angehalten. Um zur nchsten Zeile der Ausfhrung zu gelangen, tippt man den Befehl si ein. So kann man sich zeilenweise durch die Anwendung bewegen, was etwas nervend ist. Ausfhrungszeilen sind mitunter etwas anderes als Codezeilen. Um sich zeilenweise durch Code zu bewegen, gibt man den Befehl next ein. Wenn man sich durch die Anwendung bewegt, sieht man zuweilen merkwrdige Bildschirmausgaben wie diese:
(cordbg) next [0044] call (cordbg) next [004a] mov (cordbg) next [004c] call dword ptr ds:[02FF17B8h]

ecx,ebx

dword ptr ds:[02FF2C18h]

Diese Befehle verraten Ihnen, was im Speicher vor sich geht, wenn die Anwendung luft. dword ptr ist z.B. eine spezielle Anweisung, die auf eine Stelle im Arbeitsspeicher zeigt. Meistens hat Sie diese Information nicht zu interessieren, doch wenn Sie zu Ausfhrungsprozessen auf niederer Ebene hinuntergehen wollen, so erhalten Sie hierdurch Analysedaten. Informationen ber die Befehle in CorDbg.exe finden Sie in der Dokumentation zum .NET Framework.

695

Debugging und Profiling

21.3 Profiling Ihrer Anwendung


Profiling ist der Vorgang, bei dem eine Anwendung beobachtet und ihre verwendeten Ressourcen untersucht werden. Dieses Vorgehen ist sinnvoll, wenn man die Leistung einer Anwendung optimieren muss, weil man hier bemerkt, wo Engpsse auftreten und welche Codeabschnitte relativ lange brauchen, bis sie ausgefhrt sind. Auf Grund dieser Informationen kann man seinen Quellcode neu strukturieren oder umschreiben, um mehr Leistung zu erzielen eine nderung, die stets willkommen sein drfte. Leider steht fr diese Aufgabe kein Werkzeug zur Verfgung. Doch dank der ProcessKlasse knnen Sie immerhin Ihr eigenes erstellen. Ein Prozess stellt auf Ihrem Computer eine ausgefhrte Anwendung dar. Fr die Prozesse sind Threads ttig und Prozesse werden im Windows Task-Manager angezeigt (vgl. Abbildung 21.9).

Abbildung 21.9: Der Task-Manager von Windows zeigt alle aktuell gestarteten Prozesse.

Die Process-Klasse lsst sich dazu einsetzen, jeden gerade ausgefhrten Prozess zu untersuchen. Sie liefert Informationen wie etwa ber die Speichermenge, die eine Anwendung beansprucht, ihren Anteil an Prozessorzeit und die Start- und Schlusszeiten einer bestimmten Methode. Somit sehen Sie nicht nur, wie die Leistung der Anwendung aussieht, sondern auch, wie sie gegenber anderen Prozessen auf dem Computer abschneidet.

696

Profiling Ihrer Anwendung

Sehen Sie sich erst einmal eine Beispielanwendung an. Listing 21.2 verwendet die Process-Klasse zur Anzeige von statistischen Angaben ber die aktuelle Anwendung. Listing 21.2: Prozesse berwachen
1: using System; 2: using System.Windows.Forms; 3: using System.Drawing; 4: 5: using System.Diagnostics; 6: 7: namespace TYWinforms.Day21 { 8: public class Listing212 : Form { 9: private Label lblProcessor = new Label(); 10: private Label lblMemory = new Label(); 11: private Label lblTotProcessor = new Label(); 12: private Process objProcess = new Process(); 13: private System.Windows.Forms.Timer tmrProcess = new  System.Windows.Forms.Timer(); 14: 15: public Listing212() { 16: lblProcessor.Size = new Size(250,20); 17: lblProcessor.Location = new Point(10,10); 18: 19: lblMemory.Size = new Size(250,20); 20: lblMemory.Location = new Point(10,30); 21: 22: lblTotProcessor.Size = new Size(250,20); 23: lblTotProcessor.Location = new Point(10,50); 24: 25: tmrProcess.Tick += new EventHandler(this.Update); 26: tmrProcess.Interval = 500; 27: tmrProcess.Start(); 28: 29: objProcess = Process.GetCurrentProcess(); 30: 31: this.Text = ".NET Profiler"; 32: this.Controls.Add(lblProcessor); 33: this.Controls.Add(lblMemory); 34: this.Controls.Add(lblTotProcessor); 35: } 36: 37: private void Update(Object Sender, EventArgs e) { 38: objProcess.Refresh();

697

Debugging und Profiling

39: lblMemory.Text = "Speicher: " +  objProcess.PrivateMemorySize.ToString(); 40: lblProcessor.Text = "Private Prozessorzeit: " +  objProcess.PrivilegedProcessorTime.ToString(); 41: lblTotProcessor.Text = "Gesamte Prozessorzeit: " +  objProcess.TotalProcessorTime.ToString(); 42: } 43: 44: public static void Main() { 45: Application.Run(new Listing212()); 46: } 47: } 48: }

Zunchst sind wie immer die richtigen Namensrume zu importieren. In Zeile 5 importiert man den Namensraum System.Diagnostics, der die Process-Klasse enthlt. In den Zeilen 9 bis 13 erstellen Sie ein paar Steuerelemente, darunter ein Process-Objekt und einen Timer. Das Objekt beobachtet noch keine Prozesse, sondern ist noch unttig. Das wird sich in Zeile 29 ndern. Im Konstruktor in den Zeilen 16 bis 23 initialisieren Sie die verschiedenen
Label-Steuerelemente, die erstellt wurden. Jedes Bezeichnungsfeld zeigt einen Aspekt des zu beobachtenden Prozesses an. Der Timer ist so eingestellt, dass er jede halbe Sekunde ausgelst wird (Zeile 26), damit Sie so die Label-Steuerele-

mente aktualisieren knnen; sonst wrden Sie keine zutreffende Statistik erhalten. Der Tick-Ereignishandler ist die Update-Methode in Zeile 37 (gleich mehr dazu). In Zeile 29 rufen Sie die Methode GetCurrentProcess der Process-Klasse auf. Diese Zeile veranlasst das neue Process-Objekt dazu, den Prozess, der die Anwendung darstellt, zu berwachen. Sie betrachten also die Statistik der mit Listing 21.2 erstellten Anwendung. Obwohl das Beobachten des Prozesses der aktuellen Anwendung angenehm ist, so ist es doch nicht immer mglich oder sinnvoll. Man kann eine Reihe anderer Methoden der Process-Klasse benutzen, um Prozesse anderer Anwendungen zu berwachen. GetProcessById etwa beschafft einen Prozess anhand der Identifikation im Betriebssystem. (Wenn Sie die Prozess-ID [PID] nicht im Task-Manager sehen, klicken Sie auf das ANSICHT-Men, whlen SPALTEN AUSWHLEN und im Auswahlfenster PID (PROZESS-ID).) Diese Methode eignet sich gut, wenn man die PID des zu beobachtenden Prozesses kennt, aber das ist nicht immer der Fall. GetProcesses liefert alle gerade im System ablaufenden Prozesse, so dass man mehrere Anwendungen zugleich beobachten kann. GetProcessesByName schlielich liefert ein Array von Processes, die dem angegebenen

698

Profiling Ihrer Anwendung

Namen entsprechen. Dieser Name ist meist der der ausfhrbaren Datei der Anwendung, aber ohne die Erweiterung .exe. Wrde man z.B. listing21.1 .exe ausfhren, knnte man ihren Prozess mit folgendem Befehl beobachten:
objProcess = Process.GetProcessesByName("listing21.1")[0];

Denken Sie daran, dass diese Methode ein Array liefert. Sie mssen also angeben, welches Element im Array Sie beobachten wollen. Meist ist im Array nur ein Element vorhanden; gibt man also [0] an (oder (0) in VB .NET), dann funktioniert das gut. Der Prozess muss gerade ablaufen, sonst erhalten Sie eine Fehlermeldung (eine gute Stelle fr einen try-catch-Block). Springen Sie nun nach unten zur Update-Methode. Wenn man anfangs eine der Eigenschaften der Process-Klasse anzeigt, dann holt man einen Schnappschuss dieser Eigenschaft. Anders ausgedrckt: Die Eigenschaften aktualisieren sich nicht selbst. Daher muss man die Process.Refresh-Methode aufrufen, um aktualisierte Werte zu beschaffen, wie in Zeile 38 zu sehen. Die Zeilen 39 bis 41 schlielich zeigen diverse Eigenschaften des aktuell ablaufenden Prozesses an. Abbildung 21.10 zeigt das Ergebnis, nachdem die Anwendung fr einige Sekunden ausgefhrt worden ist.

Abbildung 21.10: Sie knnen zusehen, wie die Ressourcenstatistik Ihrer Anwendung dynamisch aktualisiert wird.

Die Process-Klasse liefert etliche Informationen ber eine bestimmte Anwendung, von der beanspruchten Prozessorzeit bis zur Titelzeile des aktuell geffneten Fensters. Tabelle 21.2 fhrt alle ntzlichen Eigenschaften von Process auf; es sind eine ganze Menge.

699

Debugging und Profiling

Eigenschaft
BasePriority ExitCode

Beschreibung Liefert die Prioritt des aktuellen Prozesses (vgl. Tag 19). Ein Integer, den eine Anwendung liefert, wenn sie beendet wird. Null bedeutet meist, dass das Schlieen normal erfolgte; andere Werte knnen auf Fehler hinweisen. Gibt die Zeit an, zu der der verknpfte Prozess endete. Liefert den Handle, der mit einem Prozess verknpft ist. Liefert die Anzahl der Handles, die mit einem Prozess verknpft sind. Gibt an, ob der Prozess angehalten wurde. Gibt den Wert der Prozess-Identittsnummer (PID) an. Gibt den Namen des Rechners an, auf dem der Prozess gerade luft. Liefert ein ProcessModule-Objekt, das das Modul darstellt, welches die Anwendung startete (normalerweise eine .exe- oder .dll-Datei). Gibt den Handler fr das Primrfenster des Prozesses an. Gibt die Titelzeile des Primrfensters des Prozesses an. Gibt den maximalen Speicherplatz an, der fr einen vorliegenden Prozess reserviert wird. Gibt den minimalen Speicherplatz an, der fr einen vorliegenden Prozess reserviert wird: Das Speichervolumen, das eine Anwendung nutzt, sinkt nie unter diese Marke. Liefert ein Array von ProcessModule-Objekten, die eine Anwendung geladen hat (z.B. .exe- und .dll-Dateien) der nicht der Auslagerungsdatei zugeschlagen werden kann.

ExitTime Handle HandleCount HasExited Id MachineName MainModule

MainWindowHandle MainWindowTitle MaxWorkingSet

MinWorkingSet

Modules

NonpagedSystemMemorySize Gibt den Speicherumfang an, der einem Prozess zugewiesen ist und

PagedMemorySize

Gibt den Speicherumfang an, der einem Prozess zugewiesen ist und der der Auslagerungsdatei zugeschlagen werden darf. Gibt die Menge des Systemspeichers an, die dem Prozess zugewiesen ist und die in die Auslagerungsdatei geschrieben werden kann. Gibt die maximale Speichermenge an, die in die Auslagerungsdatei geschrieben wird, welche einem Prozess zugewiesen wurde.

PagedSystemMemorySize

PeakPagedMemorySize

Tabelle 21.2: Prozessbezogene Eigenschaften von Process

700

Profiling Ihrer Anwendung

Eigenschaft
PeakVirtualMemorySize

Beschreibung Gibt die maximale Menge an virtuellem Speicher an, die ein Prozess verlangt hat. Gibt die maximale Menge an virtuellem Speicher an, die ein Prozess zu einem Zeitpunkt verlangt hat. Gibt an, ob dem Prozess vorbergehend eine hhere Prioritt eingerumt (und er so schneller ausgefhrt) werden soll, wenn sein Hauptfenster den Fokus hat. Ein PriorityClass-Objekt, das den Priorittsgrad darstellt, den ein Prozess besitzt wie etwa Normal oder High). Gibt die Speichermenge an, die dem aktuellen Prozess zugewiesen ist, und die andere Anwendungen nicht teilen knnen. Gibt die fr einen Codeverarbeitungsprozess im Betriebssystemkern verbrauchte Prozessorzeit an. Der Name des aktuellen Prozesses. Gibt die CPUs an, auf denen ein Prozess in einem Mehrprozessorsystem laufen kann. Gibt an, ob die Benutzeroberflche des aktuellen Prozesses auf Benutzereingaben reagiert (oder ob sich das Programm aufgehngt hat). Beschafft ein StreamReader-Objekt, um Fehlermeldungen der Anwendung zu lesen. Holt ein StreamWriter-Objekt, das sich dazu verwenden lsst, dem Prozess Eingaben zu schicken. Holt ein StreamReaderObjekt, um Standardausgaben der Anwendung zu lesen. Informationen, die mit dem Prozess verbunden sind, wenn er gestartet wird (so etwa der Name der ausfhrbaren Datei, die den Prozess startet). Der Zeitpunkt, zu dem der Prozess gestartet wurde. Die Thread-Objekte, die mit dem aktuellen Prozess verknpft sind. Gibt die gesamte Zeit an, die die CPU fr die Ausfhrung dieses Prozesses aufgewendet hat (also die Summe aus den Werten UserProcessorTime und PrivilegedProcessorTime).

PeakWorkingSet

PriorityBoostEnabled

PriorityClass

PrivateMemorySize

PrivilegedProcessorTime

ProcessName ProcessorAffinity

Responding

StandardError

StandardInput

StandardOutput

StartInfo

StartTime Threads TotalProcessorTime

Tabelle 21.2: Prozessbezogene Eigenschaften von Process (Forts.)

701

Debugging und Profiling

Eigenschaft
UserProcessorTime

Beschreibung Gibt die gesamte Zeit an, die die CPU fr die Ausfhrung von Code innerhalb der Anwendung, die von diesem Prozess reprsentiert wird, aufgewendet hat. Gibt die aktuelle Gre des virtuellen Speichers des gegenwrtigen Prozesses an. Gibt an, wie viel Speicher der damit verbundene Prozess gerade beansprucht.

VirtualMemorySize

WorkingSet

Tabelle 21.2: Prozessbezogene Eigenschaften von Process (Forts.)

Bei all den verschiedenen Eigenschaften ist es mitunter schwierig herauszufinden, welche man fr das Profiling einsetzen soll. Das hngt im Grunde von der Art der zu erstellenden Anwendung ab. Handelt es sich etwa um ein Datenbankprogramm, will man sicher die verschiedenen speicherrelevanten Eigenschaften sehen. Ein hoher PrivateMemorySizeWert knnte bedeuten, dass man umfangreiche Datenbankinformationen im Speicher hlt, was nicht unbedingt eine schlechte Idee ist. Ein hoher VirtualMemorySize-Wert bedeutet, dass sich eine groe Informationsmenge, die sich im Speicher befinden sollte, noch auf der Festplatte (in der Auslagerungsdatei) befindet, was der Leistung abtrglich ist. Vielleicht beschaffen Sie zu viele Daten aus der Datenbank und ein Teil davon wird auf die Festplatte ausgelagert. Eine mgliche Lsung besteht darin, nach und nach kleinere Datenmengen zu holen. Entwerfen Sie eine Anwendung mit komplizierten Berechnungen darin, so sollten Sie die verschiedenen Eigenschaften beobachten, die die Prozessorzeit betreffen. Eine 3D-CADAnwendung beispielsweise muss zahlreiche komplizierte Berechnungen der linearen Algebra ausfhren, die Prozessorzeit fressen. Man kann die Eigenschaft UserProcessorTime berwachen, um herauszufinden, wie viel Prozessorzeit Ihre Berechnungen verschlingen. Auerdem lsst sich beobachten, welche Auswirkungen das Hinzufgen weiterer Threads auf die Leistung der Anwendung hat.

21.4 Zusammenfassung
Debugging und Profiling sind zwei Schritte in der Anwendungsentwicklung, die leider hufig bergangen werden. Beide sind jedoch notwendig, um sicherzustellen, dass eine Anwendung stabil und effizient luft. Mit Hilfe des .NET Frameworks lassen sich beide Schritte leicht realisieren.

702

Fragen und Antworten

Als Erstes haben Sie den try-catch-Block kennen gelernt. Damit knnen Sie Fehler behandeln, die nicht immer vorherzusehen sind. Solche Fehler knnen in Code vorkommen, der mit E/A-Operationen zu tun hat, oder in Code, der von bestimmten Benutzereingaben abhngt. Man kann schlielich nie vorhersagen, was ein Benutzer tun wird. Als Just-In-Time- (JIT-) Debugging wird der Vorgang bezeichnet, bei dem einer laufenden Anwendung ein Debugger angehngt wird, damit Sie den Ablauf ihrer Ausfhrung beobachten knnen. Der CLR-Debugger und CorDbg sind zwei JIT-Debugger, mit denen man alles sehen kann, was in einer Anwendung passiert, von Codeausfhrung bis hin zu Verweisen auf Speicherstellen. Profiling schlielich ist der Vorgang, bei dem die Ressourcen, die eine Anwendung bei der Ausfhrung beansprucht, beobachtet werden. Wenn man den Speicherbedarf und den Anteil an Prozessorzeit betrachtet, kann man herausfinden, ob und wie eine Anwendung optimiert werden sollte. Sie haben das Ende Ihrer 21 Tage erreicht. Ich hoffe, die Reise durch Windows Forms hat Ihnen gefallen. Vergessen Sie nicht, sich das letzte Bonusprojekt anzusehen, um alles, was Sie in den letzten drei Wochen gelernt haben, noch einmal Revue passieren zu lassen.

21.5 Fragen und Antworten


F Kann ich den Windows-Systemmonitor (perfmon.exe) einsetzen, um meine Anwendungen zu untersuchen? A Natrlich. ffnen Sie den Systemmonitor (z.B. ber AUSFHREN im STARTMen, wo Sie den Dateinamen eingeben) und klicken Sie im SystemmonitorFenster auf die Plus-Schaltflche (HINZUFGEN) in der Symbolleiste, um dem Fenster Informationen ber eine gerade ablaufende Anwendung hinzuzufgen. Bei der Option DATENOBJEKT whlen Sie .NET CLR SPEICHER. Der Befehl LEISTUNGSINDIKATOREN WHLEN ber dem Listenfeld fhrt verschiedene Aspekte auf, die man beobachten kann. Im Feld INSTANZ WHLEN des Listenfeldes rechts daneben sollten Sie Ihre Anwendung sehen (neben mmc und _Global_), z.B. listing21.1.exe. Markieren Sie sie und klicken Sie auf HINZUFGEN, um diesen Aspekt der Anwendung zu berwachen. Sollte Ihnen dieses Standardverhalten nicht gengend Informationen liefern, so knnen Sie einen benutzerdefinierten Systemmonitor mit der PerformanceCounter-Klasse erstellen. Mit dieser Klasse lassen sich Aspekte einer Anwendung definieren, die man verfolgen will und die als Leistungsindikatoren bezeichnet werden.

703

Debugging und Profiling

21.6 Workshop
Dieser Workshop soll Ihnen helfen, sich besser an die heute behandelten Begriffe zu erinnern. Es hilft Ihnen sehr, die Antworten vollstndig zu verstehen, bevor Sie weiterlesen. Die Antworten zu den Quizfragen und den bungen finden Sie in Anhang A.

Quiz
1. Wahr oder falsch? Ein catch-Block, der eine Ausnahme vom Typ IOException angibt, wird auch Ausnahmen vom Typ SystemException abfangen. 2. Ist der folgende Code richtig?
Process [] objProcess = Process.GetProcessesByName ("listing21.2.exe");

3. Wie erzeugt man eine Programmdatenbankdatei? 4. Was versteht man unter einem Haltepunkt? 5. Nennen Sie drei Eigenschaften der Exception-Klasse. 6. Wie kann man eigene Fehlermeldungen auslsen?

bung
Schreiben Sie eine Anwendung, die dem Windows Task-Manager gleicht. Sie sollte den Benutzer jeden ablaufenden Prozess ber ein Listenfeld auswhlen lassen. In Bezeichnungsfeldern sollten die Eigenschaften Id, MainModule, PrivateMemorySize und TotalProcessorTime des Prozesses sowie die Zahl der aktiven Threads zu sehen sein. Denken Sie auch daran, try-catch-Blcke einzusetzen!

704

Woche 3 Rckblick
Projekt 3: Das Textprogramm vervollstndigen
In diesem letzten Wochenrckblick bauen Sie auf die in den letzten zwei Wochenrckblicken erstellte NetWord-Anwendung auf. Beim letzten Mal machten Sie daraus eine MDIApplikation und legten eine Document-Klasse an, die jedes untergeordnete Dokument im bergeordneten Formular darstellt. Diesmal gestalten Sie den Funktionsumfang noch Wordhnlicher, indem Sie ein benutzerdefiniertes Windows Forms-Steuerelement erzeugen.

Eine Klasse fr eine neue Seite erzeugen


Zunchst werfen wir einen Blick auf den vorhandenen Code. Da haben wir die Datei NetWord.cs, die unverndert bleiben soll. Sie ist der Haupteinstiegspunkt fr die Anwendung. Des Weiteren findet sich die Datei FindDialog.cs, die ein benutzerdefiniertes DialogfeldSteuerelement enthlt, mit dem der Benutzer im Dokument nach Text suchen kann. Schlielich liegt noch die Datei Document.cs vor, die den Groteil der Benutzeroberflche enthlt, ein RichTextBox-Steuerelement, in das der Benutzer sein Dokument eingibt. Diese Klasse verfgt zudem ber Mens, um Standardfunktionen bereitzustellen, und nutzt die FindDialog-Klasse fr Suchfunktionen. Soweit arbeitet die Anwendung recht hnlich wie der Windows-Editor. Sie prsentiert einen einzelnen Dateneingabepunkt, der einfach grer wird, je mehr Text der Benutzer eingibt. Dieses Verhalten steht aber im Gegensatz zu Microsoft Word, in dem Dokumente durch tatschliche Seiten dargestellt werden. In Word kann man seinen Text in logische Einheiten aufteilen und bndeln, die sich manipulieren lassen: zum Drucken, Durchsuchen, Setzen von Seitenrndern usw. Durch das Anlegen einer neuen Page-Klasse bauen wir eine hnliche Funktionalitt ein, um so auch die vorhandene Dokumentenschnittstelle zu ersetzen. Diese Klasse erweitert das RichTextBox-Steuerelement und stellt Funktionalitt bereit, die nicht in diesem Steuerelement mitgeliefert wird. Listing R3.1 zeigt den Code fr die neue Page-Klasse.

705

Woche 3 Rckblick

Listing R3.1: Die Page-Klasse


1: using System; 2: using System.Drawing; 3: using System.Windows.Forms; 4: using System.Drawing.Printing; 5: 6: namespace TYWinforms.P3 { 7: public class Page : RichTextBox { 8: public int PageNumber; 9: private int intCharIndex; 10: private int intLineIndex; 11: public event EventHandler Filled; 12: 13: private PageSettings objPageSettings; 14: 15: public int CharIndex { 16: get { return intCharIndex; } 17: } 18: 19: public int LineIndex { 20: get { return intLineIndex; } 21: } 22: 23: public Page(Form frmParent, PageSettings objPageSettings, int  intPageNumber) { 24: intCharIndex = 1; 25: intLineIndex = 1; 26: PageNumber = intPageNumber; 27: int intWidth = (int)(.96 * (objPageSettings.Bounds.Width  (objPageSettings.Margins.Left + objPageSettings.Margins.Right))); 28: int intHeight = (int)(.96 * (objPageSettings.Bounds.Height  (objPageSettings.Margins.Top + objPageSettings.Margins.Bottom))); 29: 30: this.Parent = frmParent; 31: this.ScrollBars = RichTextBoxScrollBars.None; 32: this.Font = frmParent.Font; 33: this.objPageSettings = objPageSettings; 34: this.RightMargin = objPageSettings.Bounds.Width - (int)(.96 *  (objPageSettings.Margins.Left + objPageSettings.Margins.Right)); 35: this.Size = new Size(intWidth,intHeight); 36: } 37: 38: public void SetFont(Font fnt, Color clr) { 39: this.SelectionFont = fnt; 40: this.SelectionColor = clr;

706

Woche 3 Rckblick

41: } 42: 43: protected virtual void OnFilled(EventArgs e) { 44: Filled(this, e); 45: } 46: 47: public void UpdatePageSettings(PageSettings objPageSettings) { 48: this.objPageSettings = objPageSettings; 49: 50: this.RightMargin = objPageSettings.Bounds.Width - (int)(.96 *  (objPageSettings.Margins.Left + objPageSettings.Margins.Right)); 51: } 52: 53: protected override void OnSelectionChanged(EventArgs e) { 54: intLineIndex = this.GetLineFromCharIndex (this.SelectionStart) + 1; 55: 56: int i = this.SelectionStart; 57: while (this.GetPositionFromCharIndex(i).X > 1) { 58: --i; 59: } 60: 61: intCharIndex = SelectionStart - i + 1; 62: 63: if (this.GetPositionFromCharIndex (this.SelectionStart).Y +  (this.SelectionFont.GetHeight()*2) >= this.ClientSize.Height) { 64: OnFilled(new EventArgs()); 65: } 66: 67: base.OnSelectionChanged(e); 68: } 69: } 70: }

In Zeile 7 sehen Sie die Deklaration der Page-Klasse, die von der RichTextBoxKlasse abgeleitet ist. Dadurch knnen wir (vgl. Tag 18) ein neues Steuerelement erstellen, das auf dem abgeleiteten fut. Das ist ideal, denn wir wollen das Steuerelement nicht von Grund auf neu erstellen. Die Zeilen 8 bis 21 zeigen diverse Eigenschaften, die in der Anwendung eingesetzt werden. In Zeile 11 erstellen wir ein benutzerdefiniertes Ereignis namens Filled. Es wird immer dann ausgelst, wenn die Grenzen einer Seite berschritten werden (entweder indem man zuviel Text eingibt oder indem man einen Bildlauf durch die Seite ausfhrt). Das umgebende Steuerelement kann mit diesem Ereignis herausfinden, ob eine neue Seite hinzuzufgen ist.

707

Woche 3 Rckblick

Wir wollen die Eigenschaften CharIndex und LineIndex (in den Zeilen 15 und 19) zur Verfgung stellen, damit das umgebende Steuerelement (welches die Document-Klasse wird) auf diese Variablen zugreifen kann. Wie wichtig diese Variablen sind, werden Sie spter noch sehen. Beachten Sie die Syntax der Eigenschaftendeklaration. Der Konstruktor bernimmt ab Zeile 23 ein paar Eigenschaften. Die erste, frmParent, stellt das umgebende Steuerelement dar. objPageSettings reprsentiert die Einstellungen, die durch das Containersteuerelement festgelegt wurden (z.B. Rnder, Papiergre und Seitenausrichtung). Die letzte Eigenschaft namens intPageNumber stellt die Nummer der Seite im umgebenden Steuerelement dar. Wenn die Anwendung mehr als eine Seite enthlt, lsst sich mit dieser Eigenschaft die Nummer der aktuellen Seite herausfinden. Die Klasse, die eine Instanz dieser Page-Klasse anlegt, muss beim Erstellen neuer Seiten alle drei dieser Eigenschaften liefern. Die Zeilen 24 bis 35 instantiieren verschiedene Eigenschaften des Steuerelements. Besonders interessant ist dabei die RightMargin-Eigenschaft in Zeile 34. RightMargin ist eine Eigenschaft der RichTextBox-Klasse, die festlegt, wie viel Text in eine Zeile passt d.h. der rechte Rand des Textfensters . Dies wird dadurch bestimmt, dass man die objPageSettings-Variable untersucht, die bergeben wurde; man subtrahiert die Rnder von der Gesamtseitenbreite, um den richtigen Wert zu erhalten. Die SetFont-Methode in Zeile 38 wird vom umgebenden Objekt benutzt, um die in der neuen Page-Klasse verwendeten Schriftarten festzulegen. Sie nehmen sie in Gebrauch, wenn wir die berarbeitete Document-Klasse untersuchen. In Zeile 43 wird die OnFilled-Methode erstellt, die lediglich das erwhnte FilledEreignis ausgelst. Die UpdatePageSettings-Methode in Zeile 47 sollte jedes Mal ausgefhrt werden, wenn sich die Seiteneinstellungen gendert haben. Da diese nicht in der Page-Klasse enthalten sind (sondern in der Document-Klasse), muss diese Methode mit dem neuen Seiteneinstellungsobjekt aufgerufen werden. Das RightMargin-Mitglied wird durch die neuen Einstellungen auf hnliche Weise aktualisiert, wie es im Konstruktor initialisiert wurde.
OnSelectionChanged in Zeile 53 ist die letzte Methode in der Page-Klasse. Diese Funktion befand sich in der Document-Klasse, wurde aber hierher verschoben, um einen logischeren Ablauf zu ermglichen. Im Wesentlichen bringt die Methode die Werte intLineIndex und intCharIndex auf den jeweils aktuellen Stand im Steuerelement. Das Containersteuerelement kann daraufhin die

708

Woche 3 Rckblick

ffentlichen Eigenschaften LineIndex und CharIndex nutzen, um die aktuelle Cursorposition anzuzeigen. Der einzige Unterschied in dieser Methode ist das Stck in den Zeilen 63 bis 65. In der Page-Klasse brauchen wir eine Mglichkeit, das umgebende Objekt wissen zu lassen, wann eine Seite voll ist (jedes Page-Objekt reprsentiert ja eine bedruckte Seite). Dies erledigt die Prfung in Zeile 63; sie verwendet die Methode GetPositionFromCharIndex, um die Y-Koordinate des aktuellen Zeichens festzustellen. Ist dieser Wert ausreichend hoch (im Vergleich zur Hhe des Steuerelements), bedeutet dies, dass das Steuerelement voll ist und eine neue Seite angelegt werden sollte (beachten Sie die Bercksichtigung der Schrifthhe bei der Y-Koordinate in Zeile 63; der Grund dafr ist der, dass die YKoordinate die Position am oberen Rand des Textes liefert, wir aber den unteren Rand bentigen, um herauszufinden, ob bereits der untere Rand des Steuerelements erreicht ist). Die OnFilled-Methode lst unser benutzerdefiniertes Filled-Ereignis aus, um dem umgebenden Steuerelement mitzuteilen, dass die Seite voll ist. Speichern Sie dieses Listing als Page.cs und kompilieren Sie es mit dem folgenden Befehl:
csc /t:library /r:system.dll /r:system.windows.forms.dll /r:system.drawing.dll Page.cs

Nun sind Sie bereit, die Document-Klasse so zu verndern, dass statt der normalen RichTextBox- die neue Page-Klasse eingesetzt wird. Listing R3.2 zeigt den Code fr Document.cs in seiner Gesamtheit. Das Listing ist umfangreich, doch der Groteil des Codes hat sich nicht gendert, so dass wir es nicht vollstndig untersuchen. Nhere Details finden Sie im Wochenrckblick 2. Listing R3.2: Die revidierte Document-Klasse
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: using using using using using using using System; System.Windows.Forms; System.IO; System.Drawing; System.Drawing.Printing; System.ComponentModel; System.Collections;

namespace TYWinforms.P3 { public class Document : Form { public ArrayList arrPages = new ArrayList(); public int intCurrentPage; private Panel pnlView = new Panel();

709

Woche 3 Rckblick

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:

private private private private

ContextMenu cmnuDocument = new ContextMenu(); StatusBar sbarMain = new StatusBar(); StatusBarPanel spnlLine = new StatusBarPanel(); VScrollBar scrlbar = new VScrollBar();

private MainMenu mnuMain = new MainMenu(); private private private private private private private private private private private private private private private private private MenuItem MenuItem MenuItem MenuItem MenuItem MenuItem MenuItem MenuItem MenuItem MenuItem MenuItem MenuItem MenuItem mniFile = new MenuItem("File"); mniSave = new MenuItem("Save"); mniPageSetup = new MenuItem ("Page Setup..."); mniPrintPreview = new MenuItem ("Print Preview"); mniPrint = new MenuItem("Print..."); mniEdit = new MenuItem("Edit"); mniUndo = new MenuItem("Undo"); mniCut = new MenuItem("Cut"); mniCopy = new MenuItem("Copy"); mniPaste = new MenuItem("Paste"); mniFind = new MenuItem("Find..."); mniFormat = new MenuItem("Format"); mniFont = new MenuItem("Font...");

Font fntCurrent = new Font ("Times New Roman", 10); Color fntColor = Color.Black; FontDialog dlgFont = new FontDialog(); PageSetupDialog dlgPageSetup = new PageSetupDialog();

private PageSettings objPageSettings = new PageSettings(); private PrinterSettings objPrintSettings = new PrinterSettings(); private StringReader objReader; public Document(string strName) { mniSave.Click += new EventHandler(this.FileClicked); mniPageSetup.Click += new EventHandler(this.FileClicked); mniPrintPreview.Click += new EventHandler(this.FileClicked); mniPrint.Click += new EventHandler(this.FileClicked); mnuMain.MenuItems.Add(mniFile); mniFile.MergeType = MenuMerge.MergeItems; mniFile.MenuItems.Add(mniSave); mniFile.MenuItems.Add("-"); mniFile.MenuItems.Add(mniPageSetup); mniFile.MenuItems.Add(mniPrintPreview); mniFile.MenuItems.Add(mniPrint); mniFile.MergeOrder = 1;

710

Woche 3 Rckblick

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:

mniEdit.MergeOrder = 2; mniFormat.MergeOrder = 3; mniSave.MergeOrder = 30; mniFile.MenuItems[1].MergeOrder = 35; mniPageSetup.MergeOrder = 40; mniPrintPreview.MergeOrder = 45; mniPrint.MergeOrder = 50; sbarMain.ShowPanels = true; sbarMain.Font = new Font("Arial", 10); sbarMain.Panels.Add(spnlLine); spnlLine.AutoSize = StatusBarPanelAutoSize.Spring; spnlLine.Alignment = HorizontalAlignment.Right; mniUndo.Click += new EventHandler(this.EditClicked); mniCut.Click += new EventHandler(this.EditClicked); mniCopy.Click += new EventHandler(this.EditClicked); mniPaste.Click += new EventHandler(this.EditClicked); mniFind.Click += new EventHandler(this.EditClicked); mniFont.Click += new EventHandler(this.FormatClicked); mnuMain.MenuItems.Add(mniEdit); mnuMain.MenuItems.Add(mniFormat); mniUndo.ShowShortcut = true; mniCut.ShowShortcut = true; mniCopy.ShowShortcut = true; mniPaste.ShowShortcut = true; mniFind.ShowShortcut = true; mniEdit.MenuItems.Add(mniUndo); mniEdit.MenuItems.Add("-"); mniEdit.MenuItems.Add(mniCut); mniEdit.MenuItems.Add(mniCopy); mniEdit.MenuItems.Add(mniPaste); mniEdit.MenuItems.Add("-"); mniEdit.MenuItems.Add(mniFind); mniFormat.MenuItems.Add(mniFont); cmnuDocument.MenuItems.Add(mniCut.CloneMenu()); cmnuDocument.MenuItems.Add(mniCopy.CloneMenu()); cmnuDocument.MenuItems.Add(mniPaste.CloneMenu()); cmnuDocument.MenuItems.Add("-"); cmnuDocument.MenuItems.Add(mniFont.CloneMenu());

711

Woche 3 Rckblick

107: cmnuDocument.Popup += new EventHandler (this.HandleContext); 108: pnlView.ContextMenu = cmnuDocument; 109: 110: scrlbar.Dock = DockStyle.Right; 111: scrlbar.Scroll += new ScrollEventHandler (this.ScrollView); 112: scrlbar.Maximum = 10; 113: 114: this.Text = strName; 115: this.Font = new Font("Courier New", 12); 116: this.Size = new Size(800, 900); 117: this.Name = "NetWord"; 118: this.WindowState = FormWindowState.Maximized; 119: this.Menu = mnuMain; 120: this.Closing += new CancelEventHandler (this.DocumentClosing); 121: 122: pnlView.AutoScroll = false; 123: pnlView.BackColor = Color.DarkGray; 124: pnlView.Width = this.Width - 16; 125: pnlView.Height = this.Height - 50; 126: pnlView.Location = new Point(0,0); 127: pnlView.Font = fntCurrent; 128: 129: this.Controls.Add(scrlbar); 130: this.Controls.Add(sbarMain); 131: this.Controls.Add(pnlView); 132: AddPage(); 133: } 134: 135: private void ScrollView(Object Sender, ScrollEventArgs e) { 136: pnlView.Top = 0 - (e.NewValue * 30); 137: } 138: 139: private void DocumentCreated(Object Sender, EventArgs e) { 140: AddPage(); 141: } 142: 143: private void AddPage() { 144: int intHeight; 145: 146: intHeight = objPageSettings.Bounds.Height - (int)(.96 *  (objPageSettings.Margins.Top + objPageSettings.Margins.Bottom)); 147: 148: Page objPage = new Page(this,objPageSettings,arrPages.Count+1); 149: pnlView.Height = (arrPages.Count+1) * intHeight; 150: pnlView.Controls.Add(objPage); 151: scrlbar.Maximum = (30 * arrPages.Count+1) + 15;

712

Woche 3 Rckblick

152: 153: objPage.Location = new Point(50,intHeight * arrPages.Count + 5); 154: objPage.Filled += new EventHandler (this.PageFilled); 155: objPage.Enter += new EventHandler (this.PageGotFocus); 156: objPage.SelectionChanged += new EventHandler (this.UpdateStatus); 157: 158: arrPages.Add(objPage); 159: objPage.Focus(); 160: } 161: 162: private void PageFilled(Object Sender, EventArgs e) { 163: if (intCurrentPage == arrPages.Count) { 164: AddPage(); 165: } else { 166: ((Page)arrPages[intCurrentPage]).Focus(); 167: } 168: } 169: 170: private void PageGotFocus(Object Sender, EventArgs e) { 171: intCurrentPage = ((Page)Sender).PageNumber; 172: } 173: 174: private void UpdateStatus(Object Sender, EventArgs e) { 175: Page tmpPage = (Page)arrPages[intCurrentPage-1]; 176: 177: spnlLine.Text = "Page " + intCurrentPage.ToString() + " Line " +  tmpPage.LineIndex.ToString() + " Char " + tmpPage.CharIndex.ToString(); 178: } 179: 180: private void HandleContext(Object Sender, EventArgs e) { 181: if (((Page)arrPages[intCurrentPage-1]).SelectionLength == 0) { 182: cmnuDocument.MenuItems[0].Enabled = false; 183: cmnuDocument.MenuItems[1].Enabled = false; 184: } else { 185: cmnuDocument.MenuItems[0].Enabled = true; 186: cmnuDocument.MenuItems[1].Enabled = true; 187: } 188: } 189: 190: private void FileClicked(Object Sender, EventArgs e) { 191: MenuItem mniTemp = (MenuItem)Sender; 192: PrintDocument pd; 193: String strText; 194: 195: switch (mniTemp.Text) { 196: case "Save":

713

Woche 3 Rckblick

197: FileInfo filTemp = new FileInfo(this.Text); 198: if (filTemp.Extension == ".rtf") { 199: ((Page)arrPages[intCurrentPage-1]).SaveFile(this.Text,  RichTextBoxStreamType.RichText); 200: } else { 201: ((Page)arrPages[intCurrentPage-1]).SaveFile(this.Text,  RichTextBoxStreamType.PlainText); 202: } 203: ((Page)arrPages[intCurrentPage-1]). Modified = false; 204: break; 205: case "Page Setup...": 206: dlgPageSetup.PageSettings = objPageSettings; 207: dlgPageSetup.ShowDialog(); 208: 209: foreach (Page tmpPage in arrPages) { 210: tmpPage.UpdatePageSettings (objPageSettings); 211: } 212: 213: break; 214: case "Print Preview": 215: strText = ""; 216: 217: foreach (Page tmpPage in arrPages) { 218: strText += tmpPage.Text; 219: } 220: 221: objReader = new StringReader(strText); 222: 223: pd = new PrintDocument(); 224: pd.DefaultPageSettings = objPageSettings; 225: pd.PrintPage += new PrintPageEventHandler (this.PrintIt); 226: 227: PrintPreviewDialog dlgPrintPreview = new  PrintPreviewDialog(); 228: dlgPrintPreview.Document = pd; 229: 230: dlgPrintPreview.ShowDialog(); 231: break; 232: case "Print...": 233: PrintDialog dlgPrint = new PrintDialog(); 234: dlgPrint.PrinterSettings = new PrinterSettings(); 235: if (dlgPrint.ShowDialog() == DialogResult.OK) { 236: strText = ""; 237: 238: foreach (Page tmpPage in arrPages) { 239: strText += tmpPage.Text;

714

Woche 3 Rckblick

240: } 241: 242: objReader = new StringReader(strText); 243: 244: pd = new PrintDocument(); 245: 246: pd.DefaultPageSettings = objPageSettings; 247: pd.PrintPage += new PrintPageEventHandler (this.PrintIt); 248: pd.Print(); 249: } 250: break; 251: case "Exit": 252: this.Close(); 253: break; 254: } 255: } 256: 257: private void PrintIt(Object Sender, PrintPageEventArgs e) { 258: Font fntPrint = this.Font; 259: int count = 0; 260: float yPos = 0; 261: float lpp = e.MarginBounds.Height /  fntPrint.GetHeight(e.Graphics); 262: float fltTopMargin = e.MarginBounds.Top; 263: float fltLeftMargin = e.MarginBounds.Left; 264: String strLine = null; 265: 266: while (count < lpp && ((strLine = objReader.ReadLine()) != null)) { 267: yPos = fltTopMargin + (count * fntPrint.GetHeight(e.Graphics)); 268: 269: e.Graphics.DrawString(strLine, fntPrint, Brushes.Black,  fltLeftMargin, yPos, new StringFormat()); 270: 271: count++; 272: } 273: 274: if (strLine != null) { 275: e.HasMorePages = true; 276: } else { 277: e.HasMorePages = false; 278: } 279: } 280: 281: private void EditClicked(Object Sender, EventArgs e) { 282: MenuItem mniTemp = (MenuItem)Sender;

715

Woche 3 Rckblick

283: Page tmpPage = (Page)arrPages[intCurrentPage-1]; 284: 285: switch (mniTemp.Text) { 286: case "Undo": 287: tmpPage.Undo(); 288: break; 289: case "Cut": 290: if (tmpPage.SelectedRtf != "") { 291: tmpPage.Cut(); 292: } 293: break; 294: case "Copy": 295: if (tmpPage.SelectedRtf != "") { 296: tmpPage.Copy(); 297: } 298: break; 299: case "Paste": 300: tmpPage.Paste(); 301: break; 302: case "Find...": 303: FindDialog dlgFind = new FindDialog(this); 304: dlgFind.Show(); 305: break; 306: } 307: } 308: 309: private void FormatClicked(Object Sender, EventArgs e) { 310: MenuItem mniTemp = (MenuItem)Sender; 311: 312: switch (mniTemp.Text) { 313: case "Font...": 314: dlgFont.ShowColor = true; 315: dlgFont.Font = this.Font; 316: if (dlgFont.ShowDialog() == DialogResult.OK) { 317: fntCurrent = dlgFont.Font; 318: fntColor = dlgFont.Color; 319: this.Font = fntCurrent; 320: this.ForeColor = fntColor; 321: ((Page)arrPages[intCurrentPage - 1]).SetFont(fntCurrent,  fntColor); 322: } 323: break; 324: } 325: } 326: 327: private void DocumentClosing(Object Sender, CancelEventArgs e) {

716

Woche 3 Rckblick

328: if (((Page)arrPages[intCurrentPage-1]).Modified) { 329: DialogResult dr = MessageBox.Show("Do you want to save the  changes?", this.Name, MessageBoxButtons.YesNoCancel); 330: 331: if (dr == DialogResult.Yes) { 332: FileClicked(mniSave, new EventArgs()); 333: } else if (dr == DialogResult.Cancel) { 334: e.Cancel = true; 335: } 336: } 337: } 338: } 339: }

Als Erstes ist festzustellen, dass alle Verweise auf das RichTextBox-Steuerelement getilgt sind. Statt dessen finden sich Referenzen auf unsere neue PageKlasse. Wir bentigen einen Mechanismus, um in diesem Dokument einzelne Seiten speichern zu knnen. Dies lsst sich mit einem ArrayList-Objekt durchfhren, das in Zeile 11 deklariert wird (und der Namensraum des Objekts, System.Collections, wird in Zeile 7 importiert). Wenn wir ein neues Page-Objekt anlegen, fgen wir es diesem Array hinzu. Wenn wir auf die jeweils aktuelle Seite zugreifen mssen, benutzen wir das Array und die intCurrentPage-Variable (gleich mehr davon). Die nchste nderung tritt in Zeile 14 auf, wo wir ein neues Panel-Steuerelement erzeugen. Dieses Paneel soll jede neue Seite, die erzeugt wird, aufnehmen. Seine Eigenschaften werden im Document-Konstruktor initialisiert. Zeile 18 erzeugt ein neues VScrollBar-Objekt, das fr den Bildlauf benutzt wird. Das Panel-Steuerelement verfgt ber eine AutoScroll-Eigenschaft, die uns Bildlaufleisten zur Verfgung stellt, doch sie hat auch Nachteile. Wenn man sich z.B. im Panel-Steuerelement von einer Seite zur nchsten bewegt und AutoScroll auf true gesetzt ist, ist der Bildlauf der Page-Objekte viel zu gro dadurch wird es schwierig, den berblick ber die aktuelle Texteingabestelle zu behalten. Daher implementieren wir benutzerdefinierte Bildlaufleisten, die tun, was wir wollen. Die VScrollBar wird in den Zeilen 110 bis 112 initialisiert, und ihr Ereignishandler, ScrollView, befindet sich in Zeile 135. Diese Methode verschiebt einfach unser Panel-Steuerelement um einen skalierten Betrag. Der letzte Teil des Konstruktors ruft die AddPage-Methode (Zeile 132) auf. AddPage enthlt einen Teil der wichtigsten neuen Funktionen. Sie erzeugt ein neues Page-Objekt (Zeile 148; beachten Sie bitte die bergabe der ntigen

717

Woche 3 Rckblick

Variablen). Zeile 153 legt die Platzierung einer neuen Seite im Panel-Steuerelement fest. Dazu zhlt sie einfach die vorhandenen Page-Objekte und setzt dementsprechend die oberste Koordinate. In den Zeilen 154 bis 156 werden Ereignishandler den Ereignissen Filled, Enter und SelectionChanged zugewiesen. Wir fgen das eben angelegte Page-Objekt dem arrPages-Array und dem Panel-Steuerelement hinzu und verleihen ihm den Fokus. Die Gre des Panel-Steuerelements ist zu ndern, um die neue Seite aufzunehmen (Zeile 149), und auch die Maximum-Eigenschaft der VScrollBar wird angepasst (Zeile 151). Denken Sie an die Bildlaufleiste in Microsoft Word: Fgt man neue Seiten hinzu, schrumpft die Bildlaufleiste kontinuierlich (da der Maximalwert der Bildlaufleiste laufend wchst). Wird die Seitengrenze berschritten, so ist dies der Auslser unseres benutzerdefinierten Ereignisses Filled. Dessen Ereignishandler PageFilled (Zeile 162) fgt entweder eine neue Seite hinzu (wenn es sich aktuell um die letzte Seite handelt) oder bertrgt den Fokus auf die nchste Seite im Dokument. Beachten Sie die Verwendung der Variablen arrPages und intCurrentPage in Zeile 166.
PageGotFocus, der Ereignishandler fr das Enter-Ereignis, findet sich in Zeile 170. Er aktualisiert die Variable intCurrentPage. Der Ereignishandler fr das SelectionChanged-Ereignis, UpdateStatus, wurde in Zeile 174 geschrieben. Damit knnen wir nun auf die Eigenschaft von Page in Zeile 177 zugreifen. Wieder ist auf den Einsatz der Variablen arrPages und intCurrentPage in Zeile

175 zu achten. Der brige Code zwischen Zeile 180 und Zeile 339 hat sich nur geringfgig verndert. Alle Verweise auf das RichTextBox-Steuerelement wurden durch Referenzen auf ein PageObjekt ersetzt. Die Flle Print Preview (Druckvorschau) und Print in den Zeilen 214 und 232 wurden gendert, um durch alle Page-Objekte im Dokument zu blttern, wobei sie den Text jeder Seite einsammeln, bevor sie ihn dem StringReader-Objekt in den Zeilen 221 und 242 bergeben. Die FormatClicked-Methode in Zeile 309 verweist nun auf die neue Page.SetFont-Methode, die wir in Listing P3.1 angelegt haben. Der Aufruf der FindDialog-Klasse in Zeile 303 hat sich leicht gendert, um eine neue Variable an den Konstruktor zu bergeben. (Dies wird in den nchsten Abschnitten erklrt.) Die FindDialog-Klasse ist so abzundern, dass sie jedes Page-Objekt durchsucht statt nur ein einzelnes RichTextBox-Steuerelement wie zuvor. Um diese Funktionalitt zu erreichen, sind zwei spezifische nderungen notwendig: den Konstruktor von FindDialog so modifizieren, dass er auf die Document-Klasse statt auf sein RichTextBox-Steuerelement verweist, und die Methoden fr Suchen, Ersetzen und Alle ersetzen so anpassen, dass sie jedes PageObjekt durchsuchen.

718

Woche 3 Rckblick

Zunchst ndern Sie den Konstruktor wie folgt:


private Document docParent; public FindDialog(Form frmDocument):base(){ docParent =(Document)frmDocument; ...

Nun nimmt die docParent-Variable eine Referenz auf unsere Haupt-Document-Klasse auf, welche wiederum ihrerseits einen Verweis auf alle Page-Objekte enthlt. Listing R3.3 zeigt die nderungen am ButtonClicked-Ereignishandler, der fr die eigentliche Suchen- und Ersetzen-Funktionalitt zustndig ist. Listing R3.3: Suchen und Ersetzen in Page-Objekten
1: 2: 3: 4: 5: 6: 7: 8: private void ButtonClicked(Object Sender, EventArgs e) { Button btTemp = (Button)Sender; int intLocation; int intCount = 0;

switch (btTemp.Text) { case "Suchen": for (int i = docParent.intCurrentPage-1; i <  docParent.arrPages.Count; i++) { 9: 10: rtbDoc = (RichTextBox)docParent.arrPages[i]; 11: 12: if (i != docParent.intCurrentPage-1) { 13: rtbDoc.Focus(); 14: rtbDoc.SelectionStart = 0; 15: } 16: 17: intLocation = rtbDoc.Find(tbFind.Text, rtbDoc.SelectionStart +  rtbDoc.SelectionLength, RichTextBoxFinds.None); 18: if (intLocation != -1) { 19: rtbDoc.SelectionStart = intLocation; 20: rtbDoc.SelectionLength = tbFind.Text.Length; 21: rtbDoc.Focus(); 22: break; 23: } 24: } 25: break; 26: case "Ersetzen": 27: for (int i = docParent.intCurrentPage-1; i <  docParent.arrPages.Count; i++) { 28: 29: rtbDoc = (RichTextBox)docParent.arrPages[i];

719

Woche 3 Rckblick

30: 31: if (i != docParent.intCurrentPage-1) { 32: rtbDoc.Focus(); 33: rtbDoc.SelectionStart = 0; 34: } 35: 36: intLocation = rtbDoc.Find(tbFind.Text, rtbDoc.SelectionStart +  rtbDoc.SelectionLength, RichTextBoxFinds.None); 37: if (intLocation != -1) { 38: rtbDoc.SelectionStart = intLocation; 39: rtbDoc.SelectionLength = tbFind.Text.Length; 40: rtbDoc.SelectedText = tbReplace.Text; 41: rtbDoc.SelectionStart = intLocation; 42: rtbDoc.SelectionLength = tbReplace.Text.Length; 43: } 44: } 45: break; 46: case "Replace All": 47: for (int i = 0; i < docParent.arrPages.Count; i++) { 48: 49: rtbDoc = (RichTextBox)docParent.arrPages[i]; 50: 51: if (i != docParent.intCurrentPage-1) { 52: rtbDoc.Focus(); 53: rtbDoc.SelectionStart = 0; 54: } 55: 56: intLocation = rtbDoc.Find(tbFind.Text); 57: while (intLocation != -1) { 58: rtbDoc.SelectionStart = intLocation; 59: rtbDoc.SelectionLength = tbFind.Text.Length; 60: rtbDoc.SelectedText = tbReplace.Text; 61: intCount += 1; 62: intLocation = rtbDoc.Find(tbFind.Text,  rtbDoc.SelectionStart + rtbDoc.SelectionLength, RichTextBoxFinds.None); 63: } 64: } 65: MessageBox.Show(intCount.ToString() + " Ersetzungen  ausgefhrt","Suchen"); 66: break; 67: case "Abbrechen": 68: this.Close(); 69: break; 70: } 71: }

720

Woche 3 Rckblick

Der meiste Code hat sich nicht gendert. Im Grunde gibt es nur einen neuen Codeabschnitt, welcher jedoch an drei Stellen verwendet wird. In Zeile 8 findet sich eine for-Schleife, die durch jedes Page-Objekt in der Auflistung Document.arrPages iteriert. Jedes Page-Objekt wird vorbergehend der rtbDoc-Variablen in Zeile 10 zugewiesen. Diese temporre Variable wird genau wie beim letzten Mal zum Suchen nach Text verwendet. Man kann jedoch ein RichTextBox-Steuerelement nur dann durchsuchen, wenn es den Eingabefokus besitzt. Daher geben wir in den Zeilen 12 bis 15 jedem Page-Objekt den Fokus, falls es ihn noch nicht hat. Dieser Code wird fr den Ersetzen-Fall ab Zeile 27 wiederholt und ebenso fr den Fall Alle ersetzen ab Zeile 47. Kompilieren Sie die revidierten Document- und FindDialog-Klassen (wie auch den Rest der Anwendung) mit folgendem Befehl:
csc /t:winexe /r:system.dll /r:system.drawing.dll /r:system.windows.forms.dll / r:Page.dll NetWord.cs FindDialog.cs Document.cs

Dieser Befehl hnelt den anderen, die Sie in den vorherigen Wochenrckblicken kennen gelernt haben. Sie rufen damit den C#-Compiler auf, wobei Sie auf die notwendigen Assemblies und die Quellcodedateien verweisen (beachten Sie den Verweis auf die Datei Page.dll). Abbildung R3.1 zeigt das Ergebnis dieser Anwendung nach der Erstellung einiger Seiten.

Abbildung R3.1: Ihre NetWordAnwendung verhlt sich nun sehr hnlich wie Microsoft Word.

721

Woche 3 Rckblick

Probieren Sie die Anwendung aus. Gert man ans Ende einer Seite, wird automatisch eine neue kreiert und die Cursorposition verschoben. Man kann Dokumente drucken (und sie in der Vorschau begutachten) sie werden im Druck genau wie am Bildschirm erscheinen (eine Bildschirmseite entspricht also einer Druckseite). Meinen Glckwunsch zur neuen Textverarbeitung!

Der weitere Weg


Obwohl nun eine komplette Textverarbeitung vorliegt, lassen sich noch einige Dinge daran tun, um NetWord effizienter und funktionsreicher zu machen. Die Art und Weise, wie man sich von einer Seite zur nchsten bewegt, entspricht nicht genau der in Word. Konkret: Man kann nicht hoch oder hinunter zur nchsten Seite springen, ohne explizit auf diejenige Seite zu klicken (in Word kann man einfach den Auf- bzw. Abwrts-Pfeil anklicken). Das wre ein leicht zu behebendes Problem. Die Seitenzhlfunktion funktioniert vorerst fr neue Dokumente, doch ffnet man eine vorhandene Datei, so wird diese Datei nicht automatisch paginiert. Die Seitenzhlung funktioniert bei normalem Inhalt, doch wenn man umfangreiche Textmengen einkopiert, wird dieser Text nicht korrekt paginiert. Vielmehr wird er am unteren Rand der aktuellen Seite angehngt, welche sich einfach ausdehnt, um den neuen Text aufzunehmen. Zudem lassen sich momentan keine Seiten lschen. In seiner aktuellen Implementierung kann NetWord nur reinen Text sowie RTF-Dateien bearbeiten. Doch mit Ihrer Kenntnis der Serialisierung (Tag 11 ber Windows-Ein-/Ausgabe) knnen Sie Ihre eigenen Dateitypen erstellen. Auf diese Weise lsst sich ein Dokument mit intakten Seiten (in Page-Objekten) speichern, was Ihnen den erwhnten Prozess der neuerlichen Seitenzhlung erspart, wenn man sie ffnet. Unter Verwendung von ActiveX (Tag 14) knnten Sie sogar NetWord einsetzen, um Word-Dokumente zu ffnen. NetWord knnte weitaus mehr Funktionen hinsichtlich der Bearbeitung von Dokumenten anbieten, so etwa die Fhigkeit, Text zu zentrieren oder rechtsbndig auszurichten, Zeilenabstnde zu ndern, Aufzhlungszeichen und nummerierte Listen einzufgen usw. Dieser Funktionsumfang befindet sich bereits im RichTextBox-Steuerelement, so dass man NetWord ohne weiteres damit ausstatten kann, indem man lediglich die entsprechende Benutzeroberflche bereitstellt. Whrend man einige dieser Funktionen leicht integrieren kann, verlangen andere jedoch ein wenig Einfallsreichtum und technisches Wissen. Mit den in den letzten 21 Tagen erworbenen Kenntnissen knnen Sie diese Aufgaben jedoch zweifellos bewltigen. Danke, dass Sie die letzten 21 Tage mit uns verbracht haben.

722

Anhang A

2 2

Anhang A

A.1
Tag 1
Quiz

Antworten auf die Quizfragen und bungen

1. Wahr oder falsch? Windows Forms ist Win32-Programmierung. Wahr. In diesem Buch besprechen wir keine Win16-Programmierung. 2. Wahr oder falsch? Metadaten enthalten Angaben darber, in welcher Umgebung eine Anwendung erstellt wurde. Wahr. 3. Auf welche Weise ermglicht MSIL plattformbergreifende Funktionsweise? MSIL ist eine plattformunabhngige Sprache, so dass sie sich leicht auf jedes Betriebssystem bertragen lsst. Ein JIT-Compiler wird dann eingesetzt, um die MSIL in die jeweilige Maschinensprache zu bertragen: ein wirtschaftlicherer Vorgang als fr jede zu untersttzende Plattform die Anwendung neu zu schreiben oder zu kompilieren. 4. Wie nennt man die Weiterentwicklung von ActiveX Data Objects? ADO.NET. 5. Wahr oder falsch? Man kann Windows Forms-Anwendungen mit jedem beliebigen Texteditor erstellen. Wahr, solange die Dateien im Nur-Text-Format gespeichert werden. 6. Was ist ein Windows Forms-Steuerelement? Ein Windows Forms-Steuerelement ist ein Objekt, das dem Benutzer eine Schnittstelle bereitstellt, die hufig zur Interaktion fhig ist. 7. Was bewirkt das ImportsStatement? Es versetzt eine Anwendung in die Lage, die Objekte oder eine Auflistung von Objekten in anderen Namensrumen zu nutzen.

bung
Was wrde der folgende Code bewirken, sobald er kompiliert wre und ausgefhrt wrde?
1: 2: Imports System Imports System.Windows.Forms

724

Antworten auf die Quizfragen und bungen

3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:

Public Class MyApp : Inherits Form Public Shared Sub Main() Application.Run(New MyApp) End Sub Public Sub New() Me.Height = 100 Me.Width = 50 End Sub End Class

Dies erzeugt ein Windows Form und verndert seine Gre auf 100 x 50 Pixel.

Tag 2
Quiz
1. Wie heit die Basisklasse, von der alle anderen Klassen erben? Die Object-Klasse. 2. Was bewirken die Schlsselwrter shared und static? Diese Schlsselwrter zeigen an, dass ein Mitglied ein fundamentaler Klassenbestandteil ist, der in Instanzen der Klassen vorhanden ist. Whrend beispielsweise verschiedene Autoinstanzen unterschiedliche Farben haben knnen, so haben sie doch wohl alle den gleichen Metalltyp gemeinsam. 3. Wahr oder falsch? Man kann vorhandene Namensrume um eigene Klassen erweitern. Wahr. 4. Wahr oder falsch? Die Main-Methode wird jedes Mal ausgefhrt, wenn eine neue Instanz ihrer Klasse erzeugt wird. Falsch. Die Main-Methode wird nur einmal ausgefhrt, nmlich dann, wenn die Anwendung gestartet wird. 5. Aus welchem Grund ist der folgende Code nicht ausreichend? Was muss auerdem verwendet werden?
dim MyObject as Button

725

Anhang A

Er ist nicht ausreichend, weil man damit nur eine Variable des Typs Button deklariert; man weist der Variablen keinen Wert zu, was zu erledigen ist, bevor man sie einsetzen kann. Um die Variable zu benutzen, ist das New-Schlsselwort einzusetzen:
dim MyObject as New Button

oder
dim MyObject as Button = New Button

6. Was stimmt mit dem folgenden Kompilierungsbefehl nicht? (Tipp: Er kann mehr als einen Fehler enthalten.)
csc /type:windowsexe /r:system.dll /r:system.drawing.dll / r:system.drawing.text.dll dateiname.vb

Es gibt keinen Typ windowsexe. Verwenden Sie statt dessen winexe. Es gibt auch keine Assembly system.drawing.text.dll. Der Namensraum system.drawing.text ist in der Assembly system.drawing.dll enthalten. Auerdem verwendet diese Codezeile den C#-Compiler, um eine VB .NET-Datei zu kompilieren, wie an der Erweiterung der Quellcodedatei zu erkennen ist. 7. Was bedeuten die folgenden Eigenschaften?
Button.CanSelect: Gibt an, ob die Schaltflche vom Benutzer ausgewhlt werden

kann.
Button.Cursor: Gibt das Symbol an, das benutzt wird, wenn sich der Mauszeiger ber der Schaltflche befindet. Form.AllowDrop: Gibt an, ob dieses Formular Drag & Drop verarbeiten kann. Form.Visible: Ist das Formular fr den Benutzer sichtbar?

8. Nennen Sie drei semantische Unterschiede zwischen C# und Visual Basic .NET. Variablentypen kommen in C# vor Variablennamen. Am Ende jeder Zeile muss ein Semikolon gesetzt werden. C# beachtet die Gro-/Kleinschreibung. 9. Wahr oder falsch? Jede Klasse muss einen Konstruktor haben. Das ist eine Fangfrage. Es trifft zu, dass jede Klasse einen Konstruktor haben muss, aber man muss keinen explizit selbst erzeugen. Die CLR erstellt nmlich automatisch einen, wenn er noch nicht vorhanden ist.

726

Antworten auf die Quizfragen und bungen

bung
Erweitern Sie das heutige Taschenrechner-Beispiel. Fgen Sie weitere Schaltflchen hinzu, mit denen sich arithmetische Operationen ausfhren lassen: Subtraktion, Multiplikation und Division. Versuchen Sie, die Anwendung sowohl in C# als auch in Visual Basic .NET zu erstellen. Verschnern Sie die Benutzeroberflche Ihres Taschenrechners durch den grozgigen Gebrauch von Bezeichnungsfeldern (Label-Steuerelementen). Der Code in C#:
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: using System; using System.Windows.Forms; using System.Drawing; namespace TYWinforms.Day2 { public class Calculator : Form { private Button btnAdd; private Button btnSubtract; private Button btnMultiply; private Button btnDivide; private TextBox tbNumber1; private TextBox tbNumber2; private Label lblAnswer; private Label lblNum1; private Label lblNum2; public static void Main() { Application.Run(new Calculator()); } public Calculator() { this.btnAdd = new Button(); this.btnSubtract = new Button(); this.btnMultiply = new Button(); this.btnDivide = new Button(); this.tbNumber1 = new TextBox(); this.tbNumber2 = new TextBox(); this.lblAnswer = new Label(); this.lblNum1 = new Label(); this.lblNum2 = new Label(); this.Width = 325; this.Height = 150; this.Text = "My Calculator";

727

Anhang A

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: 76: 77: 78: 79: 80: 81: 82:

tbNumber1.Location = new Point(100,0); tbNumber2.Location = new Point(100,25); btnAdd.Location = new Point(0,75); btnAdd.Text = "+"; btnAdd.Click += new EventHandler(this.Add); btnSubtract.Location = new Point(75,75); btnSubtract.Text = "-"; btnSubtract.Click += new EventHandler(this.Subtract); btnMultiply.Location = new Point(150,75); btnMultiply.Text = "*"; btnMultiply.Click += new EventHandler(this.Multiply); btnDivide.Location = new Point(225,75); btnDivide.Text = "/"; btnDivide.Click += new EventHandler(this.Divide); lblNum1.Text = "Enter number 1: "; lblNum1.Location = new Point(0,0); lblNum2.Text = "Enter number 2: "; lblNum2.Location = new Point(0,25); lblAnswer.Location = new Point(0,55); this.Controls.Add(btnAdd); this.Controls.Add(btnSubtract); this.Controls.Add(btnMultiply); this.Controls.Add(btnDivide); this.Controls.Add(tbNumber1); this.Controls.Add(tbNumber2); this.Controls.Add(lblAnswer); this.Controls.Add(lblNum1); this.Controls.Add(lblNum2); } public void Add(object Sender, EventArgs e) { lblAnswer.Text = Convert.ToString(Convert.ToInt32(tbNumber1.Text) + Convert.ToInt32(tbNumber2.Text)); } public void Subtract(object Sender, EventArgs e) { lblAnswer.Text = Convert.ToString(Convert.ToInt32(tbNumber1.Text) Convert.ToInt32(tbNumber2.Text));

728

Antworten auf die Quizfragen und bungen

83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95:

} public void Multiply(object Sender, EventArgs e) { lblAnswer.Text = Convert.ToString(Convert.ToInt32(tbNumber1.Text) * Convert.ToInt32(tbNumber2.Text)); } public void Divide(object Sender, EventArgs e) { lblAnswer.Text = Convert.ToString(Convert.ToInt32(tbNumber1.Text) / Convert.ToInt32(tbNumber2.Text)); } } }

Der Code in VB .NET:


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: Imports System Imports System.Windows.Forms Imports System.Drawing namespace TYWinforms.Day2 Public class Calculator : Inherits Form private WithEvents btnAdd as Button private WithEvents btnSubtract as Button private WithEvents btnMultiply as Button private WithEvents btnDivide as Button private tbNumber1 as TextBox private tbNumber2 as TextBox private lblAnswer as Label private lblNum1 as Label private lblNum2 aS Label Public shared Sub Main() Application.Run(new Calculator) End Sub Public Sub New() Me.btnAdd = new Button() Me.btnSubtract = new Button() Me.btnMultiply = new Button() Me.btnDivide = new Button() Me.tbNumber1 = new TextBox() Me.tbNumber2 = new TextBox() Me.lblAnswer = new Label() Me.lblNum1 = new Label() Me.lblNum2 = new Label()

729

Anhang A

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: 76: 77:

Me.Width = 325 Me.Height = 150 Me.Text = "My Calculator" tbNumber1.Location = new Point(100,0) tbNumber2.Location = new Point(100,25) btnAdd.Location = new Point(0,75) btnAdd.Text = "+" AddHandler btnAdd.Click, new EventHandler (AddressOf Add)

btnSubtract.Location = new Point(75,75) btnSubtract.Text = "-" AddHandler btnSubtract.Click, new EventHandler(AddressOf Subtract) btnMultiply.Location = new Point(150,75) btnMultiply.Text = "*" AddHandler btnMultiply.Click, new EventHandler (AddressOf Multiply) btnDivide.Location = new Point(225,75) btnDivide.Text = "/" AddHandler btnDivide.Click, new EventHandler (AddressOf Divide) lblNum1.Text = "Enter number 1: " lblNum1.Location = new Point(0,0) lblNum2.Text = "Enter number 2: " lblNum2.Location = new Point(0,25) lblAnswer.Location = new Point(0,55) Me.Controls.Add(btnAdd) Me.Controls.Add(btnSubtract) Me.Controls.Add(btnMultiply) Me.Controls.Add(btnDivide) Me.Controls.Add(tbNumber1) Me.Controls.Add(tbNumber2) Me.Controls.Add(lblAnswer) Me.Controls.Add(lblNum1) Me.Controls.Add(lblNum2) End Sub Public Sub Add(ByVal Sender as Object, ByVal e as EventArgs) lblAnswer.Text = CStr(Cint(tbNumber1.Text) + Cint(tbNumber2.Text))

730

Antworten auf die Quizfragen und bungen

78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92:

End Sub Public Sub Subtract(ByVal Sender as Object, ByVal e as EventArgs) lblAnswer.Text = CStr(Cint(tbNumber1.Text) Cint(tbNumber2.Text)) End Sub Public Sub Multiply(ByVal Sender as Object, ByVal e as EventArgs) lblAnswer.Text = CStr(Cint(tbNumber1.Text) * Cint(tbNumber2.Text)) End Sub Public Sub Divide(ByVal Sender as Object, ByVal e as EventArgs) lblAnswer.Text = CStr(Cint(tbNumber1.Text) / Cint(tbNumber2.Text)) End Sub End Class End Namespace

Tag 3
Quiz
Die Fragen 1 bis 3 beziehen sich auf das folgende Codefragment:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: using System; using System.Windows.Forms; using System.Drawing; public class MyForm : Form { private Label lblMessage = new Label(); public MyForm() { this.Text = Hello World!"; } } public class StartForm { public static void Main() { Application.Run(new MyForm()); } }

1. Was wrde das folgende Statement liefern, wenn man es unter Zeile 9 platzieren wrde?
Console.Write(this.ToString()); MyForm, Text: Hello World!

731

Anhang A

2. Was wrde der folgende Code liefern, wenn man ihn unter Zeile 9 platzieren wrde?
Label lblTemp = new Label(); Console.Write(lblTemp.Equals(this.lblMessage).ToString()); False.

3. Was wrde der folgende Code liefern, wenn man ihn unter Zeile 9 platzieren wrde?
Label lblTemp = new Label(); Console.Write(Object.ReferenceEquals(lblTemp, this.lblMessage).ToString()); False.

4. Wahr oder falsch? Das KeyPress-Ereignis erfordert einen Handler vom Typ KeyEventHandler. Falsch. Die KeyPress-Ereignisse bernehmen einen KeyPressEventHandler. 5. Nennen Sie die fnf Eigenschaften des Objekts MouseEventArgs.
Button, Clicks, Delta, X und Y.

6. Schreiben Sie ein einzelnes Statement in VB .NET, das die Breite eines Formulars auf ein Drittel der Bildschirmhhe festlegt.
Me.Width = Screen.GetWorkingArea(Me).Height / 3

7. Welche Eigenschaft kontrolliert, welche Schaltflche aktiviert wird, sobald der Benutzer die (ESC)-Taste gedrckt hat?
CancelButton.

8. Welches ist der Standardwert von FormBorderStyle?


Sizable.

9. Welche drei Ereignisse verwenden das Paradigma, dass mit einer einzelnen Aktion zwei Ereignisse verknpft sind?
Closed, InputLanguageChanged und Validated.

bung
1. Erstellen Sie in C# eine Anwendung, die alle sechs Mausereignisse berwacht. Zeigen Sie eine Meldung in einem Textfeld an, wenn die einzelnen Ereignisse auftreten. 2. Erstellen Sie in VB .NET eine Anwendung, mit der die Benutzer die Eigenschaften Text, Height, Width und Opacity durch Werteingaben in eine TextBox und das Drcken einer EINGEBEN-Schaltflche anpassen knnen.

732

Antworten auf die Quizfragen und bungen

Zu 1:
1: using System; 2: using System.Windows.Forms; 3: using System.Drawing; 4: using System.ComponentModel; 5: 6: namespace TYWinForms.Day3 { 7: 8: public class Exercise1 : Form { 9: private Label lblMessage = new Label(); 10: 11: public Exercise1() { 12: lblMessage.Width = this.Width; 13: lblMessage.Height = this.Height; 14: 15: this.Text = "Mouse events Example"; 16: this.Width = 800; 17: this.Height = 600; 18: this.MouseEnter += new EventHandler(this.MouseEntered); 19: this.MouseHover += new EventHandler(this.MouseHovered); 20: this.MouseLeave += new EventHandler(this.MouseLeft); 21: this.MouseMove += new MouseEventHandler(this.MouseMoved); 22: this.MouseDown += new MouseEventHandler(this.MouseClicked); 23: this.MouseUp += new MouseEventHandler(this.MouseReleased); 24: 25: this.Controls.Add(lblMessage); 26: } 27: 28: public void MouseEntered(Object Sender, EventArgs e) { 29: lblMessage.Text += "Mouse entered\r\n"; 30: } 31: 32: public void MouseHovered(Object Sender, EventArgs e) { 33: lblMessage.Text += "Mouse hovered\r\n"; 34: } 35: 36: public void MouseLeft(Object Sender, EventArgs e) { 37: lblMessage.Text += "Mouse left\r\n"; 38: } 39: 40: public void MouseMoved(Object Sender, MouseEventArgs e) { 41: lblMessage.Text += "Mouse moved: x=" + e.X + ", y=" + e.Y +  "\r\n"; 42: } 43:

733

Anhang A

44: 45: 46: 47: 48: 49:  "\r\n"; 50: 51: 52: 53: 54: 55: } 56: }

public void MouseClicked(Object Sender, MouseEventArgs e) { lblMessage.Text += "Button clicked: " + e.Button + "\r\n"; } public void MouseReleased(Object Sender, MouseEventArgs e) { lblMessage.Text += "Button released: x=" + e.X + ", y=" + e.Y + } public static void Main() { Application.Run(new Exercise1()); }

Zu 2. Der Code fr die Anwendung sieht wie folgt aus:


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: Imports Imports Imports Imports System System.Windows.Forms System.Drawing System.ComponentModel

Namespace TYWinForms.Day3 public class Exercise2 : Inherits Form private btSubmit as new Button private tbText as new TextBox private tbHeight as new TextBox private tbWidth as new TextBox private tbOpacity as new TextBox private lblText as new Label private lblHeight as new Label private lblWidth as new Label private lblOpacity as new Label public sub New() Me.Text = "Event Example" Me.Width = 800 Me.Height = 600 btSubmit.Location = new Point(50, 150) btSubmit.Text = "Submit" AddHandler btSubmit.Click, AddressOf Me.HandleIt lblText.Location = new Point(25,25) lblText.Text = "Text: " tbText.Location = new Point(75,25)

734

Antworten auf die Quizfragen und bungen

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:

tbText.Text = Me.Text lblHeight.Location = new Point(25,50) lblHeight.Text = "Height: " tbHeight.Location = new Point(75,50) tbHeight.Text = Me.Height lblWidth.Location = new Point(25,75) lblWidth.Text = "Width: " tbWidth.Location = new Point(75,75) tbWidth.Text = Me.Width lblOpacity.Location = new Point(25,100) lblOpacity.Text = "Opacity: " tbOpacity.Location = new Point(75,100) tbOpacity.Text = Me.Opacity Me.Controls.Add(btSubmit) Me.Controls.Add(tbText) Me.Controls.Add(tbHeight) Me.Controls.Add(tbWidth) Me.Controls.Add(tbOpacity) Me.Controls.Add(lblText) Me.Controls.Add(lblHeight) Me.Controls.Add(lblWidth) Me.Controls.Add(lblOpacity) end sub public sub HandleIt(Sender as Object, e as EventArgs) Me.Text = tbText.Text Me.Height = CInt(tbHeight.Text) Me.Width = CInt(tbWidth.Text) Me.Opacity = CInt(tbOpacity.Text) end sub end class

public class StartForm public shared sub Main() Application.Run(new Exercise2) end sub end class End Namespace

735

Anhang A

Tag 4
Quiz
1. Wahr oder falsch? Alle Steuerelemente inklusive des Form-Objekts erben von der Klasse Control. Wahr. 2. Welches Objekt muss mit Symbolleisten verknpft sein, damit Grafiken in den Symbolleistenschaltflchen angezeigt werden? Das ImageList-Objekt. 3. Wie heien die drei optionalen Parameter fr einen MenuItem-Konstruktor, und welches sind ihre Typen? Der erste Parameter ist eine Zeichenfolge, die die Aufschrift (oder Text-Eigenschaft) des MenuItem-Steuerelements darstellt. Der 2. Parameter legt den Ereignishandler fest, der auszufhren ist, sobald das MenuItem angeklickt wurde; es handelt sich um ein EventHandler-Objekt. Der 3. Parameter ist ein Wert aus der ShortCut-Aufzhlung, der die Tastenkombination festlegt, die fr den Zugriff auf das Menelement benutzt werden kann. 4. Welches Zeichen wird verwendet, um eine Tastenkombination fr einen Buchstaben in der Beschriftung eines Menelements bereitzustellen? Das kaufmnnische Und-Zeichen (&, auch Ampersand genannt). 5. Schreiben Sie eine Zeile C#-Code, die ein ToolBarButton-Steuerelement namens MyFirstButton anweist, die vierte Grafik in der zugeordneten ImageList zu verwenden.
myFirstButton.ImageIndex = 3;

6. Wahr oder falsch? Das Ereignis, das fr die Behandlung von Mausklicks auf Symbolleistenschaltflchen verwendet wird, heit Click. Falsch. Das Ereignis heit ButtonClick und gehrt zum ToolBar-Objekt. 7. Welche sind die Standardwerte fr die Eigenschaften Minimum, Maximum, SmallChange und LargeChange einer Bildlaufleiste? Jeweils 0, 100, 1 und 10.

bung
Erstellen Sie in VB .NET eine Anwendung, die ein personalisiertes Men verwendet wie jene, die mit Microsoft Office 2000 und Windows 2000 eingefhrt wurden. Diese Mens zeigen

736

Antworten auf die Quizfragen und bungen

nur die zuletzt benutzten Menelemente, whrend sie die anderen verbergen und es dem Benutzer gestatten, auf einen Pfeil zu klicken, um weniger hufig ausgewhlte Menelemente anzuzeigen. Ihre Anwendung sollte ein Men anbieten, das verborgene Menelemente besitzt. Wird ein bestimmtes Menelement angeklickt, sollen die verborgenen Elemente angezeigt werden. Kmmern Sie sich nicht um das Hinzufgen von Ereignishandlern fr jedes Menelement. Ihr Men kann recht einfach sein: Es braucht sich nicht daran zu erinnern, welche Menelemente der Benutzer am hufigsten auswhlt, und es wird keine verborgenen Menelemente anzeigen, nur weil der Mauspfeil ber dem Men schwebt. Sobald der Benutzer auf die Schaltflche WEITERE... (More) klickt, muss er das Men erneut ffnen, um die bislang verborgenen Elemente sehen zu knnen. (Sie werden sehen, wie man fortgeschrittenere Funktionen verwenden kann, wenn Sie an Tag 13 etwas ber GDI+ erfahren. Der Code fr die Anwendung sieht wie folgt aus:
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: Imports System Imports System.Windows.Forms Namespace TYWinForms.Day4 public class Exercise1 : Inherits Form private mnuFile as new MainMenu private miFile as new MenuItem("File") private miOpen as new MenuItem("Open...") private miClose as new MenuItem("Close") private miPrintPreview as new MenuItem("Print Preview") private miPrint as new MenuItem("Print...") private miProperties as new MenuItem("Properties") private miSendTo as new MenuItem("Send To...") private miExit as new MenuItem("Exit") private WithEvents miMore as new MenuItem("More items...") public event PopUp as EventHandler public sub New() Me.Text = "Exercise 1" Me.Menu = mnuFile CreateMenus() End Sub public sub CreateMenus() mnuFile.MenuItems.Add(miFile)

737

Anhang A

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:

miFile.MenuItems.Add(miOpen) miFile.MenuItems.Add(miClose) miFile.MenuItems.Add("-") miFile.MenuItems.Add(miPrintPreview) miFile.MenuItems.Add(miPrint) miFile.MenuItems.Add("-") miFile.MenuItems.Add(miSendTo) miFile.MenuItems.Add(miProperties) miFile.MenuItems.Add("-") miFile.MenuItems.Add(miExit) miFile.MenuItems.Add("-") miFile.MenuItems.Add(miMore) miPrintPreview.Visible = false miProperties.Visible = false miSendTo.Visible = false miFile.MenuItems(5).Visible = false AddHandler miMore.Click, new EventHandler(AddressOf Me.ShowMenu) end sub public sub ShowMenu(Sender as Object, e as EventArgs) miPrintPreview.Visible = true miProperties.Visible = true miSendTo.Visible = true miFile.MenuItems(5).Visible = true miFile.MenuItems(10).Visible = false miMore.Visible = false end sub public Shared Sub Main() Application.Run(new Exercise1) end sub end class End Namespace

738

Antworten auf die Quizfragen und bungen

Tag 5
Quiz
1. Wie sieht die standardmige Signatur eines Ereignishandlers aus?
public sub HandlerName(Object as Sender, e as EventArgs)

2. Erzeugen Sie in C# ein Ereignis namens ShowText, das den Delegaten KeyPressEventHandler verwendet.
public event KeyPressEventHandler ShowText;

3. Erstellen Sie einen benutzerdefinierten Delegaten, der das Objekt KeyPressEventArgs verwendet.
public delegate void CustomEventHandler(Object Sender, KeyPressEventArgs e);

4. Wahr oder falsch? Um Objekttypen miteinander zu vergleichen, verwendet man den Gleichheitszeichenoperator (=). Falsch. Das Gleichheitszeichen wird nur bei einfachen Datentypen eingesetzt. Fr Objekte sollten Sie den is-Operator benutzen. 5. Welche der folgenden Aussagen ist inkorrekt?
AddHandler btOne.Click, new EventHandler(AddressOf btOne_Click) public sub MyEventHandler(Sender as Object, e as EventArgs) Handles btTemp.Click Addhandler btOne.Click += new EventHandler(AddressOf btOne_Click)

Das dritte Statement ist falsch, denn es versucht, sowohl C#- als auch VB .NET-Syntax zu kombinieren. 6. Warum sollten die Eigenschaften eines benutzerdefinierten EventArgs-Objekts readonly sein? Der Ereignishandler, also die das benutzerdefinierte EventArgs-Objekt empfangende Methode, sollte nicht in der Lage sein, diese Eigenschaften zu verndern. Sie sollten vom Ereignis erzeugt werden, um dem Handler ergnzende Informationen zu liefern. 7. Ist das folgende Codestck korrekt? Was sollte man falls ntig ndern?
public virtual void OnMyEvent(EventArgs e) { MyEvent(this, e); }

Erstens muss der Zugriffsmodifizierer von public auf protected gendert werden; nur die Klasse, in der sich diese Methode befindet, sollte ihn ausfhren knnen und public

739

Anhang A

wrde dies jeder Klasse erlauben. Zweitens sollte es eine Prfung geben, um herauszufinden, ob ein Delegat zugewiesen wurde:
if (MyEvent != null) { MyEvent(this, e); }

8. An welcher Stelle (im Quellcode) muss man benutzerdefinierte Ereignisse und Delegaten deklarieren? Ein Ereignis muss innerhalb derjenigen Klasse deklariert werden, die das Ereignis auslsen soll, jedoch auerhalb jeglicher Methodendeklaration. Ein Delegat kann sowohl inner- als auch auerhalb einer Klasse deklariert werden.

bung
Erstellen Sie in C# eine Taschenrechner-Anwendung wie den Windows-Taschenrechner. Er sollte Zifferntasten aufweisen, mit denen sich Zahlen eingeben lassen, sowie Funktionstasten, die Berechnungen vornehmen, wenn man darauf klickt. Verwenden Sie einen Ereignishandler fr alle Zifferntasten und einen Handler fr alle Funktionstasten. (Tipp: Nehmen Sie verborgene Steuerelemente, also solche, deren Visible-Eigenschaft auf False gesetzt ist damit knnen Sie temporre Variablen speichern.)
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: using System; using System.Windows.Forms; using System.Drawing; namespace TYWinforms.Day5 { public class Calculator : Form { private TextBox tbNumber = new TextBox(); private TextBox tbHiddenNumber = new TextBox(); private TextBox tbHiddenOperator = new TextBox(); private TextBox tbHiddenAnswer = new TextBox(); private private private private private private private private private private Button Button Button Button Button Button Button Button Button Button btZero = new Button(); btOne = new Button(); btTwo = new Button(); btThree = new Button(); btFour = new Button(); btFive = new Button(); btSix = new Button(); btSeven = new Button(); btEight = new Button(); btNine = new Button();

740

Antworten auf die Quizfragen und bungen

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: 68: 69:

private private private private private

Button Button Button Button Button

btAdd = new Button(); btSubtract = new Button(); btMultiply = new Button(); btDivide = new Button(); btEquals = new Button();

public Calculator() { tbNumber.Location = new Point(210,10); tbNumber.Width = 100; tbHiddenNumber.Visible = false; tbHiddenOperator.Visible = false; tbHiddenAnswer.Visible = false; btZero.Text = "0"; btZero.Location = new Point(10,125); btZero.Click += new EventHandler(this.NumberClick); btOne.Text = "1"; btOne.Location = new Point(10,100); btOne.Click += new EventHandler(this.NumberClick); btTwo.Text = "2"; btTwo.Location = new Point(85,100); btTwo.Click += new EventHandler(this.NumberClick); btThree.Text = "3"; btThree.Location = new Point(160,100); btThree.Click += new EventHandler(this.NumberClick); btFour.Text = "4"; btFour.Location = new Point(10,75); btFour.Click += new EventHandler(this.NumberClick); btFive.Text = "5"; btFive.Location = new Point(85,75); btFive.Click += new EventHandler(this.NumberClick); btSix.Text = "6"; btSix.Location = new Point(160,75); btSix.Click += new EventHandler(this.NumberClick); btSeven.Text = "7"; btSeven.Location = new Point(10,50); btSeven.Click += new EventHandler(this.NumberClick);

741

Anhang A

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: 112: 113: 114: 115:

btEight.Text = "8"; btEight.Location = new Point(85,50); btEight.Click += new EventHandler(this.NumberClick); btNine.Text = "9"; btNine.Location = new Point(160,50); btNine.Click += new EventHandler(this.NumberClick); btAdd.Text = "+"; btAdd.Location = new Point(160,125); btAdd.Click += new EventHandler(this.OperatorClick); btSubtract.Text = "-"; btSubtract.Location = new Point(235,100); btSubtract.Click += new EventHandler(this.OperatorClick); btMultiply.Text = "*"; btMultiply.Location = new Point(235,75); btMultiply.Click += new EventHandler(this.OperatorClick); btDivide.Text = "/"; btDivide.Location = new Point(235,50); btDivide.Click += new EventHandler(this.OperatorClick); btEquals.Text = "="; btEquals.Location = new Point(235,125); btEquals.Click += new EventHandler(this.OperatorClick);

this.Text = "Calculator"; this.Width = 325; this.Height = 200; this.Controls.Add(btZero); this.Controls.Add(btOne); this.Controls.Add(btTwo); this.Controls.Add(btThree); this.Controls.Add(btFour); this.Controls.Add(btFive); this.Controls.Add(btSix); this.Controls.Add(btSeven); this.Controls.Add(btEight); this.Controls.Add(btNine); this.Controls.Add(btAdd); this.Controls.Add(btSubtract); this.Controls.Add(btMultiply); this.Controls.Add(btDivide);

742

Antworten auf die Quizfragen und bungen

116: this.Controls.Add(btEquals); 117: this.Controls.Add(tbNumber); 118: this.Controls.Add(tbHiddenOperator); 119: this.Controls.Add(tbHiddenNumber); 120: this.Controls.Add(tbHiddenAnswer); 121: } 122: 123: public void NumberClick(Object Sender, EventArgs e) { 124: if (tbNumber.Text != "" & tbNumber.Text != "0" &  tbHiddenAnswer.Text != "1") { 125: tbNumber.Text += ((Button)Sender).Text; 126: } else { 127: tbNumber.Text = ((Button)Sender).Text; 128: tbHiddenAnswer.Text = ""; 129: } 130: } 131: 132: public void OperatorClick(Object Sender, EventArgs e) { 133: int intAnswer = 0; 134: 135: if (tbNumber.Text != "" & ((Button)Sender).Text != "=") { 136: tbHiddenNumber.Text = tbNumber.Text; 137: tbHiddenOperator.Text = ((Button)Sender).Text; 138: tbNumber.Text = "0"; 139: } else if (tbNumber.Text != "" & ((Button)Sender).Text == 140: switch(tbHiddenOperator.Text) { 141: case "+": 142: intAnswer = Convert.ToInt32 (tbHiddenNumber.Text)  Convert.ToInt32(tbNumber.Text); 143: break; 144: case "-": 145: intAnswer = Convert.ToInt32 (tbHiddenNumber.Text)  Convert.ToInt32(tbNumber.Text); 46: break; 147: case "*": 148: intAnswer = Convert.ToInt32 (tbHiddenNumber.Text)  Convert.ToInt32(tbNumber.Text); 149: break; 150: case "/": 151: intAnswer = Convert.ToInt32 (tbHiddenNumber.Text)  Convert.ToInt32(tbNumber.Text); 152: break; 153: } 154: tbNumber.Text = intAnswer.ToString(); 155: tbHiddenAnswer.Text = "1"; 156: tbHiddenNumber.Text = "";

"=") {

743

Anhang A

157: 158: 159: 160: 161: 162: 163: 164: 165:

tbHiddenOperator.Text = ""; } } public static void Main() { Application.Run(new Calculator()); } } }

Tag 6
Quiz
1. Wodurch werden RadioButton-Auswahlen begrenzt? Der Benutzer kann nur ein RadioButton-Steuerelement pro Containersteuerelement auswhlen. 2. Was bedeutet die folgende Datums-/Zeit-Formatzeichenfolge?
"hh:MM:yy-dddd"

Zwei-Ziffern-Stundenanzeige, gefolgt von einem Doppelpunkt und dem Zwei-ZiffernMonat, wiederum gefolgt von einem Doppelpunkt und einer Zwei-Ziffern-Jahreszahl, gefolgt von einem Gedankenstrich und dem ausgeschriebenen Wochentag. 3. Welches Objekt ist mit dem TreeView-Steuerelement verknpft? Das TreeNode-Objekt, welches zur Darstellung jedes Knotens in der Strukturansicht benutzt wird. 4. Setzen Sie fr ein Button-Steuerelement namens btHappy Eigenschaften, die es dazu veranlassen, senkrecht zu expandieren, sobald sein Containerformular seine Gre ndert.
btHappy.Anchor = AnchorStyles.Top Or AnchorStyles.Bottom

5. Nennen Sie zehn Mitglieder, die allen heute besprochenen Steuerelementen gemeinsam sind! Die Eigenschaften Anchor, Dock, BackColor, Location, Font und Text, die Methoden GetType und Refresh sowie die Ereignisse Click und Resize. 6. Wie heien die fnf Mitglieder des Timer-Steuerelements?
Enabled, Interval, Tick, Start und Stop.

744

Antworten auf die Quizfragen und bungen

7. Wie fgt man dem Steuerelement TabControl Tabseiten hinzu? Beschreiben Sie dies in Worten und mit Hilfe einer Zeile Code. Man verwendet TabPage-Objekte, um weitere Tabseiten hinzuzufgen. Im folgenden Beispiel sei ein TabControl-Steuerelement namens MyTabControl gegeben:
MyTabControl.TabPages.Add(New TabPage("Label text"));

8. Welche beiden Methoden sollte man ausfhren, wenn man vorhat, einer ComboBox mit Hilfe der Add-Methode viele Elemente hinzuzufgen? Wenn Sie an Ihr Wissen ber Windows Forms-Steuerelemente denken: Welche anderen heute vorgestellten Steuerelemente haben Ihrer Ansicht nach ebenfalls diese Methoden? Die BeginUpdate-Methode wird benutzt, um das Steuerelement am Aktualisieren seiner Anzeige zu hindern, bis die EndUpdate-Methode aufgerufen wird. Neben der ComboBox sind auch die ListBox- und TreeView-Steuerelemente in der Lage, diese Methoden einzusetzen. 9. Wahr oder falsch? Das PictureBox-Steuerelement kann nur Bitmap- (.BMP) und GIFGrafiken anzeigen. Falsch. Das PictureBox-Steuerelement kann diese ebenso anzeigen wie viele andere Formate, beispielsweise .JPG- und .ICO-Dateien.

bung
Erstellen Sie in C# eine Anwendung, mit deren Hilfe Benutzer ihre CD-Sammlung katalogisieren und betrachten knnen. Sie mssen neue Knstler/Alben/Stcke in den Katalog aufnehmen und mit einer hierarchischen Liste betrachten knnen. (Kmmern Sie sich nicht um das Speichern der Eingaben das besprechen wir an Tag 9, wenn es um ADO.NET geht.) Denken Sie daran, dass Alben nur Knstlern hinzugefgt werden und Stcke nur Alben.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: using System; using System.Windows.Forms; using System.Drawing; namespace TYWinForms.Day6 { public class CDCatalog : Form { private TreeView tvList = new TreeView(); private TextBox tbName = new TextBox(); private Button btArtist = new Button(); private Button btAlbum = new Button(); private Button btSong = new Button(); public CDCatalog() { tvList.Location = new Point(10,10);

745

Anhang A

16: tvList.Size = new Size(200,550); 17: tvList.LabelEdit = true; 18: 19: tbName.Location = new Point(250,10); 20: tbName.Width = 150; 21: 22: btArtist.Location = new Point(225,40); 23: btArtist.Text = "Add Artist"; 24: btArtist.Click += new EventHandler(this.AddArtist); 25: 26: btAlbum.Location = new Point(300,40); 27: btAlbum.Text = "Add Album"; 28: btAlbum.Click += new EventHandler(this.AddAlbum); 29: 30: btSong.Location = new Point(375,40); 31: btSong.Text = "Add Song"; 32: btSong.Click += new EventHandler(this.AddSong); 33: 34: this.Text = "CD Catalog"; 35: this.Size = new Size(800,600); 36: this.Controls.Add(tvList); 37: this.Controls.Add(tbName); 38: this.Controls.Add(btArtist); 39: this.Controls.Add(btAlbum); 40: this.Controls.Add(btSong); 41: } 42: 43: public void AddArtist(Object Sender, EventArgs e) { 44: if (tbName.Text == "") { 45: MessageBox.Show("You forgot to enter a name"); 46: return; 47: } 48: 49: tvList.Nodes.Add(new TreeNode(tbName.Text)); 50: tbName.Text = ""; 51: } 52: 53: public void AddAlbum(Object Sender, EventArgs e) { 54: if (tbName.Text == "") { 55: MessageBox.Show("You forgot to enter a name"); 56: return; 57: } 58: 59: if (tvList.SelectedNode != null &  tvList.Nodes.Contains(tvList.SelectedNode)) { 60: tvList.SelectedNode.Nodes.Add(new TreeNode (tbName.Text));

746

Antworten auf die Quizfragen und bungen

61: tbName.Text = ""; 62: tvList.ExpandAll(); 63: } else { 64: MessageBox.Show("You must first select an artist"); 65: } 66: } 67: 68: public void AddSong(Object Sender, EventArgs e) { 69: if (tbName.Text == "") { 70: MessageBox.Show("You forgot to enter a name"); 71: return; 72: } 73: 74: if (tvList.SelectedNode != null &  tvList.Nodes.Contains(tvList.SelectedNode.Parent)) { 75: tvList.SelectedNode.Nodes.Add(new TreeNode (tbName.Text)); 76: tbName.Text = ""; 77: tvList.ExpandAll(); 78: } else { 79: MessageBox.Show("You must first select an album"); 80: } 81: } 82: 83: public static void Main() { 84: Application.Run(new CDCatalog()); 85: } 86: } 87: }

Tag 7
Quiz
1. Wahr oder falsch? Jedes Form-Objekt kann ein Dialogfeld sein. Wahr. 2. Wie macht man ein Dialogfeld modal bzw. nichtmodal? Durch Aufruf der Methode ShowDialog bzw. durch Aufruf der Methode Show. 3. Wahr oder falsch? Sie knnen ein MessageBox-Objekt direkt instantiieren. Falsch. Man kann nur auf die statische Show-Methode dieses Objekts zugreifen.

747

Anhang A

4. Wie heien die sieben Parameter, die die MessageBox.Show-Methode bernehmen kann? (Fhren Sie nur ihre Typen mit einer kurzen Beschreibung auf.)
Iwin32Window: Das Fenster, vor dem das Meldungsfeld angezeigt wird String: Der im Meldungsfeld anzuzeigende Text String: Die Titelzeile des Fensters MessageBoxButtons: Die Schaltflchen, die im Meldungsfeld erscheinen sollen MessageBoxIcon: Das anzuzeigende Symbol MessageBoxDefaultButton: Die vorausgewhlte Schaltflche MessageBoxOptions: Diverse nicht damit verknpfte Eigenschaften

5. Welche zwei Vorteile erzielen Sie, wenn Sie in Ihrer benutzerdefinierten Dialogklasse einen DialogResult-Wert zuweisen? Es liefert dem bergeordneten Formular einen DialogResult-Wert und schliet das Dialogfeld automatisch, wenn der Benutzer eine Schaltflche auswhlt (man muss die Hide-Methode nicht aufrufen). 6. Ist das folgende Codestck korrekt? Falls nicht, nennen Sie die Fehler.
public property IsClicked as string Get return blnClicked end property

Wird nur das Get-Statement benutzt, muss die Eigenschaft als readonly markiert werden. Auerdem fehlt das End Get-Statement. 7. Welche zwei Mitglieder haben fast alle Standarddialogfeld-Steuerelemente gemeinsam? Nennen Sie die Ausnahme(n).
ShowHelp und Reset. Diese Mitglieder sind Bestandteil aller Standarddialogfeld-Steuerelemente, mit Ausnahme des PrintPreviewDialog-Steuerelements.

8. Schreiben Sie eine Filterzeichenfolge fr ein OpenFileDialog-Steuerelement, das Textdateien (*.txt), Bilddateien (*.gif) und Alle Dateien (*.*) anzeigt.
"Text Dateien (*.txt)|*.txt|GIF-Bilder (*.gif)|*.gif|Alle Dateien (*.*)|*.*"

9. Nennen Sie die Haupteigenschaften (also die Eigenschaften, die Sie am meisten interessieren, wenn der Benutzer eine Auswahl trifft) fr die Steuerelemente OpenFileDialog, SaveFileDialog, ColorDialog und FontDialog. Jeweils Filename, Filename, Color und Font.

748

Antworten auf die Quizfragen und bungen

bung
Erstellen Sie in C# eine voll funktionsfhige Anwendung mit Mens, welche alle Standarddialogfeld-Steuerelemente verwendet. Setzen Sie auch ein nichtmodales Dialogfeld ein, um kontextabhngige Hilfeinformationen anzuzeigen. Wenn sich ein Benutzer von einem Menelement zum nchsten bewegt, sollte sich der Inhalt dieses Dialogfeldes ndern. (Tipp: Verwenden Sie das MenuItem.Select-Ereignis, um festzustellen, ber welchem Menelement sich die Maus gerade befindet.)
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: using using using using System; System.Windows.Forms; System.Drawing; System.Drawing.Printing;

namespace TYWinforms.Day7 { public class Exercise1 : Form { PageSettings objPageSettings = new PageSettings(); MainMenu mnuMain = new MainMenu(); MenuItem mniFile = new MenuItem("File"); MenuItem mniOpen = new MenuItem("Open"); MenuItem mniSave = new MenuItem("Save"); MenuItem mniPageSetup = new MenuItem("Page Setup"); MenuItem mniPrintPreview = new MenuItem("Print Preview"); MenuItem mniPrint = new MenuItem("Print"); MenuItem mniEdit = new MenuItem("Edit"); MenuItem mniFont = new MenuItem("Font"); MenuItem mniColor = new MenuItem("Color"); HelpDialog dlgHelp = new HelpDialog(); public Exercise1() { mnuMain.MenuItems.Add(mniFile); mnuMain.MenuItems.Add(mniEdit); mniFile.MenuItems.Add(mniOpen); mniFile.MenuItems.Add(mniSave); mniFile.MenuItems.Add("-"); mniFile.MenuItems.Add(mniPageSetup); mniFile.MenuItems.Add(mniPrintPreview); mniFile.MenuItems.Add(mniPrint); mniEdit.MenuItems.Add(mniFont); mniEdit.MenuItems.Add(mniColor); mniOpen.Click += new EventHandler(this.FileMenuClick); mniSave.Click += new EventHandler(this.FileMenuClick);

749

Anhang A

38: mniPageSetup.Click += new EventHandler(this.FileMenuClick); 39: mniPrintPreview.Click += new EventHandler(this.FileMenuClick); 40: mniPrint.Click += new EventHandler(this.FileMenuClick); 41: mniFont.Click += new EventHandler(this.EditMenuClick); 42: mniColor.Click += new EventHandler(this.EditMenuClick); 43: 44: mniOpen.Select += new EventHandler(this.DisplayHelp); 45: mniSave.Select += new EventHandler(this.DisplayHelp); 46: mniPageSetup.Select += new EventHandler(this.DisplayHelp); 47: mniPrintPreview.Select += new EventHandler(this.DisplayHelp); 48: mniPrint.Select += new EventHandler(this.DisplayHelp); 49: mniFont.Select += new EventHandler(this.DisplayHelp); 50: mniColor.Select += new EventHandler(this.DisplayHelp); 51: 52: this.Menu = mnuMain; 53: this.Text = "Exercise 1"; 54: } 55: 56: public void FileMenuClick(Object Sender, EventArgs e) { 57: MenuItem mniTemp = (MenuItem)Sender; 58: 59: switch (mniTemp.Text) { 60: case "Open": 61: OpenFileDialog dlgOpen = new OpenFileDialog(); 62: if (dlgOpen.ShowDialog() == DialogResult.OK) { 63: //open file 64: } 65: break; 66: case "Save": 67: SaveFileDialog dlgSave = new SaveFileDialog(); 68: if (dlgSave.ShowDialog() == DialogResult.OK) { 69: //save file 70: } 71: break; 72: case "Page Setup": 73: PageSetupDialog dlgPageSetup = new PageSetupDialog(); 74: dlgPageSetup.PageSettings = objPageSettings; 75: dlgPageSetup.ShowDialog(); 76: break; 77: case "Print Preview": 78: PrintPreviewDialog dlgPrintPreview = new  PrintPreviewDialog(); 79: dlgPrintPreview.Document = new PrintDocument(); 80: dlgPrintPreview.ShowDialog(); 81: break; 82: case "Print":

750

Antworten auf die Quizfragen und bungen

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: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128:

PrintDialog dlgPrint = new PrintDialog(); dlgPrint.PrinterSettings = new PrinterSettings(); if (dlgPrint.ShowDialog() == DialogResult.OK) { //print } break; } } public void EditMenuClick(Object Sender, EventArgs e) { MenuItem mniTemp = (MenuItem)Sender; switch (mniTemp.Text) { case "Font": FontDialog dlgFont = new FontDialog(); if (dlgFont.ShowDialog() == DialogResult.OK) { this.Font = dlgFont.Font; } break; case "Color": ColorDialog dlgColor = new ColorDialog(); if (dlgColor.ShowDialog() == DialogResult.OK) { this.BackColor = dlgColor.Color; } break; } } public void DisplayHelp(Object Sender, EventArgs e) { MenuItem mniTemp = (MenuItem)Sender; if (!dlgHelp.Visible) { dlgHelp.Show(); } switch (mniTemp.Text) { case "Open": dlgHelp.HelpText = break; case "Save": dlgHelp.HelpText = break; case "Page Setup": dlgHelp.HelpText = break; case "Print Preview": dlgHelp.HelpText = break;

"Open a file";

"Save this file";

"Change page settings";

"Preview the document before printing";

751

Anhang A

129: case "Print": 130: dlgHelp.HelpText = "Print the current document"; 131: break; 132: case "Font": 133: dlgHelp.HelpText = "Change the current font face, color,  size, etc"; 134: break; 135: case "Color": 136: dlgHelp.HelpText = "Change the background color of the  application"; 137: break; 138: } 139: } 140: 141: public static void Main() { 142: Application.Run(new Exercise1()); 143: } 144: } 145: 146: public class HelpDialog : Form { 147: private Label lblMessage = new Label(); 148: 149: public String HelpText { 150: get { return lblMessage.Text; } 151: set { lblMessage.Text = value; } 152: } 153: 154: public HelpDialog() { 155: lblMessage.Size = new Size(100,100); 156: lblMessage.Text = "Help screen"; 157: 158: this.Controls.Add(lblMessage); 159: this.BackColor = Color.Yellow; 160: this.Size = new Size(100,100); 161: this.FormBorderStyle = FormBorderStyle.FixedToolWindow; 162: } 163: } 164: }

752

Antworten auf die Quizfragen und bungen

Tag 8
Quiz
1. Schreiben Sie ein Statement, das einem Textfeld-Steuerelement namens tbOne eine neue Datenbindung hinzufgt. Binden Sie an die Text-Eigenschaft das Array arrOne.
tbOne.DataBindings.Add("Text", arrOne, "");

2. Ist das folgende Statement fr das Textfeld aus Frage 1 korrekt?


tbOne.BindingContext.Position += 1;

Nein, Sie mssen den bestimmten Bindungskontext so festlegen, dass er sich laufend erhht:
tbOne.BindingContext[arrOne].Position += 1;

3. Wahr oder falsch? Man kann Daten an ein Form-Objekt binden. Wahr. Solche Daten anzubinden, ist hufig eine einfache Methode, eine Anwendung mit Daten zu verknpfen. 4. Welches Ereignis wird oft verwendet, um die HitTest-Methode aufzurufen, und aus welchem Grund? Dafr wird oft das MouseDown-Ereignis benutzt. Es liefert spezifische Koordinaten, wo sich der Mausklick ereignete. 5. Wie wrde die Anzeige der Zahl "458.34e+04" aussehen, wenn man die Formatzeichenfolge "d3" darauf anwendet? Das ist eine Fangfrage. Die Anzeige wrde "4583400" lauten. Wenn man einen Przisionswert in der Formatzeichenfolge mit weniger Ziffern als im Originalwert angibt, wird die Genauigkeit ignoriert. 6. Welcher Eigenschaft des DataGrid-Steuerelements fgen Sie DataGridTableStyleObjekte hinzu? Der TableStyles-Eigenschaft. 7. Welcher Eigenschaft des DataGrid-Steuerelements fgen Sie DataGridColumnStyleObjekte hinzu? Schon wieder eine Fangfrage. Man fgt keine DataGridColumnStyle-Objekte einem DataGrid-Steuerelement hinzu, sondern fgt sie der GridColumnStyles-Eigenschaft des DataGridTableStyle-Objekts hinzu.

753

Anhang A

8. Angenommen, Sie haben eine DataTable namens dtData mit zwei Spalten namens ColA und ColB. Erzeugen Sie eine neue, leere Zeile.
DataRow myRow myRow = dtRow.NewRow(); dtData.Rows.Add(myRow);

9. Erzeugen Sie die ColA-Spalte der in Frage 8 erwhnten DataTable als einen Integer-Typ.
DataColumn colA = new DataColumn(); colA.DataType = System.Type.GetType("System.Int32"); ColA.ColumnName = "ColA"; dtData.Columns.Add(colA);

bung
Erstellen Sie eine DataGrid und DataTable verwendende Anwendung, in der der Benutzer seine Kontofhrungsinformationen wie in Quicken oder Microsoft Money eingeben kann. Gestatten Sie nicht, dass das DataGrid manuell editiert wird, sondern stellen Sie vielmehr dem Benutzer andere Steuerelemente fr die Eingabe der Informationen bereit, die dann im DataGrid beim Besttigen bzw. Absenden angezeigt werden. Um dieses Beispiel einfach zu halten, gestatten Sie momentan nur Abbuchungen.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: using using using using System; System.Windows.Forms; System.Drawing; System.Data;

namespace TYWinforms.Day8 { public class Exercise1 : Form { private DataGrid dgMoney = new DataGrid(); private DataTable dtMoney = new DataTable("Money"); private private private private private private private private private private Panel pnlAdd = new Panel(); TextBox tbNum = new TextBox(); TextBox tbDate = new TextBox(); TextBox tbPayee = new TextBox(); TextBox tbAmount = new TextBox(); Label lblNum = new Label(); Label lblDate = new Label(); Label lblPayee = new Label(); Label lblAmount = new Label(); Button btSubmit = new Button();

public Exercise1() { //create columns

754

Antworten auf die Quizfragen und bungen

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: 68: 69:

DataColumn colNum = new DataColumn(); colNum.DataType = System.Type.GetType ("System.Int32"); colNum.ColumnName = "Check Number"; dtMoney.Columns.Add(colNum); DataColumn colAmount = new DataColumn(); colAmount.DataType = System.Type.GetType ("System.Double"); colAmount.ColumnName = "Amount"; dtMoney.Columns.Add(colAmount); DataColumn colPayee = new DataColumn(); colPayee.DataType = System.Type.GetType ("System.String"); colPayee.ColumnName = "Payee"; dtMoney.Columns.Add(colPayee); DataColumn colDate = new DataColumn(); colDate.DataType = System.Type.GetType ("System.DateTime"); colDate.ColumnName = "Date"; dtMoney.Columns.Add(colDate); //create styles DataGridTableStyle dgtStyle = new DataGridTableStyle(); dgtStyle.MappingName = "Money"; DataGridTextBoxColumn dgcStyle = new DataGridTextBoxColumn(); dgcStyle.MappingName = "Check Number"; dgcStyle.ReadOnly = true; dgcStyle.HeaderText = "Check No."; dgcStyle.Width = 100; dgtStyle.GridColumnStyles.Add(dgcStyle); dgcStyle = new DataGridTextBoxColumn(); dgcStyle.MappingName = "Date"; dgcStyle.ReadOnly = true; dgcStyle.HeaderText = "Date"; dgcStyle.Width = 100; dgtStyle.GridColumnStyles.Add(dgcStyle); dgcStyle = new DataGridTextBoxColumn(); dgcStyle.MappingName = "Payee"; dgcStyle.ReadOnly = true; dgcStyle.HeaderText = "Payee"; dgcStyle.Width = 452; dgtStyle.GridColumnStyles.Add(dgcStyle); dgcStyle = new DataGridTextBoxColumn();

755

Anhang A

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: 112: 113: 114: 115:

dgcStyle.MappingName = "Amount"; dgcStyle.ReadOnly = true; dgcStyle.HeaderText = "Amount"; dgcStyle.Width = 100; dgcStyle.Format = "c"; dgtStyle.GridColumnStyles.Add(dgcStyle); //create add form lblNum.Text = "Check No."; lblNum.Location = new Point(10,250); tbNum.Location = new Point(75,245); lblDate.Text = "Date"; lblDate.Location = new Point(600,250); tbDate.Location = new Point(675,245); lblPayee.Text = "Pay To: "; lblPayee.Location = new Point(10,280); tbPayee.Location = new Point(75,275); tbPayee.Width = 400; lblAmount.Text = "Amount"; lblAmount.Location = new Point(600,280); tbAmount.Location = new Point(675,275); btSubmit.Text = "Enter"; btSubmit.Location = new Point(675,310); btSubmit.Click += new EventHandler (this.AddRecord); pnlAdd.Size = new Size(800,500); pnlAdd.Dock = DockStyle.Bottom; pnlAdd.BackColor = Color.Tan; pnlAdd.Controls.Add(tbNum); pnlAdd.Controls.Add(tbDate); pnlAdd.Controls.Add(tbPayee); pnlAdd.Controls.Add(tbAmount); pnlAdd.Controls.Add(lblNum); pnlAdd.Controls.Add(lblDate); pnlAdd.Controls.Add(lblPayee); pnlAdd.Controls.Add(lblAmount); pnlAdd.Controls.Add(btSubmit); dgMoney.Dock = DockStyle.Top; dgMoney.Size = new Size(800,300); dgMoney.CaptionText = "Checkbook Register"; dgMoney.BackgroundColor = Color.White;

756

Antworten auf die Quizfragen und bungen

116: dgMoney.TableStyles.Add(dgtStyle); 117: dgMoney.DataSource = dtMoney; 118: 119: this.Size = new Size(800,600); 120: this.Text = ".NET Check Book Register"; 121: this.Controls.Add(dgMoney); 122: this.Controls.Add(pnlAdd); 123: } 124: 125: private void AddRecord(Object Sender, EventArgs e) { 126: if (tbPayee.Text == "" | tbAmount.Text == "") { 127: MessageBox.Show("You forgot to enter a payee or amount.","Check  Register"); 128: } else { 129: DataRow newRow = dtMoney.NewRow(); 130: 131: newRow[0] = Convert.ToInt32(tbNum.Text); 132: newRow[1] = Convert.ToDouble(tbAmount.Text); 133: newRow[2] = tbPayee.Text; 134: newRow[3] = Convert.ToDateTime(tbDate.Text); 135: 136: dtMoney.Rows.Add(newRow); 137: 138: tbNum.Text = ""; 139: tbDate.Text = ""; 140: tbAmount.Text = ""; 141: tbPayee.Text = ""; 142: } 143: } 144: 145: public static void Main() { 146: Application.Run(new Exercise1()); 147: } 148: } 149: }

757

Anhang A

Tag 9
Quiz
1. Schreiben Sie ein SELECT-Statement, das aus der tblUsers-Tabelle nur solche Datenstze holt, in denen der Wert fr das Feld UserID zwischen 5 und 10 liegt. Sie knnen dieses Statement auf zwei verschiedene Arten formulieren:
SELECT * FROM tblUsers WHERE UserID < 11 and UserID > 4

oder
SELECT * FROM tblUsers WHERE UserID BETWEEN 5 AND 10

2. Welche Parameter fordert das SqlCommandBuilder-Objekt fr seinen Konstruktor? Das SqlCommandBuilder-Objekt erfordert eine Instanz eines SqlDataAdapter-Objekts. 3. Ein SqlCommandBuilder erzeugt SQL-Befehle nur dann automatisch, wenn ein Primrschlssel existiert. Warum? Die erzeugten Befehle mssen eine WHERE-Klausel enthalten, die die Zahl der genderten Datenstze auf die richtigen eingrenzt. Die Verwendung eines Primrschlssels ist ein Weg, um sicherzustellen, dass nur die richtigen Datenstze gendert werden. Das folgende Statement knnte mehr als einen Datensatz verndern:
DELETE FROM tblUsers WHERE NACHNAME = 'Payne'

Statt dessen will man sicherstellen, dass nur ein einziger Datensatz betroffen ist der Primrschlssel, der ja fr jeden Datensatz eindeutig sein muss, sorgt dafr. Der folgende Befehl wird garantiert nur einen einzigen Datensatz lschen:
DELETE FROM tblUsers WHERE UserID = 1

4. Wahr oder falsch? Meist knnen Sie die Vorsilbe Sql durch OleDb ersetzen, um die Objekte im OLE DB-Provider nutzen zu knnen. Wahr. Beispielsweise wird SqlDataAdapter zu OleDbDataAdapter und aus SqlConnection wird OleDbConnection. 5. Ist es ausreichend, die DataSource-Eigenschaft eines DataGrid-Steuerelements auf ein geflltes DataSet zu setzen? Nein. Man muss auerdem die DataMember-Eigenschaft auf die DataTable einstellen, die die anzuzeigenden Informationen enthlt. 6. Schreiben Sie fr ein gegebenes SqlCommand-Objekt namens objCmd ein Statement, das einen Parameter namens @Geburtsdatum mit einem Wert von 1/7/01 hinzufgt.

758

Antworten auf die Quizfragen und bungen

Sie knnen das Statement auf zweierlei Weise formulieren:


objCmd.Parameters.Add("@Geburtsdatum", SqlDbType.varChar, 10).Value = "1/7/ 01";

oder:
objCmd.Parameters.Add("@Geburtsdatum", SqlDbType.DateTime, 10).Value = "1/7/ 01";

7. Nennen Sie die Methode, die veranlasst, dass nderungen sofort in ein DataSet geschrieben werden. Dies ist die EndCurrentEdit-Methode des BindingContext-Objekts.

bung
Erstellen Sie eine Anwendung, mit der der Benutzer SQL-Statements in ein Textfeld eingeben kann. Sollte das Statement Ergebnisse liefern, legen Sie sie in einem DataSet ab und gestatten Sie dessen Bearbeitung. Kmmern Sie sich nicht darum, ob die Abfrage im richtigen Format eingegeben wird; Fehlerprfungen werden erst an Tag 21 durchgenommen.
1: using System; 2: using System.Windows.Forms; 3: using System.Drawing; 4: using System.Data; 5: using System.Data.SqlClient; 6: 7: namespace TYWinforms.Day9 { 8: public class Exercise1 : Form { 9: private DataGrid dgData = new DataGrid(); 10: private TextBox tbCommand = new TextBox(); 11: private Button btExecute = new Button(); 12: private DataSet objDS = new DataSet(); 13: private String strConn; 14: 15: public Exercise1() { 16: strConn = "Initial Catalog=TYWinforms;DataSource=localhost;User  ID=sa"; 17: 18: tbCommand.Location = new Point(150,75); 19: tbCommand.Multiline = true; 20: tbCommand.Height = 50; 21: tbCommand.Width = 300; 22: 23: btExecute.Location = new Point(475,75); 24: btExecute.Text = "Execute!"; 25: btExecute.Click += new EventHandler (this.Execute); 26:

759

Anhang A

27: dgData.Dock = DockStyle.Bottom; 28: dgData.Size = new Size(800,400); 29: dgData.Enabled = false; 30: dgData.CurrentCellChanged += new EventHandler(this.UpdateData); 31: 32: this.Size = new Size(800,600); 33: this.Text = "Exercise 1"; 34: this.Controls.Add(dgData); 35: this.Controls.Add(tbCommand); 36: this.Controls.Add(btExecute); 37: } 38: 39: private void Execute(Object Sender, EventArgs e) { 40: if (tbCommand.Text != "") { 41: SqlConnection objConn = new SqlConnection(strConn); 42: 43: SqlDataAdapter objCmd = new SqlDataAdapter(tbCommand.Text,  objConn); 44: objDS.Clear(); 45: int intRows = objCmd.Fill(objDS, "Query"); 46: 47: if (intRows > 0) { 48: dgData.ResetBindings(); 49: dgData.DataSource = objDS; 50: dgData.DataMember = "Query"; 51: dgData.Enabled = true; 52: } else { 53: dgData.Enabled = false; 54: } 55: } 56: } 57: 58: public void UpdateData(Object Sender, EventArgs e) { 59: SqlConnection objConn = new SqlConnection(strConn); 60: SqlDataAdapter objCmd = new SqlDataAdapter(tbCommand.Text,  objConn); 61: SqlCommandBuilder objBuilder = new SqlCommandBuilder(objCmd); 62: 63: objCmd.Update(objDS, "Query"); 64: } 65: 66: public static void Main() { 67: Application.Run(new Exercise1()); 68: } 69: } 70: }

760

Antworten auf die Quizfragen und bungen

Tag 10
Quiz
1. Welche Eigenschaft mssen Sie im bergeordneten MDI-Formular einstellen, um es zu einer MDI-Anwendung zu machen? Man muss IsMdiContainer auf true setzen. 2. Welche Eigenschaft mssen Sie im untergeordneten MDI-Dokument einstellen, um es zum Bestandteil einer MDI-Anwendung zu machen? Man setzt die MdiParent-Eigenschaft des untergeordneten Dokuments auf das bergeordnete Formular. Das muss im bergeordneten Formular selbst erfolgen. 3. ber welche drei Werte verfgt die Aufzhlung MdiLayout?
MdiLayout.Cascade, MdiLayout.TileHorizontal und MdiLayout.TileVertical.

4. Wahr oder falsch? Sie mssen erst die Show-Methode aufrufen, bevor Sie ein untergeordnetes MDI-Formular anzeigen knnen, wenn es im Konstruktor seines bergeordneten Formulars erzeugt wird. Falsch. Man braucht die Show-Methode nur aufzurufen, wenn das untergeordnete Dokument auerhalb des Konstruktors angelegt wird. 5. Der folgende Code veranlasst das TextBox-Steuerelement tbText nicht dazu, das gesamte Formular auszufllen:
TextBox tbText = new TextBox(); tbText.Dock = DockStyle.Fill;

Warum nicht? Damit sich ein TextBox-Steuerelement in der Hhe mehr als ber den Vorgabewert ausdehnt, muss man auch die Multiline-Eigenschaft auf true setzen:
tbText.Multiline = true;

6. Wie findet man heraus, ob ein untergeordnetes MDI-Fenster aktiv ist? Man bewertet die ActiveMdiChild-Eigenschaft und findet heraus, ob sie mit null (Nothing in VB .NET) bewertet wird. 7. Sie haben drei Menelemente in einem untergeordneten Dokument erzeugt, aber sie werden im bergeordneten Dokument nicht richtig angezeigt. Nennen Sie drei Dinge, die man zur Behebung dieses Problems prfen sollte.

761

Anhang A

Setzt man einen anderen MenuMerge-Wert als Remove ein, sollte man dafr sorgen, dass die Menelemente sowohl in der bergeordneten als auch in den untergeordneten Klassen zugewiesen sind. Man muss dafr sorgen, dass die MergeOrder-Werte bereinstimmen (die Mens, die sich verbinden/miteinander verschmelzen/ersetzen sollen, haben gleiche MergeOrderWerte). Prfen Sie besser doppelt, ob Sie die richtigen MenuMerge-Werte benutzen. Sie knnten aus Versehen Remove in der untergeordneten Klasse benutzen, im Glauben, dass dies das bergeordnete Menelement entfernt, doch in Wahrheit entfernt dies das untergeordnete Menelement. 8. Wahr oder falsch? Der MergeOrder-Wert muss sich schrittweise erhhen lassen; Sie knnen keine Werte in der Reihenfolge berspringen. Falsch. Der tatschliche Wert von MergeOrder ist gleichgltig, wenn man Menelemente anordnet; nur auf relative Werte kommt es an.

bung
Erstellen Sie eine MDI-Version der gestrigen Anwendung fr die Ausfhrung von SQL-Statements. Erzeugen Sie ein benutzerdefiniertes Dialogfeld, das ein Textfeld anzeigt, in dem der Benutzer seine Abfrage eingeben kann. Exercise1.cs:
1: using System; 2: using System.Windows.Forms; 3: using System.Drawing; 4: 5: namespace TYWinforms.Day10 { 6: public class Exercise1 : Form { 7: private MainMenu mnuMain = new MainMenu(); 8: private int intCounter = 0; 9: 10: public Exercise1() { 11: MenuItem miQuery = mnuMain.MenuItems.Add("Query"); 12: MenuItem miNew = miQuery.MenuItems.Add("New"); 13: miNew.Click += new EventHandler(this.HandleMenu); 14: 15: MenuItem miWindow = mnuMain.MenuItems.Add("&Window"); 16: miWindow.MenuItems.Add("Cascade", new  EventHandler(this.ArrangeWindows)); 17: miWindow.MenuItems.Add("Tile Horizontal", new  EventHandler(this.ArrangeWindows));

762

Antworten auf die Quizfragen und bungen

18: miWindow.MenuItems.Add("Tile Vertical", new  EventHandler(this.ArrangeWindows)); 19: miWindow.MdiList = true; 20: 21: QueryDialog dlgQuery = new QueryDialog(); 22: this.AddOwnedForm(dlgQuery); 23: dlgQuery.TopMost = true; 24: dlgQuery.Show(); 25: 26: this.Size = new Size(800,600); 27: this.Text = "Exercise 1"; 28: this.Menu = mnuMain; 29: this.IsMdiContainer = true; 30: } 31: 32: private void ArrangeWindows(Object Sender, EventArgs e) { 33: MenuItem miTemp = (MenuItem)Sender; 34: 35: switch (miTemp.Text) { 36: case "Cascade": 37: this.LayoutMdi(MdiLayout.Cascade); 38: break; 39: case "Tile Horizontal": 40: this.LayoutMdi(MdiLayout.TileHorizontal); 41: break; 42: case "Tile Vertical": 43: this.LayoutMdi(MdiLayout.TileVertical); 44: break; 45: } 46: } 47: 48: private void HandleMenu(Object Sender, EventArgs e) { 49: intCounter++; 50: 51: DataDocument doc = new DataDocument ("Query Results " +  intCounter.ToString()); 52: doc.MdiParent = this; 53: doc.Show(); 54: } 55: 56: public static void Main() { 57: Application.Run(new Exercise1()); 58: } 59: } 60: }

763

Anhang A

QueryDialog.cs:
1: using System; 2: using System.Windows.Forms; 3: using System.Drawing; 4: 5: namespace TYWinforms.Day10 { 6: public class QueryDialog : Form { 7: private TextBox tbCommand = new TextBox(); 8: private Button btExecute = new Button(); 9: 10: public QueryDialog() { 11: tbCommand.Location = new Point(10,10); 12: tbCommand.Multiline = true; 13: tbCommand.Height = 50; 14: tbCommand.Width = 250; 15: 16: btExecute.Location = new Point(10,75); 17: btExecute.Text = "Execute!"; 18: btExecute.Click += new EventHandler(this.Execute); 19: 20: this.Size = new Size(300,150); 21: this.Text = "Query Executor"; 22: this.Controls.Add(tbCommand); 23: this.Controls.Add(btExecute); 24: } 25: 26: private void Execute(Object Sender, EventArgs e) { 27: if (tbCommand.Text != "") { 28: if (this.Owner.ActiveMdiChild != null) { 29: 30:  ((DataDocument)this.Owner.ActiveMdiChild).Execute(tbCommand.Text); 31: } 32: } 33: } 34: } 35: }

DataDocument.cs:
1: 2: 3: 4: 5: 6: 7: using using using using using System; System.Windows.Forms; System.Drawing; System.Data; System.Data.SqlClient;

namespace TYWinforms.Day10 {

764

Antworten auf die Quizfragen und bungen

8: public class DataDocument : Form { 9: private DataGrid dgData = new DataGrid(); 10: private DataSet objDS = new DataSet(); 11: private String strConn; 12: private String strQuery; 13: 14: public DataDocument(string strName) { 15: strConn = "Initial Catalog=TYWinforms;DataSource=localhost;  User ID=sa"; 16: 17: dgData.Dock = DockStyle.Fill; 18: dgData.Enabled = false; 19: dgData.CurrentCellChanged += new EventHandler(this.UpdateData); 20: 21: this.WindowState = FormWindowState.Maximized; 22: this.Text = strName; 23: this.Controls.Add(dgData); 24: } 25: 26: public void Execute(string strQuery) { 27: this.strQuery = strQuery; 28: 29: SqlConnection objConn = new SqlConnection(strConn); 30: 31: SqlDataAdapter objCmd = new SqlDataAdapter(strQuery, objConn); 32: objDS.Clear(); 33: int intRows = objCmd.Fill(objDS, "Query"); 34: 35: if (intRows > 0) { 36: dgData.ResetBindings(); 37: dgData.DataSource = objDS; 38: dgData.DataMember = "Query"; 39: dgData.Enabled = true; 40: } else { 41: dgData.Enabled = false; 42: } 43: } 44: 45: public void UpdateData(Object Sender, EventArgs e) { 46: SqlConnection objConn = new SqlConnection(strConn); 47: SqlDataAdapter objCmd = new SqlDataAdapter(strQuery, objConn); 48: SqlCommandBuilder objBuilder = new SqlCommandBuilder(objCmd); 49: 50: objCmd.Update(objDS, "Query"); 51: } 52: } 53: }

765

Anhang A

Tag 11
Quiz
1. Worin besteht der Unterschied zwischen den Read- und Peek-Methoden? Sowohl die Read- als auch die Peek-Methoden liefern das nchste Zeichen in einem Strom, doch Peek bewegt dabei nicht den Zeiger im Strom. Daher liefern nachfolgende Peek-Aufrufe stets das gleiche Zeichen, wohingegen Read-Aufrufe den Zeiger bewegen und nachfolgende Zeichen liefern. 2. Was ist eine rekursive Funktion und wann erweist sie sich als hilfreich? Eine rekursive Funktion ruft sich selbst immer wieder neu auf. Solch eine Funktion setzt man ein, wenn man nicht im Voraus wei, wie oft etwas ausgefhrt werden muss, beispielsweise in einer Verzeichnisstruktur. 3. Worin besteht der Unterschied zwischen synchroner und asynchroner Arbeitsweise? Synchrone Arbeitsweise erfordert, dass alles andere in bestimmter Reihenfolge und synchron ausgefhrt wird, wohingegen bei asynchroner Arbeitsweise die Ausfhrung aufgeteilt werden kann, um so gleichzeitig ausgefhrt zu werden. Das erspart Zeit. 4. Zu welchem Namensraum gehrt die Druckfunktionalitt?
System.Drawing.Printing.

5. Welche sind die vier Ereignisse des FileSystemWatcher-Objekts?


Created, Changed, Renamed und Deleted.

6. Welche sind die Werte der FileMode-Aufzhlung ?


Append, Create, CreateNew, Open, OpenOrCreate und Truncate.

bung
Modifizieren Sie Listing 11.1 so, dass die TreeView-Knoten jedes Mal dynamisch erstellt werden, wenn ein Benutzer einen Knoten expandiert, anstatt alle whrend der Initialisierungsphase der Anwendung erstellen zu lassen. Fgen Sie auerdem eine benutzerdefinierte Druckfunktion hinzu, um die sichtbaren Inhalte des TreeView-Steuerelements drucken zu knnen. (Tipp: Sie mssen die Inhalte der Strukturansicht erst in einer passenden Variablen sammeln, um sie drucken zu knnen.)
1: 2: 3: using System; using System.Windows.Forms; using System.Drawing;

766

Antworten auf die Quizfragen und bungen

4: using System.IO; 5: using System.Drawing.Printing; 6: 7: namespace TYWinforms.Day11 { 8: public class Exercise1 : Form { 9: private TreeView tvFiles = new TreeView(); 10: private Label lblInfo = new Label(); 11: private MainMenu mnuMain = new MainMenu(); 12: private String strList = ""; 13: private StringReader objPrintReader; 14: 15: public Exercise1() { 16: tvFiles.Dock = DockStyle.Left; 17: tvFiles.Width = 200; 18: tvFiles.AfterSelect += new TreeViewEventHandler(this.DisplayInfo); 19: tvFiles.BeforeExpand += new  TreeViewCancelEventHandler(this.ExpandThread); 20: 21: lblInfo.Size = new Size(150,150); 22: lblInfo.Location = new Point(210,10); 23: lblInfo.Text = "Select a file or\ndirectory"; 24: 25: PopulateList("c:\\", tvFiles.Nodes.Add("c:\\")); 26: 27: MenuItem miFile = new MenuItem("File"); 28: MenuItem miPrint = new MenuItem("Print"); 29: miPrint.Click += new EventHandler (this.PrintClicked); 30: 31: mnuMain.MenuItems.Add(miFile); 32: miFile.MenuItems.Add(miPrint); 33: 34: this.Menu = mnuMain; 35: this.Text = "Exercise 1"; 36: this.Size = new Size(800,600); 37: this.Controls.Add(tvFiles); 38: this.Controls.Add(lblInfo); 39: } 40: 41: private void PopulateList(String strPath, TreeNode currNode) { 42: DirectoryInfo dir = new DirectoryInfo(strPath); 43: TreeNode nodeSubDir; 44: 45: foreach (DirectoryInfo d in dir.GetDirectories()) { 46: nodeSubDir = currNode.Nodes.Add(d.Name); 47: nodeSubDir.Nodes.Add(""); 48: }

767

Anhang A

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: 91: 92: 93: 94:

foreach (FileInfo f in dir.GetFiles("*.*")) { currNode.Nodes.Add(f.Name); } } private void ExpandThread(Object Sender, TreeViewCancelEventArgs e) { if (e.Node.Nodes[0].Text == "") { TreeNode tempNode = e.Node; String strFullName = e.Node.Text; while (tempNode.Parent != null) { strFullName = tempNode.Parent.Text + "\\" + strFullName; tempNode = tempNode.Parent; } e.Node.Nodes[0].Remove(); PopulateList(strFullName, e.Node); } } private void DisplayInfo(Object Sender, TreeViewEventArgs e) { TreeNode tempNode = e.Node; String strFullName = tempNode.Text; while (tempNode.Parent != null) { strFullName = tempNode.Parent.Text + "\\" + strFullName; tempNode = tempNode.Parent; } if (File.Exists(strFullName)) { FileInfo obj = new FileInfo(strFullName); lblInfo.Text = "Name: " + obj.Name; lblInfo.Text += "\nSize: " + obj.Length; lblInfo.Text += "\nAccessed: " + obj.LastAccessTime.ToString(); } else { DirectoryInfo obj = new DirectoryInfo (strFullName); lblInfo.Text = "Name: " + obj.Name; lblInfo.Text += "\nAttributes: " + obj.Attributes.ToString(); lblInfo.Text += "\nAccessed: " + obj.LastAccessTime.ToString(); } } private void PrintClicked(Object Sender, EventArgs e) { //Assemble string of nodes strList = "";

768

Antworten auf die Quizfragen und bungen

95: TreeNode nodeTemp = tvFiles.TopNode; 96: 97: for (int i = 0; i < tvFiles.GetNodeCount(true); i++) { 98: /*pad the string with spaces for easier 99: reading */ 100: strList += "".PadRight(GetLevel(nodeTemp)*2) + nodeTemp.Text +  "\n"; 101: 102: nodeTemp = nodeTemp.NextVisibleNode; 103: if (nodeTemp == null) break; 104: 105: nodeTemp.EnsureVisible(); 106: } 107: 108: PrintDocument pd = new PrintDocument(); 109: pd.PrintPage += new PrintPageEventHandler (this.pd_PrintPage); 110: objPrintReader = new StringReader(strList); 111: pd.Print(); 112: } 113: 114: /* Returns an integer indicating how many levels deep 115: the specified node is in the treeview */ 116: private int GetLevel(TreeNode objNode) { 117: int intLevel = 0; 118: TreeNode nodeTemp = objNode; 119: 120: while (nodeTemp.Parent != null) { 121: intLevel++; 122: nodeTemp = nodeTemp.Parent; 123: } 124: 125: return intLevel; 126: } 127: 128: private void pd_PrintPage(Object Sender, PrintPageEventArgs e) { 129: Font fntPrint = this.Font; 130: int count = 0; 131: float yPos = 0; 132: float lpp = e.MarginBounds.Height /  fntPrint.GetHeight(e.Graphics); 133: float fltTopMargin = e.MarginBounds.Top; 134: float fltLeftMargin = e.MarginBounds.Left; 135: String strLine = null; 136: 137: while (count < lpp && ((strLine = objPrintReader. ReadLine()) !=  null)) {

769

Anhang A

138: yPos = fltTopMargin + (count * fntPrint.GetHeight(e.Graphics)); 139: 140: e.Graphics.DrawString(strLine, fntPrint, Brushes.Black,  fltLeftMargin, yPos, new StringFormat()); 141: 142: count++; 143: } 144: 145: if (strLine != null) { 146: e.HasMorePages = true; 147: } else { 148: e.HasMorePages = false; 149: } 150: } 151: 152: public static void Main() { 153: Application.Run(new Exercise1()); 154: } 155: } 156: }

Tag 12
Quiz
1. Wahr oder falsch? Um dem Server POST-Informationen zu schicken, verwendet man das WebResponse-Objekt. Falsch. Man benutzt eine WebRequest-Methode und ruft GetRequestStream auf, um
POST-Daten zu senden.

2. Nennen Sie die vier Bestandteile eines URI. Das Protokoll, der Servername, der Verzeichnispfad und eine optionale Abfragezeichenfolge. 3. Welchen Typs muss der Inhalt sein, um Formularinformationen posten zu knnen?
application/x-www-form-urlencoded

4. Was ist das HttpWebRequest-Objekt? Das HttpWebRequest-Objekt, ein Helfer des WebRequest-Objekts, gestattet feiner abgestimmte Kontrolle ber eine HTTP-Anforderung. Man kann sein WebRequest-Objekt in ein HttpWebRequest umwandeln, um benutzerdefinierte HTTP-Header und andere HTTP-Einstellungen festzulegen.

770

Antworten auf die Quizfragen und bungen

5. Welche sind die drei Typen der Windows-Authentifizierung? Basis-, Digest- und integrierte Windows-Authentifizierung.

bung
Erstellen Sie eine Anwendung, die eine Webseite (Ihrer Wahl) untersucht, deren Bilder parst (in HTML beginnen Grafiken mit dem Tag <img src=") und alle Bilder eines nach dem anderen in einem PictureBox-Steuerelement anzeigt. Verwenden Sie eine Schaltflche, um durch die Grafiken zu blttern. (Tipp: Verwenden Sie die Methode Image.FromStream, um eine Grafik aus einem Stream zu holen.)
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: using using using using using System; System.Windows.Forms; System.Drawing; System.Net; System.IO;

namespace TYWinforms.Day12 { public class Exercise1 : Form { private Uri URL; private PictureBox pbImages = new PictureBox(); private Button btNext = new Button(); private Image[] arrImages; private int intCurrentImage; private String strHTML; public Exercise1() { btNext.Text = "Next"; btNext.Dock = DockStyle.Bottom; btNext.Click += new EventHandler(this.NextImage); pbImages.Dock = DockStyle.Fill; URL = new Uri("http://www.clpayne.com"); CallServer(); int intCounter = 0; int intIndex = strHTML.IndexOf("<img src=\""); while (intIndex != -1) { intCounter++; intIndex = strHTML.IndexOf("<img src=\"", intIndex+1); if (intIndex == -1) break; }

771

Anhang A

35: 36: arrImages = new Image[intCounter]; 37: 38: if (strHTML != "") { 39: intCounter = 0; 40: intIndex = strHTML.IndexOf("<img src=\""); 41: string strTemp; 42: 43: while (intIndex != -1) { 44: strTemp = strHTML.Substring(intIndex+10,  strHTML.IndexOf("\"", intIndex + 11) - intIndex-10); 45: 46: arrImages[intCounter] = GetImage(strTemp); 47: 48: intIndex = strHTML.IndexOf("<img src=\"", intIndex+1); 49: intCounter++; 50: } 51: } 52: 53: pbImages.Image = arrImages[0]; 54: intCurrentImage = 0; 55: 56: this.Text = "Exercise 1"; 57: this.Controls.Add(pbImages); 58: this.Controls.Add(btNext); 59: } 60: 61: private void NextImage(Object Sender, EventArgs e) { 62: if (intCurrentImage == arrImages.Length - 1) { 63: intCurrentImage = 0; 64: } else { 65: intCurrentImage++; 66: } 67: 68: pbImages.Image = arrImages[intCurrentImage]; 69: } 70: 71: private Image GetImage(String strImageUrl) { 72: Image imgTemp; 73: try { 74: WebRequest objRequest = WebRequest.Create ("http://" +  URL.Host + strImageUrl); 75: WebResponse objResponse = objRequest.GetResponse(); 76: 77: imgTemp = Image.FromStream(objResponse.GetResponseStream()); 78:

772

Antworten auf die Quizfragen und bungen

79: objResponse.Close(); 80: 81: return imgTemp; 82: } Catch (Exception e) { 83: return null; 84: } 85: } 86: 87: private void CallServer() { 88: WebRequest objRequest = WebRequest.Create(URL); 89: WebResponse objResponse = objRequest.GetResponse(); 90: 91: StreamReader objReader = new  StreamReader(objResponse.GetResponseStream()); 92: 93: strHTML = objReader.ReadToEnd(); 94: 95: objReader.Close(); 96: objResponse.Close(); 97: } 98: 99: public static void Main() { 100: Application.Run(new Exercise1()); 101: } 102: } 103: }

Tag 13
Quiz
1. Wie heit das Hauptobjekt in GDI+? Das ist das Graphics-Objekt. 2. Welche sind die zwei Mglichkeiten, um das Paint-Ereignis auszunutzen? Nennen Sie fr jede ein einfaches Beispiel. Um dem Paint-Ereignis einen Delegaten zuzuweisen:
this.Paint = new PaintEventHandler(this.Method);

oder um die OnPaint-Methode zu berschreiben:


protected override void OnPaint(PaintEventArgs e) { ... }

773

Anhang A

3. Nennen Sie fnf Eigenschaften des Pen-Objekts. Die 17 Eigenschaften des Pen-Objekts lauten: Alignment, Brush, Color, CompoundArray, CustomEndCap, CustomStartCap, DashCap, DashOffSet, DashPattern, DashStyle, EndCap, LineJoin, MiterLimit, PenType, StartCap, Transform und Width. 4. Wahr oder falsch? Um eine Zeichenfolge zu zeichnen, verwendet man einen Pen. Falsch. Man verwendet ein Brush-Objekt. 5. Erlutern Sie den Begriff der Matrix. Eine Matrix ist ein 3 x 3-Raster aus Daten, das hufig zur Transformation von Formen und Farben eingesetzt wird. 6. Welche sind die fnf Brush-Typen?
SolidBrush, HatchBrush, LinearGradientBrush, PathGradientBrush und TextureBrush.

7. Welche Methode muss man zuerst aufrufen, bevor man die Add-Methoden nutzen kann, um einem GraphicsPath-Objekt Figuren hinzuzufgen?
StartFigure.

8. Handelt es sich bei Treppchen um das Ergebnis von Aliasing oder von Antialiasing? Aliasing. Antialiasing versucht, die Treppchen durch Hinzufgen zustzlicher Bildpunkte zu gltten.

bung
Erstellen Sie eine Zeichenapplikation, in der der Benutzer mehrere Zeichenfunktionen nutzen kann, darunter das Zeichnen mit einem Bleistift-artigen Mauszeiger, das Fllen von Rechtecken und das Radieren. Sorgen Sie fr die Dauerhaftigkeit der Zeichnungen. Setzen Sie das Standarddialogfeld FARBE ein, damit der Benutzer die Farbe des Stiftes oder des gefllten Rechtecks whlen kann. (Tipp: Verwenden Sie die Methode Graphics.FromImage.)
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: using using using using using System; System.Windows.Forms; System.Drawing; System.Drawing.Drawing2D; System.Drawing.Imaging;

namespace TYWinforms.Day13 { public class Exercise : Form { private ToolBar tlbStandard = new ToolBar(); private string strFunction; private Point ptStartClick; private Point ptCurrentPos;

774

Antworten auf die Quizfragen und bungen

13: private bool blnDrag = false; 14: private bool blnFill = false; 15: private SolidBrush objBrush; 16: private Pen objPen = new Pen(Color.Black); 17: private Rectangle rectSelect; 18: private Color currColor = Color.Black; 19: private Bitmap imgWorking; 20: private Graphics objG; 21: 22: public Exercise() { 23: imgWorking = new Bitmap(800,600,PixelFormat.Format32bppArgb); 24: objG = Graphics.FromImage(imgWorking); 25: objG.FillRectangle(new SolidBrush(Color.White), new  Rectangle(0,0,800,600)); 26: 27: ToolBarButton tbbPen = new ToolBarButton(); 28: tbbPen.Text = "Pencil"; 29: 30: ToolBarButton tbbRect = new ToolBarButton(); 31: tbbRect.Text = "Rectangle"; 32: 33: ToolBarButton tbbErase = new ToolBarButton(); 34: tbbErase.Text = "Erase"; 35: 36: ToolBarButton tbbColor = new ToolBarButton(); 37: tbbColor.Text = "Color..."; 38: 39: tlbStandard.Buttons.Add(tbbPen); 40: tlbStandard.Buttons.Add(tbbRect); 41: tlbStandard.Buttons.Add(tbbErase); 42: tlbStandard.Buttons.Add(tbbColor); 43: tlbStandard.Appearance = ToolBarAppearance.Flat; 44: 45: tlbStandard.ButtonClick += new  ToolBarButtonClickEventHandler(this.MyToolBarHandler); 46: 47: this.BackgroundImage = imgWorking; 48: this.Text = "Exercise"; 49: this.Size = new Size(800,600); 50: this.Controls.Add(tlbStandard); 51: } 52: 53: private void MyToolBarHandler(Object Sender,  ToolBarButtonClickEventArgs e) { 54: if (e.Button.Text == "Color...") { 55: ColorDialog dlgColor = new ColorDialog();

775

Anhang A

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: 99: 100: 101:

if (dlgColor.ShowDialog() == DialogResult.OK) { currColor = dlgColor.Color; } } else { strFunction = e.Button.Text; tlbStandard.Buttons[0].Pushed = false; tlbStandard.Buttons[1].Pushed = false; tlbStandard.Buttons[2].Pushed = false; e.Button.Pushed = true; } } protected override void OnMouseDown(MouseEventArgs e) { blnDrag = true; ptStartClick = new Point(e.X, e.Y); ptCurrentPos = ptStartClick; } protected override void OnMouseUp(MouseEventArgs e) { blnDrag = false; blnFill = true; DrawIt(objG); } protected override void OnMouseMove(MouseEventArgs e) { ptCurrentPos = new Point(e.X, e.Y); if (blnDrag | blnFill) { DrawIt(objG); } } private void DrawIt(Graphics objGraphics) { switch(strFunction) { case "Pencil": objPen.Color = currColor; objPen.Width = 2; if (blnDrag) { objGraphics.DrawLine(objPen, ptStartClick, ptCurrentPos); ptStartClick = ptCurrentPos; this.Invalidate();

776

Antworten auf die Quizfragen und bungen

102: } 103: break; 104: case "Rectangle": 105: if (blnDrag) { 106: if (ptCurrentPos.X < ptStartClick.X & ptCurrentPos.Y <  ptStartClick.Y) { 107: rectSelect = new Rectangle(ptCurrentPos.X,  ptCurrentPos.Y, ptStartClick.X ptCurrentPos.X,  ptStartClick.Y - ptCurrentPos.Y); 108: } else if (ptCurrentPos.X < ptStartClick.X &  ptCurrentPos.Y > ptStartClick.Y) { 109: rectSelect = new Rectangle(ptCurrentPos.X,  ptStartClick.Y, ptStartClick.X ptCurrentPos.X,  ptCurrentPos.Y - ptStartClick.Y); 110: } else if (ptCurrentPos.X > ptStartClick.X &  ptCurrentPos.Y < ptStartClick.Y) { 111: rectSelect = new Rectangle(ptStartClick.X,  ptCurrentPos.Y, ptCurrentPos.X ptStartClick.X,  ptStartClick.Y - ptCurrentPos.Y); 112: } else { 113: rectSelect = new Rectangle(ptStartClick.X,  ptStartClick.Y, ptCurrentPos.X ptStartClick.X,  ptCurrentPos.Y - ptStartClick.Y); 114: } 115: } 116: 117: if (blnFill) { 118: objBrush = new SolidBrush(currColor); 119: 120: objGraphics.FillRectangle(objBrush, rectSelect); 121: this.Invalidate(); 122: blnFill = false; 123: } 124: break; 125: case "Erase": 126: objPen.Color = Color.White; 127: objPen.Width = 5; 128: 129: if (blnDrag) { 130: objGraphics.DrawLine(objPen, ptStartClick, ptCurrentPos); 131: ptStartClick = ptCurrentPos; 132: this.Invalidate(); 133: } 134: break; 135: } 136: }

777

Anhang A

137: 138: 139: 140: 141: 142:

public static void Main() { Application.Run(new Exercise()); } } }

Tag 14
Quiz
1. Wie heien jeweils die ausfhrbaren Dateien fr die Werkzeuge ActiveX Control Importer, Type Library Importer und Assembly Registration? ActiveX Control Importer: aximp.exe; Type Library Importer: tlbimp.exe; Assembly Registration: regasm.exe. 2. Wahr oder falsch? Der ActiveX Control Importer modifiziert den Quellcode einer .dll, um sie zu .NET konform zu machen. Falsch. Die ursprngliche .dll-Datei wird nicht verndert, sie wird lediglich in einen Wrapper eingehllt. 3. Wahr oder falsch? Es ist besser, vorhandene .NET-Technik zu nutzen als ein hnliches ActiveX-Steuerelement. Wahr. 4. Was versteht man unter Marshalling? Bei Marshalling handelt es sich um den Vorgang, bei dem ein Datentyp zu einem anderem gezwungen wird, wenn man eine Anwendung von einer Technologie (z.B. ActiveX) zu einer anderen (z.B. .NET) migriert. 5. Was wrde sich aus dem folgenden Code ergeben?
MessageBox.Show(Convert.ToChar(88).ToString());

Der Buchstabe X wird angezeigt. 6. Wie machen Sie ein Office-Anwendungsobjekt, das Sie gerade erstellt haben, fr den Benutzer sichtbar? Man setzt die visible-Eigenschaft des Objekts auf true. Zum Beispiel:
Excel.Application objApp = new Excel.Application(); objApp.Visible = true;

778

Antworten auf die Quizfragen und bungen

bung
Setzen Sie das Werkzeug Intermediate Language Diassembler ein, um das ActiveX-Steuerelement Webbrowser (AxSHDocVw.dll) zu untersuchen. Erweitern Sie dann das Listing 14.1, damit sich die Anwendung mehr wie ein richtiger Browser verhlt. Integrieren Sie Schaltflchen namens STARTSEITE, STOP und AKTUALISIEREN und ndern Sie die Text-Eigenschaft des Formulars so ab, dass sie zum Titel der gerade geladenen Webseite passt.
1: using System; 2: using System.Windows.Forms; 3: using System.Drawing; 4: using AxSHDocVw; 5: 6: namespace TYWinforms.Day14 { 7: public class Exercise : Form { 8: private StatusBar sbrMain = new StatusBar(); 9: private StatusBarPanel sbpStatus; 10: private TextBox tbAddress = new TextBox(); 11: private Button btGo = new Button(); 12: private Button btStop = new Button(); 13: private Button btRefresh = new Button(); 14: private Button btHome = new Button(); 15: 16: private AxSHDocVw.AxWebBrowser objBrowser = new  AxSHDocVw.AxWebBrowser(); 17: private object arg1 = 0; 18: private object arg2 = ""; 19: private object arg3 = ""; 20: private object arg4 = ""; 21: 22: public Exercise() { 23: sbpStatus = sbrMain.Panels.Add(""); 24: sbpStatus.AutoSize = StatusBarPanelAutoSize.Spring; 25: sbrMain.ShowPanels = true; 26: 27: btStop.Size = new Size(55,30); 28: btStop.Location = new Point(0,0); 29: btStop.Text = "Stop"; 30: btStop.Click += new EventHandler(this.Stop); 31: btStop.FlatStyle = FlatStyle.Popup; 32: 33: btRefresh.Size = new Size(55,30); 34: btRefresh.Location = new Point(54,0); 35: btRefresh.Text = "Refresh"; 36: btRefresh.Click += new EventHandler(this.Refresh); 37: btRefresh.FlatStyle = FlatStyle.Popup;

779

Anhang A

38: 39: btHome.Size = new Size(55,30); 40: btHome.Location = new Point(108,0); 41: btHome.Text = "Home"; 42: btHome.Click += new EventHandler(this.GoHome); 43: btHome.FlatStyle = FlatStyle.Popup; 44: 45: btGo.Size = new Size(50,20); 46: btGo.Location = new Point(0,33); 47: btGo.Text = "Go!"; 48: btGo.Click += new EventHandler(this.Go); 49: 50: tbAddress.Width = 750; 51: tbAddress.Location = new Point(50,33); 52: tbAddress.Anchor = AnchorStyles.Top | AnchorStyles.Left |  AnchorStyles.Right; 53: 54: objBrowser.Size = new Size(800,498); 55: objBrowser.Location = new Point(0,55); 56: objBrowser.Anchor = AnchorStyles.Top | AnchorStyles.Bottom |  AnchorStyles.Left | AnchorStyles.Right; 57: 58: //TitleChange occurs when the title of the Web browser changes 59: objBrowser.TitleChange += new  DWebBrowserEvents2_TitleChangeEventHandler(this.ChangeTitle); 60: 61: //NavigateComplete2 occurs after a web page has fully downloaded 62: objBrowser.NavigateComplete2 += new  DWebBrowserEvents2_NavigateComplete2EventHandler (this.UpdateAddress); 63: 64: //Occurs when the status bar of the browser updates 65: objBrowser.StatusTextChange += new  DWebBrowserEvents2_StatusTextChangeEventHandler(this.UpdateStatus); 66: 67: //occurs when the control has been created in the form 68: objBrowser.HandleCreated += new EventHandler(this.GoHome); 69: 70: this.Text = ".NET Browser"; 71: this.Size = new Size(800,600); 72: this.Controls.Add(objBrowser); 73: this.Controls.Add(tbAddress); 74: this.Controls.Add(btGo); 75: this.Controls.Add(btStop); 76: this.Controls.Add(btRefresh); 77: this.Controls.Add(btHome); 78: this.Controls.Add(sbrMain);

780

Antworten auf die Quizfragen und bungen

79: } 80: 81: private void Go(Object Sender, EventArgs e) { 82: //Navigate goes to the specified URL 83: objBrowser.Navigate(tbAddress.Text, ref arg1, ref arg2, ref arg3,  ref arg4); 84: } 85: 86: private void Stop(Object Sender, EventArgs e) { 87: //Stops navigation 88: objBrowser.Stop(); 89: } 90: 91: private void Refresh(Object Sender, EventArgs e) { 92: //refreshes the browser window 93: objBrowser.Refresh(); 94: } 95: 96: private void GoHome(Object Sender, EventArgs e) { 97: //goes to the default home page specified by the user 98: objBrowser.GoHome(); 99: } 100: 101: private void ChangeTitle(Object Sender,  DWebBrowserEvents2_TitleChangeEvent e) { 102: this.Text = e.text; 103: } 104: 105: private void UpdateAddress(Object Sender,  DWebBrowserEvents2_NavigateComplete2Event e) { 106: tbAddress.Text = e.uRL.ToString(); 107: } 108: 109: private void UpdateStatus(Object Sender,  DWebBrowserEvents2_StatusTextChangeEvent e) { 110: sbpStatus.Text = e.text; 111: } 112: 113: public static void Main() { 114: Application.Run(new Exercise()); 115: } 116: } 117: }

781

Anhang A

Tag 15
Quiz
1. Wofr steht die Abkrzung SOAP? Fr Simple Object Access Protocol. 2. Wahr oder falsch? Das Werkzeug disco.exe erzeugt eine lokale Kopie der WSDLDienstbeschreibung. Wahr. 3. Was ist eine Proxy-Klasse und warum setzt man eine ein? Eine Proxy-Klasse ist eine Klasse, die die Funktionen kapselt, die ntig sind, um mit einem Webdienst zu sprechen, vom Senden einer HTTP-Nachricht bis zum Erzeugen von SOAP. Man setzt sie ein, um die Entwicklung betrchtlich zu erleichtern. 4. Stimmt etwas nicht mit folgendem Code?
[WebMethod] private string HelloWorld() { return "Hello World!"; }

Es stimmt was nicht: Die HelloWorld-Methode muss als public deklariert werden, damit sie richtig als eine Webmethode funktioniert. 5. Welche Dateierweiterungen werden fr Dateien von .NET-Webdiensten benutzt?
.asmx

6. Warum sehen sich ASP.NET-Dateien und Windows Forms-Dateien so hnlich? Weil sie beide Teil des .NET Frameworks sind, das es jeder Anwendung (im Web oder sonst wo) erlaubt, die gleichen Standardobjekte und die gleiche Syntax zu verwenden, wodurch die Entwicklungsarbeit vereinfacht wird.

bung
Erstellen Sie einen Webdienst, der Werte von einer Maeinheit in eine andere umrechnet (so etwa von Zoll in Zentimeter; um die genauen Werte brauchen Sie sich nicht zu kmmern). Erstellen Sie einen Windows Forms-Client, der es dem Benutzer gestattet, mit dem Webdienst zu interagieren.
ConvertUnits.asmx: 1: 2: <%@ WebService Language="C#" Class="ConvertUnits" %>

782

Antworten auf die Quizfragen und bungen

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: 42: 43: 44: 45: 46: 47:

using System; using System.Web.Services; public class ConvertUnits : WebService { [WebMethod]public double Convert(Double dblValue, String strFrom, switch(strTo) { case "mm": return ConvertTo(dblValue/1, strFrom); break; case "cm": return ConvertTo(dblValue/10, strFrom); break; case "in": return ConvertTo(dblValue/25.4, strFrom); break; case "ft": return ConvertTo(dblValue/304.8, strFrom); break; case "m": return ConvertTo(dblValue/1000, strFrom); break; case "yd": return ConvertTo(dblValue/914.4, strFrom); break; case "mi": return ConvertTo(dblValue/1609344, strFrom); break; case "km": return ConvertTo(dblValue/1000000, strFrom); break; default: return 0.0; break; } } private double ConvertTo(double dblValue, String strFrom) { switch(strFrom) { case "mm": return dblValue * 1; break; case "cm": return dblValue * 10; break;

 String strTo) {

783

Anhang A

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:

case "in": return dblValue break; case "ft": return dblValue break; case "m": return dblValue break; case "yd": return dblValue break; case "mi": return dblValue break; case "km": return dblValue break; default: return 0.0; break; } } }

* 25.4;

* 304.8;

* 1000;

* 914.4;

* 1609344;

* 1000000;

Exercise.cs: 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: using System; using System.Windows.Forms; using System.Drawing; namespace TYWinforms.Day15.Clients { public class Exercise : Form { private ComboBox cboToUnit = new ComboBox(); private ComboBox cboFromUnit = new ComboBox(); private TextBox tbValue = new TextBox(); private Label lblTo = new Label(); private Label lblFrom = new Label(); private Label lblAnswer = new Label(); private Button btConvert = new Button(); public Exercise() { btConvert.Location = new Point(20,90); btConvert.Text = "Convert"; btConvert.Click += new EventHandler(this.ConvertIt);

784

Antworten auf die Quizfragen und bungen

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:

lblAnswer.Location = new Point(125,90); lblAnswer.Size = new Size(200,20); lblTo.Location = new Point(250,35); lblTo.Text = "To: "; lblFrom.Location = new Point(125,35); lblFrom.Text = "From: "; tbValue.Location = new Point(20,50); cboFromUnit.Location = new Point(125,50); cboFromUnit.Items.Add("mm"); cboFromUnit.Items.Add("cm"); cboFromUnit.Items.Add("m"); cboFromUnit.Items.Add("km"); cboFromUnit.Items.Add("in"); cboFromUnit.Items.Add("ft"); cboFromUnit.Items.Add("yd"); cboFromUnit.Items.Add("mi"); cboToUnit.Location = new Point(250,50); cboToUnit.Items.Add("mm"); cboToUnit.Items.Add("cm"); cboToUnit.Items.Add("m"); cboToUnit.Items.Add("km"); cboToUnit.Items.Add("in"); cboToUnit.Items.Add("ft"); cboToUnit.Items.Add("yd"); cboToUnit.Items.Add("mi"); this.Text = "Enter a value and choose the units"; this.Size = new Size(400,200); this.Controls.Add(cboToUnit); this.Controls.Add(cboFromUnit); this.Controls.Add(tbValue); this.Controls.Add(lblTo); this.Controls.Add(lblFrom); this.Controls.Add(lblAnswer); this.Controls.Add(btConvert); } private void ConvertIt(Object Sender, EventArgs e) { string strAnswer; ConvertUnits objConvert = new ConvertUnits(); if (tbValue.Text != "") {

785

Anhang A

64: strAnswer = objConvert.Convert (Convert.ToDouble(tbValue.Text),  cboFromUnit.SelectedItem.ToString(),  cboToUnit.SelectedItem.ToString()).ToString(); 65: 66: lblAnswer.Text = strAnswer + " " +  cboToUnit.SelectedItem.ToString(); 67: } 68: } 69: 70: public static void Main() { 71: Application.Run(new Exercise()); 72: } 73: } 74: }

Tag 16
Quiz
1. Zu welchem Namensraum und zu welcher Assembly gehrt die ServiceBase-Klasse? Zu System.ServiceProcess und System.ServiceProcess.dll. 2. Zu welchen Namensrumen und Assemblies gehren die Klassen ServiceProcessInstaller, ServiceInstaller und Installer? Die ersten beiden gehren zu System.ServiceProcess und System.ServiceProcess.dll, die dritte gehrt zu System.Configuration.Install und System.Configuration.Install.dll. 3. Welches Attribut muss ein Installer besitzen?
<RunInstaller(true)>

4. Wie heien die vier Sicherheitskonten, unter denen ein Dienst laufen kann?
User, LocalSystem, LocalService und NetworkService.

5. Wo werden Ereignisprotokolldateien gespeichert? blicherweise im Ordner c:\winnt\system32\config. 6. Welches Werkzeug benutzt man, um einen Dienst zu installieren? Und welches, um einen zu deinstallieren? Das Befehlszeilen-Dienstprogramm: installutil.exe. Fr die Deinstallation benutzt man das gleiche Hilfsprogramm, doch mit der /u-Option, beispielsweise so:

786

Antworten auf die Quizfragen und bungen

installutil listing16.1.exe

oder
installutil /u listing16.1.exe

7. Welcher Befehl wird verwendet, um einen Dienst von der Befehlszeile aus zu starten? Man benutzt net start "dienstname", um einen Dienst zu starten und net stop "dienstname", um einen Dienst zu beenden. 8. Ist der folgende Code gltig, wenn die Variable objController fr einen gltigen ServiceController initialisiert wird?
dim strHello as String = "RunHelloMethod" objController.ExecuteCommand(strHello)

Nein. Die Methode ExecuteCommand kann als Parameter nur Integer bernehmen. 9. Welche Zeichenfolge wird verwendet, um den lokalen Computer zu spezifizieren?
"."

bung
Erstellen Sie einen Dienst fr das Sichern von Dateien und Verzeichnissen. Dieser Dienst soll tglich zu einem bestimmten Zeitpunkt den Inhalt des Ordners EIGENE DATEIEN in ein anderes, separates Verzeichnis sichern (so etwa um 20:00 Uhr). In diesem Beispiel kommt es nicht auf das Backup-Verzeichnis an, und Sie brauchen sich auch nicht um Dateikomprimierung zu kmmern; kopieren Sie die Dateien einfach. Schreiben Sie entsprechende Informationen in das Anwendungsereignisprotokoll, sobald die Sicherung ausgefhrt wurde.
Exercise.vb: 1: Imports System 2: Imports System.ServiceProcess 3: Imports System.IO 4: Imports System.Timers 5: 6: namespace TYWinforms.Day16 7: 8: Public Class BackUpper : Inherits ServiceBase 9: private tmrTimer As New Timer() 10: private strTo As String = "c:\temp\mybackups" 11: private strFrom As String = "C:\Documents and Settings\Christopher L  Payne.CHRISMAN\My Documents" 12: 13: Public Sub New() 14: tmrTimer.Interval = 10000

787

Anhang A

15: AddHandler tmrTimer.Elapsed, new ElapsedEventHandler(AddressOf  Me.BackUpNow) 16: 17: Me.ServiceName = "TYWinforms Backup Service" 18: End Sub 19: 20: Shared Sub Main() 21: ServiceBase.Run(New BackUpper()) 22: End Sub 23: 24: Protected Overrides Sub OnStart(ByVal args() As String) 25: Dim dir As New DirectoryInfo(strTo) 26: 27: if not dir.Exists then 28: dir.Create() 29: end if 30: 31: tmrTimer.Enabled = true 32: End Sub 33: 34: private sub BackUpNow(Sender As Object, e as ElapsedEventArgs) 35: BackItUp(strFrom) 36: end sub 37: 38: private sub BackItUp(strLocalFrom As String) 39: Dim dirFrom As New DirectoryInfo(strLocalFrom) 40: Dim d As DirectoryInfo 41: Dim dirTo As DirectoryInfo 42: Dim f as FileInfo 43: 44: ' Uncomment the below code to do a recursive backup 45: 'for each d in dirFrom.GetDirectories() 46: ' dirTo = New DirectoryInfo(d.FullName.Replace(strFrom,strTo)) 47: ' if not dirTo.Exists then 48: ' dirTo.Create() 49: ' end if 50: 51: ' BackItUp(d.FullName) 52: 'next 53: 54: for each f in dirFrom.GetFiles("*.*") 55: f.CopyTo(f.FullName.Replace(strFrom,strTo), true) 56: next 57: 58: EventLog.WriteEntry("Contents of " & strLocalFrom & " backed up") 59:

788

Antworten auf die Quizfragen und bungen

60: 61: 62: 63:

end sub End Class end namespace

Exercise_Installer.vb: 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: Imports System.ServiceProcess Imports System.ComponentModel Imports System.Configuration.Install namespace TYWinforms.Day16 <RunInstaller(True)> Public Class MyInstaller : Inherits Installer Private objPInstaller As New ServiceProcessInstaller Private objInstaller As New ServiceInstaller Public Sub New() objPInstaller.Account = ServiceAccount.LocalSystem objPInstaller.Password = Nothing objPInstaller.Username = Nothing objInstaller.ServiceName = "TYWinforms Backup Service" Installers.AddRange(New Installer() {objPInstaller, objInstaller}) End Sub End Class End Namespace

Tag 17
Quiz
1. Nennen Sie vier der Werte aus der DrawItemState-Aufzhlung. Die Werte lauten Checked, ComboBoxEdit, Default, Disabled, Focus, Grayed, HotLight, Inactive, NoAccelerator, NoFocusRect, None und Selected. 2. Worin besteht der Unterschied zwischen den Aufzhlungen DrawMode und TabDrawMode? In der Aufzhlung TabDrawMode fehlt der Wert OwnerDrawVariable. 3. Wann wird das MeasureItem-Ereignis ausgelst? Wenn ein Steuerelement das erste Mal erstellt wird.

789

Anhang A

4. Wahr oder falsch? Registerkarten in einem TabControl-Steuerelement knnen von unterschiedlicher Gre sein. Falsch. Alle Registerkarten mssen die gleiche Gre besitzen. 5. Aus welchem Grund wrde man die GetTabRect-Methode aufrufen? Bei TabControl-Steuerelementen setzt man GetTabRect ein, um ein Rectangle-Objekt zu erhalten, das die Anzeigeflche einer Registerkarte darstellt. Die Bounds-Eigenschaft des DrawItemEventArgs-Objekts liefert die ganze Flche einer Registerkarte, ob sie nun sichtbar ist oder nicht. 6. In welcher Hinsicht unterscheiden sich MenuItem-Steuerelemente von anderen Steuerelementen? Normale Steuerelemente sind von der Klasse System.Windows.Forms.Form abgeleitet, MenuItem-Steuerelemente aber nicht. 7. Was mssen Sie tun, um die Gre der Registerkarten in einem TabControl-Steuerelement zu ndern? Die SizeMode-Eigenschaft auf TabSizeMode.Fixed einstellen.

bung
Schreiben Sie eine Anwendung, mit der der Benutzer das Erscheinungsbild eines Mens anpassen kann. Setzen Sie dazu angepasste Kombinationsfelder ein, damit er Farben fr die Mens whlen kann.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: using System; using System.Windows.Forms; using System.Drawing; namespace TYWinForms.Day17 { public class Exercise : Form { private MainMenu mnuCustom = new MainMenu(); private MenuItem miCustom = new MenuItem(); private MenuItem miOne = new MenuItem(); private MenuItem miTwo = new MenuItem(); private MenuItem miThree = new MenuItem(); private MenuItem miFour = new MenuItem(); private private private private Label Label Label Label lblHighlight = new Label(); lblActiveText = new Label(); lblBackground = new Label(); lblRegText = new Label();

790

Antworten auf die Quizfragen und bungen

19: 20: private ComboBox cboHighlight = new ComboBox(); 21: private ComboBox cboActiveText = new ComboBox(); 22: private ComboBox cboBackground = new ComboBox(); 23: private ComboBox cboRegText = new ComboBox(); 24: 25: private Brush brsHighlight; 26: private Brush brsActiveText; 27: private Brush brsBackground; 28: private Brush brsRegText; 29: 30: private SolidBrush[] arrBrushes = new SolidBrush[10]; 31: 32: public Exercise() { 33: arrBrushes[0] = (SolidBrush)Brushes.White; 34: arrBrushes[1] = (SolidBrush)Brushes.Red; 35: arrBrushes[2] = (SolidBrush)Brushes.Green; 36: arrBrushes[3] = (SolidBrush)Brushes.Blue; 37: arrBrushes[4] = (SolidBrush)Brushes.LightGray; 38: arrBrushes[5] = (SolidBrush)Brushes.Orange; 39: arrBrushes[6] = (SolidBrush)Brushes.Yellow; 40: arrBrushes[7] = (SolidBrush)Brushes.Black; 41: arrBrushes[8] = (SolidBrush)Brushes.BlanchedAlmond; 42: arrBrushes[9] = (SolidBrush)Brushes.DarkSalmon; 43: 44: brsHighlight = Brushes.Blue; 45: brsActiveText = Brushes.White; 46: brsBackground = Brushes.LightGray; 47: brsRegText = Brushes.Black; 48: 49: miCustom = mnuCustom.MenuItems.Add("Custom Menu"); 50: miOne = miCustom.MenuItems.Add("Item 1"); 51: miTwo = miCustom.MenuItems.Add("Item 2"); 52: miThree = miCustom.MenuItems.Add("Item 3"); 53: miFour = miCustom.MenuItems.Add("Item 4"); 54: 55: miCustom.OwnerDraw = true; 56: miCustom.DrawItem += new DrawItemEventHandler(this.DrawTopLevel); 57: miCustom.MeasureItem += new  MeasureItemEventHandler(this.MeasureItem); 58: 59: miOne.OwnerDraw = true; 60: miOne.DrawItem += new DrawItemEventHandler(this.DrawItem); 61: miOne.MeasureItem += new MeasureItemEventHandler(this.MeasureItem); 62: 63: miTwo.OwnerDraw = true;

791

Anhang A

64: miTwo.DrawItem += new DrawItemEventHandler(this.DrawItem); 65: miTwo.MeasureItem += new MeasureItemEventHandler(this.MeasureItem); 66: 67: miThree.OwnerDraw = true; 68: miThree.DrawItem += new DrawItemEventHandler(this.DrawItem); 69: miThree.MeasureItem += new  MeasureItemEventHandler(this.MeasureItem); 70: 71: miFour.OwnerDraw = true; 72: miFour.DrawItem += new DrawItemEventHandler(this.DrawItem); 73: miFour.MeasureItem += new  MeasureItemEventHandler(this.MeasureItem); 74: 75: lblHighlight.Text = "Highlight color: "; 76: lblHighlight.Width = 80; 77: lblHighlight.Location = new Point(0,10); 78: 79: lblActiveText.Text = "Active Text color: "; 80: lblActiveText.Size = new Size(80,30); 81: lblActiveText.Location = new Point(0,50); 82: 83: lblBackground.Text = "Background color: "; 84: lblBackground.Size = new Size(80,30); 85: lblBackground.Location = new Point(0,90); 86: 87: lblRegText.Text = "Regular Text color: "; 88: lblRegText.Size = new Size(80,30); 89: lblRegText.Location = new Point(0,130); 90: 91: cboHighlight.DataSource = arrBrushes.Clone(); 92: cboHighlight.Location = new Point(90,10); 93: cboHighlight.Width = 200; 94: cboHighlight.DisplayMember = "Color"; 95: cboHighlight.DrawMode = DrawMode.OwnerDrawFixed; 96: cboHighlight.DrawItem += new DrawItemEventHandler(this.DrawBox); 97: cboHighlight.SelectedValueChanged += new  EventHandler(this.ChangeHighLightColor); 98: 99: cboActiveText.DataSource = arrBrushes.Clone(); 100: cboActiveText.Location = new Point(90,50); 101: cboActiveText.Width = 200; 102: cboActiveText.DisplayMember = "Color"; 103: cboActiveText.DrawMode = DrawMode.OwnerDrawFixed; 104: cboActiveText.DrawItem += new DrawItemEventHandler(this.DrawBox); 105: cboActiveText.SelectedValueChanged += new  EventHandler(this.ChangeActiveTextColor);

792

Antworten auf die Quizfragen und bungen

106: 107: cboBackground.DataSource = arrBrushes.Clone(); 108: cboBackground.Location = new Point(90,90); 109: cboBackground.Width = 200; 110: cboBackground.DisplayMember = "Color"; 111: cboBackground.DrawMode = DrawMode.OwnerDrawFixed; 112: cboBackground.DrawItem += new DrawItemEventHandler(this.DrawBox); 113: cboBackground.SelectedValueChanged += new  EventHandler(this.ChangeBackgroundColor); 114: 115: cboRegText.DataSource = arrBrushes.Clone(); 116: cboRegText.Location = new Point(90,130); 117: cboRegText.Width = 200; 118: cboRegText.DisplayMember = "Color"; 119: cboRegText.DrawMode = DrawMode.OwnerDrawFixed; 120: cboRegText.DrawItem += new DrawItemEventHandler(this.DrawBox); 121: cboRegText.SelectedValueChanged += new  EventHandler(this.ChangeRegTextColor); 122: 123: this.Text = "Exercise"; 124: this.Menu = mnuCustom; 125: this.Controls.Add(lblHighlight); 126: this.Controls.Add(lblActiveText); 127: this.Controls.Add(lblBackground); 128: this.Controls.Add(lblRegText); 129: this.Controls.Add(cboHighlight); 130: this.Controls.Add(cboActiveText); 131: this.Controls.Add(cboBackground); 132: this.Controls.Add(cboRegText); 133: } 134: 135: private void DrawBox(Object Sender, DrawItemEventArgs e) { 136: e.DrawBackground(); 137: 138: if ((e.State & DrawItemState.Selected) == DrawItemState.Selected) { 139: e.Graphics.FillRectangle(arrBrushes[e.Index], new  Rectangle(e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height)); 140:  e.Graphics.DrawString(((SolidBrush)cboHighlight.Items[e.Index]).Color.Name,  new Font("Arial",10), ((SolidBrush)cboHighlight.Items[9-e.Index]),  new Point(e.Bounds.X, e.Bounds.Y)); 141: } else { 142:  e.Graphics.DrawString(((SolidBrush)cboHighlight.Items[e.Index]).Color.Name,  new Font("Arial",10), ((SolidBrush)cboHighlight.Items[e.Index]),  new Point(e.Bounds.X, e.Bounds.Y));

793

Anhang A

143: } 144: } 145: 146: private void ChangeHighLightColor(Object Sender, EventArgs e) { 147: brsHighlight = (SolidBrush)((ComboBox)Sender).SelectedValue; 148: } 149: 150: private void ChangeActiveTextColor(Object Sender, EventArgs e) { 151: brsActiveText = (SolidBrush)((ComboBox)Sender).SelectedValue; 152: } 153: 154: private void ChangeBackgroundColor(Object Sender, EventArgs e) { 155: brsBackground = (SolidBrush)((ComboBox)Sender).SelectedValue; 156: } 157: 158: private void ChangeRegTextColor(Object Sender, EventArgs e) { 159: brsRegText = (SolidBrush)((ComboBox)Sender).SelectedValue; 160: } 161: 162: private void MeasureItem(Object Sender, MeasureItemEventArgs e) { 163: SizeF stringSize =  e.Graphics.MeasureString(((MenuItem)Sender).Text, this.Font); 164: 165: e.ItemHeight = (int)stringSize.Height + 6; 166: e.ItemWidth = (int)stringSize.Width + 10; 167: } 168: 169: private void DrawTopLevel(Object Sender, DrawItemEventArgs e) { 170: if (((e.State & DrawItemState.HotLight) == DrawItemState.HotLight)  | (e.State & DrawItemState.Selected) == DrawItemState.Selected) { 171: e.Graphics.DrawString(((MenuItem)Sender).Text, new Font("Times  New Roman",12), Brushes.Blue, new Point(e.Bounds.X, e.Bounds.Y)); 172: } else { 173: e.Graphics.DrawString(((MenuItem)Sender).Text, new Font("Times  New Roman",12), Brushes.Black, new Point(e.Bounds.X, e.Bounds.Y)); 174: } 175: } 176: 177: private void DrawItem(Object Sender, DrawItemEventArgs e) { 178: e.Graphics.FillRectangle(brsBackground, new Rectangle(e.Bounds.X,  e.Bounds.Y, e.Bounds.Width, e.Bounds.Height)); 179: 180: if (((e.State & DrawItemState.HotLight) == DrawItemState.HotLight)  | (e.State & DrawItemState.Selected) ==  DrawItemState.Selected) {

794

Antworten auf die Quizfragen und bungen

181: e.Graphics.FillRectangle(brsHighlight, new  Rectangle(e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height)); 182: e.Graphics.DrawString(((MenuItem)Sender).Text, new Font("Times  New Roman",12), brsActiveText, new Point(e.Bounds.X, e.Bounds.Y)); 183: } else { 184: e.Graphics.DrawString(((MenuItem)Sender).Text, new Font("Times  New Roman",12), brsRegText, new Point(e.Bounds.X, e.Bounds.Y)); 185: } 186: } 187: 188: public static void Main() { 189: Application.Run(new Exercise()); 190: } 191: } 192: 193: }

Tag 18
Quiz
1. Worin bestehen die vier Schritte bei der Erstellung eines benutzerdefinierten Steuerelements? 1. Man erstellt eine Klasse, die von der Control-Klasse erbt; 2. Man definiert Eigenschaften, Methoden und Ereignisse fr diese Klasse; 3. Man berschreibt die OnPaintMethode, um das benutzerdefinierte Steuerelement zu zeichnen; 4. Kompilieren und sodann Einsatz der benutzerdefinierten Klasse. 2. Welche Methode mssen Sie aufrufen, um Ihr Steuerelement neu zeichnen zu lassen?
Invalidate().

3. Schreiben Sie in C# ein Beispiel fr eine ffentliche Eigenschaft und verwenden Sie die Syntax der Eigenschaftendeklaration.
public String Text { get { return strText; } set { strText = value; Invalidate(); } }

795

Anhang A

4. Schreiben Sie in VB.NET ein Beispiel fr eine ffentliche Eigenschaft und verwenden Sie die Syntax der Eigenschaftendeklaration.
Public Property Text As String Get Return strText End Get Set strText = value Invalidate() End Set End Property

5. Was ereignet sich zuerst: es wird ein Handle erzeugt oder Ihr Steuerelement wird am Bildschirm angezeigt? Warum? Der Handle wird stets zuerst erzeugt. Ein Steuerelement hat am Bildschirm keinen Clientbereich und kann daher erst gezeichnet werden, wenn ein Handle erzeugt wurde. 6. Wahr oder falsch? Man kann von einem RichTextBox-Steuerelement erben. Wahr. Wie sollte man es sonst erweitern knnen? 7. Nennen Sie drei Nutzeffekte der Erstellung von Benutzersteuerelementen. Kapselung, Wiederverwendbarkeit und Erweiterbarkeit.

bung
Erstellen Sie einen Thermostaten (Wrmeregler) als benutzerdefiniertes Steuerelement, das die aktuelle Temperatur in der Art eines richtigen Quecksilberthermometers anzeigt. Stellen Sie sicher, dass die Temperatur nicht ber das Maximum hinausgeht oder unter Null fllt. Implementieren Sie das Steuerelement in einer Windows Forms-Anwendung und stellen Sie dem Benutzer Schaltflchen bereit, mit denen er die Temperatur steigern oder verringern kann. (Natrlich mssen Sie sich all dies ausdenken, denn Sie knnen ja nicht die tatschliche Temperatur messen.) Das Thermostat-Steuerelement:
1: 2: 3: 4: 5: 6: 7: 8: using System; using System.Drawing; using System.Windows.Forms; namespace TYWinforms.Day18.Controls { public class Thermostat : Control { private int intTemp; private int intMaxTemp;

796

Antworten auf die Quizfragen und bungen

9: 10: public int Temperature { 11: get { return intTemp; } 12: set { 13: intTemp = value; 14: Invalidate(); 15: } 16: } 17: 18: public Thermostat() { 19: intTemp = 10; 20: intMaxTemp = 120; 21: 22: this.Size = new Size(50,150); 23: } 24: 25: protected override void OnPaint(PaintEventArgs e) { 26: e.Graphics.DrawEllipse(Pens.Black, new Rectangle(10,120,30,30)); 27: e.Graphics.DrawRectangle(Pens.Black, new Rectangle(15,10,20,120)); 28: 29: if (intTemp <= intMaxTemp & intTemp >= 0) { 30: e.Graphics.FillEllipse(Brushes.Red, new  Rectangle(10,120,30,30)); 31: e.Graphics.FillRectangle(Brushes.Red, new Rectangle(15,120  intTemp + 10,20,intTemp)); 32: e.Graphics.DrawString(intTemp.ToString() + "", new  Font("Arial",15), Brushes.White, new Point(9,126)); 33: } else if (intTemp < 0) { 34: e.Graphics.FillEllipse(Brushes.Blue, new  Rectangle(10,120,30,30)); 35: e.Graphics.DrawString("BROKE!", new Font("Arial",10),  Brushes.White, new Point(0,126)); 36: } else { 37: e.Graphics.FillEllipse(Brushes.Red, new  Rectangle(10,120,30,30)); 38: e.Graphics.FillRectangle(Brushes.Red, new  Rectangle(15,10,20,120)); 39: e.Graphics.DrawString("BROKE!", new Font("Arial",10),  Brushes.White, new Point(0,126)); 40: } 41: } 42: } 43: }

797

Anhang A

Die umfassende Applikation:


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: using using using using System; System.Windows.Forms; System.Drawing; TYWinforms.Day18.Controls;

namespace TYWinforms.Day18 { public class ExerciseContainer : Form { private Thermostat objStat = new Thermostat(); private Button btUp = new Button(); private Button btDown = new Button(); public ExerciseContainer() { btUp.Text = "Up"; btUp.Location = new Point(30,30); btUp.Click += new EventHandler(this.TurnItUp); btDown.Text = "Down"; btDown.Location = new Point(30,70); btDown.Click += new EventHandler(this.TurnItDown); objStat.Location = new Point(125,10); this.Text = "Thermostat Control"; this.Controls.Add(objStat); this.Controls.Add(btUp); this.Controls.Add(btDown); } private void TurnItUp(Object sender, EventArgs e) { objStat.Temperature += 1; } private void TurnItDown(Object sender, EventArgs e) { objStat.Temperature -= 1; } public static void Main() { Application.Run(new ExerciseContainer()); } } }

798

Antworten auf die Quizfragen und bungen

Tag 19
Quiz
1. Schlieen Sie auf drei Funktionen in Microsoft Word, die multithreaded sein knnten. Das Speichern im Hintergrund, Rechtschreibprfung, Drucken, Seitennummerierung und Makros, um nur ein paar zu nennen. 2. Was versteht man darunter, wenn man etwas als nicht Thread-sicher bezeichnet? Ein nicht threadsicheres Objekt knnen Sie nur von einem einzigen Thread aus sicher benutzen, nicht aus mehreren Threads. Jeder Thread auer dem Erzeugungs-Thread des Objekts muss Aufrufe an den Erzeugungs-Thread marshallen. 3. Aus welchem Grund kann die ThreadPool-Klasse Aufgaben effizienter ausfhren? Das liegt an der Tatsache, dass ein Thread die meiste Zeit unttig ist und dabei auf Ereignisse oder Sperren wartet. 4. Wahr oder falsch? Der Monitor.Enter-Methode muss man die this-Variable bergeben. Falsch. Man kann der Monitor.Enter-Methode jedes beliebige Objekt bergeben. 5. Ist der folgende Code korrekt?
Monitor.Enter(this) for i = 0 to 10 lblInfo.Text += "hallo!" next i

Nein. Er ruft Monitor.Exit nicht auf, und er benutzt this statt Me (der Code ist in VB .NET geschrieben). 6. Welches der folgenden Statements ist korrekt?
1. ThreadPool.QueueUserWorkItem(new ThreadStart(this.Go)); 2. ThreadPool.QueueWorkUserItem(new WaitCallBack(this.Go)); 3. ThreadPool.QueueUserWorkItem(new WaitCallBack(this.Go));

Die dritte Zeile ist korrekt. 7. Was ist ThreadStart?


ThreadStart ist ein Delegat, der ein neues Thread-Objekt auf eine Methode hinweist, die auszufhren ist.

799

Anhang A

8. Wahr oder falsch? Ein Thread beginnt mit seiner Ausfhrung, sobald man einen ThreadStart-Delegaten spezifiziert hat. Falsch. Ein Thread beginnt erst mit seiner Ausfhrung, wenn man seine StartMethode aufruft. 9. Welches ist der standardmige ThreadState- und ThreadPriority-Wert fr einen neu gestarteten Thread? Jeweils ThreadState.Running und ThreadPriority.Normal.

bung
Erstellen Sie ein Multithreading-Dateisuchprogramm. In dieser Anwendung soll ein Benutzer einen Dateinamen als Suchbegriff eingeben und auf die SUCHEN-Schaltflche klicken knnen, so dass ein neuer Thread erzeugt wird. Dann wird rekursiv das Dateisystem nach dem angegebenen Dateinamen durchsucht. Zeigen Sie die Ergebnisse in einem ListBox-Steuerelement an. (Nhere Informationen zum Algorithmus fr das Durchsuchen von Verzeichnissen finden Sie an Tag 11).
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: using using using using using System; System.Drawing; System.Windows.Forms; System.Threading; System.IO;

namespace TYWinforms.Day19 { public class Exercise : Form { private ListBox lbxResults = new ListBox(); private TextBox tbSearch = new TextBox(); private Button btSearch = new Button(); private Label lblStatus = new Label(); private bool blnSearching; private Thread thdSearch; public Exercise() { blnSearching = false; btSearch.Location = new Point(125,10); btSearch.Text = "Search!"; btSearch.Click += new EventHandler(this.BeginSearch); tbSearch.Location = new Point(10,10); lblStatus.Location = new Point(10,50);

800

Antworten auf die Quizfragen und bungen

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: 72:

lbxResults.Location = new Point(250,10); lbxResults.Size = new Size(525,590); this.Size = new Size(800,600); this.Text = "Exercise"; this.Controls.Add(lbxResults); this.Controls.Add(tbSearch); this.Controls.Add(btSearch); this.Controls.Add(lblStatus); } public void BeginSearch(Object Sender, EventArgs e){ if (blnSearching) { return; } lblStatus.Text = "Searching..."; btSearch.Enabled = false; thdSearch = new Thread(new ThreadStart (ThreadSearch)); blnSearching = true; thdSearch.Start(); } private void RecurseDirectory(string strSearchPath){ string strDirectory = Path.GetDirectoryName(strSearchPath); string strSearch = Path.GetFileName(strSearchPath); if (strDirectory == null || strSearch == null) { return; } string[] arrFiles; try { arrFiles = Directory.GetFiles(strDirectory, strSearch); } catch (Exception e) { return; } for (int i = 0; i < arrFiles.Length; i++) { lbxResults.Items.Add(arrFiles[i]); } string[] arrDirectories = Directory.GetDirectories(strDirectory); foreach(string d in arrDirectories) { RecurseDirectory(Path.Combine(d, strSearch)); } }

801

Anhang A

73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90:

private void ThreadSearch() { string strSearch = tbSearch.Text; if (Path.GetPathRoot(strSearch) == "") { strSearch = "c:\\" + strSearch; } RecurseDirectory(strSearch); btSearch.Enabled = true; blnSearching = false; lblStatus.Text = "Done"; } private static void Main() { Application.Run(new Exercise()); } } }

Tag 20
Quiz
1. Welchen Vorteil bieten .config- gegenber .ini-Dateien?
.config-Dateien sind in XML verfasst, einem Industriestandard, so dass sie universell

akzeptiert werden. 2. Was macht das Werkzeug al.exe?


al.exe ist das Werkzeug Assembly Linker, mit dem man Dateien zu Assemblies kompi-

lieren kann, was besonders bei Ressourcendateien ntzlich ist. 3. Welche zwei Parameter erwartet das Hilfsprogramm resgen? Das resgen-Werkzeug erwartet den Namen der zu konvertierenden Eingabedatei (reine Textdatei) und den Namen der zu erstellenden Ausgabedatei(Ressourcendatei mit der Erweiterung .resources). 4. Wahr oder falsch? Eine Konfigurationsdatei fr eine MyApp.exe genannte Anwendung sollte MyApp.config genannt werden. Falsch. Der richtige Name der Konfigurationsdatei lautet MyApp.exe.config.

802

Antworten auf die Quizfragen und bungen

5. Wahr oder falsch? Wenn man Ressourcendateien bersetzt, muss man auch die Eigenschaftsnamen wie auch deren Werte bersetzen. Falsch. Eigenschaftsnamen sollten nicht bersetzt werden. Wre das der Fall, knnte die betreffende Anwendung nicht richtig darauf zugreifen. 6. Welches ist das Basiselement fr alle .config-Dateien?
<configuration> ist das Basiselement fr alle .config-Dateien.

7. Zu welchem Zweck wird das Konfigurationselement <system.windows.forms> verwendet? Das <system.windows.forms>-Element bietet eine Mglichkeit, um JIT-Debugging zu aktivieren. 8. Was versteht man unter Camel-Schreibweise und wo ist sie erforderlich? Camel-Schreibweise (alternierende Gro-/Kleinschreibung) wird bei .config-XMLDateien eingesetzt. Camel-Schreibweise bedeutet, dass das erste Wort in einer Mehrwortverbindung klein geschrieben wird, wohingegen die Anfangsbuchstaben aller folgenden Wrter gro geschrieben werden. Einzelwrter schreibt man klein. Ein Beispiel:
iAmCamelCased yippee goHome

9. Schreiben Sie eine einfache .config-Datei, die ber einen <appSettings>-Abschnitt verfgt. Dieser Abschnitt sollte ein Element namens Color mit dem Wert Black enthalten.
<configuration> <appSettings> <add key="Color" value="Black"/> </appSettings> </configuration>

bung
1. Nehmen Sie drei weitere Lndereinstellungen in das Beispiel aus Listing 20.6 auf: Franzsisch (aus Frankreich), Dnisch und Griechisch. Kmmern Sie sich nicht um das bersetzen der eigentlichen Ausdrcke, sondern ndern Sie sie nur so, dass sie die jeweils aktuelle Kultur widerspiegeln. (In der .NET Framework-Dokumentation zur CultureInfo-Klasse finden Sie Angaben zu den Kulturcodes fr diese Sprachen.) 2. Erstellen Sie eine .config-Datei, die die jeweils zu verwendende Kultur von bung 1 angibt. So mssen Sie nicht Ihren Quellcode verndern und die Anwendung jedes Mal kompilieren, wenn Sie eine neue Kultureinstellung testen wollen. (Tipp: Nutzen Sie den <appSettings>-Abschnitt.)

803

Anhang A

Antwort 1: Sie knnen die Antwort von der Sams Publishing Website herunterladen. In der Lsung werden Sie mehrere Verzeichnisse bemerken: /da, /el und /fr-FR. Jedes Verzeichnis enthlt eine .txt-Datei mit den Lokalisierungsressourcen (z.B. Day20.frFR.txt), eine .resources-Datei, die aus der .txt-Datei mit Hilfe von resgen.exe erzeugt wird; und schlielich eine .dll-Datei, die eine kompilierte Satellitenassembly darstellt. Es gibt nur sehr wenig Code zu schreiben: Erstellen Sie einfach die .txtDateien, kompilieren Sie sie mit den heute besprochenen Befehlen und legen Sie sie in die notwendigen Verzeichnisse ab. Antwort 2: Die Lsung besteht in Ihrer neuen .config-Datei (die ebenfalls auf der SAMS-Website zur Verfgung steht). Ihre Anwendung sollte die ConfigurationSettings.AppSettings-Eigenschaft benutzen, um die Lokalisierungseinstellungen abzurufen.

Tag 21
Quiz
1. Wahr oder falsch? Ein catch-Block, der eine Ausnahme vom Typ IOException angibt, wird auch Ausnahmen vom Typ SystemException abfangen. Falsch. Es verhlt sich genau andersherum. Ausnahmen vom Typ SystemException fangen IOException-Typen ab. 2. Ist der folgende Code richtig?
Process [] objProcess = Process.GetProcessesByName("listing21.2.exe");

Nein. Wenn man Prozesse namentlich angibt, muss man die Erweiterung .exe im Prozessnamen weglassen. 3. Wie erzeugt man eine Programmdatenbankdatei? Wenn Sie Ihre Anwendung kompilieren, setzen Sie den Parameter /debug im Compilerbefehl ein. 4. Was versteht man unter einem Haltepunkt? Ein Haltepunkt ist eine vordefinierte Stelle im Code, an der die Ausfhrung fr Debuggingzwecke angehalten wird. 5. Nennen Sie drei Eigenschaften der Exception-Klasse. Die Eigenschaften von Exception sind HelpLink, InnerException, Message, Source, StackTrace und TargetSite.

804

Antworten auf die Quizfragen und bungen

6. Wie kann man eigene Fehlermeldungen auslsen? Man verwendet das Throw-Schlsselwort und gibt dabei einen bestimmten Typ von Exception-Objekt an.

bung
Schreiben Sie eine Anwendung, die dem Windows Task-Manager gleicht. Sie sollte den Benutzer jeden ablaufenden Prozess ber ein Listenfeld auswhlen lassen. In Bezeichnungsfeldern sollten die Eigenschaften Id, MainModule, PrivateMemorySize und TotalProcessorTime des Prozesses sowie die Zahl der aktiven Threads zu sehen sein. Denken Sie auch daran, try-catch-Blcke einzusetzen!
1: using System; 2: using System.Windows.Forms; 3: using System.Drawing; 4: using System.Diagnostics; 5: 6: namespace TYWinforms.Day21 { 7: public class Exercise : Form { 8: private ListBox lbxProcesses = new ListBox(); 9: 10: private Label lblCaptionId = new Label(); 11: private Label lblCaptionModule = new Label(); 12: private Label lblCaptionProcessor = new Label(); 13: private Label lblCaptionMemory = new Label(); 14: private Label lblCaptionThreadCount = new Label(); 15: 16: private Label lblId = new Label(); 17: private TextBox tbModule = new TextBox(); 18: private Label lblProcessor = new Label(); 19: private Label lblMemory = new Label(); 20: private Label lblThreadCount = new Label(); 21: 22: private System.Windows.Forms.Timer tmrProcess = new  System.Windows.Forms.Timer(); 23: 24: public Exercise() { 25: lbxProcesses.Location = new Point(10,10); 26: lbxProcesses.Width = 280; 27: lbxProcesses.DisplayMember = "ProcessName"; 28: lbxProcesses.SelectedIndexChanged += new  EventHandler(this.StartWatch); 29: 30: foreach (Process tmpProcess in Process.GetProcesses()) { 31: lbxProcesses.Items.Add(tmpProcess);

805

Anhang A

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: 76: 77:

} lblCaptionId.Location = new Point(10,120); lblCaptionId.Text = "PID: "; lblCaptionModule.Location = new Point(10,150); lblCaptionModule.Text = "Modules: "; lblCaptionProcessor.Location = new Point(10,180); lblCaptionProcessor.Text = "Processor Time: "; lblCaptionMemory.Location = new Point(10,210); lblCaptionMemory.Text = "Memory: "; lblCaptionThreadCount.Location = new Point(10,240); lblCaptionThreadCount.Text = "Thread Count: "; lblId.Location = new Point(110,120); lblId.Width = 150; tbModule.Location = new Point(110,150); tbModule.Multiline = true; tbModule.Size = new Size(500,25); tbModule.ReadOnly = true; tbModule.ScrollBars = ScrollBars.Vertical; lblProcessor.Location = new Point(110,180); lblProcessor.Width = 150; lblMemory.Location = new Point(110,210); lblMemory.Width = 150; lblThreadCount.Location = new Point(110,240); lblThreadCount.Width = 150; tmrProcess.Tick += new EventHandler(this.Update); tmrProcess.Interval = 500; this.Text = ".NET Profiler"; this.Width = 640; this.Controls.Add(lblCaptionId); this.Controls.Add(lblCaptionModule); this.Controls.Add(lblCaptionProcessor); this.Controls.Add(lblCaptionMemory); this.Controls.Add(lblCaptionThreadCount); this.Controls.Add(lbxProcesses); this.Controls.Add(lblId); this.Controls.Add(tbModule); this.Controls.Add(lblProcessor); this.Controls.Add(lblMemory); this.Controls.Add(lblThreadCount); } private void StartWatch(Object Sender, EventArgs e){

806

Antworten auf die Quizfragen und bungen

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:

//put things that don't change here try { Process tmpProcess = (Process)lbxProcesses.SelectedItem; tbModule.Text = ""; foreach (ProcessModule tmpModule in tmpProcess.Modules) { tbModule.Text += tmpModule.ModuleName + " "; } lblId.Text = tmpProcess.Id.ToString(); tmrProcess.Start(); } catch (Exception ex) { MessageBox.Show("That process is unwatchable"); } } private void Update(Object Sender, EventArgs e) { try { Process tmpProcess = (Process)lbxProcesses.SelectedItem; tmpProcess.Refresh(); lblMemory.Text = tmpProcess.PrivateMemorySize.ToString(); lblProcessor.Text = tmpProcess.TotalProcessorTime.ToString(); lblThreadCount.Text = tmpProcess.Threads.Count.ToString(); } catch (Exception ex) { //do nothing } } public static void Main() { Application.Run(new Exercise()); } } }

807

Stichwortverzeichnis
Symbols
.asmx (Webdienst) 523 .NET , .NET-Programmiersprache 18 , Assemblies 52 , Begriff 29 , Kompilierung 30 .NET Framework 18 , ASP.NET-Modul 423 , Begriff 29 , Dialogfelder 234 , Erweiterbarkeit 590 , installieren 33 , Kapselung 590 , Klassenbibliothek 36 , Lokalisierung 669 , SDK 33 , Sicherheitsmerkmale 37 , try-catch 683 , Wiederverwendbarkeit 590 /bugreport 65 /debug 66 /main 66 /nologo 66 /optioncompare 66 /optionexplicit 66 /optionstrict 66 /out 66 /reference 66 /target 66 /win32icon 66 /win32resource 66 <appSettings>-Abschnitt 664 <configSections>-Abschnitt 662 <startup>-Abschnitt 665 <system.windows.forms>-Abschnitt _Document 489 _WorkBook 495

A
Abfrage , in Access 338 , parametrisierte 338 Aborted 630 AbortRequested 630 abstrakte Klasse 302 Accept 428 AcceptButton-Eigenschaft 92 AcceptIt 91 AcceptsReturn-Eigenschaft 207 AcceptsTab-Eigenschaft 207 Access 19, 310, 314 , Datenbank anlegen 314 , Datentypen 316 , gespeicherte Prozedur 338 , Verbindungszeichenfolge 326 AccessibilityObject 677 AccessibleDefaultActionDescription 677 AccessibleDescription 677 AccessibleName 677 AccessibleRole 677 Account-Eigenschaft 548 Activated-Ereignis 100 Activate-Methode 93 Active Server Pages (ASP) 95 ActiveMdiChild-Eigenschaft 365 ActiveX 478, 516 , ActiveX Control Importer 481 , AxHost 480 , AxHost-Klasse 591 , AxSHDocVw.dll 482 , Lizenzierung von Komponenten 501 , Missing-Objekt 500 , NetWord 722 , OLE-Automatisierung 479 , Richtlinien fr die Verwendung 500 , SHDocVw.dll 482 , Sicherheit im Einsatz 502 , sortieren 492

666

809

Stichwortverzeichnis

, Steuerelement registrieren 499 , Steuerelemente 480 , Steuerelemente in .NET 499 , Webbrowser 482 , Wrapper 481 ActiveX Data Objects (ADO) 310 AddArc 458 AddBezier 458 AddBeziers 458 AddClosedCurve 458 AddCurve 458 AddEllipse 458 AddHandler-Methode 73, 99, 118, 161 , Delegat 161 AddLine 458 AddLines 458 Add-Methode 71 , Grafikobjekte 457 , MainMenu-Objekt 116 AddPath 458 AddPie 458 AddPolygon 458 AddRectangle 459 AddRectangles 459 Address 428 AddString 459 AddTab-Methode 205 ADO 310 , Typbibliotheken 346 ADO.NET 19, 37, 309 , Begriff 310 , Connection String 326 , DataColumn-Objekt 294 , DataMember-Eigenschaft 329 , DataReader-Objekt 335 , DataRelation 324 , DataRow-Objekt 294 , DataSet 312, 323 , DataSource-Eigenschaft 329 , DataTable-Objekt 294, 323 , DataView-Objekt 341 , Datenverwaltung 312 , Datenzugriffsmodell 311 , Integration von XML 311 , Objektmodell 325 , OLE DB-Provider 325

, OleDbCommand-Objekt 335 , OleDbDataReader-Objekt 335 , Programmierbarkeit 312 , Skalierbarkeit 312 , SqlCommand-Objekt 325, 335 , SqlConnection-Objekt 325 , SqlDataAdapter-Objekt 325 , SqlDataReader-Objekt 335 , SQL-Provider 325 , Steuerelemente 325 , Transaktion 342 , Verbindungszeichenfolge 326 , XML 313 , XmlDataDocument-Objekt 344 , Zusammenspiel mit Datenbanken Adobe Framemaker 350 Adobe Photoshop 352 , ini-Datei 657 nderungsereignisse 106 Aktionen, Drag & Drop 105 Aktualisieren , Form.Refresh 448 , Formular 448 , TreeView-Steuerelement 402 al.exe (Assembly Linker) 672 Aliasing 449 Alignment-Eigenschaft 454 , StatusBar 135 AllowAutoRedirect 429 AllowDrop-Eigenschaft 92 AllowFullOpen 252 AllowWriteStreamBuffering 429 Alt-Eigenschaft 103 AlternatingBackcolor 292 Ampersand (kaufmnnisches UndZeichen) 117 Anchor-Eigenschaft 222 Anforderung, senden 419 Anforderung/Antwort-Modell 416 Ansicht, benutzerdefinierte 341 ANSI-Zeichensatz 252 AntiAlias 450 AntiAliasGridFit 450 Antialiasing 256, 449 , Dokumentvorschau 256 , Schriftart-Dateien 450

311

810

Stichwortverzeichnis

, TextRenderingHint-Eigenschaft 450 , Wiedergabehinweise 450 Anwendung , Aufruf im Browser 430 , Bereitstellung 677 , C# 46 , CD-Sammlung 230 , Datenbank 331 , Datenquelle 290 , Eingabehilfen 676 , Einsprungspunkt 55 , ereignisgesteuert 94 , erstellen 39, 67 , Fenster zeichnen 443 , Formular posten 423 , Funktionalitt offen legen 478 , hosted 478 , kompilieren 40 , kompilieren aus mehreren Dateien 242 , konfigurieren 659 , Leerlauf 96 , Lokalisierung 669 , MDI 350 , Metadaten 31 , multithreaded 622, 632 , NetWord 261 , Profilerstellung 696 , Ressourcen 32 , singlethreaded 628 , Taschenrechner 68 , Visual Basic .NET 46 , webbasierte 311 , Wiederverwendbarkeit 591 Appearance-Eigenschaft 183 , TabControl-Steuerelement 206 Append 390 AppendText 386 application/x-www-form-urlencoded 425 Architektur , CLR 32 , Steuerelemente 592 , Windows Forms-Steuerelemente 38 Archivdatei 678 Array 687 , Datenquelle 284 ASC 321 ASP (Active Server Pages) 95

ASP.NET 416 , Daten posten 425 , Engine 659 , Modul 423 , Page_Load-Methode 423 , Request-Objekt 423 , Seite 423, 522 , Tags 522 , Webdienste 520 Assembly 49 , Assembly Registration-Tool 499 , Begriff 51 , benutzerdefinierte Steuerelemente 609 , Compiler 66 , DLLs 53 , Ressourcendatei 670 , Steuerelemente einbetten 431 , bersicht 52 Assembly Linker 672 Attributes 386 Auf-Ab-Steuerelement 192 Aufzhlung 268 , Begriff 87 , DialogResult 268 Ausfhrung , kontrollieren 98 , steuern 97 Ausnahme 682 , abfangen 683 , Hierarchie 684 , throw 688 , try-catch 683 Auswahlmodus 465 Authentifizierung 327 , Basis 433 , Digest 434 , Integrierte Windows-Identifizierung 434 , Internet 433 , NTLM 434 AutoLog 543 AutoSize 200 AutoSize-Eigenschaft 135 AutoWert 315 Aximp.exe (ActiveX Control Importer) 481 AxSHDocVw.dll 482 AxWebBrowser-Objekt 484

811

Stichwortverzeichnis

BackColor 85, 292, 569 Background 630 BackGroundColor 292 BackgroundImage-Eigenschaft 85 base-Methode 60 BasePriority 700 Basisauthentifizierung 433 Basisklasse, Konstruktor 59 Befehlszeile , Compiler 65 , Compileroptionen 65 , Dienst starten/beenden 552 BeginUpdate-Methode 185 Benutzer, anonymer 548 Benutzer-Agent (user-agent) 428 benutzerdefinierte Steuerelemente 590 , Erzeugen aus vorhandenen 610 , Kompilieren zu Assembly 609 , OnPaint-Methode berschreiben 592 benutzerdefiniertes Ereignis, NetWord 707 Benutzereingaben handhaben 94 Benutzerkonto 548 Benutzername, ExtendedPropertiesObjekt 324 Benutzeroberflche 68, 261 , aufbauen 262 , Datenbindung 283 , Dienste 544 Benutzersteuerelemente 613 , erstellen 615 , Kapselung 615 , UserControl-Klasse 615 Berechtigungen, Dienste 548 Bereich 442 , Fensterform 471 Bereitstellung 656, 677 Besitzer, Steuerelemente 581 besitzergezeichnete Steuerelemente 568 Betriebssystem 622, 657, 698 , Auslagerungsdatei 700 , Betriebssystemkern 701 , DOS 623 , multithreaded 623 , singlethreaded 623 , Systemspeicher 700

, Threadpooling 645 , virtueller Speicher 701 Bezeichnungsfeld 178 , Kontextmen 125 Beziehung , DataRelation-Objekt 324 , Parent-Child-Mens 364 , Tabelle 323 Bzierkurve 457 BigInt 340 Bildlaufleiste 125, 136 , AutoScoll-Eigenschaft 139 , Bildlauf behandeln 141 , Bildlauffeld 146 , Ereignishandler 145 , Ereignisse 143 , HScrollBar-Steuerelement 136 , LargeChange-Eigenschaft 143 , Position 142 , Scroll-Ereignis 141 , ScrollEventHandler-Objekt 141 , SmallChange-Eigenschaft 143 , VScrollBar-Steuerelement 136 Bildlaufleiste  44 Bildschirm , Koordinaten 603 , PrimaryScreen-Eigenschaft 603 , Screen-Klasse 603 Bildschirmanzeige , Hide 92 , Show 92 Binrdaten , lesen 393 , schreiben 393 , Serialisierung 397 Binary 340 BinaryFormatter-Objekt 399 BindingContext-Objekt 286 Binding-Objekt 284 Bit 340 Bitmapgrafik 441 Bitmap-Klasse 450 Boolean 340 BorderStyle 132, 292 Bounds 569 BringToFront-Methode 93

812

Stichwortverzeichnis

Brinkster 537 Browser , ActiveX-Steuerelement 482 , AxWebBrowser-Objekt 484 , Steuerelemente darstellen 431 , Steuerelemente einbetten 430 Brush 442, 454 Brush-Objekt 460 BufferedStream 379 ButtonBase-Klasse  11 ButtonClick-Ereignis 130 Buttons-Auflistung 131 Button-Steuerelement , Click-Ereignis 179 , FlatStyle-Eigenschaft 179 , PerformClick-Methode 179 Button-Steuerelement  11 Byte 340

C
C 27 C# 27 , Gro-/Kleinschreibung 47 , Inherits 49 , Typumwandlung 80 C++ 27 CAD-Anwendung, Profiling 702 Camel-Schreibweise 661 CancelButton-Eigenschaft 92 CancelIt 91 CanHandlePowerEvent 543 CanPauseAndContinue 543 CanShutdown 543 CanStop 543 CaptionBackColor 292 CaptionFont 292 CaptionForeColor 292 CaptionText 292 CaptionVisible 292 Casting 17 Casting-Operator 73, 163 CenterImage 200 Central Processing Unit (CPU) 622 ChangeColor-Methode 289 ChangeLocation-Methode 205 Char 340

CharacterCasing-Eigenschaft 207 CheckAlign-Eigenschaft , CheckBox-Steuerelement 182 , RadioButton-Steuerelement 182 CheckBoxes 220 CheckBox-Steuerelement 180 CheckBox-Steuerelement  11 CheckChanged-Ereignis 183 Checked 122, 571 CheckedListBox-Steuerelement  13, 18 CheckListBox-Steuerelement 221 CInt-Funktion 71 ClearTypeGridFit 450 Click-Ereignis 287 Client 417, 419 Client/Server-Modell 416 Clientbereich 593 ClientCertificates 429 Clipping-Region 605 CloneMenu-Methode 123, 275 Closed-Ereignis 97 Close-Methode 390 , Formular 93 Closing-Ereignis 97, 100 CLR , (Common Language Runtime) 31 , animierte Mauszeiger 86 , Architektur 32 , Benutzersteuerelemente 615 , Delegat 158 , Ereignisse 158 , Marshalling 500 , Metadaten 32, 480 , Ressourcen 32 , verwalteter Code 32 Code , Sperren 640 , verwalteter 32 Color 454 ColorDialog 235, 250 ColorDialog  20 ColumnHeadersVisible 292 COM 479, 516 ComboBox 239 ComboBoxEdit 571 ComboBox-Steuerelement 17, 183

813

Stichwortverzeichnis Control-Klasse  2 Controls 17 Convert.ToInteger-Methode 73 Convert.ToString-Methode 73 CookieContainer 429 CopyTo 387 CorDbg.exe 694 CPU 622, 702 , Mehrprozessorsystem 701 , Prozessorzeit 701 CPU (Central Processing Unit) 622 Create 387, 390 CreateNew 390 CreateSubDirectory 388 CreateText 387 CreationTime 386 Credentials 427 CryptoStream 379 CStr-Funktion 71 CType-Methode 163 CultureInfo-Klasse 675 CurrentCellChanged-Ereignis 301 CurrentThread-Methode 629 Cursors-Objekt 86 CustomEndCap 454 CustomFormat-Eigenschaft 191 CustomStartCap 454

, anpassen 570 , Aussehen steuern 186 , Datenbindung 289 , Dropdown-Liste 187 , DropDownStyle-Eigenschaft 186 , einsetzen 187 , Position 184 , SelectedIndexChanged-Ereignis 187 , SelectedIndex-Eigenschaft 187 , sortieren 187 , variable Elementhhen 575 ComboBox-Steuerelement  13 Common Dialog Box (Standarddialogfeld) 235 Common Language Runtime (CLR) 32, 158 , Begriff 31 CommonDialog  19 Compiler 41, 671, 721 , Assemblies 66 , Begriff 28 , Metadaten 32 , MSIL 32 , Optionen 65 Compilerbefehl 65 Component Object Model (COM) 479 CompoundArray 454 Connection 429 ConnectionGroupName 427 Console.WriteLine-Methode 80 Constraint  72 ConstraintCollection  72 Containersteuerelement , CheckBox-Steuerelement 182 , Form  36 , Panel 140 , RadioButton-Steuerelement 182 ContentAlign-Aufzhlung 179 ContentAlignment-Aufzhlung 182 ContentLength 425 ContentType 427 ContextMenu 122 ContextMenu  25 ContinueDelegate 429 ContinuePending 558 ControlBox 89 Control-Eigenschaft 103 Control-Klasse 113, 178, 222, 592

D
DashCap 454 DashOffset 454 DashPattern 454 DashStyle 454 DataColumn  73 DataColumnCollection  73 DataGridBoolColumn 302 DataGridCell-Objekt 301 DataGridColumnStyle-Objekt 297 DataGrid-Steuerelement 221, 290 , CurrentCellChanged-Ereignis 301 , Daten bearbeitbar machen 330 , Datenblatt 298 , Farbe wechseln 302 , Hit-Test 305 , Interaktion 296 , Stileigenschaften 296 , Treffertest 305

814

Stichwortverzeichnis

, UI-Eigenschaften 291 DataGrid-Steuerelement  28 DataGridTableStyle 297 DataGridTextBoxColumn 302 DataReader-Objekt 335 DataRelation  75 DataRelationCollection  75 DataRelation-Objekt 323 DataRow 294 DataRow  77 DataRowCollection  77 DataSet , Begriff 323 , Fill-Methode 328 DataSet  72, 79 DataSource 287 DataTable  81 DataTableCollection  81 DataTable-Objekt 294, 323 DataView 341 , Sortierreihenfolge 342 DataView  85 DataViewRowState-Aufzhlung 342 Datei , anlegen 390 , Begriff 378 , Erweiterung 523 , File.Exists-Methode 385 , Inhalt lschen 390 , kopieren 380 , lesen 388 , ffnen 390 , Reader 388 , Rekursion 382 , schreiben 388 , berwachen 402 , Writer 388 Dateisystem 378, 380 , Dateiberwachungsdienst 553 , berwachen 402 , berwachungsdienst 545 Dateityp 250 Daten , Anwendungsdaten 283 , bearbeiten 329 , Begriff 282

, Binrdaten 393 , Datenanzeige 286 , Datenquelle 283, 290 , gebundene Daten bearbeiten 287 , mit Formularen verbinden 290 , Quelle 283 , senden an ASP.NET-Seite 424 , verbinden mit Schaltflche 285 , XML-Datei 284 , XML-Daten 284, 310 Datenbank 283 , Access 314 , ADO 310 , anlegen 314 , Anwendung 331 , Authentifizierung 327 , AutoWert 315 , DataTable-Objekt 323 , Datenbankzugriff 327 , Ergebnisse anzeigen 336 , Identittsspalte 315 , Primrschlssel 316 , Profiling 702 , SQL 310 , SQL Server 317 , Systemadministrator 327 , Tabelle 284 Datenbankverbindung 325 , Verbindungszeichenfolge 326 Datenbankzugriff 37 Datenbindung 282 , Begriff 282 , BindingContext-Eigenschaft 290 , ComboBox-Steuerelement 289 , DataSource-Eigenschaft 287 , Datenquelle 284 , einfache 283 , Feld 284 , Formular 290 , IList-Schnittstelle 284 , Kombinationsfeld 287 , komplexe 283, 288 , Schaltflche 285 , TextBox-Steuerelement 333 Datenblatt 298 , Wert der Zelle 301

815

Stichwortverzeichnis

Datenblattansicht 314 Datenmenge (DataSet) 323 Datenmodell 282 Datenschicht 283 Datentyp , .NET 339 , Access 316 , AutoWert 315 , Casting-Operator 73 , SQL Server 318 , SqlDbType 339 , Umwandlung 73 DateTime 192, 340 , formatieren 303 , Zeit, aktuelle 217 DateTimePicker-Steuerelement 189 , Ereignisse 193 , Formatzeichenfolge 191 DateTimePicker-Steuerelement  32 Datum und Uhrzeit, Formatzeichenfolge 303 Datum, Kontrollkstchen 192 DbgClr.exe (Common Language Runtime Debugger) 690 Deactivate-Ereignis 100 Debugger 686 , Befehlsfenster 693 , Lokalfenster 693 , Schaltflchen fr das Bewegen im Code 694 , berwachen-Fenster 694 Debugging 682 , Ablaufschritte 690 , Ausnahme-Framework 684 , CLR-Debugger (DbgClr) 690 , CorDbg.exe 694 , Debugger anhngen 689 , Fehlerbehandlung 666, 685 , Haltepunkt 692 , Informationen ber 659 , JIT-Debugger 666 Befehlszeilenversion 694 , JIT-Debugging 689 , Programm-Datenbankdatei 690 , Sensibler Code 683 , try-catch 683, 689 , Windows-Dienst 563

Decimal 340 Default 571 DefaultItem 122 Delegat 158, 265 , AddHandler-Methode 161 , Begriff 159 , benutzerdefinierter 169, 605 , besitzergezeichnetes Steuerelement 569 , erstellen 162 , hinzufgen 276 , implementieren 161 , Parameter 162 , SelectedIndexChanged-Ereignis 188 , Trennen von Ereignis 161 , Typ 164 delegate 169 DELETE 323 Delete 387 Deployment 17 DESC 321 Deserialisierung 397 , Begriff 397 , Deserialize-Methode 399 Designer, Windows Forms 64 DesktopLocation-Eigenschaft 84, 205 Destruktor 58 , Begriff 58 , nothing 58 , null 59 Dialogfeld 232 , Aufgaben 232 , Benutzereingabe erzwingen 234 , ColorDialog 235, 250, 365 , Datei ffnen 239, 248 , Drucken 255 , eigenes erstellen 238 , Ergebnis 244 , Ersetzen 272 , Farbauswahl 251 , FileOpenDialog 268 , FileSaveDialog 268 , FindDialog 269 , FontDialog 235, 252, 269 , FormBorderStyle-Eigenschaft 233 , Grenziehpunkt 243 , Informationen abrufen 244

816

Stichwortverzeichnis

, Klasse erstellen 238 , Meldungsfeld 232 , MessageBox 235 , modales 234, 242 , Modalitt 233 , NetWord 268 , nichtmodales 234, 243, 260, 361 , OpenFileDialog 235, 509 , PageSettings 253 , PageSetupDialog 235, 253, 268 , PrintDialog 235, 255, 268 , PrintDocument 256 , PrintPreviewDialog 235, 248, 256, 268 , SaveFileDialog 235 , Schriftauswahl 252 , Seite einrichten 255 , Seitenvorschau 257 , Speichern 248 , Standard 235, 248 , Steuerelemente 239 , Suchen 272 , technische Definition 233 Dialogfeldklasse, benutzerdefinierte 243 DialogResult 244 Dienstbeschreibung , untersuchen 528 , wsdl.exe 528 Dienste , Begriff 29 , Benutzeroberflche 544 , Berechtigungen 548 , Dateisystem berwachen 545 , deinstallieren 552 , Druckspooler 540 , Ereignisprotokoll 546, 560 , erzeugen 542 , ExecuteCommand-Methode 559 , Installer 547 , Konto 548 , Meldungsfeld 544 , OnCustomCommand-Methode 559 , Schnittstellen 555 , Service Control Manager (SCM) 542 , ServiceBase-Klasse 542 , ServiceControllerStatus-Aufzhlung 558 , starten/beenden 552

, Status 558 , berwachen 553 , Verwaltung 542 , Webserver 540 , Windows 540 Digestauthentifizierung 434 Directory 386 DirectoryInfo-Objekt 380 , Mitglieder 387 DirectoryName 386 Directory-Objekt 380 DirectX 441 Disabled 571 disco.exe 527 DisplayInfo-Methode 384 Divider-Eigenschaft 132 DLL , Assemblies 53 , Begriff 53 Dock-Eigenschaft 210, 222 , RichTextBox-Steuerelement 210 Document-Klasse 353 Dokument , Antialiasing 256 , drucken 406 , MDI 350 , NetWord 509 , Objekttypen 361 , PrintDocument-Steuerelement  50 , Randeinstellung 253 , Seiteneigenschaften 254 , speichern 510 , untergeordnetes 353, 509 , verknpftes Men 357 , Vorschau 256 Domain Naming System (DNS) 417 DomainUpDown-Steuerelement  35 DOS, Singlethreaded-Betriebssystem 623 Double 340 Drag & Drop 105 DragDropEffects-Aufzhlung 105 DragDrop-Eigenschaft 105 DragEnter-Eigenschaft 105 DragLeave-Eigenschaft 105 DragOver-Eigenschaft 105 DrawBackground 570

817

Stichwortverzeichnis

DrawFocusRectangle 570 DrawImage-Methode 451 DrawItemEventArgs 569 DrawItemState-Aufzhlung 570 DrawMode 568 DrawString-Methode 409, 444 Dreischichtenmodell 283 DropDownButton 133 Dropdown-Liste , DateTimePicker-Steuerelement 189 , Kombinationsfeld 187 Drucken 253, 378, 406 , Assembly 255 , Druckschleife 512 , Druckvorschau 718 , HasMorePages-Eigenschaft 410 , Namensraum 255 , NetWord 511 , objPageSettings-Objekt 511, 708 , PrintDocument-Objekt 406 , Print-Methode 511 , PrintPage-Ereignis 406, 511 , Seiteneigenschaften 254 , Standarddialogfeld 253

E
E/A 17 EditColor-Methode 289 Editor 18, 39 , Quellcode 34 , Visual Studio .NET 34 Eigenschaft , AcceptButton 92 , ndern per JavaScript 432 , AllowDrop 92 , BackColor 85 , BackgroundImage 85 , BorderStyle 132 , CancelButton 92 , DesktopLocation 84 , Divider 132 , Drag & Drop 105 , ForeColor 84 , FormBorderStyle 86 , Formulare 82 , Height 83

, Icon 85 , ImageIndex 128 , IsMdiContainer 354 , Item 119 , KeyChar 101 , Left 84 , Location 84 , MdiParent 354 , NotifyFilter 402 , Opacity 88 , Shortcut 121 , ShownInTaskBar 86 , Size 83 , SqlDataAdapter-Objekt 329 , Tastatur 103 , Textfarbe 84 , ToolBar-Steuerelement 131 , Top 84 , TransparencyKey 88 , Visible 88 , Width 83 , zurcksetzen 93 Eigenschaftendeklaration 595, 605 Ein-/Ausgabe 248, 378 , asynchrone 405 , NetWord 507 , synchrone 405 Eingabe , maskieren 207 , PasswordChar 207 Eingabeaufforderung 40 Eingabehilfen 656, 676 , Microsoft Active Accessibility (MSAA) Einsprungspunkt 55 Einzug 207 Empfnger-Objekt 159 Enabled 122 EndCap 454 EndScroll 143 EndUpdate-Methode 186 Enter 640 Entwicklungsumgebung 34 Entwurfsansicht 314 Equals-Methode 80 Ereignis 94 , Activated 100

676

818

Stichwortverzeichnis ErrorProvider-Steuerelement  42 Ersetzen, Dialogfeld 272 Erweiterbarkeit 590 event 164 EventArgs-Parameter 162 EventHandler-Objekt 160 EventLog 543 EventLog-Klasse 560 Excel 234, 350 , Bereich holen 496 , get_Range-Methode 496 , sortieren 492 , Typbibliothek 491 , via ActiveX verwenden 491 excel9.olb 491 Exception 17 Exception-Klasse 682 ExecuteCommand-Methode 559 ExecuteNonQuery-Methode 335 ExecuteReader-Methode 336 ExecuteScalar-Methode 335 ExecuteXmlReader-Methode 335 Exists 386 Exit 640 ExitCode 700 ExitTime 700 Expect 429 ExtendedProperties-Objekt 324 eXtensible Markup Language (XML) 517 Extension 386

, Begriff 94 , behandeln 153 , benutzerdefiniertes 163 , Bildlaufleiste 143 , ButtonClick 130 , Closed 97 , Closing 97 , CurrentCellChanged 301 , Deactivate 100 , Delegat 158 , Empfnger 158 , Ereignissystem 173 , Handler 101 , instantiieren 165 , KeyPress 101 , mehrere behandeln 156 , nothing 165 , null 165 , Paint 443 , Popup 369 , Resize 139 , Sender 158 , Verdrahtung 152 , WithEvents 226 Ereignisanzeige 555 Ereignisbehandlung 94 , mehrfache Ereignisse 156 Ereignishandler 147, 152, 686 , Begriff 101, 152 , besitzergezeichnetes Steuerelement 569 , Bildlaufleiste 145 , Click-Ereignis 287 , Dateisystem berwachen 404 , mehrere Ereignisse behandeln 156 , NetWord 266 , PrintPage 407 , Signatur 155 , Standardsignatur 155 , Thread 631 Ereignisprotokoll , anpassen 560 , Begriff 553 , Dienste 546 , Ereignisse lschen 555 , FileSystemWatcher 547 , ServiceController-Klasse 560 , Systemereignisprotokoll 546

F
Farbe , Begriff 601 , DataGrid-Steuerelement 302 , Pinsel 601 , RGB 601 , Standarddialogfeld 250 Farbverlauf 461 Fehlerbeseitigung 17 Fenster , aktives MDI-Fenster 365 , bergeordnetes 353 , zeichnen 443 Fenster-Men 352 Festplatte berwachen 403

819

Stichwortverzeichnis FontDialog-Dialogfeld  22 Fontfamilie 252 FontFamily-Objekt 85 foreach-Schleife 382 ForeColor 292, 569 ForeColor-Eigenschaft 84 Form.Refresh 448 Formatierung, RichTextBoxSteuerelement 208 Formatzeichenfolge , CustomFormat-Eigenschaft 191 , DataGrid-Steuerelement 303 , DateTimePicker-Steuerelement 191 , Datum und Uhrzeit 303 , Gro-/Kleinschreibung 191, 304 , Kalender 191 FormBorderStyle-Aufzhlung 87 FormBorderStyle-Eigenschaft 86, 233 FormClicked-Ereignis 448 Form-Ereignisse 97 , Maus und Tastatur 101 Form-Objekt 84, 113 , Menu-Eigenschaft 116 , Methoden 92 , Resize-Ereignis 139 Form-Steuerelement  36 Formular , Activate-Methode 93 , ActiveMdiChild-Eigenschaft 365 , aktualisieren 93, 448 , Bildlaufleiste 136 , Close-Methode 93 , Datenbindung 290 , digitales 35 , Eigenschaften 82 , Gre 83 , Handle 593 , Hide-Methode 92, 240 , in Vordergrund/Hintergrund 93 , Position 83 , posten 422 , Show-Methode 92 , sichtbar/unsichtbar machen 92 , bergeordnetes 354 , Visible-Eigenschaft 88 Formular-Methoden 92

Figur , Bzierkurve 457 , fllen 442, 460 , Pfad 456 , zeichnen 454 File Transfer Protocol (FTP) 17 File.Copy-Methode 380 File.Exists-Methode 385 FileDialog-Klasse  20 FileInfo-Objekt 380, 687 FileMode-Aufzhlung 390 FileName-Eigenschaft 248 File-Objekt 380 FileStream 379 FileSystemWatcher-Objekt 402 FillClosedCurve 462 FillEllipse 462 Fill-Methode 328 FillPath 462 FillPie 462 FillPolygon 462 FillRectangle 462 FillRectangles 462 FillRegion 462 Filter 249, 341 , Benutzeraufforderung 250 , Beschreibung 250 , Filterzeichenfolge 250, 259 , NotifyFilter-Eigenschaft 402 , Standardfilter 250 Firewall 312 First 143 Fixed3D 87 FixedDialog 87 FixedSingle 87 FixedToolWindow 87 Flche 442 FlatMode 292 Float 340 Focus 571 Fokus , RichTextBox-Steuerelement 207, 266 , TextBox-Steuerelement 207 , untergeordnetes MDI-Fenster 367 Font 84, 444, 569 FontDialog-Dialogfeld 235, 252

820

Stichwortverzeichnis

Friend 48 FromFile-Methode 85 FromImage-Methode 469 FrontPage 18 FTP 416 Fllmodus 465 FullName 382, 386 FullRowSelect 220 Function 54 Funktionen , CInt 71 , CStr 71 , MsgBox 99

G
Ganzzahlen umwandeln 71 Garbage Collection 32 GDI 440 GDI+ 441 , Begriff 37 , Bereiche 441, 471 , Brush-Objekte 460 , DrawImage-Methode 451 , DrawString-Methode 444 , Figuren ausfllen 460 , Figuren beschneiden 471 , Figuren dauerhaft machen 468 , Figuren zeichnen 454 , Fill-Methoden 463 , Graphics-Objekt 442, 569 , GraphicsPath 456, 600 , Linien zeichnen 455 , Matrix-Objekt 470 , Metafile-Objekt 474 , Paint-Ereignis 443 , Pfade 456 , Regions 471 , Steuerelemente anpassen 567 , Transformationen 470 , Transparenz 474 gespeicherte Prozedur 338 get_Range-Methode 496 Get-Anforderung 422 GetConfig-Methode 82 GetData-Methode 426 GetDirectories 384, 388

GetFiles 382, 388 GetFileSystemInfos 388 GetHashCode-Methode 81 Get-Methode, SqlDataReader-Objekt 337 Get-Request 422 GetRequestStream 427 GetResponse 427 GetResponseStream 428 GetType-Methode 82, 178 GetWorkingArea-Methode 83 GetXml-Methode 342 globaler Namensraum 51 Grafik , Aliasing 449 , Antialiasing 449 , anzeigen 450 , bearbeiten 452 , Bzierkurve 457 , Bitmap 441 , Bogen 457 , DirectX 441 , Ellipse 457 , Figur transformieren 470 , GDI 440 , Kreisform 457 , Kurve 457 , OpenGL 441 , PathPointType-Aufzhlung 460 , Polygon 457 , Transformation 453 , transparent 193 , transparent machen 474 , Typografie 441 , Vektorgrafik 441 , Verarbeitung 440 , verzerren 451 , Wiedergabehinweise 450 Graphical Device Interface (GDI+) 440 Graphics 442, 444, 569 , FromImage-Methode 469 , Transformation 470 Grauabstufung 17 Grayed 571 GridLineColor 292 GridLineStyle 292 Grenziehpunkt, Dialogfeld 243

821

Stichwortverzeichnis HScrollBar-Steuerelement  45 HTML 421 , ActiveX-Steuerelement Webbrowser HTML-Steuerelemente 38 HTTP 416, 517 , SOAP 520 HTTP-Get 520, 535 HTTP-Header 429 HTTP-Post 520, 535 HTTPS 433 HttpWebRequest 428

Gro-/Kleinschreibung , C# 47 , Camel-Schreibweise 661 , CharacterCasing-Eigenschaft 207 , Eingabe in Textfeld 207 , Formatzeichenfolge 191, 304 GroupBox-Steuerelement 226 GroupBox-Steuerelement  43 Grundfarben 601 Gruppenfeld 226 Gltigkeitsprfung 182 Guid 340

482

H
Handle 700 , Begriff 593 , Formular 593 HandleCount 700 Handled-Eigenschaft 103 Handles (Schlsselwort) 161 HasExited 700 Hash-Code 81 HasMorePages-Eigenschaft 410 HatchBrush-Objekt 461 Hauptprozessor 17 HaveResponse 429 HeaderBackColor 292 HeaderFont 292 HeaderForeColor 292 Headers 427 HeaderText-Eigenschaft 298 HelpButton 89 HelpLink 688 Hide-Methode 240 Hintergrundfarbe 250 , Datenbindung 290 , MDI-Formular 357 , Steuerelement 569 , TabControl-Steuerelement 581 Hintergrundthread 629 Hinweis, Antialiasing 450 Hit-Test 305 HitTest-Methode 305 HotLight 571 HotTrack-Eigenschaft 206 HotTracking 220

I
Icon-Eigenschaft 85 , StatusBarPanel 135 Id 700 IDE 63 Identittsspalte 315 idling 96 IfModifiedSince 429 IIS (Internet Information Server) 419, 540 ildasm.exe (Intermediate Language Disassembler) 484, 679 IList-Schnittstelle 284 Image 85, 340 ImageIndex 128f. ImageIndex-Eigenschaft 194 Image-Klasse 450 ImageList 220 , Symbolleiste 128 ImageList-Steuerelement 193 , ImageIndex-Eigenschaft 194 , Images-Eigenschaft 193 ImageList-Steuerelement  45 Importieren, Namensrume 51 Imports 40, 49 Inactive 571 Indent 220 Index 570 IndexOf-Eigenschaft 131 Info-Menelement 660 Inherits 49 InnerException 688 InputLanguageChanged-Ereignis 100 INSERT 322 , VALUES 322

822

Stichwortverzeichnis

Installer , Dienste 547 , erstellen 547 , Hilfsprogramm 550 , Klasse 547, 549 , Werkzeug 549 , Windows-Dienst 542 Installer (Klasse) 542 installutil.exe 549 Instanz , Begriff 54 , Speicheradresse 81 Int 340 Int16 340 Int32 340 Int64 340 integrierte Entwicklungsumgebung (IDE) 63 Interaktivitt 88 Intermediate Language Disassembler 484, 504, 679 Internet 416, 516, 625 , Kommunikationsprotokolle 517 , Netzwerkbandbreite 625 , Pluggable Protocols 418 , Protokolle 416, 516 , Protokolle, austauschbare 418 Internet Explorer 120, 416 , Arbeiten mit 430 , Steuerelemente einbetten 430 Internet Information Server (IIS) 419 Internetsicherheit 433 , Authentifizierung 433 , Basisauthentifizierung 433 , Berechtigungsklassen 434 , Digestauthentifizierung 434 , HTTPS 433 , Integrierte Windows-Authentifizierung 434 , NetworkCredential-Objekt 434 , Secure Sockets Layer 433 Invalidate-Methode 597 IP 416 IsAccessible 677 IsMdiContainer-Eigenschaft 354 Isolierte Speicherung 412 Item-Eigenschaft 119 Items-Auflistung 184 IWin32Window 236

J
Java 27 JavaScript, Eigenschaften ndern JET 17 JIT-Compiler 31, 666 JIT-Debugger 666, 686 Join 323 Just-In-Time 31 432

K
Kalender 189 , DateTimePicker-Steuerelement 189 , Textbereich ausrichten 192 Kapselung 591 , Begriff 48 , Benutzersteuerelemente 615 kaufmnnisches Und-Zeichen 117 KeepAlive 429 Kennwort , Eingabe maskieren 207 , ExtendedProperties-Objekt 324 , PasswordChar 207 KeyChar-Eigenschaft 101 KeyCode-Eigenschaft 103 KeyData-Eigenschaft 103 KeyEventHandler 101 KeyPress-Ereignis 101 KeyPressEventHandler 101 KeyValue-Eigenschaft 103 Klasse 47 , abstrakte 302 , Begriff 47 , Instanz 54 , nothing 58 , null 59 Klausel , ORDER BY 321 , VALUES 322 , WHERE 320 Knoten 218 Kombinationsfeld 17, 183 , Datenbindung 287 , Dropdown-Liste 187 , Hhe 187 , Items-Auflistung 184 , variable Elementhhen 575

823

Stichwortverzeichnis

Kompatibilitt 31 Kompilieren 63 , Anwendung 40 , Listings in mehreren Dateien 242 Konfiguration , Abschnitte in Datei 660 , AppSettings-Eigenschaft 668 , benutzerdefinierte Datei 659 , ConfigurationSettings-Klasse 667 , Datei des .NET Frameworks 659 , Datei in .NET 658 , Datei in XML 658 , Datei machine.config 659 , Daten liefern 657 , GetConfig-Methode 667 , Konfigurationsabschnitte fr Windows Forms 661 , Konfigurationselement 660 , Probleme 656 , Version 665 , Werte abrufen 667 Konstruktor 40, 263, 633 , Basisklasse 59 , Begriff 58 , erben 59 , Standardkonstruktor 210 , berladen 60 Kontextmen 122 , Begriff 122 , erstellen 122 , kopieren 123 , NetWord 274 Konto , Account-Eigenschaft 548 , Berechtigungen 548 , Dienste 548 , ServiceAccount-Aufzhlung 548 Kontrollhkchen 183 Kontrollkstchen , DataGridBoolColumn 297 , Datum 192 Konventionen 21 Koordinaten 276, 709 , Bildschirm 603 , Maus 603 , bersetzen 603, 605

Kopieren , Datei 380 , File.Copy-Methode Kreisform 457

380

L
LabelEdit 220 Label-Steuerelement 202 , persistent 449 , Tabseite 203 Label-Steuerelement  46 Lndereinstellungen 669 , Kultur 672 LargeChange-Eigenschaft 143 LargeDecrement 143 LargeIncrement 143 Last 143 LastAccessTime 386 LastWriteTime 386 Layout , MDI-Fenster 358 , MdiLayout-Aufzhlung 358 , Steuerelemente 222 Leerlauf 96 , Nachrichtenschleife 96 Left-Eigenschaft 84 Legacy-Programme 29 Length 386 LIKE 321 LineJoin 454 Linie , PathPointType-Aufzhlung 460 , zeichnen 442 LinkColor 293 LinkHoverColor 293 LinkLabel-Steuerelement  46 ListBox-Steuerelement 17, 195 , anpassen 570 , benutzerdefiniertes 574 , Elemente auswhlen 195 , MultiColumn-Eigenschaft 196 , SelectedIndices-Eigenschaft 198 , UseTabStops-Eigenschaft 198 ListBox-Steuerelement  13 ListControl-Klasse  13 Liste , Dropdown-Liste 190, 239

824

Stichwortverzeichnis

, hierarchische 230 , Index 189 , nullbasiert 188 Listenfeld 17, 195 , anpassen 571 , benutzerdefiniertes 574 , FindStringExact-Methode 199 , Text suchen 199 ListView-Steuerelement  47 LoadFile-Methode 401, 686 localhost 327 LocalService-Konto 548 LocalSystem-Konto 548 Location-Eigenschaft 84 Lock 641 Lokalisierung 656, 669 , Ablauf 670 , Begriff 669 , CultureInfo-Klasse 675 , Dateinamen 672 , ResourceManager-Klasse 674 , Ressourcen 669 bersetzen 676

M
machine.config 659 MachineName 700 MainMenu 116, 154 MainMenu  26 Main-Methode 54, 69, 240, 242, 263 , untergeordnetes Dokument 353 MainModule 700 MainWindowHandle 700 MainWindowTitle 700 Managed Code 32 MappingName 17 Marshalling 500, 634 , threadbergreifendes 637 Maschinencode, JIT-Compiler 31 Maschinensprache 27 Matrix-Objekt 470 , Methoden 471 Maus , Ereignisse 101, 104 , Koordinaten 603

Mauszeiger , animierte 86 , Arrow 86 , Bildschirmkoordinaten 104 , Cursor-Eigenschaft 86 , Ereignisse 94 , IBeam 86 , QuickInfo 221 , ResetCursor-Methode 448 , zurcksetzen 448 MaximizeBox 89 MaximumAutomaticRedirections 429 MaxWorkingSet 700 MDI 262, 350 , Adobe Framemaker 351 , aktives Fenster 365 , Anwendung 350, 705 , Anwendung erstellen 349, 353 , Child-Dokumente 353 , Excel 350 , Fensteranordnung 358 , Fenster-Men 352 , hinzufgen 505 , Menu Merging 362 , Men 352 , Menleiste 357 , Mens zusammenfhren 362 , MergeOrder-Eigenschaft 364 , Nachteile 352 , Objekttypen untergeordneter Dokumente 361 , Parent-Fenster 353 , bergeordnetes Fenster 353 , bergeordnetes Formular 355 , bergeordnetes Steuerelement 368 , untergeordnete Fenster 365 , untergeordnete Steuerelemente 367 , untergeordnetes Dokument 353 MdiLayout-Aufzhlung 358 MdiList-Eigenschaft 360 MdiParent-Eigenschaft 354 Me 58 MediaType 429 Mehrfachauswahl 199 Meldungsfeld 188 , DialogResult 245

825

Stichwortverzeichnis

MemoryStream 379 Menu Merging 362 Men 146 , anpassen 120, 581 , Begriff 114 , benutzerdefiniertes 581 , Besitz 581 , dynamisches 587, 590 , Gre der Zeichenfolge messen 585 , Handler 118 , hinzufgen 114 , Kontextmen 122 , kopieren 123 , MDI 352 , Menu Merging 362 , Menebenen 119 , NetWord 274 , OwnerDraw-Eigenschaft 581 , Popup-Ereignis 369 , Separator 115 , Shortcut 121 , Tastenkombination 117 , Trennlinie 115 , bergeordnetes Formular 357 , berblick  25, 33 , bersicht 25 , verschachteln 119 , Zugriffstaste 117 , zusammenfhren 362 Menelement , Eigenschaften 122 , Hkchen 157 , Info 660 , MergeOrder-Eigenschaft 364 MenuItem 116 , OwnerDraw-Eigenschaft 581 MenuItem  26 MenuMerge-Aufzhlung 363 MergeOrder-Eigenschaft 364 Message 688 MessageBox (Meldungsfeld) 188 MessageBox.Show-Methode 188 MessageBoxOptions-Eigenschaft 238 Metadaten 37, 397, 480 , Begriff 31 , CLR 32

Metafile-Objekt 474 Method 427 Methode , AcceptIt 91 , Add 71 , AddHandler 73 , Begriff 54 , BringToFront 93 , CancelIt 91 , Console.WriteLine 80 , Equals 80 , Form-Objekt 92 , FromFile 85 , GetConfig 82 , GetHashCode 81 , GetType 82 , GetWorkingArea 83 , Main 54 , Missing 489 , MsgBox 99 , New 58 , Parameter, optionale 489 , ReferenceEquals 81 , rekursive 382 , Run 56 , SendToBack 93 , Shared 55 , static 55 , ToString 82 , Typen 54 MFC (Microsoft Foundation Classes) 27 Microsoft .NET 17 Microsoft Foundation Classes 28 Microsoft Intermediate Language (MSIL) 31 Microsoft Money 308 Microsoft Office 2000 , ActiveX 486 , Dialogfeld, nichtmodales 234 , dynamische Mens 568, 587, 590 , Objektkatalog 497 , Typbibliothek 485 Microsoft Paint 440 MinimizeBox 89 MinWorkingSet 700 Missing 489 MiterLimit 454

826

Stichwortverzeichnis

modales Dialogfeld 234 Modifiers-Eigenschaft 103 Modules 700 Money 340 Monitor-Klasse 639 MouseDown-Eigenschaft 104 MouseEnter-Eigenschaft 104 MouseEventhandler-Objekt 104 MouseHover-Eigenschaft 104 MouseLeave-Eigenschaft 104 MouseMove-Eigenschaft 104 MouseUp-Eigenschaft 104 MoveTo 387 MSAA 676 MsgBox-Methode 99 MSIL (Microsoft Intermediate Language) 31, 40, 480 , DLL 53 msword9.olb 486 Multiline-Eigenschaft 206 Multiple Document Interface (MDI) 350 Multitasking-Betriebssystem 30, 350 Multithreading 19, 622 , Anwendung 622, 625 , Anwendung erstellen 628 , Betriebssystem 623 , Browser 625 , Steuerelemente 652 , Suchanwendung 652 , Suchfunktion 644

N
Nachrichtenschleife 94 Name 386 Namensraum 49 , ActiveX 482 , Assemblies 51 , globaler 51 , importieren 51 , Objektgruppen 40 , Standard 51 , Steuerelemente einbetten , System 50 , Webdienst 523 , Word 489 NChar 340

431

NetWord 261 , ActiveX 722 , benutzerdefinierte Bildlaufleisten 717 , benutzerdefiniertes DialogfeldSteuerelement 705 , benutzerdefiniertes Ereignis 707 , Dialogfelder 268 , Document-Klasse (revidiert) 709 , Drucken 511 , Ein-/Ausgabe 508 , Ereignishandler 266 , erweitern 505 , FindDialog-Klasse (revidiert) 718 , Kontextmen 274 , MDI-Anwendung 505 , Men 274 , OpenFileDialog-Dialogfeld 509 , Page-Klasse 705 , Schriftart 269 , Schriftfarbe 269 , Statusleiste 275 , Suchen und Ersetzen 272 , Tastenkombination 274 , vervollstndigen 705 NetworkCredential-Objekt 434 NetworkService-Konto 548 NetworkStream 379 Netzwerkzugang 378 New-Methode 58 nichtmodales Dialogfeld 234 Nichtverwalteter Code 32 NoAccelerator 571 Nodes 220 NoFocusRect 571 None 87, 571 NonInheritable 107 Non-Managed Code 32 NonpagedSystemMemorySize 700 Normal, PictureBox-Steuerelement 200 NotePad 18, 34, 693 nothing 58, 165 NotifyFilter-Eigenschaft 402 NText 340 null , Destruktor 59 , Ereignis 165

827

Stichwortverzeichnis

NumericUpDown-Steuerelement NVarChar 340

 39

O
Object Linking and Embedding (OLE) 478 Object-Klasse 178 Object-Objekt 79 OBJECT-Tag 430 Objekt , Begriff 38 , Binding 284 , BindingContext 286 , Eigenschaften 38 , Hash-Code 81 , Methoden 38 , Object 79 , Screen 83 , Serialisierung 396 , Size 83 , Speicheradresse 81 , Sperren 640 , zuweisen an Variable 70 Objektinstanz 54 Objektorientierte Programmierung (OOP) 38 OLE (Object Linking and Embedding) 478 OLE DB 19 , Datenquelle 311 OLE DB-Provider 325 OLE-Automatisierung 479 OleDbCommand  87 OleDBCommandBuilder  88 OleDbCommand-Objekt 335 OleDbConnection  88 OleDbDataAdapter  90 OleDbDataReader  86, 92 OleDbDataReader-Objekt 335 OleDbError  93 OleDbErrorCollection  93 OleDbParameter  94 OleDbParameterCollection  94 OleDbTransaction  96 OnContinue 544 OnCustomCommand 544 OnCustomCommand-Methode 559 On-Methode 165 OnPaint-Methode 444

OnPause 544 OnPowerEvent 544 OnShutdown 544 OnStart 544 OnStop 544 OOP (Objektorientierte Programmierung) Opacity-Eigenschaft 88 Open 387, 390 OpenFileDialog-Dialogfeld 235 OpenFileDialog-Dialogfeld  20 OpenFile-Methode 687 OpenGL 441 OpenOrCreate 390 OpenRead 387 OpenText 387 Option Strict 79 Optionen, Compiler 65 Oracle 284, 310 ORDER BY 321 , ASC 321 , DESC 321 Ordner berwachen 402 Overloads 62 Override 445

38

P
Padding-Eigenschaft 206 Page_Load-Ereignis 423 Page_Load-Methode 423 PagedMemorySize 700 PagedSystemMemorySize 700 PageSetupDialog-Dialogfeld 235 PageSetupDialog-Dialogfeld  23 Paint-Ereignis 443 PaintEventArgs-Objekt 444 Panel-Steuerelement 140 Panel-Steuerelement  39 Parameter , Delegat 162 , Missing 489 , optionale 489 , Show-Methode 236 PARAM-Tag 431 Parent 387 ParentRowsBackColor 293 ParentRowsForeColor 293

828

Stichwortverzeichnis

ParentRowsLabelStyle 293 ParentRowsVisible 293 PathPointType-Aufzhlung 460 PathSeparator 220 Paused 558 PausePending 558 PeakPagedMemorySize 700 PeakVirtualMemorySize 701 PeakWorkingSet 701 Peek-Methode 392 Pen 442 , Aufzhlungen 455 , Eigenschaften 454 PenType 454 Pfad , definieren 456 , File.Exists-Methode 385 Pfadbezeichner 418 Photoshop 234 PictureBox-Steuerelement 199 , Image-Eigenschaft 199 , SizeMode-Eigenschaft 199 PictureBox-Steuerelement  50 Pinsel 442 , Farbe 601 , Farbverlauf 461 , Typen 461 Pipelined 429 Pipe-Symbol 250 PlainText 401 Point-Objekt 206 PopulateList-Methode 382 Popup-Ereignis 369 Prsentationsschicht 283 PreAuthenticate 427 PreferredColumnWidth 293 PreferredHeight-Eigenschaft 187 PreferredRowHeight 293 Primrschlssel 316 , PrimaryKey-Eigenschaft 331 , SqlCommandBuilder-Objekt 330 PrimaryKey-Eigenschaft 331 PrimaryScreen-Eigenschaft 603 PrintDialog-Dialogfeld 235 PrintDialog-Dialogfeld  24 PrintDocument-Objekt 406 PrintDocument-Steuerelement  50

PrintPage-Ereignis 406 PrintPreviewControl-Steuerelement  51 PrintPreviewDialog-Dialogfeld 235, 248 PrintPreviewDialog-Steuerelement 256 PrintPreviewDialog-Steuerelement  40 PriorityBoostEnabled 701 PriorityClass 701 Priority-Eigenschaft 630 Private 48 PrivateMemorySize 701 PrivilegedProcessorTime 701 Process-Klasse 699 ProcessName 701 ProcessorAffinity 701 Profilerstellung 682 Profiling 19, 682, 696 , Anwendungsstatistik 698 , Begriff 696 , Eigenschaften der Process-Klasse 700 , Process.Refresh-Methode 699 , Process-Klasse 696 , Prozesse berwachen 697 , Prozess-ID (PID) herausfinden 698 , Task-Manager 696 Programmabsturz verhindern 683 Programmiersprache , Begriff 27 , C# 18, 27f., 260, 326 , C++ 27 , Einfhrung 27 , Java 27 , SQL 319 , Visual Basic 27 ProgressBar-Steuerelement 221 ProgressBar-Steuerelement  52 property 240 PropertyGrid-Steuerelement  40 ProtocolVersion 429 Protokoll , austauschbar 418 , FTP 416 , HTTP 416 , Internet 416 , plattformunabhngig 517 , SOAP 519 , Webdienste 520 Protokollmethode 427

829

Stichwortverzeichnis

Provider, verwaltete Proxy 427 Prozess 622 Public 48 Pulse 640 PulseAll 640 PushButton 133

 86

Q
Quellcode , ActiveX Control Importer 481 , C# 46 , Editor 34 , kompilieren 63 , Visual Basic .NET 46 QueueUserWorkItem-Methode 645 Quicken 308 QuickInfo 221

R
RadioButton-Steuerelement 180 RadioButton-Steuerelement  11 Random-Klasse 172 Reader 388 , schlieen 421 ReadLine-Methode 392 Read-Methode, SqlDataReader-Objekt ReadToEnd-Methode 392 ReadXml-Methode 342 Real 340 Rechteck 442 Recordset 312 Rectangle (Rechteck) 442 Rectangle-Objekt 83, 443, 593 ref 490 ReferenceEquals-Methode 81 Referenzen 255 , vergleichen 81 Referer 429 Refresh-Methode 93 regasm.exe 499 Region (Bereich) 442 Region-Objekt 600 , erzeugen 443 Registerkarte 17, 200 Registrierungs-Editor 657 Rekursion 383

337

RelationsCollection-Objekt 323 RemoveHandler-Methode 161 Request/Response-Modell 416 Request-Objekt 423 RequestUri 427 Reset 248, 471 ResetBackColor 93 ResetCursor 93 ResetCursor-Methode 448 ResetFont 94 ResetForeColor 94 ResetText 94 resgen.exe (Resource File Generator) 671 Resize 139 Resource File Generator 671 Responding 701 ResponseUri 428 Ressourcen , Garbage Collection 32 , Lokalisierung 669 , Speicherbereinigung 32 Ressourcendatei 670 , aus Bildern erzeugen 679 , Kompilieren zu Assemblies 670 , Satellitenassemblies 672 Ressourcenfallback 673 Rich Text 208 RichNoOleObjs 401 RichText 401 RichTextBox-Steuerelement 206, 400 , Dock-Eigenschaft 210 , Dokument drucken 408 , Ereignis behandeln 212 , Fokus 207, 266 , Konstruktor 210 , LoadFile-Methode 401 , Mitglieder 215 , NetWord 262 , SaveFile-Methode 401 , Schriftart 408 , ScrollBars-Eigenschaft 210 , SelectionFont-Eigenschaft 213 , Text formatieren 208 , Word-Dokument 488 RichTextBox-Steuerelement  55 RichTextBoxStreamType-Aufzhlung 401 Root 387

830

Stichwortverzeichnis

Rotate 471 RotateAt 471 RotateTransform-Methode Round-trip-Format 303 RowHeadersVisible 293 RowHeaderWidth 293 RowStateFilter-Eigenschaft Run 56, 69, 544 Running 558, 630

470

342

S
Satellitenassemblies 671 SaveFileDialog-Dialogfeld 235 SaveFileDialog-Dialogfeld  20 SaveFile-Methode 401 Scale 471 ScaleTransform-Methode 470 Schaltflche , Aussehen 179 , Datenbindung 285 , Ergebnis in Dialogfeld 245 , FlatStyle-Eigenschaft 179 , ImageList-Objekt 128 , in Dialogfeld 233 , Symbolleiste 127 Schemabezeichner 417 Schleifenformatbezeichner 303 Schlssel/Wert-Paare 664 Schlsselwort , Begriff 48 , delegate 169 , event 164 , Friend 48 , Get 240 , Handles 161 , Imports 40, 49 , Inherits 49 , LIKE 320 , Lock (in C#) 641 , Me 58 , NonInheritable 107 , Overloads 62 , Override 445 , Private 48 , property 240 , Protected 478

, Public 48, 478 , ref 490 , sealed 107 , Set 240 , Shared 54 , static 54 , SyncLock (in VB) 641 , this 58 , throw 688 , using 49 , WithEvents 69, 226 Schnittstelle IList 284 Schriftart 209 , Antialiasing 450 , DateTimePicker-Steuerelement 190 , Font-Eigenschaft 84 , FontFamily-Objekt 84 , Font-Objekt 444 , Kalender 190 , NetWord 269 , Schriftstil 210 , Standarddialogfeld 252 , Steuerelement 569 , wechseln 155 Schriftfarbe , NetWord 269 , TabControl-Steuerelement 581 SCM 17 Screen-Klasse 603 Screen-Objekt 83 Scrollable 220 Scrollable Controls  33 ScrollBar-Klasse  44 ScrollBars 210 Scroll-Box (Bildlauffeld) 143 Scroll-Ereignis, Bildlaufleiste 141 SDI, Anwendung 350 sealed 107 Secure Sockets Layer (SSL), Begriff 433 Seite , Einstellungen ndern 411 , HasMorePages-Eigenschaft 410 , PrintPageEventArgs-Objekt 411 Seiteneigenschaften 255 SELECT 319 , WHERE 320

831

Stichwortverzeichnis

Selected 571 SelectedTab-Eigenschaft 205 SelectionBackColor 293 SelectionFont-Eigenschaft 213 SelectionForeColor 293 SendChunked 429 Sender-Objekt 158 SendToBack-Methode 93 Separator 115, 120 , Symbolleiste 133 Serialisierung 396, 722 , Begriff 396 , Binrdaten 397 , XML-Serialisierung 400 Server 417, 419 , Webserver 419, 540 Service Control Manager (SCM) 542 , benutzerdefinierter 559 ServiceAccount-Aufzhlung 548 ServiceBase-Klasse , Eigenschaften 543 , Methoden 543 , OnCustomCommand-Methode 559 ServiceController-Klasse , Ereignisprotokoll 560 , ExecuteCommand-Methode 559 ServiceControllerStatus-Aufzhlung 558 ServiceName 543 ServicePoint 429 Services 17 Shared 54 SHDocVw.dll 482 Shear 471 Shift-Eigenschaft 103 Shortcut , Men 121 , Symbolleiste 126 Shortcut-Eigenschaft 121 ShowChangeBox-Methode 242 ShowDialog-Methode 242 ShowHelp-Methode 248 ShowLines 220 Show-Methode , MessageBox 235 , Parameter 236

ShownInTaskBar-Eigenschaft 86 ShowPanels-Eigenschaft 135 ShowPlusMinus 220 ShowRootLines 220 Sicherheit 433 Sicherungsspeicher 379 Signatur, Ereignishandler 155 Simple Object Access Protocol (SOAP) 519 Single 340 Single Document Interface (SDI) 350 SingleBitPerPixel 450 SingleBitPerPixelGridFit 450 Singlethreaded-Umgebung 623 Sizable 87 SizableToolWindow 87 Size-Eigenschaft 83 SizeGripStyle 89 Size-Objekt 83 SmallChange-Eigenschaft 143 SmallDateTime 340 SmallDecrement 143 SmallIncrement 143 SmallInt 340 SmallMoney 340 SOAP 517 , Begriff 519 , Installer 552 SOAP (Simple Object Access Protocol) 519 Sortieren , ASC (aufsteigend) 321 , DESC (absteigend) 321 , Excel und ActiveX 492 , ORDER BY 321 , Reihenfolge 321 , Spalte 321 Sortierreihenfolge 342 Source 688 Spalte , formatieren 297 , sortieren 321 Speicher, Garbage Collection 32 Speichern, Dokument 510 Sperren , Code 640 , Enter-/Exit-Methoden 640 , Objekt 640

832

Stichwortverzeichnis

, Ressourcen 627 , Syntax 641 , Threading 636 Spring-Eigenschaft 135 SQL 310 , Befehle generieren 330 , Begriff 319 , Datenbankabfrage 319 , DELETE 323 , gespeicherte Prozedur 338 , INSERT 322 , Join 323 , LIKE 320 , ORDER BY 321 , SELECT 319 , Union 323 , UPDATE 322 , VALUES 322 , WHERE 320 SQL Server 19, 284, 310, 540 , Datenbank anlegen 317 , Datentypen 318 , Enterprise Manager 338 , gespeicherte Prozedur 338 , Konto fr Systemadministrator 327 , Verbindungszeichenfolge 326 SQL Server-Authentifizierung 327 SqlCommandBuilder-Objekt 330 , Primrschlssel 330 SqlCommand-Objekt 335 SqlDataAdapter-Objekt 328 , Daten bearbeiten 329 , Eigenschaften 329 SqlDataReader  86 SqlDataReader-Objekt 335 , Get-Methode 337 SQL-Provider 325 SSL 17 StackTrace 688 Standarddialogfeld 235, 248 , ColorDialog-Steuerelement 250 , Datei ffnen 248 , Drucken 253 , Farbe 250 , FileName-Eigenschaft 248 , Filterzeichenfolge 250

, PrintPreviewDialog-Steuerelement 256 , Schriftart 252 , Seite einrichten 254 , Speichern 248 Standarddialogfeld  19 StandardError 701 StandardInput 701 Standardnamensraum 51 StandardOutput 701 Standardzeichensatz 253 StartCap 454 StartFigure-Methode 457 StartInfo 701 StartPending 558 StartTime 701 State 570 static 54 Statusanzeige  52 StatusBar 133 StatusBarPanel 133 StatusBar-Steuerelement  58 Statusleiste 125, 133 , AutoSize-Eigenschaft 135 , NetWord 275 , ShowPanels-Eigenschaft 135 , Spring-Eigenschaft 135 , StatusBar-Objekt 133 Statusleiste  58 Statusleistenbereich 133 Steuerelemente 112 , ActiveX 480 , ADO.NET 325 , anpassen 566 , Appearance-Eigenschaft 183 , Architektur 592 , Auf-Ab-Steuerelemente 192 , Aussehen automatisch ndern 182 , benutzerdefinierte 590 , Benutzersteuerelemente 611, 613 , Besitzer 581 , besitzergezeichnete 568 , Bezeichnungsfeld 178 , bildlauffhige  33 , Bildlaufleiste 136, 178 , Bildlaufleiste  44 , Button 178f., 633, 686

833

Stichwortverzeichnis , Button  11 , CheckBox 180 , CheckBox  12 , CheckChanged-Ereignis 183 , CheckedListBox  18 , CheckListBox 221 , ColorDialog  20 , ComboBox 178, 183, 287, 494, 567, 575 , ComboBox  14 , Containersteuerelement 114 , Containersteuerelement  36 , ContextMenu 209, 274 , ContextMenu  25 , Control-Klasse 592 , DataGrid 221, 282, 290 , DataGrid  28 , DateTimePicker 189 , DateTimePicker  32 , Dialogfeld 239 , DomainUpDown  35 , Eigenschaften 594 , Einbetten in Internet Explorer 430 , Ereignisse 594 , ErrorProvider  42 , Erweiterung 611 , Form 569 , GDI+ 567 , GroupBox 226 , GroupBox  43 , Gruppenfeld 226 , gruppieren 182 , Hintergrundfarbe 569 , HScrollBar  45 , ImageList 193 , ImageList  45 , in Windows Forms 592 , kapseln 652 , Kombinationsfeld 178, 183 , Kontextmen 262 , Kontrollhkchen 183 , Label 178, 591, 633 , Label  46 , Layout 222 , Layout in Windows Form 229 , LinkLabel  46 , ListBox 195, 494, 567 , ListBox  16 , Listen-Steuerelement 114 , ListView  47 , MainMenu  26 , Men 114, 178, 260, 262, 352, 566 , Menelement 260 Shortcut 274 , MenuItem 209, 274 , MenuItem  26 , MessageBox 269 , Methoden 594 , neu zeichnen 597 , NumericUpDown  39 , Optionsfeld 178 , Panel 140, 717 , Panel  39 , PictureBox 199, 437 , PictureBox  50 , PrintDocument  50 , PrintPreviewControl  51 , PrintPreviewDialog  40 , ProgressBar 221 , ProgressBar  52 , PropertyGrid  40 , RadioButton 178, 180 , RadioButton  13 , RichTextBox 206, 262, 400, 421, 685, 705 , RichTextBox  55 , Schaltflche 178 , Schaltflchen-Steuerelement 114 , Schriftart 569 , Standarddialogfeld  19 , Statusanzeige  52 , StatusBar 133, 275 , StatusBarPanel 133, 275 , StatusBarPanel  58 , Statusleiste 178, 262, 275 , Statusleiste  58 , Strukturansicht 178 , Symbolleiste 178 , Symbolleiste  61 , TabControl 200, 567 , TabControl  59 , TabPage 200 , TabPage  42 , TextBox 178, 183, 206, 593

834

Stichwortverzeichnis , TextBox  57 , Textfeld  53 , Timer 178, 215, 591 , Timer  61 , ToolBar  61 , ToolBarButton  62 , ToolTip 221 , ToolTip  63 , TrackBar  64 , TreeNode  67 , TreeView 178, 218, 239, 283, 382 , TreeView  65 , Verlaufsleiste  64 , Vordergrundfarbe 569 , vorhandene Steuerelemente erweitern , VScrollBar 717 , VScrollBar  45 , Webbrowser (nur ActiveX) 482 , zeichnen 599 , zusammengesetzte 611, 613 , Zustand 570 Steuerschaltflchen 243 Stift 442 , Linie zeichnen 455 Stileigenschaften 297 Stopped 558, 630 StopPending 558 StopRequested 630 Stream , Begriff 379 , Binrdaten lesen und schreiben 393 , BinaryFormatter-Objekt 399 , BinaryReader 393 , BufferedStream 379 , Close-Methode 390 , CryptoStream 379 , Datei lesen und schreiben 388 , Datenquelle 379 , FileStream 379 , HtmlTextWriter 393 , HttpWriter 393 , in Web-Interaktion 422 , MemoryStream 379 , NetworkStream 379 , Posting 425 , Quelltyp 379

611

, Reader 380 , Sicherungsspeicher 379 , StreamReader 392 , StreamReader-Objekt 392 , StreamWriter 390 , StringReader 380, 393 , StringWriter 393 , verknpfen 379 , Writer 380 , XmlReader 380 StretchImage 200 String 340 StringReader 380 Strom 17 , StreamReader-Objekt 701 , StreamWriter-Objekt 701 , StringReader-Objekt 511 Structured Query Language (SQL) 319 Strukturansicht 17, 218 Strukturknoten 221 Style-Eigenschaft 132 Sub 54 Suchanwendung, multithreaded 642 Suchen, Dialogfeld 272 Suspended 630 SuspendRequested 630 Symbolleiste 125f. , anpassen 131 , Appearance-Eigenschaft 131 , Begrenzung 132 , Eigenschaften 131 , Handler 130 , Icon-Eigenschaft 135 , ImageList-Objekt 128 , Schaltflche 127 , Schaltflchenstil 133 , Steuerelemente  61 , Trennlinie 132 Synchronisierung 635 SyncLock 641 Syntax der Eigenschaftendeklaration 595 System (Namensraum) 50 Systemadministrator 327 SystemDefault 450 Systemmenfelder 89 Systemprotokoll 555

835

Stichwortverzeichnis

T
TabControl-Steuerelement 200 , AddTab-Methode 205 , anpassen 578 , Appearance-Eigenschaft 206 , Aussehen 206 , ChangeLocation-Methode 205 , Eigenschaften 205 , Hintergrundfarbe 581 , Padding-Eigenschaft 206 , Schriftfarbe 581 TabControl-Steuerelement  59 Tabelle , benutzerdefinierte Ansicht 341 , Beziehungen 323 , DataGrid-Steuerelement 298 , DataTable-Objekt 323 , Datenblatt 298 , Datenblattansicht 314 , Entwurfsansicht 314 , RelationsCollection-Objekt 323 , TableStyles-Eigenschaft 298 , Zeile aktualisieren 322 , Zeile einfgen 322 , Zeile lschen 323 TabPage-Steuerelement 200 TabPage-Steuerelement  42 Tabseite 17, 200 , HotTrack-Eigenschaft 206 , Label-Steuerelement 203 , Multiline-Eigenschaft 206 Tabulatorzeichen, Listenfeld 198 TargetSite 688 Taschenrechner 499 Taschenrechner-Anwendung 175 Task-Manager 696 Tastaturereignisse 101 Tastenkombination 117 , NetWord 274 , spter festlegen 122 tblimp.exe (Type Library Importer) 486 TCP 416 Temperaturumrechner 233 Text 340 , DrawString-Methode 444 , zeichnen 444, 446

TextBoxBase-Klasse  53 TextBox-Steuerelement 206 , Datenbindung 333 , Dialogfeld 239 , Einbetten in Browser 430 , Eingabe maskieren 207 , Fokus 207 , Handle 593 , Methoden 207 TextBox-Steuerelement  57 Text-Eigenschaft 84 , ListBox-Steuerelement 199 Textfarbe 84 Textfeld , DataGridTextBoxColumn 297 , rckgngig machen 207 , Steuerelemente  53 Textprogramm 34 TextRenderingHint-Eigenschaft 450 TextTextOleObjs 401 Textverarbeitung 261 Thermostat 619 this 58 Thread 696 , Begriff 622 , Ereignishandler 631 , erzeugender 652 , Hauptthread 627, 650 , Hintergrundthread 629 , Join-Methode 633 , Marshalling (eines Befehls) 634 , Prioritt 630 , Priority-Eigenschaft 630 , Profiling 702 , Thread-Objekte 701 , threadsicher 635 , ThreadState-Eigenschaft 630 , untersuchen 628 , Vordergrundthread 629 , Zustand 630 Threading 622 , Datennutzung 626 , Klasse 627 , Kommunikation 626 , Monitor-Klasse (Synchronisierung) 639 , Multiprozessorsysteme 653

836

Stichwortverzeichnis

, Mutex-Klasse 644 , Probleme mit 626 , ReaderWriterLock-Klasse 644 , Ressourcenbedarf 626 , Ressourcensperrung 627 , Sperren 636 , statische Threadvariablen 653 , Synchronisierung 635 , Threadpooling 628 Threadpool 645 , Threads benachrichtigen 649 Threadpooling 645 Threads 701 ThreadStart-Delegat 631 ThreadState-Eigenschaft 630 threadbergreifendes Marshalling 637 throw 688 ThumbPosition 143 ThumbTrack 143 Tilde 59 Timeout 427 Timer-Steuerelement 215 , Einsatzmglichkeiten 215  61 Timer-Steuerelement Timestamp 340 Time-Steuerelement, Profiling 698 TinyInt 340 ToggleButton 133 ToolBar 126 ToolBarButton-Objekt 126 ToolBarButton-Steuerelement  61 ToolTip-Steuerelement 221  63 ToolTip-Steuerelement Top-Eigenschaft 84 TopNode 220 ToString-Methode 73, 82 TotalProcessorTime 701 TrackBar-Steuerelement  64 TransferEncoding 429 Transform 454 Transformation , Grafik 453 , Graphics-Objekt 470 Transform-Methode 470 Translate 471 TranslateTransform-Methode 470 TransparencyKey-Eigenschaft 88

TreeNode-Objekt 218 TreeNode-Steuerelement  67 TreeView-Steuerelement 17, 218 , aktualisieren 402 , anpassen 219 , Eigenschaften 219 , Ereignisse 221 , Methoden 221 , Stammknoten (root node) 218 , Strukturknoten (node) 218 , TreeNode-Objekt 218 , Verzeichnis durchlaufen 381 TreeView-Steuerelement  65 Treffertest 305 Trennlinie 115, 120 , Symbolleiste 132 Treppcheneffekt 449 Truncate 390 TryEnter 640 Typbibliothek , Begriff 485 , Excel 491 , excel9.olb 491 , Microsoft Office 485 , msword9.olb 486 , Type Library Importer 486 , Word 486 , Wrapper 486 Typografie 441 Typumwandlung 73, 80 , Casting-Operator 163

U
UDDI 534 UDP 416 berladen 60 UI (User Interface) 17 UnicodePlainText 401 Uniform Resource Locator (URL) 417 Union 323 UniqueIdentifier 340 Universal Description, Discovery and Integration (UDDI) 534 Universal Resource Identifier (URI) 417 Unstarted 630 UPDATE 322 UpdateClock-Methode 217

837

Stichwortverzeichnis

Update-Methode 603 URI 417 , HTTPS 433 , Schemabezeichner 417 URL 417 , Abfragezeichenfolge 418 , Path Identifier 418 , Pfadbezeichner 418 , Schema Identifier 417 , Schemabezeichner 417 , Webbrowser 484 urlencoded 425 User Interface (UI) 68 UserAgent 429 User-Konto 548 UserProcessorTime 702 using 49

V
Validated-Ereignis 100 VALUES 322 VarBinary 340 VarChar 340 Variable 60, 209, 683 , beobachten 694 , deklarieren 69 , Objekt zuweisen 70 , Option Strict 79 , Text-Eigenschaft 168 , threadbergreifend nutzen 637 , Typ deklarieren 79 , unter GDI+ 468 , vergleichen 81 Variant 340 VB .NET 18, 28, 58 Vektorgrafik 441 Verarbeitungsregeln 283 Verbindungszeichenfolge 326 , Access 326 , OleDBConnection-Objekt 326 , SQL Server 326 , SqlConnection-Objekt 326 Vergleichen, Zeichenfolge 66 Verlaufsleiste  64 Version, Konfiguration 665 verwaltete Provider  86

verwalteter Code 32 Verzeichnis , durchlaufen 381 , berwachen 402 Verzeichnisstruktur 218 Verzerren, Grafik 451 VIEWASTEXT 431 VirtualMemorySize 702 Visible 122 VisibleCount 220 Visible-Eigenschaft 88 Visual Basic 27 , OLE-Automatisierung 479 Visual Basic-Editor 497 Visual C++, OLE-Automatisierung 479 Visual Studio .NET 18, 34, 351, 693 , kompilieren 63 , Lokalisierung 671 , Ressourcendateien 671 Vordergrundfarbe 569 Vordergrundthread 629 VS .NET 34 , Projektmappen-Explorer 64 , Quellcode-Editor 64 , Windows Forms-Designer 65 VScrollBar-Steuerelement  45

W
Wait 640 WaitCallBack-Delegat 645 WaitSleepJoin 630 Warteschlange 648 Web Service (Webdienst) 37 Web Service Discovery-Werkzeug 527 Web Services Description Language (WSDL) 525 Webanforderungen, asynchrone 436 Webbrowser, ActiveX-Steuerelement 482 Webdienst 436, 516 , .asmx 523 , Bedeutung und Anwendungen 517 , Begriff 30 , Brinkster 537 , Dateierweiterung 523 , Dienstbeschreibung 524f. , Discovery 526

838

Stichwortverzeichnis

, Ermitteln 526 , Funktionsweise 521 , in Anwendungen 526 , Konsumieren 528 , kostenlos testen 537 , Namensraum 523 , Protokolle 520 , Proxy-Klasse 528 , Richtlinien fr Einsatz 535 , suchen 534 , UDDI 534 , Webmethode 523 , WSDL 525 Web-Posting 422 WebRequest 418 , Anforderungen senden 419 WebRequest-Objekt 416 , Eigenschaften 426 , HttpWebRequest 428 , Methoden 426 , schlieen 421 WebResponse 418, 427 Webserver, Webdienst kostenlos testen 537 Weckuhr 216, 232 WHERE 320 Width 454 Wiedergabehinweis, Antialiasing 450 Wiederverwendbarkeit 591 Win16 28 Win32 28 Windows , Betriebssystem 18 , Ein-/Ausgabe 378 , Grafikfhigkeiten 440 , Registrierung 499, 503, 657 , Systemmonitor 703 , Task-Manager 696 Windows 2000 18, 33 , Dienste 541 Windows Forms 35 , automatisches Layout 178 , Dialogfeld 232 , Einfhrung 35 , Ereignisse 94, 152 , Fehlerbehandlung 666 , Funktionen 36

, mit Steuerelementen erweitern 177 , Steuerelemente 37 , Technologie 17 Windows Forms-Anwendung , C# 47 , erstellen 45 , Implementierung 37 , Konfiguration 37 , VB .NET 46 Windows Forms-Designer 64 Windows Forms-Steuerelemente 68, 112 , Architektur 38 , Control-Klasse 592 Windows Installer 678 Windows NT 4.0 18, 33 Windows Task-Manager 704 Windows XP 18, 33 , Konto 548 Windows-Authentifizierung 327 Windows-Betriebssystem 26, 96 Windows-Dienste 540 , Begriff 540 , Benutzeroberflche 544 , deinstallieren 552 , Dienststeuerung 556 , Einsatz 541 , erzeugen 542 , Installer 547 , Konto 548 , Schnittstellen 555 , ServiceBase-Klasse 542 , ServiceController-Klasse 555 , starten/beenden 552 , Status 558 , berwachen 553 Windows-Diensteverwaltung 17, 542 Windows-Editor 18, 705 Windows-Programmierung 26 WithEvents 69, 226 Word 232, 261, 474, 518 , Auflistung von Dokumenten 489 , Dokumente bearbeiten 412 , Ergebnis eines Dialogfeldes 246 , Men 352 , NetWord 705 , Prozesse 624

839

Stichwortverzeichnis

, Rechtschreibprfung 635 , Typbibliothek 486 , via ActiveX nutzen 486 WordPad 18 WorkingSet 702 Wrapper, Typbibliothek 486 WriteLine-Methode 390 Write-Methode 390 Writer 388 , Close-Methode 390 , schlieen 426 WSDL 525 , Parameter 529 , Proxy-Klasse 528 , wsdl.exe 528 WWW Consortium 429

X
XCOPY-Befehl 677 XML 310, 342, 422, 517 , ADO.NET 313 , Bedeutung 517 , GetXml-Methode 342 , ReadXml-Methode 342 , SOAP 517 , WSDL 525 , XML-Schema 313 XmlDataDocument-Objekt 344 XmlReader-Objekt 335

Z
Zeichenabstand 253 Zeichenfolge , ausgeben 80 , Eingaben umwandeln , Filter 249 , Format 191

71

, formatieren 303 , Gre messen 585 , Kombinationsfeld 184 , Listenelement 189 , Schriftart wechseln 155 , Statusleistenbereich 135 , suchen 215 , Titelzeile des Formulars 243 , Variablentyp 82 , Verbindungszeichenfolge 326 , vergleichen 66 , zeichnen 444 Zeichnen , benutzerdefinierter Modus 578 , DrawMode-Eigenschaft 568 , Farbverlauf 461 , Fenster 443 , Figur fllen 442 , GraphicsPath-Objekt 456 , Linie 442 , Steuerelemente 599 , Text 444 Zeile , aktualisieren 322 , einfgen 322 , lschen 323 Zeilenabschlusszeichen 390 Zeilenumbruch 207 Zeit , aktuelle 217 , DateTime.Now-Methode 217 Zufallszahlen (Random-Klasse) 172 Zugriffstaste 117 Zustand , Steuerelement 570 , Thread 630

840

Bonuskapitel 1: Steuerelemente in Windows Forms

Bonuskapitel 1

Dieser Anhang befasst sich mit den Eigenschaften und Methoden jedes Windows FormsSteuerelements. Er beschreibt sogar einige Steuerelemente, die im technischen Sinne keine Windows Forms-Steuerelemente sind, sondern ihnen lediglich Untersttzung bieten.

1.1

Die Klasse Control

Alle Steuerelemente in diesem Anhang (wenn nicht anders angegeben) erben die in Tabelle B.1 beschriebenen Eigenschaften von der System.Windows.Forms.Control-Klasse. Die Control-Klasse ist die wichtigste, die man sich merken muss, da sie fr alle anderen Steuerelemente verantwortlich ist.
Eigenschaft
AccessibilityObject AccessibleDefaultActionDescription AccessibleDescription

Beschreibung Liefert das AccessibleObject-Objekt fr das Steuerelement. Die Zeichenfolge, die benutzt wird, um die Standardaktion eines Steuerelements zu beschreiben. Die Beschreibung des Steuerelements, die von Eingabehilfenclients verwendet wird. Der Name des Steuerelements, der von Eingabehilfenclients benutzt wird. Die Rolle (oder der Typ) des Steuerelements; verwendet Werte aus der AccessibleRole-Aufzhlung. Gibt an, ob das Steuerelement Drag & Drop-Daten akzeptieren kann. Gibt die Rnder des Steuerelements an, die am umgebenden Steuerelement verankert sind. Die Hintergrundfarbe des Steuerelements. Die Grafik, die im Hintergrund des Steuerelements angezeigt werden soll. Der BindingContext des Steuerelements (wird fr Datenbindung verwendet). Der Abstand zwischen dem unteren Rand des Steuerelements und dem oberen Rand seines umgebenden Steuerelements.

AccessibleName

AccessibleRole

AllowDrop AllowDropAnchor

AccessibleRoleBackColor BackgroundImage BindingContext

Bottom

Tabelle 1.1: Eigenschaften der Control-Klasse, die alle Steuerelemente erben

Bonuskapitel 1

Eigenschaft
Bounds CanFocus CanSelect Capture CausesValidation

Beschreibung Ein Rectangle, das die Gre und Position des Steuerelements darstellt. Gibt an, ob das Steuerelement den Fokus erhalten kann. Gibt an, ob das Steuerelement ausgewhlt werden kann. Gibt an, ob das Steuerelement die Maus eingefangen hat. Gibt an, ob das Steuerelement Gltigkeitsprfungen ausfhren soll, wenn es den Fokus erhlt. Ein Rectangle, das den Clientbereich des Steuerelements (nicht zwingend den gleichen wie Bounds) darstellt. Die Hhe und Breite des Clientbereichs des Steuerelements. Der Name des Unternehmens oder des Entwicklers des Steuerelements oder seiner umgebenden Anwendung. Gibt an, ob das Steuerelement oder eines seiner untergeordneten Steuerelemente den aktuellen Fokus besitzt. Ein ContextMenu-Objekt, das mit diesem Steuerelement verknpft ist. Liefert ein ControlCollection-Objekt, das alle untergeordneten Steuerelemente des angegebenen Steuerelements reprsentiert. Gibt an, ob das Steuerelement erstellt worden ist. Gibt den Typ des Mauscursors an, der benutzt wird, wenn sich der Mauszeiger ber dem Steuerelement befindet. Liefert die Datenbindungen fr das Steuerelement. Die Standardhintergrundfarbe eines Steuerelements. Die Standardschriftart eines Steuerelements. Die Standardvordergrundfarbe (normalerweise die Schriftfarbe) eines Steuerelements. Ein Rectangle-Objekt, das die Anzeigeflche des Steuerelements reprsentiert. Gibt an, ob das Steuerelement gerade freigegeben wird.

ClientRectangle

ClientSize CompanyName

ContainsFocus

ContextMenu Controls

Created Cursor

DataBindings DefaultBackColor DefaultFont DefaultForeColor

DisplayRectangle

Disposing

Tabelle 1.1: Eigenschaften der Control-Klasse, die alle Steuerelemente erben (Forts.)

Bonuskapitel 1

Eigenschaft
Dock

Beschreibung Gibt an, an welchem Rand des bergeordneten Containers das Steuerelement angedockt ist. Gibt an, ob das Steuerelement auf Benutzereingaben reagieren kann. Gibt an, ob das Steuerelement den Eingabefokus besitzt. Die im Steuerelement verwendete Schriftart. Die Vordergrundfarbe (normalerweise die Schriftfarbe) des Steuerelements. Liefert den Fensterhandle, an den das Steuerelement gebunden ist. Gibt an, ob das Steuerelement untergeordnete Steuerelemente enthlt. Die Hhe des Steuerelements. Der Editormodus fr die Eingabemethode des Steuerelements (erlaubt es Ihnen, fremdsprachige Zeichen und Symbole auf einer Standardtastatur einzugeben). Gibt an, dass der Aufrufer dieses Steuerelements nicht direkt mit ihm interagieren kann, weil es sich in einem anderen Thread befindet (anders ausgedrckt: ein Invoke-Methodenaufruf ist erforderlich). Gibt an, ob das Steuerelement fr Eingabehilfenclients sichtbar ist. Gibt an, ob das Steuerelement bereits freigegeben wurde. Gibt an, ob der Handle des Steuerelements bereits erzeugt worden ist (normalerweise wenn das Steuerelement am Bildschirm angezeigt wird). Der linke Rand des Steuerelements in Bildpunkten, bezogen auf den linken Rand des umgebenden Steuerelements. Ein Point-Objekt, das die obere linke Ecke des Steuerelements festlegt, bezogen auf die obere linke Ecke seines umgebenden Steuerelements. Gibt an, welche (falls berhaupt) der Modifizierertasten ((), (Strg) oder (Alt)) gedrckt worden sind, als das Steuerelement aktiviert wurde. Gibt an, welche der Maustasten gedrckt wurde, als das Steuerelement aktiviert wurde. Gibt die Position des Mauszeigers an.

Enabled Focused Font ForeColor

Handle HasChildren Height ImeMode

InvokeRequired

IsAccessible IsDisposed IsHandleCreated

Left

Location

ModifierKeys

MouseButtons

MousePosition

Tabelle 1.1: Eigenschaften der Control-Klasse, die alle Steuerelemente erben (Forts.)

Bonuskapitel 1

Eigenschaft
Name Parent

Beschreibung Der Name des Steuerelements. Liefert den bergeordneten Container des Steuerelements in der UI-Hierarchie. Der Produktname der Assembly, die das Steuerelement enthlt. Die Version der Assembly, die das Steuerelement enthlt. Gibt an, ob das Steuerelement gerade seinen Handle erneuert. Der Fensterbereich, der dem Steuerelement zugeordnet ist. Der Abstand zwischen dem rechten Rand des Steuerelements und dem linken Rand seines umgebenden Steuerelements. Gibt an, ob das Steuerelement Sprachen akzeptieren soll, die von rechts nach links geschrieben werden. Legt die Site des Steuerelements fest. Ein Size-Objekt, das Hhe und Breite des Steuerelements festlegt. Die Aktivierreihenfolge dieses Steuerelements in seinem umgebenden Steuerelement. Gibt an, ob Benutzer diesem Steuerelement den Fokus verleihen knnen, indem sie die (_)-Taste drcken. Das Objekt, das Daten ber dieses Steuerelement enthlt. Der Text, der mit diesem Steuerelement verknpft ist (hat oft eine andere Bedeutung auf unterschiedlichen Steuerelementen). Die y-Koordinate des oberen Randes des Steuerelements, angegeben in Bildpunkten. Gibt das oberste umgebende Steuerelement des aktuellen Steuerelements an. Legt fest, ob das Steuerelement auf der Seite wiedergegeben werden soll. Die Breite des Steuerelements in Bildpunkten.

ProductName ProductVersion RecreatingHandle Region Right

RightToLeft

Site Size TabIndex

TabStop

Tag Text

Top

TopLevelControl

Visible Width

Tabelle 1.1: Eigenschaften der Control-Klasse, die alle Steuerelemente erben (Forts.)

Bonuskapitel 1

Tabelle B.2 fhrt die Methoden auf, die allen Steuerelemente gemeinsam sind, da sie ja vom Control-Objekt geerbt wurden.
Methode
BeginInvoke BringToFront

Beschreibung Fhrt einen Delegaten asynchron aus. Bringt das Steuerelement an den Anfang der z-Reihenfolge in seinem bergeordneten Steuerelement. Gibt an, ob das aktuelle Steuerelement das angegebene Steuerelement enthlt. Erzwingt das Erstellen des Steuerelements. Erzeugt ein Graphics-Objekt, das auf dem Steuerelement basiert. Beginnt eine Drag & Drop-Operation. Gibt die gelieferten Werte der asynchronen Operation zurck, die mit BeginInvoke gestartet wurde.

Contains CreateControl CreateGraphics DoDragDrop EndInvoke

FindForm Focus FromChildHandle FromHandle GetChildAtPoint

Liefert das Formular, auf dem sich das Steuerelement befindet. Setzt den Eingabefokus auf das Steuerelement. Liefert das Steuerelement, das den angegebenen Handle enthlt. Liefert das Steuerelement vom angegebenen Handle. Liefert das untergeordnete Steuerelement, das sich am angegebenen Punkt befindet. Liefert das umgebende Steuerelement des Steuerelements. Liefert das nchste Steuerelement in der Aktivierreihenfolge des umgebenden Steuerelements. Verbirgt das Steuerelement vor dem Benutzer (man beachte, dass dies nicht notwendigerweise die Funktionsweise des Steuerelements beeintrchtigt). Zwingt das Steuerelement dazu, einen bestimmten Bereich neu zu zeichnen. Fhrt einen Delegaten auf dem Erstellungsthread des Steuerelements aus. Bestimmt, ob das angegebene Zeichen das mnemonische Zeichen ist, das mit dem Steuerelement verknpft ist.

GetContainerControl GetNextControl

Hide

Invalidate Invoke IsMnemonic

Tabelle 1.2: Methoden der Control-Klasse, die alle Steuerelemente erben

Bonuskapitel 1

Methode
PerformLayout

Beschreibung Zwingt das Steuerelement dazu, auf seine untergeordneten Steuerelemente Layoutregeln anzuwenden. Konvertiert den angegebenen Bildschirmpunkt in Clientkoordinaten. Konvertiert den angegebenen Clientpunkt in Bildschirmkoordinaten. Veranlasst das Steuerelement dazu, Eingabemeldungen vorab zu verarbeiten, bevor sie an die Standardfunktionen weitergeleitet werden. Berechnet Gre und Position des Bildschirmrechtecks in Clientkoordinaten. Berechnet Gre und Position des Clientrechtecks in Bildschirmkoordinaten. Zwingt das Steuerelement dazu, sich selbst sowie alle untergeordneten Steuerelemente neu zu zeichnen. Setzt die BackColor-Eigenschaft auf ihren Standardwert zurck. Setzt die DataBindings-Eigenschaft auf ihren Standardwert zurck. Setzt die Cursor-Eigenschaft auf ihren Standardwert zurck. Setzt die Font-Eigenschaft auf ihren Standardwert zurck. Setzt die ForeColor-Eigenschaft auf ihren Standardwert zurck. Setzt die ImeMode-Eigenschaft auf ihren Standardwert zurck. Setzt die RightToLeft-Eigenschaft auf ihren Standardwert zurck. Setzt die Text-Eigenschaft auf ihren Standardwert zurck. Nimmt normale Layoutregeln nach einem Aufruf von SuspendLayout wieder auf. Skaliert das Steuerelement und alle untergeordneten Steuerelemente. Aktiviert ein Steuerelement. Aktiviert das nchste Steuerelement in der Aktivierreihenfolge des umgebenden Steuerelements. Sendet das Steuerelement an das Ende der z-Reihenfolge. Setzt die Grenzen des Steuerelements fest.

PointToClient PointToScreen PreProcessMessage RectangleToClient RectangleToScreen Refresh

ResetBackColor ResetBindings ResetCursor ResetFont ResetForeColor ResetImeMode ResetRightToLeft ResetText ResumeLayout

Scale Select SelectNextControl SendToBack SetBounds

Tabelle 1.2: Methoden der Control-Klasse, die alle Steuerelemente erben (Forts.)

Bonuskapitel 1

Methode
Show SuspendLayout

Beschreibung Zeigt das Steuerelement dem Benutzer an. Setzt vorbergehend die Layoutregeln fr das Steuerelement aus; nimmt sie mit der ResumeLayout-Methode wieder auf. Liefert eine Zeichenfolge, die das Steuerelement reprsentiert. Veranlasst das Steuerelement dazu, in seinem Clientbereich ungltige Bereiche zu zeichnen.

ToString Update

Tabelle 1.2: Methoden der Control-Klasse, die alle Steuerelemente erben (Forts.)

Tabelle B.3 fhrt die Ereignisse auf, die alle Windows Forms-Steuerelemente miteinander teilen, da sie vom Control-Objekt geerbt werden.
Ereignis
BackColorChanged BackgroundImageChanged BindingContextChanged CausesValidationChanged ChangeUICues

Beschreibung Wird ausgelst, wenn sich die BackColor-Eigenschaft ndert. Wird ausgelst, wenn sich die BackgroundImage-Eigenschaft ndert. Wird ausgelst, wenn sich die BindingContext-Eigenschaft ndert. Wird ausgelst, wenn sich die CausesValidation-Eigenschaft ndert. Wird ausgelst, wenn sich der Fokus-Cue oder der Tastatur-Cue der Benutzeroberflche ndert. Wird ausgelst, wenn das Steuerelement angeklickt wird. Wird ausgelst, wenn sich die ContextMenu-Eigenschaft ndert. Wird ausgelst, wenn ein Steuerelement der Controls-Auflistung dieses Steuerelements hinzugefgt wird. Wird ausgelst, wenn ein Steuerelement aus der Controls-Auflistung dieses Steuerelements entfernt wird. Wird ausgelst, wenn sich die Cursor-Eigenschaft ndert. Wird ausgelst, wenn sich die Dock-Eigenschaft ndert. Wird ausgelst, wenn auf das Steuerelement doppelgeklickt wird.

Click ContextMenuChanged ControlAdded

ControlRemoved

CursorChanged DockChanged DoubleClick

Tabelle 1.3: Ereignisse der Control-Klasse, die alle Steuerelemente erben

Bonuskapitel 1

Ereignis
DragDrop DragEnter

Beschreibung Wird ausgelst, wenn eine Drag & Drop-Operation beendet wird. Wird ausgelst, wenn ein Objekt in die Grenzen des Steuerelements gezogen wird. Wird ausgelst, wenn ein Objekt ber die Grenzen des Steuerelements hinausgezogen wird. Wird ausgelst, wenn ein Objekt ber das Steuerelement gezogen wird. Wird ausgelst, wenn sich die Enabled-Eigenschaft ndert. Wird ausgelst, wenn das Steuerelement betreten wird. Wird ausgelst, wenn sich die Font-Eigenschaft ndert. Wird ausgelst, wenn sich die ForeColor-Eigenschaft ndert. Tritt whrend eines Ziehvorgangs auf. Wird ausgelst, wenn das Steuerelement den Eingabefokus erhlt. Wird ausgelst, nachdem fr das Steuerelement ein Handle erzeugt wurde. Wird ausgelst, wenn der Handle des Steuerelements gerade gelscht wird. Wird ausgelst, wenn der Benutzer Hilfe zu einem Steuerelement anfordert. Wird ausgelst, wenn sich die ImeMode-Eigenschaft ndert. Wird ausgelst, wenn die grafische Oberflche eines Steuerelements erfordert, dass sie neu gezeichnet wird. Wird ausgelst, wenn eine Taste gedrckt wird, whrend das Steuerelement den Fokus hat. Tritt sofort nach dem KeyDown-Ereignis auf. Tritt sofort nach dem KeyUp-Ereignis auf. Wird ausgelst, falls ein Steuerelement seine untergeordneten Steuerelemente neu anordnen sollte. Wird ausgelst, wenn das Steuerelement den Eingabefokus verliert. Wird ausgelst, wenn sich die Location-Eigenschaft ndert. Wird ausgelst, wenn das Steuerelement den Fokus verliert.

DragLeave

DragOver EnabledChanged Enter FontChanged ForeColorChanged GiveFeedback GotFocus HandleCreated HandleDestroyed HelpRequested ImeModeChanged Invalidated

KeyDown

KeyPress KeyUp Layout

Leave LocationChanged LostFocus

Tabelle 1.3: Ereignisse der Control-Klasse, die alle Steuerelemente erben (Forts.)

Bonuskapitel 1

Ereignis
MouseDown MouseEnter

Beschreibung Wird ausgelst, wenn eine Maustaste ber dem Steuerelement gedrckt wird. Wird ausgelst, wenn der Mauszeiger in den Bereich des Steuerelements eintritt. Wird ausgelst, wenn der Mauszeiger ber dem Steuerelement schwebt. Wird ausgelst, wenn der Mauszeiger das Steuerelement wieder verlsst. Wird ausgelst, wenn der Mauszeiger ber dem Steuerelement bewegt wird. Wird ausgelst, wenn der Mauszeiger sich ber einem Steuerelement befindet und eine Maustaste losgelassen wird (man beachte, dass die Maustaste nicht notwendigerweise ber der Schaltflche gedrckt worden sein muss). Wird ausgelst, wenn sich das Mausrad bewegt, whrend das Steuerelement den Fokus hat. Wird ausgelst, wenn das Steuerelement verschoben wird. Wird ausgelst, wenn das Steuerelement neu gezeichnet wird. Wird ausgelst, wenn sich die Parent-Eigenschaft ndert. Wird ausgelst, wenn die AccessibleObject-Eigenschaft Eingabehilfenclients Hilfe gewhrt. Tritt whrend einer Drag & Drop-Operation auf und erlaubt es der Quelle des Vorgangs zu bestimmen, ob die Drag & Drop-Operation abgebrochen werden soll. Wird ausgelst, wenn das Steuerelement in seiner Gre verndert wird. Wird ausgelst, wenn sich die RightToLeft-Eigenschaft ndert. Wird ausgelst, wenn sich die Size-Eigenschaft ndert. Wird ausgelst, wenn sich das Layout des Steuerelements ndert. Wird ausgelst, wenn sich die Systemfarben ndern. Wird ausgelst, wenn sich die TabIndex-Eigenschaft ndert. Wird ausgelst, wenn sich die TabStop-Eigenschaft ndert. Wird ausgelst, wenn sich die Text-Eigenschaft ndert.

MouseHover MouseLeave MouseMove MouseUp

MouseWheel

Move Paint ParentChanged QueryAccessibility Help QueryContinueDrag

Resize RightToLeftChanged SizeChanged StyleChanged SystemColorsChanged TabIndexChanged TabStopChanged TextChanged

Tabelle 1.3: Ereignisse der Control-Klasse, die alle Steuerelemente erben (Forts.)

10

Bonuskapitel 1

Ereignis
Validated Validating VisibleChanged

Beschreibung Wird ausgelst, wenn das Steuerelement die berprfung abgeschlossen hat. Wird ausgelst, wenn das Steuerelement mit der berprfung beginnt. Wird ausgelst, wenn sich die Visible-Eigenschaft ndert.

Tabelle 1.3: Ereignisse der Control-Klasse, die alle Steuerelemente erben (Forts.)

1.2

Die Steuerelemente Button, CheckBox und RadioButton

Tabelle B.4 fhrt die Eigenschaften der ButtonBase-Klasse auf, die von den Button-, CheckBox- und RadioButton-Steuerelementen geerbt werden.
Eigenschaft
FlatStyle

Beschreibung Gibt die flache Darstellungsform des Steuerelements an; verwendet Werte aus der FlatStyle-Aufzhlung. Die Grafik, die im Steuerelement angezeigt wird. Die Ausrichtung der Grafik, die im Steuerelement angezeigt wird. Der Grafiklistenindex der aktuell im Steuerelement angezeigten Grafik. Ein ImageList-Objekt, das ein Array von Grafiken enthlt, die mit dem Steuerelement verknpft sind. Die Ausrichtung des im Steuerelement angezeigten Textes.

Image ImageAlign ImageIndex ImageList

TextAlign

Tabelle 1.4: Eigenschaften des ButtonBase-Steuerelements

Das Steuerelement Button


Das Button-Steuerelement reprsentiert eine anklickbare Schaltflche in einem Windows Form. Tabelle B.5 fhrt Eigenschaft und Methode der Button-Klasse auf.

11

Bonuskapitel 1

Eigenschaft
DialogResult

Beschreibung Ein Wert, der dem umgebenden Steuerelement geliefert wird, wenn die Schaltflche angeklickt wird. Beschreibung Erzeugt ein Click-Ereignis fr die Schaltflche.

Methode
PerformClick

Tabelle 1.5: Eigenschaft und Methode der Button-Klasse

Das Steuerelement CheckBox


Das CheckBox-Steuerelement reprsentiert ein Feld, das sich ein- oder ausschalten lsst, um die jeweilige Option zu aktivieren bzw. zu deaktivieren. Tabelle B.6 fhrt die Eigenschaften und Ereignisse der CheckBox-Klasse auf.
Eigenschaft
Appearance

Beschreibung Verwendet einen Wert aus der Appearance-Aufzhlung, um die Darstellung des CheckBox-Steuerelements festzulegen. Gibt an, ob sich die Darstellung des CheckBox-Steuerelements ndert, sobald es angeklickt wurde. Liefert oder setzt einen Wert, der die horizontale und die vertikale Ausrichtung von Kontrollkstchen in einem CheckBox-Steuerelement angibt. Gibt an, ob das CheckBox-Steuerelement mit Hkchen versehen (aktiviert) ist. Gibt den Zustand des Steuerelements an; eine Eigenschaft aus der CheckState-Aufzhlung.

AutoCheck

CheckAlign

Checked CheckState

ThreeState

Gibt an, ob das CheckBox-Steuerelement drei Zustnde statt nur zwei zulsst. Beschreibung Wird ausgelst, wenn sich die Appearance-Eigenschaft ndert. Wird ausgelst, wenn sich die Checked-Eigenschaft ndert. Wird ausgelst, wenn sich die CheckState-Eigenschaft ndert.

Ereignis
AppearanceChanged CheckedChanged CheckStateChanged

Tabelle 1.6: Eigenschaften und Ereignisse des CheckBox-Steuerelements

12

Bonuskapitel 1

Das Steuerelement RadioButton


Das RadioButton-Steuerelement reprsentiert ein Feld, das sich auswhlen lsst. RadioButton-Steuerelemente sind normalerweise gruppiert, damit der Benutzer eine Auswahl unter mehreren treffen kann. Tabelle B.7 fhrt die Eigenschaften, die Methode und die Ereignisse des RadioButton-Steuerelements auf.
Eigenschaft
Appearance

Beschreibung Verwendet einen Wert aus der Appearance-Aufzhlung, um die Darstellung des Steuerelements festzulegen. Gibt an, ob sich das Aussehen des Steuerelements ndert, wenn es angeklickt wird. Liefert oder setzt einen Wert, der die horizontale und die vertikale Ausrichtung von Kontrollkstchen in einem RadioButton-Steuerelement angibt. Gibt an, ob das Steuerelement aktiviert ist. Beschreibung Erzeugt ein Click-Ereignis fr das Steuerelement. Beschreibung Wird ausgelst, wenn sich die Appearance-Eigenschaft ndert. Wird ausgelst, wenn sich die Checked Eigenschaft ndert.

AutoCheck

CheckAlign

Checked

Methode
PerformClick

Ereignis
AppearanceChanged CheckedChanged

Tabelle 1.7: Eigenschaften, Methode und Ereignisse des RadioButton-Steuerelements

1.3

Die Steuerelemente ComboBox, ListBox und CheckedListBox

Die ListControl-Klasse stellt die bergeordnete Klasse der ComboBox-, ListBox- und CheckedListBox-Steuerelemente dar. Alle Mitglieder in ListControl werden von diesen drei Steuerelementen geerbt. Tabelle B.8 fhrt die Eigenschaften, die Methode und die Ereignisse von ListControl auf.

13

Bonuskapitel 1

Eigenschaft
DataSource DisplayMember

Beschreibung Die Datenquelle fr dieses Steuerelement. Ein Zeichenfolge, die reprsentiert, welche Eigenschaft der Datenquelle man in dem Steuerelement anzeigen mchte. Der nullbasierte Index des aktuell ausgewhlten Elements im Steuerelement. Der Wert der Mitgliedereigenschaft, der durch die ValueMember-Eigenschaft angegeben wird. Ein Zeichenfolge, die die Eigenschaft der Datenquelle festlegt, aus der der Wert abgerufen wird. Beschreibung Liefert die Textdarstellung des ausgewhlten Elements. Beschreibung Wird ausgelst, wenn sich die DataSource-Eigenschaft ndert. Wird ausgelst, wenn sich die DisplayMember-Eigenschaft ndert. Wird ausgelst, wenn sich die SelectedValue-Eigenschaft ndert.

SelectedIndex SelectedValue

ValueMember

Methode
GetItemText

Ereignis
DataSourceChanged DisplayMemberChanged SelectedValueChanged

ValueMemberChanged Wird ausgelst, wenn sich die ValueMember-Eigenschaft ndert.

Tabelle 1.8: Eigenschaften, Methode und Ereignisse von ListControl

Das Steuerelement ComboBox


Das ComboBox-Steuerelement reprsentiert die Kombination eines ListBox-und eines TextBox-Steuerelements. Damit knnen Benutzer aus einer Liste von Elementen auswhlen und Text eingeben oder angezeigten Text ndern. Tabelle B.9 fhrt die Eigenschaften, Methoden und Ereignisse des ComboBox-Steuerelements auf.
Eigenschaft
DrawMode

Beschreibung Gibt an, ob Ihr Code oder das Betriebssystem die Elemente in dem Steuerelement zeichnen soll; verwendet Werte aus der DrawMode-Aufzhlung.

Tabelle 1.9: Eigenschaften, Methoden und Ereignisse von ComboBox

14

Bonuskapitel 1

Eigenschaft
DropDownStyle

Beschreibung Das Aussehen des Steuerelements; benutzt Werte aus der ComboBoxStyle-Aufzhlung. Die Breite des Dropdownteils dieses Steuerelements. Gibt an, ob das Steuerelement seinen Dropdownteil anzeigt. Gibt an, ob das Steuerelement seine Gre ndern soll, um so zu vermeiden, dass Elemente nur teilweise angezeigt werden. Gibt die Hhe eines Elements im Steuerelement an. Eine Auflistung von Elementen im Steuerelement. Die hchste Anzahl von Elementen, die sich im Dropdownteil des Steuerelements anzeigen lassen. Die hchste Anzahl von Zeichen, die in den Textfeldbereich des Steuerelements passen. Die bevorzugte Hhe des Steuerelements. Der nullbasierte Index des aktuell ausgewhlten Elements. Das aktuell ausgewhlte Element. Der Text, der im Textfeldbereich des Steuerelements ausgewhlt ist. Die Anzahl ausgewhlter Zeichen im Textfeldbereich des Steuerelements. Der Index des am Anfang stehenden Zeichens des ausgewhlten Textes im Textfeldbereich des Steuerelements. Gibt an, ob Elemente im Steuerelement sortiert werden sollen. Beschreibung Hindert das Steuerelement daran, sich selbst jedes Mal neu zu zeichnen, wenn ein neues Element hinzugefgt wurde, bis EndUpdate aufgerufen wird. Beginnt erneut mit dem Zeichnen des Steuerelements, nachdem BeginUpdate aufgerufen wurde. Sucht das erste Element im Steuerelement, das mit der angegebenen Zeichenfolge beginnt.

DropDownWidth DroppedDown IntegralHeight

ItemHeight Items MaxDropDownItems

MaxLength

PreferredHeight SelectedIndex SelectedItem SelectedText SelectionLength SelectionStart

Sorted

Methode
BeginUpdate

EndUpdate

FindString

Tabelle 1.9: Eigenschaften, Methoden und Ereignisse von ComboBox (Forts.)

15

Bonuskapitel 1

Methode
FindStringExact

Beschreibung Sucht das erste Element im Steuerelement, das genau mit der angegebenen Zeichenfolge bereinstimmt. Liefert die Hhe eines Elements im Steuerelement. Markiert einen Textbereich. Markiert den vollstndigen Text im Textfeldbereich des Steuerelements. Beschreibung Wird ausgelst, wenn sich ein visueller Aspekt eines vom Besitzer gezeichneten Steuerelements ndert. Wird ausgelst, wenn der Dropdownteil des Steuerelements gezeigt wird. Wird ausgelst, wenn sich die DropDownStyle-Eigenschaft ndert. Tritt jedes Mal auf, wenn ein vom Besitzer gezeichnetes Steuerelement ein Element anzeigen muss. Wird ausgelst, wenn sich die SelectedIndex-Eigenschaft ndert. Wird ausgelst, wenn sich das ausgewhlte Element gendert hat und die nderung gespeichert worden ist.

GetItemHeight Select SelectAll

Ereignis
DrawItem

DropDown DropDownStyleChanged MeasureItem

SelectedIndexChanged SelectionChangeCommitted

Tabelle 1.9: Eigenschaften, Methoden und Ereignisse von ComboBox (Forts.)

Das Steuerelement ListBox


Die ListBox reprsentiert eine Liste von auswhlbaren Elementen. Tabelle B.10 fhrt die Eigenschaften, Methoden und Ereignisse des ListBox-Steuerelements auf.
Eigenschaft
ColumnWidth DrawMode

Beschreibung Die Breite der Spalten in einer mehrspaltigen ListBox. Gibt an, ob Ihr Code oder das Betriebssystem die Elemente im Steuerelement zeichnet; verwendet Werte aus der DrawMode-Aufzhlung. Die Breite, bis zu der sich ein Bildlauf mit der horizontalen Bildlaufleiste durchfhren lsst.

HorizontalExtent

Tabelle 1.10: Eigenschaften, Methoden und Ereignisse des ListBox-Steuerelements

16

Bonuskapitel 1

Eigenschaft
HorizontalScrollbar IntegralHeight

Beschreibung Gibt an, ob eine horizontale Bildlaufleiste im Steuerelement angezeigt werden soll. Gibt an, ob das Steuerelement seine Gre ndern soll, um so zu vermeiden, dass Elemente nur teilweise angezeigt werden. Gibt die Hhe eines Elements im Steuerelement an. Ein Auflistung von Elementen im Steuerelement. Gibt an, ob das Steuerelement so viele Spalten anlegen soll wie ntig sind, um einen vertikalen Bildlauf durch die Elemente zu vermeiden. Die bevorzugte Hhe des Steuerelements. Gibt an, ob die vertikale Bildlaufleiste immer angezeigt wird. Der nullbasierte Index des aktuell ausgewhlten Elements. Eine Auflistung, die die Indizes ausgewhlter Elemente im Steuerelement enthlt. Das aktuell ausgewhlte Element. Ein Auflistung von ausgewhlten Elementen. Gibt an, wie Elemente im Steuerelement ausgewhlt werden knnen; verwendet Werte aus der SelectionMode-Aufzhlung. Gibt an, ob Elemente im Steuerelement sortiert werden sollen. Liefert den Index des ersten Elements im Steuerelement. Gibt an, ob das Steuerelement Tabstoppzeichen in jedem Element der Liste anzeigen kann. Beschreibung Hindert das Steuerelement daran, sich selbst jedes Mal neu zu zeichnen, wenn ein neues Element hinzugefgt wurde, bis EndUpdate aufgerufen wird. Hebt die Auswahl aller Elemente im Steuerelement auf. Beginnt erneut mit dem Zeichnen des Steuerelements, nachdem BeginUpdate aufgerufen wurde.

ItemHeight Items MultiColumn

PreferredHeight ScrollAlwaysVisible SelectedIndex SelectedIndices

SelectedItem SelectedItems SelectionMode

Sorted TopIndex UseTabStops

Methode
BeginUpdate

ClearSelected EndUpdate

Tabelle 1.10: Eigenschaften, Methoden und Ereignisse des ListBox-Steuerelements (Forts.)

17

Bonuskapitel 1

Methode
FindString

Beschreibung Sucht das erste Element im Steuerelement, das mit der angegebenen Zeichenfolge beginnt. Sucht das erste Element im Steuerelement, das genau mit der angegebenen Zeichenfolge bereinstimmt. Liefert die Hhe eines Elements im Steuerelement. Liefert das umschlieende Rechteck fr ein Element im Steuerelement. Gibt an, ob das angegebene Element ausgewhlt ist. Liefert den nullbasierten Index desjenigen Elements, das dem angegebenen Punkt am nchsten liegt. Whlt ein angegebenes Element im Steuerelement aus oder hebt dessen Auswahl auf. Beschreibung Wird ausgelst, wenn sich ein visueller Aspekt eines vom Besitzer gezeichneten Steuerelements ndert. Tritt jedes Mal auf, wenn ein vom Besitzer gezeichnetes Steuerelement ein Element anzeigen muss. Wird ausgelst, wenn sich die SelectedIndex-Eigenschaft ndert.

FindStringExact

GetItemHeight GetItemRectangle GetSelected IndexFromPoint

SetSelected

Ereignis
DrawItem

MeasureItem

SelectedIndexChanged

Tabelle 1.10: Eigenschaften, Methoden und Ereignisse des ListBox-Steuerelements (Forts.)

Das Steuerelement CheckedListBox


Das CheckedListBox-Steuerelement reprsentiert ein ListBox-Steuerelement, in dem sich neben jedem Element in der Liste Kontrollkstchen befinden. Das direkte bergeordnete Element der CheckedListBox ist das ListBox-Steuerelement, so dass es dessen gesamte Mitglieder erbt, zustzlich zu denen im ListControl-Steuerelement. Tabelle B.11 fhrt Eigenschaften, Methoden und Ereignis des CheckedListBox-Steuerelements auf.

18

Bonuskapitel 1

Eigenschaft
CheckedIndices CheckedItems CheckOnClick

Beschreibung Die Indizes aller aktivierten Elemente in diesem Steuerelement. Eine Auflistung der aktivierten Elemente in diesem Steuerelement. Gibt an, ob das Kontrollkstchen fr ein Element aktiviert bzw. deaktiviert werden soll, wenn das Element angeklickt wird. Gibt an, ob die Kontrollkstchen mit einem 3D-Aussehen angezeigt werden sollen. Beschreibung Gibt an, ob das angegebene Element aktiviert ist. Liefert einen Wert, der den aktuellen Zustand des Kontrollkstchens fr ein Element angibt. Versieht das Element am angegebenen Index mit einem Hkchen. Legt den Aktivierungszustand des Elements am angegebenen Index fest. Beschreibung Wird ausgelst, wenn ein Element aktiviert wird.

ThreeDCheckBoxes

Methode
GetItemChecked GetItemCheckState

SetItemChecked SetItemCheckState

Ereignis
ItemCheck

Tabelle 1.11: Eigenschaften, Methoden und Ereignis von CheckedListBox

1.4

Standarddialogfeld-Steuerelemente

Standarddialogfeld-Steuerelemente reprsentieren Dialogfelder, die Standardfunktionalitt des Betriebssystems zur Verfgung stellen, darunter das ffnen und Speichern von Dateien sowie die Auswahl von Schriftarten. Jedes Standarddialogfeld ist von der System.Windows.Forms.CommonDialog-Klasse abgeleitet, deren Methoden und Ereignis in Tabelle B.12 aufgefhrt sind.
Methode
Reset ShowDialog

Beschreibung Setzt die Eigenschaften des Steuerelements auf die Standardwerte. Zeigt das Dialogfeld an. Beschreibung Wird ausgelst, wenn der Benutzer auf die HILFE-Schaltflche eines Standarddialogfeld-Steuerelements klickt.

Ereignis
HelpRequest

Tabelle 1.12: Methoden und Ereignis von CommonDialog

19

Bonuskapitel 1

Das Steuerelement ColorDialog


Das ColorDialog-Steuerelement ist ein Dialogfeld, mit dem der Benutzer aus einer Farbpalette auswhlen kann; zustzliche Steuerelemente gestatten das Definieren eigener Farben. Tabelle B.13 fhrt die Eigenschaften des ColorDialog-Steuerelements auf.
Eigenschaft
AllowFullOpen

Beschreibung Gibt an, ob der Benutzer das Dialogfeld nutzen kann, um eigene Farben zu definieren. Gibt an, ob das Dialogfeld alle verfgbaren Farben anzeigt. Die vom Benutzer ausgewhlte Farbe. Liefert oder setzt die im Dialogfeld angezeigten benutzerdefinierten Farben. Gibt an, ob die zur Definition benutzerdefinierter Farben verwendeten Steuerelemente angezeigt werden, whrend das Dialogfeld geffnet ist. Gibt an, ob die HILFE-Schaltflche im Dialogfeld angezeigt wird. Gibt an, ob das Dialogfeld Benutzern nur die Auswahl von Volltonfarben gestattet.

AnyColor Color CustomColors FullOpen

ShowHelp SolidColorOnly

Tabelle 1.13: Eigenschaften von ColorDialog

Dateidialogfelder
Die zwei fr die Dateiverarbeitung zustndigen Standarddialogfeld-Steuerelemente (OpenFileDialog und SaveFileDialog) gleichen einander. Sie sind beide von der FileDialogKlasse abgeleitet, die in Tabelle B.14 zu sehen ist.
Eigenschaft
AddExtension

Beschreibung Gibt an, ob das Dialogfeld einem Dateinamen automatisch eine Erweiterung hinzufgen soll, falls der Benutzer dies vergisst. Gibt an, ob das Dialogfeld prfen soll, ob eine vom Benutzer angegebene Datei existiert. Gibt an, ob das Dialogfeld prfen soll, ob ein vom Benutzer angegebenes Verzeichnis existiert.

CheckFileExists

CheckPathExists

Tabelle 1.14: Eigenschaften und Ereignis von FileDialog

20

Bonuskapitel 1

Eigenschaft
DefaultExt DereferenceLinks

Beschreibung Die Standarderweiterung fr Dateinamen. Gibt an, ob das Dialogfeld Verknpfungen (.lnk-Dateien) verfolgen soll oder nicht. Liefert oder setzt eine Zeichenfolge, die den im Dialogfeld ausgewhlten Dateinamen enthlt. Die Dateinamen aller ausgewhlten Dateien im Dialogfeld. Die Filterzeichenfolge, die festlegt, welche Dateityperweiterungen der Benutzer sehen kann. Der Index des aktuell ausgewhlten Filters. Das Startverzeichnis des Dialogfeldes. Gibt an, ob das Dialogfeld das Originalverzeichnis nach dem Schlieen wiederherstellen soll, falls der Benutzer es gendert hat. Gibt an, ob die HILFE-Schaltflche im Dialogfeld angezeigt wird. Der Titel der Dialogfeldes. Gibt an, ob das Dialogfeld nur gltige Win32-Dateinamen akzeptiert. Beschreibung Wird ausgelst, wenn der Benutzer auf die FFNEN- oder SPEICHERN-Schaltflche klickt.

FileName

FileNames Filter

FilterIndex InitialDirectory RestoreDirectory

ShowHelp Title ValidateNames

Ereignis
FileOk

Tabelle 1.14: Eigenschaften und Ereignis von FileDialog (Forts.)

OpenFileDialog
OpenFileDialog ermglicht es dem Benutzer, eine zu ffnende Datei auszuwhlen. Tabelle B.15 fhrt die Eigenschaften und die Methode des OpenFileDialog-Steuerelements auf. Eigenschaft
MultiSelect ReadOnlyChecked ShowReadOnly

Beschreibung Gibt an, ob der Benutzer mehrere Dateien auswhlen kann. Gibt an, ob das Kontrollkstchen SCHREIBGESCHTZT aktiviert ist. Gibt an, ob das Kontrollkstchen SCHREIBGESCHTZT angezeigt werden soll.

Tabelle 1.15: Eigenschaften und Methode von OpenFileDialog

21

Bonuskapitel 1

Methode
OpenFile

Beschreibung ffnet die vom Benutzer ausgewhlte Datei schreibgeschtzt.

Tabelle 1.15: Eigenschaften und Methode von OpenFileDialog (Forts.)

SaveFileDialog
SaveFileDialog gestattet dem Benutzer, eine Datei zu speichern, wobei er eine vorhandene

Datei berschreiben oder einen neuen Dateinamen benutzen kann. Tabelle B.16 fhrt die Eigenschaften und die Methode des SaveFileDialog-Steuerelements auf.
Eigenschaft
CreatePrompt

Beschreibung Gibt an, ob das Dialogfeld den Benutzer fragen soll, ob eine Datei anzulegen ist, falls es sie noch nicht gibt. Gibt an, ob das Dialogfeld den Benutzer fragen soll, ob eine Datei zu berschreiben ist, falls es sie bereits gibt. Beschreibung ffnet die vom Benutzer ausgewhlte Datei schreibgeschtzt.

OverwritePrompt

Methode
OpenFile

Tabelle 1.16: Eigenschaften und Methode von SaveFileDialog

FontDialog
FontDialog ermglicht es dem Benutzer, eine Schriftart und deren Eigenschaften (Gre, Auszeichnung usw.) fr den Einsatz in einer Anwendung auszuwhlen. Tabelle B.17 fhrt Eigenschaften und Ereignis des FontDialog-Steuerelements auf. Eigenschaft
AllowScriptChange

Beschreibung Gibt an, ob der Benutzer die Standardschriftart ndern kann, um einen anderen Zeichensatz anzuzeigen. Gibt an, ob das Dialogfeld GDI-Schriftartsimulationen zulsst. Gibt an, ob das Dialogfeld die Auswahl von Vektorschriften zulsst.

AllowSimulations AllowVectorFonts

AllowVerticalFonts Gibt an, ob das Dialogfeld vertikale Schriftarten zulsst. Color

Gibt die Schriftfarbe an.

Tabelle 1.17: Eigenschaften und Ereignis von FontDialog

22

Bonuskapitel 1

Eigenschaft
FixedPitchOnly Font FontMustExist

Beschreibung Gibt an, ob das Dialogfeld nur Schriftarten mit fester Zeichenbreite zulsst. Die ausgewhlte Schriftart. Gibt an, ob das Dialogfeld eine Fehlermeldung auslst, wenn die ausgewhlte Schriftart nicht existiert. Die hchste Schriftgre, die ein Benutzer auswhlen kann. Die kleinste Schriftgre, die ein Benutzer auswhlen kann. Gibt an, ob das Dialogfeld auch Nicht-OEM- und Symbolzeichenstze zulsst. Gibt an, ob das Dialogfeld ber eine BERNEHMEN-Schaltflche verfgt. Gibt an, ob das Dialogfeld dem Benutzer gestattet, eine Schriftfarbe zu whlen. Gibt an, ob das Dialogfeld dem Benutzer gestattet, Effekte wie etwa Durchstreichen, Unterstreichen und Farbe zu whlen. Gibt an, ob die HILFE-Schaltflche im Dialogfeld angezeigt wird. Beschreibung Wird ausgelst, wenn die BERNEHMEN-Schaltflche angeklickt wird.

MaxSize MinSize ScriptsOnly

ShowApply ShowColor

ShowEffects

ShowHelp

Ereignis
Apply

Tabelle 1.17: Eigenschaften und Ereignis von FontDialog (Forts.)

PageSetupDialog
Mit PageSetupDialog kann der Benutzer seitenrelevante Eigenschaften wie etwa Rnder und Papierquelle festlegen. Tabelle B.18 fhrt die Eigenschaften des PageSetupDialogSteuerelements auf.
Eigenschaft
AllowMargins

Beschreibung Gibt an, ob das Dialogfeld dem Benutzer gestattet, Werte fr die Rnder zu ndern. Gibt an, ob das Dialogfeld dem Benutzer gestattet, die Papierausrichtung (Quer- oder Hochformat) zu whlen.

AllowOrientation

Tabelle 1.18: Eigenschaften von PageSetupDialog

23

Bonuskapitel 1

Eigenschaft
AllowPaper

Beschreibung Gibt an, ob das Dialogfeld dem Benutzer gestattet, Papiergre und -quelle zu whlen. Gibt an, ob das Dialogfeld die Druckerschaltflche zeigen soll. Gibt das PrintDocument-Objekt an, von dem Seiteneinstellungen geholt werden sollen. Die kleinsten Randabstnde, die ein Benutzer angeben darf, ausgedrckt in 1/ 100 Zoll. Das zu ndernde PageSettings-Objekt. Die zu verwendenden Druckereinstellungen, wenn der Benutzer die Druckerschaltflche anklickt. Gibt an, ob die HILFE-Schaltflche im Dialogfeld angezeigt wird. Gibt an, ob das Dialogfeld die Netzwerkschaltflche anzeigen soll.

AllowPrinter Document

MinMargins

PageSettings PrinterSettings

ShowHelp ShowNetwork

Tabelle 1.18: Eigenschaften von PageSetupDialog (Forts.)

PrintDialog
Mit PrintDialog kann der Benutzer ein Dokument drucken. Tabelle B.19 fhrt die Eigenschaften des PrintDialog-Steuerelements auf.
Eigenschaft
AllowPrintToFile

Beschreibung Gibt an, ob das Kontrollkstchen AUSGABE IN DATEI UMLEITEN ANgezeigt wird. Gibt an, ob die VONBISSEITE-Option aktiviert ist. Gibt an, ob die SEITEN-Option aktiviert ist. Gibt das PrintDocument-Objekt an, von dem Seiteneinstellungen geholt werden sollen. Die Druckereinstellungen, von denen Einstellungen abzurufen sind. Gibt an, ob das Kontrollkstchen AUSGABE IN DATEI UMLEITEN aktiviert ist. Gibt an, ob die HILFE-Schaltflche im Dialogfeld angezeigt wird. Gibt an, ob das Dialogfeld die Netzwerkschaltflche anzeigen soll.

AllowSelection AllowSomePages Document

PrinterSettings PrintToFile ShowHelp ShowNetwork

Tabelle 1.19: Eigenschaften von PrintDialog

24

Bonuskapitel 1

1.5

Mens

Mens reprsentieren Gruppen von Befehlen, die sich ausfhren lassen, nachdem eine speisekartenhnliche Liste angezeigt wurde. Dazu gehren regulre Anwendungsmens und kontextabhngige Mens. Alle Steuerelemente in diesem Abschnitt sind von der System.Windows.Forms.Menu-Klasse abgeleitet, die in Tabelle B.20 gezeigt wird.
Eigenschaft
Handle IsParent MdiListItem

Beschreibung Der Handle fr ein vorliegendes Men. Gibt an, ob dieses Men ber Untermens verfgt. Gibt an, ob dieses Men dazu benutzt werden soll, die Fenster einer MDIAnwendung anzuzeigen. Eine Auflistung der in diesem Men enthaltenen Untermens. Beschreibung Liefert das ContextMenu, zu dem dieses Menu gehrt. Liefert das MainMenu-Steuerelement, zu dem dieses Menu gehrt. Fhrt die Menelemente eines angegebenen Mens mit dem aktuellen Men zusammen.

MenuItems

Methode
GetContextMenu GetMainMenu MergeMenu

Tabelle 1.20: Eigenschaften und Methoden von Menu

ContextMenu
Ein Kontextmen bildet ein Men, das kontextsensitiv ist. Dieses Men erscheint normalerweise, wenn der Benutzer auf die rechte Maustaste klickt, sobald sich die Maus ber einem Objekt befindet. Tabelle B.21 fhrt die Methoden und das Ereignis des ContextMenu-Steuerelements auf.
Methode
Show SourceControl

Beschreibung Zeigt das Men an der angegebenen Position an. Liefert das Steuerelement, das das Kontextmen enthlt.

Tabelle 1.21: Methode und Ereignis von ContextMenu

25

Bonuskapitel 1

Ereignis
Popup

Beschreibung Tritt direkt vor der Anzeige eines Kontextmens auf.

Tabelle 1.21: Methode und Ereignis von ContextMenu(Forts.)

MainMenu
Das MainMenu-Steuerelement reprsentiert die oberste Menstruktur eines Formulars. Es zeigt nicht selbst ein Men an, enthlt aber MenuItem-Steuerelemente, die dies tun. Tabelle B.22 fhrt die Methoden des MainMenu-Steuerelements auf.
Methode
CloneMenu GetForm

Beschreibung Erzeugt eine Kopie des MainMenu-Objekts. Liefert das Form-Objekt fr dieses Menelement.

Tabelle 1.22: Methoden von MainMenu

MenuItem
Ein MenuItem reprsentiert die eigentliche Benutzerschnittstelle fr ein Men; es ist der sichtbare Anteil des Mens, mit dem der Benutzer interagieren kann. Tabelle B.23 fhrt Eigenschaften, Methoden und Ereignisse des MenuItem-Steuerelements auf.
Eigenschaft
BarBreak

Beschreibung Gibt an, ob ein Menelement in einer neuen Zeile angezeigt werden soll (in einem umgebenden MenuItem-Objekt) oder in einer neuen Spalte (in einem ContextMenu-Objekt). Gibt an, ob ein Element in einer neuen Zeile angezeigt werden soll (in einem umgebenden MenuItem-Objekt) oder in einer neuen Spalte (in einem ContextMenu-Objekt). Gibt an, ob ein Hkchen neben dem Menelement erscheint. Gibt an, ob dieses Menelement das Standardmenelement ist. Gibt an, ob das Menelement Benutzerinteraktion zulsst.

Break

Checked DefaultItem Enabled

Tabelle 1.23: Eigenschaften, Methoden und Ereignisse von MenuItem

26

Bonuskapitel 1

Eigenschaft
Index MdiList

Beschreibung Die Position dieses Menelements in seinem bergeordneten Men. Gibt an, ob dieses Menelement dazu benutzt werden soll, die Fenster einer MDI-Anwendung anzuzeigen. Ein Wert, der die Position dieses Menelements in Bezug auf andere angibt, wenn es mit einem anderen Men zusammengefhrt wird. Gibt die Verhaltensweise dieses Menelements in Bezug auf andere an, wenn es mit einem anderen Men zusammengefhrt wird; verwendet Werte aus der MenuMerge-Aufzhlung. Das mnemonische Zeichen, das mit diesem Men verknpft ist. Gibt an, ob der Besitzer oder das Betriebssystem das Menelement zeichnet. Liefert das bergeordnete Men des aktuellen Menelements. Gibt an, ob das Menelement ein Optionsfeld statt eines Hkchens anzeigt. Die diesem Menelement zugeordnete Tastenkombination; verwendet Werte aus der ShortCut-Aufzhlung. Gibt an, ob das Menelement die zugeordnete Tastenkombination neben der Menbeschriftung anzeigen soll. Die Aufschrift, die das Menelement trgt. Gibt an, ob das Menelement fr den Benutzer sichtbar ist. Beschreibung Erzeugt eine Kopie des Menelements. Lst fr das Menelement ein Click-Ereignis aus. Lst fr das Menelement ein Select-Ereignis aus. Beschreibung Wird ausgelst, wenn ein Menelement gezeichnet werden muss und die OwnerDraw-Eigenschaft auf true gesetzt ist. Wird ausgelst, wenn ein Menelement auszumessen ist, bevor es gezeichnet wird und die OwnerDraw-Eigenschaft auf true gesetzt ist.

MergeOrder

MergeType

Mnemonic OwnerDraw Parent RadioCheck Shortcut

ShowShortcut

Text Visible

Methode
CloneMenu PerformClick PerformSelect

Ereignis
DrawItem

MeasureItem

Tabelle 1.23: Eigenschaften, Methoden und Ereignisse von MenuItem (Forts.)

27

Bonuskapitel 1

Eigenschaft
Popup Select

Beschreibung Wird ausgelst, bevor ein Menelement gezeigt wird. Wird ausgelst, wenn der Benutzer den Mauspzeiger ber einem Menelement platziert.

Tabelle 1.23: Eigenschaften, Methoden und Ereignisse von MenuItem (Forts.)

1.6

Das Steuerelement DataGrid

Dieses Steuerelement reprsentiert eine editierbare Tabelle von Daten. Tabelle B.24 fhrt die Eigenschaften, Methoden und Ereignisse des DataGrid-Steuerelements auf.
Eigenschaft
AllowNavigation

Beschreibung Gibt an, ob die Navigation zu untergeordneten Tabellen und Datasets zulssig ist. Gibt an, ob der Benutzer die Daten neu sortieren kann, indem er auf die Kopfzeile einer Spalte klickt. Die Hintergrundfarbe fr abwechselnde Zeilen. Die Hintergrundfarbe des Datenblattes. Die Hintergrundfarbe der Bereiche des Datenblattes, die auerhalb der Zeilen liegen. Der Randstil des Datenblattes; verwendet einen Wert der BorderStyle-Aufzhlung. Die Hintergrundfarbe des Beschriftungsbereichs. Die im Beschriftungsbereich verwendete Schriftart. Die Vordergrundfarbe des Beschriftungsbereichs. Der im Beschriftungsbereich angezeigte Text. Gibt an, ob der Beschriftungsbereich sichtbar ist. Gibt an, ob die Kopfzeilen jeder Spalte sichtbar sind. Liefert oder setzt die Zelle, die den Fokus hat.

AllowSorting

AlternatingBackColor BackColor BackgroundColor

BorderStyle

CaptionBackColor CaptionFont CaptionForeColor CaptionText CaptionVisible ColumnHeadersVisible CurrentCell

Tabelle 1.24: Eigenschaften, Methoden und Ereignisse von DataGrid

28

Bonuskapitel 1

Eigenschaft
CurrentRowIndex DataMember

Beschreibung Der Index der ausgewhlten Zeile. Liefert die spezifische Liste in der DataSource-Eigenschaft, die im Datenblatt angezeigt werden soll. Die Datenquelle, die das Datenblatt anzeigen soll. Der Index der ersten sichtbaren Spalte im Datenblatt. Gibt an, ob das Datenblatt mit 3D-Effekten oder flach angezeigt werden soll. Die Farbe der Datenblattlinien. Der Stil der Datenblattlinien; verwendet Werte aus der DataGridLineStyle-Aufzhlung. Die Hintergrundfarbe aller Kopfzellen von Zeilen und Spalten. Die Schriftart aller Zeilen- und Spaltenkpfe. Die Vordergrundfarbe aller Zeilen- und Spaltenkpfe. Der Wert einer angegebenen Zelle. Die Farbe des Textes, den man anklicken kann, um zu untergeordneten Tabellen zu navigieren. Die Farbe, die ein Link annimmt, wenn der Mauszeiger darber platziert wird. Die Hintergrundfarbe von bergeordneten Zeilen. Die Vordergrundfarbe von bergeordneten Zeilen Die Art und Weise, wie bergeordnete Zeilen angezeigt werden; verwendet Werte aus der Aufzhlung DataGridParentRowsLabelStyle. Gibt an, ob Zeilen der bergeordneten Tabelle sichtbar sein sollen. Die Standardbreite von Spalten, ausgedrckt in Bildpunkten. Die Standardhhe jeder Zeile. Gibt an, ob sich die Daten im Datenblatt editieren lassen. Gibt an, ob die Zeilenkpfe sichtbar sind.

DataSource FirstVisibleColumn FlatMode

GridLineColor GridLineStyle

HeaderBackColor HeaderFont HeaderForeColor Item LinkColor

LinkHoverColor

ParentRowsBackColor ParentRowsForeColor ParentRowsLabelStyle

ParentRowsVisible PreferredColumnWidth PreferredRowHeight ReadOnly RowHeadersVisible

Tabelle 1.24: Eigenschaften, Methoden und Ereignisse von DataGrid (Forts.)

29

Bonuskapitel 1

Eigenschaft
RowHeaderWidth SelectionBackColor SelectionForeColor TableStyles

Beschreibung Die Breite der Zeilenkpfe. Die Hintergrundfarbe von ausgewhlten Zeilen. Die Vordergrundfarbe von ausgewhlten Zeilen. Ein Auflistung von DataGridTableStyle-Objekten, die das Layout fr das Datenblatt definieren. Die Anzahl sichtbarer Spalten. Die Anzahl sichtbarer Zeilen. Beschreibung Versucht, das Datenblatt in einen Zustand zu versetzen, in dem die Bearbeitung erlaubt ist. Beginnt die Initialisierung des Datenblattes. Reduziert die Anzeige untergeordneter Beziehungen (das Gegenteil von Expand). Beendet den Bearbeitungsmodus, der von BeginEdit aufgerufen wurde. Beendet die Initialisierung des Datenblattes. Zeigt die untergeordneten Beziehungen fr jede einzelne oder alle Zeilen an. Ein Rectangle-Objekt, das die vier Ecken einer beliebigen Zelle festlegt. Ein Rectangle-Objekt, das die vier Ecken einer ausgewhlten Zelle festlegt. Liefert Informationen ber das Datenblatt am angegebenen Punkt. Gibt an, ob der Knoten einer Zeile gerade erweitert oder reduziert ist. Gibt an, ob eine Zeile ausgewhlt ist. Navigiert zu der Tabelle zurck, die zuvor im Datenblatt angezeigt worden war. Navigiert zur angegebenen Tabelle. Setzt die AlternatingBackColor-Eigenschaft auf deren Standardwert.

VisibleColumnCount VisibleRowCount

Methode
BeginEdit

BeginInit Collapse

EndEdit EndInit Expand

GetCellBounds GetCurrentCellBounds

HitTest IsExpanded IsSelected NavigateBack

NavigateTo ResetAlternatingBackColor

Tabelle 1.24: Eigenschaften, Methoden und Ereignisse von DataGrid (Forts.)

30

Bonuskapitel 1

Methode
ResetGridLineColor ResetHeaderBackColor ResetHeaderFont ResetHeaderForeColor ResetLinkColor ResetSelectionBackColor ResetSelectionForeColor Select SetDataBinding UnSelect

Beschreibung Setzt die GridLineColor-Eigenschaft auf deren Standardwert. Setzt die HeaderBackColor-Eigenschaft auf deren Standardwert. Setzt die HeaderFont-Eigenschaft auf deren Standardwert. Setzt die HeaderForeColor-Eigenschaft auf deren Standardwert. Setzt die LinkColor-Eigenschaft auf deren Standardwert. Setzt die SelectionBackColor-Eigenschaft auf deren Standardwert. Setzt die SelectionForeColor-Eigenschaft auf deren Standardwert. Whlt eine angegebene Zeile aus. Setzt die DataSource- und DataMember-Eigenschaften. Beendet die Auswahl einer bestimmten Zeile. Beschreibung Wird ausgelst, wenn sich die AllowNavigation-Eigenschaft ndert. Wird ausgelst, wenn die ZURCK-Schaltflche in einer untergeordneten Tabelle angeklickt wird. Wird ausgelst, wenn sich die BackgroundColor-Eigenschaft ndert. Wird ausgelst, wenn sich die BorderStyle-Eigenschaft ndert. Wird ausgelst, wenn sich die CaptionVisible-Eigenschaft ndert. Wird ausgelst, wenn sich die CurrentCell-Eigenschaft ndert. Wird ausgelst, wenn sich die DataSource-Eigenschaft ndert. Wird ausgelst, wenn sich die FlatMode-Eigenschaft ndert. Wird ausgelst, wenn der Benutzer zu einer anderen Tabelle im Datenblatt navigiert. Wird ausgelst, wenn sich die ParentRowsLabelStyle-Eigenschaft ndert. Wird ausgelst, wenn sich die ParentRowsVisible-Eigenschaft ndert.

Ereignis
AllowNavigationChanged BackButtonClick

BackgroundColorChanged BorderStyleChanged CaptionVisibleChanged CurrentCellChanged DataSourceChanged FlatModeChanged Navigate

ParentRowsLabelStyleChanged ParentRowsVisibleChanged

Tabelle 1.24: Eigenschaften, Methoden und Ereignisse von DataGrid (Forts.)

31

Bonuskapitel 1

Ereignis
ReadOnlyChanged Scroll

Beschreibung Wird ausgelst, wenn sich die ReadOnly-Eigenschaft ndert. Wird ausgelst, wenn der Benutzer einen Bildlauf durch das Datenblatt ausfhrt. Wird ausgelst, wenn die Schaltflche zum Anzeigen bergeordneter Details angeklickt wird.

ShowParentDetailsButtonClick

Tabelle 1.24: Eigenschaften, Methoden und Ereignisse von DataGrid (Forts.)

1.7

DateTimePicker

Das DateTimePicker-Steuerelement reprsentiert ein Kalender- und Zeit-Steuerelement, das sich dazu verwenden lsst, Datums- und Zeitinformationen einzustellen oder auszuwhlen. Tabelle B.25 beschreibt die Eigenschaften und Ereignisse des DateTimePickerSteuerelements.
Eigenschaft
CalendarFont CalendarForeColor CalendarMonthBackground CalendarTitleBackColor CalendarTitleForeColor CalendarTrailingForeColor Checked

Beschreibung Die im Kalender verwendete Schriftart. Die Vordergrundfarbe des Kalenders. Die Hintergrundfarbe des Kalendermonats. Die Hintergrundfarbe des Titels des Kalenders. Die Vordergrundfarbe des Titels des Kalenders. Die Vordergrundfarbe der Folgedaten des Kalenders. Gibt an, ob die Value-Eigenschaft auf ein gltiges Datum eingestellt und der angezeigte Wert aktualisierbar ist. Die benutzerdefinierte Formatzeichenfolge fr Datum und Uhrzeit. Die Ausrichtung des Dropdownkalenders im Steuerelement; verwendet einen Wert aus der LeftRightAlignment-Aufzhlung. Das Format des angezeigten Datums und der Zeit.

CustomFormat DropDownAlign

Format

Tabelle 1.25: Eigenschaften und Ereignisse von DateTimePicker

32

Bonuskapitel 1

Eigenschaft
MaxDate

Beschreibung Die hchste Datums- und Zeitangabe, die in diesem Steuerelement ausgewhlt werden kann. Die kleinste Datums- und Zeitangabe, die in diesem Steuerelement ausgewhlt werden kann. Die bevorzugte Hhe des Steuerelements. Gibt an, ob ein Kontrollkstchen links des Datums angezeigt wird. Gibt an, ob ein Auf-Ab-Steuerelement benutzt wird, um Datum und Zeit anzupassen. Der Datums- und Zeitwert des Steuerelements. Beschreibung Wird ausgelst, wenn der Dropdownkalender geschlossen wird. Wird ausgelst, wenn der Dropdownkalender angezeigt wird. Wird ausgelst, wenn sich die Format-Eigenschaft ndert. Wird ausgelst, wenn sich die Value-Eigenschaft ndert.

MinDate

PreferredHeight ShowCheckBox ShowUpDown

value

Ereignis
CloseUp DropDown FormatChanged ValueChanged

Tabelle 1.25: Eigenschaften und Ereignisse von DateTimePicker (Forts.)

1.8

Bildlauffhige Steuerelemente

Bildlauffhige Steuerelemente (Scrollable Controls) sind ein spezieller Steuerelementtyp, der den automatischen Bildlauf untersttzt, wenn die Inhalte des Steuerelements zu gro sind, um auf einen Bildschirm zu passen. Als Beispiele seien die Form- und Panel-Steuerelemente genannt. Alle bildlauffhigen Steuerelemente erben Werte von der System.Windows.Forms.ScrollableControl-Klasse, die in Tabelle B.26 gezeigt wird.
Eigenschaft
AutoScroll

Beschreibung Gibt an, ob das Steuerelement dem Benutzer gestattet, einen Bildlauf zu untergeordneten Elementen auszufhren, die auerhalb der Grenzen des Containers liegen. Die Gre des Autobildlaufrandes.

AutoScrollMargin

Tabelle 1.26: Eigenschaften und Methode von ScrollableControl

33

Bonuskapitel 1

Eigenschaft
AutoScrollMinSize AutoScrollPosition DockPadding

Beschreibung Die kleinste Gre des Autobildlaufs. Die Position des Autobildlaufs. Der Seitenabstand fr alle Rnder des Containersteuerelements. Beschreibung Legt die Gre der Autobildlaufrnder fest.

Methode
SetAutoScrollMargin

Tabelle 1.26: Eigenschaften und Methode von ScrollableControl (Forts.)

Darber hinaus haben die Steuerelemente Form, PropertyGrid, PrintPreviewDialog, DomainUpDown und NumericUpDown Eigenschaften aus der System.Windows.Forms.ContainerControl-Klasse gemeinsam, die in Tabelle B.27 gezeigt wird. ContainerControl erbt von ScrollableControl.
Eigenschaft
ActiveControl ParentForm

Beschreibung Das aktive Steuerelement im Container. Der bergeordnete Container des Steuerelements. Beschreibung Prft die Gltigkeit des zuletzt fr ungltig erklrten Steuerelements und dessen bergeordneter Steuerelemente, jedoch ohne das aktuelle Steuerelement einzubeziehen.

Methode
Validate

Tabelle 1.27: Eigenschaften und Methode von ContainerControl

Zustzlich zu den ScrollableControl- und ContainerControl-Klassen erben die Steuerelemente DomainUpDown und NumericUpDown auch vom System.Windows.Forms.UpDownBaseSteuerelement, das in Tabelle B.28 gezeigt wird.
Eigenschaft
BorderStyle

Beschreibung Der Darstellungsstil des Rahmens um dieses Steuerelement; verwendet Werte aus der BorderStyle-Aufzhlung. Gibt an, ob der Benutzer die Auf- und Abwrtspfeiltasten benutzen kann, um Werte auszuwhlen.

InterceptArrowKeys

Tabelle 1.28: Eigenschaften und Methoden von UpDownBase

34

Bonuskapitel 1

Eigenschaft
PreferredHeight ReadOnly TextAlign

Beschreibung Die bevorzugte Hhe des Steuerelements. Gibt an, ob sich der Wert des Steuerelements ndern lsst. Die Ausrichtung des Textes im Steuerelement; verwendet Werte aus der HorizontalAlignment-Aufzhlung. Die Ausrichtung der Auf- und Abwrts-Schaltflchen auf dem Steuerelement; verwendet Werte aus der LeftRightAlignment-Aufzhlung. Beschreibung Dient der Verarbeitung des Ereignisses, wenn die Abwrts-Schaltflche gedrckt wird. Markiert einen Textbereich im Steuerelement. Dient der Verarbeitung des Ereignisses, wenn die Aufwrts-Schaltflche gedrckt wird.

UpDownAlign

Methode
DownButton

Select UpButton

Tabelle 1.28: Eigenschaften und Methoden von UpDownBase (Forts.)

DomainUpDown
Dieses Steuerelement reprsentiert ein bildlauffhiges Steuerelement, das Zeichenfolgen anzeigt. Diese Klasse erbt direkt von der UpDownBase-Klasse und indirekt von ContainerControl und ScrollableControl. Tabelle B.29 zeigt die Eigenschaften dieses Steuerelements.
Eigenschaft
Items SelectedIndex

Beschreibung Eine Auflistung von Objekten, die diesem Steuerelement zugewiesen sind. Der nullbasierte Index des aktuell ausgewhlten Elements im Steuerelement. Das aktuell ausgewhlte Element im Steuerelement. Gibt an, ob die Elemente im Steuerelement sortiert sind. Gibt an, ob sich das Steuerelement zum ersten Element bewegt, wenn der Benutzer das Listenende berschreitet, oder zum letzten Element, wenn der Benutzer den Listenanfang berschreitet.

SelectedItem Sorted Wrap

Tabelle 1.29: Eigenschaften von DomainUpDown

35

Bonuskapitel 1

Form
Dieses Containersteuerelement reprsentiert ein Fenster in einer Anwendung (normalerweise den Hauptcontainer in einer Anwendung). Diese Klasse erbt direkt von ContainerControl und indirekt von ScrollableControl. Tabelle B.30 fhrt die Eigenschaften, Methoden und Ereignisse dieses Steuerelements auf.
Eigenschaft
AcceptButton ActiveForm ActiveMdiChild AutoScale

Beschreibung Die Schaltflche, die mit der ()-Taste verknpft ist. Liefert das aktuell aktive Formular dieser Anwendung. Das aktuell aktive untergeordnete MDI-Fenster. Gibt an, ob das Formular automatisch seine Gre anpasst, um sich verschiedenen Schriftgren anzupassen. Die fr die Autoskalierung verwendete Basisgre. Die Schaltflche, die mit der (Esc)-Taste verknpft ist. Die Gre des Clientbereichs des Formulars. Gibt an, ob der Rahmen des Steuerelements im Formular angezeigt wird. Gre und Position des Formulars auf dem Clientdesktop. Die Position des Formulars auf dem Clientdesktop. Der Wert, den das Formular liefert, wenn es geschlossen ist; verwendet einen Wert aus der DialogResult-Aufzhlung. Gibt den Darstellungsstil des Rahmens des Formulars an; verwendet einen Wert aus der FormBorderStyle-Aufzhlung. Gibt an, ob eine HILFE-Schaltflche im Formular angezeigt werden soll. Das Symbol, das mit dem Formular verknpft ist; wenn es das oberste Formular ist, wird dieses Symbol benutzt, um Ihre Anwendung zu reprsentieren. Gibt an, ob das Formular ein untergeordnetes MDI-Formular ist. Gibt an, ob das Formular ein Container fr untergeordnete MDI-Formulare ist.

AutoScaleBaseSize CancelButton ClientSize ControlBox DesktopBounds DesktopLocation DialogResult

FormBorderStyle

HelpButton Icon

IsMdiChild IsMdiContainer

Tabelle 1.30: Eigenschaften, Methoden und Ereignisse von Form

36

Bonuskapitel 1

Eigenschaft
KeyPreview

Beschreibung Gibt an, ob das Formular vor dem Steuerelement, das den Fokus hat, Tastaturereignisse empfngt. Gibt an, ob die Schaltflche fr Maximieren zu sehen ist. Gibt die maximale Gre an, zu der der Benutzer das Formular skalieren kann. Liefert ein Array von Formularen, die die untergeordneten MDI-Formulare dieses Formulars darstellen. Liefert oder setzt das bergeordnete MDI-Formular dieses Formulars. Das MainMenu, das mit diesem Formular verknpft ist. Das zusammengefhrte Men des Formulars. Gibt an, ob die Schaltflche fr Minimieren im Formular angezeigt wird. Die minimale Gre, auf die sich das Formular skalieren lsst. Gibt die Modalitt des Formulars an (d.h. ob es dem Benutzer erlaubt, von diesem Formular zu einem anderen zu wechseln). Die Durchlssigkeit des Formulars. Ein Array von Form-Objekten, die alle Formulare reprsentieren, deren Besitzer das aktuelle Formular ist. Liefert oder setzt das Formular, das der Besitzer des aktuellen Formulars ist. Gibt an, ob das Formular in der Windows-Taskleiste zu sehen ist. Bestimmt den Typ von Grenziehpunkten, die der Benutzer zur Skalierung des Formulars benutzen kann; verwendet Werte aus der SizeGripStyle-Aufzhlung. Die Ausgangsposition des Formulars. Gibt an, ob das Formular als oberstes Fenster anzuzeigen ist. Gibt an, ob das Formular als oberstes in einer Anwendung anzuzeigen ist. Die Farbe, die transparente Flchen des Formulars darstellt. Der Zustand des Formulars; verwendet Werte aus der FormWindowState-Aufzhlung.

MaximizeBox MaximumSize

MdiChildren

MdiParent Menu MergedMenu MinimizeBox MinimumSize Modal

Opacity OwnedForms

Owner ShowInTaskbar SizeGripStyle

StartPosition TopLevel TopMost TransparencyKey WindowState

Tabelle 1.30: Eigenschaften, Methoden und Ereignisse von Form (Forts.)

37

Bonuskapitel 1

Methode
Activate AddOwnedForm Close LayoutMdi

Beschreibung Aktiviert das Formular und bergibt ihm den Fokus. Fgt dem aktuellen Formular ein Formular hinzu. Schliet das Formular. Arrangiert die untergeordneten MDI-Formulare im bergeordneten Formular; verwendet Werte der MdiLayout-Aufzhlung. Entfernt ein Formular, das einem Besitzer gehrt, aus dem aktuellen Formular. Setzt die Grenzen des Formulars auf dem Clientdesktop. Setzt die Position des Formulars auf dem Clientdesktop. Zeigt das Formular als modales Dialogfeld an. Beschreibung Wird ausgelst, wenn das Formular aktiviert wird. Wird ausgelst, wenn das Formular geschlossen wird. Wird direkt, bevor das Formular geschlossen wird, ausgelst. Wird ausgelst, wenn das Formular den Fokus verliert. Wird ausgelst, wenn sich die Eingabesprache des Formulars ndert. Wird direkt, bevor sich die Eingabesprache des Formulars ndert, ausgelst. Wird ausgelst, wenn das Formular zum ersten Mal angezeigt wird. Wird ausgelst, wenn sich die MaximizedBounds-Eigenschaft ndert. Wird ausgelst, wenn sich die MaximumSize-Eigenschaft ndert. Wird ausgelst, wenn ein untergeordnetes MDI-Formular aktiviert oder geschlossen wird. Wird ausgelst, wenn das Men des Formulars den Fokus verliert. Wird ausgelst, wenn das Men des Formulars den Fokus erhlt. Wird ausgelst, wenn sich die MinimumSize-Eigenschaft ndert.

RemovedOwnedForm

SetDesktopBounds SetDesktopLocation ShowDialog

Ereignis
Activated Closed Closing Deactivate InputLanguageChanged InputLanguageChanging Load MaximizedBoundsChanged MaximumSizeChanged MdiChildActivate

MenuComplete MenuStart MinimumSizeChanged

Tabelle 1.30: Eigenschaften, Methoden und Ereignisse von Form (Forts.)

38

Bonuskapitel 1

NumericUpDown
NumericUpDown reprsentiert ein bildlauffhiges Windows-Steuerelement, das numerische Werte anzeigt. Diese Klasse erbt direkt von der UpDownBase-Klasse und indirekt von ContainerControl und ScrollableControl. Tabelle B.31 fhrt Eigenschaften und Ereignis dieses Steuerelements auf. Eigenschaft
DecimalPlaces Hexadecimal Increment

Beschreibung Die Anzahl der Dezimalstellen, die im Steuerelement anzuzeigen sind. Gibt an, ob das Steuerelement hexadezimale Werte anzeigen soll. Der Wert, um den der Wert des Steuerelements zu erhhen oder zu verringern ist, wenn die Auf- oder Ab-Schaltflchen angeklickt werden. Der hchste Wert fr das Steuerelement. Der kleinste Wert fr das Steuerelement. Gibt an, ob Tausendertrennzeichen anzuzeigen sind. Der aktuell im Steuerelement angezeigte Wert. Beschreibung Wird ausgelst, wenn sich die value-Eigenschaft ndert.

Maximum Minimum ThousandsSeparator Value

Ereignis
ValueChanged

Tabelle 1.31: Eigenschaften und Ereignis von NumericUpDown

Panel
Dieses Steuerelement reprsentiert eine Grundflche, die andere Steuerelemente enthalten kann, selbst aber ebenfalls in einem anderen Steuerelement enthalten sein muss. Diese Klasse erbt direkt von ScrollableControl. Tabelle B.32 fhrt die einzige Eigenschaft dieses Steuerelements auf.
Eigenschaft
BorderStyle

Beschreibung Der Rahmentyp um das Steuerelement herum; verwendet Werte aus der BorderStyle-Aufzhlung.

Tabelle 1.32: Eigenschaft von Panel

39

Bonuskapitel 1

PrintPreviewDialog
Dieses Steuerelement zeigt die Vorschau auf ein zu druckendes Dokument. Obwohl es ein Standarddialogfeld ist, folgt es nicht dem gleichen Vererbungspfad wie die anderen Standarddialogfelder. Vielmehr erbt es direkt von der Form-Klasse. Tabelle B.33 fhrt die Eigenschaften dieses Steuerelements auf.
Eigenschaft
Document

Beschreibung Das Dokument, das in der Vorschau angezeigt werden soll.

PrintPreviewControl Liefert das in diesem Steuerelement enthaltene PrintPreviewControl. UseAntiAlias

Gibt an, ob die Vorschau fr den Text Antialiasing verwenden soll, um so eine glatter aussehende Vorschau zu ermglichen.

Tabelle 1.33: Eigenschaften von PrintPreviewDialog

PropertyGrid
Dieses Steuerelement stellt eine Benutzeroberflche fr das Durchsuchen von Eigenschaften eines Objekts zur Verfgung. Es erbt direkt von ContainerControl und indirekt von ScrollableControl. Tabelle B.34 fhrt die Eigenschaften, Methoden und Ereignisse dieses Steuerelements auf.
Eigenschaft Beschreibung enthlt.
CanShowCommands

BrowsableAttributes Eine AttributeCollection, die die Attribute des zu durchsuchenden Objekts

Gibt an, ob Befehle fr das zu durchsuchende Objekt angezeigt werden knnen. Die Hintergrundfarbe des Befehlsbereichs. Die Vordergrundfarbe des Befehlsbereichs. Gibt an, ob der Befehlsbereich sichtbar ist. Gibt an, ob der Befehlsbereich sichtbar ist fr Objekte, die Befehle offen legen.

CommandsBackColor CommandsForeColor CommandsVisible CommandsVisibleIfAvailable

ContextMenuDefault- Die Standardposition fr das Kontextmen. Location

Tabelle 1.34: Eigenschaften, Methoden und Ereignisse von PropertyGrid

40

Bonuskapitel 1

Eigenschaft
HelpBackColor HelpForeColor HelpVisible LargeButtons

Beschreibung Die Hintergrundfarbe des Hilfebereichs. Die Vordergrundfarbe des Hilfebereichs. Gibt an, ob der Hilfebereich sichtbar ist. Gibt an, ob das Steuerelement Schaltflchen grer als vom Standard vorgegeben anzeigen soll. Die Farbe der Datenblatt- und Rahmenlinien. Der Typ der vom Steuerelement verwendeten Sortierung; verwendet Werte der PropertySort-Aufzhlung. Eine Auflistung von Registerkarten fr Eigenschaften dieses Steuerelements. Das aktuell ausgewhlte Element. Das Objekt, fr das dieses Steuerelement Eigenschaften anzeigt. Die aktuell ausgewhlten Objekte. Die aktuell ausgewhlte Registerkarte fr Eigenschaften. Gibt an, ob die Symbolleiste sichtbar ist. Die Hintergrundfarbe des Datenblattes. Die Vordergrundfarbe des Datenblattes. Beschreibung Reduziert alle Kategorien im Datenblatt. Erweitert alle Kategorien im Datenblatt. Aktualisiert die Registerkarten fr Eigenschaften des Datenblattes. Beschreibung

LineColor PropertySort

PropertyTabs SelectedGridItem SelectedObject SelectedObjects SelectedTab ToolbarVisible ViewBackColor ViewForeColor

Methode
CollapseAllGridItems ExpandAllGridItems RefreshTabs

Ereignis

PropertySortChanged Wird ausgelst, wenn sich der Sortiermodus ndert. PropertyTabChanged PropertyValueChanged

Wird ausgelst, wenn sich eine Registerkarte fr Eigenschaften ndert. Wird ausgelst, wenn sich ein Eigenschaftenwert ndert.

Tabelle 1.34: Eigenschaften, Methoden und Ereignisse von PropertyGrid (Forts.)

41

Bonuskapitel 1

Ereignis
SelectedGridItemChanged SelectedObjectsChanged

Beschreibung Wird ausgelst, wenn sich das ausgewhlte Element ndert. Wird ausgelst, wenn sich die SelectedObjects-Eigenschaft ndert.

Tabelle 1.34: Eigenschaften, Methoden und Ereignisse von PropertyGrid (Forts.)

TabPage
Dieses Steuerelement ist beinahe identisch mit dem Panel-Steuerelement, nur dass es sich ausschlielich innerhalb eines TabControl-Steuerelements verwenden lsst und eine bestimmte Tabseite reprsentiert. TabPage ist direkt von Panel abgeleitet. Tabelle B.35 fhrt die Eigenschaften dieses Steuerelements auf.
Eigenschaft
ImageIndex

Beschreibung Der Index der auf der Tabseite anzuzeigenden Grafik; schlagen Sie die ImageList-Eigenschaft von TabControl nach, um mehr Informationen zu erhalten. Der QuickInfo-Text fr dieses Steuerelement.

ToolTipText

Tabelle 1.35: Eigenschaften von TabPage

1.9

ErrorProvider

ErrorProvider erlaubt die Darstellung einer Benutzeroberflche, damit der Benutzer einen Fehler, der in Zusammenhang mit einem Steuerelement auftritt, beschreiben kann. Es ist jedoch nicht von Control abgeleitet. Tabelle B.36 fhrt die Eigenschaften und Methoden dieses Steuerelements auf. Eigenschaft
BlinkRate BlinkStyle

Beschreibung Die Frequenz, mit der das Fehlersymbol blinkt. Der Darstellungsstil, in dem das Fehlersymbol blinkt; verwendet Werte der ErrorBlinkStyle-Aufzhlung.

Tabelle 1.36: Eigenschaften und Methoden von ErrorProvider

42

Bonuskapitel 1

Eigenschaft
ContainerControl DataMember DataSource Icon

Beschreibung Das bergeordnete Steuerelement fr dieses Steuerelement. Die zu berwachende Datentabelle. Das zu berwachende Dataset. Das im Steuerelement anzuzeigende Symbol. Beschreibung Eigenschaften festzulegen.

Methode

BindToDataAndErrors Stellt eine Mglichkeit zur Verfgung, die DataMember- und DataSource-

CanExtend GetError GetIconAlignment

Gibt an, ob sich das Steuerelement erweitern lsst. Die Fehlerbeschreibung fr ein angegebenes Steuerelement. Gibt an, wo das Symbol im Verhltnis zum Text im Steuerelement platziert werden soll; verwendet Werte der ErrorIconAlignment-Aufzhlung. Der rund um das Fehlersymbol anzuzeigende freie Bereich. Legt die Fehlerbeschreibungszeichenfolge fr ein angegebenes Steuerelement fest. Wird benutzt, um festzulegen, wo das Symbol im Verhltnis zum Text im Steuerelement platziert werden soll; verwendet Werte der ErrorIconAlignment-Aufzhlung. Legt den freien Bereich fest, der rund um das Fehlersymbol anzuzeigen ist. Aktualisiert die Bindungen von DataSource, DataMember und des Fehlerbeschreibungstextes.

GetIconPadding SetError

SetIconAlignment

SetIconPadding UpdateBinding

Tabelle 1.36: Eigenschaften und Methoden von ErrorProvider(Forts.)

1.10 GroupBox
Dieses Steuerelement reprsentiert einen gruppierten Abschnitt (oder Kasten) anderer Steuerelemente. Tabelle B.37 zeigt die Eigenschaft dieses Steuerelements.

43

Bonuskapitel 1

Eigenschaft
FlatStyle

Beschreibung Die 3D-Darstellung des Steuerelements; verwendet Werte aus der FlatStyleAufzhlung.

Tabelle 1.37: Eigenschaft von GroupBox

1.11 Bildlaufleisten
Bildlaufleisten sind Steuerelemente, mit denen man in einem Steuerelement navigieren kann. Whrend einige Steuerelemente ber eingebaute Bildlaufleisten verfgen, kann man die Steuerelemente in diesem Abschnitt dazu einsetzen, weitere Bildlaufleisten jedem beliebigen Steuerelement hinzuzufgen. Alle Steuerelemente in diesem Abschnitt sind von der ScrollBar-Klasse (siehe Tabelle B.38) abgeleitet, die wiederum direkt von Control erbt.
Eigenschaft
LargeChange

Beschreibung Der Wert, der von der Value-Eigenschaft zu subtrahieren bzw. zur ValueEigenschaft zu addieren ist, wenn das Bildlauffeld ber eine grere Entfernung verschoben wird (normalerweise wenn der Benutzer in die Bildlaufleiste ber oder unter dem Bildlauffeld klickt). Der maximale Wert der Value-Eigenschaft. Der minimale Wert der Value-Eigenschaft. Der Wert, der von der Value-Eigenschaft zu subtrahieren bzw. zur ValueEigenschaft zu addieren ist, wenn das Bildlauffeld ber eine kurze Entfernung verschoben wird (normalerweise wenn der Benutzer auf die Pfeile der Bildlaufleisten klickt oder direkt das Bildlauffeld bewegt). Ein numerischer Wert, der die aktuelle Position des Bildlauffeldes reprsentiert. Beschreibung Wird ausgelst, wenn das Bildlauffeld bewegt wurde. Wird ausgelst, wenn sich die Value-Eigenschaft gendert hat.

Maximum Minimum SmallChange

Value

Ereignis
Scroll ValueChanged

Tabelle 1.38: Eigenschaften und Ereignisse von ScrollBar

44

Bonuskapitel 1

HScrollBar
Dieses Steuerelement reprsentiert eine horizontale Bildlaufleiste in einem Steuerelement. Es ist direkt von der ScrollBar-Klasse abgeleitet und enthlt keinerlei Eigenschaften, Methoden oder Ereignisse, die nicht geerbt werden.

VScrollBar
Dieses Steuerelement reprsentiert eine vertikale Bildlaufleiste in einem Steuerelement. Es ist direkt von der ScrollBar-Klasse abgeleitet und enthlt keinerlei Eigenschaften, Methoden oder Ereignisse, die nicht geerbt werden.

1.12 ImageList
Das ImageList-Steuerelement reprsentiert eine Auflistung von Image-Objekten, die sich einem anderen Steuerelement zuweisen lsst. Es ist nicht von der Control-Klasse abgeleitet. Tabelle B.39 fhrt Eigenschaften, Methode und Ereignis dieses Steuerelements auf.
Eigenschaft
ColorDepth Handle HandleCreated Images ImageSize ImageStream TransparentColor

Beschreibung Die Farbtiefe der Grafiken in der Liste. Der Handle dieses Steuerelements. Gibt an, ob der Handle fr dieses Steuerelement erzeugt worden ist. Ein ImageCollection-Objekt, das die Grafiken in der Liste reprsentiert. Die Gre der Grafiken in der Liste. Der Handle fr den ImageListStreamer, der mit dieser Liste verknpft ist. Die Farbe in den Grafiken, die als transparent behandelt werden soll. Beschreibung Zeichnet die angegebene Grafik. Beschreibung Wird ausgelst, wenn der Handle erzeugt wird.

Methode
Draw

Ereignis
RecreateHandle

Tabelle 1.39: Eigenschaften, Methode und Ereignis von ImageList

45

Bonuskapitel 1

1.13 Label-Steuerelemente
Bezeichnungsfelder stellen eine Mglichkeit dar, dem Benutzer statischen Text anzuzeigen. Es gibt zwei Label-Steuerelemente im .NET Framework: Label und LinkLabel. Tabelle B.40 fhrt die Eigenschaften und Ereignisse des Label-Steuerelements auf.
Eigenschaft
AutoSize

Beschreibung Gibt an, ob das Steuerelement seine Gre automatisch der Gre der enthaltenen Schriftart anpassen soll. Der fr das Steuerelement zu benutzende Rahmenstil; verwendet Werte aus der BorderStyle-Aufzhlung. Die 3D-Darstellung des Steuerelements; verwendet Werte aus der FlatStyleAufzhlung. Die Grafik, die auf diesem Steuerelement angezeigt wird. Die Ausrichtung der Grafik, die mit der Image-Eigenschaft angezeigt wird; verwendet einen Wert aus der ContentAlignment-Aufzhlung. Der Index der anzuzeigenden Grafik; vgl. die ImageList-Eigenschaft. Die ImageList, die mit diesem Steuerelement verknpft ist. Die bevorzugte Hhe des Steuerelements. Die bevorzugte Breite des Steuerelements. Gibt die Ausrichtung des Textes im Steuerelement an; verwendet Werte der ContentAlignment-Aufzhlung. Gibt an, ob das Steuerelement ein kaufmnnisches Und-Zeichen (&) als Prfix fr eine Zugriffstaste interpretiert. Beschreibung Wird ausgelst, wenn sich die AutoSize-Eigenschaft ndert. Wird ausgelst, wenn sich die TextAlign-Eigenschaft ndert.

BorderStyle

FlatStyle

Image ImageAlign

ImageIndex ImageList PreferredHeight PreferredWidth TextAlign

UseMnemonic

Ereignis
AutoSizeChanged TextAlignChanged

Tabelle 1.40: Eigenschaften und Ereignisse von Label

Das LinkLabel-Steuerelement hnelt dem Label-Steuerelement, nur dass es auch Hyperlinks darstellen kann. Es ist direkt von der Label-Klasse abgeleitet. Tabelle B.41 fhrt Eigenschaften und Ereignis dieses Steuerelements auf.

46

Bonuskapitel 1

Eigenschaft
ActiveLinkColor DisabledLinkColor LinkArea LinkBehavior

Beschreibung Die Farbe eines aktiven Hyperlinks. Die Farbe eines deaktivierten Hyperlinks. Der Textbereich im Steuerelement, der als Hyperlink zu behandeln ist. Die Verhaltensweise des Hyperlinks; verwendet Werte aus der LinkBehaviorAufzhlung. Die Farbe eines normalen Hyperlinks. Eine Auflistung von Hyperlinks in diesem Steuerelement. Gibt an, ob ein bestimmter Hyperlink als bereits besuchter Hyperlink angezeigt werden soll. Die Farbe eines bereits besuchten Hyperlinks. Beschreibung Wird ausgelst, wenn ein Hyperlink im Steuerelement angeklickt wird.

LinkColor Links LinkVisited

VisitedLinkColor

Ereignis
LinkClicked

Tabelle 1.41: Eigenschaften und Ereignis des LinkLabel-Steuerelements

1.14 ListView
Dieses Steuerelement zeigt eine Auflistung von Elementen an, und zwar auf vier verschiedene Weisen, hnlich wie der Windows Explorer. Tabelle B.42 fhrt die Eigenschaften, Methoden und Ereignisse dieses Steuerelements auf.
Eigenschaft
Activation

Beschreibung Die Art der Aktion, die ein Benutzer ausfhren muss, um ein Element zu aktivieren; verwendet einen Wert aus der ItemActivation-Aufzhlung. Die Ausrichtung von Elementen im Steuerelement; verwendet Werte aus der ListViewAlignment-Aufzhlung. Gibt an, ob der Benutzer Spalten im Steuerelement umstellen kann, indem er die Spaltenkpfe auf neue Positionen zieht. Gibt an, ob die Elemente automatisch arrangiert werden.

Alignment

AllowColumnReorder

AutoArrange

Tabelle 1.42: Eigenschaften, Methoden und Ereignisse von ListView

47

Bonuskapitel 1

Eigenschaft
BorderStyle

Beschreibung Der Rahmenstil des Steuerelements; verwendet Werte aus der BorderStyleAufzhlung. Gibt an, ob Kontrollkstchen neben jedem Element im Steuerelement erscheinen sollen. Die nullbasierten Indizes der aktuell aktivierten Elemente im Steuerelement. Die aktuell aktivierten Elemente im Steuerelement. Die Auflistung aller Spalten, die im Steuerelement erscheinen. Das Element, das gerade den Eingabefokus besitzt. Gibt an, ob ein Klick auf ein Element all seine Unterelemente auswhlt. Gibt an, ob Datenblattlinien gezeichnet werden sollen. Der Darstellungsstil von Spaltenkpfen; verwendet Werte aus der ColumnHeaderStyle-Aufzhlung.

CheckBoxes

CheckedIndices

CheckedItems Columns FocusedItem FullRowSelect GridLines HeaderStyle

HideSelection

Gibt an, ob das ausgewhlte Element im Steuerelement selbst dann markiert bleibt, wenn das Steuerelement den Fokus verliert. Gibt an, ob ein Element automatisch ausgewhlt wird, wenn der Mauszeiger ber dem Element schwebt. Die Auflistung aller Elemente in der Liste. Gibt an, ob der Benutzer die Bezeichnung jedes Elements im Steuerelement bearbeiten kann. Gibt an, ob Bezeichnungen umgebrochen werden sollen, wenn Elemente als Symbole angezeigt werden. Das zu benutzende ImageList-Steuerelement, wenn Elemente als groe Symbole dargestellt werden. Der Sortiervergleich fr das Steuerelement Gibt an, ob mehrere Elemente auf einmal ausgewhlt werden knnen. Gibt an, ob Bildlaufleisten automatisch nach Bedarf hinzugefgt werden. Die nullbasierten Indizes der ausgewhlten Elemente im Steuerelement.

HoverSelection

Items LabelEdit

LabelWrap

LargeImageList

ListViewItemSorter MultiSelect Scrollable SelectedIndices

Tabelle 1.42: Eigenschaften, Methoden und Ereignisse von ListView (Forts.)

48

Bonuskapitel 1

Eigenschaft
SelectedItems SmallImageList

Beschreibung Die aktuell ausgewhlten Elemente im Steuerelement. Das zu verwendende ImageList-Steuerelement, wenn Elemente als kleine Symbole angezeigt werden. Die Sortierreihenfolge fr Elemente im Steuerelement; verwendet Werte aus der SortOrder-Aufzhlung. Das ImageList-Steuerelement, das mit anwendungsdefinierten Zustnden im Steuerelement verknpft ist. Das erste sichtbare Element im Steuerelement. Gibt an, wie Elemente im Steuerelement angezeigt werden sollen; verwendet Werte aus der View-Aufzhlung. Beschreibung Arrangiert Elemente im Steuerelement, wenn sie als Symbole angezeigt werden. Hindert das Steuerelement daran, sich selbst jedes Mal neu zu zeichnen, wenn ein neues Element hinzugefgt wird, bis EndUpdate aufgerufen wird. Entfernt alle Elemente und Spalten aus dem Steuerelement. Nimmt das Zeichnen des Steuerelements wieder auf, nachdem BeginUpdate aufgerufen worden ist. Sorgt dafr, dass das aktuell ausgewhlte Element im Steuerelement sichtbar ist; fhrt ggf. einen Bildlauf durch den Inhalt des Steuerelements durch. Liefert das Element an der angegebenen Position. Liefert das umschlieende Rechteck fr ein angegebenes Element. Sortiert die Elemente im Steuerelement. Beschreibung Wird ausgelst, nachdem der Benutzer die Bezeichnung eines Elements bearbeitet hat. Wird ausgelst, bevor der Benutzer die Bezeichnung eines Elements bearbeitet. Wird ausgelst, wenn der Benutzer auf einen Spaltenkopf klickt.

Sorting

StateImageList

TopItem View

Methode
ArrangeIcons

BeginUpdate

Clear EndUpdate

EnsureVisible

GetItemAt GetItemRect Sort

Ereignis
AfterLabelEdit

BeforeLabelEdit

ColumnClick

Tabelle 1.42: Eigenschaften, Methoden und Ereignisse von ListView (Forts.)

49

Bonuskapitel 1

Ereignis
ItemActivate ItemCheck ItemDrag SelectedIndexChanged

Beschreibung Wird ausgelst, wenn ein Element aktiviert wird. Wird ausgelst, wenn ein Element aktiviert oder deaktiviert wird. Wird ausgelst, wenn der Benutzer beginnt, ein Element zu ziehen. Wird ausgelst, wenn sich der Index des ausgewhlten Elements ndert.

Tabelle 1.42: Eigenschaften, Methoden und Ereignisse von ListView (Forts.)

1.15 PictureBox
Das Steuerelement zeigt eine Grafik an. Tabelle B.43 fhrt Eigenschaften und Ereignis dieses Steuerelements auf.
Eigenschaft
BorderStyle

Beschreibung Der Rahmendarstellungsstil fr das Steuerelement; verwendet Werte aus der BorderStyle-Aufzhlung. Die im Steuerelement anzuzeigende Grafik. Gibt an, wie die Grafik anzuzeigen ist; verwendet Werte der PictureBoxSizeMode-Aufzhlung. Beschreibung Wird ausgelst, wenn sich die SizeMode-Eigenschaft ndert.

Image SizeMode

Ereignis
SizeModeChanged

Tabelle 1.43: Eigenschaften und Ereignis von PictureBox

1.16 PrintDocument
Dieses Steuerelement reprsentiert ein Dokument, das an einen Drucker geschickt wird, um gedruckt zu werden. Es erbt nicht von der Control-Klasse. Tabelle B.44 fhrt Eigenschaften, Methode und Ereignisse dieses Steuerelements auf.

50

Bonuskapitel 1

Eigenschaft

Beschreibung ten zur Verfgung stellt.

DefaultPageSettings Das PageSettings-Objekt, das die Voreinstellungen fr alle gedruckten Sei-

DocumentName PrintController PrinterSettings

Der Name des zu druckenden Dokuments. Der Druckercontroller, der den Druckvorgang steuert. Das PrinterSettings-Objekt, das den Drucker reprsentiert, der fr den Druck verwendet werden soll. Beschreibung Startet den Druckvorgang fr das Dokument. Beschreibung Wird ausgelst, wenn die Print-Methode aufgerufen wird, jedoch noch bevor irgendwelche Seiten gedruckt worden sind. Wird ausgelst, wenn die letzte Seite des Dokuments gedruckt worden ist. Wird jedes Mal ausgelst, wenn eine Seite zu drucken ist. Wird direkt vor jedem PrintPage-Ereignis ausgelst.

Methode
Print

Ereignis
BeginPrint

EndPrint PrintPage QueryPageSettings

Tabelle 1.44: Eigenschaften, Methode und Ereignisse von PrintDocument

1.17 PrintPreviewControl
Dieses Steuerelement reprsentiert den eigentlichen Vorschaubereich des PrintPreviewDialog-Dialogfeldes. Man beachte, dass sich dieses Steuerelement selbststndig auerhalb von PrintPreviewDialog benutzen lsst. Tabelle B.45 fhrt die Eigenschaften und Methode dieses Steuerelements auf.
Eigenschaft
AutoZoom

Beschreibung Gibt an, ob eine Grennderung des Steuerelements oder eine nderung der Anzahl von angezeigten Seiten automatisch die Zoom-Eigenschaft anpasst. Die Anzahl der Vorschauseiten, die horizontal im Steuerelement angezeigt werden.

Columns

Tabelle 1.45: Eigenschaften und Methode des PrintPreviewControl-Steuerelements

51

Bonuskapitel 1

Eigenschaft
Document Rows

Beschreibung Das PrintDocument fr die Vorschau. Die Anzahl von Vorschauseiten, die vertikal im Steuerelement angezeigt werden. Die Seitennummer der Seite, die im Steuerelement oben links angezeigt wird. Gibt an, ob das Steuerelement fr die Vorschaudarstellung Antialiasing einsetzen soll, um so eine glattere Grafik zu zeigen. Gibt an, wie gro die Vorschauseiten im Steuerelement sein sollen. Beschreibung Aktualisiert die Ansicht der Vorschau. Wird ausgelst, wenn sich die StartPage-Eigenschaft ndert.

StartPage

UseAntiAlias

Zoom

Methode
InvalidatePreview StartPageChanged

Tabelle 1.45: Eigenschaften und Methode des PrintPreviewControl-Steuerelements (Forts.)

1.18 ProgressBar
Das Steuerelement reprsentiert eine Windows-Statusanzeige. Tabelle B.46 fhrt die Eigenschaften und Methoden dieses Steuerelements auf.
Eigenschaft
Maximum Minimum Step

Beschreibung Der maximale Wert oder Bereich des Steuerelements. Der minimale Wert oder Bereich des Steuerelements. Der Betrag, um den sich die Position der Statusanzeige erhhen soll, wenn die PerformStep-Methode aufgerufen wird. Die aktuelle Position der Statusanzeige. Beschreibung Erhht die Position der Statusanzeige um den angegebenen Betrag. Erhht die Position der Statusanzeige um den Betrag in der Step-Eigenschaft.

Value

Methode
Increment PerformStep

Tabelle 1.46: Eigenschaften und Methoden von ProgressBar

52

Bonuskapitel 1

1.19 Textfeld-Steuerelemente
Es gibt zwei Textfeld-Steuerelemente im .NET Framework: TextBox und RichTextBox. Beide Steuerelemente erben von der in Tabelle B.47 gezeigten TextBoxBase-Klasse, welche wiederum von Control erbt.
Eigenschaft
AcceptsTab

Beschreibung Gibt an, ob ein Druck auf die (_)-Taste ein Tabstoppzeichen in das Steuerelement eingibt oder den Fokus auf das nchste Steuerelement in der Aktivierreihenfolge setzt. Gibt an, ob das Steuerelement automatisch seine Gre anpassen soll, wenn sich die Font-Eigenschaft ndert. Der Rahmendarstellungsstil des Steuerelements; verwendet Werte der BorderStyle-Aufzhlung.

AutoSize

BorderStyle

CanUndo

Gibt an, ob der Benutzer einen vorangegangenen Arbeitsschritt rckgngig machen kann. Gibt an, ob markierter Text markiert bleiben soll, wenn das Steuerelement den Fokus verliert. Ein Array von Zeichenfolgen, die die Zeilen im Steuerelement reprsentieren. Die maximale Anzahl von Zeichen, die sich in das Steuerelement eingeben lassen. Gibt an, ob das Steuerelement gendert wurde seit seine Inhalte erstmals festgelegt wurden. Gibt an, ob das Steuerelement mehrere Zeilen von Text enthalten kann. Die bevorzugte Hhe des Steuerelements. Gibt an, ob sich die Inhalte des Steuerelements bearbeiten lassen. Liefert oder setzt den aktuell im Steuerelement markierten Text. Die Anzahl von markierten Zeichen im Steuerelement. Der nullbasierte Index des Zeichens, bei dem die Markierung anfngt. Die Lnge des Textes im Steuerelement.

HideSelection

Lines

MaxLength

Modified

Multiline PreferredHeight ReadOnly SelectedText SelectionLength SelectionStart TextLength

Tabelle 1.47: Eigenschaften, Methoden und Ereignisse von TextBoxBase

53

Bonuskapitel 1

Eigenschaft
WordWrap

Beschreibung Gibt an, ob ein mehrzeiliges Textfeld automatisch Zeilen, die zu lang fr dessen Anzeige sind, umbrechen soll. Beschreibung Hngt Text an das Ende des Steuerelements an. Entfernt allen Text aus dem Steuerelement. Lscht Informationen ber die letzten Arbeitsschritte aus dem RckgngigPuffer. Kopiert die aktuelle Auswahl in die Zwischenablage. Entfernt die aktuelle Auswahl aus dem Steuerelement und legt sie in der Zwischenablage ab. Fgt die Inhalte der Zwischenablage an der aktuellen Position in das Steuerelement ein. Fhrt einen Bildlauf durch den Inhalt des Steuerelements bis zur Einfgemarke (das Caret) durch. Markiert Text im Steuerelement. Markiert den gesamten Text im Steuerelement. Macht den letzten Arbeitsschritt im Steuerelement rckgngig. Beschreibung Wird ausgelst, wenn sich die AcceptsTab-Eigenschaft ndert. Wird ausgelst, wenn sich die AutoSize-Eigenschaft ndert. Wird ausgelst, wenn sich die BorderStyle-Eigenschaft ndert. Wird ausgelst, wenn das Steuerelement angeklickt wird. Wird ausgelst, wenn sich die HideSelection-Eigenschaft ndert. Wird ausgelst, wenn sich die Modified-Eigenschaft ndert. Wird ausgelst, wenn sich die Multiline-Eigenschaft ndert. Wird ausgelst, wenn sich die ReadOnly-Eigenschaft ndert.

Methode
AppendText Clear ClearUndo

Copy Cut

Paste

ScrollToCaret

Select SelectAll Undo

Ereignis
AcceptsTabChanged AutoSizeChanged BorderStyleChanged Click HideSelectionChanged ModifiedChanged MultilineChanged ReadOnlyChanged

Tabelle 1.47: Eigenschaften, Methoden und Ereignisse von TextBoxBase (Forts.)

54

Bonuskapitel 1

RichTextBox
Dieses Steuerelement reprsentiert ein Textfenster, das mit Rich Text (.RTF) formatierte Zeichen enthalten kann. Tabelle B.48 fhrt die Eigenschaften, Methoden und Ereignisse dieses Steuerelements auf.
Eigenschaft
AutoWordSelection

Beschreibung Gibt an, ob ein ganzes Wort automatisch ausgewhlt wird, wenn eines seiner Zeichen ausgewhlt wurde. Die Anzahl von Bildpunkten, um die ein Aufzhlungssymbol im Steuerelement eingerckt werden soll. Gibt an, ob es Arbeitsschritte gibt, die erneut angewendet werden knnen. Gibt an, ob das Steuerelement automatisch einen URL formatieren soll, wenn er in das Steuerelement eingegeben wird. Der Name des letzten Arbeitsschrittes, der sich erneut auf das Steuerelement anwenden liee. Der rechte Rand des editierbaren Bereichs des Steuerelements; bestimmt, wie viele Zeichen in eine Zeile passen. Der im Steuerelement enthaltene Text, einschlielich aller Rich Text-Formatierungscodes. Gibt an, wie Bildlaufleisten im Steuerelement gezeigt werden sollen; verwendet Werte der RichTextScrollBars-Aufzhlung. Der aktuell ausgewhlte Text im Steuerelement, einschlielich aller Rich Text-Formatierungscodes. Die Ausrichtung, die auf den aktuell ausgewhlten Text anzuwenden ist; verwendet Werte aus der HorizontalAlignment-Aufzhlung. Gibt an, ob ein Aufzhlungssymbol auf die aktuelle Auswahl angewendet wird. gestellt erscheinen soll.

BulletIndent

CanRedo DetectUrls

RedoActionName

RightMargin

Rtf

ScrollBars

SelectedRtf

SelectionAlignment

SelectionBullet

SelectionCharOffset Gibt an, ob der ausgewhlte Text auf der Grundlinie, hochgestellt oder tief-

SelectionColor SelectionFont

Die Farbe markierten Textes. Die Schriftart des aktuell ausgewhlten Textes.

Tabelle 1.48: Eigenschaften, Methoden und Ereignisse von RichTextBox

55

Bonuskapitel 1

Eigenschaft
SelectionHangingIndent

Beschreibung Der Abstand zwischen dem linken Rand der ersten Textzeile im ausgewhlten Absatz und dem linken Rand nachfolgender Zeilen im gleichen Absatz (hngender Einzug). Der Abstand in Bildpunkten, um den die aktuelle Auswahl eingerckt werden soll. Gibt an, ob die aktuelle Auswahl geschtzt ist; wird solcher Text bearbeitet, wird das Protected-Ereignis ausgelst. Der Abstand in Bildpunkten zwischen dem rechten Rand des Steuerelements und dem rechten Rand des aktuell ausgewhlten Textes. Die Positionen der Tabstopps im Steuerelement. Der Typ der aktuellen Auswahl; verwendet Werte aus der RichTextBoxSelectionTypes-Aufzhlung. mal auswhlen kann, angezeigt werden soll.

SelectionIndent

SelectionProtected

SelectionRightIndent SelectionTabs SelectionType

ShowSelectionMargin Gibt an, ob ein linker Rand, mit dem der Benutzer mehrere Zeilen auf ein-

UndoActionName ZoomFactor

Der Name des letzten Arbeitsschrittes, der sich rckgngig machen lsst. Der aktuelle Zoomfaktor des Steuerelements. Beschreibung Gibt an, ob man den Inhalt der Zwischenablage an der aktuellen Position im Steuerelement einfgen kann. Sucht nach Text im Steuerelement.

Methode
CanPaste

Find

GetCharFromPosition Liefert das Zeichen, das sich dem angegebenen Punkt am nchsten befindet. GetCharIndexFromPosition GetLineFromCharIndex LoadFile Redo SaveFile

Liefert den nullbasierten Zeichenindex, der sich dem angegebenen Punkt am nchsten befindet. Liefert die Zeile, in der sich der angegebene Zeichenindex befindet. Ldt die Inhalte einer Datei in das Steuerelement. Wendet den letzten im Steuerelement ausgefhrten Arbeitsschritt erneut an. Speichert die Inhalte des Steuerelements in eine Datei.

Tabelle 1.48: Eigenschaften, Methoden und Ereignisse von RichTextBox (Forts.)

56

Bonuskapitel 1

Ereignis
ContentsResized

Beschreibung Wird ausgelst, wenn die Inhalte des Steuerelements in der Gre gendert werden. Wird ausgelst, wenn der Benutzer einen horizontalen Bildlauf durch das Steuerelement ausfhrt. Wird ausgelst, wenn der Benutzer die Eingabemethoden in asiatischen Versionen von Windows wechselt. Wird ausgelst, wenn der Benutzer im Steuerelement auf einen Hyperlink klickt. Wird ausgelst, wenn der Benutzer versucht, eine Auswahl, die mit der Protected-Eigenschaft markiert ist, zu bearbeiten.

HScroll

ImeChange

LinkClicked

Protected

SelectionChanged VScroll

Wird ausgelst, wenn sich die aktuelle Auswahl im Steuerelement ndert. Wird ausgelst, wenn der Benutzer einen vertikalen Bildlauf durch das Steuerelement ausfhrt.

Tabelle 1.48: Eigenschaften, Methoden und Ereignisse von RichTextBox (Forts.)

TextBox
Dieses Steuerelement reprsentiert ein einfaches Textfenster, das reinen Text enthalten kann, jedoch ohne Formatierung. Tabelle B.49 fhrt Eigenschaften und Ereignis dieses Steuerelements auf.
Eigenschaft
AcceptsReturn

Beschreibung Gibt an, ob ein Druck auf die ()-Taste eine neue Zeile in das Steuerelement einfgt statt die Standardschaltflche des umgebenden Formulars zu aktivieren. Gibt an, ob das Steuerelement die Gro-/Kleinschreibung von Zeichen ndert, whrend man sie eingibt; verwendet Werte der CharacterCasing-Aufzhlung.

CharacterCasing

PasswordChar

Das Zeichen, das zu benutzen ist, um die eingetippten Zeichen eines Kennwortes zu maskieren. Gibt an, ob Bildlaufleisten in einem mehrzeiligen Textfeld anzuzeigen sind; verwendet Werte aus der ScrollBars-Aufzhlung.

ScrollBars

Tabelle 1.49: Eigenschaften und Ereignis von TextBox

57

Bonuskapitel 1

Eigenschaft
TextAlign

Beschreibung Die Ausrichtung von Text im Steuerelement; verwendet Werte aus der HorizontalAlignment-Aufzhlung.

Ereignis
TextAlignChanged

Beschreibung Wird ausgelst, wenn sich die TextAlign-Eigenschaft ndert.

Tabelle 1.49: Eigenschaften und Ereignis von TextBox (Forts.)

1.20 Steuerelemente der Statusleiste


Das StatusBar-Steuerelement reprsentiert eine Statusleiste in einer Windows-Anwendung. Es kann StatusBarPanel-Steuerelemente enthalten, die Daten anzeigen. Tabelle B.50 fhrt die Eigenschaften und Ereignisse dieses Steuerelements auf.
Eigenschaft
Panels ShowPanels

Beschreibung Ein Auflistung von StatusBar-Bereichen im Steuerelement. Gibt an, ob dieses Steuerelement enthaltene StatusBar-Bereiche anzeigen soll. Gibt an, ob in der unteren rechten Ecke der Statusleiste ein Grenziehpunkt angezeigt werden soll. Beschreibung Wird ausgelst, wenn sich ein visueller Aspekt eines vom Besitzer gezeichneten Steuerelements ndert. Wird ausgelst, wenn der Benutzer auf eines der in diesem Steuerelement enthaltenen StatusBarPanel-Steuerelemente klickt.

SizingGrip

Ereignis
DrawItem

PanelClick

Tabelle 1.50: Eigenschaften und Ereignisse von StatusBar

Tabelle B.51 fhrt die Eigenschaften des StatusBarPanel-Steuerelements auf. Dieses Steuerelement erbt nicht von der Control-Klasse.

58

Bonuskapitel 1

Eigenschaft
Alignment

Beschreibung Die Ausrichtung der Inhalte innerhalb des Statusleistenbereichs; verwendet Werte aus der HorizontalAlignment-Aufzhlung. Gibt an, ob das Steuerelement automatisch seine Gre je nach der Gre des umgebenden StatusBar-Steuerelements anpassen sollte; verwendet Werte aus der StatusBarPanelAutoSize-Aufzhlung. Der Rahmendarstellungsstil fr dieses Steuerelement; verwendet Werte aus der BorderStyle-Aufzhlung. Das im Steuerelement anzuzeigende Symbol. Die kleinste Breite des Statusleistenbereichs in bereinstimmung mit dem umgebenden StatusBar-Steuerelement. Das Eltern-StatusBar-Steuerelement. Der Darstellungsstil dieses Steuerelements; verwendet Werte aus der StatusBarPanel-Aufzhlung.

AutoSize

BorderStyle

Icon MinWidth

Parent Style

Text ToolTipText Width

Der im Steuerelement anzuzeigende Text. Der als QuickInfo fr dieses Steuerelement anzuzeigende Text. Die Breite des Steuerelements innerhalb des umgebenden StatusBar-Steuerelements.

Tabelle 1.51: Eigenschaften von StatusBarPanel

1.21 TabControl
Ein TabControl reprsentiert die Struktur fr eine Ansicht mit Registerkarten in einer Anwendung. Es enthlt TabPage-Steuerelemente, die jeweils einzelne Registerkarten reprsentieren. Tabelle B.52 fhrt Eigenschaften, Methode und Ereignis des TabControl-Steuerelements auf.
Eigenschaft
Alignment

Beschreibung Die Ausrichtung von Registerkarten im Steuerelement; verwendet Werte der TabAlignment-Aufzhlung.

Tabelle 1.52: Eigenschaften, Methode und Ereignis von TabControl

59

Bonuskapitel 1

Eigenschaft
Appearance

Beschreibung Der Darstellungsstil Registerkarten; verwendet Werte aus der TabAppearanceAufzhlung. Gibt an, wie Tabseiten im Steuerelement gezeichnet werden; verwendet Werte aus der TabDrawMode-Aufzhlung. Gibt an, ob die Registerkarten ihr Aussehen verndern, wenn der Mauszeiger ber sie bewegt wird. Ein ImageList-Steuerelement, das Grafiken fr jede Registerkarte enthlt. Die Gre der Registerkarten des Steuerelements. Gibt an, ob Registerkarten in mehreren Zeilen erscheinen sollen. Der Raum in Bildpunkten um jedes Element auf einer Tabseite. Die Anzahl der Zeilen mit Registerkarten im TabControl. Der nullbasierte Index der aktuell ausgewhlten Tabseite. Das aktuell ausgewhlte TabPage-Objekt. Gibt an, ob QuickInfos fr eine Registerkarte anzuzeigen sind, wenn sich der Mauszeiger darber bewegt. Die Art und Weise, in der die Gre der Registerkarten verndert wird; verwendet Werte aus der TabSizeMode-Eigenschaft. Die Anzahl von Registerkarten in diesem Steuerelement. Eine Auflistung der TabPage-Steuerelemente in diesem Steuerelement. Beschreibung Liefert das angrenzende Rechteck fr die angegebene Registerkarte. Beschreibung Wird ausgelst, wenn die Registerkarten gezeichnet werden und die DrawMode-Eigenschaft auf OwnerDrawFixed eingestellt ist.

DrawMode

HotTrack

ImageList ItemSize Multiline Padding RowCount SelectedIndex SelectedTab ShowToolTips

SizeMode

TabCount TabPages

Methode
GetTabRect

Ereignis
DrawItem

Tabelle 1.52: Eigenschaften, Methode und Ereignis von TabControl (Forts.)

60

Bonuskapitel 1

1.22 Timer
Timer reprsentiert einen Zeitgeber, der Ereignisse in einem angegebenen Zeitintervall auslst. Dieses Steuerelement erbt nicht von Control. Tabelle B.53 fhrt Eigenschaften,

Methoden und Ereignis dieses Steuerelements auf.


Eigenschaft
Enabled Interval

Beschreibung Gibt an, ob der Timer gerade luft. Das Zeitintervall, in dem der Timer das Tick-Ereignis auslsen soll. Beschreibung Startet den Timer. Stoppt den Timer. Beschreibung Wird ausgelst, wenn das angegebene Zeitintervall verstrichen ist.

Methode
Start Stop

Ereignis
Tick

Tabelle 1.53: Eigenschaften, Methoden und Ereignis von Timer

1.23 Steuerelemente der Symbolleiste


Dieses Steuerelement stellt die Struktur einer Windows-Symbolleiste dar. Es kann ToolBarButton-Steuerelemente enthalten, die die eigentlichen Schaltflchen auf der Symbolleiste reprsentieren. Tabelle B.54 fhrt die Eigenschaften und Ereignisse dieses Steuerelements auf.
Eigenschaft
Appearance

Beschreibung Der Stil der Symbolleiste; verwendet Werte aus der ToolBarAppearance-Aufzhlung Gibt an, ob die Symbolleiste automatisch ihre Gre an die enthaltenen Schaltflchen anpassen soll. Der Darstellungsstil des Rahmens fr dieses Steuerelement; verwendet Werte aus der BorderStyle-Aufzhlung.

AutoSize

BorderStyle

Tabelle 1.54: Eigenschaften und Ereignisse von ToolBar

61

Bonuskapitel 1

Eigenschaft
Buttons

Beschreibung Die Auflistung von ToolBarButton-Steuerelementen auf diesem Steuerelement. Die Gre der Schaltflchen in diesem Steuerelement. Gibt an, ob die Symbolleiste einen Trennstrich zeigt. Gibt an, ob Dropdown-Schaltflchen auf der Symbolleiste Dropdownpfeile anzeigen sollen. Das ImageList-Steuerelement, das benutzt wird, um Grafiken mit dieser Symbolleiste zu verknpfen. Die Gre der Grafiken in dem verknpften ImageList-Steuerelement. Gibt an, ob eine QuickInfo fr jede Schaltflche angezeigt werden soll. Die Ausrichtung des Textes in jeder Schaltflche im Verhltnis zur Grafik auf der Schaltflche. Gibt an, ob die Symbolleistenschaltflchen eine neue Zeile bilden, um alle verfgbaren Symbolleistenschaltflchen anzeigen zu knnen. Beschreibung Wird ausgelst, wenn ein ToolBarButton in diesem Steuerelement angeklickt wird. Wird ausgelst, wenn ein ToolBarButton im Dropdown-Stil angeklickt wird.

ButtonSize Divider DropDownArrows

ImageList

ImageSize ShowToolTips TextAlign

Wrappable

Ereignis
ButtonClick

ButtonDropDown

Tabelle 1.54: Eigenschaften und Ereignisse von ToolBar (Forts.)

Das ToolBarButton-Steuerelement reprsentiert die Schaltflchen auf einer ToolBar und erbt nicht von der Control-Klasse. Tabelle B.55 fhrt die Eigenschaften dieses Steuerelements auf.
Eigenschaft
DropDownMenu

Beschreibung Das von Menu abgeleitete Steuerelement, das als Dropdown-Men fr diese Schaltflche angezeigt werden soll. Gibt an, ob die Schaltflche Benutzerinteraktion zulsst. Der Index der Grafik in der ImageList-Eigenschaft der umgebenden ToolBar.

Enabled ImageIndex

Tabelle 1.55: Eigenschaften von ToolBarButton

62

Bonuskapitel 1

Eigenschaft
Parent PartialPush

Beschreibung Das umgebende ToolBar-Steuerelement. Gibt an, ob eine Schaltflche das Aussehen, nur teilweise gedrckt worden zu sein, anzeigen kann. Gibt an, ob eine Schaltflche aussehen soll, als sei sie gedrckt worden. Das umschlieende Rechteck fr diese Schaltflche. Der Darstellungsstil dieser Schaltflche; verwendet Werte aus der ToolBarButtonStyle-Aufzhlung.

Pushed Rectangle Style

Tag Text ToolTipText

Das Objekt, das Daten ber das Steuerelement enthlt. Der Text, der auf der Symbolleistenschaltflche angezeigt wird. Der QuickInfo-Text, der angezeigt werden soll, wenn der Mauszeiger ber der Schaltflche ruht. Gibt an, ob die Schaltflche angezeigt werden soll.

Visible

Tabelle 1.55: Eigenschaften von ToolBarButton (Forts.)

1.24 ToolTip
Dieses Steuerelement reprsentiert ein Popup-Fenster, das erscheint, wenn der Mauspfeil des Benutzers ber einem Steuerelement ruht. Dieses Fenster sollte hilfreiche Informationen ber die Verwendung des jeweiligen Steuerelements anzeigen. ToolTip erbt nicht von der Control-Klasse. Tabelle B.56 fhrt die Eigenschaften und Methoden dieses Steuerelements auf.
Eigenschaft
Active AutomaticDelay

Beschreibung Gibt an, ob die QuickInfo gerade aktiv ist. Die automatische Verzgerung in Millisekunden fr die QuickInfo; diese Eigenschaft setzt automatisch die Eigenschaften AutoPopDelay, InitialDelay und ReshowDelay. Der Zeitraum, fr den die QuickInfo sichtbar bleibt.

AutoPopDelay

Tabelle 1.56: Eigenschaften und Methoden des ToolTip-Steuerelements

63

Bonuskapitel 1

Eigenschaft
InitialDelay

Beschreibung Der Zeitraum, fr den der Mauszeiger ber dem Steuerelement ruhen muss, bevor die QuickInfo erscheint. Die Zeit, die zwischen dem Verschwinden der einen QuickInfo und dem Auftauchen einer anderen verstreichen muss. Gibt an, ob die QuickInfo anzuzeigen ist, wenn das bergeordnete Steuerelement nicht aktiv ist. Beschreibung Liefert die QuickInfo, die mit dem angegebenen Steuerelement verknpft ist. Entfernt den Text, der mit dieser QuickInfo verknpft ist. Setzt den Text, der mit dieser QuickInfo verknpft ist.

ReshowDelay

ShowAlways

Methode
GetToolTip

RemoveAll SetToolTip

Tabelle 1.56: Eigenschaften und Methoden des ToolTip-Steuerelements (Forts.)

1.25 TrackBar
Dieses Steuerelement reprsentiert eine Windows-Verlaufsleiste, ein bildlauffhiges Steuerelement, das gewisse hnlichkeit mit ScrollBar hat. Tabelle B.57 fhrt Eigenschaften, Methode und Ereignisse dieses Steuerelements auf.
Eigenschaft
AutoSize LargeChange

Beschreibung Gibt an, ob die Verlaufsleiste automatisch in der Gre angepasst wird. Der Wert, der von der Value-Eigenschaft abgezogen oder zu dieser addiert werden soll, wenn das Bildlauffeld ber eine groe Entfernung verschoben wird (normalerweise wenn der Benutzer die Bildlaufleiste ber oder unter dem Bildlauffeld anklickt). Der hchste Wert der Value-Eigenschaft. Der kleinste Wert der Value-Eigenschaft. Gibt die horizontale oder vertikale Ausrichtung der Verlaufsleiste an; verwendet Werte aus der Orientation-Aufzhlung.

Maximum Minimum Orientation

Tabelle 1.57: Eigenschaften, Methode und Ereignisse von TrackBar

64

Bonuskapitel 1

Eigenschaft
SmallChange

Beschreibung Der Wert, der von der Value-Eigenschaft abgezogen oder zu dieser addiert werden soll, wenn das Bildlauffeld ber eine kleine Entfernung verschoben wird (normalerweise wenn der Benutzer die Pfeile auf der Bildlaufleiste anklickt oder das Bildlauffeld direkt verschiebt). Der Wert zwischen den Teilstrichen auf dem Steuerelement. Der Stil von Telilstrichen auf dem Steuerelement; verwendet Werte aus der TickStyle-Aufzhlung. Ein numerischer Wert, der die aktuelle Position des Bildlauffeldes reprsentiert. Beschreibung Legt den kleinsten und hchsten Wert fr die Verlaufsleiste fest. Beschreibung Wird ausgelst, wenn das Bildlauffeld verschoben wurde. Wird ausgelst, wenn sich die Value-Eigenschaft gendert hat.

TickFrequency TickStyle

Value

Methode
SetRange

Ereignis
Scroll ValueChanged

Tabelle 1.57: Eigenschaften, Methode und Ereignisse von TrackBar (Forts.)

1.26 TreeView-Steuerelemente
Dieses Steuerelement stellt eine hierarchische Auflistung von bezeichneten Elementen dar. Es kann eine beliebige Anzahl von verschachtelten TreeNode-Steuerelementen enthalten, die jedes Element reprsentieren. Tabelle B.58 fhrt die Eigenschaften, Methoden, und Ereignisse dieses Steuerelement.
Eigenschaft
BorderStyle

Beschreibung Der Rahmendarstellungsstil fr dieses Steuerelement; verwendet Werte der BorderStyle-Aufzhlung. Gibt an, ob Kontrollkstchen neben jedem Element in der Strukturansicht gezeichnet werden sollen. Gibt an, ob die Auswahlmarkierung die gesamte Lnge der Strukturansicht umspannt oder nur die Beschriftung des Knotens.

CheckBoxes

FullRowSelect

Tabelle 1.58: Eigenschaften, Methoden und Ereignisse von TreeView

65

Bonuskapitel 1

Eigenschaft
HideSelection

Beschreibung Gibt an, ob ein ausgewhltes Element markiert bleiben soll, wenn das Steuerelement den Fokus verliert. Gibt an, ob die Elemente in der Strukturansicht ihr Aussehen verndern sollen, sobald sich der Mauszeiger ber sie bewegt. Der Index der Grafik im ImageList-Steuerelement, die mit dieser Strukturansicht als Standardgrafik fr die Elemente verknpft ist. Ein ImageList-Steuerelement, das Grafiken enthlt, die fr jedes Element in der Strukturansicht anzuzeigen sind. Der Abstand in Bildpunkten, um den jede Knotenebene einzurcken ist. Die Hhe jedes Elements im Steuerelement. Gibt an, ob die Bezeichnungen fr jedes Element bearbeitbar sind. Liefert eine Auflistung von TreeNode-Steuerelementen, die die Elemente in der Strukturansicht reprsentieren. Die Trennzeichenfolge, die der Pfad des Strukturknotens verwendet. Gibt an, ob dieses Steuerelement bei Bedarf Bildlaufleisten anzeigt. Der verknpfte ImageList-Indexwert der Grafik, die fr das aktuell ausgewhlte Element im Steuerelement angezeigt wird. Der Strukturknoten (TreeNode), der aktuell ausgewhlt ist. Gibt an, ob Linien zwischen den Elementen in diesem Steuerelement gezeichnet werden. Gibt an, ob Plus- und Minus-Zeichen neben jenen Elementen gezeigt werden, die ber Unterelemente verfgen. Gibt an, ob Linien zwischen Stammknoten der Strukturansicht gezogen werden. Gibt an, ob Elemente automatisch sortiert werden sollen. Das erste vollstndig sichtbare Element im Steuerelement. Die Anzahl vollstndig sichtbarer Elemente im Steuerelement.

HotTracking

ImageIndex

ImageList

Indent ItemHeight LabelEdit Nodes

PathSeparator Scrollable SelectedImageIndex

SelectedNode ShowLines

ShowPlusMinus

ShowRootLines

Sorted TopNode VisibleCount

Tabelle 1.58: Eigenschaften, Methoden und Ereignisse von TreeView (Forts.)

66

Bonuskapitel 1

Methode
BeginUpdate

Beschreibung Hindert das Steuerelement daran, sich jedes Mal, wenn ein neues Element hinzugefgt wird, neu zu zeichnen, bis EndUpdate aufgerufen wird. Reduziert alle erweiterten Knoten in der Liste. Nimmt den Zeichenvorgang fr das Steuerelement wieder auf, nachdem BeginUpdate aufgerufen wurde. Erweitert alle reduzierten Knoten in der Liste. Liefert den Knoten, der dem angegebenen Punkt am nchsten liegt. Liefert die Anzahl von Elementen in der Liste; umfasst auf Wunsch alle Strukturknoten in allen Teilstrukturen.. Beschreibung Wird ausgelst, nachdem ein Kontrollkstchen neben einem Element aktiviert wurde. Wird ausgelst, nachdem ein Element reduziert wurde. Wird ausgelst, nachdem ein Element erweitert wurde. Wird ausgelst, nachdem die Bezeichnung eines Elements bearbeitet wurde. Wird ausgelst, nachdem ein Element ausgewhlt wurde. Wird ausgelst, bevor ein Kontrollkstchen neben einem Element aktiviert wird. Wird ausgelst, bevor ein Element reduziert wird. Wird ausgelst, bevor ein Element erweitert wird. Wird ausgelst, bevor die Bezeichnung eines Elements bearbeitet wird. Wird ausgelst, bevor ein Element ausgewhlt wird. Wird ausgelst, wenn der Benutzer ein Element in die Liste zieht.

CollapseAll EndUpdate

ExpandAll GetNodeAt GetNodeCount

Ereignis
AfterCheck

AfterCollapse AfterExpand AfterLabelEdit AfterSelect BeforeCheck

BeforeCollapse BeforeExpand BeforeLabelEdit BeforeSelect ItemDrag

Tabelle 1.58: Eigenschaften, Methoden und Ereignisse von TreeView (Forts.)

Das TreeNode-Steuerelement reprsentiert jedes Element in einem TreeView-Steuerelement. Jedes TreeNode-Steuerelement kann Unterknoten besitzen, um so eine hierarchisch gegliederte Ansicht zu bilden. Es ist nicht von der Control-Klasse abgeleitet. Tabelle B.59 fhrt die Eigenschaften und Methoden dieses Steuerelements auf.

67

Bonuskapitel 1

Eigenschaft
BackColor Bounds Checked FirstNode ForeColor FullPath Handle ImageIndex

Beschreibung Die Hintergrundfarbe des Knotens. Das umschlieende Rechteck des Knotens. Gibt an, ob der Knoten aktiviert ist. Liefert den ersten untergeordneten Strukturknoten in der Auflistung. Die Vordergrundfarbe des Knotens. Der TreeView-Pfad vom Stammknoten bis zum aktuellen Knoten. Der Handle fr den Knoten. Der Index der Grafik in der ImageList-Eigenschaft der umgebenden Strukturansicht, die fr den aktuellen Knoten anzuzeigen ist. Der Index des aktuellen Knotens in der Liste. Gibt an, ob sich der Knoten in einem editierbaren Zustand befindet. Gibt an, ob der Knoten erweitert ist. Gibt an, ob der Knoten ausgewhlt ist. Gibt an, ob der Knoten aktuell sichtbar ist. Liefert den letzten untergeordneten Knoten in der Auflistung. Liefert den nchsten Knoten in der Auflistung. Liefert den nchsten vollstndig sichtbaren Knoten in der Auflistung. Die fr die Textanzeige im Knoten benutzte Schriftart. Eine Auflistung der untergeordneten TreeNode-Objekte dieses Knotens. Der bergeordnete Strukturknoten des aktuellen Knotens. Liefert den vorhergehenden Knoten in der Auflistung. Liefert die vorher vollstndig sichtbaren Knoten in der Auflistung Liefert den Index der Grafik in der ImageList-Eigenschaft der umgebenden Strukturansicht, die zu verwenden ist, wenn der Knoten ausgewhlt wird. Das Objekt, das Informationen ber den Knoten enthlt.

Index IsEditing IsExpanded IsSelected IsVisible LastNode NextNode NextVisibleNode NodeFont Nodes Parent PrevNode PrevVisibleNode SelectedImageIndex

Tag

Tabelle 1.59: Eigenschaften und Methoden von TreeNode

68

Bonuskapitel 1

Eigenschaft
Text TreeView

Beschreibung Der im Knoten anzuzeigende Text. Liefert das umgebende TreeView-Steuerelement. Beschreibung Versucht, den Knoten in einen Zustand zu versetzen, in dem die Bearbeitung zulssig ist. Fertigt eine Kopie der Strukturansicht und all ihrer untergeordneten Knoten an. Reduziert den aktuellen Knoten. Stoppt den Bearbeitungsmodus, der von BeginEdit aufgerufen wurde. Stellt sicher, dass der aktuelle Knoten sichtbar ist, wobei die Methode einen Bildlauf durch die umgebende Strukturansicht ausfhrt und ggf. Strukturknoten erweitert. Erweitert den aktuellen Knoten. Erweitert alle Knoten in der Auflistung. Liefert den Strukturknoten, der mit dem angegebenen TreeView-Steuerelement und dem Handle verknpft ist. Liefert die Anzahl von Strukturknoten, die zu diesem Knoten gehren. Entfernt den aktuellen Knoten aus der Auflistung. Schaltet den Knoten entweder in den erweiterten oder den reduzierten Zustand.

Methode
BeginEdit

Clone

Collapse EndEdit EnsureVisible

Expand ExpandAll FromHandle

GetNodeCount Remove Toggle

Tabelle 1.59: Eigenschaften und Methoden von TreeNode (Forts.)

69

Bonuskapitel 2: Steuerelemente in ADO.NET: Eigenschaften und Methoden

Bonuskapitel 2

ADO.NET besteht aus zwei Teilen: dem DataSet und seinen verwandten Klassen einerseits, sowie den verwalteten Providern, die die Kommunikation mit Datenquellen ermglichen. Dieser Anhang beschreibt die Klassen in jedem dieser beiden Teile.

2.1

Die DataSet- und verwandte Klassen

Im Folgenden finden Sie Einzelheiten zum DataSet und verwandten Elementen wie etwa DataRelation, DataTable usw.

Constraint und ConstraintCollection


Die Constraint-Klasse reprsentiert eine Regel fr eine Tabelle, die die bearbeitbaren Daten eingrenzt. Tabelle C.1 listet die Eigenschaften der Constraint-Klasse auf.
Eigenschaft
ConstraintName ExtendedProperties Table

Beschreibung Der Name dieser Bedingung. Holt eine Auflistung von benutzerdefinierten Eigenschaften. Liefert eine DataTable, auf die sich diese Bedingung anwenden lsst.

Tabelle 2.1: Eigenschaften der Constraint-Klasse

Tabelle C.2 listet die Eigenschaft, die Methoden und die Ereignisse der ConstraintCollection-Klasse auf.
Eigenschaft
Item

Beschreibung Holt eine Bedingung in der Auflistung, entweder mit dem Namen der Bedingung oder ihrem Index in der Auflistung. Beschreibung Fgt der Auflistung eine Bedingung hinzu. Diese Methode ist berladen. Kopiert die Elemente aus einem anderen ConstraintCollection-Objekt in das aktuelle.

Methoden
Add AddRange

Tabelle 2.2: Die Eigenschaft, die Methoden und das Ereignis der ConstraintCollection-Klasse

72

Bonuskapitel 2

Methoden
CanRemove

Beschreibung Gibt an, ob eine Bedingung, die von Constraint festgelegt wurde, von der DataTable entfernt werden kann. Lscht die Auflistung aller Constraint-Objekte. Gibt an, ob die Bedingung mit dem Namen name in der Auflistung existiert. Holt den Index der angegebenen Bedingung. Diese Methode ist berladen. Entfernt die angegebene Bedingung aus der Auflistung. Diese Methode ist berladen. Entfernt die Bedingung beim angegebenen Index. Beschreibung Tritt auf, wenn die Auflistung durch Hinzufgungen oder Lschungen gendert wird. Verwendet ein CollectionChangeEventArgs-Objekt als Ereignisparameter. Dieses Objekt enthlt die folgenden Eigenschaften: Action: Liefert einen Wert (Hinzufgen, Entfernen, Aktualisieren), der angibt, wie sich die Auflistung verndert hat. Element: Liefert die Instanz der Auflistung, die sich verndert hat.

Clear Contains IndexOf Remove

RemoveAt

Ereignis
CollectionChanged

Tabelle 2.2: Die Eigenschaft, die Methoden und das Ereignis der ConstraintCollection-Klasse (Forts.)

DataColumn und DataColumnCollection


Eine DataColumn stellt eine Spalte von Informationen in einer DataTable dar. Tabelle C.3 listet die Eigenschaften und die Methode der DataColumn-Klasse auf.
Eigenschaft
AllowDBNull AutoIncrement

Beschreibung Gibt an, ob NULL-Werte in dieser Spalte erlaubt sind. Gibt an, ob der Wert der Spalte automatisch mit dem Hinzufgen einer neuen Zeile wchst. Der Ausgangswert fr AutoIncrement. Der Zuwachswert, den AutoIncrement benutzt. Die berschrift fr diese Spalte.

AutoIncrementSeed AutoIncrementStep Caption

Tabelle 2.3: Eigenschaften und Methode der DataColumn-Klasse

73

Bonuskapitel 2

Eigenschaft
ColumnMapping

Beschreibung Liefert ein MappingType-Objekt, das angibt, wie die Spalte abgebildet wird, wenn sie in XML geschrieben wird. Der Name der Spalte. Der Typ von Daten, der in dieser Spalte gespeichert wird. Der Standardwert fr diese Spalte, wenn neue Zeilen erzeugt werden. Ein Zeichenfolgenausdruck, der als Zeilenfilter sowie zur Berechnung des Spaltenwertes benutzt wird, oder der eine aggregierte Spalte erzeugen kann. Liefert eine PropertyCollection-Auflistung von benutzerdefinierten Benutzerinformationen. Die maximale Lnge einer Textspalte. Der XML-Namensraum, der die in dieser Spalte benutzten Elemente enthlt. Die Position dieser Spalte in der DataColumnCollection-Auflistung. Das Prfix, das fr diese Spalte benutzt wird, wenn sie als XML dargestellt wird. Gibt an, ob die Spalte nderungen zulsst. Liefert die DataTable, zu der diese Spalte gehrt. Gibt an, ob jeder Wert in dieser Spalte eindeutig sein muss. Beschreibung Liefert den Ausdruck dieser Spalte, sofern er existiert.

ColumnName DataType DefaultValue Expression

ExtendedProperties MaxLength Namespace Ordinal Prefix

ReadOnly Table Unique

Methode
ToString

Tabelle 2.3: Eigenschaften und Methode der DataColumn-Klasse (Forts.)

Tabelle C.4 listet die Eigenschaft, die Methoden und das Ereignis der DataColumnCollection-Klasse auf. Eigenschaft
Item

Beschreibung Holt eine DataColumn in der Auflistung entweder mit dem Namen der Spalte oder mit dem Index in der Auflistung.

Tabelle 2.4: Eigenschaft, Methoden und Ereignis der DataColumnCollection-Klasse

74

Bonuskapitel 2

Methoden
Add AddRange CanRemove

Beschreibung Fgt der Auflistung eine Spalte hinzu. Diese Methode ist berladen. Fgt der Auflistung ein Array von DataColumn-Objekten hinzu. Gibt an, ob eine Spalte, die durch Column angegeben ist, aus der Auflistung entfernt werden kann. Lscht alle DataColumn-Objekte in der Auflistung. Gibt an, ob die DataColumn mit dem Namen name in der Auflistung vorhanden ist. Holt den Index der angegebenen Spalte. Diese Methode ist berladen. Entfernt die angegebene Spalte aus der Auflistung. Diese Methode ist berladen. Entfernt die DataColumn beim angegebenen Index. Beschreibung Tritt auf, wenn die Auflistung durch Hinzufgungen oder Lschungen verndert wurde. Verwendet ein CollectionChangeEventArgs-Objekt als Ereignisparameter. Dieses Objekt enthlt die folgenden Eigenschaften: Action: Liefert einen Wert (Add, Remove, Refresh), der angibt, inwiefern sich die Auflistung verndert hat. Element: Liefert die Instanz der Auflistung, die sich verndert hat.

Clear Contains

IndexOf Remove

RemoveAt

Ereignis
CollectionChanged

Tabelle 2.4: Eigenschaft, Methoden und Ereignis der DataColumnCollection-Klasse (Forts.)

2.2

DataRelation und DataRelationCollection

Eine DataRelation-Klasse stellt die Beziehungen zwischen mehreren Tabellen im DataSet dar. Tabelle C.5 listet die Eigenschaften der DataRelation-Klasse auf.
Eigenschaft
ChildColumns

Beschreibung Liefert ein Array von DataColumn-Objekten, die die untergeordneten Spalten dieser Beziehung darstellen. Ein ForeignKeyConstraint-Objekt fr diese Beziehung.

ChildKeyConstraint

Tabelle 2.5: Eigenschaften der DataRelation-Klasse

75

Bonuskapitel 2

Eigenschaft
ChildTable

Beschreibung Liefert eine DataTable, die die untergeordnete Tabelle dieser Beziehung darstellt. Liefert das DataSet, zu dem diese Beziehung gehrt. Liefert eine PropertyCollection von benutzerdefinierten Benutzerinformationen. Gibt an, ob Beziehungen verschachtelt sind. Ein Array von DataColumn-Objekten, die die bergeordneten Spalten dieser Beziehung darstellen. Ein UniqueConstraint-Objekt fr diese Beziehung. Liefert die DataTable, die die bergeordnete Tabelle dieser Beziehung darstellt. Der Name dieser Beziehung.

DataSet ExtendedProperties Nested ParentColumns

ParentKeyConstraint ParentTable

RelationName

Tabelle 2.5: Eigenschaften der DataRelation-Klasse (Forts.)

Tabelle C.6 listet die Eigenschaft, die Methoden und das Ereignis der DataRelationCollection-Klasse auf. Eigenschaft
Item

Beschreibung Holt eine DataRelation aus der Auflistung, entweder mit dem Namen der Beziehung oder mit ihrem Index in der Auflistung. Beschreibung Fgt der Auflistung eine Beziehung hinzu. Diese Methode ist berladen. Fgt ein Array von DataRelation-Objekten zur Auflistung hinzu. Prft, ob sich die angegebene DataRelation aus der Auflistung entfernen lsst. Lscht alle DataRelation-Objekte aus der Auflistung. Gibt an, ob die DataRelation mit dem Namen name in der Auflistung existiert. Liefert den Index der angegebenen DataRelation.

Methode
Add AddRange CanRemove Clear Contains IndexOf

Tabelle 2.6: Eigenschaft, Methoden und Ereignis der DataRelationCollection-Klasse

76

Bonuskapitel 2

Methode
Remove

Beschreibung Entfernt die angegebene Beziehung aus der Auflistung. Diese Methode ist berladen. Entfernt die DataRelation am angegebenen Index. Beschreibung Tritt auf, wenn die Auflistung durch Hinzufgungen oder Lschungen verndert wird. Benutzt ein CollectionChangeEventArgs-Objekt als Ereignisparameter. Dieses Objekt enthlt die folgenden Eigenschaften: Action: Liefert einen Wert (Add, Remove, Refresh), der angibt, inwiefern die Auflistung verndert wurde. Element: Liefert die Instanz der Auflistung, die sich verndert hat.

RemoveAt

Ereignis
CollectionChanged

Tabelle 2.6: Eigenschaft, Methoden und Ereignis der DataRelationCollection-Klasse (Forts.)

2.3

DataRow und DataRowCollection

Eine DataRow reprsentiert eine Zeile von Informationen in einer DataTable, das heit, einen einzelnen Datensatz. Tabelle C.7 listet die Eigenschaften und Methoden der DataRow-Klasse auf.
Eigenschaft
HasErrors Item

Beschreibung Gibt an, ob die Daten in der Zeile Fehler enthalten. Legt die in der angegebenen Spalte enthaltenen Daten fest. Die Methode ist berladen. Legt mit Hilfe eines Arrays die in der ganzen Zeile enthaltenen Daten fest. Die benutzerdefinierte Fehlerbeschreibung fr die Zeile. Der Status der Zeile. Kann Detached, Unchanged, New, Deleted oder Modified sein. Liefert die DataTable, zu der diese Zeile gehrt. Beschreibung Speichert alle an der Zeile vorgenommenen nderungen. Beginnt eine Bearbeitung der Zeile.

ItemArray RowError RowState

Table

Methode
AcceptChanges BeginEdit

Tabelle 2.7: Eigenschaften und Methoden der DataRow-Klasse

77

Bonuskapitel 2

Methode
CancelEdit ClearErrors Delete EndEdit GetChildRows

Beschreibung Bricht die Bearbeitung ab und macht alle nderungen rckgngig. Lscht alle Fehler in der Zeile. Lscht die Zeile. Beendet die Bearbeitung der Zeile. Liefert ein Array von DataRow-Objekten, die die untergeordneten Zeilen dieser Zeile darstellen, wenn die angegebene DataRelation benutzt wird. Holt die Fehler fr eine angegebene Spalte in der Zeile. Diese Methode ist berladen. Holt ein Array von DataColumn-Objekten, die Fehler aufweisen. Liefert eine DataRow, die die bergeordnete Zeile dieser Zeile darstellt. Diese Methode ist berladen. Liefert ein Array von DataRow-Objekten, die mit der angegebenen DataRelation die bergeordnete Zeilen dieser Zeile reprsentieren. Diese Methode ist berladen. Gibt an, ob eine bestimmte Version dieser Zeile existiert. Gibt an, ob die angegebene Spalte in der Zeile einen NULL-Wert enthlt. Diese Methode ist berladen. Macht alle nderungen an der Zeile rckgngig. Legt die Fehlerbeschreibung fr die Spalte fest. Diese Methode ist berladen. Legt die bergeordnete Zeile einer angegebenen untergeordneten Zeile fest. Diese Methode ist berladen.

GetColumnError

GetColumnsInError GetParentRow

GetParentRows

HasVersion IsNull

RejectChanges SetColumnError SetParentRow

Tabelle 2.7: Eigenschaften und Methoden der DataRow-Klasse(Forts.)

Tabelle C.8 listet die Eigenschaft und die Methoden der DataRowCollection-Klasse auf.
Eigenschaft
Item

Beschreibung Holt eine DataRow aus der Auflistung, entweder mit dem Namen der Zeile oder mit ihrem Index in der Auflistung.

Tabelle 2.8: Eigenschaft und Methoden der DataRowCollection-Klasse

78

Bonuskapitel 2

Methode
Add Clear Contains Find InsertAt Remove

Beschreibung Fgt eine Zeile in die Auflistung ein. Diese Methode ist berladen. Lscht alle DataRow-Objekte in der Auflistung. Gibt an, ob die DataRow mit dem Namen name in der Auflistung existiert. Holt eine angegebene DataRow. Diese Methode ist berladen. Fgt eine neue DataRow an der angegebenen Position ein. Entfernt die angegebene Zeile aus der Auflistung. Diese Methode ist berladen. Entfernt die DataRow am angegebenen Index.

RemoveAt

Tabelle 2.8: Eigenschaft und Methoden der DataRowCollection-Klasse

DataSet
Tabelle C.9 listet die Eigenschaften, Methoden und das Ereignis der DataSet-Klasse auf, welches eine unverbundene Datenquelle reprsentiert.
Eigenschaft
CaseSensitive

Beschreibung Gibt an, ob Zeichenfolgenvergleiche in einer DataTable auf Gro-/Kleinschreibung achten. Der Name dieses DataSets. Liefert einen DataViewManager, der eine benutzerdefinierte Sicht der Daten im DataSet enthlt. Gibt an, ob bei der Datenaktualisierung Bedingungsregeln befolgt werden. Ein PropertyCollection-Objekt, das benutzerdefinierte Benutzerinformationen enthlt. Gibt an, ob die Daten in einer der Zeilen dieses DataSets Fehler enthalten. Die Positionsinformation, die fr den Zeichenfolgenvergleich benutzt wird. Liefert ein CultureInfo-Objekt. Gibt den Namensraum des DataSets an.

DataSetName DefaultViewManager EnforceConstraints ExtendedProperties HasErrors Locale

Namespace

Tabelle 2.9: Eigenschaften, Methoden und Ereignis der DataSet-Klasse

79

Bonuskapitel 2

Eigenschaft
Prefix Relations

Beschreibung Ein XML-Alias fr den Namensraum des DataSets. Ein DataRelationCollection-Objekt, das alle Beziehungen zwischen Tabellen im DataSet reprsentiert. Liefert eine ISite-Schnittstelle fr das DataSet (wird fr das Anbinden von Komponenten an Container verwendet). Ein DataTableCollection-Objekt, das alle Tabellen in dem DataSet reprsentiert Beschreibung Speichert alle am DataSet vorgenommenen nderungen. Entfernt alle Zeilen in allen Tabellen im DataSet. Erzeugt ein DataSet, das mit dem aktuellen DataSet identisch ist, aber keine Daten enthlt. Erzeugt ein DataSet, das mit dem aktuellen DataSet identisch ist, aber diesmal Daten enthlt. Erzeugt ein DataSet, das nur die Daten enthlt, die verndert wurden. Liefert die Daten im DataSet im XML-Format. Liefert das XML-Schema fr die Daten im DataSet. Gibt an, ob die Daten im DataSet gendert wurden. Erstellt die Datenstruktur aus einer XML-Datenquelle. Diese Funktion ist berladen. Fhrt das angegebene DataSet mit einem anderen zusammen. Fgt Daten und Schemainformationen aus einer XML-Datei in ein DataSet ein. Erstellt die Datenstruktur aus einem XML-Schema. Diese Funktion ist berladen. Macht alle nderungen an diesem DataSet rckgngig. Setzt das DataSet auf seine Standardeigenschaften zurck.

Site

Tables

Methode
AcceptChanges Clear Clone

Copy

GetChanges GetXml GetXmlSchema HasChanges InferXmlSchema

Merge ReadXml

ReadXmlSchema

RejectChanges Reset

Tabelle 2.9: Eigenschaften, Methoden und Ereignis der DataSet-Klasse(Forts.)

80

Bonuskapitel 2

Methode
WriteXml

Beschreibung Schreibt den Inhalt des DataSets im XML-Format. Diese Funktion ist berladen. Schreibt die Struktur des DataSets im XML-Format. Diese Funktion ist berladen. Beschreibung Tritt auf, wenn eine Ziel- und Quell-DataRow den gleichen Primrschlsselwert haben und EnforceConstraints true ist.

WriteXmlSchema

Ereignis
MergeFailed

Tabelle 2.9: Eigenschaften, Methoden und Ereignis der DataSet-Klasse(Forts.)

DataTable und DataTableCollection


Eine DataTable reprsentiert eine Tabelle von Informationen in einem DataSet. Tabelle C.10 listet die Eigenschaften, Methoden und Ereignisse der DataTable-Klasse auf.
Eigenschaft
CaseSensitive

Beschreibung Gibt an, ob Zeichenfolgenvergleiche in der Tabelle auf Gro-/Kleinschreibung achten. Liefert eine DataRelationCollection der untergeordneten Beziehungen dieser Tabelle. Liefert ein DataColumnCollection-Objekt, das die Spalte in dieser Tabelle reprsentiert. Liefert ein DataRelationCollection-Objekt, das die Datenbeziehungen in dieser Tabelle reprsentiert. Liefert das DataSet, zu dem diese Tabelle gehrt. Liefert eine DataView, die eine benutzerdefinierte Sicht der Daten in dieser Tabelle darstellt. Ein Zeichenfolgenausdruck, der einen Wert liefert, der angibt, wie diese Tabelle in der Benutzeroberflche angezeigt werden soll. Liefert eine PropertyCollection von benutzerdefinierten Benutzerinformationen. Gibt an, ob es Fehler in einer der Zeilen dieser Tabelle gibt.

ChildRelations

Columns

Constraints

DataSet DefaultView

DisplayExpression

ExtendedProperties HasErrors

Tabelle 2.10: Eigenschaften, Methoden und Ereignisse der DataTable-Klasse

81

Bonuskapitel 2

Eigenschaft
Locale

Beschreibung Ein CultureInfo-Objekt, das man dazu einsetzt festzulegen, wie Zeichenfolgen miteinander verglichen werden. Die anfngliche Gre dieser Tabelle. Der XML-Namensraum, der die Elemente in dieser Tabelle enthlt. Eine DataRelationCollection der bergeordneten Beziehungen dieser Tabelle. Das Prfix, das man fr diese Tabelle benutzt, wenn sie als XML dargestellt wird. Ein Array von DataColumn-Objekten, die als Primrschlssel fr die Tabelle dienen. Ein DataRowCollection-Objekt, das die Zeilen darstellt, die zu dieser Tabelle gehren. Liefert eine ISite-Schnittstelle fr die DataTable (wird benutzt, um Komponenten an Container zu binden). Der Name der Tabelle. Beschreibung Speichert alle an der Tabelle vorgenommenen nderungen. Beginnt mit der Initialisierung dieser Tabelle. Beginnt mit dem Datenladevorgang. Lscht alle Daten aus der Tabelle. Fertigt eine Kopie der DataTable-Struktur an, einschlielich all ihrer Beziehungen. Berechnet den Ausdruck, der im ersten Parameter in den Zeilen angegeben ist, die den angegebenen Filter durchlaufen. Kopiert sowohl die Struktur als auch die Daten, die in der DataTable enthalten sind. Beendet den Initialisierungsvorgang. Beendet den Datenladevorgang.

MinimumCapacity Namespace ParentRelations

Prefix

PrimaryKey

Rows

Site

TableName

Methode
AcceptChanges BeginInit BeginLoadData Clear Clone

Compute

Copy

EndInit EndLoadData

Tabelle 2.10: Eigenschaften, Methoden und Ereignisse der DataTable-Klasse(Forts.)

82

Bonuskapitel 2

Methode
GetChanges

Beschreibung Holt eine Kopie der DataTable, die diejenigen nderungen enthlt, die seit ihrer Erstellung angefallen sind oder seit AcceptChanges aufgerufen wurde. Ein Array von DataRow-Objekten, die Fehler enthalten. Kopiert ein DataRow-Objekt in die DataTable. Sucht und aktualisiert eine DataRow mit den angegebenen Werten. Wird eine Zeile nicht gefunden, wird eine neue erzeugt. Liefert eine leere DataRow mit dem gleichen Schema wie die Tabelle. Macht alle nderungen an der Tabelle rckgngig, die vorgenommen wurden, seit sie erstmals geladen wurde oder seit AcceptChanges zuletzt aufgerufen wurde. Setzt die DataTable auf ihre Standardeigenschaften zurck. Liefert ein Array von DataRow-Objekten. Diese Methode ist berladen. Liefert TableName und DisplayExpression fr diese Tabelle. Beschreibung Tritt auf, wenn eine Spalte gendert wurde. Verwendet ein DataColumnChangedEventArgs-Objekt als Ereignisparameter. Dieses Objekt enthlt die folgenden Eigenschaften:
Column: Die Spalte wurde verndert. ProposedValue: Der Wert, in den die Spalte zu ndern ist.

GetErrors ImportRow LoadDataRow

NewRow RejectChanges

Reset Select ToString

Ereignis
ColumnChanged

Row: Die zu ndernde DataRow.


ColumnChanging

Tritt auf, wenn nderungen an dieser Spalte gespeichert werden sollen. Verwendet ein DataColumnChangedEventArgs-Objekt. Tritt auf, wenn eine Zeile verndert wurde. Verwendet ein DataRowChangedEventArgs-Objekt als Ereignisparameter. Dieses Objekt enthlt die folgenden Eigenschaften: Action: Die Aktion, die an dieser DataRow ausgefhrt wurde. Row: Die zu ndernde DataRow. Tritt auf, wenn nderungen an dieser Spalte gespeichert werden sollen. Verwendet ein DataRowChangedEventArgs-Objekt.

RowChanged

RowChanging

Tabelle 2.10: Eigenschaften, Methoden und Ereignisse der DataTable-Klasse(Forts.)

83

Bonuskapitel 2

Methode
RowDeleted

Beschreibung Tritt auf, nachdem eine Zeile gelscht wurde. Verwendet ein DataRowChangedEventArgs-Objekt.

RowDeleting

Tritt auf, bevor eine Zeile gelscht wird. Verwendet ein DataRowChangedEventArgs-Objekt.

Tabelle 2.10: Eigenschaften, Methoden und Ereignisse der DataTable-Klasse(Forts.)

Tabelle C.11 listet die Eigenschaft, Methoden und Ereignisse der DataTableCollectionKlasse auf.
Eigenschaft
Item

Beschreibung Holt eine DataTable aus der Auflistung, entweder mit dem Namen der Tabelle oder mit ihrem Index in der Auflistung. Beschreibung Fgt der Auflistung eine Tabelle hinzu. Diese Methode ist berladen. Fgt der Auflistung ein Array von DataTable-Objekten hinzu. Gibt an, ob die angegebene Tabelle aus der Auflistung entfernt werden kann. Lscht alle DataTable-Objekte aus der Auflistung. Gibt an, ob die DataTable mit dem angegebenen Namen in der Auflistung existiert. Holt den Index der angegebenen Tabelle. Diese Methode ist berladen. Entfernt die angegebene Tabelle aus der Auflistung. Diese Methode ist berladen. Entfernt die DataTable am angegebenen Index. Beschreibung Tritt auf, wenn die Auflistung durch Hinzufgen oder Entfernen von Elementen verndert wird. Verwendet ein CollectionChangeEventArgs-Objekt als Ereignisparameter. Dieses Objekt enthlt die folgenden Eigenschaften: Action: Liefert einen Wert (Add, Remove, Refresh), der angibt, wie sich die Auflistung verndert hat. Element: Liefert die Instanz der vernderten Auflistung.

Methoden
Add AddRange CanRemove Clear Contains

IndexOf Remove

RemoveAt

Ereignis
CollectionChanged

Tabelle 2.11: Eigenschaft, Methoden und Ereignisse der DataTableCollection-Klasse

84

Bonuskapitel 2

Ereignis
CollectionChanging

Beschreibung Tritt auf, bevor die Auflistung verndert wird. Verwendet ein CollectionChangeEventArgs-Objekt.

Tabelle 2.11: Eigenschaft, Methoden und Ereignisse der DataTableCollection-Klasse(Forts.)

DataView
Eine DataView-Klasse reprsentiert eine benutzerdefinierte Sicht auf Daten in einem DataSet. Tabelle C.12 listet die Eigenschaften, die Methoden und das Ereignis der DataViewKlasse auf.
Eigenschaft
AllowDelete AllowEdit AllowNew ApplyDefaultSort Count DataViewManager

Beschreibung Gibt an, ob Lschvorgnge in dieser Ansicht erlaubt sind. Gibt an, ob Editiervorgnge in dieser Ansicht erlaubt sind. Gibt an, ob neue Zeilen in dieser Ansicht hinzugefgt werden knnen. Gibt an, ob die Standardsortierfolge benutzt werden soll. Liefert die Anzahl der Datenstze in der DataView. Die DataView, die diese Ansicht erzeugt hat (ein Zeiger auf die DataSetView, die das korrespondierende DataSet besitzt). Holt eine angegebene Zeile von Daten aus einer Tabelle. Ein Ausdruck, der als Filter dafr benutzt wird, welche Zeilen der DataView hinzugefgt werden. Legt fest, welche Zeilenversion der DataView hinzugefgt wird. Kann None, Unchanged, New, Deleted, ModifiedCurrent, ModifiedOriginal, OriginalRows oder CurrentRows (Standard) sein. Die Spalten, nach denen sortiert wird. Die Quell-DataTable, aus der Daten herauszuziehen sind. Beschreibung Fgt der DataView eine neue Zeile hinzu. Beginnt mit der Initialisierung dieser DataView.

Item RowFilter

RowStateFilter

Sort Table

Methode
AddNew BeginInit

Tabelle 2.12: Eigenschaften, Methoden und Ereignis der DataView-Klasse

85

Bonuskapitel 2

Methode
CopyTo Delete EndInit Find FindRows

Beschreibung Kopiert Elemente in ein Array. Lscht eine Zeile am angegebenen Index. Beendet den Initialisierungsprozess. Sucht eine angegebene Zeile in der DataView. Diese Methode ist berladen. Liefert ein Array von DataRowView-Objekten, die den angegebenen Kriterien entsprechen. Liefert einen IEnumerator, der sich dazu verwenden lsst, durch diese DataView zu iterieren. Beschreibung Tritt auf, wenn sich die Liste, die von der DataView verwaltet wird, ndert. Verwendet ein ListChangedEventArgs-Objekt als Ereignisparameter. Dieses Objekt enthlt die folgenden Eigenschaften: ListChangedType: Die Art und Weise, wie sich die Liste gendert hat. NewIndex: Der neue Index des genderten Elements. OldIndex: Der alte Index des genderten Elements.

GetEnumerator

Ereignis
ListChanged

Tabelle 2.12: Eigenschaften, Methoden und Ereignis der DataView-Klasse(Forts.)

2.4

Verwaltete Provider

Mit verwalteten Providern kann ADO.NET mit jedem Typ von OLE DB-konformen Datenquellen interagieren. Diese Provider werden dazu eingesetzt, Daten in das DataSet und dessen verwandte Klassen zu bewegen, und lassen sich zustzlich unabhngig voneinander einsetzen, um Daten zu bearbeiten. Es gibt zweierlei verwaltete Provider: OleDB und Sql. Der erste befasst sich mit allen OLE DB-konformen Datenspeichern, wohingegen der zweite nur mit SQL Server zusammenarbeitet. In fast allen Fllen entsprechen die Klassen des einen Providers genau den Klassen des anderen; der einzige Unterschied besteht in der Verwendung des Prfixes: OleDb und Sql. So haben z.B. beide Provider eine Klasse, die raschen Zugriff auf Daten gewhrt, die jeweils OleDbDataReader bzw. SqlDataReader heit. Wegen der hnlichkeit dieser beiden Provider wird hier nur der verwaltete OleDb-Provider behandelt. Besteht ein Unterschied zur SQL-Version, so wird dies erwhnt.

86

Bonuskapitel 2

OleDbCommand
Die OleDbCommand-Klasse reprsentiert ein SQL-Statement, das fr eine Datenquelle zu erzeugen ist. Tabelle C.13 listet die Eigenschaften und Methoden dieser Klasse auf.
Eigenschaft
CommandText CommandTimeout

Beschreibung Das auszufhrende SQL-Statement. Das Zeit-Limit, innerhalb dessen der Befehl vor seiner Beendigung ausgefhrt sein muss. Legt fest, wie die CommandText-Eigenschaft interpretiert wird. Kann StoredProcedure, TableDirect oder Text (Standard) sein. Legt eine OleDbConnection fest, die von diesem Objekt benutzt wird. Gibt an, ob das Befehlsobjekt in einem benutzerdefinierten Windows FormsDesigner sichtbar sein soll. Holt ein OleDbParameterCollection-Objekt, das die Parameter fr den Einsatz mit diesem Befehl reprsentiert. Die OleDbTransaction, die der Befehl benutzt. Die Anzahl von Datenstzen, die von dem Befehl betroffen sind. Normalerweise 1, falls der Befehl erfolgreich ausgefhrt wird, und kleiner 1, falls nicht. Beschreibung Bricht die Ausfhrung des Befehls ab. Erzeugt einen OleDbParameter zur Verwendung mit diesem Befehl. Fhrt ein SQL-Statement aus, das keinerlei Daten liefert. Liefert einen OleDbDataReader, der mit den Daten gefllt ist, die der Befehl geliefert hat. Fhrt die Abfrage aus und liefert Werte fr die erste Spalte und erste Zeile der gelieferten Ergebnisse. Erzeugt eine kompilierte Version des Befehls. Setzt die CommandTimeout-Eigenschaft auf den Standardwert zurck.

CommandType

Connection DesignTimeVisible

Parameters

Transaction UpdatedRowSource

Methode
Cancel CreateParameter ExecuteNonQuery ExecuteReader

ExecuteScalar

Prepare ResetCommandTimeout

Tabelle 2.13: Eigenschaften und Methoden der OleDbCommand-Klasse

87

Bonuskapitel 2

OleDBCommandBuilder
Die OleDbCommandBuilder-Klasse stellt eine einfache Mglichkeit dar, Befehle fr eine bestimmte Datenquelle zu erzeugen. Tabelle C.14 listet die Eigenschaften und Methoden dieser Klasse auf.
Eigenschaft
DataAdapter QuotePrefix

Beschreibung Der Name eines OleDbDataAdapters, fr den die Befehle erzeugt wurden. Legt ein Prfix fest, das zu benutzen ist, wenn man Datenquellenobjektnamen festlegt (wie etwa tbl fr Tabellen, sp fr gespeicherte Prozeduren (Stored Procedures)und so weiter). Legt ein Suffix fest, das zu benutzen ist, wenn man Objektnamen von Datenquellen festlegt. Beschreibung Fllt die Parameters-Auflistung von OleDbCommand mit in der gespeicherten Prozedur angegebenen Werten. Holt das automatisch generierte SQL-Statement, um Zeilen aus der Datenquelle zu lschen. Holt das automatisch generierte SQL-Statement, um Zeilen in die Datenquelle einzufgen. Holt das automatisch generierte SQL-Statement, um Zeilen in der Datenquelle zu aktualisieren. Holt das Schema der Datenquelle.

QuoteSuffix

Methode
DeriveParameters

GetDeleteCommand

GetInsertCommand

GetUpdateCommand

RefreshSchema

Tabelle 2.14: Eigenschaften und Methoden der OleDbCommandBuilder-Klasse

OleDbConnection
Die OleDbConnection-Klasse reprsentiert eine Verbindung zu einer Datenquelle. Tabelle C.15 listet die Eigenschaften, Methoden und Ereignisse dieser Klasse auf.
Eigenschaft
ConnectionString

Beschreibung Die Zeichenfolge, mit der eine Datenbank geffnet wird.

Tabelle 2.15: Eigenschaften, Methoden und Ereignisse der OleDbConnection-Klasse

88

Bonuskapitel 2

Eigenschaft
ConnectionTimeout

Beschreibung Das Zeit-Limit fr das Zustandekommen einer Verbindung zur Datenbank, nach dessen berschreiten eine Fehlermeldung erzeugt wird. Der Name der Datenbank, der nach dem Zustandekommen einer Verbindung zu verwenden ist. Der Name der Datenbank, zu der eine Verbindung aufzubauen ist. Der Name des Datenbank-Providers. Eine Zeichenfolge, die die Version des Servers enthlt, mit dem der Client verbunden ist. Der aktuelle Zustand der Verbindung. Beschreibung Beginnt mit einer Datenbanktransaktion. Diese Methode ist berladen. ndert die aktuelle Datenbank auf den angegebenen Wert. Schliet die Datenbankverbindung. Liefert ein OleDbCommand-Objekt, um damit Befehle gegen die Datenbank auszufhren. Liefert Schemainformationen aus einer angegebenen Datenquelle durch eine GUID. (Diese Methode existiert nicht in der entsprechenden SqlConnectionKlasse.) Versucht, eine Verbindung zur Datenbank zu ffnen. Gibt an, dass das Pooling von OleDbConnection-Objekten gelscht werden kann, sobald der letzte zugrunde liegende OLE DB-Provider freigegeben worden ist. Beschreibung Tritt auf, wenn der Provider eine Mitteilung schickt. Verwendet ein OleDbInfoMessageEventArgs-Objekt als Ereignisparameter. Dieses Objekt enthlt die folgenden Eigenschaften: ErrorCode: Ein HRESULT-Wert, der die Standardfehlermeldung angibt. Error: Eine OleDbErrorCollection von Alarmmeldungen, die der Provider schickt. Message: Der vollstndige Text der Fehlermeldung aus dem Provider. Source: Der Name des Objekts, das den Fehler erzeugt hat.

Database

DataSource Provider ServerVersion

State

Methode
BeginTransaction ChangeDatabase Close CreateCommand

GetOleDbSchemaTable

Open ReleaseObjectPool

Ereignis
InfoMessage

Tabelle 2.15: Eigenschaften, Methoden und Ereignisse der OleDbConnection-Klasse(Forts.)

89

Bonuskapitel 2

Ereignis
StateChange

Beschreibung Tritt auf, wenn sich der Zustand der Verbindung ndert. Verwendet ein StateChangeEventArgsObjekt als Ereignisparameter. Dieses Objekt enthlt die folgenden Eigenschaften: CurrentStat: Der neue Zustand der Verbindung. OriginalStat: Der ursprngliche Zustand der Verbindung.

Tabelle 2.15: Eigenschaften, Methoden und Ereignisse der OleDbConnection-Klasse(Forts.)

OleDbDataAdapter
Die OleDbDataAdapter-Klasse reprsentiert eine Menge datenbezogener Befehle sowie eine Datenbankverbindung, die dazu verwendet werden knnen, ein DataSet zu fllen. Tabelle C.16 listet die Eigenschaften, Methoden und Ereignisse dieser Klasse auf.
Eigenschaft
DeleteCommand

Beschreibung Liefert ein OleDbCommand-Objekt, das ein SQL-Statement fr die Lschung von Daten aus dem DataSet enthlt. Liefert ein OleDbCommand-Objekt, das ein SQL-Statement fr das Einfgen von Daten in das DataSet enthlt. Liefert ein OleDbCommand-Objekt, das ein SQL-Statement fr die Auswahl von Daten aus dem DataSet enthlt. Liefert ein OleDbCommand-Objekt, das ein SQL-Statement fr die Aktualisierung von Daten im DataSet enthlt. Beschreibung Fgt einem DataSet Zeilen hinzu oder ndert sie, damit es der Datenquelle entspricht. Diese Methode ist berladen. Fgt eine DataTable dem angegebenen DataSet hinzu und konfiguriert das Schema der Tabelle. Diese Methode ist berladen. Liefert ein Array von IDataParameter-Objekten, die mit Hilfe des SELECTBefehls eingesetzt werden. Aktualisiert die Datenquelle mit den Informationen im DataSet mit Hilfe der Eigenschaften Delete, Insert und UpdateCommand. Diese Methode ist berladen.

InsertCommand

SelectCommand

UpdateCommand

Methode
Fill

FillSchema

GetFillParameters

Update

Tabelle 2.16: Eigenschaften, Methoden und Ereignisse der OleDbDataAdapter-Klasse

90

Bonuskapitel 2

Ereignis
FillError

Beschreibung Tritt auf, wenn ein Fehler whrend einer Fill-Operation geliefert wird. Verwendet ein FillErrorEventArgs-Objekt als Ereignisparameter. Dieses Objekt enthlt die folgenden Eigenschaften: Continue: Gibt an, ob die Operation fortzusetzen ist. DataTable: Die DataTable, die beim Auftreten des Fehlers gerade aktualisiert wurde. Errors: Liefert ein Exception-Objekt, das die zu behandelnden Fehler reprsentiert. Values: Liefert ein Objekt, das die Werte fr diejenige Zeile reprsentiert, die beim Auftreten des Fehlers gerade aktualisiert wurde. Tritt auf, nachdem ein Update-Befehl ausgefhrt worden ist. Verwendet ein OleDbRowUpdatedEventArgs-Objekt als Ereignisparameter. Dieses Objekt enthlt die folgenden Eigenschaften: Command: Liefert den OleDbCommand, der ausgefhrt wird, sobald Update aufgerufen wurde. Errors: Liefert ein Exception-Objekt, das die aufgetretenen Fehler reprsentiert. RecordsAffected: Die Anzahl der betroffenen Datenstze. Row: Holt die DataRow, die Update verwendet. StatementType: Der Typ des SQL-Statements, das ausgefhrt wird. Status: Ein UpdateStatus-Objekt, das den Status des Befehls reprsentiert. TableMapping: Holt die DataTableMapping, die mit dem Update-Befehl geschickt wird. Tritt auf, bevor ein Update-Befehl ausgefhrt worden ist. Verwendet ein OleDbRowUpdatingEventArgs-Objekt als Ereignisparameter. Dieses Objekt enthlt die folgenden Eigenschaften: Command: Liefert den OleDbCommand, der beim Aufruf von Update auszufhren ist. Errors: Liefert ein Exception-Objekt, das die aufgetretenen Fehler reprsentiert. Row: Holt die DataRow, die Update verwendet. StatementType: Der Typ des SQL-Statements, das auszufhren ist. Status: Ein UpdateStatus-Objekt, das den Status des Befehls reprsentiert. TableMapping: Holt die DataTableMapping, die mit dem Update-Befehl geschickt wird.

RowUpdated

RowUpdating

Tabelle 2.16: Eigenschaften, Methoden und Ereignisse der OleDbDataAdapter-Klasse(Forts.)

91

Bonuskapitel 2

OleDbdataReader
Die OleDbDataReader-Klasse reprsentiert eine rasche Strom-basierte Methode fr den Datenzugriff auf eine Datenquelle. Sie gleicht einem DataSet, bietet aber geringere Funktionalitt bei hherer Performanz. Tabelle C.17 listet die Eigenschaften und Methoden dieser Klasse auf.
Eigenschaft
Depth FieldCount IsClosed Item

Beschreibung Die Tiefe des Lesers. Die Anzahl der Felder im aktuellen Datensatz. Gibt an, ob der Datenleser geschlossen wurde. Der Wert der angegebenen Spalte in seinem nativen Format. Diese Methode ist berladen. Die Anzahl der vom Befehl betroffenen Datenstze. Normalerweise ist diese Anzahl 1, wenn der Befehl erfolgreich ausgefhrt wurde, und kleiner 1, falls nicht. Beschreibung Schliet das OleDbDataReader-Objekt. Liefert den Wert in der angegebenen Spalte als booleschen Wert. Liefert den Wert in der angegebenen Spalte als Byte-Wert. Liefert den Wert in der angegebenen Spalte als Byte-Array. Liefert den Wert in der angegebenen Spalte als Char-Wert. Liefert den Wert in der angegebenen Spalte als Char-Array. Liefert den Datentyp der angegebenen Spalte. Liefert den Wert in der angegebenen Spalte als DateTime-Wert. Liefert den Wert in der angegebenen Spalte als Decimal-Wert. Liefert den Wert in der angegebenen Spalte als Double-Wert. Holt den Type, der dem Datentyp des Objekts entspricht. Liefert den Wert in der angegebenen Spalte als Float-Wert.

RecordsAffected

Methode
Close GetBoolean GetByte GetBytes GetChar GetChars GetDataTypeName GetDateTime GetDecimal GetDouble GetFieldType GetFloat

Tabelle 2.17: Eigenschaften und Methoden der OleDbDataReader-Klasse

92

Bonuskapitel 2

Methode
GetGuid GetInt16 GetInt32 GetInt64 GetName GetOrdinal GetSchemaTable

Beschreibung Liefert den Wert in der angegebenen Spalte als GUID-Wert. Liefert den Wert in der angegebenen Spalte als 16-Bit-Integer. Liefert den Wert in der angegebenen Spalte als 32-Bit-Integer. Liefert den Wert an der angegebenen Spalte als 64-Bit-Integer. Liefert den Namen der angegebenen Spalte. Holt den Index der Spalte, je nach dem Namen der Spalte. Liefert ein DataTable-Objekt, das die Spaltenmetadaten fr dieses Objekt beschreibt. Liefert den Wert in der angegebenen Spalte als String-Wert. Liefert den Wert in der angegebenen Spalte als TimeSpan-Wert. Liefert den Wert in der angegebenen Spalte in seinem nativen Format. Liefert alle Attribute des aktuellen Datensatzes und legt sie in einem angegebenen Array ab. Wird verwendet, um nicht vorhandene Werte anzuzeigen. Bewegt den Leser zum nchsten Ergebnis, wenn er die Resultate von StapelSQL-Statements liest. Bewegt den Leser zum nchsten Datensatz.

GetString GetTimeSpan GetValue GetValues

IsDBNull NextResult

Read

Tabelle 2.17: Eigenschaften und Methoden der OleDbDataReader-Klasse (Forts.)

OleDbError und OleDbErrorCollection


Die OleDbError-Klasse sammelt Informationen ber eine Warnung, die von der Datenquelle geschickt wurde. Tabelle C.18 listet die Eigenschaften dieser Klasse auf.
Eigenschaft
Message NativeError

Beschreibung Eine kurze Beschreibung des Fehlers. Die datenbankspezifische Fehlerinformation.

Tabelle 2.18: Eigenschaften der OleDbError-Klasse

93

Bonuskapitel 2

Eigenschaft
Source SQLState

Beschreibung Holt das Objekt, das den Fehler verursachte. Holt den Standardfehlercode aus fnf Zeichen fr die Datenbank.

Tabelle 2.18: Eigenschaften der OleDbError-Klasse (Forts.)

Tabelle C.19 listet Eigenschaften und Methode der OleDbErrorCollection-Klasse auf, die eine Auflistung von OleDbError-Objekten reprsentiert.
Eigenschaft
Count Item

Beschreibung Die Anzahl der Fehler in der Auflistung. Holt ein OleDbError-Objekt aus der Auflistung mit dem angegebenen Index. Beschreibung Kopiert die gesamte Auflistung in ein Array, beginnend am angegebenen Index.

Methode
CopyTo

Tabelle 2.19: Eigenschaften und Methode der OleDbErrorCollection-Klasse

OleDbParameter und OleDbParameterCollection


Die OleDbParameter-Klasse reprsentiert einen Wert, der in Begleitung eines Datenbankbefehls bergeben wird, um zustzliche Informationen oder Optionen bereitzustellen. Tabelle C.20 listet Eigenschaften und Methode dieser Klasse auf.
Eigenschaft
DbType Direction

Beschreibung Der Datentyp der Datenquelle. Gibt an, wie der Parameter benutzt wird. Kann Input, InputOutput, Output oder ReturnValue sein. Gibt an, ob der Parameter einen NULL-Wert enthalten darf. Legt den Type dieses Parameters fest. (Diese Eigenschaft existiert in der entsprechenden SqlParameter-Klasse nicht.) Der Offset fr die Value-Eigenschaft. (Diese Eigenschaft existiert nur in der SqlParameter-Klasse.)

IsNullable OleDbType

Offset

Tabelle 2.20: Eigenschaften und Methode der OleDbParameter-Klasse

94

Bonuskapitel 2

Eigenschaft
ParameterName Precision

Beschreibung Der Name des Parameters. Die maximale Anzahl von Ziffern, die zur Darstellung des Parameters benutzt wird. Die Anzahl von Dezimalstellen, die zur Darstellung des Parameters benutzt wird. Die maximale Gre dieses Parameters. Der Name der Spalte in der Datenquelle, die auf das DataSet abgebildet wird, das fr gelieferte Values verwendet wird. Legt die zu verwendende Zeilenversion fest, wenn Daten geladen werden. Der Wert des Parameters. Beschreibung Liefert den ParameterName.

Scale

Size SourceColumn

SourceVersion Value

Methode
ToString

Tabelle 2.20: Eigenschaften und Methode der OleDbParameter-Klasse(Forts.)

Tabelle C.21 listet Eigenschaften und Methoden der OleDbParameterCollection-Klasse auf, die eine Auflistung von OleDbParameter-Objekten reprsentiert.
Eigenschaft
Count Item

Beschreibung Die Anzahl der OleDbParameter-Objekte in der Auflistung. Holt einen OleDbParameter aus der Auflistung, entweder mit dem Namen des Parameters oder mit seinem Index in der Auflistung. Beschreibung Fgt der Auflistung einen Parameter hinzu. Diese Methode ist berladen. Lscht alle OleDbParameter-Objekte aus der Auflistung. Gibt an, ob der OleDbParameter mit dem angegebenen Name in der Auflistung existiert. Kopiert die gesamte Auflistung ab dem angegebenen Index in ein angegebenes Array. Holt den Index des angegebenen Parameters. Diese Methode ist berladen.

Methode
Add Clear Contains

CopyTo

IndexOf

Tabelle 2.21: Eigenschaften und Methoden der OleDbParameterCollection-Klasse

95

Bonuskapitel 2

Methode
Insert Remove

Beschreibung Fgt ein OleDbParameter-Objekt am angegebenen Index ein. Entfernt den angegebenen Parameter aus der Auflistung. Diese Methode ist berladen. Entfernt den OleDbParameter am angegebenen Index.

RemoveAt

Tabelle 2.21: Eigenschaften und Methoden der OleDbParameterCollection-Klasse(Forts.)

OleDbTransaction
Die OleDbTransaction-Klasse reprsentiert eine Transaktion, die in der Datenquelle auftritt. Tabelle C.22 listet Eigenschaften und Methoden dieser Klasse auf.
Eigenschaft
Connection IsolationLevel

Beschreibung Das OleDbConnection-Objekt, das mit dieser Transaktion verknpft ist. Die Ebene der Isolation fr diese Transaktion. Kann Chaos, ReadCommitted (Standard), ReadUncommitted, RepeatableRead, Serializable oder Unspecified sein. Beschreibung Startet die Transaktion. Nachfolgende Befehle und nderungen werden alle in einem Transaktionsprotokoll mit aufgeschobener Besttigung gespeichert und knnen jederzeit rckgngig gemacht werden. Speichert alle nderungen seit dem Aufruf von Begin. Macht alle nderungen rckgngig, die an der Datenquelle seit dem Aufruf der Begin- oder Commit-Methode vorgenommen wurden.

Methoden
Begin

Commit RollBack

Tabelle 2.22: Eigenschaften und Methoden der OleDbTransaction-Klasse

96

Das könnte Ihnen auch gefallen