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 d