visual basic 6 referenz
visual basic 6 referenz rudolf huttary
new technology Markt+Technik Verlag
Die Deutsche Bibliothek – CIP-Einheitsaufnahme Ein Titeldatensatz für diese Publikation ist bei Der Deutschen Bibliothek erhältlich. Die Informationen in diesem Produkt werden ohne Rücksicht auf einen eventuellen Patentschutz veröffentlicht. Warennamen werden ohne Gewährleistung der freien Verwendbarkeit benutzt. Bei der Zusammenstellung von Texten und Abbildungen wurde mit größter Sorgfalt vorgegangen. Trotzdem können Fehler nicht vollständig ausgeschlossen werden. Verlag, Herausgeber und Autoren können für fehlerhafte Angaben und deren Folgen weder eine juristische Verantwortung noch irgendeine Haftung übernehmen. Für Verbesserungsvorschläge und Hinweise auf Fehler sind Verlag und Herausgeber dankbar.
Alle Rechte vorbehalten, auch die der fotomechanischen Wiedergabe und der Speicherung in elektronischen Medien. Die gewerbliche Nutzung der in diesem Produkt gezeigten Modelle und Arbeiten ist nicht zulässig. Fast alle Hardware- und Softwarebezeichnungen, die in diesem Buch erwähnt werden, sind gleichzeitig auch eingetragene Warenzeichen oder sollten als solche betrachtet werden. Umwelthinweis: Dieses Buch wurde auf chlorfrei gebleichtem Papier gedruckt. Die Einschrumpffolie – zum Schmutz vor Verschmutzung – ist aus umweltverträglichem und recyclingfähigem PE-Material.
10 9 8 7 6 5 4 3 2 1 03 02 01 00
ISBN 3-8272-5588-0
© 2000 by Markt+Technik Verlag, ein Imprint der Pearson Education Deutschland GmbH, Martin-Kollar-Straße 10–12, D 81829 München/Germany Alle Rechte vorbehalten Lektorat: Erik Franz,
[email protected] Herstellung: Claudia Bäurle,
[email protected] Satz: reemers publishing services gmbh, Krefeld Druck und Verarbeitung: Media Print, Paderborn Printed in Germany
Inhaltsverzeichnis Vorwort
15
Lesebuch und Nachschlagewerk Gut verwurzelt: traditionelles Basic vs. Visual Basic Zielgruppe
15 16 18
Teil I: Referenzteil
21
Grundlegendes
23
Programme und Module in Visual Basic Programm Modul Projekt Arten von Modulen in einem Projekt Literale und Konstanten Anweisungen Bezeichner und Namensraum Kontrollstrukturen Unbedingte und bedingte Verzweigung, Subroutinen Fallunterscheidung Schleifen Fehlerbehandlung
23 23 24 24 25 27 33 34 36 36 38 40 43
Datentypen und ihre Operationen
49
Elementare Datentypen Der Datentyp Variant Die Datentypen Integer, Long, Single und Double Die Datentypen Boolean und Byte Die Datentypen Currency und Date Der Datentyp Decimal Operatoren für elementare Datentypen und logische Bedingungen Arrays Typumwandlung Benutzerdefinierte Datentypen Type-Datentypen Enum-Aufzählungen Funktionen und Anweisungen für Zeichenfolgen Asc-, AscB- und AscW-Funktion Chr-, ChrB- und ChrW-Funktion Filter-Funktion Format-Funktion FormatCurrency-Funktion FormatDateTime-Funktion FormatNumber-Funktion
49 50 51 52 52 53 54 55 57 60 61 62 63 66 67 68 69 73 74 75
5
Inhaltsverzeichnis
FormatPercent-Funktion 75 Hex-Funktion 76 InStr- und InStrB-Funktion 76 InStrRev-Funktion 77 Join-Funktion 78 LCase-Funktion 79 Left- und LeftB-Funktion 79 Len- und LenB-Funktion 79 LSet-Anweisung 80 LTrim-Funktion 81 Mid- und MidB-Funktion 81 Mid- und MidB-Anweisung 82 MonthName-Funktion 82 Oct-Funktion 82 Option Compare-Anweisung 83 Replace-Funktion 84 Partion-Funktion 85 Right- und RightB-Funktion 85 RSet-Anweisung 86 RTrim-Funktion 86 Space-Funktion 86 Split-Funktion 87 Str-Funktion 87 StrComp-Funktion 88 StrConv-Funktion 89 String-Funktion 90 StrReverse-Funktion 90 Trim-Funktion 90 UCase-Funktion 91 Val-Funktion 91 WeekdayName-Funktion 92 Mathematische und finanzmathematische Funktionen und Anweisungen 92 Abs-Funktion 94 Atn-Funktion 95 Cos-Funktion 95 DDB-Funktion 96 Exp-Funktion 97 Fix-Funktion 97 FV-Funktion 98 Int-Funktion 99 IPmt-Funktion 99 IRR-Funktion 100 Log-Funktion 101 MIRR-Funktion 102 NPer-Funktion 103 NPV-Funktion 104
6
Inhaltsverzeichnis
Pmt-Funktion PPmt-Funktion PV-Funktion Randomize-Anweisung Rate-Funktion Rnd-Funktion und Rnd-Anweisung Round-Funktion Sgn-Funktion Sin-Funktion SLN-Funktion Sqr-Funktion SYD-Funktion Tan-Funktion Funktionen und Anweisungen für Datums-/Zeitwerte CDate-Funktion Date-Funktion und Date-Anweisung DateAdd-Funktion DateDiff-Funktion DatePart-Funktion DateSerial-Funktion DateValue-Funktion Day-Funktion FileDateTime-Funktion FormatDateTime-Funktion Hour-Funktion Minute-Funktion Month-Funktion MonthName-Funktion Now-Funktion Second-Funktion Time-Funktion und Time-Anweisung Timer-Funktion TimeSerial-Funktion TimeValue-Funktion Weekday-Funktion WeekdayName-Funktion Year-Funktion Dateiorientierte Funktionen und Anweisungen ChDir-Anweisung ChDrive-Anweisung Close-Anweisung CurDir-Funktion Dir-Funktion Environ-Funktion EOF-Funktion FileAttr-Funktion
105 106 106 107 108 109 110 110 112 112 113 113 114 114 116 116 116 117 118 119 120 121 121 121 121 121 122 122 122 122 123 124 124 125 125 126 126 126 131 132 133 134 135 136 137 138
7
Inhaltsverzeichnis
FileCopy-Anweisung FileDateTime-Funktion FileLen-Funktion FreeFile-Funktion Get-Anweisung GetAttr-Funktion Input- und InputB-Funktion Input #-Anweisung Kill-Anweisung Line Input #-Anweisung Loc-Funktion Lock-Anweisung LOF-Funktion LSet-Anweisung MkDir-Anweisung Name-Anweisung Open-Anweisung Print #-Anweisung Put-Anweisung Reset-Anweisung RmDir-Anweisung Seek-Anweisung und Seek-Funktion SetAttr-Anweisung Shell-Anweisung Unlock-Anweisung Write #-Anweisung
Variablen Variablendeklaration Typkennzeichen und Bezeichnerbereiche für Typen Variableninitialisierung Geltungsbereiche von Variablen
Funktionen und Prozeduren Parameterübergabe an Funktionen und Prozeduren Funktionen selbst definieren Prozeduren selbst definieren Routinen aus DLLs und der Windows-API einsetzen
Objekte und Klassen Klassen als Datentypen für Objektvariablen Ereignisroutinen Standardereignisse Activate-Ereignis und Deactivate-Ereignis Change-Ereignis Click-Ereignis DblClick-Ereignis
8
138 139 139 139 140 142 142 143 144 145 146 146 147 148 148 148 149 151 153 154 155 156 157 158 159 159
161 162 167 168 173
175 178 181 183 185
195 196 204 207 212 214 215 217
Inhaltsverzeichnis
DragDrop-Ereignis DragOver-Ereignis GotFocus-Ereignis und LostFocus-Ereignis Initialize-Ereignis KeyDown-Ereignis und KeyUp-Ereignis KeyPress-Ereignis LinkClose-Ereignis LinkError-Ereignis LinkExecute-Ereignis LinkNotify-Ereignis LinkOpen-Ereignis Load-Ereignis MouseDown-Ereignis und MouseUp-Ereignis MouseMove-Ereignis OLECompleteDrag-Ereignis OLEDragDrop-Ereignis OLEDragOver-Ereignis OLEGiveFeedback-Ereignis OLESetData-Ereignis OLEStartDrag-Ereignis Paint-Ereignis QueryUnload-Ereignis Resize-Ereignis Terminate-Ereignis Unload-Ereignis Validate-Ereignis Global-Objekt App-Objekt CallByName-Methode Clipboard-Objekt Command-Methode CreateObject-Methode DeleteSetting-Methode DoEvents-Methode Err-Objekt Forms-Auflistung GetAllSettings-Methode GetObject-Methode GetSetting-Methode InputBox-Methode Load-Methode LoadPicture-Methode LoadResData-Methode, LoadResPicture-Methode, LoadResString-Methode Printer-Objekt Printers-Auflistung
218 221 222 224 225 228 230 231 232 233 233 236 237 239 241 242 245 247 248 250 253 256 257 258 259 260 261 264 269 270 273 273 275 276 277 279 279 280 281 281 282 283 284 284 297
9
Inhaltsverzeichnis
QBColor-Methode RGB-Methode SavePicture-Methode SaveSetting-Methode Screen-Objekt Sendkeys-Methode Unload-Methode Auflistungen und Collection-Objekte Formulare Form-Objekt (Basisobjekt für Formulare) MDIForm-Objekt Selbst definierte Klassen ClassModul-Schnittstelle
Steuerelemente Gemeinsame Eigenschaften Alignment-Eigenschaft Appearance-Eigenschaft AutoSize-Eigenschaft BackColor-Eigenschaft und ForeColor-Eigenschaft BackStyle-Eigenschaft BorderStyle-Eigenschaft Caption-Eigenschaft Container-Eigenschaft CausesValidation-Eigenschaft DataChanged-Eigenschaft DataField-Eigenschaft DataFormat-Eigenschaft DataMember-Eigenschaft DataSource-Eigenschaft DisabledPicture-Eigenschaft DownPicture-Eigenschaft DragIcon-Eigenschaft DragMode-Eigenschaft DrawMode-Eigenschaft DrawStyle-Eigenschaft Enabled-Eigenschaft FillColor-Eigenschaft FillStyle-Eigenschaft Font-Eigenschaft ForeColor-Eigenschaft HasDC-Eigenschaft hDC-Eigenschaft Height-Eigenschaft und Width-Eigenschaft HelpContextID-Eigenschaft und WhatsThisHelpID-Eigenschaft hWnd-Eigenschaft Image-Eigenschaft
10
298 299 299 300 300 302 303 304 306 307 313 318 320
321 323 327 328 329 330 331 332 334 335 335 336 336 338 339 339 341 341 342 343 344 346 347 348 349 349 350 350 351 351 353 354 354
Inhaltsverzeichnis
Index-Eigenschaft Left-Eigenschaft und Top-Eigenschaft MaskColor-Eigenschaft MouseIcon-Eigenschaft MousePointer-Eigenschaft MultiLine-Eigenschaft Name-Eigenschaft OLEDropAllowed- und OLETypeAllowed-Eigenschaften OLEDropMode-Eigenschaft Parent-Eigenschaft Picture-Eigenschaft RightToLeft-Eigenschaft ScaleLeft-Eigenschaft und ScaleTop-Eigenschaft ScaleHeight-Eigenschaft und ScaleWidth-Eigenschaft ScaleMode-Eigenschaft ShowTips-Eigenschaft Style-Eigenschaft TabIndex-Eigenschaft TabStop-Eigenschaft Tag-Eigenschaft ToolTipText-Eigenschaft UseMaskColor-Eigenschaft Visible-Eigenschaft WhatsThisHelp-Eigenschaft WhatsThisHelpID-Eigenschaft Width-Eigenschaft Gemeinsame Methoden Drag-Methode LinkExecute-Methode LinkPoke-Methode LinkRequest-Methode LinkSend-Methode Move-Methode OLEDrag-Methode Refresh-Methode SetFocus-Methode ShowWhatsThis-Methode ZOrder-Methode Standardsteuerelemente Anzeige-Steuerelement (Image) Befehlsschaltfläche-Steuerelement (CommandButton) Bezeichnungsfeld-Steuerelement (Label) Bildfeld-Steuerelement (PictureBox) Bildlaufleisten-Steuerelemente (HScrollBar, VScrollBar) Dateilistenfeld-Steuerelement (FileListBox) Data-Datensteuerelement (Data)
356 357 358 359 360 361 361 362 363 364 365 366 366 368 369 371 372 375 376 376 377 377 378 378 378 379 379 379 380 381 382 382 383 383 384 385 386 386 387 390 392 393 395 398 400 402
11
Inhaltsverzeichnis
Figur-Steuerelement (Shape) Kombinationsfeld-Steuerelement (ComboBox) Kontrollkästchen-Steuerelement (CheckBox) Laufwerklistenfeld-Steuerelement (DriveListBox) Linie-Steuerelement (Line) Listenfeld-Steuerelement (ListBox) OLE-Container-Steuerelement (OLE) Optionsfeld-Steuerelement (OptionButton) Rahmen-Steuerelement (Frame) Textfeld-Steuerelement (TextBox) Verzeichnislistenfeld-Steuerelement (DirListBox) Zeitgeber-Steuerelement (Timer) ActiveX-Steuerelemente (OCX) – Windows-Standardsteuerelemente Abbildungsliste-Steuerelement (ImageList) Weitere ActiveX-Steuerelemente Bildausschnitt-Steuerelement (PictureClip) Standarddialoge-Steuerelement (CommonDialog)
Teil II: Praxisteil
451
Ältere Basic-Programme nach Visual Basic portieren
453
Wie importiert man den Quelltext? Einfache Programme Implementation einer eigenen Input-Routine Anspruchsvollere Programme Implementation von Inkey$ Implementation von LOCATE, POS, CSRLIN und COLOR Koordinatensystem und Grafikmodus Zusammenfassung der Emulation als Standardmodul Von WANKEL.BAS zur WankelAnimation Die portierte Fassung
454 454 456 457 459 460 461 463 468 472
Mathematik und Algorithmen ApfelmannZoom – eine Fahrt durch die Mandelbrotmenge Implementation des Apfelmännchen-Algorithmus Optimierung Ereignisbehandlung mit DoEvents, ein komplexes Problemfeld Benutzerschnittstelle Der Stack Einzelbilder speichern
Formulare und Ansichten Ereignisbehandlung Gummiband – Bereiche interaktiv auswählen DDE-Verbindungen OLE-Drag&Drop
12
406 407 409 410 411 412 415 424 426 427 431 432 433 436 439 442 444
477 478 484 485 485 487 488 489
491 491 492 495 501
Inhaltsverzeichnis
DiaProjektor – SDI-Formulare synchronisieren Das Programmdesign Info-Dialog als gebundenes Formular aufrufen Ereignisse delegieren und Instanziierung durch One-Shot-Logik Programmstart und Auswahl der Bilddateien im Standarddialog Öffnen Das Anzeigesystem Bilder einlesen und in maximaler Größe zeichnen Vollbildanzeige Menü und Kontextmenü Benutzerschnittstelle Bildlaufleisten für Ansichten einsetzen und auf Größenänderungen reagieren Bildlauf – ein kleiner Betrachter für große Bilder HexView – eine schnelle Textansicht für große Dateien Registrierung RegTest – Sitzungen wieder aufnehmen
Objektorientiertes Basic Klassen selbst definieren Ring – eine einfache Klasse demonstriert Grundlegendes 3DAnimation – Drahtgittermodelle frei im Raum gedreht Point3D – ein Punkt im Raum Line3D – eine Linie aus zwei Punkten ActiveX-Steuerelemente und Benutzersteuerelemente LongTimer – der Timer mit Ausdauer Transparenz und Drag&Drop MemoryEdit – das Textfeld mit Gedächtnis Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
Tabellenindex
511 514 525 525 527 530 535 536 538 542 544 545 551 561 562
567 567 568 577 582 586 599 599 606 615 642
659
13
Vorwort Wer heute nicht täglich mit einem bestimmten Produkt, etwa einem Entwicklungssystem wie Visual Basic, einer Tabellenkalkulation oder einer Textverarbeitung arbeitet, sondern eher sporadisch, dem bleibt nichts anderes übrig, als erst einmal erhebliche Mengen an wertvoller Zeit schlicht damit zu vergeuden, sich und sein System immer wieder auf den neuesten Stand des jeweiligen Produkts zu bringen. Der Effizienzvorsprung, den der neueste Stand des Produkts verspricht, muss oft sehr teuer mit Lernaufwand und der Umgestaltung bestehender Lösungen erkauft werden. Die Hersteller wollen nicht nur, dass ein Produkt in der neuen Version äußerlich anders erscheint, die zusätzlichen Features sowie die Anpassung an eine sich ständig ändernde Umgebung (Benutzeroberfläche, Betriebssystem, Entwicklungsplattform, Vernetzung, verwandte Produkte) erzwingen in den meisten Fällen auch Änderungen des inneren Aufbaus, eingeführter Daten- und Kommunikationsstrukturen und nicht zuletzt auch erhebliche Änderungen in der Dokumentation. Damit wären wir bereits beim Thema.
Lesebuch und Nachschlagewerk Dieses Buch reiht sich mit seinem Titel in die 1999 neu ins Leben gerufene Referenzreihe des Markt+Technik-Verlags ein. Maßgeblich für das Erscheinen dieser neuen Reihe war der Wunsch vieler semi- wie professioneller Programmierer nach verdichteten Referenzinformationen mit ineinander greifender theoretischer und praktischer Aufarbeitung, mit anderen Worten, der schnelle und gleichzeitig kompetente Überblick über ein Sprachprodukt mit nichttrivialen praktischen Anregungen – als Nachschlagewerk für den Einstieg, Umstieg sowie die Auffrischung für die konkrete Arbeit mit dem Produkt. Die Titel in dieser Reihe geben Antwort auf die Fragen »Was ist das?« und »Wie geht das?« – und dies in einer für den Einzelnen noch überschaubaren Weise. Das Buch soll dem Käufer als Lesebuch und als Nachschlagewerk dienen, es soll ihm also das nötige Know-how für seine Programmiertätigkeit vermitteln und ihm gleichzeitig Anregungen und Lösungen anbieten, die er in seine Programmierarbeit einfließen lassen kann. Schaltstelle und Garant für die gute gegenseitige Durchdringung der Themen im Referenz- und Praxisteil ist der zentrale Tabellenindex im Anhang, dessen themen- und sachbezogene Einträge überwiegend in beiden Teilen Fundstellen ausweisen. Heutige Online-Hilfen zu Produkten können dies aus verschiedenen Gründen eher schlecht als recht bzw. immer häufiger gar nicht mehr leisten. So, weil sie sich über die Versionen hinweg immer weiter aufgeblasen haben, ohne strukturell überarbeitet worden zu sein, weil sie aufgrund schlechter und unvollständiger Aufarbeitungen teilweise bereits in sich widersprüchlich geworden sind, weil sie aufgrund erbärmlicher Indizes nur sehr zeitraubend zu handhaben sind und nicht zuletzt, weil sie in ihrem enzyklopädischen Charakter keinerlei Gewichtung der Informationen nach Kriterien der Brauchbarkeit und dem Wissensstand des Lesers mehr enthalten. Ein wirklich pathologisches Beispiel einer solchen Online-Hilfe ist die MSDN Library für Visual Studio 6.0. Der monumentale Fundus dieses in seinem Format inzwischen auf den Internet Explorer zugeschnittenen Hilfesystems ist auf zwei CDs verteilt und stellt einen Rundschlag an Informationen über die wichtigeren Sprachprodukte von Microsoft dar – darunter zu Visual Basic, Visual C++, Visual J++, Visual FoxPro sowie die SDK- und die DDK-Dokumentation. Die Benutzbarkeit des Systems lässt jedoch extrem zu wünschen übrig. Erstens wurden Text und Index teilweise eingedeutscht, was für den Index eine echte Katastrophe darstellt, teilweise im amerikanischen Original belassen, und zweitens wurde bei der indexikalischen Suche schlicht vergessen, die Produktzugehörigkeit der einzelnen Themen mit anzugeben. Die Volltextsuche leistet dies zwar, erschlägt einen aber schier mit der Fülle der Fundstellen. Wer dieser Hilfe in annehmbarer Zeit etwas halbwegs Vernünftiges über Visual Basic entlocken will, tut
15
Gut verwurzelt: traditionelles Basic vs. Visual Basic
Gut verwurzelt:
traditionelles
Bas ic
vs.
Visual Basic
gut daran, sich über das Menü ANSICHT/UNTERMENGE DEFINIEREN als Erstes eine Quellenauswahl in Form einer Untermenge zusammenzustellen und diese für die weitere Arbeit als Filter zu benutzen. Eine gedruckte Referenz zu Visual Basic im Umfang des vorliegenden Buches kann es sich nicht leisten, ausgiebigst auf allen Hochzeiten zu tanzen – das sei der Online-Hilfe vorbehalten. Vielmehr stellt sich die gedruckte Referenz der Aufgabe, die Ecken und Kanten abzuschleifen, die ein Verständnis der Sprache oder spezifische Problemlösungen erschweren, indem sie Zusammenhängendes als solches aufzeigt, begreiflich darstellt und in seinen relevanten Dimensionen exemplarisch erläutert. Entscheidend ist dabei die Auswahl und Verdichtung der behandelten Thematiken, die zwar nicht in jedem Fall, so doch aber in der Mehrzahl der Fälle eine Vertiefung der eher lax gehaltenen Online-Hilfe zu Visual Basic erreicht. Der vorliegende Band enthält eine umfassende Darstellung der Konzepte, Funktionen und Objekte von Visual Basic als Programmiersprache und Entwicklungsplattform, beschränkt sich aber in seiner Themenauswahl auf die »Innensicht der Sprache«, ohne sich allzu weit in der Welt des ActiveX, der Automatisierung, der Datenbankprogrammierung sowie generell des COM (bzw. DCOM) zu verlieren. Grob gesehen deckt der Band das gesamte Instrumentarium von Visual Basic inklusive aller Standardsteuerelemente sowie ausgewählter Windows-Standardsteuerelemente und die Programmierung eigener Benutzersteuerelemente ab.
Gut verwurzelt: traditionelles Basic vs. Visual Basic Keiner anderen Programmiersprache haftet so stark das Etikett an, in Hunderte verschiedener Dialekte zerfallen zu sein, wie Basic. Das liegt zum einen daran, dass Basic über lange Zeit hinweg, insbesondere für die ersten in ihren Möglichkeiten massiv beschränkten Mikrocomputer und später für den PC die mit dem Betriebssystem ausgelieferte Standardsprache darstellte – wenn es nicht gar, wie etwa für den Commodore C64 zur Kommandosprache für das Betriebssystem selbst avanciert war. Zum anderen liegt es aber auch daran, dass die Sprache in ihrer Entwicklung einer weniger rigiden Kontrolle durch Standardisierungskomitees und -gremien ausgesetzt war als die anderen – vielleicht wegen des Buchstabens »B« im Akronym, der ja bekanntlich für »Beginners« steht. Unterschwellig mag dabei freilich auch noch das Vorurteil mitgeschwungen haben, Basic werde aufgrund seiner Begrenztheit als Entwicklungssprache für ernst zu nehmende Anwendungen sowieso nie taugen. Ein großes Veränderungspotential geht aber auch mit einem großen Entwicklungspotential und der Fähigkeit zur schnellen Adaption einher. In der Tat haben im letzten Jahrzehnt die Philosophien für Benutzeroberflächen und Betriebssysteme einen massiven Wandel durchgemacht. Das gilt in besonders krasser Weise für den PC. Da ist es nicht verwunderlich, wenn sich auch die Anforderungen verlagert haben, denen eine Programmiersprache bei der Anwendungsentwicklung für moderne Benutzeroberflächen genügen muss. Eine wichtige Rolle spielen heute Objektorientierung, Modularisierung und Prozesskommunikation. Mit anderen Worten, das monolithische Programm, das isoliert von anderen Anwendungen den ihm zugedachten Aufgaben nachkommt, indem es seine eigenen Datenformate und Algorithmen strickt, seine eigene Benutzerschnittstelle parat hält und vielleicht noch so tut, als gehöre ihm die Maschine alleine, hat unwiderruflich ausgedient. Gefragt sind überschaubare, möglichst allgemein einsetzbare Komponenten, die im Dienste der Allgemeinheit das Ihre zum Großen Ganzen beitragen. Dieser neue Zeitgeist hört auf den Namen COM (Component Object Model), eine sprachübergreifende Spezifikation, die die Implementation und das Miteinander von Objekten in dieser Welt regelt. Microsoft hat seinen Beitrag an der technologischen Umsetzung dieser Konzeption (mit Blick auf die Internet-Programmierung) unter der Bezeichnung ActiveX zusammengefasst. Einer Anwendung, gleich in welcher Sprache sie realisiert wird, stehen damit vonseiten des Betriebssystems komplexe, allgemein gehaltene Bausteine in Form von Komponenten (Steuerelemente,
16
Gut verwurzelt:
traditionelles
Basic
vs.
Visual Basic
●
●
●
●
Module – der Modulbegriff deckt sich nicht mehr mit dem Programmbegriff, sondern erweitert die Strukturierungsfähigkeit der Sprache nach oben hin. Die Sprache vermag damit insbesondere Bibliotheken (Klassen- und Komponentenbibliotheken) konzeptuell zu integrieren Objekte – der Objektbegriff ist inzwischen sprachlich-syntaktisch verwurzelt. Neben den einfachen Datentypen stehen nun auch komplexe, verkapselte Datentypen samt zugehöriger Operationen zur Disposition. Der Sprache eröffnet sich damit unter anderem die vollständige Anbindung an alle Dienste des Betriebsystems inklusive der grafisch orientierten Benutzerschnittstelle, des gesamten Dateiwesens, der Kommunikationseinrichtungen und der Datenbankschnittstellen. Ereignisse – die Sprache adaptiert das Modell der ereignisgesteuerten Anwendungsprogrammierung von Windows über den Objektbegriff. So gestatten vordefinierte Klassen und interaktiv definierte Objekte dieser Klassen eine umfassende Repräsentation der Benutzerschnittstelle mit all ihren Elementen (Fenster, Menüs, Symbol- und Statusleisten, Steuerelemente, Komponenten) und Aktionen. ActiveX – die vom COM geforderte Codeabstraktion ermöglicht es der Sprache nicht nur, von existierenden Komponenten zu profitieren, sondern auch selbst Komponenten beizusteuern, von denen wiederum andere Anwendungen profitieren können.
Erst mit Integration dieser Konzepte besitzt Visual Basic das nötige Rüstzeug, um in der Welt der modernen Windows-Programmierung Schritt halten zu können. Das gilt mit gleichem Vorzeichen für den Programmierer: Wer auch nur halbwegs ernst zu nehmende Anwendungen mit Visual Basic zu Stande bringen will, dem bleibt nichts anderes übrig, als sein Basic-Repertoire (und sein Denken) auf den Stand dieser Neuerungen zu bringen. Dabei spielt in erster Linie der Objektbegriff eine zentrale Rolle. Wer den Objektbegriff einmal soweit verinnerlicht hat, dass er nicht mehr über die Nahtstelle zwischen dem »alten Basic« und dem »Basic mit Objekten« stolpert, dem wird auch die modulare Programmierung mit Code- und Klassenbibliotheken, ActiveX-Komponenten keine allzu großen Rätsel mehr aufgeben. Damit wären wir bereits bei der Zielgruppe dieses Buchs.
17
Gut verwurzelt: traditionelles Basic vs. Visual Basic
Schnittstellen, Player, Filter etc.) mit verkapselter Funktionalität und Implementation zur Verfügung, derer sie sich nach Belieben und ohne großes Brimborium bedienen kann. Angesichts dieser Entwicklungen scheint es etwa bei Betrachtung der Linie von BasicA über QuickBasic/QBasic nach Visual Basic beinahe schon verblüffend, wie treu sich die Sprache in ihren Wurzeln doch geblieben ist und wie viel sie von ihren ursprünglichen Konzepten in die neue Objekt-dominierte Welt retten konnte. Mehr noch, die Adaption ist so erstaunlich gut gelungen, dass sich der mit Visual Basic vorliegende Basic-Dialekt mit Fug und Recht inzwischen zu den ernst zu nehmenden Sprachen für die Anwendungsentwicklung zählen darf. Aber auch damit noch nicht genug. Microsoft hat Visual Basic vielmehr die Rolle des Flagschiffs zugedacht: In Form von VBA (Visual Basic für Anwendungen) stellt das neue Basic zudem die Automatisierungssprache für die wichtigsten Anwendungen der Microsoft Office Suite, und in der stark eingeschränkten Form von VBScript ist das neue Basic auch gleich noch zur neuen Kommandosprache des Betriebssystems Windows avanciert. Wer nun darauf beharrt, Visual Basic habe nicht mehr viel mit dem traditionellen Basic zu tun, dem kann natürlich genauso wenig widersprochen werden. Die objektorientierte Fassung von C ist C++ und von Pascal ist Modula oder Delphi, und keiner wird Letztere als Dialekte Ersterer bezeichnen wollen, obwohl die Kenntnis von C bzw. Pascal doch sehr beim Verständnis ihrer objektorientierten Nachfolger von Vorteil ist. Nehmen Sie es so: Basic war schon immer mehr ein Oberbegriff, denn ein kohärentes Gebilde, und als Oberbegriff ist es durchaus in der Lage, auch den Übergang in die objektorientierte Programmierung unter sich zu subsumieren. Die wesentlichen in Visual Basic zu findenden Neuerungen sind:
Zielgruppe
Zielgruppe Was das Vorwissen des Lesers betrifft, so wurde bei der Gestaltung der einzelnen Themen größter Wert auf eine Steigerung vom Einfachen zum Komplexen und vom Allgemeinen zum Speziellen gelegt. Der Eine liest so weit, wie er kommt, der Andere beginnt da zu lesen, wo es für ihn interessant wird. Damit ist, wie für eine Referenz üblich, die Zielgruppe dieses Buchs naturgemäß recht breit anzusetzen. Sie umfasst:
Zielgruppe
●
●
●
●
●
den unerfahrenen Programmierer, der die ersten Schritte und Hürden der Visual-BasicProgrammierung zwar hinter sich gebracht hat, aber weder den inneren Zusammenhang der Sprache so recht durchdrungen hat, noch eine Vorstellung von der Fülle ihrer Möglichkeiten bei der Bewältigung praktischer Problemfelder besitzt. Programmierer dieser Gruppe finden in diesem Buch nicht nur einen übersichtlichen Grundlagenteil, der auf die Elemente und Konzepte der Sprache genauer eingeht und gleichermaßen eine Vertiefung des Objektbegriffs vermittelt, sondern auch Anregungen für die eigene Programmiertätigkeit in Form einfacher Programmbeispiele, die Lösungen zu typischen Fragestellungen vorstellen. den Alten Hasen, der Basic seit langem kennt, die Sprache aber vernachlässigt hat und mit Visual Basic den schnellen Wiedereinstieg in die heutige objektorientierte Basic-Programmierung sucht. Programmierer dieser Gruppe werden in der Referenz nützliche Gegenüberstellungen und Anmerkungen finden, die gerade auf die Veränderung der Sprache im Vergleich zu QBasic abzielen. den ambitionierten Anwendungsentwickler, der sein Repertoire erweitern will, nach Ansätzen für neue Lösungen sucht und sich vom Griff zur Referenz nicht nur Auskunft, sondern auch Inspiration erwartet. Als Referenz bemüht sich das Buch, Licht in möglichst viele Winkel der Visual-Basic-Programmierung zu bringen. Programmierer dieser Gruppe werden daher in der Fülle der vorgestellten Programmiertechniken und Problemlösungen schnell Ansatzpunkte für die Lösung der eigenen Fragestellungen finden. den versierten Programmierer, der in vielen Programmiersprachen zu Hause ist und gezielte Hilfestellung in der einen oder anderen Fragestellung sucht. Die problemrelevante Auswahl der Einzelthemen und die Anwendungen unterschiedlicher Schwierigkeitsgrade und Komplexität umfassende Beispielsammlung dürfte das Buch für diesen Programmierertyp zu einer Fundgrube bei der Suche nach speziellen Lösungen werden lassen. den Entwickler spezifischer Datenbanklösungen, der Visual Basic unter anderem als Plattform für die Front-End-Programmierung benutzt
Auch wenn im Text vielfach nur »Visual Basic« zu lesen ist, ist der Inhalt des Buches vor dem Hintergrund der bei Drucklegung aktuellsten Version von Visual Basic zu interpretieren. Die in diesem Buch aufgeführten Thematiken und Beispiele wurden daher unter Visual Basic 6.0 (SP3) sowie der dazu von Microsoft bereitgestellten Dokumentation sorgfältig ausgearbeitet und auch getestet. Da Microsoft die Entwicklungszyklen seiner Produkte inzwischen scheinbar zunehmend auf Release-Versionen ausdehnt und eine weitgehende Fehlerfreiheit meist erst mit dem zweiten oder dritten Service-Pack erreicht wird, bietet diese Version eine hinreichend stabile Plattform für die Anwendungsentwicklung.
Die CD zum Buch Dem Buch ist eine CD beigelegt, auf der Sie die im Text explizit als Projekte bezeichneten Beispiele in je eigenen Unterverzeichnissen der Verzeichnisse Referenz und Praxis vorfinden. Zur einfacheren Orientierung tragen die Unterverzeichnisse die Namen der Projekte. Um die nicht
18
Zielgruppe
kompilierten Programme auszuführen, öffnen Sie die jeweilige VBP-Projektdatei oder VBG-Projektgruppendatei in der Visual-Basic-Entwicklungsumgebung und starten die Ausführung mittels der Taste (F5). Für Kommentare, Verbesserungen und Anregungen mit Bezug auf den Inhalt dieses Buchs bin ich jederzeit dankbar. Richten Sie Zuschriften bitte an:
[email protected]
München im August, 2000
Rudolf Huttary
Zielgruppe 19
Teil 1: Referenzteil
21
Grundlegendes Bei der prozeduralen Programmierung erfolgt die Strukturierung des Quellcodes traditionell durch Kontrollstrukturen, Funktionen/Prozeduren und Module. Während Kontrollstrukturen als Muss für jede Programmiersprache schon immer zum Kern von Basic zählten, hielten der Funktions- und Prozedurbegriff und das damit verbundene Konzept des Namensraums nur sehr zögerlich Einzug in die Sprache. Der Modulbegriff war als solcher zwar schon immer vorhanden, er implizierte bis zur Entwicklung von Visual Basic jedoch nicht mehr als die schiere Möglichkeit, von einem ersten Programm aus die Kontrolle zusammen mit gegebenenfalls speziell für den Export deklarierten Werten an ein zweites Programm abzugeben. Da das erste Programm dadurch beendet wurde und seine gesamte Umgebung verlorenging, war dieses Konzept als zusätzliche Strukturebene wenig geeignet. Brauchbare Adaptionen für Funktionen/Prozeduren gibt es seit QuickBasic und QBasic, wenngleich die etwas seltsam anmutenden Regeln für den Namensraum aus heutiger Sicht den Eindruck erwecken, als seien den Funktionen/Prozeduren Teile eines Modulkonzepts untergemischt worden. Erst die mit Visual Basic vollzogene Öffnung zur objektorientierten Programmierung brachte der Sprache ein ausgewogenes Verhältnis dieser drei Strukturebenen, wie es heute in allen modernen Programmiersprachen zu finden ist: Kontrollstrukturen sind (unverändert) bedingte Verzweigungen und Schleifen, die eine Steuerung der Programmausführung ermöglichen. Prozeduren/Funktionen fassen als Unterprogramme umfangreiche Anweisungsfolgen zu kompakten Einheiten zusammen, wobei Funktionen aufgrund des Funktionswerts, den sie verkörpern, noch der spezielle Aspekt der Auswertung komplexer Ausdrücke zufällt. Module haben Bibliothekscharakter und kombinieren Code und Datenstrukturen zu eigenständigen Programmeinheiten. Damit erfüllen sie die strukturelle Voraussetzung für die Implementation von Objektdatentypen (Klassen).
Programme und Module in Visual Basic Dieser Abschnitt diskutiert den Programm- und Modulbegriff von Visual Basic und stellt die verschiedenen Ausprägungen vor.
Programm Ein Programm ist eine Folge von Befehlen, die auf einem Computer ausführbar ist und im weitesten Sinne eine spezifische Anleitung für die spezifische Verarbeitung spezifischer Daten darstellt. Das hier etwas strapazierte Wort »spezifisch« soll deutlich machen, dass bei der Datenverarbeitung die Repräsentation eine enorme Rolle spielt und Programm nicht gleich Programm ist, selbst wenn es das Gleiche tut. Basic war ursprünglich als Interpretersprache konzipiert, was nichts anderes heißt, als dass zur Ausführung eines Basic-Programms ein Interpreter (also ein weiteres Programm) erforderlich war, das vergleichbar einem Dolmetscher die Basic-Befehle nacheinander in Maschinenbefehle übersetzte. Darüber hinaus musste der Interpreter während des Übersetzens auch noch darauf achten, dass der Programmtext keine Fehler syntaktischer oder sonstiger Natur enthielt. Das hatte zwar den Vorteil, dass die Programmentwicklung sehr einfach – weil vollständig interaktiv – vonstatten gehen konnte, aber auch den Nachteil, dass die Ausführungsgeschwindigkeit zu wünschen übrig ließ. Basic hatte daher immer schon den Ruf, »nicht gerade der schnellsten einer« zu sein. Im Gegensatz zu Basic sind andere Sprachen wie C oder Pascal von vornherein als Compilersprachen konzipiert, das heißt, Programmtexte werden einmalig (sowie nach jeder Änderung) von einem Compiler (Übersetzungsprogramm) auf syntaktische und sonstige Fehler hin untersucht und dann in eine ausführbare Version übersetzt, die aus nichts weiter als aus Maschinenbefehlen besteht und vom Prozessor direkt verarbeitet werden kann. (In der Praxis muss der vom Compiler gelieferte Code allerdings zur Auflö-
23
Programme und Module in Visual Basic
Programme und Module in Visual Basic
sung von Bibliotheksverweisen noch einem Bindevorgang unterzogen werden – dafür ist der so genannte Linker zuständig). Der Geschwindigkeitsvorteil liegt dabei natürlich auf der Hand. Visual Basic vereint heute die Vorteile einer Interpretersprache mit der einer Compilersprache. Für die interaktive Programmentwicklung stellt die Entwicklungsumgebung von Visual Basic einen Interpreter (genau genommen handelt es sich dabei um einen inkrementellen Compiler) mit integriertem Debugger bereit, und zur Fertigstellung einen Compiler, der direkt ausführbaren Code erzeugen kann. Dieser Code ist allerdings noch auf eine oder mehrere zu Visual Basic gehörende Laufzeitbibliotheken angewiesen, die in Form von DLLs bereitstehen. Neben der Übersetzung in Maschinencode bietet Visual Basic auch die Übersetzung in den so genannten PCode an, ein im Schnitt etwa um 40 Prozent kompakterer und syntaktisch fehlerfreier Zwischencode. In P-Code vorliegende Programme besitzen einen kleinen ca. 9 KB umfassenden Maschinencode-Anteil, den so genannten P-Code-Interpreter, der ein primitives Laufzeitsystem darstellt und den P-Code (mit weitaus geringeren Geschwindigkeitsverlusten als ein Basic-Interpreter) in Maschinencode entfaltet.
Modul Ein Blick auf die Entwicklung von Basic zeigt weiterhin, dass sich die Sprache von einer schwach strukturierenden Sprache, deren einzige Strukturelemente Kontrollstrukturen und (inzwischen völlig verpönte) Subroutinen bildeten, zu einer stark strukturierenden Sprache gemausert hat, die sich dem Vorwurf, sie würde Spagetti-Code fördern, nicht mehr gefallen lassen muss. So baut sich die Funktionseinheit Programm heute nicht mehr als schlichte Folge von Anweisungen, Verzweigungen und Sprüngen auf, sondern als Sammelsurium sich gegenseitig in streng hierarchischer Organisation aufrufender Funktionen und Prozeduren, also in sich abgeschlossener und gekapselter Untereinheiten mit eng begrenzten und überschaubaren Funktionalitäten. Damit aber nicht genug. Um als Programmiersprache in einer zunehmend objektorientiert gestalteten Systemumgebung noch mitmischen zu können, muss auch die Möglichkeit einer Strukturierung »nach oben hin« gegeben sein. Dies bringt den Begriff des Moduls ins Spiel. Ein Modul vereinigt im Wesentlichen das unter sich, was traditionell als Basic-Programm begriffen wurde: einen als Datei vorliegenden Programmtext, der vom Basic-Laufzeitsystem als eigenständige Programmeinheit behandelt wird. Charakteristikum des Moduls ist, dass es einen eigenen Namensraum darstellt, mithin also Variablen bereitstellen kann, die global in allen Prozeduren des Moduls bekannt sind (Deklaration mit Dim oder Private). Darüber hinaus ist auch ein Export globaler Variablen und Prozeduren an andere Module (Deklaration mit Public) möglich. Auf diese Weise lässt sich in Visual Basic die Gesamtfunktionalität eines Programms als Ansammlung kleinerer »Programme« bzw. Module mit definierten Schnittstellen begreifen. Das einzelne Modul kann im Zusammenspiel mit anderen Modulen einem höheren Zwecke dienen, aber auch für sich genommen ein vollständiges Programm sein. Mit Blick auf die Objektorientierung hat das Modul in Visual Basic eine noch weitere sehr wichtige Funktion: Es kann als eigenständiger komplexer Datentyp (Klasse) auftreten und stellt dafür die Implementation. Eine ausführliche Diskussion dieses Aspekts finden Sie im Teil »Objekte und Klassen« (S. 195).
Projekt Der Visual-Basic-Programmierer legt für jedes neue Programm ein Projekt an, welches als organisatorischer Rahmen für die verschiedenen Module seines Programms fungiert. Damit das Programm für sich lauffähig ist, muss eines (und nur eines) dieser Module ein Startobjekt bereitstellen, das unter PROJEKTEIGENSCHAFTEN auf der Seite ALLGEMEIN anzugeben ist. Der Name »Objekt« ist hier ein wenig unglücklich gewählt, weil nicht nur Objekte mit eigenständiger Nachrichtenbehandlung (wie Formulare), sondern auch eine Prozedur mit dem speziellen Bezeichner Main zur Auswahl stehen. Ist für ein Projekt kein Startobjekt definiert, kann der resultierende Code nur als Bibliothek für andere Programme oder Bibliotheken fungieren. (Eine
24
Arten von Modulen in einem Projekt
Bibliothek kann allerdings gleichfalls ein Startobjekt benötigen, wenn noch vor dem Aufruf der ersten Bibliotheksroutine gegebenenfalls notwendige Initialisierungen durchzuführen sind.) So ergibt sich, dass in Visual Basic geschriebene Programme aus unterschiedlichen Teilen zusammengesetzt sind, die jeweils unterschiedliche Gestalten haben bzw. annehmen können: ●
Arten von Modulen in einem Projekt Visual Basic unterscheidet mehrere Arten von Modulen. Um einem bestehenden Projekt ein weiteres Modul hinzuzufügen, finden sich im Menü PROJEKT der Entwicklungsumgebung entsprechende Einträge. Mit Ausnahme von Standardmodulen des Typs Modul sowie allgemeinen Klassenmodulen setzt sich ein Modul aus einer Codekomponente und einer Objektkomponente (lies: sichtbare Repräsentation) zusammen. Die Bearbeitung der Codekomponente erfolgt im ASCII-Editor und die Bearbeitung der Objektkomponente im Designerfenster der integrierten Entwicklungsumgebung Visual Studio. Standardmäßiges Modul für die Programmierung kleiner Anwendungen mit Ein- und Ausgabefunktionalität ist das Formular. Sein Charakteristikum ist, dass seine Funktionalität im Wesentlichen auf ein einzelnes Fenster ausgerichtet ist, das ihm als Eingabe- und Ausgabemedium dient. Nachdem ein Formularmodul zur Laufzeit durch ein Formularobjekt mit eigenständiger Nachrichtenbehandlung repräsentiert wird, kann jedes Formularmodul formal zum Startobjekt erklärt werden. (Natürlich muss dann auch das Design sowie die Logik des Formulars auf diese Eigenschaft zugeschnitten sein.) Somit kann ein aus einem einzelnen Formularmodul bestehendes Projekt bereits als vollständiges, interaktives Visual-Basic-Programm auftreten. Sie erhalten ein solches Minimalprojekt (mit geeignet definiertem Startobjekt), wenn Sie ein Projekt des Typs »Standard-EXE« anlegen. Mit dieser Konstellation kommt das Formularmodul dem traditionellen Basic-Programm am weitesten entgegen, auch wenn die Programmiertechniken für die Ein- und Ausgabe unter Windows doch erheblich anders aussehen als vergleichsweise noch unter DOS. Eine Anleitung, wie ältere, noch mit QBasic geschriebene Basic-Programme unter Visual Basic zum Laufen gebracht werden, findet sich zu Anfang des Praxisteils. Eine andere, gleichfalls auf die Arbeit mit Fenstern ausgerichtete Modulart ist das MDI-Formular. Es unterscheidet sich vom gewöhnlichen Formularmodul darin, dass es das Rüstzeug für die Programmierung mit einem Hauptfenster und beliebig vielen weiteren, ersterem untergeordneten Fenstern sozusagen bereits mitbringt. MDI-Formularmodule kommen dementsprechend dann zum Einsatz, wenn es um die Darstellung und Verwaltung mehrerer Dokumente (Formularmodule) »unter einem gemeinsamen Dach« (MDI-Formularmodul) geht. Weitere Informationen hierzu im Abschnitt »Selbst definierte Klassen« auf Seite 318. Die schlichte Bezeichnung Modul für die dritte Modulart verrät bereits, dass es sich hierbei um ein unspezifisches Standardmodul handelt. Ein solches Modul kann beispielsweise die Logik für die Koordination weiterer Module enthalten oder schlicht als Bibliothek für von allen Modulen eines Projekts gemeinsam genutzte Funktionen, Prozeduren, Variablen, Typen und Konstanten fungieren.
25
Programme und Module in Visual Basic
●
Quelltext oder Interpretercode – liegt im ASCII-Format vor und kann die Dateierweiterungen .frm (Formular und MDI-Formular), .bas (Programmmodul), .ctl (ActiveXSteuerelement) oder .cls (Klassenmodul, ActiveX-Komponente) haben. Zur Ausführung des Quelltextes im Visual-Basic-Interpreter sind darüber hinaus je Quelltext eine Projektdatei (.vbp) und bei Gruppierung mehrerer Projekte zu einem Ganzen eine Gruppierungsdatei (.vbg) vonnöten. Compilercode – liegt als ausführbarer Maschinencode oder P-Code vor und kann die Dateierweiterungen .exe (Ausführbare Datei oder prozessexterne ActiveX-Komponente), .dll (dynamische Bibliothek oder prozessinterne ActiveX-Komponente) oder .ocx (ActiveX-Steuerelement) tragen.
Programme und Module in Visual Basic
Programme und Module in Visual Basic
Geheimnisvoller ist die Bezeichnung Klasse für die vierte Modulart. Sie ist dafür gedacht, die neu mit Visual Basic hinzugekommene Seite der objektorientierten Programmierung aktiv zu bedienen, sprich, eigene Objekte zu implementieren. Sieht man ein Objekt als Kombination aus einem benutzerdefinierten Datentyp und den zugehörigen Operationen über diesem Datentyp an, wird die Rolle des Klassenmoduls schnell deutlich. Es beherbergt die Beschreibung des Objektdatentyps hinsichtlich seiner Repräsentation und seiner Methoden (lies: Funktionen und Prozeduren). Weitere Informationen hierzu im Abschnitt »Selbst definierte Klassen« auf Seite 318. Die Modulart Benutzersteuerelement ist dem Bereich der ActiveX-Programmierung zuzurechnen. Sie erlaubt die Bereitstellung eigener Steuerelemente im OCX-Format, deren Funktionalität im (allerdings recht reichhaltigen) Angebot der unter Windows allgemein verfügbaren Steuerelemente nicht enthalten ist. Anders als ein Formular ist ein Benutzersteuerelement im Allgemeinen nicht dazu da, ein komplettes Fenster darzustellen, sondern – wie der Name schon sagt – nur ein Steuerelement, das sich auf einem Formular platzieren lässt. Aus diesem Grund besitzt ein Benutzersteuerelement für gewöhnlich weder Menüleisten, Symbolleisten und andere Dinge, die bei Formularen anzutreffen sind, noch ein feste Größe. Vergleichbar mit den Standardsteuerelementen, werden die sichtbaren Abmessungen eines Benutzersteuerelements erst bei Platzierung des fertigen Steuerelements im Verlauf des Formularentwurfs oder programmgesteuert festgelegt. Das Benutzersteuerelementmodul enthält die gesamte Logik eines Benutzersteuerelements sowie die Schnittstellendefinition für den Datenaustausch (Eigenschaften). Wie das Formular Minimalbestandteil des Projekttyps »Standard-EXE« ist, so ist das Benutzersteuerelement Minimalbestandteil des Projekttyps »ActiveX-Steuerelement«. Auch die Modulart Eigenschaftsseite fällt in den Bereich der ActiveX-Programmierung. ActiveX-Steuerelemente haben oft komplexe Eigenschaften (so etwa zusammengesetzte Datentypen oder Objekte), deren Darstellung im standardmäßigen Fenster Eigenschaften der Entwicklungsoberfläche nicht mehr erfolgen kann. Für diesen Fall besteht die Möglichkeit, eine oder mehrere Eigenschaftsseiten für die Eingabe der Eigenschaften bereitzustellen. Da ein ActiveX-Steuerelement, das beim Entwurf eines Formulars, eines anderen Steuerelements oder einer anderen Komponente in den Entwurfbereich gezogen wird, standardmäßig (als Entwurfsinstanz) zur Ausführung kommt, steht insbesondere auch der hinter einer Eigenschaftsseite steckende Code zur Verfügung. Eine Eigenschaftsseite lässt sich wie ein Formular unter Verwendung von Steuerelementen im Designerfenster der Entwicklungsumgebung zusammenstellen und im Code-Editor mit der entsprechenden Logik für den Datenaustausch mit einem ActiveX-Steuerelement, aber auch mit mehreren ActiveX-Steuerelementen (bei Mehrfachauswahl von ActiveXSteuerelementen im Designerfenster, denen die gleiche Eigenschaftsseite zugeordnet ist) versehen. Die letzte Modulart, Designer, ist mehr oder weniger ein Sammelbecken für die verschiedensten Objekte. Visual Basic hält für jedes dieser Objekte ein Designer-Werkzeug bereit, das ähnlich dem Formulardesigner die interaktive visuelle Gestaltung der Objektkomponente ermöglicht. Wie beim Formular wird in der Codekomponente eines solchen Objekts seine spezifische Reaktion auf bestimmte Ereignisse festgelegt. Unter den Werkzeugen finden sich der DHTML-Designer für den Entwurf und die Programmierung dynamischer HTML-Seiten, der Data-ReportDesigner für den Entwurf und die Programmierung von Datenberichten, der Data-Environment-Designer für die interaktive Gestaltung und das Management der Datenumgebung für Datenbankverbindungen mit programmgesteuerten Datenzugriffen sowie der WebClass-Designer für die Bereitstellung von Active Server Pages (.asp-Dateien) im Zusammenhang mit verteilten Anwendungen im Internet oder Intranet und dem Internet Information Server. Tipp
................................................... Tipp
Module sind ein sehr geeignetes Mittel für die Strukturierung von Programmcode hinsichtlich seiner Wiederverwendbarkeit. Die Vielfalt der unter Visual Basic vorhandenen Modularten för-
26
Literale und Konstanten
dert zwar bereits von sich aus die modulare Programmierung, ersetzt aber verständlicherweise nicht die bewusste Aufteilung des Codes in spezifische und allgemein verwendbare Programmtexte. Wer sich beizeiten vernünftige Bibliotheken anlegt, kann später um so mehr davon profitieren. Beispiele
................................................... Beis piele
' Projekt: Modultest ' vollständiger Code von Module1 Sub Main() ' Main ist Startobjekt des Projekts Dim f As New Form1 f.Caption = "Erster Aufruf" ' Lädt Formular Form1 in Hauptspeicher f.Show 1 ' Zeigt Formular an und wartet, bis dieses endet f.Caption = "Zweiter Aufruf" f.Show 1 ' Zeigt Formular an und wartet, bis dieses endet End Sub ' vollständiger Code von Form1 ' Formular enthält Schaltfläche "Beenden" Private Sub Beenden_Click() Unload Me End Sub
Das in den folgenden beiden Abbildungen gezeigte Projekt EuroDMTestprojekt besteht aus zwei Modulen, einem einfachen Formularmodul und einem Benutzersteuerelementmodul. Das Programm testet ein Benutzersteuerelement namens EuroDM, welches die Ein- und Ausgabe von Geldbeträgen wahlweise in DM oder Euro gestattet. Der Einfachheit halber wurde auf eine Rundung und eine Korrektur der Punkt-/Komma-Problematik verzichtet. Das aus zwei Textund zwei Bezeichnungsfeldern bestehende Benutzersteuerelement rechnet Eingaben in der einen Währung automatisch in die andere um und erlaubt die Definition und Abfrage seiner beiden Textfelder über die Eigenschaften Euro und DM. Da die Textfelder eine gegenseitige Wertänderung im Rahmen ihrer Change-Behandlung vornehmen, sind ein wenig Logik und die globale Zustandsvariable Changing nötig, um eine Endlosschleife zu verhindern.
Literale und Konstanten [Public | Private] Const ConstName [As Typ] = Literal [Public | Private] Const ConstName [As Typ] = LiteralerAusdruck
27
Literale und Konstanten
Das einfache Projekt Modultest besteht aus zwei Modulen, einem gewöhnlichen Modul Module1 und einem Formularmodul Form1. Module1 enthält eine Startprozedur namens Main, die im Dialog PROJEKTEIGENSCHAFTEN als Startobjekt spezifiziert ist. Form1 enthält dagegen nichts weiter als eine Schaltfläche Beenden zum Beenden des Formulars. In diesem Programm ruft Main als Startobjekt Form1 zweimal hintereinander als gebundenes Formular auf und passt dazu den Fenstertitel entsprechend an. Wählt man Form1 als Startobjekt, erfolgt nur ein Aufruf des Formulars, während das Modul Module1 gar nicht erst zur Ausführung kommt:
Literale und Konstanten
Literale und Konstanten
Testprojekt für das Benutzersteuerelement EuroDM im Entwurfsmodus
Das Benutzersteuerelement EuroDM im Einsatz Beschreibung
................................................... Bes c hreibung
Die elementarste Unterscheidung bei der Datenverarbeitung, an der selbst einfachste Taschenrechner nicht vorbeikommen, ist die zwischen Daten und Befehlen. Als Datum bezeichnet man die Repräsentation eines Werts in einem spezifischen Datenformat (Datentyp). Ein Datum kann als Literal, als Konstante oder als Variable vorliegen. Im Falle des Literals steht der Wert für sich selbst, etwa wie 42 für die Zahl Zweiundvierzig oder »Rudolf« für den Vornamen des Autors dieses Buches. Im Falle der Konstanten hat man es genau genommen gleichfalls mit einem Literal zu tun, da der Interpreter bzw. Compiler jedes Vorkommen einer Konstanten im Quelltext automatisch durch ihren literalen Wert ersetzt. Variablen sind dagegen auf ein bestimmtes Datenformat (Datentyp) zugeschnittene Behälter, die einen Bezeichner (lies: einen Namen) tragen und einen (in dem bestimmten Datenformat gehaltenen) Wert enthalten. Während sich der Wert eines Literals oder einer Konstanten im Programmverlauf nicht ändern kann, weil – wenn man so will – Bezeichner und Wert identisch sind, kann eine Variable (im
28
Literale und Konstanten
Anwendung
................................................... Anwendung
Ein Literal kann überall dort stehen, wo ein Wert erforderlich ist – so bei Zuweisungen als Rechtswert (lies: rechts von Gleichheitszeichen), in Ausdrücken als Operand, bei Funktionsund Prozeduraufrufen als Argument für einen Parameter. Für die Notation literaler Größen gibt es feste Regeln, die sich auch von Datentyp zu Datentyp unterscheiden können. So sind Zeichenfolgenliterale in doppelte Anführungszeichen einzuschließen, und damit es zu keiner Verwechslung zwischen dem doppelten Anführungszeichen als Zeichen und dem Zeichenfolgenende kommt, muss das Anführungszeichen doppelt notiert werden, wenn es als Zeichen auftritt. strMyName = "Rudolf Huttary" Position$ = "Das Schiff liegt 2° 3' und 42"" Ost"
Zahlenliterale können Sie in dezimaler Schreibweise, wie gewohnt: Const cMaxSize = 255
in hexadezimaler Schreibweise durch Voranstellen von &H: Const cMaxSize = &HFF
' dezimaler Wert 255
oder in oktaler Schreibweise durch Voranstellen von &O notieren: Const cMaxSize = &O377
' dezimaler Wert 255
Die dezimale Notation von Zahlenliteralen mit Nachkommastellen erfolgt mit Dezimalpunkt. (Achtung: In Zeichenfolgen, die später implizit oder mittels einer Funktion wie Val umgewandelt werden, gelten jedoch die Ländereinstellungen, so dass hier auf einem deutschen System ein Komma zu notieren ist.) Etwaige Zehnerpotenzen sind, durch ein »E« abgetrennt, an die Dezimaldarstellung anzuhängen. Positive Vorzeichen können Sie weglassen; negative Vorzeichen stellen Sie jeweils dem Wert voran, für den sie gelten. Const delta = -2.14E-27
' Minus 2 Komma 14 mal 10 hoch Minus 27
29
Literale und Konstanten
Rahmen ihres Datenformats) unter Beibehaltung ihres Namens jederzeit ihren Wert ändern. Anders gesagt: Ein Literal ist ein Wert, eine Konstante steht für einen Wert und eine Variable enthält einen Wert. Ein Befehl ist eine allgemeine Vorschrift für die Manipulation von Daten im weitesten Sinne und wird in Visual Basic häufig mit der so genannten Anweisung gleichgesetzt (vgl. »Anweisungen«, S. 33). So ist beispielsweise die Zuweisung eines Werts an eine Variable, die so genannte Zuweisungsoperation mit »=« ebenso ein Befehl wie die Ausgabe einer Grafik mit automatischer Größenanpassung in ein Bildfeld, das auf einem Formular gelegen ist. Als Operationen bezeichnet man dagegen eine besondere Art von Befehlen, bei der ein Operator (lies: Rechenvorschrift) auf einen oder mehrere Operanden (lies: Werte einer bestimmten Art) angewendet wird und einen neuen Wert (gleicher Art) produziert – so etwa die Bildung der Summe zweier Zahlen, die Verkettung von Zeichenfolgen, die Restwertdivision (Modulo-Rechnung) oder, wie gesagt, die Zuweisung eines Werts an eine Variable. Ist ein Wert so notiert, dass er erst durch Anwendung von Operatoren auf Operanden berechnet werden muss, spricht man von einem Ausdruck. Sind die Operanden als Literale notiert, spricht man genauer von einem literalen Ausdruck.
Literale und Konstanten
Der standardmäßige Datentyp von Zahlenliteralen ist Integer bzw. – bei vorhandenen Nachkommastellen – Single. Um einen anderen Datentyp zu erzwingen, können Sie dem literalen Wert ein Typkennzeichen anhängen.
Literale und Konstanten
Const cAlpha = 100& Const cBeta = 100! Const cGamma = 100#
' cAlpha ist vom Typ Long ' cBeta ist vom Typ Single ' cGamma ist vom Typ Double
Nicht ganz so eingängig sind die Regeln für die Notation von Datumsliteralen. Werte dieses Typs repräsentiert Visual Basic als 64-Bit-Gleitkommazahlen (8 Bytes) nach IEEE und kann somit Datumsangaben im Bereich zwischen dem 1. Januar 100 und dem 31. Dezember 9999 sowie Uhrzeiten im Bereich von 0:00:00 bis 23:59:59 unterscheiden. Datumsliterale können Datumsangaben und Zeitangaben kombinieren, aber auch je einzeln ausdrücken. Der Notation literaler Datums- und Zeitangaben liegt das amerikanische Format zugrunde, das heißt, in Datumsangaben wird der Monat vor dem Tag notiert und die Abtrennung erfolgt durch das Zeichen »/«, und Zeitangaben sind in 12-Stunden-Darstellung gefolgt von »PM« oder »AM« zu treffen. Zudem ist die Darstellung durch ein führendes und abschließendes Zeichen »#« einzuschließen. (Der Editor erkennt übrigens auch andere Darstellungen, so etwa #1 Feb 99#, und korrigiert diese beflissen in das erwünschte Format.) Dim a = b = c =
a As Date, b As Date, c As Date #2/1/1996 1:12:01 PM# #12:01:00 PM# #2/1/1996#
Konstanten lassen sich – wie Literale – überall dort einsetzen, wo ein Wert erforderlich ist. Gegenüber Literalen bieten sie den Vorteil, dass der Wert, für den sie stehen, an zentraler Stelle definiert und fortan formal durch Angabe des Konstantenbezeichners notiert werden kann. Die Definition einer Konstanten erfolgt zur Entwurfszeit im Rahmen einer Const-Anweisung durch Zuweisung eines Literals oder eines literalen Ausdrucks an einen Konstantenbezeichner. Bei der Wahl des Konstantenbezeichners gelten die üblichen Vorschriften für die Bezeichnerwahl (vgl. »Bezeichner und Namensraum«, S. 34). Es ist legitim, bei der Definition von Konstanten, andere, bereits definierte Konstanten als Rechtswerte oder als Operanden in Ausdrücken zu verwenden. Const MinSize = 8 Const MaxSize = MinSize + 2741
' MinSize steht für den Wert 8 ' MaxSize steht für den Wert 2749
................................................... Warnung
Warnung
Wer denkt, dass die Konstantendeklarationen Const a = 65535
und Const b = &HFFFF
identisch sind, der irrt. a steht für die Zahl 65535 (wie notiert), b jedoch für -1. Das liegt daran, dass Visual Basic a implizit den Datentyp Long und b den Datentyp Integer zuordnet und der Wert -1 aufgrund der Repräsentation negativer Zahlen durch Zweierkomplementbildung die hexadezimale Darstellung &HFFFF hat. Damit beide Konstanten den gleichen Wert erhalten, ist die hexadezimale Darstellung so zu notieren: Const b = &H0FFFF
30
Literale und Konstanten
In Basic existiert nur formal ein Unterschied zwischen der bitweisen Operation und der logischen Operation, da die Repräsentation logischer Werte genau so gewählt ist, dass eine bitweise Operation die jeweilige logische Operation hervorbringt. Const a = Not 2
' a steht für -3 (Zweierkomplement)
und s% = -3 And 42 Or 1 ' s% erhält den Wert 41
Tipps
................................................... Tipps Vermeiden Sie im Programmtext nach Möglichkeit die Verwendung von Literalen und arbeiten Sie mit Konstanten, die Sie jeweils zu Anfang des Moduls im Bereich ALLGEMEIN (seltener: zu Anfang einer Prozedur) definieren. Auf diese Weise können Sie den Wert eines Literals an zentraler Stelle mit globaler Wirkung für den gesamten Programmtext ändern. Const cMaxSize = 255 ... For x = 0 to cMaxSize
Um für Konstanten einen bestimmten Typ zu erzwingen, erklären Sie diesen entweder durch die Angabe eines Typkennzeichens oder durch eine explizite Typdeklaration. Const cMaxSize As Integer = 255
Bildet ein literaler Wert die Grundlage für einen weiteren literalen Wert, so empfiehlt es sich, den zweiten Wert als Ausdruck zu schreiben, der auf den ersten aufbaut, oder anstelle des zweiten Literals eine Variable einzusetzen und deren Wert rechnerisch aus dem ersten Literal zu ermitteln. Const cMinSize = 100 ' Const cMaxSize = 199 Const cMaxSize = 2 * cMinSize – 1 Const cVerschlüsseltesKennWort = "2XVf5uhf" ' Const KennwortLänge = 8 ... Dim KennwortLänge As Integer KennwortLänge = Len(cVerschlüsseltesKennwort)
' falsch ' richtig
' falsch
' richtig
Visual Basic besitzt eine sehr reichhaltige Fülle an vordefinierten Konstanten (auch Objektkonstanten), deren Einsatz nicht nur ein Garant für Kompatibilität ist, sondern auch (gerade im Zusammenhang mit Objektkonstanten) zu Laufzeitverbesserungen führt. Einen Überblick über diese Konstanten gibt der Objektkatalog (aufzurufen über ANSICHT/OBJEKTKATALOG oder (F2)). Beispiel
................................................... Beis piel
Das folgende vollständige Programm Apfelmann, eine stark vereinfachte Version des mehrfach in diesem Buch diskutierten Apfelmännchens, demonstriert den Umgang mit literalen Werten. Da das Programm während der Berechnung keine Aktualisierung der Anzeige vornimmt, erscheint das Bild erst nach Abschluss aller Berechnungen – es sei denn, man fügt einen ShowAufruf in die Load-Routine ein und setzt die AutoRedraw-Eigenschaft des Formulars auf False.
31
Literale und Konstanten
Die von Visual Basic verwendete interne Repräsentation für Datumswerte kann mit folgender Kapriole aufwarten: Der 30. Dezember 1899 ist nicht darstellbar. Visual Basic interpretiert diesen Tag vielmehr als reine Zeitangabe #12:00:00 AM#.
Literale und Konstanten
Literale und Konstanten
' Bereich Allgemein Const cx1 As Double = -2.1 Const cy1 As Double = -1.2 Const cx2 As Double = 0.7 Const cy2 As Double = 1.2 Const cFenster_x = 4000 Const cFenster_y = 3600 Const cMaxIterat As Integer = 200 Const cGrenze As Double = 100
' Bildausschnitt des Apfelmännchens
' Fensterabmessungen nicht zu groß ' sonst dauert es zu lange
Private Sub Apfel(m_x1, m_x2, m_y1, m_y2, step_x, step_y) Dim r1 As Double, re As Double, im As Double Dim zr As Double, zi As Double, it As Integer For zr = m_x1 To m_x2 Step step_x ' alle Spalten in x-Richtung For zi = m_y1 To m_y2 Step step_y ' alle Punkte in Spalte zr re = 0 ' Realteil initialisieren im = 0 ' Imaginärteil initialisieren For it = 0 To cMaxIterat ' Iteration für Punkt r1 = re * re – im * im + zr im = 2 * re * im + zi re = r1 If re < -cGrenze Or re > cGrenze Or _ im < -cGrenze Or im > cGrenze Then PSet (zr, zi), it * 16 ' Punkt ausgeben Exit For End If Next it Next zi Next zr End Sub Private Sub Form_Load() Dim step_x As Double, step_y As Double Width = cFenster_x ' Fensterabmessungen anpassen Height = cFenster_y ' AutoRedraw = False ' Show ScaleMode = vbPixels ' Ab jetzt Bildpunkte als Einheit step_x = (cx2 – cx1) / ScaleWidth ' Breite eines Bildpunkts step_y = (cy2 – cy1) / ScaleHeight ' Höhe eines Bildpunkts Scale (cx1, cy1)-(cx2, cy2) ' Koordinatensystem vorgeben Apfel cx1, cx2, cy1, cy2, step_x, step_y ' Apfelmännchen berechnen End Sub Verwandte Befehle
................................................... Verwa ndte Befehle
Dim, Public, Private, ReDim Verwandte Themen
................................................... Verwandte Them en
Datentypen und ihre Operationen (S. 49); Operatoren für elementare Datentypen und logische Bedingungen (S. 54); Anweisungen (S. 33); Bezeichner und Namensraum (S. 34)
32
Anweisungen
Anweisungen _ (Operator für Übertrag in nächste Zeile) : (Operator für Aneinanderreihung von Anweisungen) [Let] Var = Wert Rem Kommentar ' Kommentar
Beschreibung
................................................... Bes c hreibung
Der Quelltext eines Visual-Basic-Moduls setzt sich aus einer Folge von Anweisungen zusammen. Unter einer Anweisung versteht man in Visual Basic alles, was einen Befehl darstellt, ohne selbst einen Wert zu verkörpern. Man unterscheidet zwischen einfachen Anweisungen, die in einer einzelnen Zeile stehen können, und Kontrollstrukturen, die als zusammengesetzte Anweisungen ihrerseits Anweisungsfolgen strukturieren und auf höherer Ebene zu einer logischen Einheit zusammenfassen. Eine besondere Anweisungsform stellen Compileranweisungen dar, denen das Zeichen # vorangeht (#If Then...#Else...#End If). Sie werden nicht erst zur Laufzeit, sondern bereits zur Übersetzungszeit interpretiert und führen dazu, dass bestimmte Teile des Codes gar nicht erst übersetzt werden. Ausdrücke in solchen Anweisungen dürfen nur aus Literalen und Konstanten zusammengesetzt sein. Visual Basic definiert in diesem Zusammenhang die Boolean-Konstanten Win16 und Win32, deren (jeweils gegensätzlicher) Wahrheitswert ausdrückt, ob die Übersetzung auf einer 16-Bit- oder 32-Bit-Plattform erfolgt. Anwendung
................................................... Anwendung
Zu den einfachen Anweisungen zählen Zuweisungsoperationen (Let, Set, LSet, RSet Mid), Prozeduren (vordefinierte und benutzerdefinierte), Deklarationen (Const, Dim, Private, Public, Declare) und Definitionen (Function, Sub) aller Art. Alles, was Visual Basic als einfache Anweisungen betrachtet, kann als eigenständige Programmzeile stehen. Überlange einfache Anweisungen lassen sich mittels des Übertragsoperators _ (Unterstrich) in zwei oder mehrere Zeilen aufteilen. Dabei muss der Übertragsoperator durch ein Leerzeichen getrennt als letztes Zeichen in der Zeile notiert sein – und darf insbesondere keine Zeichenfolgenliterale trennen. Mehrere Anweisungen lassen sich in eine Zeile stellen, wenn zwischen den Anweisungen der Aneinanderreihungsoperator : (Doppelpunkt) notiert ist. Eine Kontrollstruktur ist eine zusammengesetzte Anweisung, die sich über mehrere Zeilen hinweg erstrecken kann und eine Rahmung untergeordneter Anweisungsfolgen mittels Schlüsselwörtern vorsieht, entsprechend den die Kontrollstruktur geltenden syntaktischen Regeln. Im Falle der If-Anweisung akzeptiert Visual Basic sowohl die kompakte Zeilenform als auch eine mehrzeilige Form. Rem-Anweisungen sind nur aus formaler Sicht Anweisungen. Eigentlich stellen sie Kommentare dar, da sie der Compiler bei der Übersetzung ebenso ignoriert wie Kommentare, die durch ein Hochkomma ' eingeleitet sind.
33
Anweisungen
#If Ausdruck Then Anweisungsfolge1 [#Else | ElseIf AusdruckN AnweisungsfolgeN #End If
Bezeichner und Namensraum
Bezeichner und Namensraum Bezeichner, Namensraum, Schlüsselwörter Private, Public, Dim Beschreibung
Bezeichner und Namensraum
................................................... Bes c hreibung
Bezeichner fungieren in Visual Basic als formale Namen für verschiedenen Größen: Konstanten, Variablen und Objekte, Funktionen und Prozeduren (Methoden) sowie Module. Bei der Wahl eines Bezeichners für eine bestimmte Größe genießt der Programmierer weitestgehende Freiheiten. Er muss lediglich die formalen Regeln zur Bezeichnerbildung einhalten und auf Eindeutigkeit im jeweiligen Namensraum achten. Zudem verbietet sich die Verwendung der von Visual Basic reservierten Schlüsselwörter, die von der Sprache selbst mit einer festen Bedeutung belegt sind. Die formalen Regeln für die Bezeichnerbildung in Visual Basic sind: ● ●
●
●
Bezeichner dürfen zwischen ein und 255 Zeichen lang sein Bezeichner müssen mit einem Buchstaben beginnen, wobei auch deutsche Umlaute und Sonderzeichen (genau genommen sogar alle ANSI-Zeichen mit einem Code zwischen 128 und 255) erlaubt sind. Für alle weiteren Zeichen sind darüber hinaus auch die Ziffern »0« bis »9« und der Unterstrich »_« erlaubt. Visual Basic unterscheidet in der Notation von Bezeichnern nicht zwischen Groß- und Kleinschreibung. MyVar und Myvar bezeichnen somit dasselbe. (Der Editor der integrierten Entwicklungsumgebung setzt allerdings auf Modulebene eine konsistente Schreibweise durch, indem er jeweils die bei der Deklaration eines Bezeichners verwendete Schreibweise erzwingt und bei implizit deklarierten Bezeichnern jeweils die zuletzt verwendete Schreibweise für alle Vorkommen des Bezeichners übernimmt.)
Als Namensraum eines Bezeichners wird der Bereich bezeichnet, innerhalb dessen ein Bezeichner bekannt ist. Der größte Namensraum ist der so genannte globale Namensraum oder die Programmebene. Bezeichner, die diesem Namensraum angehören, sind in allen Modulen eines Programms bekannt und somit global ansprechbar. Jeweils kleinere Namensräume sind die Modulebene und Prozedurebene. Bezeichner, deren Namensraum auf die Modulebene beschränkt ist, sind nur innerhalb des Moduls bekannt, in dem sie deklariert wurden, und Bezeichner, deren Namensraum auf die Prozedurebene beschränkt ist, sind nur innerhalb der Prozedur (bzw. Funktion) bekannt, in dem sie deklariert wurden. Bei Bezeichnerkonflikten, die durch die Wahl gleicher Bezeichner für unterschiedliche Größen in überlappenden Namensräumen unterschiedlicher Stufe entstehen, ist im kleineren Namensraum immer nur der in diesem Namensraum deklarierte Bezeichner sichtbar. Anwendung
................................................... Anwendung
Während Modulbezeichner von vornherein dem globalen Namensraum angehören, müssen Prozeduren, Funktionen und Methoden explizit mit dem Zusatz Public deklariert werden, damit ihr Bezeichner im globalen Namensraum sichtbar wird. Geschieht die Deklaration dagegen mit dem Zusatz Private oder ohne Zusatz, ist ihr Bezeichner jeweils immer nur innerhalb des Moduls bekannt, das den Quelltext enthält. Die vielfältigsten Möglichkeiten für die Gestaltung des Namensraums bieten Konstanten, Variablen und Objekte. Bei impliziter oder expliziter Deklaration in einem Prozedur- oder Funktionskörper erstreckt sich der Namensraum des Bezeichners ab Deklaration bis zum Endes des Körpers. Findet die Deklaration dagegen auf Modulebene (im Bereich ALLGEMEIN) ohne Zusatz oder unter Angabe von Dim oder Private statt, ist der Bezeichner fortan im restlichen Teil des Modulkörpers bekannt und insbesondere
34
Bezeichner und Namensraum
auch in allen Funktionen und Prozeduren (bzw. Methoden) sichtbar. Während sich der Namensraum einer Konstanten grundsätzlich nicht über mehrere Module erstrecken kann, sondern maximal auf die Modulebene beschränkt bleibt, unterliegen Variablen und Objekte dieser Einschränkung nicht. Damit eine Variable oder ein Objekt im globalen Namensraum bekannt wird, muss eine Public-Deklaration auf Modulebene erfolgen. Warnung
................................................... Wa rnung
Dim x As Single, y As Single ... Sub BerechneMatrix(a(), b()) ... For x = 1 To 100 For y = 1 To 200 ... Next x, y End Sub
Die Variablen x und y werden hier leichtfertig und im Vertrauen auf die implizite Deklaration als Zählvariablen eingesetzt. Eine solche findet aber nicht statt, da unter diesen Bezeichnern globale Variablen bekannt sind. Diese verlieren durch die Schleifen leider ihren Wert, was an völlig anderer Stelle seltsame Nebeneffekte hervorbringen kann. Deshalb immer fleißig deklarieren und auf globaler Ebene keine nichtssagenden 08/15-Bezeichner vergeben. Tipp
................................................... Tipp
Wer die Wahl hat, hat die Qual. Dieser Spruch bewahrheitet sich insbesondere, wenn es darum geht, sich immer wieder neue Bezeichner auszudenken. Eine gute Hilfe ist es, sich dabei von formalen Kriterien leiten zu lassen und bei der Auswahl darauf zu achten, dass ein Bezeichner möglichst viel über die Größe aussagt, für die er steht. Um Konstanten, Variablen und Prozeduren besser auseinander halten zu können, folgen viele Programmierer daher bei der Bildung von Bezeichnern bestimmten Konventionen. So hat es sich beispielsweise bewährt, Konstanten immer in Großschreibung zu notieren, Variablen mit einem Kleinbuchstaben beginnen zu lassen und Prozeduren mit einem Großbuchstaben. Sehr verbreitet ist zudem die so genannte ungarische Notation, die durch Bezeichnerpräfixe den zu einem Bezeichner gehörigen Datentyp zum Ausdruck bringt. Variablen vom Typ Integer würden mit einem »i« beginnen, Variablen vom Typ String mit einem »s« oder »str« usw. Der restliche Teil des Bezeichners kann dann noch in gemischter Groß- und Kleinschreibung notiert werden, um seine Funktion mnemonisch besser zum Ausdruck zu bringen. (Diese Schreibweise wird inzwischen der früher weit verbreiteten Zusammensetzung mit Unterstrichen »_« vorgezogen.) Bei sehr enger Verwandtschaft zweier Größen leistet oft auch die Abzählung durch Anhängen von Ziffern als Suffix gute Dienste.
35
Bezeichner und Namensraum
Visual Basic betrachtet es nicht als Fehler, wenn Sie ein und denselben Bezeichner in verschiedenen Namensräumen vergeben. Solange die Namensräume nichts miteinander zu tun haben, wird dies keine allzu große Verwirrung stiften, ja, vielfach sogar Vorteile bringen. Problematisch wird es allerdings, wenn ein Namensraum den anderen umfasst, wie etwa die Modulebene eine Prozedurebene. In diesem Fall wird die auf Modulebene deklarierte Variable in der Prozedurebene unsichtbar, weil ihr Bezeichner durch die lokal in der Prozedur deklarierte Variable »verdeckt« ist. Dies ist meist zu verschmerzen, oft sogar auch gewollt. Fehlerträchtig wird es allerdings, wenn der Programmierer auf eine implizite Deklaration auf Prozedurebene setzt und unwissentlich oder fahrlässig den Bezeichner einer global deklarierten Variable erwischt. So führt beispielsweise der folgende Code zu unangenehmen Nebeneffekten:
Kontrollstrukturen
Beispiel
................................................... Beis piel
Kontrollstrukturen
' Unterscheidung der Notation durch Klein und Großschreibung Const MAXSIZE = 10 ' Konstanten in Großschreibung Private anzahl As Integer ' Variable in Kleinschreibung Private ergebnis As Single, werte(MAXSIZE) As Single Function StandardAbweichung(a() As Single) As Single ' Ungarische Notation Const cMaxSize = 10 ' Präfix "c" für Konstante Private iElemAnzahl As Integer ' Präfix "i" für Integer Private bChanging As Boolean ' Präfix "b" für Boolean Private frmAnmeldung As Form1 ' Präfix "frm" für Formular Private sErgebnis1 As Single ' Präfix "s" für Single, Abzählung Private sErgebnis2 As Single ' Präfix "s" für Single, Abzählung Function sStandardAbweichung(a() As Single) As Single Verwandte Befehle
................................................... Verwa ndte Befehle
DefType Verwandte Themen
................................................... Verwandte Them en
Datentypen und ihre Operationen (S. 49); Typkennzeichen und Bezeichnerbereiche für Typen (S. 167); Geltungsbereiche von Variablen (S. 173)
Kontrollstrukturen Aufgabe einer Kontrollstruktur ist die Steuerung der Programmausführung in Abweichung von der im Quelltext notierten Anweisungsfolge.
Unbedingte und bedingte Verzweigung, Subroutinen GoTo {Marke | Zeile} ... Marke: GoSub SubRoutine ... SubRoutine: ... Return If Bedingung Then Anweisung [Else Anweisung] If Bedingung Then [ThenAnweisungsBlock] [ElseIf Bedingung1 [ElseIfAnweisungsBlock]] ... [Else [ElseAnweisungsBlock]] End If
36
Unbedingte und bedingte Verzweigung,
Subroutinen
Beschreibung
................................................... Bes c hreibung
Anwendung
................................................... Anwendung
Der unbedingte Sprung mit GoTo, aber auch die GoSub-Unterprogramme, zählen zu den Erblasten von Basic, denen die Sprache in ihrer Anfangszeit das Attribut »Spagetti-Code« zu verdanken hatte. In der modernen, strukturierten Programmierung findet in seltenen Fällen nur noch die GoTo-Anweisung Anwendung, nämlich im Zusammenhang mit der Fehlerbehandlung, wo der Befehl im Zusammenspiel mit On Error gezielte Sprünge zu Fehlerbehandlungsroutinen ermöglicht. Anstelle von GoSub-Unterprogrammen benutzt man heute ausschließlich benutzerdefinierte Funktionen und Prozeduren. Anders die If-Anweisung. Sie hat an Wichtigkeit im Laufe der Zeit nichts eingebüßt. In der mehrzeiligen Form eignet sie sich bei Verwendung von ElseIf-Zweigen für umfangreichere Fallunterscheidungen, auch wenn die Select-Anweisung hier oft die bessere Wahl darstellt. IfAnweisungen können natürlich auch vollständig verschachtelt werden, von der Laufzeit her sind ElseIf-Formulierungen jedoch überlegen. Warnung
................................................... Wa rnung
Aufrufe parameterloser Prozeduren und schlichte Sprungmarken kann Visual Basic von der Notation her nicht unterscheiden, wenn der Doppelpunkt-Operator zur Anreihung mehrerer Anweisungen in einer Programmzeile ins Spiel kommt. Visual Basic zieht in diesem Fall die Interpretation »Sprungmarke« der anderen vor. Setzen Sie parameterlose Prozeduraufrufe daher besser in eigene Zeilen (ohne nachfolgenden Doppelpunkt) oder benutzen Sie die CallAnweisung für den Aufruf. Beispiel
................................................... Beis piel
Die folgenden Zeilen zeigen, wie die Logik für die Berechnung der Sgn-Funktion aussieht:
37
Kontrollstrukturen
Die einfachste (inzwischen aber verpönte) Möglichkeit, die Kontrolle einem anderen Programmteil weiterzugeben, ist der unbedingte Sprung mittels GoTo zu einer Sprungmarke oder Zeilennummer. Die Anweisung GoSub ermöglicht den Aufruf eines Unterprogramms (auch verschachtelt), das an der Sprungmarke oder Zeilennummer SubRoutine beginnt und bis zur nächsten Return-Anweisung (auf gleicher Ebene) reicht. Sprungmarken und Zeilennummern müssen jeweils innerhalb der gleichen Funktion/Prozedur liegen und am Zeilenanfang stehen. Eine Sprungmarke ist ein beliebiger Bezeichner gefolgt von einem Doppelpunkt. Die wichtigste Kontrollstruktur zur Steuerung des Programmablaufs ist die bedingte Verzweigung in Form der If-Anweisung, für die zwei unterschiedliche Notationen zulässig sind. In der kürzeren Schreibweise (Zeilenform) muss die Anweisung – gegebenenfalls samt Else-Teil – in einer Zeile und ohne abschließendes End If notiert sein. In der mehrzeiligen Schreibweise ist dagegen das abschließende End If unerlässlich. Die Syntax erlaubt in dieser Form zudem ElseIf-Fallunterscheidungen. Die If-Anweisung führt den im Then-Zweig gelegenen ThenAnweisungsblock aus, wenn die – meist als logischer Ausdruck formulierte – Bedingung Bedingung einen Wert ungleich 0 ergibt. Ist der Wert 0, kommt der ElseAnweisungsblock des Else-Zweiges zur Ausführung. Der Else-Zweig kann durch Einfügung von ElseIf-Zweigen weiter aufgespreizt sein, wobei ein ElseIf-Zweig nichts anderes als die optimierte Form einer verschachtelten If-Anweisung im Else-Zweig ist. Ergibt sich für die Bedingung eines ElseIf-Zweiges ein Wert ungleich 0, kommt der ElseIfAnweisungsblock zur Ausführung. Bei vorhandenen ElseIfZweigen kommt ein ElseAnweisungsblock nur zur Ausführung, wenn alle bis dahin geprüften Bedingungen den Wert 0 ergeben haben.
Kontrollstrukturen
If a > 0 Then a = 1 ElseIf a < 0 Then a = -1 End If ' für den Fall a = 0 hat a bereits den richtigen Wert! Verwandte Befehle
................................................... Verwa ndte Befehle
On Error, Select, On ... GoSub
Kontrollstrukturen
Verwandte Themen
................................................... Verwandte Them en
Operatoren für elementare Datentypen und logische Bedingungen (S. 54); Fallunterscheidung (S. 38); Fehlerbehandlung (S. 43)
Fallunterscheidung Select Case TestAusdruck [Case Fall1 [AnweisungsBlock1]] ... [Case FallN [AnweisungsBlockN]] [Case Else [ElseAnweisungsBlock]] End Select Function Switch(Ausdruck1, Wert1,[Ausdruck2, Wert2[, ...]]) As Variant Function Choose(Index As Integer, Wert1[, Wert2[,... WertN]]) As Variant On Ausdruck GoSub SprungzielListe On Ausdruck GoTo Sprungzielliste Function IIf(Kriterium As Boolean, ThenWert, ElseWert) As Variant Beschreibung
................................................... Bes c hreibung
Die Select Case-Kontrollstruktur führt eine Fallunterscheidung auf Basis des Werts von TestAusdruck durch. Für jeden zu unterscheidenden Fall ist ein Case-Zweig mit der Anweisungsfolge AnweisungsblockN zuständig. Der Case-Ausdruck Fall kann als Konstante/Literal, Is-Vergleich (offener Bereich), To-Bereich (geschlossener Bereich) oder als Fall-Aufzählung formuliert sein: Fall Fall Fall Fall
= = = =
Zahl | Zeichenfolge Is VglOperator VglWert Fall1 To FallN Fall1, ..., FallN
In einer Select-Struktur kommt maximal ein, bei vorhandenem Else-Zweig genau ein Zweig zur Ausführung. Die Switch-Funktion führt eine Fallunterscheidung auf Basis ihrer in Paaren zu je einem Ausdruck und einem potentiellen Funktionswert organisierten Parameterliste durch. Den Funktionswert WertN eines solchen Paares gibt die Funktion zurück, wenn der Ausdruck AusdruckN von links beginnend der erste Ausdruck ist, der einen von 0 verschiedenen Wert hat, also den Wahrheitswert True besitzt. Falls keiner der Ausdrücke ungleich 0 ist, liefert die Funktion den Wert Null.
38
Fallunterscheidung
Anwendung
................................................... Anwendung
Obwohl sich Fallunterscheidungen prinzipiell gesehen genauso gut über If-Anweisungen (verschachtelt oder mit ElseIf-Zweigen) formulieren lassen, sind Select Case-Formulierungen im Allgemeinen eleganter und übersichtlicher – vor allem, wenn viele Fälle zu unterscheiden sind. Die Syntax für die Formulierung der einzelnen Fälle ist reichhaltig. Der Compiler nimmt allerdings keine Überprüfung auf Überlappung vor, so dass auf eine disjunkte Formulierung der Fall-Ausdrücke geachtet werden sollte. In einfachen Fällen, wenn sich die Fallunterscheidung (wie bei Arrays) auf die indexorientierte Auswahl eines Werts aus einer Werteliste zurückführen lässt, sollte die Choose-Funktion das Mittel der Wahl sein. Dieselbe Funktionalität lässt sich allerdings mittels der Array-Funktion erzielen, wie das Beispiel am Ende des Abschnitts zeigt. Für Fallunterscheidungen, die auf die Auswahl eines Werts in Abhängigkeit verschiedener Bedingungen hinauslaufen, ist die Switch-Funktion meist das eleganteste Mittel. Die Anwendung der Funktion widerspricht aber in gewisser Hinsicht der Intuition, da es grundsätzlich zur Auswertung aller Ausdrücke kommt. In der Praxis hat das zwei Auswirkungen: Erstens muss jeder Ausdruck für jeden Fall wohldefiniert sein (kein Überlauf, keine Division durch 0 etc.), sonst kommt es zu einem Laufzeitfehler, und zweitens hat die Funktion unabhängig vom unterschiedenen Fall immer die gleichen Seiteneffekte. Von einem Gebrauch der On-Anweisung (jenseits von On Error) ist abzuraten, weil GoTo-Sprünge und GoSub-Unterprogramme angesichts der reichhaltigen Ausstattung von Visual Basic für die strukturierte Programmierung nicht mehr zeitgemäß sind. Diese Befehle werden nur noch für die Abwärtskompatibilität unterstützt. Warnung
................................................... Wa rnung
Da in Select-Strukturen immer nur ein Case-Zweig zur Ausführung kommt, ist im Falle der Überlappung von Case-Ausdrücken die Reihenfolge der Case-Aufzählung relevant. Visual Basic wertet bei Abarbeitung der Switch-Funktion immer alle Ausdrücke aus, so dass eventuelle Seiteneffekte vom Funktionswert unabhängig bleiben. Beispiel
................................................... Beis piel
Der folgende Code zeigt die eine Select-Fallunterscheidung für den von einem Textfeld gemeldeten Tastaturcode: Private Sub Text1_KeyUp(KeyCode As Integer, Shift As Integer) Select Case KeyCode Case vbKeyDelete, vbKeyBack ' wg. Überschreibmodus Case vbKeyUp, vbKeyDown ' Nächste Zeile in Textfeld AktualisiereZeile(sZeile) ' Änderungen zurückschreiben
39
Kontrollstrukturen
Die Choose-Funktion liefert den an der Position Index in der Werteliste stehenden Wert – der Wert 1 wählt Wert1, der Wert 2 wählt Wert2 usw. Die On-Kontrollstruktur ermöglicht eine Fallunterscheidung durch Auswahl eines Sprungziels aus der Liste SprungzielListe in Abhängigkeit von Ausdruck. Der Wert von Ausdruck darf zwischen 0 und 255 liegen. Ist er 0 oder größer als die Anzahl der Sprungziele in der Liste, findet kein Sprung bzw. Unterprogrammaufruf statt, ansonsten benennt er die Listenposition des anzusteuernden Sprungziels. Als Sprungziele können wahlweise Zeilennummern oder Sprungmarken genannt sein, die in der gleichen Funktion/Prozedur definiert sind. Eine Fallunterscheidung der besonderen Art stellt die Funktion IIf dar. Sie wird mit drei Parametern aufgerufen und liefert in Abhängigkeit vom Wahrheitswert des ersten Parameters den zweiten Parameter (erster Parameter ist True) oder dritten Parameter (erster Parameter ist False) als Funktionsergebnis.
Kontrollstrukturen
Kontrollstrukturen
Case vbKeyEscape List1_Click ' Ursprüngliche Fassung holen Case vbKeyEnd Start = LineLen – 1 Case vbKeyLeft Start = LinePos – 1 Case vbKeyRight Start = LinePos + 1 Case vbKeyHome Start = 0 Case Else ' Sonstige Tasten VerarbeiteTaste(KeyCode, sZeile) End Select Text1.SelStart = Start ' Position in Puffer aktualisieren Text1.SelLength = 1 ' Überschreibmodus End Sub
Hier verschiedene Formulierungen für die Zuordnung eines numerischen Werts zu einer Zeichenfolge: sZahl = Choose(a + 1, "Null", "Eins", "Zwei", "Drei", "Vier", "Fünf") sZahl = Switch(a = 0, "Null", a = 1, "Eins", a = 2, "Zwei", a = 3, _ "Drei", a = 4, "Vier", a = 5, "Fünf") sZahl = Array("Null", "Eins", "Zwei", "Drei", "Vier", "Fünf")(a)
IIf macht die Min-/Max-Berechnung leicht: Min = IIF(a < b, a, b) Max = IIF(a > b, a, b) Verwandte Befehle
................................................... Verwa ndte Befehle
If, GoTo, GoSub Verwandte Themen
................................................... Verwandte Them en
Unbedingte und bedingte Verzweigung, Subroutinen (S. 36)
Schleifen For CountVar = StartVal To EndVal [Step StepVal] [Anweisungsfolge] [Exit For] [Anweisungsfolge] Next [CountVar[, CountVar1]] For Each IteratVar In Liste [Anweisungsfolge] [Exit For] [Anweisungsfolge] Next [IteratVar[, IteratVar1 ...]] While Bedingung [Anweisungsfolge] [Exit While]
40
Schleifen
[Anweisungsfolge] Wend Do [{While | Until} Bedingung] [Anweisungsfolge] [Exit Do] [Anweisungsfolge] Loop
Beschreibung
................................................... Bes c hreibung
Schleifen sind Kontrollstrukturen, die es gestatten, einen Anweisungsblock oder Schleifenkörper in Abhängigkeit eines Kriteriums – einer logischen Bedingung oder dem Wert einer Zählvariablen – mehrmals zu durchlaufen. Das klassische Programmiermodell unterscheidet zwischen abweisenden Schleifen, die das Schleifenkriterium im Schleifenkopf vor dem ersten Durchlaufen des Schleifenkörpers prüfen (und selbst die einmalige Ausführung des Schleifenkörpers unterbinden können), und nicht abweisenden Schleifen, die ihr Kriterium nach Abarbeitung des Schleifenkörpers prüfen. Visual Basic weicht diese Unterscheidung allerdings auf, indem es sprachliche Mittel für den Schleifenabbruch an beliebiger Stelle vorsieht. Darüber hinaus lässt sich zwischen Schleifen unterscheiden, bei denen eine Berechnung der benötigten Schleifendurchläufe noch vor dem ersten Eintritt in die Schleife möglich ist (Zählschleifen), und solchen, bei denen diese Berechnung nicht möglich ist (Schleifen mit implizitem Abbruchkriterium). Schleifen, deren Kriterium sich nie erfüllt, heißen Endlosschleifen. Falls der Schleifenkörper einer Schleife eine weitere Schleife enthält, spricht man von verschachtelten Schleifen. Anwendung
................................................... Anwendung
Der Einsatzbereich von Schleifen ist derartig breit, dass es wohl nur wenige auch nur halbwegs anspruchsvolle Programme geben wird, die ohne dieses Mittel auskommen. Abweisende Schleifen werden in Visual Basic als For-, While- oder Do-Schleifen und nicht abweisende Schleifen ausschließlich als Do-Schleifen formuliert. Als Zählschleife ist die starre Form der For-Schleife am besten geeignet. Schleifen lassen sich beliebig verschachteln, solange gewährleistet ist, dass eine äußere Schleife den Schleifenkörper einer inneren Schleife jeweils vollständig enthält.
Die For...Next- Schleife Eine For-Schleife kann in einer von zwei Formen benutzt werden. In der ersten Form verlangt sie die Bekanntgabe einer Zählvariablen (CountVar) eines beliebigen numerischen Typs, eines Startwerts (StarVal), eines Endwerts (EndVal) und gegebenenfalls einer Schrittweite, wenn diese von der standardmäßigen Schrittweite 1 abweicht. Der Ablauf ist: 1. Beim ersten Eintritt der Programmausführung in den Schleifenkopf initialisiert die ForSchleife die Zählvariable mit dem Startwert. 2. Unmittelbar vor jedem Durchlaufen des Schleifenkörpers prüft die Schleife, ob die Zählvariable den Endwert bereits überschritten hat. Falls ja, endet die Schleife, und die Ausführung überspringt den Schleifenkörper. 3. Nach jedem Durchlaufen des Schleifenkörpers erfolgt automatisch die Aktualisierung der Zählvariablen unter Beachtung der geltenden Schrittweite.
41
Kontrollstrukturen
Do [Anweisungsfolge] [Exit Do] [Anweisungsfolge] Loop [{While | Until} Bedingung]
Kontrollstrukturen
Kontrollstrukturen
Beachten Sie, dass Sie zwar den Wert der Zählvariablen jederzeit ändern und somit das Verhalten der Schleife von innerhalb des Schleifenkörpers manipulieren können, nicht jedoch den geltenden Endwert oder die Schrittweite. Von einer solchen Manipulation ist aber generell eher abzuraten, da der Code dann nicht mehr übersichtlich ist. Um eine For-Schleife verfrüht abzubrechen, bedienen Sie sich besser des Befehls Exit For. Als Datentyp für die Zählvariable kommen ganzzahlige Typen wie Integer oder Long in Betracht, ebenso aber auch Fließkommatypen wie Single oder Double. Das beste Laufzeitverhalten hat eine Schleife, wenn die Zählvariable vom Typ Long ist. Um eine Schleife zum Rückwärtszählen zu bewegen, geben Sie StepVal mit einer negativen Schrittweite an sowie einen Endwert, der kleiner als der Startwert ist. Dim i As Long For i = 10 to 2 Step -2 Print i, i+1 Next i
In Form der For Each-Schleife ermöglicht die For-Schleife das Durchlaufen aller Elemente einer Auflistung Liste. An die Stelle der Zählvariable tritt die Aufzählvariable IterateVar, die bei jedem Schleifendurchlauf den Wert des jeweils nächsten Elements von Liste annimmt, bis das Ende der Auflistung erreicht ist. Eine Modifikation der Aufzählvariable innerhalb des Schleifenkörpers hat keine Auswirkung auf die Aufzählung. Einzig der vorzeitige Abbruch mittels Exit For ist möglich. Als Voraussetzungen für die Anwendung dieser Form sind zu beachten: Liste muss eine Auflistung (Collection-Objekt) oder ein Array sein, und IterateVar muss den Datentyp Variant oder einen Objektdatentyp tragen. Dim a(20) As Integer Dim i As Variant For Each i In a i = 2 Next i
' Array elementweise initialisieren
Die Nennung der Zähl- bzw. Aufzählvariablen nach dem Schlüsselwort Next ist optional. Als Abschluss verschachtelter For-Schleifen ist auch die Nennung mehrerer Zähl- bzw. Aufzählvariablen nach dem Schlüsselwort Next zulässig.
Die While...Wend- Schleife Als Standardlösung für abweisende Schleifen bietet sich die While-Schleife an. Schleifen dieser Art prüfen unmittelbar vor jedem Eintritt in den Schleifenkörper, ob das Kriterium Bedingung einen Wert ungleich 0 hat. Falls dem so ist, bricht die Schleife ab und die Ausführung überspringt den Schleifenkörper. While-Schleifen lassen sich jederzeit auch von innerhalb des Schleifenkörpers mittels einer Exit While-Anweisung abbrechen. While Not EOF(1) Line Input #1, a MyList.AddItem a Wend
' Zeilen aus Datei in Auflistung einlesen
Die Do...Loop- Schleife Am meisten Flexibilität bietet die Do-Schleife. Sie lässt sich als abweisende, als nicht abweisende Schleife und als Endlosschleife formulieren. In der abweisenden Form wird das Schleifenkriterium mit einem While- oder Until-Zusatz hinter das Schlüsselwort Do, in der nicht abweisenden Form hinter das Schlüsselwort Loop gesetzt. Ein While-Kriterium bricht die Schleife ab, wenn es den Wert 0 hat, während ein Until-Kriterium mit einem Wert ungleich 0 den Abbruch bedingt. Wie die anderen Schleifen lässt sich auch die Do-Schleife jederzeit von innerhalb des Schleifen-
42
Fehlerbehandlung
körpers mittels einer Exit Do-Anweisung abbrechen. Endlosschleifen sind in Wirklichkeit halbabweisende Schleifen, da sie mittels Exit Do irgendwo mitten im Schleifenkörper abgebrochen werden müssen. Do Line Input #1, a Loop While a = "" MyList.AddItem a
' Leerzeilen überlesen
' Zeile in Auflistung einfügen
Warnung
................................................... Wa rnung
Tipps
................................................... Tipps Als Faustregel lässt sich sagen, dass Schleifen in einem Programm ca. zwischen 80 und 99 Prozent der Ausführungszeit absorbieren. Es lohnt sich daher, wenn es um die Einsparung von Laufzeit geht, insbesondere innere Schleifen zu optimieren, die besonders oft durchlaufen werden. Rekursive Aufrufe von Funktionen/Prozeduren lassen sich in der Regel ohne große Schwierigkeiten mittels Schleifen in iterative Aufrufe überführen. Iterative Fassungen dienen insbesondere der Vermeidung des Fehlers 28, »Nicht genügend Stapelspeicher« und sind von der Laufzeit her meist überlegen. Endlosschleifen nicht kompilierter Programme lassen sich innerhalb der Entwicklungsumgebung mittels (Strg)+(Untbr) abbrechen. Schwieriger ist der Abbruch von endlosen Ereignisschleifen unter Windows 9x. Aber auch hierzu gibt es einen Trick, den unter Windows 9x allfälligen Reset zu vermeiden. Das Problem ist, dass der Task-Scheduler von Windows 9x der aus dem Rahmen fallenden Anwendung nach gewisser Zeit nahezu die gesamte Rechenzeit zur Verfügung stellt, weil die Nachrichtenflut die Priorität so weit hochschraubt, dass keine Hintergrundanwendung mehr Nachrichten zugestellt bekommt (noch nicht einmal der Task-Switcher). Der Trick: Man muss die Visual-Basic-Anwendung durch den impliziten Start einer Vordergrundanwendung in den Hintergrund verbannen. Für einen solchen impliziten Start reicht bereits das Einlegen einer CD (gegebenenfalls mit Autorun-Anwendung) oder der Empfang eines Faxes. Windows NT ist gegen endlose Ereignisschleifen unempfindlich, da die Ereigniswarteschlange in diesem Betriebssystem anders implementiert ist. Beispiele
................................................... Beis piele
Für die For-Anweisung vgl. das Beispiel zu »Literale und Konstanten« (S. 23). Für die Do-Anweisung vgl. das Beispiel zu »InStrRev-Funktion« (S. 79). Für die While-Anweisung vgl. die Beispiele zu »Dir-Funktion« (S. 148). Komplexer ist das Beispiel des Bubble-Sort-Algorithmus im Abschnitt »Prozeduren selbst definieren« (S. 204).
Fehlerbehandlung Sub Error(lFehlerNr As Long)
43
Kontrollstrukturen
Da Schleifen die Reaktionen eines Windows-Programms auf Benutzereingaben sehr beeinträchtigen können, empfiehlt es sich, in Schleifen mit zeitaufwändigen Berechnungen etwa jede Zehntelsekunde die Anweisung DoEvents aufzurufen. Beachten Sie jedoch, dass es zur Rekursion kommt, wenn bei der Ereignisverarbeitung die so unterbrochene Funktion/Prozedur erneut aufgerufen wird. Achtung! Endlosschleifen entstehen häufig auch ungewollt durch Ereignisse, die sich gegenseitig oder selbst auslösen. Ereignisfluten dieser Art können das System destabilisieren.
Kontrollstrukturen
Sub Err.Raise(Number As Long[, Source As String, _ Description As String, Helpfile As String, _ Helpcontext As String]) Function Error(lFehlerNr As Long) As String
Kontrollstrukturen
On Error Resume Next On Error GoTo Marke ... [On Error GoTo 0] ... Exit {Function | Sub} Marke: ... Resume [Next] Function CVErr(FehlerNummer As Long) As Error Funktion IsError(VarWert) As Boolean Beschreibung
................................................... Bes c hreibung
Die Vielfalt der möglichen Ursachen für das Fehlverhalten eines Systems steigt mit der Komplexität des Systems. Es ist nicht nur aus theoretischen Gründen unmöglich, die Logik eines Programms so zu gestalten, dass dieses vor jedem Schritt jede Fehlermöglichkeit ausschließt – schließlich könnte sich eine Fehlerbedingung im zeitlichen Verlauf auch erst unmittelbar nach der Prüfung einstellen –, auch aus praktischen Erwägungen heraus ist es unsinnig, Laufzeit für die Überprüfung selbst halbwegs wahrscheinlicher Fehlerbedingungen aufzuwenden. Anstatt umfangreicher und letztlich doch unzulänglicher Fehlerprüfungen setzt sich daher in den Programmiersprachen zunehmend das Konzept der Ausnahmebehandlung bzw. der Behandlung von Laufzeitfehlern durch. Zu einem Laufzeitfehler kommt es, wenn eine Routine ihrer Aufgabe aufgrund einer Fehlerbedingung nicht nachkommen kann und dies als Laufzeitfehler mit einer bestimmten Nummer signalisiert. Das Signal für einen Laufzeitfehler kann von einer Routine des Betriebssystems, einer Komponente oder einem sonstigen Objekt gesetzt werden (die entsprechenden Fehlernummern sind im Hilfesystem dokumentiert), aber auch von einer Visual-Basic-Routine selbst, wenn diese die Anweisung Error unter Angabe einer Fehlernummer, lFehlerNr, oder die RaiseMethode des vordefinierten Err-Objekts ausführt. Die Funktion Error liefert die zu einer Fehlernummer gehörige textuelle Fehlermeldung. Zur Behandlung eines Laufzeitfehlers – gleich welcher Natur dieser ist – lässt sich mittels einer On Error GoTo-Anweisung eine vorbereitete Fehlerbehandlungsroutine auf Funktions-/Prozedurebene installieren. Die Anweisung On Error Resume Next installiert dagegen eine standardmäßige Fehlerbehandlungsroutine für die Inline-Fehlerbehandlung, deren Strategie darin besteht, den für den Laufzeitfehler verantwortlichen Befehl zu überspringen und die Fehlerbehandlung unmittelbar im Anschluss an den Befehl durch Auswertung der Fehlernummer durchzuführen. Für die De-installation einer Fehlerbehandlungsroutine sorgt On Error GoTo 0. Ein anderes Konzept für die Signalisierung von Fehlern eröffnet die Funktion CVErr. Sie liefert einen Wert von Typ Variant mit dem Untertyp Error zurück und ermöglicht die Implementation von Funktionen/Prozeduren, die als Ergebniswert auch eine Fehlernummer zurückgeben können. Ob ein Variant-Wert den Untertyp Error hat, lässt sich mittels der IsError-Funktion in Erfahrung bringen. Aufgerufen mit einem Variant-Wert, liefert Sie den Wert True, wenn dieser eine Fehlernummer repräsentiert.
44
Fehlerbehandlung
Anwendung
................................................... Anwendung
45
Kontrollstrukturen
Die Anweisungen Error bzw. Err.Raise werden im Allgemeinen zur Simulation von Fehlern eingesetzt, um den Code für die Behandlung von Laufzeitfehlern zu testen. Darüber hinaus eignen sich die Befehle auch für die Realisierung programminterner Fehlerbehandlungsmechanismen. (Einzelheiten über den Einsatz des Fehlerobjekts für die Signalisierung von Laufzeitfehlern finden Sie im Abschnitt »Err-Objekt«, S. 277). Die Fehlernummer 0 sowie Fehlernummern größer als 65535 sind nicht zulässig, sondern führen ihrerseits zu einem Laufzeitfehler 5, »Ungültiger Prozeduraufruf oder ungültiges Argument«. Die Fehlernummern zwischen 1 und 1000 sind für das Laufzeitsystem von Visual Basic reserviert, wenn auch derzeit noch nicht alle mit einer Bedeutung versehen sind. Die anderen Fehlernummern sind frei verfügbar und lassen sich für programmspezifische Zwecke nutzen. Für die reservierten Fehlernummern von Visual Basic stehen textuelle Fehlermeldungen zur Verfügung, die sich mittels der Funktion Error – jeweils auf den letzten Fehler bezogen – abfragen lassen. Bei Einsatz des Err-Objekts lässt sich diese Fehlermeldung für jede Fehlernummer Number frei gestalten, da Error nichts weiter als den Wert der Description-Eigenschaft dieses Objekts liefert. Wenn der Vorgabetext des Systems für die Signalisierung programmspezifischer Fehler nicht angemessen ist, ist nur der Wert dieser Eigenschaft anzupassen – bzw. der Text als Wert bei Aufruf der Raise-Methode für den zweiten Parameter anzugeben. Um Laufzeitfehler abzufangen und darauf reagieren zu können, installiert man mittels On Error GoTo Marke eine Fehlerbehandlungsroutine, wobei Marke (am Zeilenanfang gefolgt von einem Doppelpunkt zu notieren) eine innerhalb derselben Routine befindliche Sprungmarke darstellt, die den Beginn des Fehlerbehandlungscodes kennzeichnet. Die für die genauere Analyse des Fehlers erforderliche Fehlernummer liefert die Eigenschaft Err.Number des vordefinierten Fehlerobjekts. Nachdem es sich dabei um die Standardeigenschaft des Objekts handelt, ergibt sich der Wert bereits durch schlichte Nennung des Objektbezeichners – was insbesondere die Kompatibilität mit älterem Basic-Code sicherstellt. Der zur Fehlernummer gehörige Fehlertext liefert die Error-Funktion oder die Eigenschaft Err.Description. Der Befehl Resume beendet die Fehlerroutine nach erfolgreicher Behandlung des Fehlers mit einem Rücksprung und bewirkt die erneute Ausführung jenes Befehls, der den Laufzeitfehler ausgelöst hatte. Falls eine Wiederholung dieses Befehls nicht in Frage kommt, lässt sich die Ausführung mit Resume Next beim nächstfolgenden Befehl wieder aufnehmen. Bei Fehlern, die sich nicht erfolgreich behandeln lassen oder für die die Fehlerroutine nicht ausgelegt ist, bleibt der Routine nur der Ausweg, ihrerseits einen Fehler auszulösen, der dann auf der Ebene des Aufrufers behandelt wird – und somit den gleichen Weg nimmt wie ein Fehler, für den keine Fehlerbehandlung auf aktueller Aufrufebene eingerichtet ist. Anders herum: Kommt während der Behandlung eines Fehlers eine Funktion/Prozedur zum Aufruf, die eine eigene Fehlerbehandlung vornimmt, ist deren Fehlerbehandlungsroutine für alle auf dieser Ebene ausgelösten Fehler zuständig. Hat die Funktion/Prozedur keine eigene Fehlerbehandlung, fällt die Fehlerbehandlung der Fehlerbehandlungsroutine des Aufrufers zu. Damit besteht die Möglichkeit, dass eine Fehlerbehandlungsroutine während der Behandlung eines Fehlers erneut zum Aufruf kommt, was von der Sache her nicht unbedingt mit Schwierigkeiten verbunden sein wird, logisch aber ein wenig verzwickt sein kann. Wenn ein Laufzeitfehler bereits durch Überspringen des jeweiligen Befehls behoben ist, kann die Fehlerbehandlungsroutine auch inline mittels On Error Resume Next installiert werden. In diesem Fall ist kein weiterer Code erforderlich. Die Zuständigkeit der jeweils aktuellen Fehlerbehandlungsroutine in einer Funktion/Prozedur erlischt mit dem Befehl On Error GoTo 0. Fehlt dieser Befehl, endet Zuständigkeit mit der Rückgabe der Kontrolle an den Aufrufer.
Kontrollstrukturen
Bei Signalisierung eines Fehlers ermittelt das Laufzeitsystem jeweils die nächste zuständige Fehlerbehandlungsroutine. Eine auf gleicher Ebene (Prozedurebene) gelegene Fehlerbehandlungsroutine kommt als Erstes zum Zuge, ansonsten wandert der Fehler in umgekehrter Aufruffolge von Aufrufer zu Aufrufer, bis sich eine zuständige Fehlerbehandlungsroutine findet oder das Laufzeitsystem schließlich eine standardmäßige Behandlung vornimmt. Warnung
Kontrollstrukturen
................................................... Wa rnung
Die Logik von Fehlerbehandlungsroutinen ist meist komplizierter als man denkt. So ist erstens davon auszugehen, dass jeder zwischen einer On Error GoTo Marke-Anweisung und einer On Error GoTo 0-Anweisung auftretende Laufzeitfehler zum Aufruf der Routine führen kann: falsche Argumente bei Prozedur-/Funktionsaufrufen ebenso wie Laufzeitfehler, die von einer aufgerufenen Prozedur oder Funktion signalisiert werden, weil sie dort keine Behandlung erfahren oder eine Behandlung nicht möglich ist. Zweitens ist aber auch davon auszugehen, dass ein Fehler immer wieder auftritt, weil er nicht korrekt behoben ist. Der unbedachte Einsatz von Resume ohne den Zusatz Next kann dann zu lästigen Endlosschleifen führen, die sich in der Entwicklungsumgebung nur noch mittels (Strg)+(Untbr) oder in der kompilierten Fassung des Programms nur noch über den Task-Manager abbrechen lassen. Ein häufiger Fehler im Zusammenhang mit Fehlerbehandlungsroutinen ergibt sich aus einer Nachlässigkeit: Die Ausführung durchläuft die Routine auch ohne Vorliegen eines Laufzeitfehlers, weil diese nicht durch den Befehl Exit Sub oder Exit Function vom gewöhnlichen Code abgesetzt ist. Tipp
................................................... Tipp
Testen Sie jede Fehlerbehandlungsroutine auf Herz und Nieren, indem Sie Testcode einsetzen, der alle Fehler – also auch solche, die die Routine nicht behandeln kann – mittels Error-Anweisungen oder Aufrufen der Methode Err.Raise simuliert. Beispiel
................................................... Beis piel
Das folgende Codebeispiel demonstriert die Fehlerbehandlung, wenn das Öffnen einer Datei mit vorangehender Abfrage des Dateinamens fehlschlägt. Da es keinen Sinn macht, die OpenAnweisung ohne erneute Eingabe des Dateinamens (hier über ein Standarddialoge-Objekt CommonDialog1 gelöst) zu wiederholen, ist der betreffende Code in einer eigenen Funktion zusammengefasst, der die Dateinummer zurückliefert. Die in der aufrufenden Routine installierte Fehlerbehandlungsroutine bietet dem Benutzer die Wahl zwischen der Wiederholung des gesamten Funktionsaufrufs oder einem Abbruch der aktuellen Routine. Der Abbruch wird seinerseits durch Auslösung eines Laufzeitfehlers bewerkstelligt. (Hinweis: Der FreeFile-Aufruf liefert nach einem Fehlschlag wieder die gleiche Dateinummer, da Open keine Reservierung der angebotenen Dateinummer vornimmt.) ... Dim Handle As Integer Dim Meldung As String ... On Error GoTo OpenFehler ' Routine installieren Handle = OpenBinary ' Funktion aufrufen On Error GoTo 0 ' Routine de-installieren ... Exit Sub ' Hier nicht weiter! OpenFehler: Meldung = "Fehler" + Str(Err) + ": " + Error(Err)
46
Fehlerbehandlung
If MsgBox(Meldung, vbRetryCancel) = vbRetry Then Resume ' Funktionsaufruf wiederholen Else ' Fehler an Aufrufer weitermelden Err.Raise(1001, "Binärdatei lässt sich nicht öffnen") End If ...
Vgl. auch das Beispiel zu »Open-Anweisung« (S. 166). Verwandte Themen
................................................... Verwa ndte Them en
Dateiorientierte Funktionen und Anweisungen (S. 126); Err-Objekt (S. 277); Standarddialoge-Steuerelement (CommonDialog) (S. 444)
47
Kontrollstrukturen
Function OpenBinary() As Integer OpenBinary = FreeFile ' freie Dateinummer ermitteln CommonDialog1.ShowOpen Open CommonDialog1.FileName For Binary As FreeFile End Function
Datentypen und ihre Operationen Wie alle Programmiersprachen, deren Wurzeln in der prozeduralen Programmierung liegen, verfügt Basic über ein Typkonzept, das im Laufe der Sprachentwicklung zunehmend reichhaltiger geworden ist. Das ursprüngliche Typsystem von Basic war geschlossen. Es gab vier numerische Datentypen mit unterschiedlichen Wertebereichen, über denen sich Ganzzahl- und Fließkommaarithmetik und ein wenig Boolsche Algebra betreiben ließ, sowie einen Zeichenfolgendatentyp mit vielen komfortablen Funktionen. QuickBasic und QBasic brachten der Sprache eine »Pascalisierung«, die sich nicht nur in einem verallgemeinerten Prozedur- und Funktionsbegriff bemerkbar machte, sondern auch in der Öffnung des Typsystems für benutzerdefinierte Datentypen. Darüber hinaus erhielt die Sprache auch einen flexiblen Datentyp, der jeden elementaren Datentyp (nicht jedoch benutzerdefinierte Datentypen) vertreten konnte und aufgrund impliziter Typumwandlungen eine gewisse Typtoleranz mit sich brachte. Seit die Sprache mit Visual Basic in den Bereich der objektorientierten Programmierung vorgedrungen ist, hat das Typsystem der Sprache in Form der in Typbibliotheken vorliegenden Objektdatentypen eine explosive Verbreiterung erfahren.
Elementare Datentypen Byte, Boolean, Integer, Long, Single, Double, Currency, Decimal, Date, Object, String, Variant True, False, Empty, Nothing, Null, Error Function IsEmpty(Var) As Boolean Function IsError(Var) As Boolean Function IsNull(Var) As Boolean Function IsNumeric(Var) As Boolean Function IsObject(Var) As Boolean Beschreibung
................................................... Bes c hreibung
Elementare Datentypen sind die Grundelemente einer Sprache für die Darstellung und Interpretation von Informationen. Die einzelnen Datentypen unterscheiden sich hinsichtlich ihrer Darstellung, ihrer Wertebereiche und der für sie definierten grundlegenden Operationen. Visual Basic kennt elementare Datentypen für die Repräsentation logischer Werte (Boolean), binärer Werte (Byte), numerischer Ganzzahlwerte (Integer und Long), numerischer Fließkommawerte (Single und Double), Datumsgaben (Date), Zeichenfolgen (String) und die Referenz auf Objekte (Object). Anwendung
................................................... Anwendung
Mit Hilfe der elementaren Datentypen lassen sich Konstanten und Variablen für die Handhabung der in Programmen anfallenden Daten deklarieren. Jeder dieser Datentypen mit Ausnahme von Variant steht für eine ganz bestimmte Art der Repräsentation und Interpretation von Information. Die folgende Tabelle gibt einen Überblick über ihre Wertebereiche.
49
Elementare Datentypen
Elementare Datentypen
Datentyp
Bytes
Beschreibung und Wertebereich
Byte
1
Kurze vorzeichenlose Ganzzahl zwischen 0 bis 255
Boolean
2
Logischer Wert, True oder False
Integer
2
Ganzzahl zwischen –32.768 bis 32.767
Long
4
Lange Ganzzahl zwischen –2.147.483.648 und 2.147.483.647
Single
4
Gleitkommazahl einfacher Genauigkeit von –3,402823E38 bis –1,401298E–45 für negative Werte und von 1,401298E-45 bis 3,402823E38 für positive Werte
Double
8
Gleitkommazahl mit doppelter Genauigkeit: –1,79769313486232E308 bis –4,94065645841247E-324 für negative Werte und 4,94065645841247E–324 bis 1,79769313486232E308 für positive Werte
Currency
8
Ganzzahl mit Skalierung in 10.000stel zwischen –922.337.203.685.477,5808 und 922.337.203.685.477,5807
Decimal
14
29stellige Dezimalzahl ohne Exponent, als Ganzzahl zwischen ±79.228.162.514.264.337.593.543.950.335 mit Abstand 1, als Zahl mit 28 Nachkommastellen zwischen ±7,9228162514264337593543950335 mit Abstand ±0,0000000000000000000000000001
Date
8
Datum zwischen 1. Januar 100 und 31. Dezember 9999
Object
4
Referenz auf ein Objekt, eine gültige Adresse oder Nothing
String
10 + Länge Zeichenfolge mit variabler Länge. 0 bis theoretisch 231-1 Zeichen. In der Praxis ist je nach Speicherausbau des Systems aber bereits bei einer Länge von ca. 100 Millionen Zeichen Schluss, wegen »Überlauf des Zeichenfolgenspeichers»
String *
Länge
Zeichenfolge mit fester Länge zwischen 1 und 65.535 (216-1)
Variant
16
Standardtyp, bei numerischem Untertyp wie der Datentyp Double
Variant
22 + Länge Standardtyp, bei Zeichenfolge als Untertyp, wie Datentyp String mit variabler Länge
Die elementaren Datentypen von Visual Basic
Der Datentyp Variant Der Typ Variant bildet eine Hülle für alle anderen Datentypen und kann alle durch elementare Datentypen darstellbaren Werte repräsentieren (außer Zeichenfolgen fester Länge) sowie die Werte Empty, Nothing, Null und Error. Variablen vom Typ Variant müssen nicht eigens deklariert werden, da Visual Basic diesen Datentyp als Standardtyp für alle nicht explizit oder via Typkennzeichen deklarierten Variablen einsetzt. Es ist aber kein Fehler (ja sogar guter Programmierstil), Variablen vom Typ Variant explizit zu deklarieren. In diesem Fall erhält die Variable Empty als Initialisierungswert. Man kann einer Variant-Variablen auch den Wert Null zuweisen, was üblicherweise signalisiert, dass die Variable absichtlich keinen gültigen Wert enthält. Bei Zuweisung eines typgebundenen Werts speichert die Variable nicht nur den Wert an sich, sondern auch seinen Typ. Man spricht in diesem Zusammenhang vom Untertyp eines
50
Die Datentypen Integer,
Long,
Single und Double
Die Datentypen Integer, Long, Single und Double Die Datentypen Integer und Long setzt man üblicherweise für Zählvariablen, Indizierungen oder Berechnungen im Bereich der Ganzzahlarithmetik ein. Sie unterscheiden sich von einander allein durch ihren Wertebereich. Single und Double eignen sich dagegen speziell für die Darstellung gebrochen-rationaler Werte (lies: Dezimalzahlen mit Nachkommaanteil). Die zugehörige Gleitkommaarithmetik entspricht der üblichen Dezimalrechnung mit Rundung auf eine feste Anzahl von Dezimalstellen. Das bedingt, dass benachbarte Werte in den jeweiligen Wertebereichen keinen festen Abstand zueinander aufweisen (der bei den Datentypen Integer und Long ja 1 ist), sondern ihren Abstand mit wachsendem Betrag vergrößern (bzw. gegen 0 hin verkleinern). Double weist gegenüber Single die doppelte Genauigkeit sowie einen erheblich größeren Wertebereich auf. Visual Basic initialisiert diese Datentypen mit dem Wert 0 bzw. 0,0. ' Vollständige Deklaration Const cDelta As Double = 2.71364234E-123 Dim iGanzzahl As Integer Dim lGanzzahl As Long Dim sFlKommazahl As Single Dim dFlKommazahl As Double ' Deklaration mit Typkennzeichen Const ci# = 1.2 Dim i% Dim l&
51
Elementare Datentypen
Variant-Werts. Um die Sonderwerte abzufragen, lassen sich die eigens dafür definierten Boolean-Funktionen IsEmpty, IsError und IsNull verwenden – ein expliziter Vergleich mit den Werten Empty, Nothing, Null und Error tut es natürlich auch. Bei der Operation mit typgebundenen Werten kann es passieren, dass der Wertebereich des aktuellen Untertyps einer Variant-Variablen für die Darstellung eines Wert nicht mehr ausreicht. In diesem Fall nimmt Visual Basic eine implizite Typumwandlung im Sinne einer Wertebereichserweiterung vor. Implizite Typumwandlungen finden auch statt, wenn ein VariantWert mit numerischem Untertyp einer String-Variable oder ein Variant-Wert mit Untertyp String einer Variable mit numerischem Typ zugewiesen werden soll. Im ersten Fall wandelt Visual Basic den Variant-Wert in eine adäquate Zeichendarstellung um und im zweiten Fall in eine adäquate numerische Darstellung. Der Sonderwert Error in einer Variablen vom Datentyp Variant ist dafür vorgesehen, in Prozeduren erkannte Fehlerbedingungen anzuzeigen, wenn keine On Error-Fehlerbehandlung durch die Anwendung stattfindet. In diesem Fall muss die rufende Prozedur selbst den Fehler auswerten und nötige Maßnahmen ergreifen. Gesetzt wird der Sonderwert gewöhnlich mittels der Funktion CVErr, die eine Fehlernummer entgegennimmt und einen entsprechenden Error-Wert vom Typ Variant liefert. Trägt ein Variant-Wert den Untertyp Object, drückt der Wert Nothing aus, dass auf kein Objekt referiert wird. Um in Erfahrung zu bringen, ob ein Variant-Wert den Untertyp Object trägt, lässt sich die Boolean-Funktion IsObject verwenden. Nach all dem dürfte offensichtlich sein, welch enorme Flexibilität der Datentyp Variant mit sich bringt und wie sehr er die Programmierung vereinfachen kann, indem er die Typenvielfalt auf einen einzigen Datentyp zurückführt. Allerdings verhält es sich bei der Programmierung mit diesem Datentyp anders als im richtigen Leben: Hier sollten die einfachen Dinge eher den Profis vorbehalten bleiben. Ungeübten Programmierern mit einem noch nicht vollständig ausgeprägten Verständnis für die elementaren Datentypen spielt das verborgene Wirken von Visual Basic gerne mal den einen oder anderen Streich.
Elementare Datentypen
Dim s! Dim d# For n% = 1 to 100
' Deklaration mit Typkennzeichen durch Nennung
Elementare Datentypen
Die Datentypen Boolean und Byte Der mit Visual Basic neu zu Basic hinzugekommene Datentyp Boolean weist einen sehr kleinen Wertebereich auf, nämlich nur die als Konstanten vordefinierten Wahrheitswerte True und False. In Basic entspricht der Wahrheitswert True traditionell dem Wert -1 und False dem Wert 0. Bei Typumwandlungen in den Datentyp Boolean wird jeder Wert ungleich 0 als True interpretiert und nur der Wert 0 als False. Boolean leistet vornehmlich in Kontrollstrukturen für die Programmsteuerung sowie bei der Berechnung logischer Funktionen auf Grundlage der Booleschen Algebra gute Dienste. Visual Basic initialisiert Variablen vom Typ Boolean mit False. Dim bLogValue As Boolean bLogValue = True ... If bLogValue Then ... End If
Gleichfalls ein neues Mitglied im Club ist der Datentyp Byte. Er dient insbesondere dem Umgang mit binären Inhalten, denen keine feste Interpretation zukommt. (Traditionell wurden für Inhalte dieser Art Zeichenfolgen benutzt.) Dieser Datentyp wird zwar noch als numerischer Datentyp gehandelt, das heißt, er wird mit 0 initialisiert, und man kann mit ihm ganz normal rechnen, solange der Wertebereich nicht überschritten wird. Er ist aber als einziger vorzeichenlos. Dim MyByte(256) As Byte ... For i = 0 to 255 Print #1, MyByte(i); Next i
Die Datentypen Currency und Date Der eng mit den ganzzahligen Typen Integer und Long verwandte Datentyp Currency wurde speziell zur Erleichterung finanzmathematischer Berechnungen mit in die elementaren Datentypen aufgenommen. Die Werte in seinem Wertebereich sind ein ganzzahliges Vielfaches von 0,0001. Damit stellt der Datentyp noch vier Stellen hinter dem Komma dar und seine Arithmetik entspricht der üblichen Dezimalrechnung mit Kaufmannsrundung an der vierten Stelle hinter dem Komma. Visual Basic initialisiert Currency-Variablen mit 0,0000. Interessanter ist der gleichfalls neue Datentyp Date, der nicht zuletzt im Schatten des Jahr-2000Problems eine ebenso zweifelhafte wie steile Karriere hinter sich hat. Visual Basic repräsentiert Werte dieses Typs als 64-Bit-Gleitkommazahlen (8 Bytes) nach IEEE und kann somit Datumsangaben im Bereich zwischen dem 1. Januar 100 und dem 31. Dezember 9999 sowie Uhrzeiten im Bereich von 0:00:00 bis 23:59:59 unterscheiden. Sie können mit diesem Datentyp Datumsangaben und Zeitangaben in Kombination, aber auch einzeln als Werte darstellen. Der Notation literaler Datums- und Zeitangaben liegt das amerikanische Format zugrunde, das heißt, in Datumsangaben wird der Monat vor dem Tag notiert und die Abtrennung erfolgt durch das Zeichen »/«, während Zeitangaben in 12-Stunden-Darstellung gefolgt von »PM« oder »AM« zu treffen sind. Zudem ist die Darstellung durch ein führendes und abschließendes Zeichen »#« einzuschließen. (Der Editor erkennt übrigens auch andere Darstellungen, so etwa #1 Feb 99#, und korrigiert diese beflissen in das erwünschte Format.)
52
Der Datentyp Decimal
Dim a = b = c =
a As Date, b As Date, c As Date #2/1/1996 1:12:01 PM# #12:01:00 PM# #2/1/1996#
Der Datentyp Decimal Der gleichfalls neue Datentyp Decimal ist kein eigenständiger Datentyp, sondern kann nur als Untertyp eines Variant-Werts auftreten. Um einen Wert dieses Typs zu erzeugen, bedarf es der Typumwandlungsfunktion CDec. Dim vDez1 vDez1 = CDec(100.00000021)
' aus Double erzeugt
Werte vom Typ Decimal haben eine 29-stellige dezimale Repräsentation mit Komma ohne Exponent im BCD-Code. Den Typen Single und Double liegt dagegen eine binäre Repräsentation zugrunde, die nicht mehr als 8 bzw. 15 dezimale Stellen erfasst. Um Decimal-Werte mit mehr als 15 Stellen (mehr erlaubt der Datentyp Double nicht) noch direkt definieren zu können, ist der Funktion CDec eine entsprechende Zeichenfolgendarstellung des Werts zu übergeben. Beachten Sie, dass dabei die auf dem jeweiligen System geltenden Ländereinstellungen eine Rolle spielen. So legt die Einstellung Deutsch (Standard) als Dezimalzeichen ein Komma fest. Dim vDez2 vDez2 = CDec("100,000000000000000021")
' aus String erzeugt
Warnung
................................................... Wa rnung
Visual Basic vereinbart jede Variable standardmäßig als Variant, wenn sie nicht explizit im Rahmen einer Variablendeklaration oder implizit durch eine DefType-Anweisung bzw. durch eines der traditionellen Typkennzeichen von Basic ($, %, &, !, #) eine feste Typzuweisung erfährt. Tipp
................................................... Tipp
Beobachten Sie bei der Fehlersuche in Programmen besonders Werte vom Typ Variant, insbesondere wenn Seltsames und Absurdes mit im Spiel zu sein scheint. Entlaufene Variant-Werte waren schon Ursache so manchen Übels. Vereinbaren Sie Variablen besser explizit, um einen bestimmten Datentypen zu erzwingen, das verbessert die Übersicht. Wenn Sie das Tippen länglicher Typdeklarationen scheuen, verwenden Sie doch DefType oder die Typkennzeichen ($, %, &, !, #). Oft entlarvt eine Option Explicit-Anweisung den Übeltäter bereits. Verwandte Befehle
................................................... Verwa ndte Befehle
Dim, Public, Private, Function, DefType Verwandte Themen
................................................... Verwandte Them en
Literale und Konstanten (S. 27); Typumwandlung (S. 57); Typkennzeichen und Bezeichnerbereiche für Typen (S. 167); Variablendeklaration (S. 162)
53
Elementare Datentypen
Beim Umwandeln anderer numerischer Datentypen in Werte des Datentyps Date repräsentieren die Vorkommastellen das Datum und die Nachkommastellen die Uhrzeit. Mitternacht entspricht dem Wert 0,0 und 12 Uhr Mittags dem Wert 0,5. Negative ganze Zahlen repräsentieren ein Datum vor dem 30. Dezember 1899, positive ein Datum nach dem 30. Dezember 1899. Den 30. Dezember 1899 selbst kennt Visual Basic dagegen überhaupt nicht, sondern interpretiert ihn als #12:00:00 AM#.
Operatoren für elementare Datentypen und logische Bedingungen
Operatoren für elementare Datentypen und logische Bedingungen +, –, *, /, \, MOD, ^, Not, And, Or, Xor, Eqv, Imp, <, >, =, >=, <=, <>, Is, TypeOf Beschreibung
Operatoren für elementare Datentypen und logische Bedingungen
................................................... Bes c hreibung
Operatoren dienen der Bildung von Ausdrücken. Die Menge der Operatoren, die Visual Basic für die elementaren Datentypen kennt, unterteilt sich in die Gruppe der logischen Operatoren (And, Or, Xor, Eqv, Imp, Not) – die in Visual Basic aufgrund der besonderen Wahl der Repräsentation der Wahrheitswerte mit den bitweisen Operatoren übereinstimmen –, die Gruppe der arithmetischen Operatoren (+, –, *, /. \, Mod, ^), die Gruppe der Vergleichsoperatoren (<, >, =, <=, >=, <>, Is) und die Gruppe der Zeichenfolgenoperatoren (die derzeit nur die Verkettungsoperatoren »+« und »&« umfasst). Eine Art Doppelrolle spielt traditionell der Zuweisungsoperator »=«, der für alle elementaren Datentypen definiert ist und gegebenenfalls auch für die implizite Typumwandlung zwischen verwandten Typen sorgt. Er darf nicht mit dem gleich notierten Vergleichsoperator verwechselt werden. Ein Vergleichsoperator der besonderen Art ist der Is-Operator für den Vergleich zweier Objektvariablen. Er liefert den Wert True, wenn die beiden Operanden auf dasselbe Objekt verweisen, ansonsten False. Er kommt insbesondere häufig im Zusammenhang mit dem TypeOf-Schlüsselwort zur Anwendung, wenn abgefragt werden soll, ob eine Objektvariable auf einen bestimmten Objekttyp verweist. If TypeOf MeineVar Is Form1 Anwendung
................................................... Anwendung Operator
Notation
Operation
Operanden
+ &
a + b a & b
Verkettung
Zeichenfolgen
+
a + b
Summenbildung
Numerische Werte
-
a – b
Differenzbildung
Numerische Werte
-
-a
Vorzeichenumkehr
Numerische Werte
*
a * b
Multiplikation
Numerische Werte
/
a / b
Division
Numerische Werte
\
a \ b
Division mit ganzzahligem Ergebnis
Numerische Werte
Mod
a Mod b
Restwertdivision (Modulo-Operation)
Numerische Werte
^
a ^ b
Potenz
Numerische Werte
And
a And b
Logisches und bitweises UND
Numerische Werte, logische Werte
Or
a Or b
Logisches und bitweises ODER Numerische Werte, logische Werte
Not
Not a
Logische und bitweise Negation
Operationen der elementaren Datentypen
54
Numerische Werte, logische Werte
Arrays
Notation
Operation
Operanden
Xor
a Xor b
Logisches und bitweises Exklu- Numerische Werte, logische Werte siv-ODER
Eqv
a Eqv b
Logische und bitweise Äquiva- Numerische Werte, logische Werte lenz
Imp
a Imp b
Logische und bitweise Implika- Numerische Werte, logische Werte tion
=
a = b
Logische Bedingung: »Gleichheit« (vgl. auch Eqv)
Numerische Werte, logische Werte, Zeichenfolgen, Datums-/Zeitwerte
<
a < b
Logische Bedingung: »kleiner als« (bei Zeichenfolgen von Option Compare abhängig)
Numerische Werte, logische Werte, Zeichenfolgen, Datums-/Zeitwerte
>
a > b
Logische Bedingung: »größer als« (bei Zeichenfolgen von Option Compare abhängig)
Numerische Werte, logische Werte, Zeichenfolgen, Datums-/Zeitwerte
<=
a <= b
Logische Bedingung: »kleiner Numerische Werte, logische Werte, gleich« (bei Zeichenfolgen: von Zeichenfolgen, Datums-/Zeitwerte Option Compare abhängig)
>=
a >= b
Logische Bedingung: »größer Numerische Werte, logische Werte, gleich« (bei Zeichenfolgen: von Zeichenfolgen, Datums-/Zeitwerte Option Compare abhängig)
<>
a <> b
Logische Bedingung: »ungleich«
Numerische Werte, logische Werte, Zeichenfolgen, Datums-/Zeitwerte
Is TypeOf
a Is b TypeOf a Is b
Testet, ob Operanden auf gleiches Objekt verweisen bzw. gleichen Objekttyp tragen
Objektvariablen
Operationen der elementaren Datentypen
Arrays Dim StatArray({iDimension1 | Untergrenze1 To Obergrenze1}[, _ {Dimension2 | Untergrenze2 To Obergrenze2}[, _ ... {DimensionN | Untergrenze2 To Obergrenze2}]]) _ [As Typ] Dim DynArray() [As Typ] ReDim [Preserve] DynArray [As Typ] Function Array([Wert1[, Wert2[, ... WertN]]]) As Variant Function UBound(Array[, iDimension As Integer = 1]) As Long Function LBound(Array[, iDimension As Integer = 1]) As Long Function IsArray(Var) As Boolean Option Base {0 | 1}
55
Arrays
Operator
Arrays
Beschreibung
Arrays
................................................... Bes c hreibung
Ein Array stellt eine ein- oder mehrdimensionale Struktur dar, die eine Menge von Werten gleichen Typs unter einem gemeinsamen Variablenbezeichner zusammenfasst. Visual Basic unterscheidet zwischen statischen und dynamischen Arrays. Die (endgültige) Deklaration statischer Arrays erfolgt im Rahmen einer Dim-Anweisung. Für die Deklaration sind neben dem Array-Bezeichner StatArray auch ein oder mehrere Indexbereiche anzugeben – je nachdem, wie viele Dimensionen das Array besitzt – sowie optional ein Elementtyp Typ. Wird ein Indexbereich durch Nennung einer Obergrenze iDimensionN angegeben, beginnt die Zählung der Array-Elemente standardmäßig bei 0 (Option Base 0 ist die Voreinstellung von Visual Basic), es sei denn, das betreffende Modul enthält die Anweisung Option Base 1. Es lassen sich aber auch Indexbereiche mit beliebigen Grenzen durch explizite Nennung unterer und oberer Grenzen (UntergrenzeN To ObergrenzeN) vereinbaren. Die formale Deklaration eines dynamischen Arrays kann durch Vereinbarung einer Array-Variablen im Rahmen einer Dim-Anweisung geschehen, muss aber nicht. Dabei notiert man den Bezeichner DynArray gefolgt von einem leeren Klammerpaar und vereinbart optional einen Elementdatentyp. Die konkrete Definition kann dann an passender Stelle vermittels der ReDimAnweisung unter Angabe von Dimensionen und Array-Grenzen geschehen – auch mehrmals mit unterschiedlichen Grenzen und Dimensionen, aber gleichem Datentyp. ReDim eignet sich auch zur Vergrößerung eines bereits definierten dynamischen Arrays unter Beibehaltung der Dimensionen und Elementwerte. In diesem Fall muss das Schlüsselwort Preserve angegeben werden. Der Zugriff auf den Wert eines Array-Elements erfolgt durch Angabe des Array-Bezeichners gefolgt von einem Index oder einer Indexliste (bei mehrdimensionalen) Arrays in Klammern). Anwendung
................................................... Anwendung
Arrays eignen sich hervorragend zur Verwaltung großer Mengen von gleichartigen, aber auch ungleichartigen (wenn als Datentyp Variant vereinbart wird) Daten, auf die ein effizienter indexsequenzieller Zugriff erfolgen soll. Seien es Matrixelemente bei der numerischen Vektorrechnung, Messwerte, Adressenstämme, Datensätze oder auch Ansammlungen gleichartiger Steuerelemente, die Array-Struktur bietet sich hervorragend für die einfache und schnelle Handhabung von variablen Werten an. Als Elementtyp für Arrays sind alle elementaren und komplexen Datentypen erlaubt, insbesondere auch benutzerdefinierte Datentypen, Objekte und der Typ Variant. Da ein Wert vom Typ Variant seinerseits ein Array sein kann, sind also auch implizit verschachtelte Arrays mit individueller Tiefe je Element möglich. Eine Array-Variable lässt sich wie eine gewöhnliche Variable verwenden – sie erhält ihren »Wert« durch Zuweisung oder durch (partielle) Initialisierung im Zuge einer ReDim-Anweisung. Im Gegensatz zu anderen Programmiersprachen (vgl. C/C++) beschränkt sich Visual Basic bei der Zuweisung eines Arrays an eine Array-Variable nicht auf eine reine Adresszuweisung, sondern erstellt eine physikalische Kopie des gesamten Arrays, wie folgender Code beweist: Dim a(0), b() a(0) = 10 b = a a(0) = 11 Print b(0), a(0)
' Ausgabe: 10
11
Diese Aussage ist allerdings mit Vorsicht zu genießen, sie gilt nämlich nur, wenn die Elemente einfache Datentypen tragen. Von Objekten fertigt Visual Basic keine Kopien an. Das liegt daran, dass der Wert einer Objektvariablen einen Verweis auf das tatsächliche Objekt darstellt und die einfache Zuweisungsoperation (im Gegensatz zur Set-Anweisung) sich auf eine Kopie des Verweises beschränkt, wie folgender Code zeigt:
56
Typumwandlung
Dim a(0) As Object, b() As Object Set a(0) = Me ' Formular wird zugewiesen b = a b(0).Caption = "Dieser " a(0).Caption = "oder jener?" Print b(0).Caption ' Ausgabe: "oder jener?"
Array- Funktionen
Beispiel
................................................... Beis piel
Dim StatArray(-10 To 10, -10 To 10) As Integer ' statisch, zweidim. Dim DynArray() As String ' dynam. deklarieren ... Redim DynArray(10) As String ' dynam. definieren ... Redim Preserve DynArray(UBound(DynArray) + 1) As String 'erweitern Verwandte Themen
................................................... Verwandte Them en
Auflistungen und Collection-Objekte (S. 304); Get-Anweisung (S. 140); Elementare Datentypen (S. 49)
Typumwandlung Function CBool(Ausdruck) As Boolean Function CByte(Ausdruck) As Byte Function CCur(Ausdruck) As Currency Function CDate(Ausdruck) As Date Function CDbl(Ausdruck) As Double Function CDec(Ausdruck) As Decimal Function CInt(Ausdruck) As Integer
57
Typumwandlung
Visual Basic unterstützt Array-Konstanten ebenso wenig wie literale Arrays. Während bei statischen Arrays die Wertzuweisung elementweise erfolgen muss, kann im Falle von dynamischen Arrays eine Zuweisung auch auf Array-Ebene erfolgen. Das macht ein kompakte Initialisierung mittels der Array-Funktion möglich, die ihre als Aufrufparameter übergebene Werteliste als Array zurückliefert. Um die obere bzw. untere Indexgrenze eines Arrays in Erfahrung zu bringen, gibt es die Funktionen UBound bzw. LBound. Falls das Array mehr als eine Dimension besitzt, lässt sich die Dimension als Wert iDimension spezifizieren (die Zählung beginnt bei 1). Variablen des Typs Variant können bekanntlich Werte verschiedenster Datentypen annehmen, darunter auch Arrays. Die Funktion IsArray liefert eine Aussage in Form eines Wahrheitswerts darüber, ob es sich bei einer Variablen um eine Array-Variable handelt oder nicht. Leider kennt Visual Basic keine Funktion, die den kompletten Speicherbedarf für das Speichern eines Arrays ermittelt. Dieser Wert muss vielmehr aus dem Elementdatentyp, der Dimensionenzahl, der Dimensionsgrenzen und gegebenenfalls mitzuspeichernder Deskriptoren »zu Fuß« errechnet werden (vgl. »Get-Anweisung«, S. 140), da Arrays in den unterschiedlichen Dateimodi unterschiedlich repräsentiert werden.
Typumwandlung
Function CLng(Ausdruck) As Long Function CSng(Ausdruck) As Single Function CVar(Ausdruck) As Variant Function CVDate(Ausdruck) As Variant Function CStr(Ausdruck) As String Function IsDate(Ausdruck) As Variant
Typumwandlung
Function IsNumeric (Ausdruck) As Boolean Beschreibung
................................................... Bes c hreibung
Die explizite Typumwandlung wird bei Visual Basic zwar nicht so groß geschrieben wie bei manch anderer Programmiersprache, da der Visual Basic Compiler von sich aus implizite Typanpassungen nach Bedarf vornimmt, vermeidbar ist sie aber nicht in allen Fällen. Die folgende Tabelle gibt einen Überblick über die auf die einzelnen Datentypen zugeschnittenen Konvertierungs- und Typumwandlungsfunktionen. Funktion
Beschreibung
CBool
Konvertiert Ausdruck nach Möglichkeit (bei Zeichenfolgen ohne Unterscheidung der Großschreibung) in einen Wert vom Typ Boolean; Interpretiert die Zeichenfolgen "Wahr", "True" sowie numerische Werte ungleich 0 als True und die Zeichenfolgen "Falsch", "False" sowie den Wert 0 als False. Bei anderen Zeichenfolgen liefert die Funktion einen Laufzeitfehler.
CByte
Liefert Ausdruck als Wert vom Typ Byte. Fließkommawerte mit einem Nachkommaanteil von exakt 0,5 werden zur nächsten geraden Zahl hin gerundet (0,5 wird zu 0 und 1,5 zu 2), ansonsten gilt die Kaufmannsrundung. Liegt der Wert von Ausdruck nicht im Bereich zwischen 0 und 255 oder ist Ausdruck von Typ String, löst die Funktion einen Laufzeitfehler aus.
CCur
Liefert Ausdruck als Wert vom Typ Currency. Rundet numerische Werte auf vier Stellen hinter dem Komma und interpretiert Zeichenfolgen im gebietsspezifischen Währungsformat. Für das Gebietsschema »Deutsch (Standard)« gilt: Komma ist Dezimalzeichen, Punkt ist Tausendertrennzeichen, »DM« als führendes oder nachgestelltes Währungssymbol ist erlaubt, sonstige nicht zur Notation von Zahlenliteralen gehörende Zeichen führen zu einem Laufzeitfehler. Der zulässige Wertebereich für Ausdruck geht von -922.337.203.685.477,5808 bis 922.337.203.685.477,5807.
CDate
Liefert Ausdruck als Wert vom Typ Date. Interpretiert numerische Werte und Zeichenfolgen als Zeit-/Datumswert. Bei numerischen Werten ist ein Vorkommaanteil zwischen -657.434 und 2.958.465 zulässig – er wird als Tage-Offset bezogen auf das Datum 1.1.1899 interpretiert – und ein Nachkommaanteil von nicht mehr als sechs Stellen – er wird als Sekunden-Offset bezogen auf das Datum interpretiert. Bei der Interpretation von Zeichenfolgen erkennt die Funktion das kurze, mittlere oder lange gebietsspezifische Datumsformat (das lange Datumsformat jedoch nur, wenn kein Wochentag notiert ist). Die Funktion IsDate erlaubt eine Vorabprüfung, ob ein gegebener Wert in einen Zeit-/Datumswert wandelbar ist.
Konvertierungs- und Typumwandlungsfunktionen
58
Typumwandlung
Beschreibung
CDbl
Liefert Ausdruck als Wert vom Typ Double. Bei der Konvertierung aus Decimal und Currency werden nicht darstellbare Stellen gegebenenfalls gerundet, alle anderen numerischen Datentypen lassen sich ohne Genauigkeitsverlust überführen. Bei der Konvertierung von Zeichenfolgen erwartet die Funktion das gebietsspezifische Zahlenformat. Für das Gebietsschema »Deutsch (Standard)« gilt: Komma ist Dezimalzeichen, Punkt ist Tausendertrennzeichen. Der Wertebereich geht von -1,79769313486232E308 bis -4,94065645841247E-324 für negative Werte und von 4,94065645841247E-324 bis 1,79769313486232E308 für positive Werte.
CDec
Liefert Ausdruck als Wert vom Typ Decimal, der jedoch (ohne erneute Typumwandlung) nur Variablen des Typs Variant zugewiesen werden kann. Bei der Konvertierung von Zeichenfolgen erwartet die Funktion das gebietsspezifische Zahlenformat. Für das Gebietsschema »Deutsch (Standard)« gilt: Komma ist Dezimalzeichen, Punkt ist Tausendertrennzeichen. Der Wert selbst darf im Bereich ±79.228.162.514.264.337.593.543.950.335 liegen, wobei das Komma fließen darf.
CInt
Liefert Ausdruck als Ganzzahlwert vom Typ Integer. Fließkommawerte mit einem Nachkommaanteil von exakt 0,5 werden zur nächsten geraden Zahl hin gerundet (0,5 wird zu 0 und 1,5 zu 2) ansonsten gilt die Kaufmannsrundung. Der Wertebereich geht von -32.768 bis 32.767.
CLng
Liefert Ausdruck als Ganzzahlwert vom Typ Long. Fließkommawerte mit einem Nachkommaanteil von exakt 0,5 werden zur nächsten geraden Zahl hin gerundet (0,5 wird zu 0 und 1,5 zu 2), ansonsten gilt die Kaufmannsrundung. Der Wertebereich geht von -2.147.483.648 bis 2.147.483.647.
CSng
Liefert Ausdruck als Wert vom Typ Single. Bei der Konvertierung aus Double, Decimal und Currency werden nicht darstellbare Stellen gegebenenfalls gerundet, alle anderen numerischen Datentypen lassen sich ohne Genauigkeitsverlust überführen. Bei der Konvertierung von Zeichenfolgen erwartet die Funktion das gebietsspezifische Zahlenformat. Für das Gebietsschema »Deutsch (Standard)« gilt: Komma ist Dezimalzeichen, Punkt ist Tausendertrennzeichen. Der Wertebereich geht von -1,79769313486232E308 bis -4,94065645841247E-324 für negative Werte und von 4,94065645841247E-324 bis 1,79769313486232E308 für positive Werte.
CVar
Konvertiert numerische Werte in den Untertyp Double (vgl. CDbl) und alle anderen in den Untertyp String
CVDate
Konvertiert einen numerischen Wert oder eine Zeichenfolge Ausdruck in das von älteren Visual-Basic-Versionen benutzte Datumsformat Variant mit Untertyp Date. Da Date inzwischen als elementarer Datentyp verfügbar ist, dient diese Funktion nur noch dem Erhalt der Kompatibilität mit älteren Programmen. Dieselbe Funktionalität ergibt sich auch implizit, wenn der Funktionswert eines CDate-Aufrufs einer Variablen vom Typ Variant zugewiesen wird. Umgekehrt lässt sich aufgrund der impliziten Typanpassung des Compilers auch der Funktionswert eines CVDate-Aufrufs einer Variablen vom Typ Date zuweisen, so dass zwischen CDate und CVDate praktisch gesehen kein Unterschied besteht.
Konvertierungs- und Typumwandlungsfunktionen
59
Typumwandlung
Funktion
Benutzerdefinierte Datentypen
Benutzerdefinierte Datentypen
Funktion
Beschreibung
CStr
Liefert literale Darstellung von Ausdruck als Zeichenfolge im gebietsspezifischen Format. Wahrheitswerte erscheinen als "Wahr" und "Falsch", numerische Werte ohne Tausendertrennzeichen jedoch mit Komma als Dezimalzeichen und erforderlichenfalls in Fließkommanotation mit Exponentenanteil. Der Wert Empty entspricht der leeren Zeichenfolge, während Null einen Laufzeitfehler auslöst. Datumswerte erhalten das für das Gebietsschema geltende kurze Datumsformat. Die literale Darstellung eines Objekts ist die seiner Standardeigenschaft – so liefert CStr(Err) die zuletzt gesetzte Fehlernummer Err.Number.
Konvertierungs- und Typumwandlungsfunktionen Anwendung
................................................... Anwendung
Wer viel mit dem flexiblen Datentyp Variant arbeitet oder von einer streng typisierenden Programmiersprache her kommt, wird in so manchem Fall die Typumwandlungsfunktionen von Visual Basic zu schätzen wissen. Obwohl der Visual-Basic-Compiler weitgehend tolerant ist, was die implizite Typanpassung bei der Zuweisung, der Parameterübergabe und der Ausdrucksberechnung betrifft, lässt sich die Flexibilität im Miteinander von Datentypen durch explizite Typumwandlungen noch weiter verbessern, insbesondere wenn die länderspezifische Darstellung entlang des in der Systemsteuerung eingestellten Gebietsschemas bei der Umwandlung aus Zeichenfolgen ins Spiel kommt. So ist beispielsweise die explizite Typumwandlung in Visual Basic 6.0 der einzige Weg, um Werte des nur als Untertyp von Variant existierenden Datentyps Decimal zu generieren, denn eine Deklaration mit diesem vergleichsweise neuen Datentyp ist in der Version 6.0 noch nicht möglich. Auch greift der Compiler nur dann zum Mittel der impliziten Typumwandlung, wenn eine Verengung oder Erweiterung des gegebenen Typs unvermeidbar ist. Tipps
................................................... Tipps Um festzustellen, ob ein Wert als Zahl ausgewertet werden kann, steht die Boolean-Funktion IsNumeric zur Verfügung. Die Typumwandlung von einem benutzerdefinierten Typ in einen elementaren Typ ist nicht so ohne Weiteres möglich. Einen Schleichweg eröffnet jedoch die LSet-Anweisung (vgl. S. 80). Verwandte Befehle
................................................... Verwa ndte Befehle
Asc, Chr, ChrB, Date, DateSerial, Fix, Format, FormatCurrency, FormatDateTime, FormatNumber, FormatPercent, Int, Round, Str, StrConv Time, TimeSerial, Val Verwandte Themen
................................................... Verwandte Them en
Elementare Datentypen (S. 49)
Benutzerdefinierte Datentypen Visual Basic kennt drei verschiedene Arten von benutzerdefinierten Datentypen: in TypeKonstrukten vereinbarte Datenstrukturen; in Enum-Konstrukten vereinbarte Aufzählungsdatentypen; in Klassenmodulen definierte Klassen (Objektdatentypen). Letztere werden ausführlicher im objektorientierten Teil vorgestellt, wenn es um »Selbst definierte Klassen« (S. 318) geht. Ein Vergleich zwischen Type-Datentypen und Objektdatentypen findet sich in der Tabelle auf Seite 197.
60
Type- Datentypen
Type- Datentypen {Private | [Public]} Type BDT ElementName [([Indizes])] As Typ [ElementName1 [([Indizes])] As Typ] ... End Type With bdtVar .ElementName = Wert End With
................................................... Bes c hreibung
Die Type-Struktur ermöglicht die Definition eines eigenen Datentyps BDT, der aus Elementen ElementName bereits bekannter Datentypen zusammengesetzt ist. Die Private-Vereinbarung des Datentyps ist für beliebige Modularten auf Modulebene möglich und beschränkt den Geltungsbereich der Definition auf das jeweilige Modul. Fehlt der Spezifizierer Private, behandelt der Compiler die Definition als Public-Vereinbarung, die jedoch nur in einem Standardmodul zulässig ist. Ein Public-Datentyp ist in allen Modulen des gegebenen Projekts sowie in allen Projekten einer gegebenenfalls existierenden Projektgruppierung bekannt. Anwendung
................................................... Anwendung
Im Gegensatz zu älteren Basic-Versionen sind in Visual Basic statische und dynamische Arrays als Feldelemente für benutzerdefinierte Datentypen ebenso zulässig wie Objektvariablen. Auch einer Verschachtelung benutzerdefinierter Datentypen durch Verwendung bereits definierter Datentypen als Feldtyp steht nichts im Wege. Die Vereinbarung eines benutzerdefinierten Datentyps BDT für eine Variable weist formal keine Besonderheiten gegenüber der Vereinbarung eines elementaren Datentyps auf. Der einzige Unterschied zu den vordefinierten Datentypen von Visual Basic besteht darin, dass die PublicVereinbarung von Variablen benutzerdefinierter Typen nur in einem Standardmodul erfolgen darf, während in allen anderen Modularten eine Private-Deklaration erforderlich ist. An Operationen für benutzerdefinierte Datentypen gibt es nur den Punktoperator und den Zuweisungsoperator. Der Punktoperator ermöglicht den Zugriff auf die einzelnen Felder des benutzerdefinierten Datentyps für die Arbeit mit einzelnen Feldwerten nach dem Schema Var.Feld. Der Zuweisungsoperator ermöglicht die Zuweisung eines Werts mit einem benutzerdefinierten Datentyp an eine Variable gleichen Typs. Dabei kopiert Visual Basic alle Feldwerte en bloc, selbst wenn Arrays mit im Spiel sind. Zur Vereinfachung der Notation beim Zugriff auf Elementwerte eines benutzerdefinierten Datentyps (aber auch auf Eigenschaften und Methoden von Objekten, mit Ausnahme von Print, Circle und Line) kann die With-Struktur benutzt werden. Dabei führt die With-Anweisung den genannten Bezeichner als Qualifizierungskontext ein. Innerhalb eines Moduls benutzt Visual Basic von sich den Modulbezeichner als Qualifizierungskontext. Mit jeder With-Anweisung (Verschachtelung ist erlaubt) verengt sich der bestehende Qualifizierungskontext. Warnung
................................................... Wa rnung
Vom Prinzip her besteht zwar die Möglichkeit, LSet-Zuweisungen zwischen benutzerdefinierten Datentypen mit unterschiedlichen Strukturen vorzunehmen, von einem Gebrauch dieses Hintertürchens ist aber generell eher abzuraten, da dabei unvorhergesehene Effekte auftreten können (vgl. »LSet-Anweisung«, S. 80).
61
Benutzerdefinierte Datentypen
Beschreibung
Benutzerdefinierte Datentypen
Tipp
................................................... Tipp
Anstelle benutzerdefinierter Datentypen können Sie eigene Datentypen auch über Klassenmodule implementieren. Der dahinter stehende objektorientierte Ansatz verschmilzt das Konzept des benutzerdefinierten Datentyps mit den zu dem Datentyp gehörigen Manipulationsoperationen und bietet somit den Vorteil der Datenabstraktion. Beispiel
Benutzerdefinierte Datentypen
................................................... Beis piel
' Bereich (Allgemein) eines Moduls Private Type BDT lFeld As Long sFeld As String lDynArray() As Long sStatArray(10) As String End Type ... Dim bdtVar1 As BDT, bdtVar1 As BDT ... bdtVar1.lFeld = 10 ' numerischen Wert zuweisen bdtVar1.sFeld = "Wert des String-Feldes" ' Zeichenfolge zuweisen ReDim bdtVar1.lDynArray(10) As Long ' dyn. Array dimensionieren bdtVar2 = bdtVar1 ' Struktur en bloc zuweisen ' Schreibweise bei Qualifizierung mit With With bdtVar1 .lFeld = 10 ' numerischen Wert zuweisen .sFeld = "Wert des String-Feldes" ' Zeichenfolge zuweisen ReDim .lDynArray(10) As Long ' dyn. Array dimensionieren End With bdtVar2 = bdtVar1 ' Struktur en bloc zuweisen Verwandte Befehle
................................................... Verwa ndte Befehle
LSet Verwandte Themen
................................................... Verwandte Them en
Elementare Datentypen (S. 49); Routinen aus DLLs und der Windows-API einsetzen (S. 185); Objekte und Klassen (S. 195); Klassen als Datentypen für Objektvariablen (S. 196)
Enum- Aufzählungen {Private | [Public]} Enum MeineAufzählung Element1 = LiteralerWert1 [Element2 = LiteralerWert2] ... End Enum Beschreibung
................................................... Bes c hreibung
Eine Enum-Aufzählung ist eine Sammlung von Konstanten des Typs Long, die formal in einem Aufzählungsdatentyp zusammengefasst sind. Die einzelnen Konstanten sind jedoch auch jede für sich im Rahmen des jeweiligen Geltungsbereichs bekannt. Ein Aufzählungsdatentyp lässt
62
Funktionen und Anweisungen für Zeichenfolgen
sich wie jeder andere Datentyp für die Vereinbarung von Variablen, Parametern und Funktionswerten heranziehen, eine Überprüfung auf Einhaltung des über die Aufzählung definierten Wertebereichs findet jedoch nicht statt; auch unterscheidet der Compiler nicht zwischen unterschiedlichen Aufzählungsdatentypen, vielmehr behandelt er jeden Aufzählungsdatentyp als Long. Anwendung
................................................... Anwendung
Beispiel
................................................... Beis piel
Public Enum VbTest vbA1 = &H11111111 vbA2 = 101 vbA3 = 105 vbA4 = 103 vbA5 = 104 End Enum ... ' innerhalb einer Prozedur/Funktion Dim a As VbTest a = vbA3 Print TypeName(a) ' Ausgabe: "Long"
Funktionen und Anweisungen für Zeichenfolgen Der unkomplizierte Umgang mit Zeichenfolgen war schon immer die ureigenste Domäne der Programmiersprache Basic. Darüber hinaus hat sich die Ausstattung für die Manipulation von Zeichenfolgen mit jeder Neuauflage von Basic noch einmal verbessert, so dass dem Programmierer unter Visual Basic 6.0 ein ausgesprochen reichhaltiges Angebot an vordefinierten Funktionen und Anweisungen für die Arbeit mit Zeichenfolgen zur Verfügung steht. Während in früheren Versionen von Visual Basic so manche Zeichenfolgenfunktion in zwei Varianten implementiert war – eine mit $-Suffix und eine ohne –, von denen diejenige mit $-Suffix »echte« Zeichenfolgen lieferte und diejenige ohne $-Suffix Werte des Typs Variant, unterstützt Visual Basic 6.0 die Varianten mit $-Suffix zwar noch zur Wahrung der Kompatibilität, kommt aber grundsätzlich ohne sie aus. Beschreibung
................................................... Bes c hreibung
Seit Einführung der 32-Bit-Version mit Visual Basic 4.0 legt Visual Basic intern allen Zeichenfolgen die Unicode-Darstellung zugrunde. Im Gegensatz zu dem bis dahin verwendeten ANSICode, der zur Repräsentation eines Zeichens gerade mal ein Byte vorsieht (und somit nicht mehr als 256 Zeichen unterscheiden kann), umfasst die Repräsentation eines Zeichens im Unicode zwei Bytes. In Unicode gibt es daher bis zu 65.535 unterschiedliche Zeichen, die von der
63
Funktionen und Anweisungen für Zeichenfolgen
Obwohl es möglich ist, Variablen zu vereinbaren, die einen Aufzählungsdatentyp tragen, und der Editor der Entwicklungsumgebung bei der Notation von Rechtswerten dieses Typs eine Liste mit den vereinbarten Konstanten einblendet, sind Aufzählungsdatentypen faktisch nichts weiter als eine Umbenennung des Datentyps Long und somit reine Fassade. Sie verwenden Aufzählungsdatentypen, um Konstanten zu gruppieren und Symbolvorräte formal als endliche Wertebereiche ausdrücken zu können. Ein Blick in den Objektkatalog (Taste (F2)) zeigt, dass Visual Basic in seinen Bibliotheken massiv von diesem Konzept Gebrauch macht.
Funktionen und Anweisungen für Zeichenfolgen
Funktionen und Anweisungen für Zeichenfolgen
ISO international festgelegt wurden. Zeichen mit Unicode kleiner als 256 entsprechen den Zeichen des ANSI-Codes, so dass beispielsweise das Zeichen »A« mit dem ANSI-Code 65 (oder &H40) in Unicode die Darstellung »0, 65« (oder &H0040) hat. Neben Unicode gibt es noch einen weiteren Zeichencode namens DBCS (Double-Byte Character Set), dessen Darstellung zwei Bytes je Zeichen vorsieht. Dieser Code ist in erster Linie im asiatischen Sprachraum verbreitet und dazu gedacht, die Vielfalt der in diesen Sprachen verwendeten Schriftzeichen ausdrücken zu können. Er hat weder etwas mit ANSI-Code noch mit Unicode gemeinsam, stellt also einen völlig eigenständigen Zeichensatz dar. All dies heißt für den Visual-Basic-Programmierer, dass er es potenziell mit allen drei, sicherlich jedoch mit zwei unterschiedlichen Repräsentationsarten für Zeichenfolgen zu tun bekommen kann – so etwa beim Lesen von Datensätzen aus Dateien mit älteren und neueren Datenformaten. Damit aber nicht genug: Es gilt auch systembedingte Unterschiede zu beachten, die sich aufgrund unterschiedlicher Versionen für Objektbibliotheken und Windows selbst einschleichen. Die folgende Tabelle zeigt eine Aufstellung, welche Umgebung mit welcher Zeichendarstellung einhergeht. Umgebung
Verwendete Zeichensätze
Windows-9x-API
ANSI und DBCS
16-Bit-Objektbibliotheken
ANSI und DBCS
Visual Basic
Unicode
32-Bit-Objektbibliotheken
Unicode
Windows-NT-API
Unicode
Automatisierung in Windows NT
Unicode
Automatisierung in Windows 9x
Unicode
Zeichendarstellung in verschiedenen Windows- Komponenten
Um den Programmierer gegen alle Fälle zu wappnen, unterhält Visual Basic daher für die traditionellen Zeichenfolgenfunktionen Asc, Chr, Len, InStr, Left, Right und Mid unterschiedliche Varianten, deren Funktionsweise spezifisch auf die eine oder andere Repräsentation ausgerichtet ist. Varianten mit Suffix B (so etwa AscB) gehen von einer ANSI-Darstellung und Varianten mit Suffix W (so etwa ChrW) von einer Unicode- oder DBCS-Darstellung aus. Die folgende Tabelle gibt eine Übersicht über die unter Visual Basic 6.0 verfügbaren Funktionen und Prozeduren, die im engeren oder weiteren Sinne etwas mit der Manipulation von Zeichenfolgen zu tun haben. Bezeichner
Kurzbeschreibung
Asc
Liefert den ANSI-Code des ersten Zeichens einer Zeichenfolge (auf DBCSSystemen DBCS-Code)
AscB
Liefert den ANSI-Code des ersten Bytes einer Zeichenfolge
AscW
Liefert den Unicode des ersten Doppel-Bytes einer Zeichenfolge
Chr
Liefert das zu einem ANSI-Code gehörige Unicode-Zeichen
ChrB
Liefert das zu einem ANSI-Code gehörige ANSI-Zeichen
ChrW
Liefert das zu einem Unicode gehörige Unicode-Zeichen
Zeichenfolgenfunktionen und - prozeduren von Visual Basic
64
Funktionen und Anweisungen für Zeichenfolgen
Kurzbeschreibung
Filter
Durchsucht alle Elemente eines Zeichenfolgen-Arrays nach einer bestimmten Zeichenfolge und liefert die Array-Elemente, in denen diese enthalten ist
Format
Liefert Zeichenfolgendarstellung eines Werts im spezifizierten Format
FormatCurreny
Liefert die Darstellung eines Währungswerts im spezifizierten Format
FormatDateTime
Liefert die Darstellung eines Datumswerts im spezifizierten Format
FormatNumber
Liefert die Darstellung eines Zahlenwerts im spezifizierten Format
FormatPercent
Liefert die Darstellung eines Prozentwerts im spezifizierten Format
Hex
Liefert die hexadezimale Notation eines Werts
InStr
Liefert die Startposition einer Zeichenfolge in einer anderen Zeichenfolge
InStrB
Liefert die Startposition einer ANSI-Zeichenfolge in einer anderen ANSIZeichenfolge
InStrRev
Wie InStr, beginnt die Suche aber von hinten
Join
Umkehrfunktion zu Split, setzt die Zeichenfolgen eines String-Arrays zu einer einzigen Zeichenfolge zusammen (Trennzeichen optional)
LCase
Liefert die Kopie einer Zeichenfolge in Kleinschreibung
Left
Liefert den linken Teil einer Zeichenfolge
LeftB
Liefert den linken Teil einer ANSI-Zeichenfolge
Len
Liefert die Anzahl der Zeichen in Zeichenfolge – trägt der im Parameter übergebene Wert nicht den Typ String, liefert die Funktion die Anzahl der zum Speichern dieses Werts erforderlichen Bytes.
LenB
Liefert die Anzahl der Bytes, die für die Darstellung des Werts (zum Beispiel bei Speichern) erforderlich sind; liefert damit auch die Länge einer ANSI-Zeichenfolge
LSet
Byteorientierte, linksbündige Zuweisungsoperation für beliebige Datentypen, ohne Längenanpassung des Linkswerts. Elementare Datentypen unterliegen einer Typüberprüfung, benutzerdefinierte nicht.
LTrim
Liefert eine Kopie der Zeichenfolge ohne führende Leerzeichen zurück
Mid
Liefert den mittleren Teil einer Zeichenfolge
Mid
Zuweisungsoperation für den mittleren Teil einer Zeichenfolge
MidB
Liefert den mittleren Teil einer Zeichenfolge als ANSI-Zeichenfolge
MidB
Zuweisungsoperation für mittleren Teil einer ANSI-Zeichenfolge
MonthName
Liefert für eine Zahl zwischen 1 und 12 den Monatsname als Zeichenfolge
Oct
Liefert die oktale Notation eines Werts
Option Compare
Standardvorgabe für Zeichenfolgenvergleiche auf binären Vergleich oder Textvergleich (ohne Beachtung der Großschreibung) setzen
Zeichenfolgenfunktionen und - prozeduren von Visual Basic
65
Funktionen und Anweisungen für Zeichenfolgen
Bezeichner
Funktionen und Anweisungen für Zeichenfolgen
Funktionen und Anweisungen für Zeichenfolgen
Bezeichner
Kurzbeschreibung
Partition
Liefert das Intervall, in das eine Zahl fällt, als Zahlenbereich der Form »IntervallAnf: IntervallEnde«. Der Aufruf der Funktion erfolgt unter Angabe eines Startwerts für das erste Intervall, eines Endwerts für das letzte Intervall und einer Intervallbreite.
Replace
Sucht Vorkommen einer Suchzeichenfolge in Zeichenfolge, ersetzt dieses gegen Ersatzzeichenfolge und liefert resultierende Zeichenfolge
Right
Liefert den rechten Teil einer Zeichenfolge
RightB
Liefert den rechten Teil einer ANSI-Zeichenfolge
RSet
Rechtsbündige Zuweisungsoperation für Zeichenfolgen ohne Längenänderung des Linkswerts; füllt mit Leerzeichen auf oder schneidet ab
RTrim
Liefert die Kopie einer Zeichenfolge ohne abschließende Leerzeichen
Space
Liefert eine Zeichenfolge mit der gewünschten Anzahl von Leerzeichen
Split
Umkehrfunktion zu Join, durchsucht eine Zeichenfolge nach Trennzeichen und liefert die Zeichenfolgenabschnitte als Array
Str
Liefert standardmäßige Repräsentation eines Zahlenwerts als Zeichenfolge
StrComp
Führt alphanumerischen Zeichenfolgenvergleich durch und unterscheidet dabei vier Fälle
StrConv
Konvertiert eine Zeichenfolge nach einem von sieben möglichen Konvertierungsschemata und liefert die konvertierte Version als Kopie
String
Liefert Zeichenfolge mit der gewünschten Anzahl eines einzelnen Zeichencodes
StrReverse
Liefert die Kopie einer Zeichenfolge mit verkehrter Reihenfolge der Zeichen
Trim
Liefert Zeichenfolge ohne führende und abschließende Leerzeichen
UCase
Liefert die Kopie einer Zeichenfolge in Großschreibung
Val
Liefert den Zahlenwert einer Zeichenfolge
WeekdayName
Liefert für eine Zahl zwischen 1 und 7 den Wochentag als Zeichenfolge
Zeichenfolgenfunktionen und - prozeduren von Visual Basic
................................................... Warnung
Warnung
Eine notorische Ursache für Ungereimtheiten im Programmverhalten ist die auf die länderspezifische Zahlendarstellung (Gebietseinstellungen des Systems) zurückzuführende Punkt-/KommaProblematik im Zusammenhang mit Zeichenfolgen. Wer mit Ländereinstellung »Deutsch (Standard)« auf seinem System einen Zahlenwert als literale Zeichenfolge spezifiziert, muss darauf achten, das Komma als echtes Komma zu schreiben, während in »echten« numerischen Literalen der Punkt als Dezimalsymbol notiert werden muss.
Asc- , AscB- und AscW- Funktion Function Asc (s As String) As Integer Function AscB (s As String) As Integer Function AscW (s As String) As Integer
66
Chr- ,
ChrB-
und ChrW - Funktion
Beschreibung
................................................... Bes c hreibung
Die Funktion Asc liefert den Code des ersten Zeichens der Zeichenfolge s entsprechend dem auf dem System geltenden Zeichensatz als Ganzzahl. Die beiden anderen Varianten der Funktion liefern (ohne Rücksicht auf den geltenden Zeichensatz) den ANSI-Code des ersten Bytes der Zeichenfolge (AscB) bzw. den Unicode des ersten Doppel-Bytes der Zeichenfolge (AscW). Beispiel
................................................... Beis piel
Die Funktion StrToHex wandelt eine Zeichenfolge beliebiger Länge byteweise in eine hexadezimale Repräsentation der Form "hh hh ..." um.
Funktionen und Anweisungen für Zeichenfolgen
' Funktion wandelt beliebig lange Byte-Strings in Hex-String um Function StrToHex(sByteString As String) As String Dim i As Integer, iByte As Integer Dim sLow As String, sHigh As String For i = 1 To LenB(sByteString) ' byteweise analysieren iByte = AscB(MidB(sByteString, i, 1)) If iByte \ 16 > 9 Then ' Ziffer oder Buchstabe? sHigh = Chr(iByte \ 16 + 55) ' Buchstabe Else sHigh = Chr(iByte \ 16 + 48) ' Ziffer End If If iByte Mod 16 > 9 Then sLow = Chr(iByte Mod 16 + 55) Else sLow = Chr(iByte Mod 16 + 48) End If StrToHex = StrToHex + sHigh + sLow + " " ' Zusammensetzen Next i End Function Verwandte Befehle
................................................... Verwa ndte Befehle
Chr, Str Verwandte Themen
................................................... Verwandte Them en
Typumwandlung (S. 57)
Chr- , ChrB- und ChrW- Funktion Function Chr (iANSICode As Integer) As String Function ChrB (iANSICode As Integer) As String Function ChrW (iUnicode As Integer) As String Beschreibung
................................................... Bes c hreibung
Die Funktion Chr liefert das Unicode-Zeichen zu einem ANSI-Code als Unicode-Zeichenfolge der Länge 1. ChrB liefert dagegen zu einem ANSI-Code das ANSI-Zeichen als ANSI-Zeichenfolge der Länge 1 und ChrW das Unicode-Zeichen zu einem Unicode als Unicode-Zeichenfolge der Länge 1.
67
Funktionen und Anweisungen für Zeichenfolgen
Beispiel
................................................... Beis piel
Funktionen und Anweisungen für Zeichenfolgen
Die Funktion HexToStr ist die Umkehrfunktion zu der Funktion StrToHex (vgl. Beispiel zu Asc). Sie wandelt beliebig lange Hexadezimaldarstellungen der Form "hh hh ..." in Bytefolgen des Typs String um. Public Function HexToStr(sHexString As String) As String Dim i As Integer, iHigh As Integer, iLow As Integer Dim sHexChar As String Dim sLow As String, sHigh As String If LenB(" ") = Len(" ") Then ' Unicode-System? HexToStr = Space(Len(sHexString) / 3) Else HexToStr = Space(Len(sHexString) / 6) End If For i = 1 To Len(sHexString) Step 3 ' iHigh = Asc(Mid(sHexString, i, 1)) – 48 ' If iHigh > 15 Then iHigh = iHigh – 7 ' iLow = Asc(Mid(sHexString, i + 1, 1)) – 48 If iLow > 15 Then iLow = iLow – 7 MidB(HexToStr, (i + 2) \ 3) = ChrB(iHigh * Next i End Function
aus 3 Zeichen wird 1 Byte! Annahme: Ziffer War es etwa ein Buchstabe?
16 + iLow) ' Zusammensetzen
Verwandte Befehle
................................................... Verwa ndte Befehle
Asc, Str Verwandte Themen
................................................... Verwandte Them en
Typumwandlung (S. 57)
Filter- Funktion Function Filter( _ StringPool() As String, _ SuchString As String, _ [bVorkommenLiefern As Boolean = True], _ [vgl As vbCompareMethod = vbBinaryCompare]) _ As String() Beschreibung
................................................... Bes c hreibung
Die Funktion Filter durchsucht die Elemente des Zeichenfolgenarrays StringPool nach dem Vorkommen der Zeichenfolge SuchString und liefert die entsprechenden Array-Elemente als Zeichenfolgenarray. Hat der optionale Parameter VorkommenLiefern den Wert True oder fehlt er, enthält das Ergebnisarray alle Elemente, die SuchString enthalten, andernfalls (False) alle Elemente, die SuchString nicht enthalten. Hat der optionale Parameter vgl den Wert vbBinaryCompare oder fehlt er, ist die Vergleichsoperation ein zeichenweiser Unicode-Vergleich; hat vgl den Wert vbTextCompare, legt die Funktion der Vergleichsoperation die standardmäßige Sortierordnung der für die auf dem System geltenden Landessprache zugrunde – ohne Unterscheidung der Groß- und Kleinschreibung.
68
Format- Funktion
Warnung
................................................... Wa rnung
Das Standardverhalten der Funktion bei fehlendem Parameter vgl kann sich durch eine Option Compare-Anweisung ändern. Beispiel
................................................... Beis piel
Das Programm benutzt die Funktion Filter, um herauszufinden, welche Wochentage (hierfür gilt die Ländereinstellung) ohne Unterscheidung der Großschreibung die Zeichenfolge "TAG" nicht enthalten.
Verwandte Befehle
................................................... Verwa ndte Befehle
Join, Split
Format- Funktion Function Format( _ vData, _ sFormat As String], _ [FirstDayOfWeek As VbFirstDayOfWeek = vbSunday], _ [FirstWeekOfYear As VbFirstWeekOfYear = vbFirstJan1]) _ As String Beschreibung
................................................... Bes c hreibung
Die Funktion Format liefert für den Wert vData eine formatierte Darstellung als Zeichenfolge. Fehlt der optionale Parameter sFormat, folgt die Darstellung den auf dem jeweiligen System geltenden Ländereinstellungen, und das Ergebnis entspricht bis auf das fehlende führende Leerzeichen bei der Darstellung positiver Zahlen dem von Str. Ansonsten drückt sFormat unter Beachtung der Syntax für Formatausdrücke ein benutzerdefiniertes Darstellungsformat aus. Die optionalen Parameter FirstDayOfWeek und FirstWeekOfYear beziehen sich auf exotische Formen der Datumsdarstellung und können im Allgemeinen getrost weggelassen werden. Anwendung
................................................... Anwendung
Für den Formatausdruck sFormat gelten folgende Regeln: 1. Der Formatausdruck kann den Namen eines benannten Formats enthalten. Die folgenden drei Tabellen geben einen Überblick über die benannten Formate.
69
Funktionen und Anweisungen für Zeichenfolgen
Private Sub Form_Load() Dim sWochenTage(7) As String, sErgebnis() As String AutoRedraw = True For i = 0 To 6 ' Wochentage generieren sWochenTage(i) = WeekdayName(i + 1) Next i sErgebnis = Filter(sWochenTage, "TAG", False, vbTextCompare) For i = 0 To UBound(sErgebnis) – 1 ' es kommt nur der Mittwoch Print sErgebnis(i) Next i End Sub
Funktionen und Anweisungen für Zeichenfolgen
Formatname
Beschreibung
Funktionen und Anweisungen für Zeichenfolgen
"General Date" Übliche Datums-/Zeitdarstellung mit zweistelliger Jahreszahl – bei Angabe von Datum und Zeit: 01.01.00 13:00:00; bei Angabe von Zeit: 13:00:00; bei Angabe von Datum: 01.01.00 "Long Date"
Ausgeschriebenes Datum, entsprechend der Systemeinstellungen: Samstag, 1. Januar 2000
"Medium Date"
Mittleres Datumsformat (von Host-Anwendung definierbar): 1. Jan. 00
"Short Date"
Kurzes Datum, entsprechend der Systemeinstellungen: 01.01.00
Benannte Datumsformate
Formatname
Beschreibung
"Long Time"
Zeitangabe enthält auch Sekunden: 13:00:00
"Medium Time"
Zeitangabe im 12-Stunden-Format, ohne Sekunden: 1:00
"Short Time"
Zeitangabe im 24-Stunden-Format, ohne Sekunden: 13:00
Benannte Zeitformate
Formatname
Beschreibung
"General Number"
Zahlen werden ohne Tausendertrennzeichen notiert.
"Currency"
Zahlen werden mit Tausendertrennzeichen und zwei Nachkommastellen sowie unter Beachtung der auf dem System geltenden Gebietsschema-Einstellungen notiert.
"Fixed"
Festkommadarstellung unter Beachtung der auf dem System geltenden Gebietsschema-Einstellungen, ohne Tausendertrennzeichen und gerundet auf zwei Stellen hinter dem Komma: 10001,34
"Standard"
Wie "Fixed", jedoch mit Tausendertrennzeichen: 10.001,34
"Percent"
Wie "Fixed", jedoch mit Faktor 100 multipliziert und angehängtem Prozentzeichen %
"Scientific"
Wissenschaftliches Standardformat mit einer Stelle vor dem Komma, zwei Stellen nach dem Komma und Zehnerpotenz: 1,00E34
"Yes/No"
Ausgabe von »Nein« anstatt 0, »Ja« für jeden anderen Wert
"True/False"
Ausgabe von »False« anstatt 0, »True« für jeden anderen Wert
"On/Off"
Ausgabe von »Aus« anstatt 0, »Ein« für jeden anderen Wert
Benannte Zahlenformate
2. Der Formatausdruck kann ein benutzerdefiniertes Format enthalten. Die folgende Tabelle gibt einen Überblick über die Bedeutung der Symbole, die für die Zusammenstellung benutzerdefinierter Formate eine feste Interpretation haben.
70
Format- Funktion
Bedeutung
0
Platzhalter für Ziffer (vor und nach dem Dezimalzeichen). Auch Platzhalter für Ganzzahlanteil vor dem Dezimalzeichen. Platzhalterpositionen, die die Dezimaldarstellung nicht bedient, erhalten das Füllzeichen 0. Das Format "0.000" für 1234,12 ergibt "1234,120" und das Format "000000" für 1234,12 ergibt "001234".
#
Wie Symbol 0, es gibt jedoch kein Füllzeichen
.
Platzhalter für Dezimalzeichen
%
Zahlenwert wird mit 100 multipliziert und als Prozentwert mit anhängendem Prozentzeichen % dargestellt. Das Format "0.000%" für 1234,12 ist "123412,000%".
,
Die Position dieses Zeichens im Formatausdruck ist ausschlaggebend für seine Bedeutung. Steht es zwischen zwei Ziffernplatzhaltern links vom Dezimalzeichenplatzhalter erfolgt die Darstellung mit Tausendertrennzeichen. Steht es dagegen links vom ersten Ziffernplatzhalter in einer Zahlendarstellung, erscheint es als gewöhnliches Komma. Steht es bei fehlendem Dezimalzeichenplatzhalter rechts vom letzten Ziffernplatzhalter oder unmittelbar links neben dem Dezimalzeichenplatzhalter, wird der Zahlenwert durch 1000 dividiert. So bewirkt "#," für 123.4123.456 die Darstellung "1234123" und "#,,.0" für 1234.887.654 die Darstellung "1234,9".
E- oder e-
Wissenschaftliche Darstellung ohne Ausgabe des Pluszeichens im Exponenten
E+ oder e+
Wissenschaftliche Darstellung mit Ausgabe des Pluszeichens im Exponenten
- + $ ( )
Diese Symbole stehen für sich selbst. Sie erscheinen als Zeichen. Alle anderen bedeutungsbeladenen Symbole müssen einen umgekehrten Schrägstrich vorangestellt bekommen, um sie als einfaches Zeichen darzustellen.
\
Dieses Symbol kann einem anderen bedeutungsbeladenen Symbol vorangestellt werden, um dieses als Zeichen darzustellen. "\\" bewirkt die Ausgabe des Symbols selbst als Zeichen. Um ein Anführungszeichen auszugeben, muss es doppelt und in Kombination mit diesem Symbol notiert werden. Die Zahl 30405 wird mit "#0\°##\'##\"" \N\o\r\d" zu "3°04'05"" Nord".
"Nord"
Zeigt die Zeichenfolge in Anführungszeichen an. Die Regeln für Zeichenfolgenliterale fordern die doppelte Notation von Anführungszeichen. Die Zahl 30405 wird mit "#0\°##\'##\"" ""Nord""" gleichfalls zu "3°04'05"" Nord".
Symbole für benutzerdefinierte Zahlenformate
Symbol
Bedeutung
:
Platzhalter für Zeittrennzeichen zwischen Stunden, Minuten und Sekunden
/
Platzhalter für Datumstrennzeichen zwischen Tagen, Monaten und Jahren
C
Zeigt Datumsanteil in der Form ddddd und Zeitanteil in der Form ttttt an
d
Tag erscheint als Zahl ohne führende Null (1 bis 31)
dd
Tag erscheint als Zahl mit führender Null (01 bis 31)
ddd
Tag erscheint als abgekürzter Wochentag (Mo bis So)
Symbole für benutzerdefinierte Datums- und Zeitformate
71
Funktionen und Anweisungen für Zeichenfolgen
Symbol
Funktionen und Anweisungen für Zeichenfolgen
Funktionen und Anweisungen für Zeichenfolgen
Symbol
Bedeutung
dddd
Tag erscheint als ausgeschriebener Wochentag (Montag bis Sonntag)
ddddd
Datum erscheint vollständig im kurzen Datumsformat (vgl. benanntes Format "Short Date")
dddddd
Datum erscheint vollständig im langen Datumsformat (vgl. benanntes Format "Long Date")
w
Wochentag erscheint als Zahl ohne führende Null (1 bis 7)
ww
Woche erscheint als Kalenderwoche (1 bis 54)
m
Monat erscheint als Zahl ohne führende Null (1 bis 12)
mm
Monat erscheint als Zahl mit führender Null (01 bis 12)
mmm
Monat erscheint als abgekürzter Monatsname (Jan bis Dez)
mmmm
Monat erscheint als ausgeschriebener Monatsname (Januar bis Dezember)
q
Quartal erscheint als Zahl (1 bis 4)
y
Kalendertag erscheint als Zahl (1 bis 366)
yy
Jahr erscheint als zweistellige Zahl (00 bis 99)
yyyy
Jahr erscheint als vierstellige Zahl (100 bis 9999)
h
Stunde erscheint als Zahl ohne führende Null (0 bis 23)
hh
Stunde erscheint als Zahl mit führender Null (00 bis 23)
n
Minute erscheint als Zahl ohne führende Null (0 bis 59)
nn
Minute erscheint als Zahl mit führender Null (00 bis 59)
s
Sekunde erscheint als Zahl ohne führende Null (0 bis 59)
ss
Sekunde erscheint als Zahl mit führender Null (00 bis 59)
ttttt
Zeit erscheint als vollständiges langes Zeitformat mit Stunden, Minuten und Sekunden (vgl. benanntes Format "Long Time")
AM/PM
Zeit erscheint im 12-Stunden-Format mit Anhang »AM« für Stunden zwischen 0 und 12 und mit Anhang »PM« für Stunden zwischen 13 und 23. Das amerikanische Zeitformat ist somit "hh.nn.ss AM/PM".
am/pm
Wie AM/PM, jedoch mit kleingeschriebenem Anhang »am« bzw. »pm»
Symbole für benutzerdefinierte Datums- und Zeitformate
Symbol
Bedeutung
@
Platzhalter für alphanumerisches Zeichen; Füllzeichen ist das Leerzeichen
&
Platzhalter für alphanumerisches Zeichen; kein Füllzeichen
<
Erzwingt Kleinschreibung
>
Erzwingt Großschreibung
!
Erzwingt Auswertung der Platzhalter von links nach rechts (standardmäßig werden Platzhalter von rechts nach links ausgewertet)
Symbole für benutzerdefinierte Zeichenfolgenformate
72
FormatCurrency- Funktion
Warnung
................................................... Wa rnung
Leider ist die Format-Funktion von Visual Basic 6.0 nicht fehlersicher implementiert, und liefert im Zusammenhang mit der Print-Anweisung gerne mal den obskuren Fehler 480, »Anwendungs- oder objektdefinierter Fehler«, wenn der Formatausdruck syntaktisch nicht korrekt ist oder einfach nicht zum Typ oder Wert von vData passt. Da dieser Fehler danach selbst bei Angabe korrekter Argumente nicht mehr weg zu bekommen ist, hilft in diesem Fall nur ein Neustart von Visual Basic. Beispiel
................................................... Beis piel
' Projekt FormatDemo Private Sub Form_Load() Const cJahr = 2001 ' Jahr Const cHalbjahr = 2 AutoRedraw = True For iMonat = 1 To 6 ' erstes Halbjahr Dat = DateSerial(cJahr, iMonat + (cHalbjahr – 1) * 6, 1) Print Format(Dat, "mmmm yyyy") Print "Kal.-Woche", For i = 0 To 6 ' Wochentage ausgeben Print Format(Dat + i, "dddd"), Next Print ' Anzahl der Tage in Monat berechnen Tage = Day(DateSerial(Year(Dat), Month(Dat) + 1, Day(Dat) – 1)) For i = 1 To Tage ' Kalenderwoche und Tage ausgeben If (i – 1) Mod 7 = 0 Then Print Format(Dat + i – 1, "ww"), Print Format(i, "00"), If i Mod 7 = 0 Then Print Next Print Next End Sub Verwandte Befehle
................................................... Verwa ndte Befehle
FormatCurrency, FormatDateTime, FormatNumber, FormatPercent, Str Verwandte Themen
................................................... Verwandte Them en
Typumwandlung (S. 57)
FormatCurrency- Funktion Function FormatCurrency( _ vData, _ [NachkommaStellen As Long = -1], _ [NullVorKommaErgänzen As VbTriState = vbUseDefault], _ [WennNegativKlammernSetzen As VbTriState = vbUseDefault], _ [TausenderTrennzVerwenden As VbTriState = vbUseDefault]) _ As String
73
Funktionen und Anweisungen für Zeichenfolgen
Das Projekt FormatDemo gibt einen Halbjahreskalender auf einem gewöhnlichen Formular aus:
Funktionen und Anweisungen für Zeichenfolgen
Beschreibung
................................................... Bes c hreibung
Die Funktion FormatCurrency ist eine spezialisierte Variante der Funktion Format. Sie liefert unter Verwendung der in der Systemsteuerung definierten Währung eine als Währungswert formatierte Zeichenfolge. Mittels der vier optionalen Parameter lassen sich Vorgaben für die Anzahl der Nachkommastellen, die Ergänzung einer führenden Null bei Werten kleiner eins, die Klammerung negativer Zahlen sowie die Ergänzung eines Tausendertrennzeichens treffen. Beispiel
Funktionen und Anweisungen für Zeichenfolgen
................................................... Beis piel
' Ausgabe: "(-14.345,13 DM)" Print FormatCurrency(-14345.1253, , , vbTrue) ' Ausgabe: "-14345,125 DM" Print FormatCurrency(-14345.1253, 3, vbFalse, vbFalse, vbFalse) Verwandte Befehle
................................................... Verwa ndte Befehle
Format, FormatDateTime, FormatNumber, FormatPercent, Str, CStr Verwandte Themen
................................................... Verwandte Them en
Typumwandlung (S. 57)
FormatDateTime- Funktion Function FormatDateTime( _ vData, _ BenanntesFormat As VbDateTimeFormat = vbGeneralDate]) _ As String Beschreibung
................................................... Bes c hreibung
Die Funktion FormatDateTime ist eine spezialisierte Variante der Funktion Format. Sie liefert unter Verwendung des in der Systemsteuerung definierten Datums- und Zeitformats eine als Datums- und/oder Zeitangabe formatierte Zeichenfolge. Mittels des optionalen Parameters BenanntesFormat lässt sich das zu verwendende Format vorgeben. Zur Auswahl stehen je ein kurzes und ein langes Format für Zeitangaben und Datumsangaben. Beispiel
................................................... Beis piel
Print FormatDateTime(14345.1223, vbShortTime) ' Ausgabe: "02:56" Print FormatDateTime(14345.1223, vbLongTime) ' Ausgabe: "02:56:07" Print FormatDateTime(14345.1223, vbShortDate) ' Ausgabe: "10.4.39" ' Ausgabe: "Montag, 10 April 1939" Print FormatDateTime(14345.1223, vbLongDate) ' Ausgabe: "10.4.39 02:56:07" Print FormatDateTime(14345.1223, vbGeneralDate) Print FormatDateTime(14345.1223) ' Ausgabe: "10.4.39 02:56:07" Verwandte Befehle
................................................... Verwa ndte Befehle
Format, FormatCurrency, FormatNumber, FormatPercent, Str
74
FormatNumber- Funktion
Verwandte Themen
................................................... Verwandte Them en
Typumwandlung (S. 57)
FormatNumber- Funktion
Beschreibung
................................................... Bes c hreibung
Die Funktion FormatNumber ist eine spezialisierte Variante der Funktion Format. Sie liefert eine Zahlendarstellung in Form einer Zeichenfolge. Der einzige Unterschied zu FormatCurrency besteht darin, dass die Funktion ihre Darstellung ohne Währungssymbol vornimmt. Mittels der fünf optionalen Parameter lassen sich Vorgaben für die Anzahl der Nachkommastellen, die Ergänzung einer führenden Null bei Werten kleiner eins, die Klammerung negativer Zahlen sowie die Ergänzung eines Tausendertrennzeichens treffen. Beispiel
................................................... Beis piel
Print FormatNumber(-143.1253) ' Ausgabe:"-143,13" Print FormatNumber(-143.1253, 3, , vbTrue) ' Ausgabe:"(143,125)" Print FormatNumber(-143.1253, 0, vbFalse, vbFalse) ' Ausgabe:"-143)" Verwandte Befehle
................................................... Verwa ndte Befehle
Format, FormatCurrency, FormatDateTime, FormatNumber, Str Verwandte Themen
................................................... Verwandte Them en
Typumwandlung (S. 57)
FormatPercent- Funktion Function FormatPercent( _ vData, _ [NachkommaStellen As Long = -1], _ [NullVorKommaErgänzen As VbTriState = vbUseDefault], _ [WennNegativKlammernSetzen As VbTriState = vbUseDefault], _ [TausenderTrennzVerwenden As VbTriState = vbUseDefault], _ [ZiffernGruppieren As VbTriState = vbUseDefault)] _ As String Beschreibung
................................................... Bes c hreibung
Die Funktion FormatPercent ist eine spezialisierte Variante der Funktion Format. Sie multipliziert vData mit 100 und liefert das Ergebnis als Prozentangabe mit Prozentzeichen in Form einer Zeichenfolge. Mittels der fünf optionalen Parameter lassen sich Vorgaben für die Anzahl der Nachkommastellen, die Ergänzung einer führenden Null bei Werten kleiner eins, die Klammerung negativer Zahlen sowie die Ergänzung eines Tausendertrennzeichens treffen.
75
Funktionen und Anweisungen für Zeichenfolgen
Function FormatNumber( _ vData, _ [NachkommaStellen As Long = -1], _ [NullVorKommaErgänzen As VbTriState = vbUseDefault], _ [WennNegativKlammernSetzen As VbTriState = vbUseDefault], _ [TausenderTrennzVerwenden As VbTriState = vbUseDefault]) _ [ZiffernGruppieren As VbTriState = vbUseDefault)] _ As String
Funktionen und Anweisungen für Zeichenfolgen
Beispiel
................................................... Beis piel
Print FormatPercent(-143.1253) ' Ausgabe: "-14.312,53%" Print FormatPercent(-143.1253, , , vbTrue) ' Ausgabe: "(14.312,53%)" Print FormatPercent(-143.1253, 0, vbFalse, vbFalse) 'Ausgabe:"-14.313%)" Verwandte Befehle
................................................... Verwa ndte Befehle
Format, FormatCurrency, FormatDateTime, FormatNumber, Str
Funktionen und Anweisungen für Zeichenfolgen
Verwandte Themen
................................................... Verwandte Them en
Typumwandlung (S. 57)
Hex- Funktion Function Hex(vZahl) As String Beschreibung
................................................... Bes c hreibung
Die Funktion Hex interpretiert den Wert vZahl als Ganzzahl und liefert deren hexadezimale Repräsentation als Zeichenfolge. Die Darstellung enthält nicht mehr Stellen als nötig, mithin also keine führenden Nullen oder Leerzeichen. Beispiel
................................................... Beis piel
Print Hex(255) ' Ausgabe: "FF" lWert = 10 Print "&H" Right("0000000"+Hex(lWert) , 8) ' Ausgabe: "&H0000000A" Verwandte Befehle
................................................... Verwa ndte Befehle
Oct
InStr- und InStrB- Funktion Function InStr( [lStart As Long = 1,] _ sDurchsuchterString As String, _ sSuchString As String,_ [vgl As VbCompareMethode = vbBinaryCompare]) _ As Long Function InStrB( [lStart As Long = 1,] _ sDurchsuchterANSIString As String, _ sANSISuchString As String,_ [vgl As VbCompareMethode = vbBinaryCompare]) _ As Long Beschreibung
................................................... Bes c hreibung
Die Funktion InStr durchsucht die Zeichenfolge sDurchsuchterString ab der Zeichenposition lStart (optional mit Vorgabewert 1) nach dem nächsten Vorkommen der Zeichenfolge sSuchString. Ist die Suche erfolgreich, liefert die Funktion ihre Startposition in der durchsuchten Zeichenfolge, ansonsten den Wert 0. Hat der optionale Parameter vgl den Wert vbBinary-
76
InStrRev- Funktion
Compare oder fehlt er, ist die Vergleichsoperation ein zeichenweiser Unicode-Vergleich; hat vgl den Wert vbTextCompare, legt die Funktion der Vergleichsoperation die standardmäßige Sortierordnung der für die auf dem System geltenden Landessprache zugrunde – ohne Unterscheidung der Groß- und Kleinschreibung. Die Variante InStrB dieser Funktion ist für Zeichenfolgen im ANSI-Code zuständig. Hinweis
................................................... Hinweis
Warnung
................................................... Wa rnung
Das Standardverhalten der Funktion bei fehlendem Parameter vgl kann sich durch eine Option Compare-Anweisung ändern. Beispiel
................................................... Beis piel
Der folgende Code zählt mittels der Zählvariablen iCount, wie oft die Zeichenfolge s1 innerhalb der Zeichenfolge s2 vorkommt. iCount = 0 lPos = 0 Do lPos = InStr(lPos + 1, s1, s2) If lPos Then iCount = iCount +1 Loop While lPos ... ' iCount enthält nun die Anzahl der Vorkommen Verwandte Befehle
................................................... Verwa ndte Befehle
Filter, InStrRev, StrComp
InStrRev- Funktion Function InStrRev( sDurchsuchterString As String, _ sSuchString As String,_ [lStart As Long = -1,] _ [vgl As VbCompareMethod = vbBinaryCompare]) _ As Long Beschreibung
................................................... Bes c hreibung
Die Funktion InStrRev vollbringt die gleiche Aufgabe wie InStr, mit dem feinen Unterschied, dass sie die Zeichenfolge sDurchsuchterString ab der Zeichenposition lStart (optional, mit Vorgabewert -1) rückwärts, also von rechts nach links, durchsucht. Hat der optionale Parameter vgl den Wert vbBinaryCompare oder fehlt er, ist die Vergleichsoperation ein zeichenweiser Unicode-Vergleich; hat vgl den Wert vbTextCompare, legt die Funktion der Vergleichsoperation die standardmäßige Sortierordnung der für die auf dem System geltenden Landessprache zugrunde – ohne Unterscheidung der Groß- und Kleinschreibung.
77
Funktionen und Anweisungen für Zeichenfolgen
Die mit Visual Basic 6.0 vorliegende Implementation dieser Funktion hat kein Problem damit, wenn Start größer ist als die Länge der durchsuchten Zeichenfolge. Negative Werte sowie ein Wert von 0 führen dagegen zu einem Laufzeitfehler.
Funktionen und Anweisungen für Zeichenfolgen
Hinweis
................................................... Hinweis Die mit Visual Basic 6.0 vorliegende Implementation dieser Funktion hat kein Problem damit, wenn lStart größer ist als die Länge der durchsuchten Zeichenfolge. Werte kleiner gleich 0 (außer dem Sonderwert -1) führen dagegen zu einem Laufzeitfehler. Warnung
................................................... Wa rnung
Das Standardverhalten der Funktion bei fehlendem Parameter vgl kann sich durch eine Option Compare-Anweisung ändern.
Funktionen und Anweisungen für Zeichenfolgen
Beispiel
................................................... Beis piel
Der folgende Code zählt mittels der Zählvariablen iCount, wie oft die Zeichenfolge s1 innerhalb der Zeichenfolge s2 vorkommt. iCount = 0 lPos = -1 Do lPos = InStrRev(s1, s2, lPos) – 1 If lPos >= 0 Then iCount = iCount + 1 Loop While lPos > 0 ... ' iCount enthält nun die Anzahl der Vorkommen Verwandte Befehle
................................................... Verwa ndte Befehle
Filter, InStr, StrComp
J oin- Funktion Function Join( _ StringArray() As String, _ [TrennZeichen As String] = " ") _ As String Beschreibung
................................................... Bes c hreibung
Die Funktion Join ist die Umkehrfunktion zu Split. Sie verkettet die Elemente des Zeichenfolgenarrays StringArray zu einer einzelnen Zeichenfolge und liefert diese zurück. Fehlt der optionale Parameter TrennZeichen, fügt die Funktion zwischen den Elementen ein Leerzeichen ein. Durch Angabe eines Werts lassen sich auch andere Trennzeichenfolgen vorgeben, so etwa die leere Zeichenfolge "", das Tabulatorzeichen vbTab oder Zeilenvorschübe vbCrLf. Beispiel
................................................... Beis piel
Das Projekt JoinDemo initialisiert ein Zeichenfolgenarray mit den Namen der Wochentage und gibt das Ergebnis des nachfolgenden Join-Aufrufs aus: ' Projekt: JoinDemo Private Sub Form_Load() Dim sWochenTage(7) As String, sErgebnis() As String AutoRedraw = True For i = 0 To 6 ' Wochentage generieren sWochenTage(i) = WeekdayName(i + 1) Next i Print Join(sWochenTage, vbCrLf) End Sub
78
LCase- Funktion
Verwandte Befehle
................................................... Verwa ndte Befehle
Filter, Split
LCase- Funktion Function LCase(s As String) As String Beschreibung
................................................... Bes c hreibung
Beispiel
................................................... Beis piel
if LCase(sTest1) = LCase(sTest2) Then
' Schreibweise egal
Verwandte Befehle
................................................... Verwa ndte Befehle
UCase
Left- und LeftB- Funktion Function Left(sUnicode As String, lZeichenAnz As Long) As String Function LeftB(sANSI As String, lZeichenAnz As Long) As String Beschreibung
................................................... Bes c hreibung
Die Funktion Left liefert die ersten lZeichenAnz Zeichen der Zeichenfolge sUnicode als Unicode-Zeichenfolge und die Funktion LeftB die ersten lZeichenAnz Bytes der Zeichenfolge sANSI als ANSI-Zeichenfolge. Ist der Wert von lZeichenAnz größer oder gleich der Länge der Zeichenfolge, gibt die Funktion die vollständige Zeichenfolge zurück. Mit 0 als Wert für lZeichenAnz gibt die Funktion die leere Zeichenfolge zurück. Beispiel
................................................... Beis piel
Die folgende Ausgabeanweisung gibt die Zeichenfolge s linksbündig in einem Feld aus. Ist die Länge der Zeichenfolge s kleiner als 8, werden von rechts her Leerzeichen ergänzt (linksbündige Prokrustes-Zuweisung, vgl. auch »LSet-Anweisung«, S. 80). Const cFeldBreite = 8 Print Left(s + Space(cFeldBreite), cFeldBreite) Verwandte Befehle
................................................... Verwa ndte Befehle
Right, RightB, Mid, MidB, LTrim, RTrim, Trim
Len- und LenB- Funktion Function Len(sUnicode As String) As Long Function LenB(Var As Type) As Long Beschreibung
................................................... Bes c hreibung
Die Funktion Len liefert die Anzahl der Zeichen in der Zeichenfolge sUnicode. Die leere Zeichenfolge Null hat die Länge 0. Die Funktion LenB gibt die Anzahl der Bytes zurück, die zum Speichern des Werts von Var benötigt werden.
79
Funktionen und Anweisungen für Zeichenfolgen
Die Funktion LCase liefert eine Kopie der im Parameter s übergebenen Zeichenfolge in Kleinschreibung zurück. Null wird wieder als Null zurückgegeben.
Funktionen und Anweisungen für Zeichenfolgen
Anwendung
................................................... Anwendung
Wie es die Beschreibung bereits vermuten lässt, verbirgt sich hinter der Funktion LenB so etwas wie der »Längenoperator« der Sprache Visual Basic, den Sie benutzen können, wann immer Sie wissen wollen, wie viele Bytes für die Repräsentation eines bestimmten Werts von einem bestimmten Datentyp erforderlich sind. Achtung jedoch, die Längenangaben von Zeichenfolgen enthalten nicht den Längenbedarf des Deskriptors (vgl. auch »Get-Anweisung«, S. 140). Auf Arrays lässt sich diese Funktion gar nicht anwenden. Beispiel
Funktionen und Anweisungen für Zeichenfolgen
................................................... Beis piel
Siehe Beispiel zu »Chr-, ChrB- und ChrW-Funktion« (S. 67) Verwandte Befehle
................................................... Verwa ndte Befehle
VarType
LSet- Anweisung LSet sString1 = sString2 LSet bdtVar = bdtWert Beschreibung
................................................... Bes c hreibung
Die Anweisung LSet nimmt eine linksbündige Prokrustes-Zuweisung für Zeichenfolgen und benutzerdefinierte Datentypen vor. Auf Zeichenfolgen angewendet, überträgt sie den Wert von sString2 linksbündig in die Variable sString1, ohne deren Länge anzupassen. Dabei wird der Wert von sString2 gegebenenfalls zurechtgestutzt oder um Leerzeichen ergänzt (vgl. Beispiel in »Left- und LeftB-Funktion«, S. 79). Auf benutzerdefinierte Datentypen angewendet, überträgt LSet den Wert bdtWert Byte-weise und linksbündig in die Variable bdtVar, ohne deren Länge anzupassen. Ist bdtWert kürzer als der Wert, den bdtVar erwartet, ergänzt die Anweisung Nullbytes, ist er länger, wird zurechtgestutzt. Anwendung
................................................... Anwendung
Der ursprüngliche Sinn dieser Anweisung war die Bereitstellung einer blockorientierten Zuweisungsoperation für zusammengesetzte bzw. benutzerdefinierte Datentypen. Solche Werte lassen sich im Rahmen einer gewöhnlichen Let-Zuweisung en bloc nämlich nicht mehr übertragen. Ein sehr wichtiger Einsatzbereich von LSet ist daher die Überführung traditionell via Get oder Input gelesener Datensätze in Datensatzvariablen mit benutzerdefinierter Struktur bzw. die Vorbereitung von Datensatzvariablen für die Put-Operation. Aus diesem Grund ist die Typüberprüfung, die Visual Basic LSet angedeihen lässt, nicht sehr rigide. Warnung und Tipp
................................................... Wa rnung
»Nachtigall, ick hör dir trapsen«. Ja, mit LSet lassen sich auf dem Umweg über benutzerdefinierte Datentypen implizite Typumwandlungen ausführen, wie das Beispiel zu diesem Befehl zeigt. Von einer ernsthaften Anwendung im Zusammenhang mit disparaten Datentypen, die über eine Analyse von Repräsentationen bestimmter Datentypen oder Werte hinausgeht, ist jedoch abzuraten, da für gewöhnlich nicht klar ist, welche Speicherausrichtung die Felder benutzerdefinierter Datentypen vom Compiler erhalten.
80
LTrim- Funktion
Beispiel
................................................... Beis piel
Wer die Speicherdarstellung eines spezifischen Werts zu Gesicht bekommen will, muss schon ein wenig tricksen, um der rigiden Typkontrolle von Visual Basic zu entrinnen. Der folgende Code benutzt die als Beispiel zu Chr vorgestellte Funktion StrToHex, um die hexadezimale Repräsentation eines Double-Werts nach einer LSet-Zuweisung zu erhalten. Beachten Sie bei der Interpretation der Hexadezimaldarstellung, dass Intel-Prozessoren mit verkehrter Bytereihenfolge arbeiten und in einem aus mehreren Bytes bestehenden Wert das erste Byte das niederwertige Byte ist. erster benutzerdefinierter Typ
zweiter benutzerdefinierter Typ
bdt2 wie sieht die Repräsentation dieses Werts aus? Zuweisung unter Umgehung der Typprüfung Ausgabe der hexadezimalen Darstellung
Verwandte Befehle
................................................... Verwa ndte Befehle
Get, Let, Put, RSet Verwandte Themen
................................................... Verwandte Them en
Typumwandlung (S. 57)
LTrim- Funktion Function LTrim(s As String) As String Beschreibung
................................................... Bes c hreibung
Die Funktion liefert eine Kopie der Zeichenfolge s zurück, die keine führenden Leerzeichen mehr enthält. Beispiel
................................................... Beis piel
Dim i As Integer i = 121 Print LTrim(Str(i))
' schneidet Leerstelle für Vorzeichen ab
Verwandte Befehle
................................................... Verwa ndte Befehle
RTrim, Trim
Mid- und MidB- Funktion Function Mid(sUnicode As String, lStart As Long _ [, lAnz As Long]) As String Function MidB(sANSI As String, lStart As Long _ [, lAnz As Long]) As String
81
Funktionen und Anweisungen für Zeichenfolgen
Private Type bdt1 ' f As Double End Type Private Type bdt2 ' s As String * 4 End Type Private Sub Form_Load() AutoRedraw = True Dim bt1 As bdt1, bt2 As bt1.f = 2.234E-23 ' LSet bt2 = bt1 ' Print StrToHex(bt2.s) ' End Sub
Funktionen und Anweisungen für Zeichenfolgen
Beschreibung
................................................... Bes c hreibung
Die Funktion Mid liefert lAnz Zeichen der Zeichenfolge sUnicode ab der Position lStart als Unicode-Zeichenfolge zurück. In gleicher Weise liefert die MidB-Funktion lAnz Bytes der Zeichenfolge sANSI ab der Byteposition lStart als Zeichenfolge. Fehlt der optionale Parameter lAnz, liefern beide Funktionen den gesamten Rest der Zeichenfolge ab der Position lStart. Beispiel
................................................... Beis piel
Funktionen und Anweisungen für Zeichenfolgen
Vgl. die Beispiele in »Chr-, ChrB- und ChrW-Funktion« (S. 67) und »Asc-, AscB- und AscW-Funktion« (S. 66).
Mid- und MidB- Anweisung Mid s, lStart [, lAnz] = sErsatz MidB s, lStart [, lAnz] = sErsatz Beschreibung
................................................... Bes c hreibung
Die Mid-Zuweisung kopiert die ersten lAnz Zeichen der Zeichenfolge sErsatz in die Zeichenfolge s ab Position lStart. Ist lAnz größer als die Länge von sErsatz oder fehlt lAnz, wird die gesamte Zeichenfolge sErsatz kopiert. MidB unterscheidet sich von Mid nur darin, dass sie lAnz als Byteanzahl interpretiert und Byte-weise kopiert. Beispiel
................................................... Beis piel
Siehe Beispiel zu »Chr-, ChrB- und ChrW-Funktion« (S. 67) Verwandte Befehle
................................................... Verwa ndte Befehle
Left, LeftB, Right, RightB, LTrim, RTrim, Trim
MonthName- Funktion Function MonthName(lMonat As Long[, bAbk As Boolean = False]) As String Beschreibung
................................................... Bes c hreibung
Die Funktion MonthName liefert den zur Monatszahl lMonat gehörigen Monatsnamen als Zeichenfolge. Hat der optionale Parameter bAbk den Wert False oder fehlt er, gibt die Funktion den vollen Namen zurück, ansonsten seine Abkürzung (drei Buchstaben). Monatszahlen, die nicht zwischen 1 und 12 liegen, führen zu einem Laufzeitfehler. Beispiel
................................................... Beis piel
Print MonthName(Month(Date))
' Name aktueller Monat, ausgeschrieben
Verwandte Befehle
................................................... Verwa ndte Befehle
WeekdayName
Oct- Funktion Function Oct(vOctWert) As Double
82
Option Compare- Anweisung
Beschreibung
................................................... Bes c hreibung
Die Funktion Oct zählt wie Hex nur mittelbar zu den Typumwandlungsfunktionen. Während Hex die hexadezimale Notation seines als Ganzzahl interpretierten Arguments als Zeichenfolge liefert, liefert Oct dessen oktale Notation. Anwendung
................................................... Anwendung
Die Umwandlung in das oktale Zahlensystem mittels Oct findet heutzutage eher selten Anwendung. Sie ist ein Relikt aus vergangenen Zeiten, als das hexadezimale Zahlensystem im Computerbereich noch nicht die alles dominierende Rolle spielte.
................................................... Beis piel
Print Oct(77) Print Oct(&o1234)
' 64+8+5 = "115" ' Oktales Literal: "1234"
Verwandte Befehle
................................................... Verwa ndte Befehle
Hex
Option Compare- Anweisung Option Compare Binary Option Compare Text Beschreibung
................................................... Bes c hreibung
Die Anweisung Option Compare legt fest, welche Art von Zeichenfolgenvergleich bestimmte Zeichenfolgenfunktionen laut Vorgabe (das heißt ohne Angabe des jeweiligen optionalen Parameters) durchführen. Der Vergleichsart Binary liegt ein schlichter Vergleich der binären Repräsentation (zeichenweiser Unicode-Vergleich), der Vergleichsart Text dagegen die standardmäßige Sortierordnung der für die auf dem System geltende Landessprache zugrunde – ohne Unterscheidung der Groß- und Kleinschreibung. Diese Anweisung darf in einem Modul nicht mehrfach gegeben werden, das heißt, sie gilt immer für das gesamte Modul und muss auf Modulebene stehen. Fehlt die Anweisung, gilt für das jeweilige Modul die Voreinstellung Binary. Eine Option Compare-Anweisung wirkt sich auf die Zeichenfolgenoperatoren <, >, <=, >=, = und <> aus. Weiterhin kann sie sich bei Fehlen des entsprechenden optionalen Parameters auch auf die Funktionen: Filter, InStr, InStrB, InStrRev, Replace, Split, StrComp auswirken. Beispiel
................................................... Beis piel
Option Compare Text ... Print (StrComp("ö", "u", vbBinaryCompare) = -1) = ("ö" < "ü") ' Ausgabe: Falsch
aber Option Compare Binary ' Standardvergleichsart ... Print (StrComp("ö", "u", vbBinaryCompare) = -1) = ("ö" < "ü") ' Ausgabe: Wahr
83
Funktionen und Anweisungen für Zeichenfolgen
Beispiel
Funktionen und Anweisungen für Zeichenfolgen
Verwandte Befehle
................................................... Verwa ndte Befehle
Option Base, Option Explicit, Option Private
Funktionen und Anweisungen für Zeichenfolgen
Replace- Funktion Function Replace( _ sDurchsuchterString As String, _ sSuchString As String,_ sErsatzString As String,_ [lStart As Long = 1], _ [lAnz As Long = -1], [vgl As VbCompareMethode = vbBinaryCompare]) _ As Long Beschreibung
................................................... Bes c hreibung
Die Funktion Replace durchsucht die Zeichenfolge sDurchsuchterString ab der Zeichenposition lStart nach maximal lAnz Vorkommen der Zeichenfolge sSuchString und liefert eine Kopie von sDurchsuchterString ab der Zeichenposition lStart, in der all diese Vorkommen gegen die Zeichenfolge sErsatzString ersetzt sind. Fehlt der optionale Parameter lStart, beginnt die Kopie beim ersten Zeichen. Fehlt der Parameter lAnz, ersetzt die Funktion beliebig viele Vorkommen von sSuchString. Hat der optionale Parameter vgl den Wert vbBinaryCompare oder fehlt er, ist die Vergleichsoperation ein zeichenweiser Unicode-Vergleich; hat vgl den Wert vbTextCompare, legt die Funktion der Vergleichsoperation die standardmäßige Sortierordnung der für die auf dem System geltenden Landessprache zugrunde – ohne Unterscheidung der Groß- und Kleinschreibung. Hinweis
................................................... Hinweis Die mit Visual Basic 6.0 vorliegende Implementation dieser Funktion hat kein Problem damit, wenn Start größer ist als die Länge der durchsuchten Zeichenfolge; negative Werte sowie ein Wert von 0 führen dagegen zu einem Laufzeitfehler. Warnung
................................................... Wa rnung
Das Standardverhalten der Funktion bei fehlendem Parameter vgl kann sich durch eine Option Compare-Anweisung ändern. Beispiel
................................................... Beis piel
Dim s Print Print Print Print
As String Replace("12222223", Replace("12222223", Replace("12222223", Replace("abbbbbbc",
"22", "22", "22", "BB",
"3") "3", 2, 2) "3", 2 ,0) "C", 2, , vbTextCompare)
Vergleiche auch das Beispiel zu »Print #« (S. 151). Verwandte Befehle
................................................... Verwa ndte Befehle
Filter, InStr, StrComp
84
' ' ' '
"13333" "33223" "2222223" "CCCc"
Partion- Funktion
Partion- Funktion Function Partition(Number As Long, Start As Long, Stop As Long, _ Interval As Long) As String Beschreibung
................................................... Bes c hreibung
Anwendung
................................................... Anwendung
Der Einsatzbereich dieser ziemlich spezialisierten Funktion liegt hauptsächlich im Bereich der Datenbankprogrammierung, wo sie für die Berechnung der Datensatzgruppierung verwendbar ist. Beispiel
................................................... Beis piel
Print Print Print Print
Partition(1, 2, 100, 23) Partition(24, 2, 100, 23) Partition(25, 2, 100, 23) Partition(101, 2, 100, 23)
' ' ' '
Ausgabe: Ausgabe: Ausgabe: Ausgabe:
": 2" "2: 24" "25: 47" "101: "
Right- und RightB- Funktion Function Right(sUnicode As String, lZeichenAnz As Long) As String Function RightB(sANSI As String, lZeichenAnz As Long) As String Beschreibung
................................................... Bes c hreibung
Die Funktion Right liefert die letzten lZeichenAnz Zeichen der Zeichenfolge sUnicode als Unicode-Zeichenfolge und die Funktion RightB die letzten lZeichenAnz Bytes der Zeichenfolge sANSI als ANSI-Zeichenfolge. Ist der Wert von lZeichenAnz größer oder gleich der Länge der Zeichenfolge, gibt die Funktion die vollständige Zeichenfolge zurück. Mit 0 als Wert für lZeichenAnz gibt die Funktion die leere Zeichenfolge Null zurück. Beispiel
................................................... Beis piel
Die folgende Ausgabeanweisung gibt die Zeichenfolge s rechtsbündig in einem Feld aus. Ist die Länge der Zeichenfolge s kleiner als 8, werden von links her Leerzeichen ergänzt (rechtsbündige Prokrustes-Zuweisung, vgl. auch »RSet-Anweisung«, S. 86). Const cFeldBreite = 8 Print Right(Space(cFeldBreite) + s , cFeldBreite) Verwandte Befehle
................................................... Verwa ndte Befehle
Right, RightB, Mid, MidB, LTrim, RTrim, Trim
85
Funktionen und Anweisungen für Zeichenfolgen
Die Funktion Partition ordnet eine gegebene Zahl Number in das zugehörige Intervall einer Intervallfolge ein und liefert dann eine Beschreibung des Intervalls in der Form »UntereGrenze: ObereGrenze«. Die Intervallfolge ist über Start (untere Grenze des ersten Intervalls), Stop (obere Grenze des letzten Intervall) und Interval (Breite der einzelnen Intervalle in der Folge) definiert und enthält die halboffenen (nach oben offen) Intervalle, die sich durch wiederholte Addition der Intervalllänge zum Startwert ergeben. Ist Number kleiner als Start, beschreibt die Funktion das Intervall in der Form: »: UntereGrenze », ist Number größer als Stop, in der Form: »ObereGrenze: «.
Funktionen und Anweisungen für Zeichenfolgen
RSet- Anweisung RSet sString1 = sString2 Beschreibung
................................................... Bes c hreibung
Die Anweisung RSet nimmt eine rechtsbündige Prokrustes-Zuweisung für Zeichenfolgen vor, das heißt, sie kopiert den Wert von sString2 rechtsbündig in die Variable sString1, ohne deren Länge anzupassen. Dabei wird der Wert von sString2 gegebenenfalls vorne zurechtgestutzt oder um Leerzeichen ergänzt.
Funktionen und Anweisungen für Zeichenfolgen
Beispiel
................................................... Beis piel
Dim s As String * 20 RSet s = FormatCurrency(vWert, 2) Verwandte Befehle
................................................... Verwa ndte Befehle
Get, Let, LSet, Put
RTrim- Funktion Function RTrim(s As String) As String Beschreibung
................................................... Bes c hreibung
Die Funktion liefert eine Kopie der Zeichenfolge s zurück, die keine Leerzeichen am Ende enthält. Beispiel
................................................... Beis piel
Dim sFix As String * 100 Dim sVariable As String sFix = "Literaler Wert" ' Prokrustes-Zuweisung sVariable = sFix Print RTrim(sVariable) Verwandte Befehle
................................................... Verwa ndte Befehle
LTrim, Trim
Space- Funktion Function Space(lAnz As Long) As String Beschreibung
................................................... Bes c hreibung
Die Funktion Space liefert eine Unicode-Zeichenfolge der Länge lAnz, die mit Leerzeichen gefüllt ist. Beispiel
................................................... Beis piel
Siehe die Beispiele zu »Chr-, ChrB- und ChrW-Funktion« (S. 67), »Left- und LeftB-Funktion« (S. 79 ) und »Right- und RightB-Funktion« (S. 85). Verwandte Befehle
................................................... Verwa ndte Befehle
LTrim, RTrim
86
Split- Funktion
Split- Funktion Function Split(_ sKombination As String, _ [sTrennzeichen As String = " "], _ [lAnz As Long = -1], _ [vgl As vbCompareMethod = vbBinaryCompare]) _ As String() Beschreibung
................................................... Bes c hreibung
Warnung
................................................... Wa rnung
Das Standardverhalten der Funktion bei fehlendem Parameter vgl kann sich durch eine Option Compare-Anweisung ändern. Beispiel
................................................... Beis piel
cVornamen = "Audra, Petra, Jennifer, Maria, Magdalena" Dim sVornamen() As String sVornamen = Split(cVornamen, ", ") For i = 0 To UBound(sVornamen) ' zeilenweise ausgeben Print sVornamen(i) Next i Print cVornamen = Join(sVornamen, ", ") ' Ausgabe: Wahr Verwandte Befehle
................................................... Verwa ndte Befehle
Filter, Join
Str- Funktion Function Str(lZahl As Long) As String Function Str(dZahl As Double) As String Beschreibung
................................................... Bes c hreibung
Die Funktion Str liefert den Wert von lZahl bzw. dZahl als Unicode-Zeichenfolge im standardmäßigen amerikanischen Zahlenformat. Warnung
................................................... Wa rnung
Leider ist die Str-Funktion von Visual Basic 6.0 nicht fehlersicher implementiert und liefert im Zusammenhang mit der Print-Anweisung gerne mal den Fehler 480, wenn dZahl als literaler Wert angegeben wird. Da dieser Fehler danach selbst bei Angabe korrekter Argumente nicht mehr weg zu bekommen ist, hilft in diesem Fall nur ein Neustart von Visual Basic.
87
Funktionen und Anweisungen für Zeichenfolgen
Als Umkehrfunktion zu Join unterteilt die Funktion Split die Zeichenfolge sKombination in mehrere Zeichenfolgen und gibt diese als Zeichenfolgenarray zurück. Fehlt der optionale Parameter sTrennzeichen, wertet die Funktion standardmäßig jedes erscheinende Leerzeichen als Trennzeichen zwischen zwei Zeichenfolgen, ansonsten den Wert von sTrennzeichen. Fehlt der optionale Parameter lAnz, nimmt die Funktion so viele Unterteilungen vor, wie sie Trennzeichen findet, ansonsten nicht mehr als lAnz. Hat der optionale Parameter vgl den Wert vbBinaryCompare oder fehlt er, ist die Vergleichsoperation ein zeichenweiser Unicode-Vergleich; hat vgl den Wert vbTextCompare, legt die Funktion der Vergleichsoperation die standardmäßige Sortierordnung der für die auf dem System geltenden Landessprache zugrunde – ohne Unterscheidung der Groß- und Kleinschreibung.
Funktionen und Anweisungen für Zeichenfolgen
Wird die Funktion mit einer literalen Zeichenfolge bzw. einer Konstante als Argument aufgerufen, verwandelt Visual Basic die Zeichenfolge zunächst implizit in eine Zahl vom Typ Long oder Double, um mit Str daraus wieder eine Zeichenfolge zu machen. In Systemen mit deutschem Zahlenformat (Ländereinstellungen) passiert dabei dummerweise Folgendes: Print Str("1234,1234") Print Str(Str("1234,1234"))
' Ausgabe: 1234,1234 ' Ausgabe: 12341234
Fehler, die aus dieser Ungereimtheit heraus entstehen, sind meist recht schwer zu finden (vgl. auch Variableninitialisierung, S. 168).
Funktionen und Anweisungen für Zeichenfolgen
Beispiel
................................................... Beis piel
Der folgende Code geht von einem Formular mit zwei Textfeldern namens Text1 und Text2 aus und übersetzt Eingaben in die Standardnotation. Private Sub Text1_Change() Text2 = Str(Val(Text1)) End Sub Verwandte Befehle
................................................... Verwa ndte Befehle
Val, Round
StrComp- Funktion Function StrComp( _ s1 As String, _ s2 As String, _ [vgl As vbCompareMethod = vbBinaryCompare]) _ As Integer Beschreibung
................................................... Bes c hreibung
Die Funktion StrComp unterzieht die beiden Zeichenfolgen s1 und s2 einer Vergleichsoperation und unterscheidet dabei die vier in der folgenden Tabelle aufgelisteten Fälle. Hat der optionale Parameter vgl den Wert vbBinaryCompare oder fehlt er, ist die Vergleichsoperation ein zeichenweiser Unicode-Vergleich; hat vgl den Wert vbTextCompare, legt die Funktion der Vergleichsoperation die standardmäßige Sortierordnung der für die auf dem System geltenden Landessprache zugrunde – ohne Unterscheidung der Groß- und Kleinschreibung. Funktionswert
Ergebnis des Vergleichs (hängt von Vergleichsoperation ab)
-1
s1 ist »kleiner als« s2
1
s1 ist »größer als« s2
0
s1 ist »gleich« s2
Null
Einer der beiden Vergleichswerte ist Null
Warnung
................................................... Wa rnung
Das Standardverhalten der Funktion bei fehlendem Parameter vgl kann sich durch eine Option Compare-Anweisung ändern.
88
StrConv- Funktion
Beispiel
................................................... Beis piel
Print StrComp("Blöd", "Blut") ' Ausgabe: 1 Print StrComp("Blöd", "Blut", vbTextCompare) ' Ausgabe: -1 Print StrComp("BlOnD", "blond", vbTextCompare) ' Ausgabe: 0 Verwandte Befehle
................................................... Verwa ndte Befehle
Option Compare, <, > =
Function StrConv( _ s As String, _ KonvSchema As VbStrConv, _ [lGebietsschemaID As Long) _ As String Beschreibung
................................................... Bes c hreibung
Die Funktion StrComp liefert eine konvertierte Variante der Zeichenfolge s. Der Wert von KonvSchema (siehe Tabelle) bestimmt, welches Konversionsschema die Funktion der Konversion zugrunde legt. Bestimmte Konversionen sind allerdings nur möglich, wenn im System ein entsprechendes Gebietsschema eingestellt oder im optionalen Parameter lGebietsschemaID spezifiziert ist. Wert für KonvSchema
Verwendetes Konversionsschema
vbUpperCase (1)
Umwandlung in Großschreibung
vbLowerCase (2)
Umwandlung in Kleinschreibung
vbProperCase (3)
Umwandlung in gemischte Groß-/Kleinschreibung, Wortanfang groß, Rest klein
vbWide (4)
Umwandlung von Ein-Byte-Zeichen in Zwei-Byte-Zeichen (DBCS)
vbNarrow (8)
Umwandlung von Zwei-Byte-Zeichen (DBCS) in Ein-Byte-Zeichen
vbKatakana (16)
Umwandlung Hiragana-Zeichen in Katagana-Zeichen
vbHiragana (32)
Umwandlung Katagana-Zeichen in Hiragana-Zeichen
vbUnicode (64)
Umwandlung von systemspezifischem ANSI-Zeichensatz in Unicode unter Verwendung der Zeichenumsetzungstabelle des Systems
vbFromUnicode (128)
Umwandlung von Unicode in systemspezifischen ANSI-Zeichensatz unter Verwendung der Zeichenumsetzungstabelle des Systems
Hinweis
................................................... Hinweis Sie können die in der Tabelle genannten Konstanten für KonvSchema auch kombinieren. Allerdings hängen die sinnvollen Kombinationsmöglichkeiten stark vom geltenden Gebietsschema ab. Der Wert vbUnicode + vbProperCase bewirkt beispielsweise die Umwandlung einer ANSIZeichenfolge in Unicode mit gemischter Groß-/Kleinschreibung. Folgende Zeichen wertet die Funktion als Trennzeichen zwischen Wörtern: Chr(0), Chr(9), Chr(10), Chr(11), Chr(12), Chr(13), Chr(32) – das gilt aber nicht für alle Gebietsschemata.
89
Funktionen und Anweisungen für Zeichenfolgen
StrConv- Funktion
Funktionen und Anweisungen für Zeichenfolgen
Beispiel
................................................... Beis piel
LSET s = StrConv(s, vbFromUnicode) ' ANSI-String für Ausgabe vorbereiten Print StrConv(s1, vbUnicode + vbProperCase) Verwandte Befehle
................................................... Verwa ndte Befehle
Chr, CStr
Funktionen und Anweisungen für Zeichenfolgen
String- Funktion Function String(lAnz As Long, iANSI As Integer) As String Function String(lAnz As Long, sVorgabe As String) As String Beschreibung
................................................... Bes c hreibung
Die Funktion String gibt es in zwei unterschiedlichen Varianten. Beide Varianten liefern eine Unicode-Zeichenfolge der Länge lAnz, die mit einem einzelnen Zeichen aufgefüllt ist. Für die erste Variante wird das Zeichen als ANSI-Code iANSI spezifiziert. Ist iANSI größer als 255, bildet die Funktion den Wert mittels einer Restwertdivision in das Intervall zwischen 0 und 255 ab. Die zweite Variante von String arbeitet mit beliebigen Unicode-Zeichen. In dieser Syntax verwendet die Funktion das erste Unicode-Zeichen der Zeichenfolge sVorgabe. Beispiel
................................................... Beis piel
Print StrToHex(String(2, 300)) ' Ausgabe: "2C 00 2C 00" Print StrToHex(String(2, ChrW(300))) ' Ausgabe: "2C 01 2C 01" Verwandte Befehle
................................................... Verwa ndte Befehle
LTrim, RTrim
StrReverse- Funktion Function StrReverse(s As String) As String Beschreibung
................................................... Bes c hreibung
Die Funktion StrReverse liefert eine Zeichenfolge, in der alle Zeichen der Zeichenfolge s in umgekehrter Reihenfolge enthalten sind. Bei Aufruf mit einer leeren Zeichenfolge ist das Ergebnis des Funktionsaufrufs gleichfalls eine leere Zeichenfolge. Ein Aufruf der Funktion mit Null führt dagegen zu einem Laufzeitfehler. Beispiel
................................................... Beis piel
Print StrReverse("ABCD")
' Ausgabe: "DCBA"
Trim- Funktion Function Trim(s As String) As String Beschreibung
................................................... Bes c hreibung
Die Funktion liefert eine Kopie der Zeichenfolge s zurück, die weder am Anfang noch am Ende Leerzeichen enthält.
90
UCase- Funktion
Beispiel
................................................... Beis piel
Print RTrim(LTrim(" Test ")) = Trim(" Test ") ' Gibt "Wahr" aus Verwandte Befehle
................................................... Verwa ndte Befehle
LTrim, RTrim
UCase- Funktion Function UCase(s As String) As String
................................................... Bes c hreibung
Die Funktion UCase liefert eine Kopie der im Parameter s übergebenen Zeichenfolge in Großschreibung zurück. Die leere Zeichenfolge Null wird wieder als Null zurückgegeben. Beispiel
................................................... Beis piel
If UCase(sTest1) = UCase(sTest2) Then
' Schreibweise egal
Verwandte Befehle
................................................... Verwa ndte Befehle
LCase
Val- Funktion Function Val(sZahl As String) As Double Beschreibung
................................................... Bes c hreibung
Die Funktion Val ist die Umkehrfunktion zu Str. Sie interpretiert die Zeichenfolge sZahl als Zahl in Standardnotation (mit Dezimalpunkt, nicht mit Dezimalkomma, da die Funktion keine Ländereinstellungen berücksichtigt) und liefert den Wert dieser Zahl. Anwendung
................................................... Anwendung
Val liefert auch einen Zahlenwert als Ergebnis, wenn sZahl mit einer Ziffernfolge beginnt und danach Zeichen folgen, die nichts mehr mit der Zahlendarstellung zu tun haben – so macht Val aus »12.34 Abfahrt Hamburg HBF« den Wert 12.34, aus »12,34 Abfahrt Hamburg HBF« jedoch 12. Um festzustellen, ob ein Wert vollständig als Zahl ausgewertet werden kann, steht die Boolean-Funktion IsNumeric zur Verfügung – sie liefert für beide Beispielzeichenfolgen False. Warnung
................................................... Wa rnung
Anders als die anderen Zeichenfolgenfunktionen arbeitet Val nicht mit der länderspezifischen Zahlendarstellung und will einen Punkt als Dezimalsymbol sehen. Dennoch ergeben sich im Zusammenspiel mit Str »seltsame Effekte«: Print Print Print Print
Val("10,1") Val("10.1") Str(10.1) Val(Str(10.1))
' ' ' '
Ausgabe: Ausgabe: Ausgabe: Ausgabe:
10 10,1 "10,1" 10,1 (seltsam,nicht?)
91
Funktionen und Anweisungen für Zeichenfolgen
Beschreibung
Mathematische und finanzmathematische Funktionen und Anweisungen
Beispiel
................................................... Beis piel
Private Sub Text1_Change() Text2.Text = Sqr(Val(Text1.Text)) End Sub Verwandte Befehle
................................................... Verwa ndte Befehle
Mathematische und finanzmathematische Funktionen und Anweisungen
LCase
WeekdayName- Funktion Function WeekdayName( _ lTag As Long, _ [bAbk As Boolean = False], _ [FirstDayOfWeek As VbFirstDayOfWeek = vbUseSystemDayOfWeek]) _ As String Beschreibung
................................................... Bes c hreibung
Die Funktion WeekdayName liefert den zur Tageszahl lTag gehörigen Wochentag als Zeichenfolge. Hat der optionale Parameter bAbk den Wert False oder fehlt er, gibt die Funktion den vollen Namen zurück, ansonsten seine Abkürzung (zwei Buchstaben). Über den optionalen Parameter FirstDayOfWeek lässt sich zudem vorgeben, mit welchem Wochentag die Zählung beginnen soll. Fehlt er, beginnt die Zählung mit dem im System festgelegten Tag, und das ist der Montag. Tageszahlen, die nicht zwischen 1 und 7 liegen, führen zu einem Laufzeitfehler. Beispiel
................................................... Beis piel
Print WeekdayName(Weekday(Date))
' aktueller Wochentag ausgeschrieben
Verwandte Befehle
................................................... Verwa ndte Befehle
MonthName
Mathematische und finanzmathematische Funktionen und Anweisungen Wenn bei den Zeichenfolgen zuvor die Rede davon war, dass die Manipulation derselben schon immer eine der ureigensten Domänen der Programmiersprache Basic war, dann sollte das nicht etwa heißen, dass die Sprache in puncto Mathematik wenig zu bieten habe. Auch für die Formulierung mathematischer und numerischer Problematiken war die Sprache für sich genommen immer schon recht reichhaltig ausgestattet. Insbesondere bot die weitgehend interaktive Programmentwicklung eine enorme Flexibilität bei der Programmierung von On-the-fly-Anwendungen für die Lösung minder komplexer Problematiken, die nicht auf das überlegene Laufzeitverhalten von Compilersprachen angewiesen waren. Als problematisch erwiesen sich allerdings die fehlenden Mittel für eine geeignete Modularisierung und eine Anbindung an bereits bestehende mathematische Bibliotheken. Was die Modularisierbarkeit von Code betrifft, so hat sich Basic mit Visual Basic inzwischen zu einer wahrlich vorbildlichen Programmiersprache entwickelt. Die Möglichkeiten der Anbindung an bestehende mathematische Bibliotheken sind aber nach wie vor eher als lau einzustufen, da solche Bibliotheken (sofern sind nicht in Visual Basic selbst, als ActiveX-Bibliotheken
92
Mathematische und finanzmathematische Funktionen und Anweisungen
Bezeichner
Kurzbeschreibung
Abs
Liefert den Absolutwert (vorzeichenlosen Wert) einer Zahl
Atn
Liefert den Arcustangens einer Zahl als Winkel im Bogenmaß
Cos
Liefert den Cosinus zu einem Winkel im Bogenmaß
DDB
Liefert den Abschreibungswert eines Vermögenswerts über einen bestimmten Zeitraum bei geometrisch degressiver Abschreibung
Exp
Liefert den Wert der Exponentialfunktion (mit Basis e) zu einer Zahl
Fix
Liefert den Vorkommaanteil einer Dezimalzahl als Ganzzahl
FV
Liefert den künftigen Wert einer Annuität (Ansparung oder Kredit) bei konstanter Zahlung, konstantem Zins und fester Laufzeit
Int
Liefert den nächsten ganzzahligen Wert kleiner oder gleich einer Zahl
IPmt
Liefert den Zinsanteil für eine bestimmte Periode bei Abzahlung oder Ansparung mit konstanten Raten und konstantem Zinssatz
IRR
Liefert den internen Ertragssatz für eine Folge regelmäßiger Ein- und Auszahlungen
Log
Liefert den natürlichen Logarithmus einer Zahl
MIRR
Liefert den modifizierten internen Ertragssatz für eine Folge regelmäßiger, mit unterschiedlichen Zinssätzen behafteten Ein- und Auszahlungen
NPer
Liefert die Anzahl der Zeiträume für eine Annuität (Ansparung oder Kredit) bei konstanter Zahlung und konstantem Zins
NPV
Liefert den Nettobarwert einer Investition bei regelmäßigen Aus- und Einzahlungen und konstantem Diskontsatz
Pmt
Liefert den Auszahlungswert (Höhe der Rate bei Kredit) für eine Annuität mit einer festen Anzahl von Zeiträumen, konstanter Zahlung und konstantem Zins
PPmt
Liefert den Kapitalanteil (bei Kredit: Tilgung) eines bestimmten Zeitraums für eine Annuität (Ansparung oder Kredit) mit einer festen Anzahl von Zeiträumen, konstanter Zahlung und konstantem Zins
Mathematische und finanzmathematische Funktionen und Anweisungen
93
Mathematische und finanzmathematische Funktionen und Anweisungen
oder in C implementiert sind, und das sind Bibliotheken aus dem Bereich der Numerischen Mathematik auch heutzutage noch eher nicht, weil da immer noch FORTRAN herumgeistert) von ihrem Aufrufmodell her meist nicht kompatibel sind. Auch wenn es Visual Basic wohl nicht mehr zur lingua franca der numerischen Mathematik bringen wird, ihre mathematische und auch finanzmathematische Grundausstattung ist reichhaltig genug für die meisten Fragestellungen im naturwissenschaftlichen, wirtschaftlichen und informationstechnologischen Bereich, und es spricht inzwischen absolut nichts mehr gegen die Entwicklung eigener mathematischer Bibliotheken als solide Basis für künftige Projekte. Bei einer solchen Bibliothek kann es sich um eine Klassenbibliothek für abstrakte Datentypen handeln, so etwa für die Vektor- und Matrizenrechnung, oder um ein gewöhnliches Bibliotheksmodul, das spezifische Funktionen und Prozeduren etwa für den Bereich Statistik bereitstellt. Nichtsdestoweniger bleibt jede eigene Implementation von Datentypen und Operationen auf das mathematische Inventar der Sprache selbst angewiesen. Die folgende Tabelle gibt einen Überblick über die mathematischen und finanzmathematischen Funktionen und Anweisungen von Visual Basic.
Mathematische und finanzmathematische Funktionen und Anweisungen
Mathematische und finanzmathematische Funktionen und Anweisungen
Bezeichner
Kurzbeschreibung
PV
Liefert den Barwert für eine Annuität (Ansparung oder Kredit) bei konstanter Zahlung, konstantem Zins und fester Laufzeit
Randomize
Initialisiert den Zufallsgenerator mit einem Startwert
Rate
Liefert den Zinssatz zu einer Annuität (Anzahlung oder Kredit) mit einer festen Anzahl von Zeiträumen, konstanter Zahlung und konstantem Zins
Rnd
Liefert eine gleich verteilte Zufallszahl zwischen 0 und 1
Round
Liefert den Wert einer Zahl auf eine bestimmte Dezimalstelle gerundet
Sgn
Liefert das Vorzeichen eines Werts als Faktor (-1, 0 oder 1)
Sin
Liefert den Sinus zu einem Winkel im Bogenmaß
SLN
Liefert den periodischen Abschreibungswert eines Vermögenswerts bei linearer Abschreibung über einen bestimmten Zeitraum
Sqr
Liefert die Quadratwurzel eines positiven Zahlenwerts
SYD
Liefert den Abschreibungswert eines Vermögenswerts für eine bestimmte Periode nach dem Modell der Jahressummengewichtung
Tan
Liefert den Tangens zu einem Winkel im Bogenmaß
Mathematische und finanzmathematische Funktionen und Anweisungen Warnung
................................................... Wa rnung
Die mit Visual Basic 6 (SP3) vorliegende Implementation der finanzmathematischen Funktionen ist alles andere als fehlersicher. So erhält man in nicht klar zu bestimmenden Situationen oder bei Angabe falscher Parameterwerte gerne mal den lästigen Laufzeitfehler 480, »Anwendungsoder objektdefinierter Fehler«, den man im Lauf der aktuellen Visual-Basic-Sitzung nicht mehr los wird – selbst bei Angabe korrekter Parameterwerte. In diesem Fall hilft nur noch ein Neustart von Visual Basic. Tipp
................................................... Tipp
Aufgrund der internen Darstellung gewöhnlicher Zahlenwerte im hexadezimalen Format kommt es bei verschiedenen Funktionen zu Rundungsfehlern, die gerade im finanzmathematischen Bereich, aber auch anderswo für peinliche Fehler (so genannte »Pfennigfuchser-Fehler«) im Bereich der letzten Nachkommastelle sorgen können. Sie vermeiden solche Fehler, indem Sie für Währungsbeträge konsequent den Datentyp Currency und für dezimale Berechnungen mit nachfolgenden Rundungsoperationen den Datentyp Decimal einsetzen. Gegebenenfalls erforderliche Typumwandlungen nimmt Visual Basic dann implizit vor.
Abs- Funktion Function Abs(dZahl As Double) As Double Beschreibung
................................................... Bes c hreibung
Die Funktion Abs liefert den Absolutwert zu der Zahl dZahl, das heißt den Zahlenwert mit positivem Vorzeichen.
94
Atn- Funktion
Beispiel
................................................... Beis piel
DeltaX = Abs(MouseX – X) DeltaY = Abs(MouseY – Y) Verwandte Befehle
................................................... Verwa ndte Befehle
Sgn
Atn- Funktion Beschreibung
................................................... Bes c hreibung
Die zu den trigonometrischen Umkehrfunktionen zählende Arcustangensfunktion Atn ist die Umkehrfunktion der Tangensfunktion (Tan). Sie liefert den Arcustangens zu der Zahl d als Winkel im Bogenmaß. Das Ergebnis der Funktion liegt immer im offenen Intervall ]-π/2; π/2[. Für die Umwandlung in Grad ist der Wert mit 180/π = 57,2957795130823 zu multiplizieren. Anwendung
................................................... Anwendung
Diese Funktion berechnet den Winkel zwischen Hypothenuse und Ankathete eines rechtwinkligen Dreiecks, wenn deren Längen bekannt sind, bzw. den zu einem Tangenswert (Steigung) gehörigen Winkel. Tipp
................................................... Tipp
Da Visual Basic die Zahl π nicht als Konstante kennt, muss ein anderer Weg gefunden werden, an ihren Wert zu kommen. Ein sehr eleganter Weg ist die Berechnung des Werts über die Funktion Atn. Dim Pi As Double Pi = Atn(1) * 4
' 3.14159265358979
Beispiel
................................................... Beis piel
Print Atn(1)
' Ausgabe: 0,785398163397448
Verwandte Befehle
................................................... Verwa ndte Befehle
Cos, Sin, Tan
Cos- Funktion Function Cos(dWinkel As Double) As Double Beschreibung
................................................... Bes c hreibung
Die zu den trigonometrischen Funktionen zählende Cosinusfunktion Cos liefert den Cosinus zu dem im Bogenmaß angegebenen Winkel dWinkel. Das Ergebnis der Funktion liegt immer im Intervall [-1; 1]. Für die Umwandlung eines Gradwerts in das Bogenmaß ist der Wert mit π/180 = 1,74532925199433E-02 zu multiplizieren. Anwendung
................................................... Anwendung
Diese Funktion berechnet aus dem Winkel zwischen Ankathete und Hypothenuse die Ankathete eines rechtwinkligen Dreiecks mit Hypothenuse 1.
95
Mathematische und finanzmathematische Funktionen und Anweisungen
Function Atn(d As Double) As Double
Mathematische und finanzmathematische Funktionen und Anweisungen
Beispiel
................................................... Beis piel
Dim Pi As Double Pi = Atn(1) * 4 Print Cos(Pi / 3)
' 3.14159265358979 ' Ausgabe: 0,5
Verwandte Befehle
................................................... Verwa ndte Befehle
Mathematische und finanzmathematische Funktionen und Anweisungen
Atn, Sin, Tan
DDB- Funktion Function DDB( _ dKosten As Double, _ dRestwert As Double, _ dNutzungsDauer As Double, _ dPeriode As Double, _ [vFaktor As Variant = 2]) _ As Double Beschreibung
................................................... Bes c hreibung
Die zu den finanzmathematischen Funktionen zählende Funktion DDB ermöglicht die Berechnung der Abschreibung einer Investition dKosten mit dem Restwert dRestwert nach der angenommenen Nutzungsdauer dNutzungsDauer für eine bestimmte Periode dPeriode. Fehlt der optionale Parameter vFaktor oder hat er den Wert 2, berechnet die Funktion den Abschreibungswert nach der geometrisch degressiven Methode, ansonsten mit der gleichen Formel, jedoch mit vFaktor als Degressionsfaktor. Anwendung
................................................... Anwendung
dNutzungsDauer und dPeriode muss die gleiche Einheit (Monate oder Jahre) zugrunde liegen. Diese Funktion berücksichtigt den Parameter dRestwert nur insofern, als sie 0 zurückliefert, wenn der Restwert für eine Periode bereits erreicht ist. Mit anderen Worten, die Funktion rechnet nicht mit der üblichen Formel ( Wert(Periode) – Restwert ) ⋅ Faktor DDB = -----------------------------------------------------------------------------------------------Nutzungszeit sondern nach der schlichteren Formel Wert(Periode) ⋅ Faktor DDB = ------------------------------------------------------------Nutzungszeit Das kann zwar verwirren, gestattet aber einen Test, nach wie vielen Perioden eine Investition einen bestimmten Restwert erreicht hat. Beispiel
................................................... Beis piel
Das folgende Codefragment zeigt, wie sich ein Wert von 10.000 € bei Abschreibung nach der geometrisch degressiven Methode und einer Laufzeit von 10 Jahren entwickelt: Laufzeit = 10 AnschaffungsWert = 10000 RestWert = 2000 For Per = 1 to Laufzeit – 1 Print DDB(AnschaffungsWert, RestWert, Laufzeit, Per) Next
96
Exp- Funktion
Verwandte Befehle
................................................... Verwa ndte Befehle
SLN, SYD
Exp- Funktion Function Exp(dZahl As Double) As Double Beschreibung
................................................... Bes c hreibung
Beispiel
................................................... Beis piel
Print Exp(1) Print Exp(Log(10))
' Ausgabe: 2,71828182845905 ' Ausgabe: 10
Verwandte Befehle
................................................... Verwa ndte Befehle
Log
Fix- Funktion Function Fix(dZahl As NumTyp) As NumTyp Beschreibung
................................................... Bes c hreibung
Die Rundungsfunktion Fix liefert den Ganzzahlanteil von dZahl, indem sie alle Nachkommastellen abschneidet. Sie weist damit die gleiche Funktionalität wie CInt auf. Im Gegensatz zu CInt richtet die Funktion den Datentyp des Rückgabewerts aber nach dem Datentyp des Parameters. Warnung
................................................... Wa rnung
Wie das Beispiel zeigt, ist die Dezimalrechnung mit den Datentypen Single und Double nicht immer exakt. Das liegt daran, dass die Repräsentation dieser Werte intern im hexadezimalen bzw. echten binären Zahlenformat erfolgt und viele endliche Dezimalbrüche im binären Zahlensystem nur als unendliche Binärbrüche darstellbar sind. Wenn Sie Exaktheit benötigen, müssen Sie mit einem der Datentypen Currency oder Decimal rechnen! Beispiel
................................................... Beis piel
a = 27.003 Print Fix(a) Print (a – Fix(a))
' Ausgabe: 27 ' Ausgabe: 3,00000000000011E-03
Dim b As Variant b = CDec(a) Print Fix(b) Print (b – Fix(b))
' Ausgabe: 27 ' Ausgabe: 0,003
Verwandte Befehle
................................................... Verwa ndte Befehle
CInt, Int, Round
97
Mathematische und finanzmathematische Funktionen und Anweisungen
Die Wachstumsfunktion Exp ist die Umkehrfunktion zur natürlichen Logarithmusfunktion Log. Sie liefert den Wert der Eulerschen Konstante (2,71828182845905) hoch dZahl.
Mathematische und finanzmathematische Funktionen und Anweisungen
FV- Funktion Function FV( _ dZins As Double, _ dLaufzeit As Double, _ dRate As Double, _ [vJetztWert], _ [vTermin]) _ As Double
Mathematische und finanzmathematische Funktionen und Anweisungen
Beschreibung
................................................... Bes c hreibung
Die zu den finanzmathematischen Funktionen zählende Funktion FV liefert den zukünftigen Wert einer Annuität mit einer Laufzeit von dLaufzeit Zahlungsperioden, konstantem Zins dZins und der Rate dRate. Der optionale Parameter vJetztWert ermöglicht die Angabe eines Ausgangswerts bei bereits erfolgter Ansparung. Fehlt der optionale Parameter vTermin oder ist er 0, geht die Funktion davon aus, dass die Ratenzahlung jeweils zu Ende einer Periode erfolgt; ist der Wert dagegen 1, geht die Funktion davon aus, dass die Ratenzahlung jeweils zu Beginn einer Periode erfolgt. Anwendung
................................................... Anwendung
Beachten Sie, dass die Werte für die Parameter dZins und dLaufzeit auf denselben Einheitszeitraum (meist Monat oder Jahr) bezogen sein müssen. Für alle finanzmathematischen Funktionen gilt, dass Zahlungsausgänge durch negative Zahlen und Zahlungseingänge durch positive Zahlen dargestellt werden. Die Funktion lässt sich sowohl zur Berechnung von Krediten als auch von Ansparungen verwenden, wird aber vorzugsweise für die Kreditberechnung eingesetzt, da der zukünftige Wert bei Ansparungen meist einen Ausgangswert darstellt. Bei der Kreditberechnung sind dRate und dJetztWert als negative Werte anzugeben, bei der Ansparberechnung als positive Werte. Tipp
................................................... Tipp
Die Funktionen PV und FV liefern mit vertauschten Vorzeichen für dRate und dJetztWert die gleichen Ergebnisse. Beispiel
................................................... Beis piel
Das folgende Codefragment zeigt, wie sich eine Ansparung mit einer monatlichen Rate von 100 € und einem Zinssatz von 10% p.a. mit und ohne eine Starteinlage von 10.000 € über zehn Jahre hin entwickelt: Laufzeit = 10 * 12 MonatsRate = 100 Zins = 0.1 / 12 Starteinlage = 10000 Print FV(Zins, Monatsrate, Laufzeit) ' -18619,84 Print FV(Zins, Monatsrate, Laufzeit, Starteinlage) ' -41550,28
Das gleiche als Kredit: Laufzeit = 10 * 12 MonatsRate = -100 Zins = 0.1 / 12 Starteinlage = -10000
98
Int- Funktion
Print FV(Zins, Monatsrate, Laufzeit) Print FV(Zins, Monatsrate, Laufzeit, Starteinlage)
' 8120,14 ' 12481,15
Verwandte Befehle
................................................... Verwa ndte Befehle
IPmt, IRR, NPer, NPV, Pmt, PPmt, PV
Int- Funktion Function Int(dZahl As NumTyp) As NumTyp Beschreibung
Die Rundungsfunktion Int liefert die nächste Ganzzahl kleiner gleich dZahl. Im Gegensatz zu Fix schneidet die Funktion nicht nur die Nachkommastellen ab, sondern rundet echt. Die Funktion richtet den Datentyp des Rückgabewerts nach dem Datentyp des Parameters. Warnung
................................................... Wa rnung
Wie das Beispiel zeigt, ist die Dezimalrechnung mit den Datentypen Single und Double nicht immer exakt. Das liegt daran, dass die Repräsentation dieser Werte intern im hexadezimalen bzw. echten binären Zahlenformat erfolgt und viele endliche Dezimalbrüche im binären Zahlensystem nur als unendliche Binärbrüche darstellbar sind. Wenn Sie Exaktheit benötigen, müssen Sie mit einem der Datentypen Currency oder Decimal rechnen. Beispiel
................................................... Beis piel
a = -27.002 Print Int(a) Print (a – Int(a))
' Ausgabe: -28 ' Ausgabe: 0,998000000000001
Dim b As Variant b = CDec(a) Print Int(b) Print (b – Int(b))
' Ausgabe: -28 ' Ausgabe: 0,998
Verwandte Befehle
................................................... Verwa ndte Befehle
CInt, Fix, Round
IPmt- Funktion Function IPmt( _ dZins As Double, _ dPeriode As Double, _ dLaufzeit As Double, _ dBarwert As Double, _ [vZukünftigerWert], _ [vTermin]) _ As Double Beschreibung
................................................... Bes c hreibung
Die zu den finanzmathematischen Funktionen zählende Funktion IPmt (engl.: Interest Payment) liefert den Zinsanteil in der Periode dPeriode zu einer Annuität mit einer Laufzeit von dLaufzeit Zahlungsperioden, konstantem Zins dZins und dem Barwert dBarwert (vgl. »PV-Funktion«,
99
Mathematische und finanzmathematische Funktionen und Anweisungen
................................................... Bes c hreibung
Mathematische und finanzmathematische Funktionen und Anweisungen
S. 106). Der optionale Parameter vZukünftigerWert (vgl. »FV-Funktion«, S. 98) ermöglicht die Angabe eines zukünftigen Werts, der den nach der letzten Zahlung erreichten Kontostand angibt. Bei einem Kredit ist dieser Wert im Allgemeinen mit 0 anzugeben, was dem Vorgabewert für den Parameter entspricht. Fehlt der optionale Parameter vTermin oder ist er 0, geht die Funktion davon aus, dass die Ratenzahlung jeweils zu Ende einer Periode erfolgt; ist der Wert dagegen 1, geht die Funktion davon aus, dass die Ratenzahlung jeweils zu Beginn einer Periode erfolgt. Anwendung
Mathematische und finanzmathematische Funktionen und Anweisungen
................................................... Anwendung
Beachten Sie, dass die Werte für die Parameter dZins, dPeriode und dLaufzeit auf denselben Einheitszeitraum (meist Monat oder Jahr) bezogen sein müssen. Für alle finanzmathematischen Funktionen gilt, dass Zahlungsausgänge durch negative Zahlen und Zahlungseingänge durch positive Zahlen dargestellt werden. Die Funktion wird im Allgemeinen zur Berechnung von Krediten verwendet. Für die Kreditberechnung setzen Sie dBarwert auf die Höhe des Kredits (Zahlungsausgang) und vZukünftigerWert auf 0 (Kredit abbezahlt). Tipp
................................................... Tipp
Die Funktion PPmt liefert den Tilgungs- bzw. Kapitalanteil. Die Summe aus Tilgungs- bzw. Kapitalanteil und Zins entspricht der von der Funktion Pmt gelieferten Rate. Beispiel
................................................... Beis piel
Das Projekt ZinsAnteil zeigt, wie sich Zins und Tilgung prozentual für einen Kredit beliebiger Höhe bei einem Zinssatz von 10% p.a. über zehn Jahre hin entwickeln. ' Projekt: Annuität-Zinsanteil Const Laufzeit = 10 * 12 Const Zins = 0.1 / 12 Const mRate = 100
' 100 Prozent(!)
Private Sub Form_Load() AutoRedraw = True BarWert = PV(Zins, Laufzeit, mRate) ' Barwert in Prozent (!) ' Kreditmodalitäten Print "Laufzeit", Laufzeit; "Monate" Print "Zins p.a.", Zins * 12 * 100; "%" Print "Periode", "Zinsanteil (%)", "Tilgungsanteil (%)" Print String(40, "=") ' prozentualen Zins- und Tilgungsanteil ausgeben. For i = 1 To Laufzeit Step 12 Print i, Round(IPmt(Zins, i, Laufzeit, BarWert), 2), Print Round(PPmt(Zins, i, Laufzeit, BarWert), 2) Next i End Sub Verwandte Befehle
................................................... Verwa ndte Befehle
FV, NPer, NPV, Pmt, PPmt, PV
IRR- Funktion Function IRR(dWerte() As Double[, vErwartet As Variant = 0.1)] As Double
1 00
Log- Funktion
Beschreibung
................................................... Bes c hreibung
Die zu den finanzmathematischen Funktionen zählende Funktion IRR (engl.: Internal Rate of Return) liefert den internen Ertragssatz für eine via dWerte() spezifizierte Folge regelmäßiger Ein- und Auszahlungen (Cash Flows). Fehlt der optionale Parameter vErwartet, rechnet die Funktion mit dem Erwartungswert 0,1, ansonsten mit dem angegebenen Wert. Anwendung
................................................... Anwendung
Beispiel
................................................... Beis piel
Das folgende Codefragment zeigt beispielhaft, wie die Analyse eines Unternehmens aussehen kann: Dim dWerte(9) As Double dWerte(0) = -50000 dWerte(1) = -25000 dWerte(2) = 0 dWerte(3) = 5000 dWerte(4) = -1000 dWerte(5) = 7000 dWerte(6) = 21000 dWerte(7) = 45000 dWerte(8) = 60000 dWerte(9) = 110000 Print Round(IRR(dWerte) * 100, 2)
' ' ' ' ' ' ' ' ' ' '
Investition bei Gründung Unternehmensaufbau Break Even Erster Gewinn im vierten Jahr Verlust wg. schwacher Konjunktur Gewinn im sechsten Jahr Gewinn im siebten Jahr Gewinn im achten Jahr Gewinn im neunten Jahr Gewinn im zehnten Jahr 17,32 Prozent
Verwandte Befehle
................................................... Verwa ndte Befehle
MIRR, Rate
Log- Funktion Function Log(dZahl As Double) As Double Beschreibung
................................................... Bes c hreibung
Die natürliche Logarithmusfunktion Log ist die Umkehrfunktion zur Wachstumsfunktion Exp. Sie liefert den Wert, mit dem man die Eulersche Konstante (2,71828182845905) potenzieren muss, um dZahl zu erhalten.
1 01
Mathematische und finanzmathematische Funktionen und Anweisungen
Der interne Ertragssatz ist der Zinssatz für eine Investition, die aus Ein- und Auszahlungen in regelmäßigen Abständen besteht, wobei sich aber das Verhältnis zwischen Ein- und Auszahlungen mit der Zeit verschieben kann (wie bei einem neuen Unternehmen üblich, das zu Beginn große Investitionen tätigt und seine Einnahmen erst mit der Zeit verbessert, bis der Break-even überschritten ist). Für alle finanzmathematischen Funktionen gilt, dass Zahlungsausgänge durch negative Zahlen und Zahlungseingänge durch positive Zahlen dargestellt werden. Die Elemente des Arrays dWerte enthalten die Werte der Cash Flows. Dabei ist die Reihenfolge eine wichtige Größe. IRR wird iterativ berechnet. IRR verwendet vErwartet als Startwert und wiederholt die Berechnung so lange, bis das Ergebnis auf 0,00001 Prozent genau ist. Wenn nach 20 Versuchen kein Ergebnis gefunden werden kann (im Allgemeinen, weil vErwartet zu schlecht ist), generiert IRR den Laufzeitfehler 5.
Mathematische und finanzmathematische Funktionen und Anweisungen
Beispiel
................................................... Beis piel
Print Log(2.71828182845905) Print Exp(Log(10))
' Ausgabe: 1 ' Ausgabe: 10
Verwandte Befehle
................................................... Verwa ndte Befehle
Exp
Mathematische und finanzmathematische Funktionen und Anweisungen
MIRR- Funktion Function MIRR( _ dWerte() As Double, _ dZinsFinanz As Double, _ dZinsReinv As Double) _ As Double Beschreibung
................................................... Bes c hreibung
Die zu den finanzmathematischen Funktionen zählende Funktion MIRR (engl.: Modified Internal Rate of Return) liefert den modifizierten internen Ertragssatz für eine via dWerte() spezifizierte Folge regelmäßiger Ein- und Auszahlungen (Cash Flows). Im Gegensatz zu IRR rechnet MIRR mit unterschiedlichen Zinssätzen für die Finanzierung ungedeckter Ausgaben (dZinsFinanz) und die Reinvestition von Gewinnen (dZinsReinv). Anwendung
................................................... Anwendung
Die beiden Zinswerte, dZinsFinanz und dZinsReinv, sind als Faktoren (Prozentwert dividiert durch 100) anzugeben. Beispiel
................................................... Beis piel
Das folgende Codefragment zeigt beispielhaft, wie die Analyse eines Unternehmens aussehen kann, wenn für Investitionen ein Zins von 10% und für Reinvestitionen der interne Ertragssatz des Unternehmens angesetzt wird: Dim dWerte(9) As Double dWerte(0) = -50000 ' Investition bei Gründung dWerte(1) = -25000 ' Unternehmensaufbau dWerte(2) = 0 ' Break Even dWerte(3) = 5000 ' Erster Gewinn im vierten Jahr dWerte(4) = -1000 ' Verlust wg. schwacher Konjunktur dWerte(5) = 7000 ' Gewinn im sechsten Jahr dWerte(6) = 21000 ' Gewinn im siebten Jahr dWerte(7) = 45000 ' Gewinn im achten Jahr dWerte(8) = 60000 ' Gewinn im neunten Jahr dWerte(9) = 110000 ' Gewinn im zehnten Jahr Print Round(IRR(dWerte) * 100, 2) ' 17,32 Prozent Print Round(MIRR(dWerte, 0.1, IRR(dWerte)) * 100, 2) ' 17,04 Prozent Verwandte Befehle
................................................... Verwa ndte Befehle
IRR, Rate
1 02
NPer- Funktion
NPer- Funktion Function NPer( _ dZins As Double, _ dRate As Double, _ dBarwert As Double, _ [vZukünftigerWert], _ [vTermin]) _ As Double Beschreibung
Die zu den finanzmathematischen Funktionen zählende Funktion NPer (engl.: Number of Periods) liefert die Laufzeit (Anzahl der Zahlungsperioden) einer Annuität mit konstantem Zins dZins und dem Barwert dBarwert (vgl. die Funktion »PV-Funktion« S. 106). Der optionale Parameter vZukünftigerWert (vgl. »FV-Funktion«, S. 98) ermöglicht die Angabe eines zukünftigen Werts, der den nach der letzten Zahlung erreichten Kontostand angibt. Bei einem Kredit ist dieser Wert im Allgemeinen mit 0 anzugeben, was dem Vorgabewert für den Parameter entspricht. Fehlt der optionale Parameter vTermin oder ist er 0, geht die Funktion davon aus, dass die Ratenzahlung jeweils zu Ende einer Periode erfolgt; ist der Wert dagegen 1, geht die Funktion davon aus, dass die Ratenzahlung jeweils zu Beginn einer Periode erfolgt. Anwendung
................................................... Anwendung
Beachten Sie, dass die Werte für die Parameter dZins, dRate auf denselben Einheitszeitraum (meist Monat oder Jahr) bezogen sein müssen, wie der von der Funktionen erwartete Wert. Für alle finanzmathematischen Funktionen gilt, dass Zahlungsausgänge durch negative Zahlen und Zahlungseingänge durch positive Zahlen dargestellt werden. Wird die Funktion zur Berechnung eines Kredits eingesetzt, ist für dBarwert die Kredithöhe als negativer Wert zu spezifizieren. Wird die Funktion zur Berechnung einer Ansparung eingesetzt, ist für dBarwert der augenblickliche Wert der Ansparung als positiver Wert anzugeben und für vZukünftigerWert das Sparziel als negativer Wert. Beispiel
................................................... Beis piel
Das folgende Codefragment berechnet die Laufzeit für einen Kredit über 7.000 € zu 10% p.a. sowie die Laufzeit einer Ansparung zu denselben Bedingungen: Print NPer(0.1 / 12, 100, -7000) Print NPer(0.1 / 12, 100, 0, -7000)
' Laufzeit f. Kredit: 106 ' Laufzeit f. Ansparung: 55
Auf dieselben Ergebnisse kommt man auch mittels: Print -NPer(0.1 / 12, 100, 0, 7000) Print -NPer(0.1 / 12, 100, 7000)
' Laufzeit f. Kredit: 106 ' Laufzeit f. Ansparung: 55
Warnung
................................................... Wa rnung
Die Funktion liefert einen Laufzeitfehler, wenn die Kreditlaufzeit bei gegebenem Zins und gegebener Rate über alle Grenzen wachsen würde, sprich: die Rate zu klein ist, als dass jemals eine Tilgung erfolgen könnte. (Solche Kredite würden Banken natürlich am liebsten geben: Man zahlt ein Leben lang, und die Schulden werden immer größer.) Print NPer(0.1 / 12, 100, -20000)
' Laufzeitfehler
1 03
Mathematische und finanzmathematische Funktionen und Anweisungen
................................................... Bes c hreibung
Mathematische und finanzmathematische Funktionen und Anweisungen
Verwandte Befehle
................................................... Verwa ndte Befehle
FV, IPmt, NPV, Pmt, PPmt, PV
NPV- Funktion Function NPV(dDiskontsatz As Double Werte() As Double) As Double Beschreibung
Mathematische und finanzmathematische Funktionen und Anweisungen
................................................... Bes c hreibung
Die zu den finanzmathematischen Funktionen zählende Funktion NPV (engl.: Net Present Value) liefert den Nettobarwert einer Investition für eine via dWerte() spezifizierte Folge regelmäßiger Ein- und Auszahlungen (Cash Flows) bei festem Diskontsatz dDiskontsatz. Anwendung
................................................... Anwendung
Der Nettobarwert einer Investition ist der aktuelle Wert einer Folge zukünftiger regelmäßiger Aus- und Einzahlungen. Für alle finanzmathematischen Funktionen gilt, dass Zahlungsausgänge durch negative Zahlen und Zahlungseingänge durch positive Zahlen dargestellt werden. Die Elemente des Arrays dWerte enthalten die Werte der erwarteten Cash Flows. Dabei ist die Reihenfolge eine wichtige Größe. Die Investition beginnt mit dem Zeitraum des ersten CashFlow-Werts und endet mit dem Auftreten des letzten Cash-Flow-Werts. Im Gegensatz zur Funktion PV, die den Barwert einer Folge von Zahlungen unveränderlicher Höhe bei konstantem Zins und konstanter Laufzeit berechnet, erlaubt NPV Zahlungen unterschiedlicher Höhe (und Vorzeichen) und ermittelt die Laufzeit selbstständig aus der Anzahl der Werte. Die Funktion geht davon aus, dass die Zahlungen jeweils zum Ende der Zahlungsperioden erfolgen. Bezieht sich der erste Cash Flow auf den Beginn des ersten Zeitraums, so muss die Folge mit der zweiten Zahlung beginnen und die erste Zahlung gesondert zum Ergebnis von NPV addiert werden. Beispiel
................................................... Beis piel
Das folgende Codefragment zeigt beispielhaft, wie die NPV-Analyse für ein Unternehmen aussehen kann: Dim dWerte(9) As Double dWerte(0) = -50000 dWerte(1) = -25000 dWerte(2) = 0 dWerte(3) = 5000 dWerte(4) = -1000 dWerte(5) = 7000 dWerte(6) = 21000 dWerte(7) = 45000 dWerte(8) = 60000 dWerte(9) = 110000 Print NPV(0.1, dWerte) Verwandte Befehle
' ' ' ' ' ' ' ' ' ' '
................................................... Verwa ndte Befehle
MIRR, Rate
1 04
Investition bei Gründung Unternehmensaufbau Break Even Erster Gewinn im vierten Jahr Verlust wg. schwacher Konjunktur Gewinn im sechsten Jahr Gewinn im siebten Jahr Gewinn im achten Jahr Gewinn im neunten Jahr Gewinn im zehnten Jahr 40254
Pmt- Funktion
Pmt- Funktion Function Pmt( _ dZins As Double, _ dLaufzeit As Double, _ dBarwert As Double, _ [vZukünftigerWert], _ [vTermin]) _ As Double Beschreibung
Die zu den finanzmathematischen Funktionen zählende Funktion Pmt (engl.: Payment) liefert die zu zahlende Rate zu einer Annuität mit einer Laufzeit von dLaufzeit Zahlungsperioden, konstantem Zins dZins und dem Barwert dBarwert (vgl. die Funktion »PV-Funktion«, S. 106). Der optionale Parameter vZukünftigerWert (vgl. die Funktion »FV-Funktion«, S. 98) ermöglicht die Angabe eines zukünftigen Werts, der den nach der letzten Zahlung erreichten Kontostand angibt. Bei einem Kredit ist dieser Wert im Allgemeinen mit 0 anzugeben, was dem Vorgabewert für den Parameter entspricht. Fehlt der optionale Parameter vTermin oder ist er 0, geht die Funktion davon aus, dass die Ratenzahlung jeweils zu Ende einer Periode erfolgt; ist der Wert dagegen 1, geht die Funktion davon aus, dass die Ratenzahlung jeweils zu Beginn einer Periode erfolgt. Anwendung
................................................... Anwendung
Beachten Sie, dass die Werte für die Parameter dZins und dLaufzeit auf denselben Einheitszeitraum (meist Monat oder Jahr) bezogen sein müssen. Für alle finanzmathematischen Funktionen gilt, dass Zahlungsausgänge durch negative Zahlen und Zahlungseingänge durch positive Zahlen dargestellt werden. Für die Kreditberechnung setzen Sie dBarwert auf die Höhe des Kredits (Zahlungsausgang) und vZukünftigerWert auf 0 (Kredit abbezahlt). Tipp
................................................... Tipp
Die Funktionen IPmt und PPmt liefern den Zinsanteil und den Kapitalanteil zu einer bestimmten Periode. Die Summe aus Kapitalanteil und Zinsanteil entspricht der von Pmt gelieferten Rate. Beispiel
................................................... Beis piel
Sie wollen einen Kredit über 250.000 € mit einer Laufzeit von 20 Jahren und einem festen Zinssatz von 8,3% aufnehmen. Die monatliche Rate berechnet sich dann zu: Print Pmt(.083 / 12, 20 * 12, -250000)
' Ausgabe: 2138
Die gleichen 250.000 € im Rahmen einer Lebensversicherung mit 4,6% Kapitalverzinsung angespart, ergibt eine monatliche Rate von: Print Pmt(.046 / 12, 20 * 12, 0, -250000)
' Ausgabe: 636,82
Man sieht, Geben ist eben doch billiger denn Nehmen. Verwandte Befehle
................................................... Verwa ndte Befehle
FV, IPmt, NPer, NPV, PPmt, PV
1 05
Mathematische und finanzmathematische Funktionen und Anweisungen
................................................... Bes c hreibung
Mathematische und finanzmathematische Funktionen und Anweisungen
PPmt- Funktion
Mathematische und finanzmathematische Funktionen und Anweisungen
Function PPmt( _ dZins As Double, _ dPeriode As Double, _ dLaufzeit As Double, _ dBarwert As Double, _ [vZukünftigerWert], _ [vTermin]) _ As Double Beschreibung
................................................... Bes c hreibung
Die zu den finanzmathematischen Funktionen zählende Funktion PPmt (engl.: Principal Payment) liefert den Kapitalanteil in der Periode dPeriode zu einer Annuität mit einer Laufzeit von dLaufzeit Zahlungsperioden, konstantem Zins dZins und dem Barwert dBarwert (vgl. »PVFunktion«, S. 106). Der optionale Parameter vZukünftigerWert (vgl. »FV-Funktion«, S. 98) ermöglicht die Angabe eines zukünftigen Werts, der den nach der letzten Zahlung erreichten Kontostand angibt. Bei einem Kredit ist dieser Wert im Allgemeinen mit 0 anzugeben, was dem Vorgabewert für den Parameter entspricht. Fehlt der optionale Parameter vTermin oder ist er 0, geht die Funktion davon aus, dass die Ratenzahlung jeweils zu Ende einer Periode erfolgt; ist der Wert dagegen 1, geht die Funktion davon aus, dass die Ratenzahlung jeweils zu Beginn einer Periode erfolgt. Anwendung
................................................... Anwendung
Beachten Sie, dass die Werte für die Parameter dZins, dPeriode und dLaufzeit auf denselben Einheitszeitraum (meist Monat oder Jahr) bezogen sein müssen. Für alle finanzmathematischen Funktionen gilt, dass Zahlungsausgänge durch negative Zahlen und Zahlungseingänge durch positive Zahlen dargestellt werden. Die Funktion wird im Allgemeinen zur Berechnung von Krediten eingesetzt und liefert dann den Tilgungsanteil zu einer bestimmten Periode. Für die Kreditberechnung setzen Sie dBarwert auf die Höhe des Kredits (Zahlungsausgang) und vZukünftigerWert auf 0 (Kredit abbezahlt). Tipp
................................................... Tipp
Die Funktion IPmt liefert den Zinsanteil. Die Summe aus Tilgungs- bzw. Kapitalanteil und Zins entspricht der von der Funktion Pmt gelieferten Rate. Beispiel
................................................... Beis piel
Vgl. das Beispiel in »IPmt-Funktion« (S. 99). Verwandte Befehle
................................................... Verwa ndte Befehle
FV, IPmt, NPer, NPV, Pmt, PV
PV- Funktion Function PV( _ dZins As Double, _ dLaufzeit As Double, _ dRate As Double, _ [vJetztWert], _ [vTermin]) _ As Double
1 06
Randomize- Anweisung
Beschreibung
................................................... Bes c hreibung
Die zu den finanzmathematischen Funktionen zählende Funktion PV (engl.: Payment Value) liefert den Barwert einer Annuität mit einer Laufzeit von dLaufzeit Zahlungsperioden, konstantem Zins dZins und der Rate dRate. Der optionale Parameter vJetztWert ermöglicht die Angabe eines Ausgangswerts bei bereits erfolgter Ansparung. Fehlt der optionale Parameter vTermin oder ist er 0, geht die Funktion davon aus, dass die Ratenzahlung jeweils zu Ende einer Periode erfolgt; ist der Wert dagegen 1, geht die Funktion davon aus, dass die Ratenzahlung jeweils zu Beginn einer Periode erfolgt. Anwendung
Beachten Sie, dass die Werte für die Parameter dZins und dLaufzeit auf denselben Einheitszeitraum (meist Monat oder Jahr) bezogen sein müssen. Für alle finanzmathematischen Funktionen gilt, dass Zahlungsausgänge durch negative Zahlen und Zahlungseingänge durch positive Zahlen dargestellt werden. Die Funktion lässt sich sowohl zur Berechnung von Krediten als auch von Ansparungen verwenden, wird aber vorzugsweise für die Ansparberechnung eingesetzt, da der Barwert bei Krediten üblicherweise einen Ausgangswert darstellt. Bei der Kreditberechnung sind dRate und dJetztWert als positive Werte anzugeben, bei der Ansparberechnung als negative Werte. Tipp
................................................... Tipp
Die Funktionen PV und FV liefern mit vertauschten Vorzeichen für dRate und dJetztWert die gleichen Ergebnisse. Beispiel
................................................... Beis piel
Das folgende Codefragment ermittelt den Barwert einer auf 100.000 € abgeschlossenen Lebensversicherung mit einer Laufzeit von 35 Jahren und einem Zinssatz von 4,45%: Zins = 0.045/12 Laufzeit = 35 * 12 Ansparung = -100000 Raten = Pmt(Zins, Laufzeit, 0, Ansparung) Barwert = PV(Zins, Laufzeit, Raten) ZukWert = FV(Zins, Laufzeit, Raten)
' 99,33 ' 21126,98 ' 100000 Probe stimmt!
Verwandte Befehle
................................................... Verwa ndte Befehle
FV, IPmt, IRR, NPer, NPV, Pmt, PPmt
Randomize- Anweisung Sub Randomize ([vSeed]) Beschreibung
................................................... Bes c hreibung
Die Anweisung Randomize initialisiert den Zufallsgenerator mit einem Startwert (engl.: Seed). Ausgehend von diesem Startwert lässt sich mittels der Zufallsfunktion Rnd eine gleich verteilte Sequenz von Zufallszahlen berechnen. Zu einem bestimmten Startwert liefert der Zufallsfunktion Rnd immer die gleiche Folge. Dabei benutzt sie jeweils den zuletzt gelieferten Wert als neuen Startwert. Fehlt vSeed, nimmt die Anweisung den von der Funktion Timer gelieferten Wert als Startwert.
1 07
Mathematische und finanzmathematische Funktionen und Anweisungen
................................................... Anwendung
Mathematische und finanzmathematische Funktionen und Anweisungen
Anwendung
................................................... Anwendung
Mathematische und finanzmathematische Funktionen und Anweisungen
Eine wirklich zufällige Initialisierung des Zufallsgenerators lässt sich erreichen, indem man den Wert der Funktion Timer als Startwert benutzt, der die Anzahl der seit Mitternacht (Systemzeit) vergangenen Sekunden liefert. Wird Randomize in einem Programm dagegen gar nicht ausgeführt, arbeitet Visual Basic mit dem Vorgabewert 0 als Startwert. Um im gleichen Programmlauf mehrere Folgen von Zufallszahlen mit deterministischem Startwert zu erzeugen, übergeben Sie der Funktion Rnd jeweils bei Berechnung des letzten Folgenelements einen negativen Wert und rufen danach Randomize mit dem Startwert der nächsten Folge auf. Beispiel
................................................... Beis piel
Der Startwert 10 sorgt dafür, dass Rnd reproduzierbar die folgende »Zufallsfolge« {0,5749933; 0,2375866; 0,5295308; 0,2520258} generiert: Randomize 10 Print Rnd(1), Rnd(1), Rnd(1), Rnd(1) Rnd(-1) Randomize Timer Print Rnd(1), Rnd(1), Rnd(1), Rnd(1)
' Folge für Startwert 10 ' Randomize vorbereiten ' wirklich zufällige Folge
Verwandte Befehle
................................................... Verwa ndte Befehle
Rnd
Rate- Funktion Function Rate( _ dLaufzeit As Double, _ dRate As Double, _ dBarWert As Double, _ [vZukünftigerWert], _ [vTermin]) _ [vSchätzung As Variant = 0.1] As Double Beschreibung
................................................... Bes c hreibung
Die zu den finanzmathematischen Funktionen zählende Funktion Rate liefert den Zinssatz einer Annuität mit dem Barwert (vgl. »PV-Funktion«, S. 106) dBarWert, einer Laufzeit von dLaufzeit Zahlungsperioden und der Zahlungsrate dRate. Der optionale Parameter vZukünftigerWert (vgl. »FV-Funktion«, S. 98) ermöglicht die Angabe eines zukünftigen Werts, der den nach der letzten Zahlung erreichten Kontostand angibt. Bei einem Kredit ist dieser Wert im Allgemeinen mit 0 anzugeben, was dem Vorgabewert für den Parameter entspricht. Umgekehrt ist bei einer Ansparung dBarWert mit 0 anzugeben. Fehlt der optionale Parameter vTermin oder ist er 0, geht die Funktion davon aus, dass die Ratenzahlung jeweils zu Ende einer Periode erfolgt; ist der Wert dagegen 1, geht die Funktion davon aus, dass die Ratenzahlung jeweils zu Beginn einer Periode erfolgt. Da die Funktion das Ergebnis mit einem iterativen Verfahren berechnet, benötigt sie für den Start der Iteration einen Schätzwert vSchätzung. Fehlt dieser optionale Parameter, arbeitet die Funktion mit dem Vorgabewert 0,1.
1 08
Rnd- Funktion und Rnd- Anweisung
Anwendung
................................................... Anwendung
Beachten Sie, dass die Werte für die Parameter dRate und dLaufzeit auf denselben Einheitszeitraum (meist Monat oder Jahr) bezogen sein müssen. Für alle finanzmathematischen Funktionen gilt, dass Zahlungsausgänge durch negative Zahlen und Zahlungseingänge durch positive Zahlen dargestellt werden. Die Funktion lässt sich sowohl bei der Berechnung von Krediten als auch von Ansparungen verwenden. Bei der Kreditberechnung ist dBarwert als negativer Wert anzugeben, bei der Ansparberechnung dBarwert als 0 und dZukünftigerWert als negativer Wert. Beispiel
................................................... Beis piel
dLaufzeit = 35 * 12 dZukWert = -250000 dRate = 250 Print Rate(dLaufzeit, dRate, 0, dZukWert) * 1200; "%" ' Ausgabe: 4,42 % Verwandte Befehle
................................................... Verwa ndte Befehle
FV, IPmt, NPer, NPV, Pmt, PPmt, PV
Rnd- Funktion und Rnd- Anweisung Function Rnd [(vSchalter)] As Single Sub Rnd ([vSchalter]) Beschreibung
................................................... Bes c hreibung
Rnd lässt sich sowohl als Funktion als auch als Anweisung verwenden. Die Anweisung unterscheidet sich von der Funktion nur insoweit, als sie keinen Wert liefert. Die Zufallsfunktion Rnd ermittelt ausgehend von einem Startwert eine Zufallszahl mit dem Datentyp Single, die dem halboffenen Intervall [0; 1[ entstammt. Die Berechnung ist gleichverteilt, das heißt, jede der im Intervall als Single darstellbaren Zahlen wird mit gleicher Wahrscheinlichkeit ermittelt. Der Wert des optionalen Parameters vSchalter bestimmt, wie die Funktion mit dem Startwert verfährt. Fehlt der Parameter oder ist sein Wert größer als 0, berechnet die Funktion jeweils die nächste Zufallszahl aus der vorangegangenen, indem sie diese als Startwert verwendet. Damit liefert Rnd eine deterministische Folge von Zufallszahlen, die durch den ersten Startwert ausgewählt wird. Standardmäßig initialisiert Visual Basic den Startwert für jeden Programmlauf auf 0, so dass Rnd ohne weitere Vorkehrungen in der Tat die immer gleiche Folge liefert. Um eine andere Folge auszuwählen, muss der Startwert zuvor mittels der Anweisung Randomize explizit gesetzt werden. Hat der Parameter vSchalter den Wert 0, liefert Rnd erneut die vorangegangene Zufallszahl. Ist vSchalter dagegen ein negativer Wert, legt die Funktion dem nächsten Aufruf den durch Randomize (erneut) gesetzten Startwert zugrunde. Anwendung
................................................... Anwendung
Im Allgemeinen wird man vor dem ersten Rnd-Aufruf einen Randomize-Aufruf setzen, der für eine Auswahl der Zufallsfolge sorgt. Eine wirklich zufällige Initialisierung des Zufallsgenerators lässt sich erreichen, indem man den Wert der Funktion Timer, die die Anzahl der seit Mittnacht (Systemzeit) vergangenen Sekunden liefert, als Startwert einsetzt. Dazu reicht es, Randomize ohne Angabe eines Parameters auszuführen. Um im gleichen Programmlauf mehrere Folgen von Zufallszahlen mit deterministischem Startwert zu erzeugen, setzen Sie vor jeden Randomize-Aufruf die Anweisung Rnd(-1).
1 09
Mathematische und finanzmathematische Funktionen und Anweisungen
Das folgende Codefragment ermittelt den Zins in Prozent für eine Ansparung über 250.000 € mit einer Laufzeit von 35 Jahren und einer monatlichen Rate von 250 €:
Mathematische und finanzmathematische Funktionen und Anweisungen
Beispiel
................................................... Beis piel
Vgl. das Beispiel zu »Randomize-Anweisung« (S. 107). Verwandte Befehle
................................................... Verwa ndte Befehle
Randomize Verwandte Themen
................................................... Verwandte Them en
Mathematische und finanzmathematische Funktionen und Anweisungen
Zeitgeber-Steuerelement (Timer) (S. 432)
Round- Funktion Function Round(dZahl As NumTyp, [iDezStellen As Integer = 0]) As NumTyp Beschreibung
................................................... Bes c hreibung
Die Rundungsfunktion Round führt eine Kaufmannsrundung durch. Fehlt der Parameter iDezimalstellen oder hat der den Wert 0, liefert die Funktion die nächste Ganzzahl in der Umgebung von dZahl und führt dabei – im Gegensatz zu Fix und Int – eine echte Kaufmannsrundung durch. Jeder andere positive Wert für iDezimalstellen benennt eine Dezimalstelle hinter dem Komma, an der die Rundung stattfinden soll. Die Funktion richtet den Datentyp des Rückgabewerts nach dem Datentyp des Parameters. Warnung
................................................... Wa rnung
Wie das Beispiel zeigt, ist die Dezimalrechnung mit den Datentypen Single und Double nicht immer exakt. Das liegt daran, dass die Repräsentation dieser Werte intern im hexadezimalen bzw. echten binären Zahlenformat erfolgt und viele endliche Dezimalbrüche im binären Zahlensystem nur als unendliche Binärbrüche darstellbar sind. Im Zusammenhang mit der Funktion Round kann das zu peinlichen Fehlern führen. Wenn Sie Exaktheit benötigen, müssen Sie mit einem der Datentypen Currency oder Decimal rechnen. Beispiel
................................................... Beis piel
Print Print Print Print Print Print
Round(14.005, Round(14.015, Round(14.025, Round(14.035, Round(14.045, Round(14.055,
2) 2) 2) 2) 2) 2)
' ' ' ' ' '
14 14,02 14,02 14,04 14,04 14,06
(erwartet: 14,01) (korrekt) (erwartet 14,03) (korrekt) (erwartet 14,03) (korrekt)
Verwandte Befehle
................................................... Verwa ndte Befehle
CInt, Fix, Int
Sgn- Funktion Function Sgn(dZahl As NumTyp) As Integer Beschreibung
................................................... Bes c hreibung
Die Vorzeichenfunktion Sgn liefert das Vorzeichen des Werts dZahl als Faktor. Das Ergebnis ist 1, wenn dZahl positiv ist, 0, wenn dZahl 0 ist, und -1, wenn dZahl negativ ist.
110
Sgn- Funktion
Anwendung
................................................... Anwendung
Die Funktion Sgn ist zuweilen nützlich, wenn es darum geht, Laufzeitoptimierungen vorzunehmen und If-Anweisungen durch geschicktere Formulierung logischer Ausdrücke zu vereinfachen oder ganz zu vermeiden. Beispiel
................................................... Beis piel
Die folgende Zeile wird erheblich schneller verarbeitet als die auskommentierte If-Anweisung (allerdings haben die beiden Formulierungen nicht die gleiche Semantik, da Nullwerte nicht mitgezählt werden):
In der folgenden, dem Projekt Apfelmann entstammenden Routine führt die Formulierung der zeitaufwändigen If-Abfrage mit den Funktionen Abs und Sgn zu einer Laufzeitverbesserung um immerhin ca. 30 %. Messen Sie es nach, indem Sie einmal die eine und einmal die andere Zeile auskommentieren! Private Sub Apfel(m_x1, m_x2, m_y1, m_y2, step_x, step_y) Dim r1 As Double, re As Double, im As Double Dim zr As Double, zi As Double, it As Long Zeit = Timer For zr = m_x1 To m_x2 Step step_x ' alle Spalten in x-Richtung For zi = m_y1 To m_y2 Step step_y ' alle Punkte in Spalte zr re = 0 ' Realteil initialisieren im = 0 ' Imaginärteil initialisieren For it = 0 To cMaxIterat ' Iteration für Punkt r1 = re * re – im * im + zr im = 2 * re * im + zi re = r1 ' If re < -cGrenze Or re > cGrenze Or im < -cGrenze Or im > cGrenze Then If Sgn(cGrenze – Abs(re)) + Sgn(cGrenze – Abs(im)) = -2 Then PSet (zr, zi), it * 16 ' Punkt ausgeben Exit For End If Next it Next zi Next zr Caption = Timer – Zeit End Sub Verwandte Befehle
................................................... Verwa ndte Befehle
CInt, Fix, Int
111
Mathematische und finanzmathematische Funktionen und Anweisungen
Dim lZähler As Long Zeit = Timer ' Laufzeitmessung beginnen For i = 1 To 1000000 zv = Rnd – 0.5 lZähler = lZähler + Sgn(zv) ' If zv > 0 Then lZähler = lZähler + 1 Else lZähler = lZähler – 1 Next Caption = Timer – Zeit ' Laufzeit als Titelzeile ausgeben
Mathematische und finanzmathematische Funktionen und Anweisungen
Sin- Funktion Function Sin(dWinkel As Double) As Double Beschreibung
................................................... Bes c hreibung
Die zu den trigonometrischen Funktionen zählende Sinusfunktion Sin liefert den Sinus zu dem im Bogenmaß angegebenen Winkel dWinkel. Das Ergebnis der Funktion liegt immer im Intervall [-1; 1]. Für die Umwandlung eines Gradwerts in das Bogenmaß ist der Wert mit π/180 = 1,74532925199433E-02 zu multiplizieren.
Mathematische und finanzmathematische Funktionen und Anweisungen
Anwendung
................................................... Anwendung
Diese Funktion berechnet aus dem Winkel zwischen Ankathete und Hypothenuse die Gegenkathete eines rechtwinkligen Dreiecks mit Hypothenuse 1. Beispiel
................................................... Beis piel
Dim Pi As Double Pi = Atn(1) * 4 Print Sin(Pi / 6)
' 3.14159265358979 ' Ausgabe: 0,5 [= Sin(30°)]
Verwandte Befehle
................................................... Verwa ndte Befehle
Atn, Cos, Tan
SLN- Funktion Function SLN( _ dAnschaffungsKosten As Double, _ dRestwert As Double, _ dNutzungsDauer As Double) _ As Double Beschreibung
................................................... Bes c hreibung
Die zu den finanzmathematischen Funktionen zählende Funktion SLN (engl.: Straight Line Depreciation) berechnet die periodische Abschreibung einer Investition dAnschaffungsKosten mit dem Restwert dRestwert über die Nutzungsdauer dNutzungsDauer nach dem linearen (arithmetischen) Abschreibungsmodell. Anwendung
................................................... Anwendung
Der von SLN gelieferte Wert bezieht sich auf die gleiche Einheit (Monate oder Jahre) wie dNutzungsDauer. Er errechnet sich wie folgt: Anschaffungswert – Endwert SLN = --------------------------------------------------------------------------Nutzungsdauer Beispiel
................................................... Beis piel
Print SLN(10000, 100, 10) Verwandte Befehle
' Ausgabe 990
................................................... Verwa ndte Befehle
DDB, SYD
112
Sqr- Funktion
Sqr- Funktion Function Sqr(dPositiveZahl As Double) As Double Beschreibung
................................................... Bes c hreibung
Die Wurzelfunktion Sqr liefert die Quadratwurzel von dPositiveZahl. Ist der Wert von dPositiveZahl negativ, meldet die Funktion einen Laufzeitfehler. Beispiel
................................................... Beis piel
det = (b * b – 4 * a * c) If det < 0 Then Print "Keine Lösung" If det = 0 Then Print "x1, x2 = "; -b / 2 / a If det > 0 Then Print "x1 = "; (-b + det) / 2 / a Print "x2 = "; (-b – det) / 2 / a End If Verwandte Befehle
................................................... Verwa ndte Befehle
^ (Operator)
SYD- Funktion Function SYD( _ dAnschaffungsKosten As Double, _ dRestwert As Double, _ dNutzungsDauer As Double_ dPeriode As Double) _ As Double Beschreibung
................................................... Bes c hreibung
Die zu den finanzmathematischen Funktionen zählende Funktion SYD (engl.: Sum of Years Depreciation) berechnet die periodische Abschreibung einer Investition dAnschaffungsKosten mit dem Restwert dRestwert über die Nutzungsdauer dNutzungsDauer für die Periode dPeriode nach dem Abschreibungsmodell der Jahressummengewichtung. Beispiel
................................................... Beis piel
Der folgende Code zeigt eine alternative Implementation der Funktion SYD: Function MySYD(Wert, Rest, Nutzung, per) d = Nutzung * (Nutzung + 1) / 2 MySYD = (Wert – Rest) / d * (Nutzung + 1 – per) End Function
Beide Funktionen liefern die gleichen Werte: For per = 1 To 10 Print SYD(10000, 100, 10, per), Print MySYD(10000, 100, 10, per) Next
' 1800, 1620, 1440, 1260,... ' 1800, 1620, 1440, 1260,...
113
Mathematische und finanzmathematische Funktionen und Anweisungen
Das folgende Codefragment berechnet die Lösung(en) der allgemeinen quadratischen Gleichung ax² + bx +c = 0:
Funktionen und Anweisungen für Datums- / Zeitwerte
Verwandte Befehle
................................................... Verwa ndte Befehle
DDB, SLN
Tan- Funktion Function Tan(dWinkel As Double) As Double Beschreibung
Funktionen und Anweisungen für Datums- / Zeitwerte
................................................... Bes c hreibung
Die zu den trigonometrischen Funktionen zählende Tangensfunktion Tan liefert den Tangens zu dem im Bogenmaß angegebenen Winkel dWinkel. Die Umkehrfunktion zu Tan ist Atn. Für die Umwandlung von Grad in das Bogenmaß ist der Wert mit π/180 = 1,74532925199433E-02 zu multiplizieren. Anwendung
................................................... Anwendung
Diese Funktion berechnet aus dem Winkel zwischen Ankathete und Hypothenuse die Steigung der Hypothenuse, welche ihrerseits als Quotient zwischen Ankathete und Gegenkathete definiert ist. Beispiel
................................................... Beis piel
Dim Pi As Double Pi = Atn(1) * 4 Print Sin(Pi / 4) / Cos(Pi / 4) Print Tan(Pi / 4)
' 3.14159265358979 ' 1 ' 1
Verwandte Befehle
................................................... Verwa ndte Befehle
Atn, Cos, Sin
Funktionen und Anweisungen für Datums- / Zeitwerte Mit der zunehmenden Bedeutung von Visual Basic als Programmiersprache für Front-endAnwendungen im Datenbankbereich versteht es sich beinahe von selbst, dass die Sprache insbesondere für solche Datentypen Äquivalente anbietet, die bei der Datenbankprogrammierung gemeinhin als grundlegende Datentypen gehandelt werden. Die Welt der Datums-/Zeitwerte und ihrer Operationen stellt nicht zuletzt aus diesem Grund neben der Welt der Zeichenfolgen und der Welt der numerischen Datentypen eine eigene Kategorie dar, um die Visual Basic die Sprache Basic bereichert hat. Werte des Typs Date repräsentiert Visual Basic als 64-Bit-Gleitkommazahlen (8 Bytes) nach IEEE und kann somit Datumsangaben im Bereich zwischen dem 1. Januar 100 und dem 31. Dezember 9999 sowie Uhrzeiten im Bereich von 0:00:00 bis 23:59:59 unterscheiden. Datumswerte können Datumsangaben und Zeitangaben kombinieren, aber auch je einzeln ausdrücken. Der Notation literaler Datums- und Zeitangaben liegt das amerikanische Format zugrunde, das heißt, in Datumsangaben wird der Monat vor dem Tag notiert, die Abtrennung erfolgt durch das Zeichen »/«, und Zeitangaben sind in 12-StundenDarstellung gefolgt von »PM« oder »AM« zu treffen. Zudem ist die Darstellung durch ein führendes und ein abschließendes #-Zeichen einzuschließen. An Operationen für den Datentyp Date hält Visual Basic eine ganze Latte parat, die mehr oder weniger etwas mit der Konvertierung unter Berücksichtigung verschiedener Notationen oder mit der Extraktion einer bestimmten Teilinformation zu tun haben. Die folgende Tabelle gibt einen Überblick über alle für diesen Datentyp relevanten Funktionen und Anweisungen:
114
Funktionen und Anweisungen für Datums- / Zeitwerte
Kurzbeschreibung
CDate
Interpretiert eine Zeichenfolge oder einen Zahlenwert als Datums-/Zeitwert
Date
Liefert das aktuelle Systemdatum als Datums-/Zeitwert
Date
Setzt den Datumsanteil von Datums-/Zeitwert als aktuelles Systemdatum
DateAdd
Berechnet die Summe aus einem Datums-/Zeitwert und einem Zeitintervall, das wahlweise in Sekunden, Minuten, Stunden, Tage etc. anzugeben ist
DateDiff
Berechnet das Zeitintervall zwischen zwei Datums-/Zeitwerten und liefert das Ergebnis wahlweise in einer der Einheiten Sekunden, Minuten, Stunden, Tage etc.
DatePart
Liefert eine Teilinformation aus einem Datums-/Zeitwert, wahlweise Sekunden, Minuten, Stunden, Tage etc.
DateSerial
Setzt einen Datums-/Zeitwert aus Ganzzahlwerten (Tag, Monat und Jahr) zusammen
DateValue
Interpretiert eine Zeichenfolge oder einen Zahlenwert als Datumsanteil von Datums-/Zeitwert
Day
Liefert den Tag aus einem Datums-/Zeitwert als Zahlenwert (1 bis 31)
FileDateTime
Liefert den Zeitpunkt der Erstellung bzw. letzten Änderung einer Datei als Datums-/Zeitwert
FormatDateTime
Liefert die Darstellung eines Datums-/Zeitwerts als Zeichenfolge im spezifizierten Format
Hour
Liefert die Stunde aus einem Datums-/Zeitwert als Zahlenwert (0 bis 23)
Minute
Liefert die Minute aus einem Datums-/Zeitwert als Zahlenwert (0 bis 59)
Month
Liefert den Monat aus einem Datums-/Zeitwert als Zahlenwert (1 – 12)
MonthName
Liefert für einen Zahlenwert zwischen 1 und 12 den Monatsnamen als Zeichenfolge
Now
Liefert Systemdatum und -zeit als Datums-/Zeitwert
Second
Liefert die Sekunde aus einem Datums-/Zeitwert als Zahlenwert (0 bis 59)
Time
Liefert die aktuelle Systemzeit als Datums-/Zeitwert
Time
Setzt den Zeitanteil von Datums-/Zeitwert als aktuelle Systemzeit
Timer
Liefert den Wert des System-Timers (Sekunden seit Mitternacht)
TimeSerial
Setzt einen Datums-/Zeitwert aus Ganzzahlwerten (Sekunde, Minute, Stunde) zusammen
TimeValue
Interpretiert eine Zeichenfolge oder einen Zahlenwert als Zeitanteil eines Datums-/Zeitwerts
WeekDay
Liefert den Wochentag aus einem Datums-/Zeitwert als Zahl (1 bis 7)
WeekDayName
Liefert den Wochentag als Zeichenfolge für Werte zwischen 1 und 7
Year
Liefert das Jahr aus einem Datums-/Zeitwert als Zahlenwert
Auf Datum und Zeit bezogene Funktionen und Anweisungen
115
Funktionen und Anweisungen für Datums- / Zeitwerte
Bezeichner
Funktionen und Anweisungen für Datums- / Zeitwerte
CDate- Funktion Function CDate(vWert) As Date Siehe »Typumwandlung« (S. 57).
Date- Funktion und Date- Anweisung Function Date() As Date Date = datDatum Beschreibung
Funktionen und Anweisungen für Datums- / Zeitwerte
................................................... Bes c hreibung
Als Funktion liefert Date das aktuelle Systemdatum und als Anweisung setzt Date das aktuelle Systemdatum (dauerhaft). Beispiel
................................................... Beis piel
Immer häufiger spielen sich Shareware-Programme als Timeware-Programme auf, indem sie nach einer gewissen Zeit überraschend die Arbeit einstellen und einen so dazu »überreden« wollen, sich für die Vollversion registrieren zu lassen. Für die Übergangszeit bietet sich ein kleines Visual-Basic-Programm namens SharewareTrick an, das kurz am Systemdatum dreht: Dim datSystem As Date Private Sub Form_Load() datSystem = Date Date = "1.1.00" Zeitware = Shell("notepad.exe", vbNormalFocus) Timer1.Interval = 10000 End Sub Private Sub Timer1_Timer() Timer1.Interval = 0 Date = datSystem End End Sub Verwandte Befehle
................................................... Verwa ndte Befehle
Now, Time
DateAdd- Funktion Function DateAdd(Interval As String, Number As Double, Date) As Date Beschreibung
................................................... Bes c hreibung
Die Funktion DateAdd addiert zu einem gegebenen Datums-/Zeitwert Date ein Zeitintervall Number, dem die Einheit Interval zugrunde gelegt ist. Wert für Interval
Beschreibung
"yyyy"
Die Funktion interpretiert Number als Jahre
"q"
Die Funktion interpretiert Number als Quartale
"m"
Die Funktion interpretiert Number als Monate
116
DateDiff- Funktion
Wert für Interval
Beschreibung
"ww"
Die Funktion interpretiert Number als Wochen
"d", "w", "y"
Die Funktion interpretiert Number als Tage
"h"
Die Funktion interpretiert Number als Stunden
"n"
Die Funktion interpretiert Number als Minuten
"s"
Die Funktion interpretiert Number als Sekunden
Anwendung
Der Datentyp Date verwendet für die interne Darstellung von Datums-/Zeitwerten einen Wert vom Typ Double, der sich nicht so ohne Weiteres in seine Bestandteile (Sekunden, Minuten, Stunden, Tage, Wochen, Monate Jahre) zerlegen lässt. Mit der Funktion DateAdd stellt Visual Basic ein bequemes Mittel für das Rechnen mit Zeitwerten zur Verfügung. Beachten Sie, dass der Wertebereich von Date auf Datums-/Zeitwerte zwischen dem 1.1.100 und dem 31.12.9999 beschränkt ist. Beispiel
................................................... Beis piel
Die folgende Zeile berechnet den Datums-/Zeitwert für ein Ereignis, das NewInterval Sekunden in der Zukunft gelegen ist. m_TriggerTime = DateAdd("s", NewInterval, Now)
Ein praxisnahes Beispiel für die Anwendung dieser Funktion ist das im Praxisteil vorgestellte Projekt LongTimer (vgl. »LongTimer – der Timer mit Ausdauer«, S. 599). Verwandte Befehle
................................................... Verwa ndte Befehle
DateDiff, DatePart, DateSerial, DateValue, TimeSerial
DateDiff- Funktion Function DateDiff(Interval As String, Date1, Date2, [FirstDayOfWeek As VbFirstDayOfWeek = vbSunday], _ [FirstWeekOfYear As VbFirstWeekOfYear = vbFirstJan1]) _ As Long Beschreibung
................................................... Bes c hreibung
Die Funktion DateDiff bildet die Differenz aus den beiden Datums-/Zeitwerten Date2 und Date1 und liefert das Ergebnis als Ganzzahl in der Einheit Interval. Die optionalen Parameter FirstDayOfWeek und FirstWeekOfYear beziehen sich auf exotische Formen der Datumsdarstellung und können im Allgemeinen getrost weggelassen werden. Wert für Interval
Beschreibung
"yyyy"
Funktion liefert das Ergebnis in der Einheit »Jahre»
"q"
Funktion liefert das Ergebnis in der Einheit »Quartale»
"m"
Funktion liefert das Ergebnis in der Einheit »Monate»
"w", "ww"
Funktion liefert das Ergebnis in der Einheit »Wochen»
117
Funktionen und Anweisungen für Datums- / Zeitwerte
................................................... Anwendung
Funktionen und Anweisungen für Datums- / Zeitwerte
Wert für Interval
Beschreibung
"d", "y"
Funktion liefert das Ergebnis in der Einheit »Tage»
"h"
Funktion liefert das Ergebnis in der Einheit »Stunden»
"n"
Funktion liefert das Ergebnis in der Einheit »Minuten»
"s"
Funktion liefert das Ergebnis in der Einheit »Sekunden»
Anwendung
Funktionen und Anweisungen für Datums- / Zeitwerte
................................................... Anwendung
Der Datentyp Date verwendet für die interne Darstellung von Datums-/Zeitwerten einen Wert vom Typ Double, der sich nicht so ohne Weiteres in seine Bestandteile (Sekunden, Minuten, Stunden, Tage, Wochen, Monate Jahre) zerlegen lässt. Mit der Funktion DateDiff stellt Visual Basic ein bequemes Mittel für das Rechnen mit Zeitwerten zur Verfügung. Beachten Sie, dass der Wertebereich von Date auf Datums-/Zeitwerte zwischen dem 1.1.100 und dem 31.12.9999 beschränkt ist. Warnung
................................................... Wa rnung
Die Reihenfolge der Parameter Date1 und Date2 ist leider kontraintuitiv gewählt. DateDiff rechnet: Date2 – Date1. Beispiel
................................................... Beis piel
Die folgende Zeile berechnet die Anzahl der Sekunden, die seit der Geburtsstunde des Autors verstrichen sind: Print DateDiff("s", #3/3/1959 11:32:00 PM#, Now)
Ein praxisnahes Beispiel für die Anwendung dieser Funktion ist das im Praxisteil vorgestellte Projekt LongTimer (vgl. »LongTimer – der Timer mit Ausdauer«, S. 599). Verwandte Befehle
................................................... Verwa ndte Befehle
DateAdd, DatePart, DateSerial, DateValue, TimeSerial
DatePart- Funktion Function DatePart( _ Interval As String, Date, [FirstDayOfWeek As VbFirstDayOfWeek = vbSunday], _ [FirstWeekOfYear As VbFirstWeekOfYear = vbFirstJan1]) _ As Long Beschreibung
................................................... Bes c hreibung
Die Funktion DatePart extrahiert den über den Parameter Interval spezifizierten Bestandteil des Datums-/Zeitwerts Date und liefert ihn als Wert vom Typ Long. Die optionalen Parameter FirstDayOfWeek und FirstWeekOfYear beziehen sich auf exotische Formen der Datumsdarstellung und können im Allgemeinen getrost weggelassen werden.
118
DateSerial- Funktion
Wert für Interval
Beschreibung
"yyyy"
Funktion extrahiert die Information »Jahre»
"q"
Funktion extrahiert die Information »Quartale»
"m"
Funktion extrahiert die Information »Monate»
"ww"
Funktion extrahiert die Information »Wochen»
"w"
Funktion extrahiert die Information »Tag der Woche» Funktion extrahiert die Information »Tag im Jahr» Funktion extrahiert die Information »Tag im Monat»
"h"
Funktion extrahiert die Information »Stunden»
"n"
Funktion extrahiert die Information »Minuten»
"s"
Funktion extrahiert die Information »Sekunden»
Anwendung
................................................... Anwendung
Der Datentyp Date verwendet für die interne Darstellung von Datums-/Zeitwerten einen Wert vom Typ Double, der sich nicht so ohne Weiteres in seine Bestandteile (Sekunden, Minuten, Stunden, Tage, Wochen, Monate Jahre) zerlegen lässt. Mit der Funktion DatePart stellt Visual Basic ein bequemes Mittel für das Zerlegen von Datums-/Zeitwerten zur Verfügung. Beispiel
................................................... Beis piel
Print "Diese Woche ist die " & DatePart("ww", Now) & ". Kalenderwoche" Verwandte Befehle
................................................... Verwa ndte Befehle
Day, Hour, Minute, Month, MonthName, Second, WeekDay, Year
DateSerial- Funktion Function DateSerial( _ iJahr As Integer, iMonat As Integer, iTag As Integer) As Date Beschreibung
................................................... Bes c hreibung
Die Funktion DateSerial generiert aus den Ganzzahlwerten iTag, iMonat und iJahr einen Datums-/Zeitwert. Anwendung
................................................... Anwendung
Die Funktion prüft die Werte der Parameter nicht, das heißt, sie löst auch keinen Laufzeitfehler aus, wenn die Komponenten kein reelles Datum ergeben. Wichtig ist nur das Resultat: Es darf sich kein Datum vor dem 1.1.100 und nach dem 31.12.9999 ergeben. Auf diese Weise gestattet die Funktion sehr schön das Rechnen mit relativen Zeitangaben wie: heute, vor zwei Jahren, drei Monaten, vier Tagen. Wenn Sie versuchen, dies allgemein »zu Fuß« auszurechnen, werden Sie merken, dass Sie schnell in des Teufels Küche geraten, da die Monate unterschiedlich lang sind und unser Gregorianische Kalender eine nicht unkomplizierte Schaltregel hat.
119
Funktionen und Anweisungen für Datums- / Zeitwerte
"y" "d"
Funktionen und Anweisungen für Datums- / Zeitwerte
Beispiel
................................................... Beis piel
Funktionen und Anweisungen für Datums- / Zeitwerte
Mit DateSerial gerät die Lösung des genannten Problems zum Einzeiler. Probieren Sie es aus; für den 1.1.2001 stimmt die alternative Lösung, für den 1.1.2000 dagegen nicht. d = Date Date = "1.1.2000" ' Date = "1.1.2001" Print DateSerial(Year(Date) – 2, Month(Date) – 3, Day(Date) – 4) Print Date – 2 * 365 – 3 * 31 – 4 ' alternative Lösung Date = d
Ein praxisnahes Beispiel für die Anwendung dieser Funktion ist das Projekt FormatDemo (vgl. »Format-Funktion«, S. 69), das einen Halbjahreskalender ausgibt. Verwandte Befehle
................................................... Verwa ndte Befehle
TimeSerial
DateValue- Funktion Function DateValue( sDatum As String) As Date Beschreibung
................................................... Bes c hreibung
Die Funktion DateValue interpretiert die Zeichenfolge sDatum als Datum und generiert daraus einen Datums-/Zeitwert. Falls die Zeichenfolge auch Zeitangaben enthält, werden diese nicht berücksichtigt. Anwendung
................................................... Anwendung
Was die Notation des Datums betrifft, ist DateValue weitgehend tolerant. So ergänzt die Funktion bei fehlendem Jahr das aktuelle Jahr aus dem Systemdatum des Computers und akzeptiert darüber hinaus neben dem kurzen und dem langen Datumsformat (entsprechend den auf dem System geltenden Datumseinstellungen) auch mittlere Datumsformate, in denen der Monat mit mindestens drei Buchstaben abgekürzt ist und Tag und Monat gegebenenfalls vertauscht sind. Tipp
................................................... Tipp
CDate und TimeValue unterscheiden sich insofern, als CDate auch Werte anderer Datentypen als String in einen Datums-/Zeitwert umsetzt und zusätzlich auch einen Zeitanteil berücksichtigt. Beispiel
................................................... Beis piel
Viele Wege führen nach Rom! Print Print Print Print Print Print Print Print Print
1 20
DateValue("1.2") DateValue("1. Feb") DateValue("1. Febr.") DateValue("1. Februar 00") DateValue("Feb, 1 00") DateValue("00, Feb 01 ") DateValue("1.2.00") DateValue("1.2.2000 ") DateValue("1. Feb")
' ' ' ' ' ' ' ' '
"01.02.2000" "01.02.2000" "01.02.2000" "01.02.2000" "01.02.2000" "01.02.2000" "01.02.2000" "01.02.2000" "01.02.2000"
Day- Funktion
Verwandte Befehle
................................................... Verwa ndte Befehle
TimeSerial, CDate
Day- Funktion Function Day(datWert As Date) As Integer Beschreibung
................................................... Bes c hreibung
Beispiel
................................................... Beis piel
Siehe »Format-Funktion« (S. 69) und das dort vorgestellte Projekt FormatDemo. Verwandte Befehle
................................................... Verwa ndte Befehle
DatePart, Hour, Minute, Month, Second, Year
FileDateTime- Funktion Function FileDateTime(sPfad As String) As Date Siehe »FileDateTime-Funktion« (S. 139 ).
FormatDateTime- Funktion Function FormatDateTime(vData, _ BenanntesFormat As VbDateTimeFormat = vbGeneralDate]) _ As String Siehe »FormatDateTime-Funktion« (S. 74 ) und »Format-Funktion« (S. 69).
Hour- Funktion Function Hour(datWert As Date) As Integer Beschreibung
................................................... Bes c hreibung
Die Funktion Hour liefert den Stundenanteil des Datums-/Zeitwerts datWert als Ganzzahl zwischen 0 und 23. Beispiel
................................................... Beis piel
Print "Es ist"; Minute(Time); " Minuten und "; Second(Time); _ "Sekunden nach"; Hour(Time) Verwandte Befehle
................................................... Verwa ndte Befehle
DatePart, Day, Minute, Month, Second, Year
Minute- Funktion Function Minute(datWert As Date) As Integer
1 21
Funktionen und Anweisungen für Datums- / Zeitwerte
Die Funktion Day liefert den Tagesanteil des Datums-/Zeitwerts datWert als Ganzzahl zwischen 1 und 31.
Funktionen und Anweisungen für Datums- / Zeitwerte
Beschreibung
................................................... Bes c hreibung
Die Funktion Minute liefert den Minutenanteil des Datums-/Zeitwerts datWert als Ganzzahl zwischen 0 und 59. Beispiel
................................................... Beis piel
Print "Es ist"; Minute(Time); " Minuten und "; Second(Time); _ "Sekunden nach"; Hour(Time)
Funktionen und Anweisungen für Datums- / Zeitwerte
Verwandte Befehle
................................................... Verwa ndte Befehle
DatePart, Day, Hour, Month, Second, Year
Month- Funktion Function Month(datWert As Date) As Integer Beschreibung
................................................... Bes c hreibung
Die Funktion Month liefert den Monatsanteil des Datums-/Zeitwerts datWert als Ganzzahl zwischen 1 und 12. Beispiel
................................................... Beis piel
Siehe »Format-Funktion« (S. 69) und das dort vorgestellte Projekt FormatDemo. Verwandte Befehle
................................................... Verwa ndte Befehle
DatePart, Day, Hour, Minute, Second, Year
MonthName- Funktion Function MonthName(lMonat As Long, bAbk As Boolean = False) As String Siehe »MonthName-Funktion« (S. 82).
Now- Funktion Function Now() As Date Beschreibung
................................................... Bes c hreibung
Die Funktion Now liefert die aktuelle Systemzeit mit Datums- und Zeitanteil. Beispiel
................................................... Beis piel
Print Now Verwandte Befehle
................................................... Verwa ndte Befehle
Time, Date
Second- Funktion Function Second(datWert As Date) As Integer
1 22
Time- Funktion und Time- Anweisung
Beschreibung
................................................... Bes c hreibung
Die Funktion Second liefert den Sekundenanteil des Datums-/Zeitwerts datWert als Ganzzahl zwischen 0 und 59. Beispiel
................................................... Beis piel
Print "Es ist"; Minute(Time); " Minuten und "; Second(Time); _ "Sekunden nach"; Hour(Time) Verwandte Befehle
DatePart, Day, Hour, Month, Minute, Year
Time- Funktion und Time- Anweisung Function Time() As Date Time = datZeit Beschreibung
................................................... Bes c hreibung
Als Funktion liefert Time die aktuelle Systemzeit und als Anweisung setzt Time den Datums-/ Zeitwert datZeit als aktuelle Systemzeit (dauerhaft). Anwendung
................................................... Anwendung
Wer nicht gerade eine Uhr programmiert, dürfte sehr gut ohne diese Funktion bzw. Anweisung auskommen. Es kommt zwar häufiger vor, dass man die aktuelle Systemzeit ausgeben muss – so etwa beim Drucken oder wenn Datensätze mit Zeitstempeln versehen werden müssen, im Allgemeinen wird dann aber die Funktion Now die bessere Lösung sein, da sie den Zeit- und den Datumsanteil der Systemzeit ermittelt. Wenn es dagegen um die Bestimmung von Zeiträumen innerhalb eines Tages geht, so etwa für Laufzeitmessungen, wird die Funktion Timer die bessere Wahl sein. Anwendung
................................................... Anwendung
Wer kennt das nicht? Das Dokument ist erst in der Nacht fertig geworden, und der Empfänger braucht das nicht unbedingt zu wissen. Anstatt umständlich auf den Zeitstempel der Datei loszugehen, ist es geschickter, der Anwendung eine Zeit lang eine falsche Zeit vorzugaukeln, während sie die Datei abspeichert. Genau das macht das Beispielprojekt ZeitStempel. ' Projekt: Zeitstempel Const cVerzögerung = 10 Const cZivilZeit = #3:59:00 PM# Dim datSysZt As Date
' Sekunden ' Kurz vor Feierabend
Private Sub Form_Load() datSysZt = Time Time = cZivilZeit Timer1.Interval = cVerzögerung * 1000 End Sub
' aktuelle Zeit merken ' Kurz vor Feierabend ... ' Timer setzen
Private Sub Timer1_Timer() Timer1.Interval = 0 t = Time
1 23
Funktionen und Anweisungen für Datums- / Zeitwerte
................................................... Verwa ndte Befehle
Funktionen und Anweisungen für Datums- / Zeitwerte
Time = datSysZt + (t – cZivilZeit) If Time < datSysZt Then Date = Date + 1 End End Sub
' Zeit richtig korrigeren ' Mitternachtskorrektur ' und Tschüss
Verwandte Befehle
................................................... Verwa ndte Befehle
Now, Date
Funktionen und Anweisungen für Datums- / Zeitwerte
Timer- Funktion Function Timer() As Single Beschreibung
................................................... Bes c hreibung
Die Funktion Timer liefert die seit Mitternacht (Systemzeit) vergangene Zeit in Sekunden mit Nachkommaanteil als Fließkommawert einfacher Genauigkeit (Windows 9x: in 18tel Sekundenschritten; Windows NT: in 50stel Sekundenschritten). Beispiel
................................................... Beis piel
Siehe die Beispiele zu »Sgn-Funktion« (S. 110). Verwandte Befehle
................................................... Verwa ndte Befehle
Time, Date
TimeSerial- Funktion Function TimeSerial( _ iStd As Integer, iMin As Integer, iSek As Integer) As Date Beschreibung
................................................... Bes c hreibung
Die Funktion TimeSerial generiert aus den Ganzzahlwerten iStd, iMin und iSek einen Datums-/ Zeitwert. Anwendung
................................................... Anwendung
Die Funktion prüft die Werte der Parameter nicht, das heißt, sie löst auch keine Laufzeitfehler aus, wenn die Komponenten keine reelle Zeit ergeben. Ergibt die akkumulierte Zeit einen Wert größer #23:59:59# oder kleiner #0:00:00#, liefert die Funktion gleichzeitig einen Datumsoffset bezogen auf das Referenzdatum 30.12.1899. Auf diese Weise gestattet die Funktion sehr schön das Rechnen mit relativen Zeitangaben wie: »vor 22 Stunden, 97 Minuten und 123 Sekunden«. Beispiel
................................................... Beis piel
cZt = #11:11:11# For iSek = 0 to 400 Step 10 Print TimeSerial(Hour(cZt), Minute(cZt), Second(cZt) + iSek) next iSek Verwandte Befehle
................................................... Verwa ndte Befehle
DatePart, DateSerial
1 24
TimeValue- Funktion
TimeValue- Funktion Function TimeValue(sZeit As String) As Date Beschreibung
................................................... Bes c hreibung
Die Funktion DateValue interpretiert die Zeichenfolge sZeit als Zeitangabe und generiert daraus einen Datums-/Zeitwert. Falls die Zeichenfolge auch Datumsangaben enthält, werden diese nicht berücksichtigt. Anwendung
................................................... Anwendung
Tipp
................................................... Tipp
CDate und TimeValue unterscheiden sich insofern, als CDate auch Werte anderer Datentypen als String in einen Datums-/Zeitwert umsetzt und zusätzlich einen Datumsanteil berücksichtigt. Beispiel
................................................... Beis piel
Alle Zeitangaben notieren die gleiche Zeit: Print Print Print Print Print Print Print
TimeValue("13:0") TimeValue("13:00:00") TimeValue("1 pm") TimeValue("13 pm") TimeValue("13 am") TimeValue("1 PM") TimeValue("13 AM")
' ' ' ' ' ' '
"13:00:00" "13:00:00" "13:00:00" "13:00:00" "13:00:00" "13:00:00" "13:00:00"
Verwandte Befehle
................................................... Verwa ndte Befehle
DateValue, CDate
Weekday- Funktion Function Weekday( _ datDatum As Date, _ [FirstDayOfWeek As VbDayOfWeek = vbSunday]) _ As Integer Beschreibung
................................................... Bes c hreibung
Die Funktion Weekday interpretiert den Datums-/Zeitwert datDatum als Zeitangabe und liefert dazu den Wochentag als Ganzzahl zwischen 1 und 7. Fehlt der optionale Parameter FirstDayOfWeek oder hat er den Wert vbSunday, interpretiert die Funktion den Sonntag als ersten Tag der Woche, ansonsten den Tag, den die Konstante ausdrückt. Warnung
................................................... Wa rnung
Microsoft hat den Standardwert für den Parameter FirstDayOfWeek nicht richtig gewählt, da der Aufruf Weekday(Now) ein falsches Ergebnis liefert. In unseren Sphären gilt der Montag als erster Tag der Woche – wohl aufgrund der biblischen Devise »und am Siebten Tage ruhte der Herr«. Es empfiehlt sich daher, die Funktion mit dem Wert vbMonday für ErsteTagWoche aufzurufen.
1 25
Funktionen und Anweisungen für Datums- / Zeitwerte
Was die Notation der Zeit betrifft, ist TimeValue weitgehend tolerant, solange nur das Trennzeichen »:« erscheint und die Zeitangabe reell ist. So interpretiert die Funktion fehlende Minutenund Sekundenanteile als »00« und kommt auch mit den Zusätzen »am«, »AM«, »pm« und »PM« zurecht, wenn die Zeit in 12-Stunden-Darstellung notiert ist.
Dateiorientierte Funktionen und Anweisungen
Beispiel
................................................... Beis piel
Alle Zeitangaben notieren die gleiche Zeit: Print WeekdayName(Weekday(Now)) Print WeekdayName(Weekday(Now, vbMonday))
' Falscher Wochentag!!! ' Richtiger Wochentag
Verwandte Befehle
................................................... Verwa ndte Befehle
Dateiorientierte Funktionen und Anweisungen
DatePart, DateValue, CDate
WeekdayName- Funktion Function WeekdayName( _ lTag As Long, _ [bAbk As Boolean = False], _ [FirstDayOfWeek As VbFirstDayOfWeek = vbUseSystemDayOfWeek]) _ As String Siehe »WeekdayName-Funktion« (S. 92).
Year- Funktion Function Year(datWert As Date) As Integer Beschreibung
................................................... Bes c hreibung
Die Funktion Year liefert den Jahresanteil des Datums-/Zeitwerts datWert als Ganzzahl zwischen 100 und 9999. Beispiel
................................................... Beis piel
Siehe »Format-Funktion« (S. 69) und das dort vorgestellte Projekt FormatDemo. Verwandte Befehle
................................................... Verwa ndte Befehle
DatePart, Day, Hour, Minute, Month, Second
Dateiorientierte Funktionen und Anweisungen Um es gleich vorwegzunehmen: Visual Basic weist nicht mehr die ursprüngliche Geschlossenheit der Sprache Basic gegenüber dem Dateisystem und der dateibezogenen Ein- und Ausgabe auf. Die Sache ist komplizierter geworden, denn verloren gegangen ist eher wenig, hinzugekommen dagegen viel. Freilich sind da noch die traditionellen Mittel, wohlbekannte Befehle wie Open, Input, Print#, Get, Put und Close, mit denen sich letztlich jeder Datei zu Leibe rücken lässt und die auch eine gesunde Basis für das delikate Thema der Abwärtskompatibilität darstellen. Zudem sind die dateisystembezogenen Befehle ChDir, ChDrive, MkDir, RmDir, Name weiterhin vorhanden. Ja, sie haben sogar noch Gesellschaft in Form recht nützlicher Befehle wie Dir, FileCopy, FileLen, GetAttr und SetAttr usw. erhalten, die man sich zu Zeiten von QBasic etwa noch als entsprechende DOS-Kommandos mit Zeichenfolgenoperationen selbst zusammenbasteln und über die Shell-Anweisung ausführen musste. Darüber hinaus versteht es sich schon fast von selbst, dass Dateinamen inzwischen lang und Pfadangaben UNC-konform (engl.: Universal Naming Convention, dazu gleich mehr) sein können, was letztlich Tür und Tor für den netzwerkweiten Zugriff auf Dateien öffnet, ohne auf logische Laufwerke oder andere Krücken aus der DOSZeit angewiesen zu sein. Damit lässt sich schon was anfangen.
1 26
Dateiorientierte Funktionen und Anweisungen
Bezeichner
Kurzbeschreibung
ChDir
Ändert das aktuelle Arbeitsverzeichnis
ChDrive
Setzt das aktuelle logische Laufwerk
Auf Dateioperationen bezogene Funktionen und Anweisungen
1 27
Dateiorientierte Funktionen und Anweisungen
Komplett neu ist dagegen alles, was der Sprache von der objektorientierten Seite her an Potenzial zuteil geworden ist. So beispielsweise das FileSystemObject-Objekt, das als operative Grundlage des Windows-Explorers ein wahres Füllhorn an Möglichkeiten darstellt, wenn es darum geht, etwas über das Dateisystem in Erfahrung zu bringen, Manipulationen darin vorzunehmen oder auch nur mit Textdateien zu arbeiten. Zudem ist das Tor weit offen für beliebige Erweiterungen in dieser Richtung – es steht Ihnen frei, Ihre ganz persönliche Klassenbibliothek für dateibezogenene Operationen zu schreiben und fortan alle Dateizugriffe über die darin definierten Objekte abzuwickeln. Das könnte beispielsweise den Vorteil haben, dass diese Objekte Ihren Anwendungen den ganzen Ballast logistischer und formaler Natur abnehmen, den bestimmte Dateiformate so mit sich bringen. Paradebeispiel für diesen Ansatz sind die Anweisungen LoadPicture, SavePicture, Load und Unload. (Wer genau hinsieht, wird erkennen, dass zumindest die ersten drei Anweisungen in BLoad und BSave ihre Vorläufer hatten). So bequem solche Operationen auch sind, ihre Implementation – und da beißt die Maus keinen Faden ab – lebt letztlich wieder von den erwähnten traditionellen Mitteln des Dateizugriffs, es sei denn, man kann dafür von entsprechenden – zumeist in C oder C++ geschriebenen – Bibliotheksroutinen profitieren oder gar von Betriebssystemroutinen aus der reichhaltigen Windows API (vgl. hierzu »Routinen aus DLLs und der Windows-API einsetzen«, S. 185). Ein etwas konsequenterer Ansatz, der der Idee der objektorientierten Programmierung näher steht, besteht darin, die im Programm verwendeten Datenstrukturen selbst als Objekte zu verpacken und ihnen Operationen wie SaveMyObject und LoadMyObject zu spendieren. Ein komplexes Objekt, das seinerseits verschiedene Objekte als Elemente enthält, wird in seiner SaveMethode dann schlicht die Save-Methoden dieser Objekte aufrufen usw. Wann immer aber – und das gilt auf letzter Ebene für alle Objektrepräsentationen – die Werte elementarer Datentypen zu speichern sind, geht es ans »Eingemachte«, und die traditionellen Mittel sind wieder gefragt. Das alles ist aber noch nicht genug. Tatkräftige Unterstützung gibt es auch noch vonseiten der Steuerelemente. Da wären schon mal die Standardsteuerelemente der Klassen FileListBox und DriveListBox, die eine interaktive Auswahl von Dateien und logischen Laufwerken ermöglichen. Da wären die Standarddialoge, die eine große Hilfe beim Öffnen und Speichern von Dateien darstellen und eine Fülle von Operationen so ganz nebenbei und kostenlos mit sich bringen (vgl. »Standarddialoge-Steuerelement (CommonDialog)«, S. 444). Und nicht zuletzt wäre da auch noch die schier unerschöpfliche Fülle an Möglichkeiten, mittels so genannter Datenquellen (Data-Steuerelement, RemoteData-Steuerelement, ADO-Daten-Steuerelement, ADORecordset-Objekte) sowie DAO- und ADO-Objekten mit Daten zu hantieren, die in Datenbanken organisiert sind. Nicht nur weil die historische Entwicklung der Sprache Basic es so hervorgebracht hat, sondern auch weil es der Aufbau dieses Buches so will, geht es erst im Kapitel »Objekte und Klassen« (S. 195) um die Dateioperationen von Objekten – und da eher nur am Rande. Ein wenig über den Umgang mit Datenbanken finden Sie im Abschnitt »Data-Datensteuerelement (Data)« (S. 402). Wer aber die in diesem Abschnitt vorgestellte traditionelle Grundlage aller Dateioperationen kennt und verstanden hat, wird auf der objektorientierten Seite keine Not haben, beides miteinander zu verheiraten. Die folgende Tabelle gibt einen Überblick über die traditionellen Funktionen und Anweisungen, die unmittelbar oder mittelbar etwas mit Dateizugriffen zu tun haben.
Dateiorientierte Funktionen und Anweisungen
Dateiorientierte Funktionen und Anweisungen
Bezeichner
Kurzbeschreibung
Close
Schließt eine oder mehrere zuvor geöffnete Dateien
CurDir
Liefert das aktuelle Arbeitsverzeichnis auf dem angegebenen logischen Laufwerk
Dir
Liefert den nächsten Dateinamen, der auf ein Suchmuster passt
Environ
Liefert eine oder alle Umgebungsvariablen
EOF
Testet, ob das Dateiende erreicht ist
FileAttr
Ermittelt den Öffnungsmodus einer Datei
FileCopy
Kopiert eine Datei
FileDateTime
Liefert den Zeitpunkt der Erstellung bzw. letzten Änderung einer Datei als Datums-/Zeitwert
FileLen
Liefert die Länge einer Datei in Bytes
FreeFile
Liefert die nächste freie Dateinummer
Get
Liest Daten aus einer indexsequenziellen Datei oder Binärdatei
GetAttr
Liefert die Attribute einer Datei oder eines Ordners bzw. Verzeichnisses
Input
Liest die angegebene Anzahl an Zeichen aus einer Datei und liefert diese als Unicode-Zeichenfolge
Input #
Liest den Wert einer oder mehrerer Variablen aus einer Textdatei
InputB
Liest die angegebene Anzahl an Bytes aus einer Datei und liefert diese als Bytearray (ANSI-Zeichenfolge)
Kill
Löscht alle Dateien, die auf das angegebene Suchmuster passen
Line Input #
Liest eine Zeile aus einer geöffneten sequenziellen Datei in eine Variable vom Typ String ein
Loc
Liefert die aktuelle Schreib- bzw. Leseposition (Byte-Nummer) einer geöffneten Datei
Lock
Regelt die Zugriffsmöglichkeiten anderer Programme auf eine bereits geöffnete Datei
LOF
Liefert die Länge einer Datei in Bytes
LSet
Zuweisungsoperation, die Daten beliebiger Datentypen für die Ausgabe in eine Datei als Zeichenfolge aufbereitet
MkDir
Generiert ein neues Verzeichnis
Name
Benennt eine Datei um oder verschiebt diese in ein anderes Verzeichnis
Open
Öffnet eine Datei zum Lesen oder Schreiben
Print#
Schreibt Daten im gebietsspezifischen Format in eine sequenzielle Datei
Put
Schreibt Daten in eine indexsequenzielle Datei oder Binärdatei
Reset
Schließt alle geöffneten Dateien
RmDir
Löscht ein Verzeichnis
Seek
Liefert die aktuelle Schreib- oder Leseposition in einer geöffneten Datei
Auf Dateioperationen bezogene Funktionen und Anweisungen
1 28
Dateiorientierte Funktionen und Anweisungen
Bezeichner
Kurzbeschreibung
Seek
Setzt die aktuelle Schreib- oder Leseposition in einer geöffneten Datei
SetAttr
Setzt ein oder mehrere Attribute für eine Datei oder ein Verzeichnis
Shell
Startet eine andere Anwendung und liefert deren Task-ID
Unlock
Regelt die Zugriffsmöglichkeiten anderer Programme auf eine bereits geöffnete Datei
Write#
Schreibt Daten in standardisiertem Format in eine sequenzielle Datei
UNC- Pfad und Laufwerkspfad Ein wichtige Größe im Umgang mit Dateien ist ihre Position im lokalen Dateisystem respektive im Netzwerk. Um eine irgendwo im Netzwerk (das kann auch auf dem lokalen System sein) gelegene Datei anzusprechen, geben Sie den UNC-Pfad der Datei an, der nach folgendem Schema notiert wird: \\Server\Freigabe\[Verzeichnis ... \]Dateiname.Erw
Liegt die Datei dagegen auf dem lokalen Computer oder existiert für seine im Netzwerk gelegene Position eine Netzlaufwerkverbindung, die der betreffenden Freigabe einen Laufwerksbuchstaben Laufw zuordnet, lässt sie sich auch über ihren traditionellen Laufwerkspfad ansprechen. Laufw:\[Verzeichnis ... \]Dateiname.Erw
Beide Pfadangaben können nach allen Regeln der Kunst absolut oder relativ zum jeweils aktuellen Arbeitsverzeichnis sein; der Unterschied zwischen beiden besteht eigentlich nur darin, dass der absolute Laufwerkspfad einen Laufwerksbuchstaben mit nachfolgenden Doppelpunkt hat, wo der absolute UNC-Pfad einen Servernamen und eine Freigabe enthält.
Fehler und deren Behandlung »Wo gehobelt wird, fallen Späne«. Da Dateioperationen sozusagen aus der geschlossenen Welt eines Programms hinausführen und mit anderen Programmen am Gerangel um Gemeingut (gemeinsam genutzte Ressourcen) teilnehmen, ist es völlig normal, dass zur Laufzeit Fehler auftreten können, die zur Entwurfszeit nicht absehbar waren. Die zu solchen Fehlern führenden Konstellationen sind recht vielfältig, so dass man sich beim Programmentwurf einer stattlichen Anzahl möglicher Laufzeitfehler gegenüber sieht, die es einzukalkulieren gilt. Hier einige der Ursachen für häufige Fehler: Nr.
Fehlermeldung
Mögliche Ursachen
52
Dateiname oder -nummer Der Dateiname einer anzulegenden Datei entspricht nicht falsch der Konvention für Dateinamen. Ihr Programm hat nicht das Recht, die Datei auf einer Freigabe anzulegen, (z.B. weil das Kennwort nicht richtig ist). Die verwendete Dateinummer wurde entweder durch eine Close-Anweisung bereits freigegeben oder nie im Rahmen einer Open-Anweisung vergeben, etwa weil diese gescheitert ist.
Häufige Laufzeitfehler im Zusammenhang mit Dateioperationen
1 29
Dateiorientierte Funktionen und Anweisungen
Auf Dateioperationen bezogene Funktionen und Anweisungen
Dateiorientierte Funktionen und Anweisungen
Dateiorientierte Funktionen und Anweisungen
Nr.
Fehlermeldung
Mögliche Ursachen
53
Datei nicht gefunden
Die Datei kann nicht geöffnet werden, weil sie nicht existiert.
55
Datei bereits geöffnet
Die Datei kann nicht geöffnet werden, weil sie bereits geöffnet ist. Passiert bei Open, Name, FileCopy, SetAttr, Kill.
57
Fehler beim Lesen von/ Schreiben auf Gerät
Das Ein- bzw. Ausgabegerät enthält einen defekten Datenträger oder die Netzwerkverbindung ist während der Dateioperation zusammengebrochen.
58
Datei existiert bereits
Sie versuchen, eine Datei mit Name umzubenennen, der angegebene Dateiname ist aber bereits an eine andere Datei vergeben.
61
Datenträger voll
Der freie Platz auf einer Diskette, Festplatte oder Netzwerkfreigabe reicht für die Durchführung der Operation nicht aus.
62
Einlesen hinter Dateiende
Die Dateioperation passt nicht zu dem Modus, in dem die Datei geöffnet wurde. Beispielsweise dürfen Sie EOF nicht im Modus Binary und die Funktion Input nicht im Modus Input verwenden.
70
Zugriff verweigert
Sie haben versucht, auf eine Datei oder einen Datensatz zuzugreifen, die bzw. der von einem anderen Prozess gesperrt wurde, oder es fehlt das Schreibrecht, um auf dem entsprechenden Datenträger zu schreiben.
71
Datenträger nicht bereit
Im angegebenen Laufwerk befindet sich kein Datenträger oder das Laufwerk wurde nicht verriegelt.
75
Fehler beim Zugriff auf Pfad/Datei
Das Format der Pfadangabe ist nicht korrekt, das Ausgabegerät unterstützt keine Schreiboperationen oder die Datei ist schreibgeschützt.
76
Pfad nicht gefunden
Der angegebene Pfad existiert nicht oder der Server, der ihn freigibt, ist nicht am Netz.
Häufige Laufzeitfehler im Zusammenhang mit Dateioperationen
In Form der On Error GoTo-Anweisung stellt Basic ein Mittel bereit, solchen Laufzeitfehlern zu begegnen. Mehr darüber finden Sie im Abschnitt »Fehlerbehandlung« (S. 43).
Dateien Um in Visual Basic eine Datei mit traditionellen Mitteln bearbeiten zu können, muss sie zunächst durch eine Open-Anweisung geöffnet werden – ein Vorgang, der unter anderem die Zugriffsrechte anderer Prozesse auf diese Datei einschränken kann und ihr eine logische Dateinummer zuordnet. Diese Dateinummer ist für alle folgenden Operationen anzugeben, die mit dieser Datei in Zusammenhang stehen. Die Zugriffsrechte auf die Datei oder Teile der Datei lassen sich auch danach noch mittels Lock- und Unlock-Anweisungen entsprechend den Erfordernissen handhaben. Sobald kein weiterer Zugriff auf die Datei mehr erfolgt, kann sie mittels Close geschlossen werden. Diese Operation schreibt gegebenenfalls noch nicht geschriebene Pufferinhalte und gibt die Dateinummer wieder frei.
1 30
ChDir- Anweisung
ChDir- Anweisung ChDir sPfad Beschreibung
................................................... Bes c hreibung
Windows kennt und pflegt in der Systemumgebung eines Programms für jedes Laufwerk ein aktuelles Arbeitsverzeichnis. Das standardmäßige Arbeitsverzeichnis (vgl. »CurDir-Funktion«, S. 134) ist das aktuelle Arbeitsverzeichnis auf dem standardmäßigen Laufwerk (vgl. »ChDriveAnweisung«, S. 132). Die Anweisung ChDir setzt sPfad als neues aktuelles Arbeitsverzeichnis auf dem in sPfad spezifizierten Laufwerk. Enthält sPfad keine Laufwerksspezifikation, setzt die Funktion das standardmäßige Arbeitsverzeichnis auf sPfad. Anwendung
................................................... Anwendung
Ist der in sPfad spezifizierte Pfad nicht existent, löst die Funktion den Laufzeitfehler 76, »Pfad nicht gefunden« aus. Solange das standardmäßige Arbeitsverzeichnis lokal auf dem Computer liegt, zeigt ChDir bei Angabe eines UNC-Pfads für sPfad keine Wirkung – meldet aber auch keinen Fehler. Ergibt sich dagegen die Situation, dass das standardmäßige Arbeitsverzeichnis auf einem anderen Computer liegt (vgl. »CurDir-Funktion«, S. 134) und somit bereits ein UNCPfad ist, kommt ChDir auch bei Angabe eines UNC-Pfads seiner Aufgabe nach. Warnung
................................................... Wa rnung
Mit dem UNC-Pfad ist das so eine Sache. Wie unter CurDir näher ausgeführt, besteht zwar die Möglichkeit, das standardmäßige Arbeitsverzeichnis von vornherein als UNC-Pfad zu erhalten, Visual Basic sieht aber keine Mittel vor, das standardmäßige Arbeitsverzeichnis, sofern es einmal auf ein lokales Verzeichnis gesetzt wurde, wieder auf einen anderen Computer (etwa einen File Server) im Netzwerk zu verlagern, sprich: in einen UNC-Pfad umzuwandeln. Damit könnte man natürlich leben, wenn sich ChDir darauf beschränken würde, ein UNC-Arbeitsverzeichnis bei Angabe eines UNC-Pfades als UNC-Arbeitsverzeichnis beizubehalten. Leider ist das jedoch
1 31
Dateiorientierte Funktionen und Anweisungen
Was die Dateioperationen betrifft, so ist zu beachten, dass Basic traditionell drei Dateikonzepte unterstützt, die sich darin unterscheiden, welche Art von Information gespeichert wird und wie der Zugriff auf diese Information erfolgt: die sequenzielle Datei (auch Textdatei genannt), die indexsequenzielle Datei und die Binärdatei. Für jede dieser Dateiarten gibt es spezifische Operationen, die speziell auf ihren Zweck zugeschnitten sind. Sequenzielle Dateien enthalten durch spezifische Trennzeichen voneinander abgetrennte Zeichenfolgen, die textuelle Repräsentationen von Werten unterschiedlicher Datentypen darstellen. Zum Schreiben dieser Werte verwendet man die Anweisungen Print# und Write#, wobei erstere die gebietsspezifische Zeichenfolgendarstellung eines Werts generiert und letztere die internationale. Das Lesen besorgt dagegen die Anweisung Input# oder – wenn auch die Trennzeichen gelesen werden sollen – die Funktion Input. Im speziellen Fall von Textdateien ermöglicht die Anweisung Line Input# das zeilenweise Lesen. Indexsequenzielle Dateien enthalten eine Folge von Datensätzen. Da alle Datensätze eine feste Länge aufweisen, kann Basic die Position eines bestimmten Datensatzes aus der Datensatznummer und der Datensatzlänge errechnen, so dass keine Trennzeichen notwendig sind. Das Schreiben von Datensätzen besorgt die Anweisung Get und das Lesen die Anweisung Put. Binärdateien liegt dagegen a priori keine bestimmte Interpretation zugrunde. Sie werden als einfache Aneinanderreihung von Bytes behandelt, und es unterliegt der Verantwortung des Programms, sie zu einzelnen Werten für Variablen der unterschiedlichen Datentypen zu gruppieren. Als Leseoperationen stehen dafür die Funktion Input und die Anweisung Get zur Verfügung und als Schreiboperation die Anweisung Put.
Dateiorientierte Funktionen und Anweisungen
nicht so: ChDir setzt bei Angabe eines UNC-Pfads, der in das Dateisystem des lokalen Computers führt, ungefragt den Laufwerkspfad. Beispiele
................................................... Beis piele
Dateiorientierte Funktionen und Anweisungen
Angenommen das Programm, das den folgenden Code enthält, ist im Verzeichnis C:\VB-Programme gelegen und wird über den Explorer gestartet: Print App.Path Print CurDir ChDir ".." Print CurDir ChDir "Windows" Print CurDir ChDir "C:\VB-Programme" Print CurDir ChDir "D:\Eigene Dateien" Print CurDir ChDrive "D" Print CurDir
' ' ' ' ' ' ' ' ' ' ' '
C:\VB-Programme C:\VB-Programme (wie App.Path) Ein Verzeichnis höher C:\ relativer Pfad C:\Windows absoluter Pfad auf Standardlaufwerk C:\VB-Programme absoluter Pfad auf Nicht-Standardlaufwerk C:\VB-Programme (C ist Standardlaufwerk) erst jetzt ändert sich Standardlaufwerk D:\Eigene Dateien (D ist Standardlaufwerk)
Angenommen das Programm, das den folgenden Code enthält, liegt im Verzeichnis \\Adler\c\VB-Programme\ und wird über die Netzwerkumgebung des Explorers gestartet: Print Print ChDir Print ChDir Print ChDir Print
App.Path CurDir ".." CurDir "Windows" CurDir "C:\VB-Programme" CurDir
' ' ' ' ' ' ' '
\\Adler\c\VB-Programme\ \\Adler\c\VB-Programme\ (wie App.Path) Ein Verzeichnis höher \\Adler\c\ relativer Pfad \\Adler\c\windows UNC-Pfad ist ab jetzt verloren C:\VB-Programme
Verwandte Befehle
................................................... Verwa ndte Befehle
ChDrive, CurDir
ChDrive- Anweisung Sub ChDrive (sLaufw As String) Beschreibung
................................................... Bes c hreibung
Windows kennt und pflegt in der Systemumgebung eines Programms für jedes Laufwerk ein aktuelles Arbeitsverzeichnis. Die Anweisung ChDrive setzt das erste Zeichen von sLaufw als neues aktuelles Laufwerk. Das aktuelle Arbeitsverzeichnis auf diesem Laufwerk wird damit zum standardmäßigen Arbeitsverzeichnis. Anwendung
................................................... Anwendung
Ist sLaufw eine leere Zeichenfolge, ändert ChDrive das aktuelle Laufwerk nicht, meldet aber auch keinen Fehler. Existiert das erste Zeichen von sLaufw nicht als logisches Laufwerk, löst die Funktion den Laufzeitfehler 68, »Gerät nicht verfügbar«, aus.
1 32
Close- Anweisung
Warnung
................................................... Wa rnung
Ergibt sich die Situation, dass das standardmäßige Arbeitsverzeichnis auf einem anderen Computer liegt (vgl. »CurDir-Funktion«, S. 134) und somit durch einen UNC-Pfad beschrieben wird, geht dieser mit dem nächsten Aufruf von ChDrive unwiederbringlich verloren, das heißt, als standardmäßiges Arbeitsverzeichnis lassen sich fortan nur noch Verzeichnisse einstellen, auf die der Zugriff über ein logisches Laufwerk erfolgen kann. Beispiel
................................................... Beis piel
Siehe das Beispiel zu »ChDir-Anweisung« (S. 131).
................................................... Verwa ndte Befehle
ChDir, CurDir
Close- Anweisung Close [#]iDateiNr[, [#]DateiNr1, ...] Beschreibung
................................................... Bes c hreibung
Die Anweisung Close schließt die unter den Dateinummern iDateiNr, iDateiNr1 usw. (mittels Open) geöffneten Dateien oder Geräte. Gegebenenfalls existierende Schreibpuffer werden zuvor herausgeschrieben. Wird der Befehl ohne Parameter aufgerufen, schließt er alle mit Dateinummern assoziierten Dateien und Geräte. Anwendung
................................................... Anwendung
Bevor ein Programm mit traditionellen Mitteln auf eine Datei oder ein Gerät zugreifen kann, muss es eine Open-Anweisung durchführen, die der Datei eine Dateinummer zuordnet und gegebenenfalls Pufferspeicher organisiert. Aus Gründen der Effizienz puffert Visual Basic alle Schreib- und Leseoperationen für Dateien, die im Input-, Output- oder Append-Modus geöffnet wurden, und führt sie blockweise aus. Das hat unter anderem die Folge, dass Schreiboperationen wie Print# ihre Daten nicht direkt auf den Datenträger schreiben, sondern immer erst, wenn der Schreibpuffer voll ist. Der Befehl Close hat nun folgende Funktionen: Er bewirkt für jede genannte Dateinummer ein explizites Herausschreiben gegebenenfalls noch gefüllter Schreibpuffer mit nachfolgender Freigabe des Puffers und der Dateinummer. Die Freigabe einer Dateinummer durch Close hebt gleichzeitig alle Zugriffsbeschränkungen auf, die für die betreffende Datei mittels Open und Lock getroffen wurden. Entgegen anders lautenden Informationen in der Online-Hilfe zu Visual Basic entfernt Close alle Sperren, die mit Lock gesetzt und nicht explizit mit Unlock freigegeben wurden. Tipp
................................................... Tipp
Zwar schließt ein Visual-Basic-Programm bei Beendigung von sich aus alle Dateien und Geräte durch einen impliziten Close-Aufruf, dennoch ist es guter defensiver Programmierstil, eine Datei unmittelbar, nachdem der letzte Zugriff darauf erfolgt ist, wieder zu schließen. Das verkürzt die Einschränkungen, denen andere Programme in Bezug auf diese Datei ausgesetzt sind, auf ein Minimum. Beispiel
................................................... Beis piel
Der folgende Code lässt sich in einem Programm einsetzen, dessen Aufrufe protokolliert werden sollen:
1 33
Dateiorientierte Funktionen und Anweisungen
Verwandte Befehle
Dateiorientierte Funktionen und Anweisungen
Open "Aufrufe.log" For Append As 1 Print #1, "Aufruf am "; Now Close 1 Verwandte Befehle
................................................... Verwa ndte Befehle
Open
CurDir- Funktion
Dateiorientierte Funktionen und Anweisungen
Function CurDir[(sLaufw As String)] As String Beschreibung
................................................... Bes c hreibung
Windows kennt und pflegt in der Systemumgebung eines Programms für jedes Laufwerk ein aktuelles Arbeitsverzeichnis und ein aktuelles Laufwerk. Im Allgemeinen ist das standardmäßige Arbeitsverzeichnis dann das aktuelle Arbeitsverzeichnis auf dem aktuellen Laufwerk. Die Funktion CurDir liefert das aktuelle Arbeitsverzeichnis auf dem Laufwerk sLaufw. Fehlt der optionale Parameter sLaufw, liefert die Funktion das standardmäßige Arbeitsverzeichnis. Anwendung
................................................... Anwendung
Unmittelbar nach Programmstart entspricht das standardmäßige Arbeitsverzeichnis dem Verzeichnis, aus dem heraus der Start erfolgt ist. Erfolgt der Start direkt durch Aufruf der ausführbaren Datei aus einem Fenster des Explorers heraus, deckt sich das Arbeitsverzeichnis mit dem Programmverzeichnis (vgl. »App.Path-Eigenschaft«, S. 264). Wird der Programmstart dagegen von einem anderen Programm veranlasst, erbt das Visual-Basic-Programm schlicht das standardmäßige Arbeitsverzeichnis des Aufrufers. Das ist so, wenn der Aufruf aus einem Kommandozeileninterpreter oder Scripting-Host heraus erfolgt, wenn er von einem anderen Visual-Basic-Programm aus mittels einer Shell-Anweisung erfolgt oder wenn er über eine Verknüpfung führt, in deren Eigenschaften unter AUSFÜHREN IN ein anderer Pfad als das Programmverzeichnis eingetragen ist. Das ist aber auch so, wenn das standardmäßige Arbeitsverzeichnis des Aufrufers auf einem anderen Computer im Netzwerk liegt. In diesem Fall lässt sich das standardmäßige Verzeichnis nur als UNC-Pfad notieren, wenn es kein logisches Laufwerk für die entsprechende Freigabe gibt. Warnung
................................................... Wa rnung
Ist das standardmäßige Verzeichnis einmal ein Laufwerkspfad, lassen sich fortan nur noch solche Verzeichnisse als standardmäßiges Arbeitsverzeichnis setzen, die über ein logisches Laufwerk ansprechbar sind. UNC-Pfade sind nicht mehr möglich. Tipp
................................................... Tipp
Beim Öffnen von Dateien, für die kein absoluter Pfad spezifiziert ist, bezieht sich Visual Basic auf das ererbte Arbeitsverzeichnis. Das aber kann mal so und mal so aussehen, je nach dem, ob das Programm in der Entwicklungsumgebung, als kompilierte Fassung, über eine Verknüpfung oder aus einem Explorer-Fenster heraus gestartet wurde. Als probates Mittel, um einen definierten und situationsunabhängigen Ausgangspunkt für das Laden von Hilfsdateien zu erhalten, empfiehlt es sich, entsprechende Pfadangaben auf die Exe-Datei zu beziehen und das standardmäßige Arbeitsverzeichnis via ChDir auf den Wert von App.Path zu setzen. Beispiel
................................................... Beis piel
Siehe »ChDir-Anweisung« (S. 131)
1 34
Dir- Funktion
Verwandte Befehle
................................................... Verwa ndte Befehle
ChDir, ChDrive
Dir- Funktion Function Dir [(sSuchmuster As String, _ [Attributvkt As VbFileAttribute = vbNormal])] As String Beschreibung
................................................... Bes c hreibung
Anwendung
................................................... Anwendung
Die überaus nützliche Funktion Dir vereinigt in sich das Funktionsschema der Win32-APIFunktionen FindFirstFile, FindNextFile und FindClose. Um alle Dateinamen in einem Verzeichnis zu erhalten, die auf ein gegebenes Suchmuster passen und darüber hinaus bestimmte Attribute aufweisen, legen Sie mit dem ersten Aufruf von Dir das Suchmuster und gegebenenfalls den Attributvektor fest und rufen Dir ab dann so oft parameterlos auf, bis die Funktion die leere Zeichenfolge liefert. Das Suchmuster besteht aus einem Pfadanteil (absolut oder relativ notiert) und einem Dateianteil, wobei der Pfadanteil das Verzeichnis spezifiziert, das durchsucht wird, und der Dateianteil ein Dateinamensmuster, das auch die Platzhalter »?« (Zeichen an dieser Stelle wird nicht unterschieden) und »*« (Zeichen ab dieser Stelle werden nicht unterschieden) enthalten kann. Fehlt der Pfadanteil oder ist er relativ notiert, bezieht die Funktion den Pfad auf das standardmäßige Arbeitsverzeichnis (vgl. »CurDir-Funktion«, S. 134). Um den Namen eines Datenträgers zu erfahren, geben Sie das Stammverzeichnis des Laufwerks als Suchmuster an (z.B. C:\) und die Konstante vbVolume als Attributvektor. In anderen Pfaden hat vbVolume keine Wirkung. Die in der VbFileAttribute-Aufzählung enthaltene Konstante vbAlias ist für zukünftige Zwecke definiert. Unter Visual Basic 6.0 (SP3) löst sie einen Laufzeitfehler aus. Warnungen
................................................... Wa rnungen Da diese Funktion in der von Microsoft stammenden Dokumentation recht lieblos abgehandelt wurde, ist viel Falsches darüber zu lesen, insbesondere was den Attributvektor betrifft. Richtig ist: Die Funktion schränkt die Ergebnismenge nicht auf Dateien mit bestimmten Attributen ein, sondern sie erweitert die zu vbNormal gehörige Ergebnismenge um die Dateien, die eines der im Attributvektor genannten zusätzlichen Attribute tragen. Die mit Visual Basic vorliegende Implementation von Dir ist nicht für den rekursiven Aufruf geeignet. Das Beispiel zu »RmDir-Anweisung» (S. 155) zeigt einen Work-around. Ein Beispiel für eine alternative Implementation, die speziell für rekursive Prozeduren geeignet ist, finden Sie im Abschnitt »Routinen aus DLLs und der Windows-API einsetzen« (S. 185).
1 35
Dateiorientierte Funktionen und Anweisungen
Die Funktion Dir kennt zwei unterschiedliche Arbeitsmodi. Wird sie unter Angabe eines Werts für den optionalen Parameter sSuchmuster aufgerufen, interpretiert sie diesen als Suchmuster für Dateinamen und liefert den ersten Dateinamen, der passt. Passt das Suchmuster auf keinen Dateinamen, gibt sie die leere Zeichenfolge zurück. Wird die Funktion Dir, nachdem sie einmal mit Suchmuster aufgerufen wurde, ohne den optionalen Parameter sSuchmuster aufgerufen, liefert sie den jeweils nächsten Dateinamen, der auf das Suchmuster passt. Der zweite optionale Parameter Attributvkt ermöglicht die Angabe eines Attributvektors, der die Ergebnismenge um Dateien mit entsprechenden Attributen erweitert(!).
Dateiorientierte Funktionen und Anweisungen
Tipp
................................................... Tipp
Falls Sie sich nur für Dateien mit bestimmten Attributen interessieren, übergeben Sie Dir diese Attribute als Attributvektor und filtern dann die gewünschten Dateien mittels GetAttr aus der von Dir gelieferten Ergebnismenge selbst heraus. Beispiel
................................................... Beis piel
Dateiorientierte Funktionen und Anweisungen
Das folgende Codefragment liefert alle Dateien im Verzeichnis C:\Eigene Dateien, deren Dateiname mit »T« beginnt, mindestens zwei Zeichen enthält und die Erweiterung »txt« trägt: sDatName = Dir("C:\Eigene Dateien\T?*.txt") While sDatName <> "" Print sDatName Dir Wend
Und dieses Codefragment liefert nur die Dateien aus dem Verzeichnis C:\, die verborgen und schreibgeschützt sind: Attr = vbHidden + vbReadOnly Pfad = "C:\" sDatName = Dir(Pfad + "*.*", Attr) While sDatName <> "" If (GetAttr(Pfad + sDatName) And Attr) = Attr Then ' Filter Print sDatName End If sDatName = Dir Wend Verwandte Befehle
................................................... Verwa ndte Befehle
ChDir, CurDir, FileDateTime, GetAttr Verwandte Themen
................................................... Verwandte Them en
Routinen aus DLLs und der Windows-API einsetzen (S. 185)
Environ- Funktion Function Environ(sEnvVar As String) Function Environ(iEnvIndex As Integer) Beschreibung
................................................... Bes c hreibung
Wird die Funktion Environ mit einer Zeichenfolge als Parameter aufgerufen, spezifiziert sEnvVar den Namen einer Umgebungsvariablen. Environ liefert dann den Wert dieser Umgebungsvariablen oder die leere Zeichenfolge, wenn diese nicht bekannt ist. Bei Aufruf der Funktion mit einem Zahlenwert liefert die Funktion dagegen die durch iEnvIndex spezifizierte Zeile aus der Umgebungsdefinition oder die leere Zeichenfolge, falls die Umgebungsdefinition weniger Zeilen umfasst.
1 36
EOF- Funktion
Anwendung
................................................... Anwendung
Auch wenn diese Funktion nicht unmittelbar etwas mit Dateizugriffen zu tun hat, mittelbar hat sie es schon, weil sie Informationen über vordefinierte Pfade (PATH, TEMP, TMP, windir, winbootdir usw.) liefert. Beispiel
................................................... Beis piel
Der folgende Code gibt die gesamte Umgebungsinformation eines Programms aus:
Vgl. auch das Beispiel zu »Kill-Anweisung« (S. 144).
EOF- Funktion Function EOF(iDateiNummer As Integer) As Boolean Beschreibung
................................................... Bes c hreibung
Die Funktion EOF liefert True, wenn das Dateiende einer unter der Nummer iDateiNummer geöffneten Datei beim Lesen erreicht ist, ansonsten False. Anwendung
................................................... Anwendung
Leseoperationen auf Dateien können nur so lange durchgeführt werden, bis das Dateiende erreicht ist. Um festzustellen, ob dies der Fall ist, wird die Funktion EOF eingesetzt. Bei Dateien, die mit dem Zugriffsmodus Input geöffnet wurden, liefert sie False, wenn die letzte Leseoperation (meist Line Input #) durch das Ende der Datei beendet wurde. Im Zusammenhang mit den Zugriffsmodi Binary und Random liefert sie True, wenn die letzte Leseoperation (Input bzw. Get) aufgrund des Dateiendes nicht oder nicht vollständig ausgeführt werden konnte. Warnung
................................................... Wa rnung
Die Online-Hilfe zu EOF bezichtigt diese Funktion, Laufzeitfehler zu produzieren, wenn sie im Zugriffsmodus Binary für die Kontrolle der Input-Funktion benutzt wird. Das ist falsch. Input ist so implementiert, dass es im Modus Binary generell keinen Laufzeitfehler auslöst. Zu einem Laufzeitfehler kann es dagegen kommen, wenn EOF im Zugriffsmodus Input für die Kontrolle der Funktion Input benutzt wird und diese mehr als ein Zeichen je Aufruf liest. Beispiel
................................................... Beis piel
Der folgende Code liest die Textdatei Scandisk.log zeilenweise für die Ausgabe durch Print: Open "c:\scandisk.log" For Input As 1 While Not EOF(1) Line Input #1, sZeile Print sZeile Wend Close 1
1 37
Dateiorientierte Funktionen und Anweisungen
i = 1 While Environ(i) <> "" Print Environ(i) i = i + 1 Wend
Dateiorientierte Funktionen und Anweisungen
Verwandte Befehle
................................................... Verwa ndte Befehle
LOF, Loc, Get, Input, Line Input, Open, Seek
FileAttr- Funktion Function FileAttr(iDateiNummer As Integer[, iTyp As Integer = 1]) Beschreibung
Dateiorientierte Funktionen und Anweisungen
................................................... Bes c hreibung
Die Funktion FileAttr liefert eine Aussage darüber, in welchem Modus die zu der Dateinummer iDateiNummer gehörige Datei geöffnet wurde. Es sind folgende Funktionswerte möglich: 1 steht für Input, 2 für Output, 4 für Random, 8 für Append und 32 für Binary. Anwendung
................................................... Anwendung
Der zweite, optionale Parameter wird nur noch aus Kompatibilitätsgründen mitgeschleppt. Er hatte eine Bedeutung, als es noch 16-Bit-Systeme gab. Auf 32-Bit-Systemen muss er 1 sein. Beispiel
................................................... Beis piel
Open sDatei For Binary As 2 Print FileAttr(2) Close2
' Ausgabe: 32
Verwandte Befehle
................................................... Verwa ndte Befehle
Open
FileCopy- Anweisung Sub FileCopy(sQuellDatei As String, sZielDatei As String) Beschreibung
................................................... Bes c hreibung
Die Anweisung FileCopy erstellt eine Kopie der Datei sQuellDatei als sZielDatei. Anwendung
................................................... Anwendung
Es versteht sich, dass sQuellDatei einen gültigen Pfad zu einer existierenden Datei und sZielDatei einen gültigen Pfad mit gültigem Dateinamen enthalten müssen. Die Funktion löst verschiedene Laufzeitfehler aus – so den Laufzeitfehler 76, wenn mit den Pfaden etwas nicht in Ordnung ist, den Laufzeitfehler 53, wenn die Datei nicht existiert, und den Laufzeitfehler 55, wenn die Datei zum Schreiben geöffnet wurde. Warnung
................................................... Wa rnung
Sollte unter dem Dateinamen sZielDatei bereits eine andere Datei existieren, wird diese kommentarlos überschrieben. Beispiel
................................................... Beis piel
FileCopy sQuellDatei, sZieldatei Verwandte Befehle
................................................... Verwa ndte Befehle
Kill, Name
1 38
FileDateTime- Funktion
FileDateTime- Funktion Function FileDateTime(sDatei As String) As Date Beschreibung
................................................... Bes c hreibung
Die Funktion FileDateTime liefert das letzte Änderungsdatum bzw. das Erstellungsdatum der Datei oder des Verzeichnisses sDatei als Datums-/Zeitwert. Beispiel
................................................... Beis piel
Print FileDateTime ("C:\Windows")
' Datum der Windows-Installation
................................................... Verwa ndte Befehle
FileLen, GetAttr
FileLen- Funktion Function FileLen(sDatei As String) As Long Beschreibung
................................................... Bes c hreibung
Die Funktion FileDateTime liefert die Länge der Datei sDatei in Bytes. Spezifiziert sDatei ein Verzeichnis, gibt die Funktion den Wert 0 zurück. Anwendung
................................................... Anwendung
Ein Aufruf dieser Funktion für eine zum Schreiben geöffnete Datei liefert die Länge der Datei, bevor diese zum Schreiben geöffnet wurde. Die aktuelle Länge einer bereits geöffneten Datei ermittelt die Funktion LOF. Beispiel
................................................... Beis piel
Print FileLen("C:\Msdos.sys")
' muss größer 1024 Bytes sein
Verwandte Befehle
................................................... Verwa ndte Befehle
FileDateTime, GetAttr, LOF
FreeFile- Funktion Function FreeFile[(iBereich As Integer)] Beschreibung
................................................... Bes c hreibung
Die Funktion FreeFile liefert jeweils die kleinste freie Dateinummer im Block iBereich. Fehlt der optionale Parameter iBereich oder hat er den Standardwert 0, entstammt die gelieferte Dateinummer dem Block 0 bis 255, ansonsten dem Block 256 bis 512. Anwendung
................................................... Anwendung
Nachdem Dateinummern globale Größen in einem Modul sind, ist es in größeren Programmen oft schwierig, den Überblick darüber zu behalten, welche bereits vergeben sind und welche nicht. Indem Sie sich neue Dateinummern von FreeFile zuteilen lassen, entlasten Sie sich von der gesamten Problematik.
1 39
Dateiorientierte Funktionen und Anweisungen
Verwandte Befehle
Dateiorientierte Funktionen und Anweisungen
Beispiel
................................................... Beis piel
iBer = FreeFile Open sDatei For Input As iBer ... Close iBer
Vgl. auch das Beispiel im Abschnitt »Fehlerbehandlung« (S. 43). Verwandte Befehle
Dateiorientierte Funktionen und Anweisungen
................................................... Verwa ndte Befehle
Open
Get- Anweisung Get [#]iDateiNr, [lSatzNr], Variable Beschreibung
................................................... Bes c hreibung
Die Anweisung Get ist das Gegenstück zur Anweisung Put. Sie liest Daten aus der Datei mit der Dateinummer iDateiNr und weist diese der Datensatzvariablen Variable als Wert zu. Bei Angabe einer Satznummer lSatzNr liest die Anweisung im Modus Random den entsprechenden Datensatz unter Berücksichtigung der bei der Open-Anweisung vereinbarten Datensatzlänge und im Modus Binary ab der Byteposition lSatzNr. Fehlt lSatzNr, liest die Anweisung jeweils den nächsten Datensatz (Random) bzw. ab der aktuellen Leseposition (Binary) und aktualisiert danach den Datensatzzeiger bzw. die aktuelle Leseposition. Die Datensatzvariable kann einen beliebigen Typ tragen. Objekte lassen sich mittels Get jedoch nicht en bloc einlesen. Anwendung
................................................... Anwendung
Wenn es um das Schreiben und Lesen von Dateiformaten ging, waren Get und Put schon immer die Arbeitspferde der Sprache Basic. Die Arbeitsweise dieser Anweisungen ist traditionell auf indexsequenzielle Dateien zugeschnitten, deren Bearbeitung im Modus Random erfolgt. Get und Put werden aber auch – jedoch mit leicht modifizierter Arbeitsweise – im Modus Binary eingesetzt.
Der Modus Random Beim Öffnen von Dateien im Modus Random wird normalerweise eine Datensatzlänge in Bytes festgelegt, die fortan bestimmt, wie viele Byte mit jeder Get- bzw. Put-Operation je Datensatz gelesen bzw. geschrieben werden. Die Zählung der Datensätze beginnt bei 1. Den Bytebedarf der elementaren Datentypen entnehmen Sie der Tabelle im Abschnitt »Elementare Datentypen« (S. 49); den von Datentypen mit fester Länge sowie von benutzerdefinierten Datentypen ermittelt man am besten mittels LenB, da die Speicherausrichtung der Datenfelder noch eine zusätzliche Rolle spielt. Trägt die Datensatzvariable einen Typ variabler Länge (bzw. einen Typ, der Felder variabler Länge enthält), muss der Datensatz einen Deskriptor (lies: Längenangabe) enthalten. Dabei gilt, dass ein Deskriptor dem Wert unmittelbar vorausgeht. Mithin muss also der Längenbedarf eines oder mehrerer Deskriptoren bei der Berechnung der Datensatzlänge berücksichtigt werden. Für Zeichenfolgen variabler Länge schlagen dabei 2 Längenbyte (!) zu Buche, für Variant-Variablen gleichfalls zunächst 2 Bytes für den VarType-Typ plus gegebenenfalls weitere Längenbyte für Wertanteile mit variablen Längen. Dynamische Arrays schlagen mit 2 Bytes für den Array-Deskriptor zu Buche plus 8 Bytes je Dimension. Die maximale Datensatzlänge beträgt 32.767.
1 40
Get- Anweisung
Die Wertzuweisung an Variablen mit Datentypen fester Länge erfolgt als linksbündige Prokrustes-Zuweisung (vgl. »LSet-Anweisung«, S. 80), wenn die Datensatzlänge nicht dem Längenbedarf der Variablen entspricht.
Der Modus Binary
Warnung
................................................... Wa rnung
Get liest ANSI-Zeichenfolgen als Unicode und Put schreibt Unicode-Zeichenfolgen als ANSICode. Beispiel
................................................... Beis piel
Der folgende Code schreibt eine Zeichenfolge und einen Double-Wert in eine indexsequenzielle Datei und liest diese Werte dann einmal im Modus Random und einmal im Modus Binary: ' Schreiben der Daten Open "Test.Idx" For Random As 1 Len = 8 Dim sDatenSatz As String ' Zeichenfolge (Unicode!) Dim a As Double ' Double-Wert sDatenSatz = "abc" ' Initialisierung a = 2.1E-122 Put 1, , sDatenSatz ' erster Datensatz wird geschrieben Put 1, 17, a ' 17ter Datensatz Close 1 ' Lesen der Daten im Modus Random sDatenSatz = "" a = 0 Open "Test.Idx" For Random As 1 Len Get 1, , sDatenSatz Print Len(sDatenSatz); sDatenSatz Get 1, 17, a Print a Close 1 ' Lesen der Daten im Modus Binary sDatenSatz = Space(3) a = 0 Open "Test.Idx" For Binary As 1 Get 1, 3, sDatenSatz Print Len(sDatenSatz), sDatenSatz Get 1, (17 – 1) * 8 + 1, a Print a Close 1
' Initialisierung = 8 ' erster Datensatz wird gelesen ' Ausgabe: "3 abc" (Deskriptor!) ' Doublewert braucht 8 Bytes! ' Ausgabe: 2,1E-122
' drei Unicodezeichen sind zu lesen ' Initialisierung ' ' ' '
Deskriptoranteil überspringen! Ausgabe: "3 abc" (Deskriptor!) Datensatzlänge 8 einberechnen Ausgabe: 2,1E-122
1 41
Dateiorientierte Funktionen und Anweisungen
Für den Modus Binary gilt im Wesentlichen dasselbe, mit folgenden Abweichungen: Eine Datensatzlänge wird nicht berücksichtigt, und die Anweisung liest ab der aktuellen Position bzw. der Position lSatzNr so viele Bytes, wie für den Wert der Datensatzvariablen erforderlich sind. Zeichenfolgen variabler Länge behandelt Get daher wie Zeichenfolgen fester Länge und liest nur so viele Zeichen, wie der Wert der String-Variablen aktuell enthält – ein Deskriptoranteil entfällt somit. Analoges gilt auch für Arrays. Im Zusammenhang mit benutzerdefinierten Datentypen finden Deskriptoranteile einzelner Datenfelder jedoch sehr wohl Berücksichtigung.
Dateiorientierte Funktionen und Anweisungen
Verwandte Befehle
................................................... Verwa ndte Befehle
Input, Input#, Line Input#, Open, LenB, LSet, Print#, Put Verwandte Themen
................................................... Verwandte Them en
Elementare Datentypen (S. 49); Benutzerdefinierte Datentypen (S. 60)
GetAttr- Funktion
Dateiorientierte Funktionen und Anweisungen
Function GetAttr(sPfad As String) As Integer Beschreibung
................................................... Bes c hreibung
Die Funktion GetAttr liefert die Attribute der Datei oder des Verzeichnisses sPfad als Attributvektor. Anwendung
................................................... Anwendung
Um festzustellen, ob in dem Attributvektor ein bestimmtes Attribut (als Bit) gesetzt ist, führen Sie eine And-Operation mit dem für das Attribut definierten Wert durch. Als Element des Aufzählungstyps VbFileAttribute ist für ihn eine Konstante definiert. Zur Auswahl stehen: Konstante
Wert
Beschreibung
vbNormal
0
Keines der Bits im Attributvektor ist gesetzt. Es handelt sich um eine gewöhnliche Datei.
vbReadOnly
1
Datei/Verzeichnis ist schreibgeschützt.
vbHidden
2
Datei/Verzeichnis ist verborgen.
vbSystem
4
Datei ist Systemdatei bzw. Verzeichnis ist Systemverzeichnis.
vbVolume
8
Logischer Datenträger (wird von GetAttr nicht geliefert)
vbDirectory
16
Verzeichnis
vbArchive
32
Datei wurde seit der letzten Datensicherung geändert
Konstanten für Dateiattribute Beispiel
................................................... Beis piel
Dim Attribut As VbFileAttribut Attribut = vbHidden If Attribut And GetAttr(sDatei) Then
' Datei ist verborgen
Wie man das Vorhandensein mehrerer Attribute prüft, zeigt das Beispiel zur »Dir-Funktion« (S. 135). Verwandte Befehle
................................................... Verwa ndte Befehle
Dir, FileAttr, SetAttr
Input- und InputB- Funktion Function Input(lZeichenAnz, [#]iDateiNr) As String Function InputB(lBytes, [#]iDateiNr) As String
1 42
Input #- Anweisung Beschreibung
................................................... Bes c hreibung
Die Funktion Input liest lBytes Bytes aus der unter der Dateinummer iDateiNr im Modus Input oder Binary geöffneten Datei und liefert diese als Unicode-Zeichenfolge, wobei aus jedem Byte ein Unicode-Zeichen wird. In Abweichung dazu liefert InputB ein echtes Byte-Array in Form einer ANSI-Zeichenfolge. Anwendung
................................................... Anwendung
Warnung
................................................... Wa rnung
Fallen Sie nicht darauf herein, dass Input einzelne Bytes in Unicode-Zeichen umwandelt – und somit aus einem Byte zwei Bytes macht. Um wirklich die gelesene Bytefolge zu erhalten, müssen Sie mit InputB arbeiten. Wenn Sie Input oder InputB beim Lesen einer Textdatei mittels EOF kontrollieren wollen, riskieren Sie einen Laufzeitfehler, sobald die Funktion mehr als ein Byte auf einmal lesen muss. Man arbeitet in diesem Fall besser mit dem Wert von LOF bzw. der Differenz aus den Werten von LOF und Seek. Beispiel
................................................... Beis piel
Open sTextDatei For Input As 1 sGesamteDatei = Input(LOF(1), 1) Close 1 Open sBinärDatei For Binary As 1 sGesamteDatei = InputB(LOF(1), 1) Close 1
' Textdatei ' Gesamte Datei auf einmal lesen ' Binärdatei ' Gesamte Datei als Bytearray lesen
Verwandte Befehle
................................................... Verwa ndte Befehle
Get, Print#, Put, LOF, EOF
Input #- Anweisung Input #iDateiNr, Variable[, Variable ...] Beschreibung
................................................... Bes c hreibung
Die Anweisung Input# liest den im literalen Standardformat repräsentierten Wert einer oder mehrerer Variablen aus einer sequenziellen Datei, die im Modus Input oder Binary geöffnet wurde. Als Trennzeichen zwischen zwei aufeinander folgenden Werten wertet die Anweisung ein Komma oder ein Wagenrücklaufzeichen Chr(13) bzw. vbCr sowie die Kombination Chr(13)+Chr(10) bzw. vbCrLf. Führende und abschließende Leerzeichen sowie doppelte Anführungszeichen schneidet die Anweisung ab.
1 43
Dateiorientierte Funktionen und Anweisungen
Traditionell ist Input (heute InputB genannt) die Leseoperation für Binärdateien (Modus: Binary), Get die Leseoperation für indexsequenzielle Dateien (Modus: Random) und Input# die Leseoperation für Textdateien (Modus: Input). Binärdateien lassen sich aber genauso gut auch mit Get lesen und Textdateien mit Input. Im Gegensatz zu Input# liefert Input alle gelesenen Zeichen, einschließlich Kommas, Wagenrücklaufzeichen (Chr(13) bzw. vbCr), Zeilenvorschubzeichen (Chr(10) bzw. vbLf), Anführungszeichen und führende Leerzeichen.
Dateiorientierte Funktionen und Anweisungen
Anwendung
................................................... Anwendung
Der traditionelle Zweck der Anweisung Input# ist, Daten aus sequenziellen Dateien zu lesen, denen unterschiedliche Datentypen zugrunde gelegt sind. Die Anweisung nimmt daher beim Lesen des Werts für eine Variable eines bestimmten Datentyps von sich aus eine entsprechende Typumwandlung vor. Warnung
Dateiorientierte Funktionen und Anweisungen
................................................... Wa rnung
Im Unterschied zu Line Input# interpretiert Input# auch Kommas als Trennzeichen, wenn diese nicht von Anführungszeichen eingeschlossen sind. Daher hat die Funktion insbesondere Schwierigkeiten mit dem Einlesen von Dezimalzahlen mit Nachkommaanteil sowie Datumswerten, wenn diese mit Print# geschrieben wurden. Print# legt der textuellen Darstellung von Zahlen- und Datumswerten nämlich das gebietsspezifische Format zugrunde. Für das Gebietsschema »Deutsch (Standard)« ist das Dezimaltrennzeichen ein Komma, und das Datumsformat ist komplett anders als das amerikanische. Sie sollten die Daten daher mit der Write#-Anweisung in die Datei schreiben, weil diese grundsätzlich das internationale Standardformat benutzt, das auch für die Notation von Literalen im Quelltext verwendet wird. Beispiel
................................................... Beis piel
' Daten schreiben Open "test.seq" For Output As 1 Write #1, 270; "Er sagte, ""Hi!""" ' Die Anweisung schreibt 2 Werte (!) Close 1 ' Daten lesen Dim i As Integer, a As String, b As String Open "test.seq" For Input As 1 Print Input(24, 1) ' So steht es in der Datei: 270,"Er sagte, ""Hi!""" Seek 1, 1 ' Lesezeiger wieder an den Anfang Input #1, i, a, b ' Die Anweisung liest drei Werte (!), wegen " Print i, a, b ' Ausgabe: 270 Er sagte, Hi! Verwandte Befehle
................................................... Verwa ndte Befehle
EOF, Get, Line Input#, Input, Print#, Put, Write# Verwandte Themen
................................................... Verwandte Them en
Literale und Konstanten (S. 27)
Kill- Anweisung Sub Kill(sSuchmuster As String) Beschreibung
................................................... Bes c hreibung
Die Anweisung Kill löscht alle Dateien, deren Dateiname auf das Suchmuster sSuchmuster passt. Anwendung
................................................... Anwendung
Das Suchmuster besteht aus einem Pfadanteil (absolut oder relativ notiert) und einem Dateianteil, wobei der Pfadanteil das Verzeichnis spezifiziert, das durchsucht wird, und der Dateianteil ein Dateinamensmuster, das auch die Platzhalter »?« (Zeichen an dieser Stelle wird nicht unter-
1 44
Line Input #- Anweisung schieden) und »*« (Zeichen ab dieser Stelle werden nicht unterschieden) enthalten kann. Fehlt der Pfadanteil oder ist er relativ notiert, bezieht die Anweisung den Pfad auf das standardmäßige Arbeitsverzeichnis (vgl. »CurDir-Funktion«, S. 134). Beispiel
................................................... Beis piel
sPfad = Environ("Temp") if sPfad <> "" Then Kill(sPfad + "*.tmp") Verwandte Befehle
................................................... Verwa ndte Befehle
Line Input #- Anweisung Line Input #iDateiNr, sVariable Beschreibung
................................................... Bes c hreibung
Die Anweisung Line Input# liest eine Textzeile aus einer sequenziellen Datei, die im Modus Input oder Binary geöffnet wurde, und weist diese der Zeichenfolgenvariablen sVariable als Wert zu. Als Trennzeichen zwischen zwei aufeinander folgenden Textzeilen wertet die Anweisung das Wagenrücklaufzeichen Chr(13) bzw. vbCr sowie die Kombination Chr(13)+Chr(10) bzw. vbCrLf. Anwendung
................................................... Anwendung
Der traditionelle Zweck der Anweisung Line Input# ist, Zeichenfolgen für die Textverarbeitung aus sequenziellen Dateien zu lesen. Im Unterschied zu Input# behandelt Line Input# weder Kommas als Trennzeichen, noch schneidet die Anweisung Leerzeichen und doppelte Anführungszeichen ab. Zum Schreiben der Daten, die mit Line Input# gelesen werden sollen, verwenden Sie am besten Print#. Beispiel
................................................... Beis piel
Der folgende Code liest eine Textdatei zeilenweise in ein dynamisches Zeichenfolgenarray. Dim sZeilen() As String Dim lZeilen As Long Open "Textdatei.txt" For Input As 1 While Not EOF(1) ReDim Preserve sZeilen(lZeilen + 1) As String Line Input #1, sZeilen(UBound(sZeilen)) lZeilen = UBound(sZeilen) Wend Verwandte Befehle
................................................... Verwa ndte Befehle
EOF, Get, Input, Input#, Print#, Put, Write#
1 45
Dateiorientierte Funktionen und Anweisungen
RmDir
Dateiorientierte Funktionen und Anweisungen
Loc- Funktion Function Loc(iDateiNr As Integer) As Long Beschreibung
................................................... Bes c hreibung
Die Funktion Loc liefert abhängig vom Modus, in dem die Datei mit der DateiNr geöffnet wurde, die Nummer des zuletzt bearbeiteten Datensatzes oder die Position des zuletzt gelesenen oder geschriebenen Bytes. Anwendung
Dateiorientierte Funktionen und Anweisungen
................................................... Anwendung
Visual Basic verwaltet für jede Datei einen Dateizeiger, der die aktuelle Schreib-/Leseposition angibt. Den »nackten« Wert dieses Zeigers liefert die Funktion Seek als Byte-Nummer, gleich in welchem Modus eine Datei geöffnet wurde. Loc liefert dagegen den logischen Dateizeiger. Im Modus Binary unterscheidet sich der Wert des logischen Dateizeigers von dem des gewöhnlichen Dateizeigers dahingehend, dass er um 1 kleiner ist als dieser. Im Modus Random hat der logische Dateizeiger dagegen den Wert des zuletzt gelesenen oder geschriebenen Datensatzes. Es gilt die Formel: ( Dateizeiger – 1 ) Datensatznummer = ---------------------------------------------Datensatzlänge Mithin liefert Loc vor der ersten Dateioperation den Wert 0. Warnung
................................................... Wa rnung
Loc liefert für sequenzielle Dateien zwar einen Wert, dieser ist aber unbrauchbar, da er auf eine Datensatzlänge von 128 gemünzt ist. Sie arbeiten in diesem Fall besser mit Seek. Beispiel
................................................... Beis piel
Open "test.idx" For Random As 1 Len = Datensatzlänge For i = 1 To iDatensätze Put 1, , bdt(i) Next Print Loc(1) ' Wert von iDatensätze Close1 Verwandte Befehle
................................................... Verwa ndte Befehle
EOF, LOF, Seek
Lock- Anweisung Sub Lock([#]iDateiNr As Integer[, lDatensatz As Long]) Sub Lock([#]iDateiNr As Integer, [lStartNr As Long] To lEndNr As Long) Beschreibung
................................................... Bes c hreibung
Die Anweisung Lock sperrt einen einzelnen Datensatz lDatensatz oder einen Bereich von Datensätzen lStartNr bis lEndNr in der Datei mit der Dateinummer iDateiNr vor dem Zugriff durch andere Prozesse. Fehlt bei Gebrauch der ersten Syntax der optionale Parameter lDatensatz, verhängt die Anweisung die Sperre über die gesamte Datei. Fehlt bei Gebrauch der zweiten Syntax der optionale Parameter lStartNr, sperrt die Anweisung alle Datensätze von der Nummer 1 bis zur Nummer lEndNr. Bei Dateien, die im Modus Binary geöffnet wurden, beziehen sich die Bereichsangaben auf Positionen des Dateizeigers.
1 46
LOF- Funktion
Anwendung
................................................... Anwendung
Wenn in einem System oder in einem Netzwerk mehrere Programme gleichzeitig Schreibzugriffe auf dieselbe Datei durchführen, kann das recht unangenehme Inkonsistenzen zur Folge haben. Das Anweisungspaar Lock/Unlock ermöglicht es einem Programm, sich temporär die Exklusivrechte für das Beschreiben eines gewissen Teils der Datei zu sichern. Lock löst den Laufzeitfehler 70, »Zugriff verweigert«, aus, wenn ein anderes Programm bereits eine Sperre auf einen Bereich hält, der mit dem angegebenen Bereich überlappt. Warnung
................................................... Wa rnung
Beispiel
................................................... Beis piel
Der folgende Code schreibt eine Reihe von Datensätzen in die gemeinsame genutzte Datei Kunden.idx: Open "Kunden.idx" For Random As 1 Len = Datensatzlänge On Error GoTo Fehler70 Lock 1, lStartBereich To lEndBereich ' Sperre errichten On Error GoTo 0 For i = lStartBereich To lEndBereich Put 1, i, bdt(i) Next Unlock 1, lStartBereich To lEndBereich ' Sperre aufheben Close 1 Verwandte Befehle
................................................... Verwa ndte Befehle
Open, Unlock Verwandte Themen
................................................... Verwandte Them en
Fehlerbehandlung (S. 43)
LOF- Funktion Function LOF(iDateiNummer As Integer) As Long Beschreibung
................................................... Bes c hreibung
Die Funktion LOF liefert die Länge der unter der Nummer iDateiNummer geöffneten Datei in Bytes. Anwendung
................................................... Anwendung
Wo immer die Länge einer geöffneten Datei eine Rolle spielt, beispielsweise wenn es darum geht, eine Datei en bloc mittels Input einzulesen oder die Anzahl der bereits geschriebenen Bytes (nicht jedoch Datensätze) zu ermitteln, ist LOF das Mittel zum Zweck.
1 47
Dateiorientierte Funktionen und Anweisungen
Bei Lock/Unlock-Anweisungen für sequenzielle Dateien (Input, Output, Append) ignoriert Visual Basic Bereichsangaben und sperrt immer die gesamte Datei. Entgegen anders lautenden Informationen in der Online-Hilfe zu Visual Basic 6.0 entfernt Close (respektive das Programmende) jedoch alle Sperren, die mit Lock gesetzt und nicht explizit mit Unlock freigegeben wurden. Sie sollten aber dennoch darauf achten, jede Lock-Anweisung mit einer Unlock-Anweisung zu paaren, um errichtete Sperren zum frühestmöglichen Zeitpunkt wieder aufzuheben.
Dateiorientierte Funktionen und Anweisungen
Tipp
................................................... Tipp
Die Länge einer noch nicht geöffneten Datei ermittelt die Funktion FileLen. Beispiel
................................................... Beis piel
Der folgende Code liest die unter der Dateinummer 1 geöffnete Datei in einem Stück: sDatei = Input(LOF(1), 1) Verwandte Befehle
Dateiorientierte Funktionen und Anweisungen
................................................... Verwa ndte Befehle
Loc, FileLen, Get, Input, Line Input, Open, Seek
LSet- Anweisung LSet sString1 = sString2 LSet bdtVar = bdtWert Siehe »LSet-Anweisung« (S. 80).
MkDir- Anweisung Sub MkDir(sVerzeichnisname As String) Beschreibung
................................................... Bes c hreibung
Die Anweisung MkDir legt unter dem Namen sVerzeichnisname ein neues Verzeichnis an. Anwendung
................................................... Anwendung
Der Wert des Parameters sVerzeichnisname kann ein (absoluter oder relativer) UNC-Pfad oder Laufwerkspfad sein. Enthält sVerzeichnisname keinen Pfadanteil, legt die Anweisung das Verzeichnis als Unterverzeichnis des standardmäßigen Verzeichnisses an (vgl. »CurDir-Funktion«, S. 134). Falls das Verzeichnis bereits existiert oder die Rechte für das Anlegen des Verzeichnisses nicht ausreichen, löst die Anweisung den Laufzeitfehler 75, »Fehler beim Zugriff auf Pfad/ Datei«, aus. Beispiel
................................................... Beis piel
MkDir "C:\Visual-Basic-Daten\MeineDaten" ' Laufwerkspfad MkDir "\\Tiger\c\Visual-Basic-Daten\MeineDaten" ' UNC-Pfad Verwandte Befehle
................................................... Verwa ndte Befehle
ChDir, CurDir, RmDir
Name- Anweisung Name sAlterDateiname As sNeuerDateiname Beschreibung
................................................... Bes c hreibung
Die Anweisung Name ändert den Namen der Datei oder des Verzeichnisses sAlterDateiname in sNeuerDateiname. Falls sich die Pfadanteile von sAlterDateiname und sNeuerDateiname unterscheiden, wird die Datei bzw. das Verzeichnis verschoben.
1 48
Open- Anweisung
Anwendung
................................................... Anwendung
Die Werte der Parameter sAlterDateiname und sNeuerDateiname können (absolute oder relative) UNC-Pfade oder Laufwerkspfade sein. Fehlt ein Pfadanteil, bezieht sich die Anweisung auf das standardmäßige Verzeichnis (»CurDir-Funktion«, S. 134). Name lässt sich nur auf einzelne Dateien und Verzeichnisse anwenden, Platzhalterzeichen wie »*« oder »?« kann die Anweisung nicht interpretieren – sie lösen den Laufzeitfehler 52, »Dateiname oder -Nummer falsch«, aus. Falls die Datei oder das Verzeichnis sAlterDateiname nicht existiert, löst die Anwendung den Laufzeitfehler 53, »Datei nicht gefunden«, aus. Existiert dagegen sNeuerDateiname bereits als Datei oder Verzeichnis, kommt es zum Laufzeitfehler 58, »Datei existiert bereits«.
................................................... Beis piel
Kill sPfad + "Daten.dat" Name sPfad + "Daten.tmp" As sPfad + "Daten.dat Verwandte Befehle
................................................... Verwa ndte Befehle
Kill
Open- Anweisung Open sName For Modus [Access Zugriffsart] [Sperre] As [#] iNr [Len = lDatensatzlänge] Beschreibung
................................................... Bes c hreibung
Die Anweisung Open öffnet die Datei sName und ordnet ihr für den weiteren Zugriff die Dateinummer iNr zu. Zugleich legt die Anweisung einen Bearbeitungsmodus Modus fest, der als Input, Output, Random oder Binary anzugeben ist. Mittels des optionalen Schlüsselwortes Access gefolgt von Read, Write oder Read Write für Zugriff lässt sich zudem die Art des Zugriffs genauer festlegen. Darüber hinaus besteht die Möglichkeit, für den indexsequenziellen Zugriff im Modus Random eine Datensatzlänge lDatensatzlänge zwischen 1 und 32767 als Len-Zusatz anzugeben. Anwendung
................................................... Anwendung
Für den schreibenden oder lesenden Zugriff auf eine Datei ist es erforderlich, diese zuvor mit Open unter Angabe des entsprechenden UNC- oder Laufwerkspfades zu öffnen. Dabei kann es sich um einen absoluten oder relativen Pfad handeln. Ist sName nur als Dateiname ohne Pfadanteil spezifiziert, geht die Anweisung davon aus, dass die Datei im standardmäßigen Verzeichnis zu finden ist. Als Dateinummer iNr lässt sich ein Wert zwischen 1 und 511 wählen, der fortan als Alias der Datei für jegliche Zugriffe fungiert, bis diese mittels Close wieder geschlossen wird. Bei der Vergabe der Dateinummer ist zu beachten, dass diese nicht bereits einer anderen Datei zugeordnet wurde (vgl. »FreeFile-Funktion«, S. 139). Open ordnet der Datei je nach Bearbeitungsmodus und Zugriffsart auch einen Schreib- und/oder Lesepuffer sowie einen Dateizeiger zu, wobei der Dateizeiger die jeweils aktuelle Position für die nächste Dateioperation wiedergibt, falls diese nicht explizit die Angabe einer Startposition enthält. Für Modus kennt Visual Basic als reine Textmodi Input, Output und Append, wobei mit Input nur ein lesender Zugriff möglich ist und mit Output und Append nur ein schreibender. Ein AccessZusatz für diese Modi ergibt keinen Sinn, da die Zugriffsart ja bereits feststeht. Daneben gibt es noch den indexsequenziellen Zugriffsmodus Random für den rein datensatzorientierten Zugriff sowie den binären Zugriffsmodus Binary, der als flexibelster Modus sowohl für den textorien-
1 49
Dateiorientierte Funktionen und Anweisungen
Beispiel
Dateiorientierte Funktionen und Anweisungen
Dateiorientierte Funktionen und Anweisungen
tierten als auch für den datensatzorientierten Zugriff geeignet ist. Dateioperationen finden im Allgemeinen mit Bezug auf eine Byteposition statt, einzig Operationen im Modus Random liegt eine Datensatznummer zugrunde, bei der die Datensatzlänge lDatensatzlänge eine Rolle spielt. Der Dateizeiger gibt in diesem Fall die Datensatznummer an und nicht die Byteposition. In allen anderen Modi hat die Angabe einer Datensatzlänge keinen Effekt. Für den Modus Append setzt Open den Dateizeiger an das Ende der Datei, ansonsten an den Anfang. Wird eine Datei im Modus Binary oder Random mit dem Zusatz Access Read geöffnet, führt jeder Versuch, eine Schreiboperation darauf auszuführen, zu dem Laufzeitfehler 75, »Fehler bei Zugriff auf Datei«. Analoges gilt für den Zusatz Access Write und Lesezugriffe. Lautet der Zusatz Access Read Write oder fehlt der Access-Zusatz, öffnet Open die Datei in diesen beiden Modi für den Schreib- und Lesezugriff. Ein- und Ausgabeoperationen beziehen sich in diesem Fall auf den gleichen Puffer und den gleichen Dateizeiger. Eine Datei lässt sich auch mehrfach öffnen, sofern sie nicht in einem der Modi Input, Output oder Append geöffnet wird. In diesem Fall verwendet Visual Basic für die zugehörigen Dateinummern den gleichen Puffer, so dass zu jeder Zeit über jede der Dateinummern ein und derselbe Dateiinhalt verfügbar ist. Dateizeiger gibt es aber je Dateinummer einen eigenen (vgl. Beispiel). Gleich, in welchem Modus Sie eine Datei öffnen, Visual Basic verwendet standardmäßig die Zugriffsart Shared, denn diese Zugriffsart erlegt Programmen die geringsten Einschränkungen für den gemeinsamen Zugriff auf dieselbe Datei auf. Vom Prinzip her ist das zwar erwünscht, im Einzelfall kann es aber dazu führen, dass sich Programme gegenseitig ins Handwerk pfuschen, weil ihre Puffer unter ungünstigen Bedingungen unterschiedliche Versionen des gleichen Datensatzes oder des gleichen Dateibereichs enthalten können. Es gibt drei Möglichkeiten, dem entgegenzuwirken: Die erste ist das härteste Geschütz und besteht darin, die Datei gleich beim Öffnen durch Angabe einer der Zugriffsarten Lock Read, Lock Write oder Lock Read Write für den exklusiven Lesezugriff, Schreibzugriff oder Schreib-/Lesezugriff zu öffnen. Die zweite besteht darin, sich die gesamte Datei unmittelbar vor einem Schreib- bzw. Lesezugriff mittels Lock für den exklusiven Zugriff zu reservieren. Diese beiden Techniken können natürlich scheitern, wenn ein anderes Programm seinerseits eine Sperre auf die Datei errichtet hat – sei es auch nur eine Sperre für einen einzelnen Datensatz respektive für einen kleinen Bereich der Datei. Die dritte und moderateste Möglichkeit, auf einen bestimmten Datensatz oder Dateibereich ungestört zugreifen zu können, ist das Sperren genau dieses Datensatzes oder Bereichs mittels Lock und das sofortige Entsperren mittels Unlock nach dem Zugriff. Die Wahrscheinlichkeit, dass diese Technik scheitert, ist am geringsten, da es nur dann zu dem Laufzeitfehler 70, »Zugriff verweigert«, kommt, wenn ein anderes Programm eine Sperre auf die gesamte Datei oder auf genau denselben Datensatz bzw. Dateibereich errichtet hat. Insbesondere, wenn auch die anderen Programme mit der gleichen Technik arbeiten, sind kaum Kollisionen zu befürchten – was natürlich eine Fehlerbehandlung nicht entbehrlich macht. Auch wenn Visual Basic bei Programmende offene Dateien automatisch schließt, sollten Sie generell darauf achten, eine Datei unmittelbar nach der letzten Dateioperation mittels Close zu schließen. Das gibt die Dateinummer und den Puffer frei und ermöglicht anderen Programmen wieder den uneingeschränkten Zugriff auf die Datei. Insbesondere entfernt Close auch alle Sperren, die mit Lock gesetzt und nicht explizit mit Unlock freigegeben wurden. Beispiel
................................................... Beis piel
Der folgende Codeauszug demonstriert zum einen die Fehlerbehandlung im Zusammenhang mit der Open-Anweisung und verschiedenen Dateioperationen. Die Variable sDateipfad enthält die leere Zeichenfolge als Wert. Daher scheitern alle dateibezogenen Operationen. Der Benutzer kann den Vorgang abbrechen, wiederholen oder überspringen (ignorieren). Letzteres ist zu empfehlen, um die verschiedenen Fehlermeldungen zu erhalten. Löscht man das erste Kommen-
1 50
Print #- Anweisung tarzeichen in der zweiten Zeile, läuft der Code fehlerlos und demonstriert, wie es sich mit den Dateizeigern verhält, wenn eine Datei im Modus Binary zweimal geöffnet wird. sDateipfad = "" ' Als Dateiname nicht erlaubt! ' sDateipfad = "Test1" ' Dateiname ist OK On Error GoTo OpenFehler ' Fehlerbehandlung einrichten Open sDateipfad For Binary As 1 Open sDateipfad For Binary As 2
1 5 5 1
OpenFehler: ' Routine für Fehlerbehandlung sFehler = "Fehler" + Str(Err.Number) + ": " + Err.Description Select Case MsgBox(sFehler, vbAbortRetryIgnore, "Fehler") Case vbAbort: Error 1001 Case vbRetry: Resume Case vbIgnore: Resume Next End Select
Vgl. auch die Beispiele in »Close-Anweisung« (S. 133), »EOF-Funktion« (S. 137), »FileAttrFunktion« (S. 138), »FreeFile-Funktion« (S. 139), »Get-Anweisung« (S. 140), »Input- und InputB-Funktion« (S. 142), »Input #-Anweisung« (S. 143) und »Loc-Funktion« (S. 146) sowie das Beispiel im Abschnitt »Fehlerbehandlung« (S. 43). Verwandte Befehle
................................................... Verwa ndte Befehle
Close, FreeFile
Print #- Anweisung Print #iDateiNr[, Ausdr1[Trennz [Ausdr2] ... ]] Beschreibung
................................................... Bes c hreibung
Die Anweisung Print # führt Ausgaben im gebietsspezifischen Format in die unter der Dateinummer iDateiNr geöffnete sequenzielle Datei durch. Die auf die Dateinummer folgende Parameterliste Ausdr1, Ausdr2 usw. darf Werte beliebiger Standardtypen enthalten. Print # gibt diese Werte unter Beachtung des jeweils zwischen den Parametern stehenden Trennzeichens Trennz aus. Anwendung
................................................... Anwendung
Während Print nur noch als Methode eines Form-, UserControl- und Debug-Objekts verfügbar und somit sozusagen zur objektorientierten Seite von Visual Basic übergelaufen ist, ist Print#
1 51
Dateiorientierte Funktionen und Anweisungen
Put 1, 1, "abcd" Print Seek(1), Seek(2) ' Dateizeiger #1 = 5, #2 = Put 2, 1, StrReverse(Input(LOF(2), 2)) Print Seek(1), Seek(2) ' Dateizeiger #1 = 5, #2 = Seek 1, 1 ' Dateizeiger #1 = 1, #2 = Seek 2, 1 ' Dateizeiger #1 = 1, #2 = Print LOF(1), Input(LOF(1), 1) ' Ausgabe: 4, "dcba" Print LOF(2), Input(LOF(2), 2) ' Ausgabe: 4, "dcba" On Error GoTo 0 ' Fehlerbehandlung beenden Close 1, 2 Exit Sub
Dateiorientierte Funktionen und Anweisungen
Dateiorientierte Funktionen und Anweisungen
nach wie vor auf der traditionellen Seite der Sprache verblieben. Charakteristikum für die Ausgabe von Print# im Gegensatz zu Write# ist, dass die literale Darstellung der Werte im Allgemeinen im gebietsspezifischen Format erfolgt. »Im Allgemeinen« deshalb, weil es auch Werte gibt, die ungeachtet des Gebietsschemas nicht übersetzt werden. So die Werte True und False des Datentyps Boolean und das Wort »Error« in der Ausgabe eines Error-Werts. Und obwohl die Anweisung Null als »Null« in eine Datei schreibt, gibt sie den Wert Empty als leere Zeichenfolge und somit gar nicht aus. Als Trennzeichen Trennz sind das Semikolon und das Komma erlaubt. Bei Angabe eines Semikolons setzt die Anweisung den folgenden Wert unmittelbar hinter den vorigen, wie bei der Verkettung von Zeichenfolgen. Das Komma hingegen bewirkt, dass Print# den folgenden Wert an die nächste Tabulatorposition setzt und den Zwischenraum gegebenenfalls mit Leerzeichen auffüllt. Der Abstand zwischen je zwei Tabulatorpositionen beträgt 14 Zeichen. Ist im Anschluss an den letzten Parameter der Liste kein Trennzeichen spezifiziert, ergänzt die Anweisung einen Wagenrücklauf Chr(13) und einen Zeilenvorschub Chr(10) bzw. vbCrLf. Als Werte in der Parameterliste sind auch die Ausdrücke Spc(n) und Tab(n) zulässig, die jedoch keine eigenständigen Zeichenfolgenfunktionen darstellen, sondern als Argument von Print, Print# und Write# zulässig sind. Spc(n) fügt n Leerzeichen ein, und Tab(n) füllt die aktuelle Zeile bis zur Tabulatorposition n mit Leerzeichen. Warnung
................................................... Wa rnung
Die Anweisung gibt Zeichenfolgen im ANSI-Code und nicht im Unicode aus. Es ist im Allgemeinen nicht möglich, Fließkommawerte und Datumswerte, die mit Print# geschrieben wurden, mittels Input # wieder in Variablen des gleichen Typs einzulesen, da Print # seinen Ausgaben das gebietsspezifische Format verpasst, Input# aber Literale im standardisierten Format erwartet. Damit das klappt, müssen Sie die Daten mit Write# und nicht mit Print# schreiben. Tipp
................................................... Tipp
Im Gegensatz zu Write# versieht Print# Zeichenfolgen nicht automatisch mit Anführungszeichen. Wenn Sie ausschließlich Zeichenfolgen ausgeben, also reine Textverarbeitung betreiben, ist Print# in jedem Fall die bessere Lösung. Zum zeilenweisen Lesen der Daten verwenden Sie dann die Anweisung Line Input#. Beispiel
................................................... Beis piel
Die Prozedur Ersetze sucht in einer Textdatei alle Vorkommen der Zeichenfolge sSuch und ersetzt diese gegen die Zeichenfolge sErsetz. Function Ersetze(sDatei As String, sSuch As String, sErsetz As String) Dim sZeile As String Open sDatei For Input As 1 Open "Ersetz.tmp" For Output As 2 ' Temporäre Ausgabedatei anlegen While Not EOF(1) Line Input #1, sZeile ' Zeile lesen Print #2, Replace(sZeile, sSuch, sErsetz) ' Ersetzen und schreiben Wend Close 1, 2 ' beide Dateien schließen Kill sDatei ' Ausgangsdatei löschen Name "Ersetz.tmp" As sDatei ' Temporäre Datei umbenennen End Function
1 52
Put- Anweisung
Verwandte Befehle
................................................... Verwa ndte Befehle
Input, Input#, Line Input#, Print, Write# Verwandte Themen
................................................... Verwandte Them en
Literale und Konstanten (S. 27)
Put- Anweisung Put [#]iDateiNr, [lSatzNr], Wert
................................................... Bes c hreibung
Die Anweisung Put ist das Gegenstück zur Anweisung Get. Sie schreibt die über den Parameter Wert spezifizierten Daten in der gegebenen Repräsentation (typerhaltend) in die Datei mit der Dateinummer iDateiNr. Ist eine Satznummer lSatzNr angegeben, schreibt die Anweisung im Modus Random den Wert Wert als Datensatz unter Berücksichtigung der bei der Open-Anweisung vereinbarten Datensatzlänge und im Modus Binary als schlichte Bytefolge ab der Byteposition lSatzNr. Fehlt lSatzNr, schreibt die Anweisung den Wert als nächsten Datensatz (Random) bzw. an der jeweils aktuellen Schreibposition (Binary) und aktualisiert danach den Datensatzzeiger bzw. die aktuelle Schreibposition. Der Parameter Wert kann einen beliebigen Typ tragen, nicht jedoch einen Objekttyp. Anwendung
................................................... Anwendung
Wenn es um das Schreiben und Lesen von Dateiformaten ging, waren Get und Put schon immer die Arbeitspferde der Sprache Basic. Die Arbeitsweise dieser Anweisungen ist traditionell auf indexsequenzielle Dateien zugeschnitten, deren Bearbeitung im Modus Random erfolgt. Get und Put werden aber auch – jedoch mit leicht modifizierter Arbeitsweise – im Modus Binary eingesetzt.
Der Modus Random Beim Öffnen von Dateien im Modus Random wird normalerweise eine Datensatzlänge in Bytes festgelegt, die fortan bestimmt, wie viele Bytes mit jeder Get- bzw. Put-Operation je Datensatz gelesen bzw. geschrieben werden. Die Zählung der Datensätze beginnt bei 1. Den Bytebedarf der elementaren Datentypen entnehmen Sie der Tabelle im Abschnitt »Elementare Datentypen« (S. 49); den von Datentypen mit fester Länge sowie von benutzerdefinierten Datentypen ermittelt man am besten mittels LenB, da die Speicherausrichtung der Datenfelder noch eine zusätzliche Rolle spielt. Liegt Wert ein Typ variabler Länge zugrunde (bzw. ein Typ, der Felder variabler Länge enthält), erweitert das den Datensatz um einen Deskriptor (lies: Längenangabe). Dabei gilt, dass ein Deskriptor dem Wert unmittelbar vorausgeht. Mithin muss also der Längenbedarf eines oder mehrerer Deskriptoren bei der Berechnung der Datensatzlänge berücksichtigt werden. Für Zeichenfolgen variabler Länge schlagen dabei 2 Längenbytes (!) zu Buche (Zeichenfolgen dürfen demnach nicht länger als 65.535 Zeichen sein), für Variant-Variablen gleichfalls zunächst 2 Bytes für den VarType-Typ plus gegebenenfalls weitere Längenbytes für Wertanteile mit variablen Längen. Dynamische Arrays schlagen mit 2 Bytes für den Array-Deskriptor zu Buche plus 8 Bytes je Dimension. Die maximale Datensatzlänge beträgt 32.767. Put schreibt Wert linksbündig in den Datensatz, ohne Füllbytes anzuhängen, wenn die Repräsentation von Wert kürzer als die Datensatzlänge ist. Sollte die Repräsentation von Wert länger sein als die bei Open angegebene Datensatzlänge, kommt es zum Laufzeitfehler 59, »Falsche Datensatzlänge«.
1 53
Dateiorientierte Funktionen und Anweisungen
Beschreibung
Dateiorientierte Funktionen und Anweisungen
Der Modus Binary Für den Modus Binary gilt im Wesentlichen dasselbe, mit folgenden Abweichungen: Eine Datensatzlänge wird nicht berücksichtigt, und die Anweisung schreibt ab der aktuellen Position bzw. der Position lSatzNr so viele Bytes, wie die Darstellung von Wert benötigt. Zeichenfolgen variabler Länge behandelt Put wie Zeichenfolgen fester Länge, schreibt also nur so viele Bytes wie der Wert an Zeichen enthält – ein Deskriptoranteil entfällt somit. Analoges gilt auch für Arrays. Im Zusammenhang mit benutzerdefinierten Datentypen finden Deskriptoranteile einzelner Datenfelder jedoch sehr wohl Berücksichtigung.
Dateiorientierte Funktionen und Anweisungen
Warnung
................................................... Wa rnung
Put schreibt Unicode-Zeichenfolgen als ANSI-Code und Get liest ANSI-Zeichenfolgen als Unicode. Beispiel
................................................... Beis piel
Die folgende Prozedur Crypt32 nimmt eine einfache 32 Bit-Verschlüsselung einer Datei sDatei mit dem Wert lKennzahl vor. Der gleiche Wert entschlüsselt die Datei auch wieder; probieren Sie es aus. Sub Crypt32(lKennzahl As Long, sDatei As String) Dim lData As Long Open sDatei For Binary As 1 For i = 1 To LOF(1) – Len(lData) + 1 Get 1, i, lData Put 1, i, lData Xor lData Next i Close 1 End Sub Verwandte Befehle
................................................... Verwa ndte Befehle
Get, Input, Input#, LenB, Line Input#, LSet, Open, Print# Verwandte Themen
................................................... Verwandte Them en
Elementare Datentypen (S. 49); Benutzerdefinierte Datentypen (S. 60)
Reset- Anweisung Reset Beschreibung
................................................... Bes c hreibung
Die Anweisung Reset schließt alle offenen Dateien, die mittels Open-Anweisungen geöffnet wurden. Anwendung
................................................... Anwendung
Sinn und Zweck dieser Anweisung ist es, im Fehlerfall oder bei einem überraschenden Programmabbruch schnell aufräumen zu können. Reset erzwingt insbesondere die Ausgabe von Pufferinhalten in Dateien, die zum Schreiben geöffnet wurden. Es besteht kein Unterschied zwischen dem parameterlosen Aufruf von Close und Reset.
1 54
RmDir- Anweisung
Verwandte Befehle
................................................... Verwa ndte Befehle
Close, End
RmDir- Anweisung Sub RmDir(sVerzeichnisname As String) Beschreibung
................................................... Bes c hreibung
Die Anweisung RmDir löscht das Verzeichnis sVerzeichnisName.
................................................... Anwendung
Der Wert des Parameters sVerzeichnisName kann ein (absoluter oder relativer) UNC-Pfad oder Laufwerkspfad sein. Enthält sVerzeichnisName keinen Pfadanteil, geht die Anweisung davon aus, dass das Verzeichnis ein Unterverzeichnis des standardmäßigen Verzeichnisses (vgl. »CurDir-Funktion«, S. 134) ist. Falls das Verzeichnis noch Dateien enthält, löst die Anweisung den Laufzeitfehler 75, »Fehler beim Zugriff auf Pfad/Datei«, aus. Existiert das Verzeichnis überhaupt nicht, kommt es zum Laufzeitfehler 76, »Pfad nicht gefunden«. Beispiel
................................................... Beis piel
Die rekursive Prozedur DelTree löscht ein Verzeichnis mit allen Dateien und Unterverzeichnissen. Sub DelTree(sPfad As String) Dim sUVz As String If Right(sPfad, 1) = "\" Then sPfad = Left(sPfad, Len(sPfad) – 1) End On Error GoTo Fehlerbehandlung Kill sPfad + "\*.*" sUVz = Dir(sPfad + "\", vbDirectory) While sUVz <> "" If sUVz <> "." And sUVz <> ".." Then DelTree (sPfad + "\" + sUVz) sUVz = Dir(sPfad + "\", vbDirectory) Else sUVz = Dir End If Wend RmDir sPfad On Error GoTo 0 Exit Sub Fehlerbehandlung: Resume Next End Sub
' "\" am Ende? ' Abschneiden ' Falls Kill scheitert ' oder Schreibschutz
' Unterverz. abklappern ' Rekursion ' da Dir nicht rekursiv ' arbeitet ' Nächstes Verzeichnis löschen
Verwandte Befehle
................................................... Verwa ndte Befehle
ChrDir, CurDir, Kill, MkDir
1 55
Dateiorientierte Funktionen und Anweisungen
Anwendung
Dateiorientierte Funktionen und Anweisungen
Seek- Anweisung und Seek- Funktion Function Seek(iDateiNr As Integer) As Long Seek [#]iDateiNr, lPosition Beschreibung
Dateiorientierte Funktionen und Anweisungen
................................................... Bes c hreibung
Als Funktion liefert Seek die aktuelle Position des Dateizeigers der unter der Dateinummer iDateiNr geöffneten Datei. Für den Modus Random gibt diese Angabe die Nummer des nächsten zu lesenden oder zu schreibenden Datensatzes ab Dateianfang wieder, für alle anderen Modi die Nummer des nächsten zu lesenden oder zu schreibenden Bytes ab Dateianfang. Das erste Byte bzw. der erste Datensatz trägt die Nummer 1. Als Anweisung setzt Seek den Wert lPosition als neue Position des Dateizeigers. Im Modus Random ist diese Position eine Datensatznummer, in allen anderen Modi eine Bytenummer. Anwendung
................................................... Anwendung
Lese- und Schreiboperationen verschieben den Dateizeiger jeweils immer nur um die gelesene Anzahl von Bytes bzw. Datensätzen, was dem Muster für den sequenziellen Dateizugriff entspricht. Seek ermöglicht die Orientierung und Positionierung innerhalb von Dateien, wenn indexsequenzielle Dateizugriffe erforderlich sind. Warnung
................................................... Wa rnung
Als Anweisung setzt Seek nichts weiter als den Dateizeiger. Falls der Wert von lPosition größer als die von LOF gelieferte Dateilänge ist, bewirkt die nächste Schreiboperation einer zum Schreiben geöffneten Datei eine entsprechende Verlängerung. Dagegen führt die nächste Leseoperation, gleich im welchem Modus die Datei geöffnet wurde, zum Laufzeitfehler 62, »Einlesen hinter Dateiende«. Tipp
................................................... Tipp
Die Syntax für die Dateioperationen Get und Put sieht die Angabe expliziter Positionsangaben vor, so dass auf Seek verzichtet werden kann. Beispiel
................................................... Beis piel
Open sDatei For Output As 1 Seek 1, lOffs ' Dateizeiger auf bestimmte Zeile Line Input #1, sZeile ' Zeile lesen Print Seek(1) ' Dateizeiger abfragen
Die folgende Zeile zeigt, was passiert, wenn Seek über das Dateiende hinausschießt: Open "Test" For Input As 1 ' Die Datei enthält 4 Bytes! Seek 1, 10000 ' Dateizeiger auf 10000 setzen Print Loc(1) ' Ausgabe: 9999 Print Seek(1) ' Ausgabe: 10000 Input #1, a ' Laufzeitfehler 62 Close
Vgl. auch das Beispiel zu »Open-Anweisung« (S. 149). Verwandte Befehle
................................................... Verwa ndte Befehle
Get, Loc, Open, Put
1 56
SetAttr- Anweisung
SetAttr- Anweisung Sub SetAttr(sPfad As String, iAttributvektor As Integer) Beschreibung
................................................... Bes c hreibung
Die Anweisung SetAttr setzt die Attribute der Datei oder des Verzeichnisses sPfad entsprechend dem Attributvektor iAttributvektor. Anwendung
................................................... Anwendung
Konstante
Beschreibung
vbNormal (0)
Keines der Bits im Attributvektor ist gesetzt, es handelt sich um eine gewöhnliche Datei
vbReadOnly (1)
Datei/Verzeichnis ist schreibgeschützt
vbHidden (2)
Datei/Verzeichnis ist verborgen
vbSystem (4)
Datei ist Systemdatei bzw. Verzeichnis ist Systemverzeichnis
vbVolume (8)
Logischer Datenträger (wird von SetAttr nicht gesetzt)
vbDirectory (16)
Verzeichnis (wird von SetAttr nicht gesetzt)
vbArchive (32)
Datei wurde seit der letzten Datensicherung geändert
Konstanten für Dateiattribute Beispiel
................................................... Beis piel
Die folgenden Zeilen zeigen, wie man temporär einen Schreibschutz auf eine Datei entfernt und dann wieder errichtet: SetAttr sDateName, GetAttr(sDateiname) And Not vbReadOnly ... SetAttr sDateName, GetAttr(sDateiname) Or vbReadOnly
Falls nicht klar ist, ob eine Datei schreibgeschützt ist oder nicht, schreiben Sie vorsichtiger: AttrVekt = GetAttr(sDateiname) SetAttr sDateName, AttrVekt And Not vbReadOnly ... SetAttr sDateName, AttrVekt Verwandte Befehle
................................................... Verwa ndte Befehle
Dir, FileAttr, GetAttr
1 57
Dateiorientierte Funktionen und Anweisungen
Das Dateisystem pflegt für jeden Namenseintrag einen Attributvektor (Bytewert), dessen Elemente (Bits des Bytewerts) eine genauere Aussage darüber machen, welcher Art die zugeordnete Datei (bzw. das Verzeichnis) ist. Die folgende Tabelle gibt einen Überblick über die möglichen Attribute sowie über die Konstanten, die in Visual Basic als Elemente des Aufzählungstyps VbFileAttribute für diese Attribute definiert sind. SetAttr ignoriert Attribute, die es nicht setzen kann – so ist es beispielsweise nicht möglich, mittels SetAttr aus einer Datei ein Verzeichnis zu machen.
Dateiorientierte Funktionen und Anweisungen
Shell- Anweisung Function Shell( _ sAnwendName As String, _ [FensterStil As VbAppWinStyle = vbMinimizedFocus]) _ As Double Beschreibung
Dateiorientierte Funktionen und Anweisungen
................................................... Bes c hreibung
Die Funktion Shell ermöglicht den Start einer anderen Anwendung sAnwendName als eigenständige Anwendung und liefert als Funktionswert die Task-ID des zugehörigen Prozesses respektive 0, wenn der Start nicht erfolgreich verlaufen ist. Die Bedeutung der Werte für den optionalen Parameter FensterStil listet die folgende Tabelle auf. Konstante
Beschreibung
vbHide (0)
Das Fenster der Anwendung ist nicht sichtbar, erhält aber den Fokus.
vbNormalFocus (1)
Das Fenster der Anwendung ist sichtbar, hat Normalgröße und erhält den Fokus.
vbMinimizedFocus (2)
Die Anwendung startet als Symbol, erhält aber den Fokus (Standardwert).
vbMaximizedFocus (3)
Die Anwendung startet mit maximiertem Fenster und erhält den Fokus.
vbNormalNoFocus (4)
Das Fenster der Anwendung ist sichtbar, hat Normalgröße, erhält aber nicht den Fokus.
vbMinimizedNoFocus (6)
Die Anwendung startet als Symbol, ohne den Fokus zu erhalten.
Fensterstile für den Start einer Anwendung aus Visual Basic heraus Anwendung
................................................... Anwendung
Das gestartete Programm erbt das standardmäßige Verzeichnis des aufrufenden Programms (vgl. »CurDir-Funktion«, S. 134). Mit der von Shell gelieferten Task-ID lässt sich mit den inhärenten Mitteln von Visual Basic nicht anderes anfangen, außer sie der Anweisung AppActivate zu übergeben, um der gestarteten Anwendung den Fokus zuzuschanzen. Das könnte etwa geschehen, um diese Anwendung mittels der Anweisung SendKeys fernzusteuern. Auf diese Weise lassen sich »die Geister, die man rief« beispielsweise auch wieder loswerden. Tipp
................................................... Tipp
Um eine Anwendung unter Vorgabe eines bestimmten Arbeitsverzeichnisses zu starten, stellen Sie dieses Verzeichnis vor dem Shell-Aufruf mittels ChDrive und ChDir als standardmäßiges Arbeitsverzeichnis ein. Beispiel
................................................... Beis piel
Vgl. das Beispiel zur »Date-Funktion und Date-Anweisung« (S. 116).
1 58
Unlock- Anweisung
Unlock- Anweisung Sub Unlock([#]iDateiNr As Integer[, lDatensatz As Long]) Sub Lock([#]iDateiNr As Integer, [lStartNr As Long] To lEndNr As Long) Beschreibung
................................................... Bes c hreibung
Anwendung
................................................... Anwendung
Wenn in einem System oder in einem Netzwerk mehrere Programme gleichzeitig Schreibzugriffe auf dieselbe Datei durchführen, kann das recht unangenehme Inkonsistenzen zur Folge haben. Das Anweisungspaar Lock/Unlock ermöglicht es einem Programm, sich temporär die Exklusivrechte für das Beschreiben eines gewissen Teils der Datei zu sichern. Jeder Unlock-Anweisung muss eine gleichlautende Lock-Anweisung vorangegangen sein, sonst löst diese den Laufzeitfehler 70, »Zugriff verweigert« aus. Warnung
................................................... Wa rnung
Bei Lock/Unlock-Anweisungen für sequenzielle Dateien (Input, Output, Append) ignoriert Visual Basic Bereichsangaben und sperrt immer die gesamte Datei. Entgegen anders lautenden Informationen in der Online-Hilfe zu Visual Basic 6.0 entfernt Close (respektive das Programmende) jedoch alle Sperren, die mit Lock gesetzt und nicht explizit mit Unlock wieder freigegeben wurden. Beispiel
................................................... Beis piel
Der folgende Code schreibt eine Reihe von Datensätzen in die gemeinsame genutzte Datei Kunden.idx: Open "Kunden.idx" For Random As 1 Len = Datensatzlänge On Error GoTo Fehler70 Lock 1, lStartBereich To lEndBereich ' Sperre errichten On Error GoTo 0 For i = lStartBereich To lEndBereich Put 1, i, bdt(i) Next Unlock 1, lStartBereich To lEndBereich ' Sperre aufheben Close 1 Verwandte Befehle
................................................... Verwa ndte Befehle
Lock, Open
Write #- Anweisung Write #iDateiNr[, Ausdr1[Trennz [Ausdr2] ... ]]
1 59
Dateiorientierte Funktionen und Anweisungen
Die Anweisung Unlock hebt bestehende (programmeigene) Sperren für einen einzelnen Datensatz lDatensatz oder einen Bereich von Datensätzen lStartNr bis lEndNr auf, die für die Datei mit der Dateinummer iDateiNr mittels eines vorangegangenen Lock-Aufrufs errichtet wurden. Fehlt bei Gebrauch der ersten Syntax der optionale Parameter lDatensatz, hebt Unlock eine Sperre auf, die für die gesamte Datei errichtet wurde. Fehlt bei Gebrauch der zweiten Syntax der optionale Parameter lStartNr, entsperrt die Anweisung alle Datensätze von der Nummer 1 bis zur Nummer lEndNr. Bei Dateien, die im Modus Binary geöffnet wurden, beziehen sich die Bereichsangaben auf Positionen des Dateizeigers.
Dateiorientierte Funktionen und Anweisungen
Beschreibung
................................................... Bes c hreibung
Die Anweisung Write # schreibt Daten im Standardformat für literale Werte in die unter der Dateinummer iDateiNr geöffnete sequenzielle Datei. Die auf die Dateinummer folgende Parameterliste Ausdr1, Ausdr2 usw. darf Werte beliebiger Standardtypen enthalten. Write # gibt diese Werte unter Beachtung des jeweils zwischen den Parametern stehenden Trennzeichens Trennz aus. Anwendung
Dateiorientierte Funktionen und Anweisungen
................................................... Anwendung
Da Print# Daten im gebietsspezifischen Format schreibt, wird Write# immer dann anstelle von Print# eingesetzt, wenn die geschriebenen Daten später mittels Input# wieder eingelesen werden sollen. Das Standardformat für literale Werte sieht insbesondere vor, dass Zeichenfolgen in Anführungszeichen und Datumswerte im Format #mm/tt/jjjj# notiert werden. Als Trennzeichen Trennz sind das Semikolon und das Komma erlaubt. Im Gegensatz zu Print # unterscheidet Write# nicht zwischen den beiden Trennzeichen und trennt aufeinanderfolgende Werte der Parameterliste durch ein Komma. Ist im Anschluss an den letzten Parameter der Liste kein Trennzeichen spezifiziert, ergänzt die Anweisung einen Wagenrücklauf Chr(13) und einen Zeilenvorschub Chr(10) bzw. vbCrLf. Warnung
................................................... Wa rnung
Write# gibt Zeichenfolgen im ANSI-Code und nicht im Unicode aus. Tipp
................................................... Tipp
Im Gegensatz zu Write# versieht Print# Zeichenfolgen nicht automatisch mit Anführungszeichen. Wenn Sie ausschließlich Zeichenfolgen ausgeben, also reine Textverarbeitung betreiben, ist Print# in jedem Fall die bessere Lösung. Zum zeilenweisen Lesen der Daten verwenden Sie dann die Anweisung Line Input#. Beispiel
................................................... Beis piel
Vgl. das Beispiel zu »Input #-Anweisung« (S. 143). Verwandte Befehle
................................................... Verwa ndte Befehle
Input#, Open, Print# Verwandte Themen
................................................... Verwandte Them en
Literale und Konstanten (S. 27)
1 60
Variablen Eine Variable ist ein auf einen bestimmten Datentyp (Datenformat) zugeschnittener »Behälter«, der einen Bezeichner (lies: einen Namen) trägt und einen (in dem Datenformat gehaltenen) Wert enthält. Beschreibung
................................................... Bes c hreibung
Charakteristikum der Variable ist, dass ihr Wert veränderlich ist, das heißt, er kann nach Belieben zur Laufzeit im Rahmen von Zuweisungsoperationen sowie von Funktions- und Prozeduraufrufen geändert werden. Mit dieser Eigenschaft ist die Variable unverzichtbarer Bestandteil einer jeden Programmiersprache und Medium für den formalen Umgang mit Daten jeglicher Art. Anwendung
................................................... Anwendung
Für die Wahl des Variablenbezeichners gelten die üblichen Vorschriften für die Bezeichnerwahl (vgl. »Bezeichner und Namensraum«, S. 34). Variablen erhalten ihren Wert durch Zuweisung eines Literals, einer Konstante, einer anderen gleichartigen Variablen, eines Funktionswerts oder eines Ausdrucks, wobei die jeweiligen Datenformate miteinander verträglich sein müssen. Dim MyVar1 As Integer, MyVar2 As Integer, MyVar3 As Integer Dim MyVar4 As Integer MyVar1 = 17 ' Variable MyVar1 erhält Literal als Wert MyVar2 = MyVar1 ' Variable MyVar2 erhält Wert von MyVar1 MyVar3 = MyVar1 + 17 ' Variable MyVar3 erhält Ergebnis von Berechnung MyVar4 = Max(MyVar1, MyVar3) ' Variable MyVar4 erhält Funktionswert
Somit können Variablen für alles stehen, was einen in einen Datentyp gefassten Wert darstellt. Auch wenn es die Sprache Visual Basic im Vergleich zu anderen Programmiersprachen nicht gerade offensichtlich macht: Es besteht ein gewisser Unterschied zwischen Variablen, die einen einfachen Datentyp, und solchen, die einen komplexen Datentyp tragen. Zu den einfachen Datentypen zählen: alle elementaren Datentypen (vgl. »Elementare Datentypen«, S. 49), ausgenommen String und Variant; weiterhin: Aufzählungsdatentypen (vgl. »Enum-Aufzählungen«, S. 62), alle benutzerdefinierten Datentypen, die ihrerseits nur einfache Datentypen als Felder enthalten; der Typ String* für Zeichenfolgen fester Länge und Datenfelder fester Größe, deren Elemente einen einfachen Datentyp tragen. Diese Datentypen zeichnen sich dadurch aus, dass sich ihre Repräsentation während des Programmablaufs von der Länge her nicht verändert und sie deshalb als kompakte Bytefolge gespeichert werden können. Die einfachen Datentypen noch abgerechnet, die als spezielle öffentliche Datentypen im Objektkatalog des Systems registriert sind und ihrerseits aus einfachen Datentypen ausgebaut sind, rechnet man alle anderen Datentypen zu den komplexen Datentypen. Insbesondere zählen dynamische Zeichenfolgen des Typs String, der flexible Datentyp Variant, dynamische Arrays sowie alle zu Objekten gehörigen Datentypen (Klassen) zu den komplexen Datentypen. Komplexe Datentypen lassen sich nicht kompakt speichern, sondern bestehen aus Deskriptoranteilen (Verweisen, Längen etc.) und Wertanteilen. Während die komplexen Datentypen String und Variant mit einem elementaren Datentyp als Untertyp noch relativ einfach und größtenteils intuitiv zu handhaben sind, ist der Umgang mit dynamischen Arrays und Objekten nicht ohne ein gewisses Verständnis für die Natur der »dahinter liegenden« Größen zu bewerkstelligen. Obwohl andere Programmiersprachen hier eine recht klare Linie zwischen Variablen ziehen, die für eine konkrete Größe stehen und solchen, die nur einen Verweis auf eine andere Größe darstellen, gibt es in Visual Basic das Konzept des Verweises offiziell nur in Bezug auf die Parameterübergabe bei Funktionsaufrufen.
1 61
Variablendeklaration
Variablendeklaration
»Hinter den Kulissen« arbeitet aber auch Visual Basic massiv mit Verweisen. Deutlich wird das beispielsweise, wenn man eine Objektvariable einer abstrakten Klasse (etwa der Klasse Form) vereinbart, der man zwar konkrete via Set ins Leben gerufene und von Form abstammende Objekte als Werte zuweisen kann, nicht jedoch via Set einen Wert der Klasse Form selbst – einen solchen gibt es nämlich nicht als konkreten Wert, weil eine Objektvariable des Typs Form ein reiner Verweis ist. »Verständnis für die Natur eines Werts zu haben«, bedeutet für den Visual-Basic-Programmierer natürlich nicht, dass er bis auf das Genaueste wissen muss, wie Visual Basic Objekte repräsentiert (das ist auch C++-Programmierern im Allgemeinen nicht bis auf das Letzte klar, weil es letztlich vom Compiler abhängt, wie er ein Objekt repräsentiert). Das genau ist ja die Stärke der abstrakten Datentypen, deren Implementationen hinter Objekten verborgen bleiben. Es bedeutet aber sehr wohl, dass man bei der Verwendung von Variablen nicht alle Werte in einen Topf schmeißen darf und insbesondere wissen muss, ob man es mit einer Variablen für einen einfachen Datentyp, einen dynamischen Datentyp, ein konkretes Objekt oder ein abstraktes Objekt zu tun hat.
Variablendeklaration Dim [WithEvents] VarName[([Indizes])] [As Typ] Dim VarName[([Indizes])] [As [New] Typ] Dim [WithEvents] VarName[...], [WithEvents] VarName[...][, ...] Private VarName[([Indizes])] [As [New] Typ] Private [WithEvents] VarName[([Indizes])] [As Typ] Private [WithEvents] VarName[...], VarName[...][, ...] Public VarName[([Indizes])] [As [New] Typ] Public [WithEvents] VarName[([Indizes])] [As Typ] Public [WithEvents] VarName[...], VarName[...][, ...] Static VarName[([Indizes])] [As [New] Typ] Static VarName[...], VarName [...][, ...] Option Explicit Beschreibung
................................................... Bes c hreibung
Visual Basic ist eine typisierende Programmiersprache. Zwar nimmt der Compiler von sich aus eine – für den Programmierer weitgehend transparente – implizite Typzuordnung und Typumwandlung von Werten je nach Erfordernis vor, doch dieses Standardverhalten überdeckt nur einen kleinen Teil der mit Visual Basic verfügbaren Typenvielfalt. Der volle Umfang der von Visual Basic unterstützten Datentypen mit allen ihren Repräsentationsformen und Geltungsbereichen lässt sich nur bei Verwendung der expliziten Variablendeklaration nutzen. Jede Deklaration impliziert eine Initialisierung mit dem Standardwert des – implizit oder explizit – vereinbarten Datentyps. Anwendung
................................................... Anwendung
Bei der expliziten Deklaration von Variablen unterscheiden Programmiersprachen traditionell zwischen der statischen und der automatischen Vereinbarung. Statische Variablen, für deren Deklaration das Schlüsselwort Static erforderlich ist, finden ihre Repräsentation im Datensegment des Prozesses, während automatische Variablen ihr (meist kurzes) Dasein auf dem Stack fristen. Visual Basic erlaubt die Vereinbarung statischer Variablen nur auf Prozedurebene
1 62
Variablendeklaration
Dim myForm As Form1 Set MyForm = New Form1 MyForm.Show und Dim myForm As New Form1 MyForm.Show In diesem Fall kann also eine implizite Initialisierung durch Nennung erfolgen. Visual Basic unterscheidet bei der expliziten, automatischen Variablendeklaration weiterhin zwischen einer Vereinbarung auf Prozedurebene und einer Vereinbarung auf Modulebene (im Bereich ALLGEMEIN). Vereinbarungen auf Prozedurebene und auf Modulebene lassen sich mit dem traditionellen Basic-Schlüsselwort Dim treffen. Vereinbarungen auf Modulebene lassen sich alternativ auch mittels der neuen Visual-Basic-Schlüsselwörter Private und Public treffen. Private entspricht dabei dem Schlüsselwort Dim und begrenzt den Geltungsbereich der Variablen auf das aktuelle Modul. Public dehnt dagegen den Geltungsbereich der Variablen auf alle Module aus. Bei der Deklaration einer Objektvariablen auf Modulebene ist der Zusatz WithEvents möglich. Er spezifiziert, dass die Objektvariable auf ein Objekt verweisen wird, das von sich aus (mittels RaiseEvent) Ereignisse generiert und die der »Besitzer« (das ist die Ebene, auf der die Objektvariable bekannt ist) behandeln oder weiterreichen sollte. Zur Behandlung solcher Ereignisse lassen sich dann in dem jeweiligen Modul unter dem Namen der Objektvariablen Behandlungsroutinen bereitstellen – so wie man das von Steuerelementen, die auf einem Formular platziert wurden, oder von Menüeinträgen her gewöhnt ist. ' Bereich Allgemein Private WithEvents myObject As MyClass ... Private Sub myObject_EventOfMyClass() ...
In der Tat nimmt Visual Basic für jedes Steuerelement, das auf einem Formular platziert wird, implizit nichts anderes als eine WithEvents-Deklaration vor und vereinbart dabei den unter der Eigenschaft Name des Steuerelements genannten Bezeichner als Objektvariable. WithEvents lässt sich nur bei der Vereinbarung von Instanzen konkreter Klassen verwenden und schließt eine gleichzeitige Verwendung des Spezifizierers New aus.
1 63
Variablendeklaration
(innerhalb von Prozedur- bzw. Funktionskörpern) und ermöglicht damit Prozeduren »mit Gedächtnis«: Im Gegensatz zur automatischen Variablen, die bei jedem Aufruf neu auf dem Stack angelegt wird, bleibt eine statische Variable und damit auch ihr Wert von einem Aufruf zum nächsten erhalten. Bei der Darstellung komplexer Datentypen, so beispielsweise von dynamischen Arrays und Objekten, kommt – bei beiden Vereinbarungsarten – ein dritter Speicherbereich ins Spiel, der Heap. In diesem Fall stellt die statisch oder automatisch vereinbarte Variable eine Referenz auf den eigentlichen Wert dar, der seinerseits auf dem Heap liegt. Der Variablendeklaration muss dann eine explizite oder implizite Initialisierung mit New folgen, damit die Variable einen von Empty, Nothing bzw. Null abweichenden Wert erhält. Implizit erfolgt die Initialisierung, wenn bei der Deklaration der Variablen das Schlüsselwort New auftaucht. Äquivalent sind daher:
Variablendeklaration
Abkürzungen
Variablendeklaration
Visual Basic vereinbart Variablen bei schlichter Nennung implizit als automatische Variablen und ordnet ihnen den Datentyp Variant zu: Private Sub Form_Click() Set f = Me For i = 1 to 10 s = s + CStr(i) Next i f.Caption = s End Sub
' f ist Variant mit Objektreferenz ' i ist Variant mit numerischem Wert ' a ist Variant mit Stringwert ' Fenstertitel ändert sich zu 12345678910
Ein weitere, recht praktische Möglichkeit für die Abkürzung von Variablendeklarationen liefern die traditionellen Typkennzeichen $, %, &, !, # der Sprache Basic. Mehr dazu unter »Typkennzeichen und Bezeichnerbereiche für Typen« (S. 167). Beispiele
................................................... Beis piele
Das Projekt DMzuEuroRechner benutzt die explizite Variablendeklaration mit dem Datentyp Long für die Rundung auf zwei Stellen hinter dem Komma. Das Formular enthält die Textfelder TextDM und TextEuro, die bei Verlust des Eingabefokus den Wert des jeweils anderen Feldes an ihren eigenen Wert anpassen. ' Projekt: DMzuEuroRechner Private Const iKurs As Single = 1.95583 Private Sub TextDM_LostFocus() Dim l As Long l = TextDM.Text * 100 / iKurs TextEuro.Text = l / 100 End Sub
' implizite Typumwandlung ' Typumwandlung und Rundung
Private Sub TextEuro_LostFocus() Dim l As Long l = TextEuro.Text * iKurs * 100 ' implizite Typumwandlung TextDM.Text = l / 100 ' Typumwandlung und Rundung End Sub
Im folgenden Beispielprojekt NewForm erzeugt das von Visual Basic automatisch gestartete Formular bei jedem Mausklick eine weitere Instanz seiner selbst. Die zuständige Variable frmVar wird implizit durch Nennung initialisiert. Jede Formularinstanz zählt die Mausklicks, auf die es bereits reagiert hat, und gibt sie aus. Für das Zählen der Klicks ist iKlicks als statische Variable vereinbart. ' Projekt: NewForm Private frmVar As New Form1 Private Sub Form_Click() Static iKlicks As Integer ' mit 0 initialisiert iKlicks = iKlicks + 1 Print iKlicks; ". Klick in dieses Formular" frmVar.Show ' implizite Initialisierung End Sub
1 64
Variablendeklaration
Das kleine Programm zeigt sehr schön, dass jedes Formularobjekt auch von dieser statischen Variablen seine eigene Kopie erhält. Von jedem Formular aus lässt sich immer nur ein weiteres Formular starten, da fVar global deklariert ist. Schließt der Benutzer ein Fenster, bleibt das Formularobjekt erhalten und sein Fenster kommt durch den nächsten Klick in das jeweils vorgeordnete Formular erneut zur Anzeige. Man erkennt das an dem erhaltenen Wert von iKlicks, den ein Mausklick in das wieder sichtbare Formular zu Tage fördert.
................................................... Warnung
Warnung
Dim s1, s2, s3, s4 As String Dim i1, i2 As Integer
vier String-Variablen und zwei Integer-Variablen zu vereinbaren, ist bereits auf dem Holzweg, wie der folgende Code zeigt: i1 = 1234 i2 = 5678 s1 = i1 ' s2 = i2 ' s3 = s1 + s2 ' s4 = CStr(i1) + CStr(i2) ' Print s3, s4 '
erwartet: s1 = "1234" erwartet: s2 = "5678" erwartet: Stringaddition Stringaddition nach expliziter Typumwandlung unerwartete Ausgabe: 6912 12345678
Tatsächlich betrachtet der Compiler in diesem Codefragment i1, s1, s2 und s3 als Variablen vom Typ Variant, wie der Debugger schnell anhand des Initialisierungswerts Leer enthüllt. Die korrekte Formulierung lautet: Dim s1 As String, s2 As String, s3 As String, s4 As String Dim i1 As Integer, i2 As Integer i1 = 1234 i2 = 5678 s1 = i1 ' s1 = "1234" s2 = i2 s3 = s1 + s2 ' Stringaddition s4 = CStr(i1) + CStr(i2) ' CStr ist nun eigentlich unnötig Print s3, s4 ' erwartete Ausgabe: 12345678 12345678
Würde man den impliziten Typumwandlungsmechanismus nutzen, ließe sich die gesamte Variablendeklaration auf die Zeile Dim s1 As String, s2 As String
beschränken. Wenn nämlich s1 und s2 Zeichenfolgen sind, nimmt Visual Basic bei den Zuweisungen s1 = i1 s2 = i2
' Typumwandlung: von Variant mit numerischen ' Daten in String
eine implizite Typumwandlung in den Datentyp String vor.
1 65
Variablendeklaration
Die Syntax von Visual Basic für die akkumulierende Variablenvereinbarung ist leider in fehlerträchtiger Abweichung zu anderen Programmiersprachen (etwa zu C/C++, Pascal, Delphi) gestaltet, was gerade geübten Programmierern, die in vielen Sprachen zu Hause sind, gerne zum Stolperstein wird. Die resultierenden Bugs sind nicht nur extrem schwer zu finden, sie sind noch dazu ziemlich lästig, weil die implizite Typumwandlung des Compilers zuweilen die seltsamsten Kapriolen auf Lager hat. Wer glaubt, mit der Formulierung
Variablendeklaration
Generell merke man sich die einfache Regel: Visual Basic ordnet jeder Variablen, die nicht explizit und mit unmittelbar anschließender Nennung eines Datentyps deklariert wurde, den Datentyp Variant zu. Auf diese Weise werden auch schlichte Schreibfehler schnell zur Falle. Visual Basic vereinbart für den vertippten Bezeichner eine eigene Variable des Typs Variant. Tipp
Variablendeklaration
................................................... Tipp
Gewöhnen Sie sich an, Variablen explizit zu vereinbaren und auch explizite Typumwandlungen hinzuschreiben. Das hilft, unliebsame Überraschungen durch falsche Typisierung und durch ungewollte Doppelverwendung weitgehend zu vermeiden. Sie erhalten eine bessere Kontrolle über die Datentypen und gleichzeitig auch einen schnelleren Code. Wenn Sie wollen, dass Sie der Compiler zur expliziten Typdeklaration zwingt, fügen Sie die Anweisung Option Explicit
in den Bereich ALLGEMEIN des jeweiligen Moduls ein. Auf diese Weise schützen Sie sich (und ihre Kunden) vor unbemerkten Tippfehlern in Bezeichnern, die Visual Basic sonst ungefragt als eigenständige Variablen des Typs Variant vereinbart und mit dem jeweiligen Standardwert initialisiert. Wenn Sie in der Entwicklungsumgebung von Visual Basic über das Menü EXTRAS/ OPTIONEN auf der Eigenschaftsseite EDITOR die Einstellung VARIABLENDEKLARATION ERFORDERLICH treffen, setzt der Editor diese Anweisung automatisch in jedes neue Modul, das Sie anlegen. Wenn Sie es lieber auf die »sanfte Art« beigebracht haben wollen: Streuen Sie bei der Deklaration (explizit oder implizit) Großbuchstaben in die Variablenbezeichner ein und tippen Sie die Bezeichner später in Kleinbuchstaben. Der Editor von Visual Basic wandelt den Bezeichner bei Verlassen der Zeile automatisch in die bei der Deklaration gewählte Schreibweise um. Bleibt die Kleinschreibung erhalten, weist dies auf einen Tippfehler hin. Const cMinLänge = 1 Const cMaxLänge = 1000 Private lFileLength As Long ... ' Bei Schreibfehlern korrigiert Visual Basic die Schreibweise nicht! If lFileLength < cMaxLänge And lfilelenght > cminläng Then
Nachdem Dim-Anweisungen an beliebiger Stelle im Programm stehen dürfen, ist es darüber hinaus eine Frage des Programmierstils, wo Sie Ihre Variablen explizit deklarieren: ● ●
direkt »vor Ort«, unmittelbar vor der Initialisierung (populär) gleich zu Anfang der Prozedur (klassisch)
Die Deklaration »vor Ort« wird zwar immer populärer, weil sie angeblich die Lesbarkeit von Programmen verbessern soll, meines Erachtens bewirkt sie aber eher das Gegenteil: Deklarationen, die irgendwo mitten im Programmtext versteckt sind, lassen sich schwer auffinden, wenn der Typ der Variablen an späterer Stelle einmal unklar ist. Dagegen ist die Deklaration »zu Beginn« ein klare Sache – man weiß immer, wo die Deklaration einer Variablen zu finden ist. Welchen Stil auch immer Sie bevorzugen, bleiben Sie ihm treu. Es hat wenig Sinn, beide zu mischen, dann ist die Verwirrung nämlich komplett. Eine Variable mit eingestreuter Deklaration liest sich schnell als Variant, wenn einige zu Beginn deklarierte Variablen auf den klassischen Deklarationsstil hindeuten Verwandte Befehle
................................................... Verwa ndte Befehle
ReDim
1 66
Typkennzeichen und Bezeichnerbereiche für Typen
Verwandte Themen
................................................... Verwandte Them en
ActiveX-Steuerelemente (OCX) – Windows-Standardsteuerelemente (S. 433); Arrays (S. 55); Datentypen und ihre Operationen (S. 49); Funktionen selbst definieren (S. 181); Geltungsbereiche von Variablen (S. 173); Klassen als Datentypen für Objektvariablen (S. 196); Objekte und Klassen (S. 195); Prozeduren selbst definieren (S. 183); Typkennzeichen und Bezeichnerbereiche für Typen (S. 167); Variableninitialisierung (S. 168)
Typkennzeichen und Bezeichnerbereiche für Typen $, %, &, !, #
Beschreibung
................................................... Bes c hreibung
Neben der expliziten Typdeklaration unterstützt Visual Basic natürlich auch die traditionellen impliziten Möglichkeiten der Typdeklaration durch frei definierbare Präfixe (Bezeichnerbereiche) und durch vordefinierte Suffixe (Typkennzeichen). Anwendung
................................................... Anwendung
Syntaktisch gesehen findet die Deklaration hier durch Erweiterung des Bezeichners um ein Zeichen statt. Die Suffixlösung – einst die populärere – muss mit einem festen Vorrat an Typkennzeichen auskommen: $ für String, % für Integer, & für Long, ! für Single, # für Double. Selbst angesichts der reicheren Typenvielfalt von Visual Basic ist dieser Vorrat nicht mehr aufgestockt worden (nicht zuletzt mangels weiterer Sonderzeichen). Verwendung findet am ehesten noch das Zeichenfolgenzeichen $ For i% = 1 to 100 ' Implizite Deklaration durch Typkennzeichen a$ = a$ + Val(i%) Next i%
Die Präfixlösung erfordert eine Deklaration, die festlegt, welches Präfix welchen Datentyp kennzeichnen soll. Präfixe sind aus der Menge der für Bezeichner zulässigen Zeichen zu wählen. Dabei können auch mehrere Präfixe, ja sogar Buchstabenbereiche, ein und denselben Datentyp kennzeichnen. Der Compiler bemängelt allerdings, wenn Sie versuchen, einen Buchstaben mehreren Datentypen zuzuordnen. ' Bereich Allgemein DefLng L,X-Z DefStr S DefBool B DefCur C DefObj O
Damit erhalten alle Variablen, deren Bezeichner mit einem der Zeichen L, X, Y oder Z beginnt, bei schlichter Nennung (implizite Deklaration) automatisch den Typ Long. Zeichenfolgenvariablen werden dagegen durch einen mit S beginnenden Bezeichner implizit vereinbart usw. Bei expliziter Typdeklaration oder Verwendung eines vordefinierten Suffix ist diese Namenskonvention außer Kraft. DefCur C ...
1 67
Variablendeklaration
DefTyp AbisZ [, AbisZ[, ...]] DefBool, DefByte, DefInt, DefLng, DefCur, DefSng, DefDbl, DefDec, DefDate, DefStr, DefObj, DefVar
Variableninitialisierung
cWertInEuro c$ = "DM" Dim cents As Integer
' Typ ist Currency ' Typ ist String ' Typ ist Integer
Visual Basic verwendet implizit die Vereinbarung DefVar A-Z
das heißt, alle Variablen erhalten bei impliziter Deklaration den Standardtyp Variant. Um Long als neuen Standardtypen zu vereinbaren, schreiben Sie:
Variableninitialisierung
DefLong A-Z
Eine solche Vereinbarung kann durchaus sinnvoll sein, weil sie nicht nur den Code schneller macht, sondern auch eine explizite Deklaration des komplizierten Typs Variant erzwingt. Tipp
................................................... Tipp
Um den Typ einer Variablen mittels Suffix festzulegen, genügt es, das Suffix bei erstmaliger Nennung des Variablenbezeichners zu gebrauchen. Für weitere Nennungen ist ein Suffix nicht mehr erforderlich – aber auch nicht schädlich. Wenn Sie nicht wissen, welchen Typ eine Variable hat, können Sie den Typ mittels TypeName in Erfahrung bringen. a$ = 10 Print a, TypeName(a)
' implizite Deklaration als String, 10 ist "10" ' Ausgabe: 10 String
Verwandte Befehle
................................................... Verwa ndte Befehle
Option Base, Option Explicit Verwandte Themen
................................................... Verwandte Them en
Elementare Datentypen (S. 49); Variableninitialisierung (S. 168); Geltungsbereiche von Variablen (S. 173); Arrays (S. 55); Funktionen selbst definieren (S. 181); Prozeduren selbst definieren (S. 183)
Variableninitialisierung [Let] SimpleVar = Ausdruck Set ObjektVar = [New] ObjektAusdr Set ObjektVar = Nothing Beschreibung
................................................... Bes c hreibung
Im klassischen Programmiermodell für typisierende Sprachen muss eine Variable vor ihrem ersten Einsatz als »Rechtswert« (lies: auf der rechten Seite des Gleichheitszeichens oder als Parameter bei einem Funktions-/Prozeduraufruf) geeignet deklariert und initialisiert werden. Basic war in dieser Hinsicht schon immer etwas legerer, weil es weder die Deklaration noch die Initialisierung zwingend vorschreibt, sondern bei Nennung eines Bezeichners eine implizite Deklaration und im selben Aufwasch auch eine Initialisierung mit dem Standardwert des jeweiligen Datentyps vornimmt. Somit kann der Basic-Programmierer im Allgemeinen davon ausgehen, dass jede Variable zu jedem Zeitpunkt einen definierten Zustand besitzt (das ist nicht in allen Sprachen so). Dennoch wird in den meisten Fällen eine explizite Variableninitialisierung nicht nur die Lesbarkeit verbessern, sondern auch vonnöten sein.
1 68
Variableninitialisierung
Anwendung
................................................... Anwendung
Dim myObject As Object ... Set myObject = New Form1
' Deklaration ' an späterer Stelle ...
Warnung
................................................... Wa rnung
Wie schon angedeutet, spielt Visual Basic dem Programmierer gerne mal den einen oder anderen Streich, indem es von sich aus implizite Typzuordnungen und -umwandlungen vornimmt. Eine notorische Ursache für Ungereimtheiten im Programmverhalten ist die auf die gebietsspezifische Zahlendarstellung (Ländereinstellungen des Systems) zurückzuführende Punkt-/KommaProblematik bei der Initialisierung mit numerischen Literalen und literalen Zeichenfolgen. Wer mit der Gebietseinstellung »Deutsch (Standard)« auf seinem System einen Zahlenwert als literale Zeichenfolge spezifiziert, muss darauf achten, das Dezimalsymbol als echtes Komma zu schreiben. Punkte in literalen Zeichenfolgen ignoriert Visual Basic geflissentlich. Diese Problematik ist inzwischen fast schon älter als des Kaisers Bart, und man muss sich wahrlich die Frage stellen, warum Microsoft die Sprache inzwischen nicht um einen entsprechenden Schalter erweitert hat (etwa Option Locale), der es erlaubt, das Problem mit globaler Wirkung schlicht abzuschalten. Ein weiteres Problem wirft zuweilen die Initialisierung mit literalen Werten in hexadezimaler Darstellung auf. Visual Basic betrachtet ein vierstelliges Hex-Literal grundsätzlich als Wert vom Typ Integer, auch wenn der Linkswert vom Typ Long ist. Somit konvertiert Visual Basic beispielsweise den Wert &HFFFF in -1 und nicht in 65.535. Als Lösung bietet es sich an, dem Literal entweder eine (oder mehrere) führende Null(en) zu verpassen oder das Typkennzeichen & anzuhängen (vgl. unter Beispiele). Tipp
................................................... Tipp
Mit Visual Basic 6 hat sich zumindest etwas getan, was die Beilegung der Punkt-/Komma-Problematik betrifft, wenn auch nicht gerade viel. Die neue Funktion StrConv ermöglicht unter anderem die Angabe einer Gebietsschema-ID für die Umwandlung einer Zeichenfolge in den Typ Variant mit Untertyp String. Damit lässt sich ein bestimmtes Gebietsschema als Referenzschema verwenden, was eine gewisse Unabhängigkeit von den Systemeinstellungen bedeutet. Sollte eine Variable trotz sorgfältig geprüfter Deklaration und Initialisierung ihren Wert aus unerklärlichen Gründen plötzlich ändern, sind Sie aller Wahrscheinlichkeit nach einer Verletzung der Regeln für den Geltungsbereich aufgesessen. In den meisten Fällen wurde ein Prozedurparameter genauso benannt wie eine globale Variable.
1 69
Variableninitialisierung
Die Initialisierung von Variablen, die keine Objektvariablen sind, erfolgt grundsätzlich durch eine einfache Let-Zuweisung, wobei das optionale Schlüsselwort Let so gut wie immer der Einfachheit halber weggelassen wird. Visual Basic nimmt bei einer solchen Zuweisung gegebenenfalls erforderliche Typumwandlungen von sich aus vor – was nicht gerade immer zum gewünschten Ergebnis führt. Anders sieht die Sache bei Objektvariablen aus. Erfolgt die Deklaration einer Objektvariablen unter Angabe von New, erzeugt Visual Basic das entsprechende Objekt umgehend, das heißt, die Variable wird im Rahmen der Deklaration vollständig initialisiert (lies: an ein vollständig initialisiertes Objekt gebunden). Fehlt der Spezifizierer New, initialisiert Visual Basic die Objektvariable zunächst mit dem Standardwert Empty für den Datentyp Object (welcher für Objekte aller Klassen benutzt werden kann). Dem muss an späterer Stelle eine explizite Initialisierung der Objektvariablen im Rahmen einer Set-Anweisung unter Angabe des Schlüsselwortes New sowie einer konkreten Klasse folgen, die das Objekt schließlich ins Leben ruft:
Variableninitialisierung
Beispiele
................................................... Beis piele
Variableninitialisierung
' String Dim A As String Dim B As String A = "Literal" B = A + A A = 100 A = Hex$(65)
' ' ' ' ' '
' String * Dim A As String * 6 ' A = "String" ' A = "Integer" ' A = "DM" ' ' Byte Dim A As Byte A = i% A = AscB(s$) A = Asc(s$) ' Integer Dim A As Integer Dim B As Integer A = 10 A = &HFFFF A = 10.5 A = -10.5 B = A B = "10.5"
' Long Dim A As Long A = 123456789& A = &HFFFF
"" (Standardwert) "" (Standardwert) "Literal" "LiteralLiteral" "100" implizite Str-Umwandlung "41"
' "||||||" (Standardwert mit | als Nullzeichen) "String" "Intege" Wert ist abgeschnitten "DM " mit Leerzeichen aufgefüllt
' ' ' ' '
0 (Standardwert) 0 (implizite Typumwandlung geht gut, solange i% < 256) geht immer gut geht nicht immer gut, wenn s$ Unicode
' ' ' ' ' ' ' ' '
0 (Standardwert) 0 (Standardwert) 10 -1 (Zweierkomplement) 10 (Nachkommastellen abgeschnitten) -10 (Nachkommastellen abgeschnitten) 10 105 (implizite Val-Umwandlung, Punkt nicht als Komma erkannt, wegen Ländereinstellungen!)
' ' ' ' A = &HFFFF& ' A = &HFFFFFFFF ' b% = 10: A = b% ' A = "123,456789" ' ' A = "123.456789" ' '
0 (Standardwert) 123456789 (Long-Literal wegen &) -1 (Zweierkomplement und Interpretation als Integer-Literal) 65535 (Long-Literal) -1 (Zweierkomplement) 10 (implizite Typumwandlung) 123 (implizite Val-Umwandlung Nachkommastellen abgeschnitten) 123456789 (implizite Val-Umwandlung und Punkt nicht als Komma erkannt, wegen Ländereinstellungen!)
' Single Dim A As Single ' 0.0 (Standardwert) A = 1023.1234567 ' 1023.123 (nicht mehr als 7 Stellen)
1 70
Variableninitialisierung
A = 1E-23 ' A = "123,123456" ' A = "123.456789" ' '
' Boolean Dim A As Boolean Dim B As Boolean A = True A = (A <> True) A = 1234 A = 0
' ' ' ' ' '
0.0 (Standardwert) 1023.1234567 (maximal 14 Stellen) -1 (Hexzahl zuerst als Long, Zweierkomplement) 123.1234 (implizite Val-Umwandlung) 1.234568E+8 (Rundung! Punkt wird wegen Ländereinstellungen nicht als Komma erkannt!)
' ' ' ' ' '
False (Standardwert) False (Standardwert) True False True False
Variableninitialisierung
' Double Dim A As Double A = 1023.1234567 A = &HFFFFFFFF A = "123,123456" A = "123.456789"
1E-23 123.1234 (implizite Val-Umwandlung) 1.234568E+8 (Rundung und Punkt wird wegen Ländereinstellungen nicht als Komma erkannt!)
' Currency Dim A As Currency ' 0 (Standardwert) A = 12345.1234 ' 12345.1234 (Literal) A = "12345.1234" ' 123451234 (implizite Val-Umwandlung, Punkt wird ' wegen Ländereinstellungen nicht als Komma erkannt!) A = 12.123456 ' 12.1235 (Rundung auf vier Stellen hinter dem Komma) A = &HFFFFFFFF ' -1 (Longliteral und Zweierkomplement) ' Decimal Dim A As Variant ' Datentyp kann nicht direkt vereinbart werden! A = CDec("123456789012345678901234567890") ' 29 Stellen erlaubt A = CDec("12.12") ' 1212 (implizite Val-Umwandlung, Punkt wird
' wegen Ländereinstellungen nicht als Komma erkannt!) A = CDec("12,12") ' 12.12 A = CDec(12.12) ' 12.12 (Wandlung über Single) A = CDec(&HFFFF) ' -1 (Zweierkomplement und Interpretation als ' Integer -Literal) ' Date Dim A As Date ' 00:00:00 A = "01.01.2000" ' 01.01.00 A = "1.1.00" ' 01.01.00 A = 12.12 ' 11.01.1900 02:52:48 (Wandlung über Single) A = "1.1" ' 01.01.00 (aktuelles Jahr wird ergänzt) A = &HFFFF ' 29.12.1899 (Hausnummer, Wandlung über Integer) A = "11.11.11 11:11" ' Faschingsbeginn im Jahre 2011 A = "12:23:01" ' 12:23:01 (Uhrzeit!) ' Object Dim A As Object
' Nothing (Standardwert)
1 71
Variableninitialisierung
Variableninitialisierung
Dim B As New Form1 ' B ist Objekt der Klasse Form1 Set A = New Form1 ' A ist Objektvariable und verweist auf neues Objekt ' der Klasse Form1. New ist erforderlich! B.Hide ' Set oder New ist nicht erforderlich! Set A = B ' Altes Objekt von A stirbt, und A wird Objektvariable ' für B. Set ist erforderlich! A.Show ' B-Formular wird angezeigt B.Caption = "B" ' Titelleiste von B-Formular ändert sich in "B", denn ' Objektvariable A ist Synonym für Objekt B ' Variant Dim A As Variant Dim B Dim C As Object B = Null A = C A = "1234" A = 1234 A = TypeName(A)
' ' ' ' ' ' ' '
Empty (Standardwert) Empty (Standardwert) Nothing (Standardwert) Null Nothing "1234" 1234 "Integer"
' Arrays Dim A(20) As Integer
' ' ' ' Dim B(3 To 30) As Date ' ' ' ' Dim C(10,2) As Integer ' Dim D() ' ' ' ReDim D(2) ' ' '
A ist Arrayvariable für Array mit 21 Feldern bei standardmäßiger Indexzählung ab 0, sonst mit 20 Feldern. Alle Feldwerte sind mit 0 (Standardwert für Integer) initialisiert. B ist Arrayvariable für Array mit 28 Feldern. Die Indexzählung beginnt ab 3 und geht bis 30. Alle Feldwerte sind mit 00:00:00, dem Standardwert für Date, initialisiert. C ist Arrayvariable für zweidimensionales Array D wird als dynamisches Datenfeld des Typs Variant vereinbart (Visual Basic nimmt keine Initialisierung vor) D wird dimensioniert, dabei erhalten alle Felder den Wert Empty (Standardwert für Variant).
Verwandte Befehle
................................................... Verwa ndte Befehle
Property Let, Property Set Verwandte Themen
................................................... Verwandte Them en
Datentypen und ihre Operationen (S. 49); Geltungsbereiche von Variablen (S. 173); Variablendeklaration (S. 162)
1 72
Geltungsbereiche von Variablen
Geltungsbereiche von Variablen Private, Public, Dim Beschreibung
................................................... Bes c hreibung
' Modul Form1 Public iWert As Integer Sub MeineSub() Dim iWert As Integer ... iWert = Form1.iWert ...
' auf Programmebene vereinbart ' auf Prozedurebene vereinbart ' Mit Modulnamen qualifiziert
Anwendung
................................................... Anwendung
In der geschilderten Weise klingt das zunächst einmal recht abstrakt. Die Geltungsbereiche haben aber große Vorteile. Zunächst einmal unterliegt man bei der Programmierung von Prozeduren und Funktionen so gut wie keinen Einschränkungen, was die Wahl der Bezeichner betrifft. Zu beachten ist lediglich, dass man keinen Bezeichner erwischt, der auf Modulebene als Private vereinbart wurde und dessen aktueller Wert innerhalb der Prozedur benötigt wird. Die auf Modulebene gelegene Variable, Funktion oder Prozedur wäre dann nämlich nicht ansprechbar. Für öffentliche Bezeichner lässt sich die Herkunft dagegen jederzeit durch Qualifizierung ausdrücken, so dass hier keine Schwierigkeiten zu befürchten sind. Das ermöglicht es insbesondere Objekten, ihre öffentlichen Elementvariablen und Methoden gegenseitig anzusprechen: Sub Form1_Load() Dim frm2 As New Form2 frm2.ShowInTaskBar = False
' Qualifizierung
Tipp
................................................... Tipp
Die Bezeichner der öffentlichen Elemente von öffentlichen Datentypen lassen sich über den Objektkatalog (Taste (F2)) herausfinden. Verwandte Themen
................................................... Verwandte Them en
Bezeichner und Namensraum (S. 34)
1 73
Geltungsbereiche von Variablen
Der Geltungsbereich oder Gültigkeitsbereich einer Variablen ist der Teil des Codes, in dem eine Variable und ihr Wert bekannt sind. Der engste Geltungsbereich ist die Prozedurebene. Er gilt für Variablen, die innerhalb einer Prozedur bzw. Funktion als automatische oder statische Variablen vereinbart werden, sowie für alle Argumentvariablen. Der nächstgrößere Geltungsbereich ist die Modulebene. Er gilt für automatische Variablen, die mittels Private oder Dim außerhalb eines Prozedurkörpers vereinbart werden. Den größten Geltungsbereich, die Programmebene, haben Variablen, wenn sie auf Modulebene mittels Public als öffentliche Variablen vereinbart werden. Wird ein Variablenbezeichner in zwei unterschiedlichen Geltungsbereichen eingeführt, wovon der eine den anderen überlappt, bezeichnet er zwei unterschiedliche Variablen, von denen die eine nur im kleineren Geltungsbereich sichtbar ist und die andere im gesamten größeren Geltungsbereich, abzüglich des kleineren. Es besteht aber die Möglichkeit, von der Prozedurebene aus gleichnamige Variablen anzusprechen, die der Programmebene angehören, indem man den Bezeichner mit dem Modulnamen qualifiziert:
Funktionen und Prozeduren ProzedurBez [ParamListe] Call ProzedurBez [(WerteListe)] [Let] Var = FunktionsBez[(WerteListe)] := (Operator für benannte Parameter) Man kann sagen, was mal will, einem Basic-Programmierer der sechziger Jahre würde ein heutiges Visual-Basic-Programm nicht nur ausgesprochen »spanisch« vorkommen, er würde es wahrscheinlich gar nicht als solches erkennen. In der Tat hat die Einführung von Funktionen und Prozeduren den Charakter der Sprache von Grund auf und nachhaltig verändert – SpagettiCode ade, es lebe die strukturierte Programmierung. Das mit Visual Basic vorliegende Funktions- und Prozedurkonzept ist inzwischen soweit mit dem der Sprache C/C++ kompatibel, dass Visual Basic vollen Zugriff auf Bibliotheken bietet, die dem Standard für DLLs genügen. Dazu zählen natürlich in erster Linie die Systembibliotheken von Windows (Windows-APIs), aber auch die gesamte Palette an DLLs, die zur Unterstützung der einen oder anderen Anwendung geschrieben wurden. Ein eingefleischter C/C++-Programmierer wird aber immer noch das Konzept des Funktionszeigers vermissen, das Visual Basic wohl deshalb nicht explizit unterstützt, weil noch nicht einmal das Konzept des Zeigers an sich seinen Weg in die Sprache gefunden hat. Erfreulicherweise kann Visual Basic aber dennoch Systemdienste nutzen, für deren Aufruf die Angabe einer Rückruffunktion erforderlich ist – es gibt ein Hintertürchen, wenn auch nur ein recht schmales. Beschreibung
................................................... Bes c hreibung
Funktionen/Prozeduren stellen ein eigenständiges Stück Code dar, das eine definierte Schnittstelle zum restlichen Teil des Programms hat. Auf diese Weise erreichen Funktionen und Prozeduren eine weitgehende semantische Geschlossenheit, die für die strukturierte Programmierung eine wichtige Voraussetzung ist. Die Definition der Schnittstelle findet sich im so genannten Funktionskopf bzw. Prozedurkopf. Ein solcher Kopf enthält einen Bezeichner mit optionalem Spezifizierer für den Geltungsbereich, eine formale Auflistung mit Deklaration aller Argumentvariablen, in der für jede Variable festgelegt wird, ob der entsprechende Parameter als Wert oder als Verweis übergeben wird, sowie – im Falle einer Funktion – den Typ des Rückgabewerts. Die Definition dessen, was eine Prozedur/Funktion macht (ihr Code), findet sich schließlich als Anweisungsfolge im Prozedurkörper bzw. Funktionskörper. Das dem Bezeichnerwesen von Visual Basic zugrunde liegende Qualifizierungsmodell ordnet eine Funktion/Prozedur immer dem Modul als privates oder öffentliches Element zu, das seinen Körper enthält. Im Zusammenhang mit Objekten und Klassen spricht man statt von Funktionen und Prozeduren von Methoden. Um auf eine Funktion/Prozedur von DLLs zugreifen zu können, die nicht standardmäßig von Visual Basic unterstützt werden, sind in dem jeweiligen Modul explizite Importdeklarationen nötig (vgl. »Routinen aus DLLs und der Windows-API einsetzen«, S. 185) Anwendung
................................................... Anwendung
In Visual Basic ist die Unterscheidung zwischen Prozeduren und Funktionen sehr streng. Eine Prozedur verkörpert eine Anweisung und wird wie eine solche notiert. Eine Funktion ist dagegen eine Operation, die einen Wert mit einem zugrunde liegenden Datentyp verkörpert. Mithin lässt sich eine Funktion also überall da notieren, wo ein Wert erwartet wird: als Rechtswert bei Zuweisungen und in Ausdrücken. Rein formal gesehen besteht der Unterschied zwischen Prozeduren und Funktionen somit darin, dass Prozeduren keinen Wert zurückgeben, während Funk-
1 75
Geltungsbereiche von Variablen
tionen einen Funktionswert liefern. In allen anderen Aspekten sind sich die beiden mehr ähnlich als dass sie sich unterscheiden. So lässt sich eine Anweisung ohne Schwierigkeiten als Funktion formulieren und eine Funktion ebenso einfach als Prozedur – die Wertübergabe lässt sich ja über Parameter lösen.
Geltungsbereiche von Variablen
Aufruf Der Aufruf einer Funktion kann überall dort erfolgen, wo ein Wert mit dem Ergebnistyp der Funktion stehen kann. Man notiert dazu einfach den Funktionsbezeichner gefolgt von einer geklammerten Werteliste mit Komma als Trennzeichen, welche der Reihenfolge nach die einzelnen Parameter mit Werten versorgt. sDateiname = Left(sDateiname,10)
Für den Aufruf von Prozeduren kennt Visual Basic dagegen zwei unterschiedliche Notationen: das Anweisungsformat oder das Call-Format. Das Anweisungsformat sieht die Nennung des Prozedurbezeichners mit nachfolgender nicht geklammerter Werteliste als eigenständige Anweisung vor. Rnd 1.1234
Das Call-Format überlässt der Anweisung Call den Aufruf der Prozedur, wobei diese im Anschluss an das Schlüsselwort Call wie eine Funktion – also mit geklammerter Werteliste – notiert wird. Call Rnd (1.1234)
Die Werteliste kann auch fehlen, wenn die Funktions-/Prozedurdefinition dies zulässt. Ein leeres Klammerpaar ist dann nicht zu notieren: datZeit = Now Rnd
' Now ist parameterlose Funktion (Eigenschaft) ' Prozedur ist für parameterlosen Aufruf definiert
Benannte Argumente Seit noch nicht langer Zeit unterstützt Visual Basic auch das Konzept der benannten Argumente für Funktionen und Prozeduren (genauer: Visual Basic 6.0 (SP3) unterstützt das Konzept in zunehmendem Maße, da die Objektbibliothek VB dem Konzept bisher noch nicht angepasst wurde). Es sieht vor, dass für den Aufruf einer Funktion/Prozedur anstelle einer Werteliste auch eine Wertzuordnungsliste übergeben werden kann, deren Einträge eine explizite Wertzuordnung zwischen dem Bezeichner eines Parameters und seinem Wert vorschreiben. Das hat den Vorteil, dass die Reihenfolge der Elemente in der Liste nicht mehr an die Vereinbarungsreihenfolge angepasst sein muss, erspart aber nicht die Versorgung obligatorischer Parameter mit Werten. Während in einfachen Wertelisten weggelassene optionale Parameter zumindest durch ein Komma zu notieren sind, es sei denn, sie stehen am Schluss der Liste, müssen in Wertzuordnungslisten tatsächlich nur so viele Einträge enthalten sein, wie es obligatorische Parameter gibt. Die ganze Sache sieht dann so aus: Sub MeineProzedur(a, Optional b = 0, Optional c = 0, Optional d = 2) ... End Sub Sub Demo() MeineProzedur 2, , , 1 ' Aufruf mit Werteliste MeineProzedur d:= 1, a:= 2 ' Aufruf mit Wertzuordnungsliste End Sub
1 76
Geltungsbereiche von Variablen
Gegenseitiger und rekursiver Aufruf Funktionen und Prozeduren können jederzeit andere Funktionen und Prozeduren aufrufen, deren Bezeichner im jeweiligen Geltungsbereich bekannt sind. Erlaubt ist auch der Selbstaufruf – man spricht dann von einer rekursiven Funktion oder einer rekursiven Prozedur. Bei rekursivem Aufruf ist allerdings auf einen sicheren Abbruch der Rekursion zu achten, damit kein Laufzeitfehler wegen Überlaufs des Stapelspeichers auftritt. Die Festlegung des Funktionswerts einer Funktion erfolgt durch Zuweisung eines Werts an den Funktionsbezeichner innerhalb des Funktionskörpers. Als Linkswert verkörpert der Funktionsbezeichner nichts weiter als eine Variable mit dem Rückgabetyp der Funktion – als Rechtswert dagegen einen rekursiven Aufruf!
In allen Fällen ist anstelle der rekursiven aber auch eine iterative Formulierung möglich, die zwar weniger elegant aussieht, dafür aber sicherer und meist auch schneller ist: Function Fakultät(zahl) Dim Erg Erg = 1 while zahl > 1 Erg = Erg * zahl zahl = zahl -1 Wend Fakultät = Erg End Function Tipp
................................................... Tipp
Es lohnt sich, beim Programmentwurf auf eine gute Strukturierung durch Funktionen und Prozeduren zu achten. Wer zu viel in eine Funktion/Prozedur packt, verschenkt Universalität. Fassen Sie Ihren Code daher am besten in viele kleine möglichst allgemein und übersichtlich gehaltene Funktionen/Prozeduren und dokumentieren Sie die Aufrufschnittstellen gut in Form von Kommentaren. Warnungen
................................................... Wa rnungen Da in Visual Basic sowohl Sprungmarken als auch mehrere Befehle in einer Zeile durch einen Doppelpunkt notiert werden, ist folgender Aufruf zweideutig: Save: Print " Me"
Der Compiler betrachtet Save hier als Sprungmarke, wie folgendes Programm zeigt: Private Sub Form_Load() Save: Print " Me" Call Save: Print "Me", End Sub
' Ausgabe: Me ' Ausgabe: Save Me
Sub Save()
1 77
Geltungsbereiche von Variablen
Function Fakultät(a) If a < 2 Then Fakultät = 1 Else Fakultät = a * Fakultät(a-1) End If End Function
Parameterübergabe an Funktionen und Prozeduren
Parameterübergabe an Funktionen und Prozeduren
Print "Save "; End Sub
Ein kleiner Tipp: Der Editor der Entwicklungsumgebung zieht Sprungmarken automatisch an den Zeilenanfang. Daran müssten Sie bereits merken, dass etwas nicht stimmt. Am besten aber, Sie gewöhnen sich an, keine Anweisungen hinter Sprungmarken zu setzen, das erhöht auch die Lesbarkeit des Programms. Der Compiler von Visual Basic kann arithmetische Ausdrücke aus Optimierungsgründen intern umstellen. Vermeiden Sie daher Function-Aufrufe innerhalb eines arithmetischen Ausdrucks, wenn die Funktion als Seiteneffekt den Wert von Variablen ändert, die in diesem Ausdruck auftreten. Verwandte Themen
................................................... Verwa ndte Them en
Methoden (S. 199); Eigenschaften (S. 201); Ereignisroutinen (S. 204)
Parameterübergabe an Funktionen und Prozeduren [Geltungsbereich] [Static] Function FuncName ([ParamListe]) [As Typ] ... End Function [Geltungsbereich] [Static] Sub ProcName ([ParamListe]) ... End Sub Function IsMissing(ArgumentName) As Boolean Funktionen/Prozeduren lassen sich als parametergesteuerte Unterprogramme auffassen, die aus den Werten ihrer Parameter weitere Werte generieren und zudem gewisse Seiteneffekte haben. Die Sichtbarkeit des Bezeichners FuncName bzw. ProcName beschränkt sich auf den vereinbarten Geltungsbereich Geltungsbereich. Geltungsbereich = Public | Private | Friend Fehlt die Angabe eines Geltungsbereichs oder lautet der Spezifizierer Public, ist der Bezeichner auf Programmebene bekannt. Lautet der Spezifizierer dagegen Private, ist der Bezeichner nur auf Modulebene bekannt. In Klassenmodulen ist weiterhin die Angabe des Spezifizierers Friend möglich, der die Sichtbarkeit der Methode auf das aktuelle Projekt beschränkt und einen expliziten Aufruf durch Besitzer von Objekten dieser Klasse vereitelt. Die zusätzliche Angabe des Spezifizierers Static bewirkt, dass alle innerhalb der Funktion/Prozedur implizit oder explizit deklarierten Variablen generell als statische Variablen vereinbart werden, so dass diese von einem Aufruf zum nächsten ihren Wert behalten und nur beim ersten Aufruf automatisch initialisiert werden. Jeder Funktion/Prozedur ist eine Parameterliste ParamListe zugeordnet, die bis zu 59 Parametervereinbarungen umfassen, aber auch leer sein kann. ParamListe = [Param[, Param ...]] Die Vereinbarung eines einzelnen Parameters Param hat die Gestalt Param = [Optional][ByVal | ByRef][ParamArray] Var[()] As Typ [= Vorgabe] Ein Parameter kann als Platzhalter für einen Eingabewert, einen Ausgabewert oder einen Ein-/ Ausgabewert fungieren, der innerhalb der Funktion/Prozedur über die Argumentvariable Var verfügbar wird. Die Deklaration von Argumentvariablen für reine Eingabeparameter erfordert
1 78
Parameterübergabe an Funktionen und Prozeduren
Sub TestProc(Optional OptParam) If IsMissing(OptParam) Then ... End Sub
Für den letzten Parameter in einer Parameterliste ist darüber hinaus der Spezifizierer ParamArray erlaubt – unter der Voraussetzung jedoch, dass die Argumentvariable als dynamisches Array des Typs Variant ohne weitere Spezifizierer vereinbart wird. Eine Funktion/Prozedur, für die ein ParamArray-Parameter vereinbart ist, kann beliebig lange Parameterlisten verarbeiten (vgl. Print). Anwendung
................................................... Anwendung
Für die Reihenfolge der Parameter gibt es keine Vorschriften. Es ist aber üblich, Eingabeparameter an den Beginn der Parameterliste zu setzen, Ein-/Ausgabeparameter in der Mitte und die Ausgabeparameter an den Schluss. Allerdings hat die exakt umgekehrte Reihenfolge den Vorteil, dass sich optionale Parameter an den Schluss setzen lassen, damit sie nicht notiert werden müssen. Wenn Sie jedoch die Aufrufsyntax der »benannten Argumente« benutzen, spielt die Reihenfolge ohnehin keine Rolle. Der Unterschied zwischen reinen Eingabeparametern und Parametern mit Ein- und Ausgabefunktion bzw. reiner Ausgabefunktion ist konzeptueller Natur und erfordert aufseiten des Programmierers eine gewisse Disziplin. Da ByVal-Variablen beim Aufruf Kopien der übergebenen Werte erhalten, kann die Funktion/Prozedur damit anstellen, was sie will: Eventuelle Wertänderungen verbleiben auf der Prozedurebene und sind für den Aufrufer generell unsichtbar. Anders verhält es sich, wenn die Deklaration einer Argumentvariablen mit dem standardmäßigen Spezifizierer ByRef erfolgt. In diesem Fall wird aus der Argumentvariablen eine Zeigervariable, deren Wert die Adresse des in dem entsprechenden Parameter übergebenen Werts ist – ein Konzept, das als solches in Visual Basic nirgendwo sonst so offen zu Tage tritt. Am Gebrauch der Argumentvariablen ändert sich äußerlich dadurch nichts, außer dass sie als Platzhalter für einen Ausgabewert bzw. Ein-/Ausgabewert fungieren kann. Nach außen hin wirksame Wertänderungen sind sinnvollerweise nur möglich, wenn als Parameter eine Variable (also ein Linkswert) übergeben wurde, da diese das indirekte Ziel jeglicher Wertänderungen seitens der Argumentvariablen ist. Wird einem ByRef-Parameter ein literaler Wert, eine Konstante, ein Funktionswert oder der Wert eines berechneten Ausdrucks übergeben, steht dieser nur als Eingabewert zur Verfügung; der Versuch einer Änderung führt zwar zu keinem Laufzeitfehler, bleibt aber wirkungslos.
1 79
Parameterübergabe an Funktionen und Prozeduren
den Spezifizierer ByVal, die von Argumentvariablen für Ausgabe- bzw. Ein-/Ausgabeparameter den Spezifizierer ByRef. Letzterer ist optional, da Visual Basic Argumentvariablen auch implizit mit dem Zusatz ByRef vereinbart. Arrays und Objekte lassen sich ausschließlich als ByRef-Parameter übergeben. Parameter sind im Allgemeinen obligatorisch, es sei denn, die zugehörige Argumentvariable wird mit dem Zusatz Optional vereinbart. Ein obligatorischer Parameter muss beim Aufruf der Funktion/Prozedur mit einem Wert versorgt werden, ein optionaler kann auch weggelassen werden. Als Vorgabewert für optionale Parameter nimmt Visual Basic den standardmäßigen Initialisierungswert des Datentyps Typ, sofern keine explizite Zuweisung eines Vorgabewerts Vorgabe deklariert ist. Um innerhalb der Funktion/Prozedur festzustellen, ob für einen optionalen Variant-Parameter beim Aufruf ein Wert spezifiziert wurde, lässt sich die Funktion IsMissing einsetzen. Sie ist vom Typ Boolean und liefert den Wert True, wenn der im Argument genannte optionale Parameter beim Aufruf weggelassen wurde.
Parameterübergabe an Funktionen und Prozeduren
Warnung
................................................... Wa rnung
Nachdem Visual Basic alle Parameter, die nicht explizit mit ByVal spezifiziert werden, als ByRefParameter vereinbart, sind bei sorglosem Umgang mit Argumentvariablen für Eingabewerte unerwünschte Rückwirkungen auf die Ebene des Aufrufers möglich – ein Eingabewert kann so unbemerkt zum Ein-/Ausgabewert mutieren. Fehler dieser Art sind meist schwer zu finden. Um sie auszuschließen, besteht auch die Möglichkeit, Variablen, die nur Eingabewerte bereitstellen, in der Werteliste zu klammern. Visual Basic sieht einen geklammerten Wert als Ausdruck an und verhindert so Rückwirkungen auf die Variable.
Parameterübergabe an Funktionen und Prozeduren
Beispiele
................................................... Beis piele
Die Funktion Standardabweichung berechnet die Standardabweichung einer Messreihe mit der Menge der Messwerte als Grundgesamtheit. Die Anzahl der Parameter für den Aufruf der Funktion ist nicht festgelegt, wie die Aufrufe zeigen. ... Print Standardabweichung(15.2, 11.1, 13.4,12.2, 18.3) ' 2,517... Print Standardabweichung(15.2, 11.1, 13.4,12.2, 18.3, 12.1) ' 2,417... ... Function Standardabweichung(ParamArray MessWerte()) Dim iAnz As Integer iAnz = UBound(MessWerte) + 1 For Each i In MessWerte Sum = Sum + i Qsum = Qsum + i * i Next i Standardabweichung = Sqr((iAnz * Qsum – Sum * Sum) / (iAnz * iAnz)) End Function
Die folgende Prozedur demonstriert den Unterschied zwischen einem ByRef- und einem ByValParameter sowie die Wirkung eines Ausdrucks auf einen ByRef-Parameter: ... Dim iWert1, iWert2 As Integer iWert1 = 10 iWert2 = 10 ByValByRef iWert1, iWert2 Print iWert1, iWert2 iWert2 = 10 ByValByRef iWert1, (iWert2) Print iWert1, iWert2 ... Sub ByValByRef(ByVal a As Integer, b a = a * a b = b * b End Sub Verwandte Themen
................................................... Verwandte Them en
Ereignisroutinen (S. 204)
1 80
' Ausgabe: 10
100
' (iWert2) ist Ausdruck! ' Ausgabe: 10 10 As Integer)
Funktionen selbst definieren
Funktionen selbst definieren [Public| Private | Friend][Static] Function Name [(ParamListe)][ As Typ] [Anweisungsfolge] [Name = Ausdruck] [Exit Function] [Anweisungsfolge] [Name = Ausdruck] End Function Beschreibung
Die Definition einer eigenen Funktion (Methode) gliedert sich in drei Schritte: 1. Deklaration der prozeduralen Schnittstelle 2. Deklaration eines Rückgabetyps 3. Definition des Funktionskörpers mit Zuweisung des Funktionswerts Die prozedurale Schnittstelle einer Funktion umfasst die Spezifikation des Funktionsbezeichners sowie die Deklaration der Parameterliste. Dieser Schritt ist im Abschnitt »Parameterübergabe an Funktionen und Prozeduren« (S. 178) ausführlich beschrieben. Da eine Funktion im Gegensatz zu einer Prozedur einen Wert verkörpert, den sie als Funktionswert zurückliefert, definiert der Rückgabetyp Typ den Datentyp des Werts, den die Funktion zurückgibt. Fehlt die Angabe eines Rückgabetyps, lautet die Voreinstellung Variant. Zu den möglichen Rückgabetypen zählen die elementaren Datentypen Boolean, Byte, Integer, Long, Currency, Single, Double, Date, String (Decimal wird derzeit nur als Untertyp von Variant unterstützt), benutzerdefinierte Datentypen, Variant sowie Objektverweise des Typs Object. Darüber hinaus kann eine Funktion auch ein dynamisches Array mit einem der genannten Datentypen als Elementtyp zurückgeben. Der Funktionskörper kann eine beliebige Anweisungsfolge enthalten, die für die Berechnung des Funktionswerts sowie der gegebenenfalls über Argumentvariablen zurückzugebenden Ausgabewerte erforderlich ist. Der Funktionswert Ausdruck wird dem Funktionsbezeichner Name zugewiesen und die anderen Ausgabewerte den entsprechenden Argumentvariablen. Die an beliebiger Stelle erlaubte Anweisung Exit Function gibt die Kontrolle unmittelbar an den Aufrufer zurück. Falls dem Funktionsbezeichner bis dahin noch kein Rückgabewert zugewiesen wurde, liefert die Funktion den standardmäßigen Initialisierungswert des Rückgabetyps. Anwendung
................................................... Anwendung
Der Unterschied zwischen einem Ausdruck und einer Funktion ist gar nicht so groß. Beide liefern einen Wert, den sie auf Basis anderer Werte berechnen. Im Gegensatz zu einem Ausdruck legt eine Funktion im Allgemeinen aber ihre Schnittstelle mit der Außenwelt en bloc in Form der prozeduralen Schnittstelle offen – so zumindest der hehre Anspruch. In der Praxis sieht es oft anders aus: Um Aufrufparameter zu sparen, wird viel mit globalen Variablen hantiert, was zwar das Laufzeitverhalten der Funktion verbessern kann, jedoch gegen die goldenen Regeln für die strukturierte Programmierung verstößt und nicht selten zu schwer auffindbaren Bugs im Code führt. Gewöhnen Sie sich an, nach Möglichkeit alle Werte, die eine Funktion vom Aufrufer benötigt, über die prozedurale Schnittstelle zugänglich zu machen und gegebenenfalls als optionalen Parameter mit sinnvollem Vorgabewert zu deklarieren. Der Gebrauch von globalen Variablen sollte weitgehend auf das Wesentliche beschränkt bleiben. ByVal-Parameter schützen zwar vor ungewollten Seiteneffekten, kosten aber aufgrund der zu erstellenden Kopien bei Werten mit umfangreichen Repräsentationen – etwa bei Zeichenfolgen oder benutzerdefinierten Datentypen – wertvolle Laufzeit. ByRef-Parameter sind dann oft die bessere Wahl.
1 81
Funktionen selbst definieren
................................................... Bes c hreibung
Funktionen selbst definieren
Funktionen selbst definieren
Die größte Kunst bei der Programmierung besteht darin, es einer potenziellen Anweisungsfolge anzusehen, dass sie eine gute Funktion abgibt, und dann die Schnittstelle dieser Funktion geeignet festzulegen. Das Motto dafür lautet, wie schon einst bei Cäsar: Teile und herrsche. Je allgemeiner und je kürzer eine Funktion, desto besser. Das erleichtert die Fehlersuche ebenso wie die spätere Codepflege. Zu guter Letzt noch ein Wort zum Gebrauch der Anweisung Exit Function. Eine weitere goldene Regel der strukturierten Programmierung besagt, dass Routinen möglichst nur einen Ausgang haben sollten. Gegen den überlegten Gebrauch von Exit Function ist natürlich nichts einzuwenden. Je komplizierter die Logik jedoch wird, desto unübersichtlicher und schwerer lesbar wird eine Funktion, wenn sie zu viele Ausgänge hat. Am besten, Sie spalten den Code dann in mehrere kleine Funktionen auf. Tipp
................................................... Tipp
Bei Problemstellungen, in denen die Laufzeit eine Rolle spielt, können Funktionsaufrufe zum Hemmschuh werden, insbesondere wenn noch ByVal-Parameter im Spiel sind. In einigen Fällen verbessert sich das Laufzeitverhalten, wenn man zu Inline-Formulierungen übergeht, also anstelle eines Funktionsaufrufs den eigentlichen Code der Funktion setzt. Allerdings ist auch der Visual-Basic-Compiler in der Lage, von sich aus Funktionen in Inline-Code zu verwandeln, sofern der Compilerschalter CODE-AUSFÜHRUNGSGESCHWINDIGKEIT OPTIMIEREN gesetzt ist, so dass sich Vergleichsmessungen lohnen. Beispiel
................................................... Beis piel
Die rekursive Funktion NextPrim ermittelt die nächste Primzahl, die kleiner oder gleich dem Eingabeparameter lZahl ist. Für negative Werte und 0 liefert die Funktion den Wert 0. Function NextPrim(ByVal lZahl As Long) Dim i As Long If lZahl <= 3 Then If lZahl <= 0 Then NextPrim = 0 Else NextPrim = lZahl End If Else For i = 2 To Sqr(lZahl) If lZahl Mod i = 0 Then NextPrim = NextPrim(lZahl – 1) Exit Function End If Next NextPrim = lZahl End If End Function Verwandte Befehle
................................................... Verwa ndte Befehle
Sub Verwandte Themen
................................................... Verwandte Them en
Ereignisroutinen (S. 230)
1 82
As Long
' ' ' '
Alle möglichen Teiler prüfen Ist i Teiler? Ja: Rekursion liefert Ergebnis Das war's schon
' Kein Teiler, lZahl ist Primzahl
Prozeduren selbst definieren
Prozeduren selbst definieren [Private | Public][Static] Sub Name [(ParamListe)] [Anweisungsfolge] [Exit Sub] [Anweisungsfolge] End Sub Beschreibung
................................................... Bes c hreibung
Die Definition einer eigenen Prozedur (Methode) gliedert sich in zwei Schritte:
Die prozedurale Schnittstelle einer Prozedur umfasst die Spezifikation des Prozedurbezeichners sowie die Deklaration der Parameterliste. Dieser Schritt ist im Abschnitt »Parameterübergabe an Funktionen und Prozeduren« (S. 178) ausführlich beschrieben. Der Prozedurkörper kann eine beliebige Anweisungsfolge enthalten, die für die Berechnung der gegebenenfalls über Argumentvariablen zurückzugebenden Ausgabewerte erforderlich ist. Ausgabewerte werden einfach den entsprechenden Argumentvariablen zugewiesen. Die an beliebiger Stelle erlaubte Anweisung Exit Sub unterbricht die Ausführung der Prozedur und gibt die Kontrolle unmittelbar an den Aufrufer zurück. Anwendung
................................................... Anwendung
Der gesamte Code eines Visual-Basic-Programms liegt in Form von Prozeduren und Funktionen vor, die sich teils gegenseitig aufrufen, teils vom Laufzeitsystem aufgrund entsprechender Benutzerinteraktionen im Zuge der Ereignisbehandlung (Ereignisprozeduren) aufgerufen werden. Während eine Funktion einen Rückgabewert liefern muss und somit als Wert zu betrachten ist, erweitert eine Prozedur das Spektrum der in Visual Basic verfügbaren Anweisungen. Da Prozeduren aber genau wie Funktionen über Ein-/Ausgabeparameter mit ihren Aufrufern kommunizieren, ist der Unterschied zwischen Funktionen und Prozeduren mehr eine Frage der Formulierung einer Lösung und damit des persönlichen Stils als des Einsatzzwecks, denn jede Funktion lässt sich letztlich auch als Prozedur schreiben und umgekehrt. Eine Prozedur legt genauso wie eine Funktion ihre Schnittstelle mit der Außenwelt en bloc in Form der prozeduralen Schnittstelle offen. Gewöhnen Sie sich an, nach Möglichkeit alle Werte, die eine Prozedur vom Aufrufer benötigt, über die prozedurale Schnittstelle zugänglich zu machen und gegebenenfalls als optionalen Parameter mit sinnvollem Vorgabewert zu deklarieren. Das vereinfacht die Handhabung der Prozedur. Der Gebrauch von globalen Variablen sollte weitgehend auf das Wesentliche beschränkt bleiben. ByVal-Parameter schützen zwar vor ungewollten Seiteneffekten, kosten aber aufgrund der zu erstellenden Kopien bei Werten mit umfangreichen Repräsentationen – etwa bei Zeichenfolgen oder benutzerdefinierten Datentypen – wertvolle Laufzeit. ByRef-Parameter sind dann meist die bessere Wahl.
Sub Main In einem Standardmodul kommt der Prozedur mit dem Bezeichner Main eine spezielle Bedeutung zu: Sie lässt sich im Dialog PROJEKTEIGENSCHAFTEN eines Projekts anstelle eines Formularobjekts als Startroutine einer Komponente (lies: ausführbare Datei oder Bibliothek) festlegen. Lassen Sie sich dabei nicht durch die unglücklich gewählte deutsche Beschriftung »Startobjekt« verwirren. Startobjekt ist in dem Fall das Standardmodul, das die Prozedur Main bereitstellt. Enthält ein Projekt mehrere Standardmodule, darf jeweils nur in einem die Prozedur Main definiert sein.
1 83
Prozeduren selbst definieren
1. Deklaration der prozeduralen Schnittstelle 2. Definition des Prozedurkörpers
Prozeduren selbst definieren
Prozeduren selbst definieren
Sub Main eines Standardmoduls als Startroutine festlegen Das Laufzeitsystem von Visual Basic führt Main in diesem Fall bei jedem Start der Komponente als Startcode aus. Main ist dann gegebenenfalls für den Aufruf des Startformulars verantwortlich, wenn die Komponente im Stand-alone-Modus und nicht als Automatisierungsobjekt ausgeführt wird. Sub Main() ... If App.StartMode = vbSModeStandalone Then StartForm.Show ... Beispiel
................................................... Beis piel
Das folgende Beispiel ist eine recht nützliche Sortierroutine für Arrays mit beliebigem Elementtyp. Sie arbeitet nach dem Bubble-Sort-Algorithmus. Die Routine selbst ist eine Prozedur, bedient sich aber einer Funktion für den Vergleich und einer Prozedur für das Vertauschen von Werten. Obwohl die Routine bereits recht allgemein formuliert ist – sie funktioniert mit allen elementaren Datentypen – sind noch Anpassungen an andere Datentypen denkbar. Haben Sie beispielsweise schon einmal Bilder nach Farben sortiert? ... Dim b() b() = Array("Essig", "Fliege", "Apfel", "Gold") BubbleSort b() For Each i In b Print i; " "; ' Ausgabe: Apfel Essig Fliege Gold Next Print b() = Array(23.3, 45.2, 15.3, 893.2, 123, 12.123) BubbleSort b() For Each i In b Print i; " "; ' Ausgabe: 893,2 123 45,2 15,3 12,123 Next ...
1 84
Routinen aus DLLs und der Windows- API einsetzen
Sub BubbleSort(Werte()) ' ByRef! For j = UBound(Werte) To 1 Step -1 ' rückwärts durch Array For i = 0 To j – 1 ' vorwärts durch unsort. Teil If Vgl(Werte(i), Werte(i + 1)) Then Vertausche Werte(i), Werte(i + 1) End If Next i Next j End Sub
Sub Vertausche(a, b) c = a: a = b: b = c End Sub
Prozeduren selbst definieren
Function Vgl(a, b) As Boolean ' Select Case VarType(a) ' Case vbString If StrComp(a, b, vbTextCompare) Case vbInteger To vbBoolean If a < b Then Vgl = True ' End Select End Function
Vergleichsoperation ByRef-Parameter Abhängig von Untertyp = 1 Then Vgl = True Vergleich könnte auch anders lauten
' ByRef-Parameter!
Verwandte Befehle
................................................... Verwa ndte Befehle
Function Verwandte Themen
................................................... Verwandte Them en
Ereignisroutinen (S. 204)
Routinen aus DLLs und der Windows- API einsetzen [Public | Private] Declare Function APIFkt Lib "API" _ [Alias "APIRoutine"][(ParamListe)] As Typ [Public | Private] Declare Sub APIProc Lib "API" [Alias "APIRoutine"][(ParamListe)] Beschreibung
................................................... Bes c hreibung
Bei der Windows-API handelt es sich um die Menge der Betriebssystemroutinen, die Windows für die Anwendungsprogrammierung zur Verfügung stellt. Obwohl Visual Basic an sich eine geschlossene Programmierwelt mit integrierter Automatisierungsschnittstelle zu ActiveXSteuerelementen und ActiveX-Komponenten ist, stellt die Sprache aber auch Mittel und Wege bereit, in gewöhnlichen DLLs beheimatete Routinen anzusprechen, die nicht in Visual Basic programmiert sind. Dazu zählen nicht nur die gut tausend Funktionen und Prozeduren der Win32-API, von denen nur wenige in der standardmäßigen Bibliothek von Visual Basic eine Entsprechung gefunden haben, sondern auch die gesamte Vielfalt der Bibliotheksprodukte zahlloser Drittanbieter, die sich auf dem Windows-Markt tummeln. Sich bei der Visual-Basic-Programmierung auf bereits bestehende DLL-Routinen zu stützen, hat nicht nur Vorteile, sondern auch ganz klare Nachteile: Aufrufe von DLL-Routinen bzw. der Einsatz von API-Funktionen führen aus der Geborgenheit der Visual-Basic-Umgebung heraus.
1 85
Prozeduren selbst definieren
Prozeduren selbst definieren
Die Umsicht des Compilers endet nämlich mit den Schnittstellendeklarationen für die externen Routinen. Da die Routinen in einer anderen Programmiersprache geschrieben sind – für die Windows-API ist das C/C++ –, gilt es, sich an die Regeln dieser Sprache zu halten. Es ist zwar nicht unbedingt erforderlich, diese Sprache selbst zu können, vorteilhaft ist es aber allemal. Um ein gewisses Verständnis der Sprache und der von ihr verwendeten Repräsentationen für die elementaren Datentypen kommt man daher nicht herum, damit die Übergabe von Parametern und Funktionswerten auch klappt. Darüber hinaus ist natürlich auch ein Verständnis der verwendeten Bibliothek in ihrem Funktionszusammenhang erforderlich. Auf die Windows-API bezogen bedeutet das, dass man zunächst einmal wissen muss, welche Funktionen es gibt, in welcher Bibliothek diese zu finden sind (dafür gibt es das Add-In API-VIEWER), mit welchen Konzepten und Datentypen diese Funktionen arbeiten, welche Seiteneffekte sie haben und welche Pflichten mit ihrer Benutzung verbunden sind. Letzteres ist ein recht heikles Thema, da das Laufzeitsystem von Visual Basic sämtliche Verwaltungsarbeiten im Zusammenhang mit komplexen Datentypen, ins Leben gerufenen Objekten, geöffneten Dateien usw. von sich aus übernimmt und notfalls auch einmal hinterher räumt, wenn etwa eine Datei nicht geschlossen oder gesperrte Datensätze nicht wieder entsperrt wurden. Die Windows-API weist da bei weitem keine solche Toleranz auf und fordert einen wahrhaft peniblen Umgang mit den angeforderten Ressourcen, seien es Handles, Laufzeitinstanzen oder sonstige Größen, die aufseiten des Betriebssystems eine Speicherbelegung bewirken. DLLs werden im selben Prozess wie die Visual-Basic-Anwendung ausgeführt. Fehler durch Angabe falscher Parameterwerte können sich daher katastrophal auswirken und nicht nur das Programm zum Absturz bringen, sondern zuweilen auch Windows. Leider kann der VisualBasic-Compiler im Zusammenhang mit DLL-Routinen nur eine sehr unvollständige Typüberprüfung durchführen, die über das Grobformale nicht hinausgeht. Obwohl die Abbildung der elementaren Datentypen zwischen C/C++ und Visual Basic noch halbwegs geradlinig ist, treten bereits die ersten Komplikationen auf, wenn Zeichenfolgen ins Spiel kommen. Da stellt sich die Frage, ob Unicode oder ANSI-Code und ob ein C-String mit Nullbyte am Ende oder ein PascalString mit Deskriptor erwartet wird. Richtig schwierig kann es bei den benutzerdefinierten Datentypen werden, wenn nicht klar ist, mit welcher Speicherausrichtung (8, 16 oder 32 Bit) eine DLL in Bezug auf die zusammengesetzten Datentypen, die sie verwendet, kompiliert worden ist. In anderer Hinsicht problematisch ist der Umgang mit Zeigern, einem Konzept, das es in Visual Basic offiziell gar nicht gibt und das gewissermaßen nur über die Hintertür in Form von ByRef-Parametern bei Funktionen/Prozeduren zugänglich wird. Ein noch offensichtlicheres Hintertürchen ist der AddressOf-Operator, mit dessen Hilfe sich schließlich sogar Funktionszeiger auf Visual-Basic-Routinen gewinnen lassen, wenn Rückrufroutinen gefordert sind. Um eine DLL-Routine in einem Modul ansprechen zu können, muss diese im Bereich ALLGEMEIN über eine geeignete Declare-Anweisung als öffentliche (Public) oder moduleigene (Private) Funktion (Function) oder Prozedur (Sub) deklariert sein (öffentliche Vereinbarungen importierter Routinen sind in Visual Basic 6.0 nur in Standardmodulen erlaubt). Der Bezeichner APIFkt bzw. APIProc ermöglicht es, die importierte Routine innerhalb des vereinbarten Geltungsbereichs wie eine ganz gewöhnliche Funktion oder Prozedur aufzurufen. Falls dieser Bezeichner mit dem Bezeichner APIRoutine der Routine übereinstimmt, kann der optionale Alias-Zusatz entfallen, ansonsten nicht. Ein Alias-Zusatz ermöglicht es insbesondere, auch Routinen einzuführen, deren Bezeichner in Visual Basic keine gültigen Bezeichner wären, etwa weil sie mit einem Unterstrich beginnen. Der obligatorische Lib-Zusatz macht den Namen der DLL bekannt, in der die Routine enthalten ist. Er muss als literale Zeichenfolge notiert sein. Die optionale Parameterliste ParamListe und der bei Funktionsdeklarationen zusätzlich anzugebende Rückgabetyp Typ unterliegen den gleichen Anforderungen wie bei der Definition einer gewöhnlichen Funktion/Prozedur (vgl. »Parameterübergabe an Funktionen und Prozeduren«,
1 86
Routinen aus DLLs und der Windows- API einsetzen
S. 178). Für einen ordnungsgemäßen Aufruf der Bibliotheksroutine ist es aber unerlässlich, dass Sie die Parameter- und Typanforderungen der Routine mit den sprachlichen Mitteln von Visual Basic exakt abbilden. Anwendung
................................................... Anwendung
Win32- API Für die Windows-API liegt mit der zum Lieferumfang von Visual Basic gehörenden Datei Win32api.txt eine Deklarationsdatei vor, die alle für die Programmierung mit der Win32-API erforderlichen Konstantendefinitionen, Funktionsdeklarationen und Typvereinbarungen bereitstellt. Öffnet man diese Datei im API-Viewer, nachdem dieser über den Add-In-Manager des Menüs ADD-INS der Visual-Basic-Entwicklungsumgebung geladen wurde, wird die Handhabung von API-Funktionen aus Visual Basic heraus zu einer rein formalen Angelegenheit: Man sucht sich die benötigten Definitionen heraus, stellt eine Auswahl zusammen und kopiert diese über die Zwischenablage in den Bereich ALLGEMEIN des betroffenen Moduls. (Beachten Sie, dass für bestimmte API-Funktionen auch Typvereinbarungen erforderlich sein können, um den einen oder anderen Parameter mit einem Wert zu versorgen.)
Der API- Viewer liefert die Deklarationen für drei API- Funktionen und eine Typvereinbarung
1 87
Prozeduren selbst definieren
Eines vorweg: Der Einsatz von API-Routinen wird nur Programmierern empfohlen, die bereits eine gewisse Vorstellung davon haben, was einen bei der Programmierung mit der Win32-API erwartet. Vom Prinzip her ist es ist zwar denkbar, sich der Win32-API von Visual Basic aus zu nähern, für die ernsthafte Programmierung ist der Weg aber zu steinig. Wer sich dagegen einen Vorgeschmack holen oder die Win32-API zur Lösung spezifischer Probleme in Anspruch nehmen will, der kann dies von Visual Basic aus tun und muss nicht gleich die Programmiersprache wechseln.
Prozeduren selbst definieren
Prozeduren selbst definieren
Worauf Sie beim Aufruf einer so deklarierten Routine wirklich peinlich achten sollten: Setzen Sie nicht auf implizite Typumwandlungen, sondern bedienen Sie jeden Parameter mit dem »richtigen« Datentyp. In besonderen Fällen, wenn eine API-Routine unterschiedliche Datentypen für denselben Parameter akzeptiert und Ihr Programm die Routine mit mehr als einem Parametertyp aufruft, bleibt der »Datentyp« Any als einziger Ausweg, der Typüberprüfung von Visual Basic zu entrinnen. Beachten Sie jedoch, dass Sie dann für die Einhaltung des Datentyps selbst verantwortlich sind. Die Deklarationen einiger Routinen in Win32api.txt enthalten As Any-Vereinbarungen. Sollten Sie die entsprechenden Routinen nur mit einem bestimmten Parametertyp aufrufen, können Sie die Deklaration auf diesen Typ zuschneiden, um von der Typüberprüfung des Compilers zu profitieren. Vielfach ist es auch sinnvoll, eine Routine mit einem As Any-Parameter für jeden der benötigten Parametertypen spezifisch zu deklarieren – jeweils unter Verwendung eines anderen Bezeichners. Die folgende Tabelle gibt einen Überblick darüber, wie verschiedene von der Win32-API verwendete C/C++-Datentypen in Visual Basic deklariert sind und welche Art von Wert für den Aufruf erforderlich ist. API-Datentyp
Visual-Basic-Deklaration
Parameter verlangt ...
ATOM
ByVal Var As Integer
Wert, Variable oder Ausdruck mit Datentyp Integer
BOOL, BOOLEAN
ByVal Var As Long
Wert, Variable oder Ausdruck mit Datentyp Long
BYTE
ByVal Var As Byte
Wert, Variable oder Ausdruck mit Datentyp Byte
CHAR
ByVal Var As Byte
Wert, Variable oder Ausdruck mit Datentyp Byte
COLORREF
ByVal Var As Long
Wert, Variable oder Ausdruck mit Datentyp Long
DWORD
ByVal Var As Long
Wert, Variable oder Ausdruck mit Datentyp Long
HWND, HDC, HMENU
ByVal Var As Long
Wert, Variable oder Ausdruck mit Datentyp Long (von Windows vergebener Handle für Fenster, Gerätekontext, Menü)
INT, UINT
ByVal Var As Long
Wert, Variable oder Ausdruck mit Datentyp Long
LONG
ByVal Var As Long
Wert, Variable oder Ausdruck mit Datentyp Long
LPARAM
ByVal Var As Long
Wert, Variable oder Ausdruck mit Datentyp Long
LPDWORD
[ByRef] Var As Long
Variable vom Datentyp Long
LPINT, LPUINT
[ByRef] Var As Long
Variable vom Datentyp Long
LPRECT
[ByRef] Var As bdtTyp
Variable vom Datentyp bdtTyp (benutzerdefinierter Typ)
LPSTR, LPCSTR
ByVal Var As String
Wert, Variable oder Ausdruck mit Datentyp String
LPVOID
[ByRef] Var As Any
Variable des entsprechenden Typs (bei Übergabe einer Variablen vom Typ String ist dem Wert das Schlüsselwort ByVal voranzustellen)
LPWORD
[ByRef] Var As Integer
Variable mit Datentyp Integer
LRESULT
ByVal Var As Long
Wert, Variable oder Ausdruck mit Datentyp Long
Deklaration grundlegender Datentypen der Win32- API in Visual Basic
1 88
Routinen aus DLLs und der Windows- API einsetzen
Visual-Basic-Deklaration
Parameter verlangt ...
NULL
As Any oder ByVal Var As Long
ByVal Nothing oder ByVal 0& oder vbNullString
SHORT
ByVal Var As Integer
Wert, Variable oder Ausdruck mit Datentyp Integer
VOID
–
(Rückgabetyp einer Prozedur)
WORD
ByVal Var As Integer
Wert, Variable oder Ausdruck mit Datentyp Integer
WPARAM
ByVal Var As Long
Wert, Variable oder Ausdruck mit Datentyp Long
Deklaration grundlegender Datentypen der Win32- API in Visual Basic
Zeichenfolgen und ihre Besonderheiten Bei Durchsicht der in der obigen Tabelle aufgelisteten Datentypen wird Ihnen vielleicht aufgefallen sein, dass Zeichenfolgen überraschenderweise eine ByVal-Deklaration erfordern. Das ist kein Druckfehler, sondern eine gewisse Ungereimtheit, die Visual Basic im Umgang mit Zeichenfolgen anhaftet – die berühmte Ausnahme von der Regel also. Während der ByVal-Zeichenfolgenparameter lpBuffer nach allen Regeln der Kunst in dem Szenario ... a = "ByVal!" TestByVal a Print a ' Ausgabe: "ByVal!" ... Sub TestByVal(ByVal lpBuffer As String) lpBuffer = "ByRef?" End Sub
nur als Eingabeparameter taugt, behandelt Visual Basic den gleichen Parameter als Ein-/Ausgabewert, wenn eine Declare-Vereinbarung vorliegt: Private Declare Function GetUserName Lib "advapi32.dll" Alias _ "GetUserNameA" (ByVal lpBuffer As String, nSize As Long) As Long ... Dim sUN As String, lLen As Long sUN = Space(20) ' String der Länge 20 generieren lLen = Len(sUN) ' Stringlänge in Variable If GetUserName(sUN, lLen) Then ' Benutzername (hier: Rudi) Print lLen, Len(sUN), sUN ' Ausgabe: 20 5 "Rudi|" sUN = Left(sUN, lLen – 1) ' Länge anpassen, Nullbyte abschneiden ...
Dieser Code zeigt bereits recht eindrucksvoll, mit welchen Besonderheiten man bei der Verwendung von Routinen der Win32-API zu rechnen hat, wenn Zeichenfolgen mit im Spiel sind. Da Visual Basic für Zeichenfolgen intern den für die Automatisierung erforderlichen C/C++-Datentyp BSTR verwendet, treffen hier zwei recht unterschiedliche Repräsentationen aufeinander. Die auf das gewöhnliche Zeichenfolgenformat der Sprache C/C++ eingestellte Routine GetUserName erwartet die Adresse eines Puffers, in den sie den Namen des Computers (im ANSI-Format) ohne Deskriptor und mit anhängendem Nullbyte schreiben kann. Die Länge des bereitgestellten Puffers erfährt sie über den Parameter nSize – er nimmt die Funktion des Längendeskriptors ein. Ist der Puffer zu klein dimensioniert, um den Namen des Computers aufnehmen zu können,
1 89
Prozeduren selbst definieren
API-Datentyp
Prozeduren selbst definieren
liefert die Funktion das Ergebnis 0 und gibt die benötigte Pufferlänge über den Ein-/Ausgabeparameter nSize zurück. Bei ausreichender Pufferlänge überträgt die Routine den Namen des Computers in den Puffer, setzt nSize auf die Länge der geschriebenen Zeichenfolge unter Berücksichtigung des angehängten Nullbytes und gibt den Funktionswert 1 zurück. Das von Visual Basic verwendete Zeichenfolgenformat BSTR setzt sich aus einem vier Bytes umfassenden Deskriptor, der die Länge der Zeichenfolge beschreibt, und dem Puffer für die Zeichenfolge (Unicode) zusammen. Ein Nullbyte wird weder gesetzt noch interpretiert. Damit dürfte klar sein, warum als Vorbereitung für den Aufruf der Routine die Zeile
Prozeduren selbst definieren
sUserName = Space(20)
' String der Länge 20 generieren
erforderlich ist. Sie sorgt dafür, dass der Zeichenpuffer Platz für 20 Zeichen bietet. Als zweite Voraussetzung für den erfolgreichen Aufruf der Routine muss diese die Adresse des Zeichenfolgenpuffers erfahren. Da der Zeichenfolgenpuffer in Visual Basic keine eigenständige Größe darstellt, mussten die Entwickler hier tricksen: Sie haben den Compiler so ausgelegt, dass er den ByVal-Parameter des Typs String in der Parameterliste einer via Declare importierten Routine nicht als Kopie übergibt, sondern statt dessen die Adresse eines Zeichenfolgenpuffers mit einer in ANSI gewandelten Kopie der Zeichenfolge bereitstellt (API-Routinen mit angehängtem »A« im Bezeichner arbeiten mit ANSI-Code, solche mit angehängtem »W« mit Unicode). So weit, so gut. Die Routine kann so zwar in den Puffer der übergebenen Zeichenfolge schreiben, eine Längenanpassung ist ihr aber nicht möglich, weil das von ihr angehängte Nullbyte aufseiten von Visual Basic nicht interpretiert wird. Visual Basic wandelt somit die gesamte Zeichenfolge in ihrer ursprünglichen Länge sofort nach dem Aufruf wieder in Unicode um. Mithin ist die Länge der Zeichenfolge nach dem Aufruf der API-Routine unverändert und muss explizit angepasst werden: a = Left(a, lLen – 1)
' Länge anpassen und Nullbyte abschneiden
DLL- Routinen im Allgemeinen Zu echten Hürden wachsen sich die in der Beschreibung geschilderten Problemfelder eigentlich nur im Zusammenhang mit DLLs aus, die der Hersteller nicht eigens mittels einer Deklarationsdatei für die Benutzung mit dem Add-In API-VIEWER der Entwicklungsumgebung auf die Zusammenarbeit mit Visual Basic vorbereitet hat. Einem geübten und in mehreren Sprachen versierten Programmierer wird es bei Kenntnis einer Bibliothek zwar nicht sehr schwer fallen, sich die entsprechenden Informationen selbst zusammenzustellen, der Arbeitsaufwand ist aber nicht zu unterschätzen. Während Visual Basic für die zum Kern von Windows zählenden Bibliotheken User32, Kernel32 und GDI32 weder eine Dateierweiterung noch einen Pfad erwartet, muss der LibAbschnitt in der Declare-Anweisung bei anderen DLLs zumindest eine Dateierweiterung enthalten und in besonderen Fällen auch einen Pfad. Beim dynamischen Binden von DLLs durchsucht Windows der Reihe nach: das Verzeichnis der Exe-Datei, die die Dienste der DLL anfordert, das standardmäßige Arbeitsverzeichnis, das Systemverzeichnis von Windows (meist: C:\Windows\System\) das Windows-Verzeichnis (meist: C:\Windows) und schließlich die in der DOSUmgebungsvariablen PATH spezifizierten Verzeichnisse. Sollte die DLL in keinem der genannten Verzeichnisse zu finden sein, ist der Zugriffspfad explizit zu nennen. Eine gute Orientierungshilfe für eigene Deklarationen bietet die zum Lieferumfang von Visual Basic gehörende Deklarationsbibliothek Win32api.txt. Laden Sie die Datei am besten in einen Texteditor und vergleichen Sie die darin zu findenden Visual-Basic-Deklarationen mit den originalen C/C++-Deklarationen der Win32-API. Die folgende Tabelle zeigt, wie die Deklaration grundlegender C/C++-Datentypen in Visual Basic aussieht und welcher Wert dann jeweils erwartet wird.
1 90
Routinen aus DLLs und der Windows- API einsetzen
Visual-Basic-Deklaration
Parameter verlangt ...
bdt*
[ByRef] Var As bdt
Variable mit benutzerdefiniertem Datentyp bdt
char
ByVal Var As Byte
Wert, Variable oder Ausdruck mit Datentyp Byte
char*, char[]
[ByRef] Var As Byte
Variable vom Datentyp Byte oder erstes Element von Byte-Array
double
ByVal Var As Double
Wert, Variable oder Ausdruck mit Datentyp Double
double*, double[] [ByRef] Var As Double
Variable mit Datentyp Double oder erstes Element von Double-Array
float
ByVal Var As Single
Wert, Variable oder Ausdruck mit Datentyp Single
float*, float[]
[ByRef] Var As Single
Variable mit Datentyp Single oder erstes Element von Single-Array
hyper
ByVal Var As bdt64
Wert, Variable oder Ausdruck mit benutzerdefiniertem 64-Bit-Datentyp
hyper*
[ByRef] Var As bdt64
Variable mit benutzerdefiniertem 64-BitDatentyp
int*, int[]
[ByRef] Var As Long
Variable mit Datentyp Long oder erstes Element von Long-Array
long*, long[]
[ByRef] Var As Long
Variable mit Datentyp Long oder erstes Element von Long-Array
int, long
ByVal Var As Long
Wert, Variable oder Ausdruck mit Datentyp Long
short
ByVal Var As Integer
Wert, Variable oder Ausdruck mit Datentyp Integer
short*, short[]
[ByRef] Var As Integer
Variable mit Datentyp Integer oder erstes Element von Integer-Array
void*
[ByRef] Var As Any
Variable mit Datentyp Long (wird als Adresse interpretiert)
wchar_t
ByVal Var As Integer
Wert, Variable oder Ausdruck mit Datentyp Integer Variable mit Datentyp Integer
wchar_t* wchar_t* wchar_t[]
ByVal Var As String, [ByRef] Var As Integer
Wert, Variable oder Ausdruck mit Datentyp String bzw. Variable vom Datentyp Integer oder erstes Element von IntegerArray
Deklaration elementarer C/ C+ + - Datentypen in Visual Basic
Diese Aufstellung dürfte gut 95 Prozent der Fälle abdecken, die einem bei der Parameterdeklaration begegnen – von schlichten Umbenennungen dieser Datentypen einmal abgesehen. Fehlt die Angabe ByVal oder ByRef für einen Parameter, behandelt ihn Visual Basic als ByRef-Parame-
1 91
Prozeduren selbst definieren
C/C++-Datentyp
Prozeduren selbst definieren
Prozeduren selbst definieren
ter. In einzelnen Fällen (insbesondere im Zusammenhang mit As Any-Vereinbarungen) kann es auch nötig sein, einen ByRef-Parameter mit einem Wert oder einen ByVal-Parameter mit einer Adresse zu bedienen. In diesem Fall besteht im Zusammenhang mit Declare-Funktionen/-Prozeduren die Möglichkeit, einzelne Parameterwerte durch Voranstellen von ByVal oder ByRef explizit als Wert oder als Adresse zu übergeben. Lassen Sie aber Vorsicht walten, die Übergabe eines Werts anstelle einer Adresse führt im Allgemeinen sicher zum Absturz der Visual-Basic-Anwendung. Verschiedentlich erwartet eine DLL-Routine auch einen Funktionszeiger als Parameter, wenn sie für einen bestimmten Zweck eine anwendungseitige Funktion (Rückruffunktion) aufrufen muss – etwa, wie im Falle der API-Routinen waveOutOpen und waveInOpen der Multimediaerweiterung Winmm.dll von Windows 9x, wo die Rückrufroutine für die Pflege des Ein- bzw. Ausgabepuffers verantwortlich ist. Speziell für solche Fälle unterstützt Visual Basic den AddressOfOperator. Für das Generieren von Funktionszeigern mit AddressOf sind allerdings eine Reihe von Einschränkungen zu beachten: Der gesamte Code, der mit dem Aufruf der API-Routine zusammenhängt (Deklaration und Aufruf der API-Routine) muss in einem Standardmodul enthalten sein – AddressOf-Aufrufe in Klassen- oder Formularmodulen sind nicht erlaubt. Die Routine, deren Adresse AddressOf preisgibt, muss eine benutzerdefinierte Funktion oder Prozedur sein – via Declare importierte externe Routinen, sind nicht zulässig (als Work-around schreibt man eigene Routinen, die die Importroutinen umhüllen). Warnung
................................................... Wa rnung
Die ByVal-Deklaration von String-Parametern hat im Zusammenspiel mit Declare-Routinen eine andere Bedeutung als in den Parameterlisten gewöhnlicher Sub- und Function-Definitionen – sie dient der ByRef-Übergabe des Zeichenfolgenpuffers eines String-Werts. Zudem ist eine Vor- und Nachbehandlung der Werte von Zeichenfolgenparametern nötig. Tipps
................................................... Tipp
Falls Sie nicht über die Datei Win32api.txt verfügen, finden Sie diese in der Version vom 3.8.1995 unter http://www.microsoft.com/OfficeDev/Articles/Exe/Win32api.exe. Wenn Ihnen die Geschichte mit den Zeichenfolgen im obigen Beispiel nicht ganz geheuer vorkommt oder Sie mit unbekannten Datentypen konfrontiert werden, können Sie auch mit ByteArrays arbeiten. Beachten Sie aber, dass Visual Basic dann keine automatische Umwandlung von und nach Unicode vornimmt. Der Code für das im Abschnitt »Zeichenfolgen und ihre Besonderheiten« (S. 189 ) genannte Beispiel sieht dann etwa so aus: Private Declare Function GetUserName Lib "advapi32.dll" Alias _ "GetUserNameA" (lpBuffer As Byte, nSize As Long) As Long ... Dim sUN (20) As Byte, i As Long lLen = 20 If GetUserName(sUN, lLen) Then ' Benutzername (hier: Rudi) For i = 0 to lLen – 2 ' 0-Basiert, Nullbyte abschneiden Print Chr(a(i)) ' Ausgabe: "Rudi" (Kein Unicode!) Next i ... Beispiele
................................................... Beis piele
Das folgende Beispiel zeigt eine alternative Implementation der Dir-Funktion mit Hilfe der entsprechenden Win32-API-Routinen:
1 92
Routinen aus DLLs und der Windows- API einsetzen
Private Sub TestDir() Dim a As String a = Dir("*.frm") While a <> "" a = Dir Print a Wend End Sub
Prozeduren selbst definieren
Option Explicit Private Declare Function FindFirstFile Lib "kernel32" Alias _ "FindFirstFileA" (ByVal lpFileName As String, lpFindFileData _ As WIN32_FIND_DATA) As Long Private Declare Function FindNextFile Lib "kernel32" Alias _ "FindNextFileA" (ByVal hFindFile As Long, lpFindFileData _ As WIN32_FIND_DATA) As Long Private Declare Function FindClose Lib "kernel32" (ByVal hFindFile _ As Long) As Long Private Const MAX_PATH = 260 Private Type FILETIME dwLowDateTime As Long dwHighDateTime As Long End Type Private Type WIN32_FIND_DATA dwFileAttributes As Long ftCreationTime As FILETIME ftLastAccessTime As FILETIME ftLastWriteTime As FILETIME nFileSizeHigh As Long nFileSizeLow As Long dwReserved0 As Long dwReserved1 As Long cFileName As String * MAX_PATH cAlternate As String * 14 End Type ' Testen der alternativen Dir-Funktion
Private Function Dir(Optional sSuchm As String = "", Optional AttrVkt _ As VbFileAttribute = vbNormal) As String Static w32FD As WIN32_FIND_DATA Static lHandle As Long w32FD.dwFileAttributes = AttrVkt If sSuchm <> "" Then ' Erster Aufruf von Dir lHandle = FindFirstFile(sSuchm, w32FD) If lHandle Then Dir = Left(w32FD.cFileName, InStr(w32FD.cFileName, Chr(0)) – 1) End If Else ' weiterer Aufruf von Dir If FindNextFile(lHandle, w32FD) Then Dir = Left(w32FD.cFileName, InStr(w32FD.cFileName, Chr(0)) – 1) Else ' keine weiteren Dateinamen mehr FindClose (lHandle) ' Handle freigeben
1 93
Prozeduren selbst definieren
End If End If AttrVkt = w32FD.dwFileAttributes End Function
Prozeduren selbst definieren
Die hier vorgestellte Alternative zum Dir-Befehl von Visual Basic ist gleichfalls nicht rekursiv verwendbar, liefert aber immerhin den passenden Attributvektor. Eine für den rekursiven Aufruf taugliche Variante von Dir ergibt sich, wenn lHandle nicht als statische Variable, sondern als Ein-/Ausgabeparameter vereinbart wird: Private Function Dir(lHandle As Long, Optional sSuchm As String = "", _ Optional AttrVkt As VbFileAttribute = vbNormal) As String
In diesem Fall muss sich der Aufrufer um den Such-Handle kümmern. Hier noch ein Beispiel für den Einsatz einer von Visual Basic gestellten Funktion ZähleFenster als Rückruffunktion für die API-Funktion EnumWindows. Die Rückruffunktion wird beim Aufruf von EnumWindows installiert und dann (solange sie den Wert 1 liefert) für jedes Fenster einmal aufgerufen. Declare Function EnumWindows Lib "user32" (ByVal lpEnumFunc As Long, _ ByVal lParam As Long) As Long Private lFenster As Long ' Rückruffunktion, zählt Fenster in globaler Variable Function ZähleFenster (ByVal hwnd As Long, ByVal data As Long) As Long lFenster = lFenster + 1 ZähleFenster = 1 ' True, damit die Aufzählung weitergeht End Function ' Liefert Anzahl der bei Windows registrierten Fenster Function FensterAnz() As Long Dim Dummy As Long Dummy = EnumWindows(AddressOf ZähleFenster, 0) FensterAnz = lFenster End Function
Ein Beispiel für den Einsatz der API-Funktion SendMessage im Zusammenhang mit einem Kombinationsfeld-Steuerelement finden Sie im Abschnitt »Printer-Objekt«, S. 284. Verwandte Befehle
................................................... Verwa ndte Befehle
Function, Sub Verwandte Themen
................................................... Verwandte Them en
Variablendeklaration (S. 162); Elementare Datentypen (S. 49); Benutzerdefinierte Datentypen (S. 60)
1 94
Objekte und Klassen Wenn etwas aus der heutigen Programmierwelt nicht mehr wegzudenken ist, dann sind das Objekte. Während die klassische imperative Programmierung mit ihrem so genannten »anwendungszentrierten Ansatz« noch die strikte Trennung zwischen Daten und Code predigte und weitgehend monolithische Anwendungen hervorbrachte, die über oft sehr komplexen Datenstrukturen operierten, hat sich die objektorientierte Programmierung geradezu die gegenteilige Ansicht zu eigen gemacht: Sie geht davon aus, dass die Repräsentation und die Manipulation von Daten eng zusammengehören und nichts weiter als unterschiedliche Aspekte des gleichen »Dings« sind – nämlich des Objekts. Wo in der imperativen Programmierung eine Variable oder ein Wert eines bestimmten Datentyps vereinbart und initialisiert wird, wird in der objektorientierten Programmierung eine Objektvariable oder ein Objekt einer bestimmten Klasse vereinbart und instanziiert. Und wo die imperative Programmierung mit einer losen Sammlung von Funktionen und Prozeduren arbeitet, die nur implizit – nämlich über die Datentypen der Ein-/Ausgabeparameter und des Funktionswerts – auf einen oder mehrere Datentypen zugeschnitten sind, sorgt der Klassenbegriff bei der objektorientierten Programmierung für eine feste Zuordnung zwischen einer konkreten Datenstruktur (Eigenschaften des Objekts) und den zugehörigen Operationen (Methoden des Objekts). Die Operation eines Objekts hat einen Ich-Bezug; das hat eine gewöhnliche Funktion oder Prozedur nicht. Als eine der Altvordern heutiger Programmiersprachen hatte die einstige Interpretersprache Basic nicht nur gewisse Mühe, mit der Tradition der klassischen imperativen Programmierung aufzuschließen, auch der Übergang zur objektorientierten Programmierung verlief nicht ganz schmerzlos, wenngleich sich das Ergebnis mit Blick auf Visual Basic 6.0 wahrlich sehen lassen kann. Während die Integration benutzerdefinierter Funktionen und Prozeduren noch völlig geradlinig und auch ohne bleibende Narben vor sich ging (man betrachte sich die Sprache etwa auf dem Stand von QuickBasic oder QBasic), bescherte die Einführung des Objektbegriffs der Sprache jedoch nachhaltig eine Spaltung, die gerade bei unerfahrenen Programmierern zuweilen für eine gewisse Verwirrung sorgt. Angesichts dessen, was die Sprache dadurch aber an Ausdruckskraft und Vielseitigkeit gewonnen hat, ist diese Spaltung jedoch leicht in Kauf zu nehmen. Im Vergleich mit anderen Programmiersprachen war der unkomplizierte Umgang mit den Windows-Steuerelementen seit jeher eine der großen Stärken der integrierten Entwicklungsumgebung von Visual Basic (und wohl auch das hinter dem Produkt steckende Erfolgsrezept), insbesondere die Möglichkeit, auf relativ einfache Art und Weise zu benutzerdefinierten Steuerelementen zu gelangen, die sich auch jenseits von Visual Basic erfolgreich einsetzen ließen. Bis hin zur Version 3.0 war es noch der VBX-Standard, ein reiner 16-Bit-Standard, der für die Implementierung der Steuerelemente maßgeblich war. VBX lag zweifelsohne ein Objektmodell zugrunde, wenngleich dies damals noch nicht so explizit zum Ausdruck kam, da die umgebende Landschaft fehlte. Als Microsoft Visual Basic mit der Version 4.0 an die 32-Bit-Welt anpasste, erhielt die Sprache mit VBA (Visual Basic for Applications), damals auch »Object Basic« genannt, einen neuen und vollständig objektorientierten Kern. VBA unterwarf Visual Basic der Herrschaft des COM (Component Object Model) und verheiratete die objektorientierte Seite der Sprache mit OLE 2.0. VBX blieb dabei als nicht COM-konformer Standard auf der Strecke. An seiner Stelle hielt der neue 32-Bit-Standard OCX (OLE Control) – seinerseits eine Erweiterung von OLE 2.0 – Einzug in die Welt der Steuerelemente. Der durch das COM geschaffene Objektbegriff ist unabhängig von einer spezifischen Programmiersprache oder -plattform. Er beinhaltet:
1 95
Klassen als Datentypen für Objektvariablen
Klassen als Datentypen für Objektvariablen
1. Einen Standardmechanismus für die global (= weltweit) eindeutige Benennung eines COMObjekts mit einer GUID (Globally Unique Identifier), der systemweit (auf Basis der Systemregistrierung) das Auffinden und Laden des Objekts sowie das Auffinden der für die Bearbeitung des Objekts zuständigen Anwendungen ermöglicht 2. Einen Standardmechanismus (Referenzenzählung), durch den ein Objekt selbstständig verfolgen kann, inwieweit es noch benötigt wird 3. Einen Standardmechanismus zur Fehlermeldung und einen Satz an Fehlercodes 4. Einen Standardmechanismus für das Ansprechen von Eigenschaften und Methoden 5. Einen Standardmechanismus für den Austausch von Objekten DCOM (Distributed COM) dehnt die COM-Spezifikation auf Netzwerke aus. Bei OLE 2.0, das Microsoft seit einiger Zeit mit Zielrichtung Internet unter dem Namen ActiveX vermarktet, handelt es sich um eine Sammlung verschiedener COM-konformer Technologien, die das Miteinander von Objekten und Komponenten für die Gestaltung verteilter Anwendungen ermöglichen. Nicht nur Windows bietet inzwischen immer mehr Bestandteile seiner selbst als ActiveX-Komponenten feil, ein großer Teil der gängigen Anwendungen angefangen von den »Schlachtschiffen« Word, Excel und Access aus der Microsoft Office Suite über den Internet Explorer bis hin zu kleinsten Hilfsanwendungen ist mit von der Partie. War die Objektorientierung für Visual Basic vor der Version 4.0 mehr oder minder nur die Eintrittskarte in die »ereignisreiche« Welt der grafisch orientierten Benutzerschnittstelle von Windows, so erschließt sie der Sprache seit der Adaption des COM-Objektbegriffs im Zuge der Version 4.0 die gesamte Welt des ActiveX und der Automatisierung – darunter: ActiveX-Steuerelemente, ActiveX-Komponenten, ActiveX-Container, ActiveX-Datenobjekte (ADOs), Automatisierungsobjekte, Automatisierungsserver.
Klassen als Datentypen für Objektvariablen {Dim | Public | Private | Static} [WithEvents] ObjVar As ObjTyp {Dim | Public | Private | Static} ObjVar As New ObjTyp Set ObjVar = [New] {Klasse | Nothing} Set ObjVar = ObjAusdr Beschreibung
................................................... Bes c hreibung
Seit Visual Basic Module für die Definition eigener Objektdatentypen oder Klassen erlaubt, haben sich die Möglichkeiten für die Definition und Ausgestaltung benutzerdefinierter Datentypen drastisch erweitert. Die Vereinbarung einer Variablen ObjVar für einen gegebenen Objektdatentyp Klasse geschieht im Rahmen einer Dim-, Private-, Public- oder Static-Anweisung, mit den üblichen Auswirkungen für den Geltungsbereich. Falls Klasse auf die Signalisierung von Ereignissen eingerichtet ist und die Variablendeklaration auf Modulebene erfolgt, kann die Vereinbarung zusätzlich das Schlüsselwort WithEvents enthalten. Dies eröffnet die Möglichkeit, in dem Modul Ereignisroutinen bereitzustellen, die auf Ereignisse des der Variablen zugeordneten Objekts reagieren. Ohne WithEvents besteht die Möglichkeit, das Schlüsselwort New anzugeben, was den Compiler zu einer impliziten Instanziierung der Objektvariablen bei ihrem ersten Einsatz veranlasst. Fehlt das Schlüsselwort New bei der Variablendeklaration, initialisiert Visual Basic die vereinbarte Objektvariable standardmäßig mit dem Wert Nothing. In diesem Fall ist eine explizite SetZuweisung (mit der Variablen als Linkswert) erforderlich, um ihr ein konkretes Objekt zuzuordnen. Je nach Beschaffenheit des Rechtswerts in der Anweisung erhält die Objektvariable dabei als Wert entweder einen (weiteren) Verweis auf ein bereits bestehendes Objekt oder einen Verweis auf eine neue Instanz des Objekts. Ersteres geschieht, wenn der Rechtswert ObjAusdr in
1 96
Klassen als Datentypen für Objektvariablen
Anwendung
................................................... Anwendung
Obwohl Klassen mehr oder weniger direkte Nachfahren der benutzerdefinierten Type-Datentypen sind, existieren doch eine Reihe signifikanter Unterschiede. Die folgende Tabelle gibt einen Überblick. Variable mit Type-Datentyp
Objektvariable
Benutzerdefinierter Datentyp enthält nur Datenelemente (Element kann aber Objektvariable sein).
Objekt kann neben Eigenschaften (Datenelemente) auch Methoden (Funktionen/Prozeduren mit »IchBezug«) haben sowie auf Ereignisse reagieren und Ereignisse auslösen.
Visual Basic initialisiert den Wert der Variablen automatisch unter Beachtung der Vorgabewerte für die Datentypen der Elemente.
Eine frisch deklarierte Objektvariable hat den Wert Nothing, verweist also auf kein Objekt. Wertzuweisungen an eine Objektvariable erfordern das Schlüsselwort Set (anstelle von Let). Wenn dabei ein neues Objekt generiert werden soll, ist entweder bei der Deklaration oder bei der Zuweisung das Schlüsselwort New erforderlich. (Bei WithEvents-Deklaration darf New nur in einer Set-Anweisung stehen!)
Der Wert einer Objektvariablen ist ein Verweis auf ein Die Variable repräsentiert einen Objekt. Es können mehrere unterschiedliche Objektvakonkreten Wert des benutzerdefinierten Datentyps. (ByRef-Parameter riablen auf ein und dasselbe Objekt verweisen. in Funktions-/Prozeduraufrufen behandelt Visual Basic wie temporäre Umbenennungen ein- und derselben Variablen). Der Wert einer benutzerdefinierten Variablen existiert so lange wie die Variable.
Ein Objekt existiert so lange, wie eine Objektvariable darauf verweist. Visual Basic baut ein Objekt ab, wenn der Geltungsbereich der letzten darauf verweisenden Variablen erlischt, diese den Wert Nothing erhält oder einen neuen Wert zugewiesen bekommt.
Vergleich zwischen Type-Datentypen und Objektdatentypen
1 97
Klassen als Datentypen für Objektvariablen
der Zuweisung durch eine andere Objektvariable oder als Funktionsergebnis bereitgestellt wird. Zur Instanziierung eines neuen Objekts des Objektdatentyps Klasse kommt es dagegen, wenn der Rechtswert durch das Schlüsselwort New gefolgt von dem Objektdatentyp gebildet wird (oder, wie gesagt, eine New-Deklaration erfolgt ist). Mit Blick auf die objektorientierte Programmierung unterstützt Visual Basic den vordefinierten Datentyp Object. Es handelt sich dabei um einen generischen (lies: universellen) Objektdatentyp, der es einer Objektvariablen ermöglicht, auf Objekte einer beliebigen Klasse zu referieren und somit einen beliebigen Objektdatentyp anzunehmen. (Da Object auch als Untertyp von Variant definiert ist, gilt das hier Gesagte auch für Variant). Einer Object-Variablen kann also mit einer Set-Zuweisung jedes beliebige Objekt zugeordnet werden – um den Preis einer späten Bindung zur Laufzeit. Das Konzept des Polymorphismus ermöglicht darüber hinaus die Vereinbarung von Objektvariablen übergeordneter Schnittstellenklassen (beispielsweise Form oder UserControl) und deren Verwendung für Objekte spezifischer Klassen, die Implementationen für die jeweiligen Schnittstellen bereitstellen. Ein und dieselbe Objektvariable kann dann für Objekte unterschiedlichen Typs verwendet werden, solange diese die gleiche Schnittstelle implementieren.
Klassen als Datentypen für Objektvariablen
Variable mit Type-Datentyp
Objektvariable
Klassen als Datentypen für Objektvariablen
Zuweisungsoperator kann Kopie des Für Objektvariablen sind nur Set-Zuweisungen defiWerts erzeugen. niert, die mit Referenzen auf ein gegebenes Objekt hantieren. Jede Set-Zuweisung erzeugt eine neue Referenz auf ein gegebenes Objekt. Die Kopie eines Objekts kann nur das Objekt selbst anfertigen. Das Objekt muss dazu eine geeignete Methode bereitstellen. Da Visual Basic keine Vergleichsoperatoren für benutzerdefinierte Typen kennt (das wäre auch nicht sehr sinnvoll), muss beispielsweise die Gleichheit komponentenweise (Datenfeld für Datenfeld) geprüft werden.
Für den Vergleich von Objektreferenzen ist der Is-Operator definiert. Er liefert den Wert True, wenn zwei Objektvariablen auf ein und dasselbe Objekt verweisen, ansonsten False.
Eine Private-Definition eines benutzerdefinierter Datentyps kann in jedem Modul (im Bereich ALLGEMEIN) geschehen, eine Public-Definition nur in einem Standardmodul.
Der Objektdatentyp ist in einem eigenen Klassenmodul definiert und somit im gesamten Projekt sichtbar. Darüber hinaus kann er veröffentlicht (registriert) werden was ihn für alle Projekte auf dem jeweiligen System sichtbar macht.
Der Zugriff auf Datenelemente erfolgt durch Notation des Punktoperators oder durch With-Qualifizierung. Alle Datenelemente haben den gleichen Geltungsbereich.
Der Zugriff auf Eigenschaften und Methoden erfolgt durch Notation des Punktoperators oder durch WithQualifizierung. Der Besitzer eines Objekts (lies: der Besitzer einer Objektvariablen mit einer gültigen Referenz auf das Objekt) kann nur die als Public oder – innerhalb ein und desselben Projekts – Friend deklarieren Eigenschaften und Methoden ansprechen. Als Private deklarierte Eigenschaften oder Methoden sind nur im Codemodul des Objektdatentyps bekannt. Innerhalb des Codemoduls eines Objektdatentyps ist keine Qualifizierung beim Zugriff auf Eigenschaften und Methoden erforderlich. Die Qualifizierung kann jedoch optional durch Voranstellen des Bezeichners Me erfolgen (vgl. folgende Abbildung).
Es gibt kein »Standarddatenelement«.
Für den Objektdatentyp kann eine Standardeigenschaft definiert sein. Der Zugriff darauf erfordert nur die Objektvariable ohne weitere Qualifizierung.
Da für Variant der Untertyp Object definiert ist, lassen Der Wert lässt sich einer Variantsich einer Variant-Variablen die Werte beliebiger Variablen nicht zuweisen. ZuweiObjektvariablen mittels Set zuweisen. sungen (nicht jedoch die LSetZuweisung) setzen für Rechts- und Linkswert exakt den gleichen Datentyp voraus. Vergleich zwischen Type-Datentypen und Objektdatentypen
1 98
Klassen als Datentypen für Objektvariablen
Wann immer Sie mit einer Größe arbeiten wollen, die nur als Objektdatentyp zur Verfügung steht – und das sind so gut wie alle in Visual Basic definierten Datentypen jenseits der elementaren und benutzerdefinierten Datentypen sowie der Arrays –, müssen Sie eine Objektvariable vereinbaren. Die Visual-Basic-Entwicklungsumgebung bietet Ihnen bei der Programmierung mit Objekten in recht eindrucksvoller Weise Unterstützung an, indem sie (ähnlich wie bei den Funktionsparametern) die zu der Klasse einer Objektvariablen gehörigen Eigenschaften und Methoden parat hält und Ihnen beim Tippen als Auswahlliste einblendet (vgl. die folgende Abbildung). Auf diese Weise haben Sie jederzeit einen guten Überblick über die Strukturelemente eines Objekts.
Für die Definition eigener Objektdatentypen müssen Sie Klassenmodule in Ihr Projekt einfügen (mehr dazu im Abschnitt »Selbst definierte Klassen«, S. 318). Tipp
................................................... Tipp
Der über den Befehl OBJEKTKATALOG im Menü ANSICHT oder die Taste (F2) aufgerufene Objektkatalog gibt einen Überblick über sämtliche unter Visual Basic verfügbaren Objektdatentypen und deren Eigenschaften, Methoden und Ereignisse (siehe folgende Abbildung). Beispiel
................................................... Beis piel
Der folgende Codeauszug aus dem Beispielprojekt FormMitEreignis zeigt die WithEvents-Vereinbarung einer privaten Objektvariablen sowie deren Set-Initialisierung im Rahmen einer Property-Routine mit dem Wert einer öffentlichen Eigenschaft, die den Objektdatentyp BasisForm trägt (das vollständige Beispiel ist im Abschnitt »Ereignisroutinen«, S. 204, abgedruckt). ' Klasse: clsFormMitEreignis Private WithEvents mBasisForm As BasisForm ' Formular ist Eigenschaft Public Property Set BasisForm(ByVal newBasisForm As BasisForm) Set mBasisForm = newBasisForm ' Wert der Eigenschaft setzen End Property ' (Formularobjekt zuordnen) Methoden
................................................... Metho den [Public | Private | Friend][Static] Function Name [(ParmListe)][ As Typ] [Public | Private | Friend][Static] Sub Name [(ParmListe)]
1 99
Klassen als Datentypen für Objektvariablen
Die Entwicklungsumgebung bietet die Eigenschaften und Methoden eines Objekts während des Schreibens an
Klassen als Datentypen für Objektvariablen
Klassen als Datentypen für Objektvariablen
Der Objektkatalog hebt die Objektdatentypen des aktuellen Projekts sowie deren implementierte Eigensc haften, Ereignisse und Methoden durc h Fettsc hrift hervor (vgl. Beispiel zu
»Ereignisroutinen«,
S. 204)
Beschreibung
................................................... Bes c hreibung
In der objektorientierten Programmierung versteht man unter Methode nichts anderes als eine zu einem spezifischen Objektdatentyp gehörige Funktion oder Prozedur, die über der konkreten Datenstruktur eines Objekts dieses Datentyps operieren kann. Dieser »Ich-Bezug« auf ein spezifisches Objekt ist zugleich das auffälligste Merkmal der Methode. Eine Methode »sieht« alle Eigenschaften, Datenfelder und Methoden ihres Objekts, gleich welchen Geltungsbereich diese haben. Als Geltungsbereich für eine Methode kann Public, Private oder Friend vereinbart werden. Standardmäßig vereinbart Visual Basic den Geltungsbereich Public. Mit Private als Geltungsbereich kann eine Methode von anderen Methoden des gleichen Objekts aufgerufen werden, nicht jedoch vom Besitzer des Objekts (Modulebene). Umgekehrt macht der Geltungsbereich Public eine Methode für alle sichtbar, die über eine Referenz auf das Objekt verfügen (Systemebene) – insbesondere auch für den Besitzer. Darüber hinaus ist in Formular- und Klassenmodulen auch eine eingeschränkte öffentliche Deklaration mit dem Schlüsselwort Friend möglich, die den Geltungsbereich auf die Formular- und Klassenmodule des aktuellen Projekts verengt (Prozessebene). Ist bei der Deklaration einer Methode der Zusatz Static angegeben, vereinbart Visual Basic implizit alle Variablen der Methode als Static (statische Variablen vergessen ihren Wert von einem Aufruf zum nächsten nicht). Neben den gewöhnlichen Methoden sieht das Objektmodell von Visual Basic zwei Arten von Methoden vor, denen eine besondere Funktionalität zukommt: Ereignisroutinen und PropertyMethoden. Ereignisroutinen sind für die Nachrichtenbehandlung zuständig und PropertyMethoden für das Setzen (Set und Let) und Abfragen (Get) der Eigenschaften eines Objekts. (Mehr dazu in den folgenden beiden Abschnitten.) Ist eine Methode als Standardelement (Standardeigenschaft) eines Objektdatentyps definiert (vgl. Dialogfeld PROZEDURATTRIBUTE), kann ihr Aufruf durch alleinige Notation des Objektbezeichners unter Weglassung des Methodenbezeichners geschehen.
200
Klassen als Datentypen für Objektvariablen
Anwendung
................................................... Anwendung
[Erg =] [Me.]Methode[(ParamListe)]
Von jedem anderen Modul aus kann der Aufruf der Methode dagegen nur unter Angabe eines Objekts bzw. einer Objektvariablen erfolgen, vorausgesetzt, die Methode ist mit dem Geltungsbereich Public (bzw. Friend innerhalb des gleichen Projekts) deklariert worden: [Erg =] MeinObjekt.MethodeVonMeinObjekt[(ParamListe)]
Ansonsten gilt für Methoden dasselbe wie für Funktionen/Prozeduren. Tipp
................................................... Tipp
Wenn es Sie stört, dass Visual Basic bei Deklarationen implizit den Geltungsbereich Public annimmt, können Sie eine Option Private-Anweisung in das betreffende Modul setzen. Beispiel
................................................... Beis piel
Vgl. »Eigenschaften« (S. 201) Verwandte Themen
................................................... Verwandte Them en
Funktionen und Prozeduren (S. 175) Eigenschaften
................................................... Eigens c ha ften [Public | Private | Friend][Static] Property Get Eigenschaft () As Typ [Public | Private | Friend][Static] Property Let Eigenschaft _ (NeuerWert As Typ) [Public | Private | Friend][Static] Property Set Eigenschaft _ (NeuerWert As ObjektTyp)
201
Klassen als Datentypen für Objektvariablen
Da sich Visual Basic spätestens seit der Version 4 nicht mehr als »Sprache mit Objektorientierung«, sondern als objektorientierte Sprache versteht, muss man sich natürlich die Frage stellen, inwieweit nicht alle Funktionen und Prozeduren Methoden eines Objekts sind. In der Tat ist eine derartige Betrachtung ebenso richtig wie sinnlos, da auch Datentypen wie Integer und Long eine objektorientierte Sicht rechtfertigen, die konkrete Implementation jedoch anders aussieht. Auch weist der Objektkatalog so manche Funktion/Prozedur als »Methode« des einen oder anderen Moduls aus, das aber selbst nicht als Klasse in Erscheinung tritt. Die Grenze zwischen Methode und Funktion/Prozedur ist am einfachsten pragmatisch zu ziehen: Betrachten Sie als Methode, was sich wie eine Methode notieren lässt, und alles andere als Funktion/Prozedur. Diese Sicht ist zwar nicht »immer« richtig, wie ein Blick auf die »Methode« Print etwa des Form-Objekts offenbart (sie ist im Objektkatalog nicht zu finden), so doch aber »in aller Regel«. Insbesondere verbleiben so alle traditionell zu Basic gehörenden Routinen sowie alles zu den elementaren Datentypen Gehörige im Bereich der Funktionen/Prozeduren. Der Objektbezug einer Methode muss in jeder Situation gewahrt sein; im eigenen Klassenmodul ist er implizit, ansonsten explizit. Der Aufruf einer Methode innerhalb ihres Klassenmoduls unterscheidet sich syntaktisch nicht vom Aufruf einer Funktion/Prozedur, da keine Qualifizierung durch einen Objektbezeichner gefordert ist. Optional kann aber eine Qualifizierung erfolgen, indem die den Ich-Bezug ausdrückende spezielle Objektvariable Me angegeben wird bzw. eine Objektvariable, die auf das gleiche Objekt wie Me verweist (vgl. das Beispiel zu »PrinterObjekt«, S. 284).
Klassen als Datentypen für Objektvariablen
Beschreibung
Klassen als Datentypen für Objektvariablen
................................................... Bes c hreibung
Bei den Eigenschaften wird es am deutlichsten: Es gibt die Innensicht und die Außensicht eines Objekts. Letztere ist Teil der ersteren und offenbart die Eigenschaften des Objekts. Die Innensicht gibt den Blick frei auf die tatsächliche Repräsentation der von einem Objekt gespeicherten Daten, also auf die Datenstrukturen und Datenfelder, die das Objekt ausmachen und über denen seine Methoden operieren. Als Objektdatenfelder fungieren gewöhnliche, auf (Klassen-) Modulebene deklarierte Variablen, denen ihrerseits ein Datentyp bzw. ein Objektdatentyp zugrunde liegt. Der Zugriff auf diese Datenstrukturen von innerhalb des Moduls unterscheidet sich nicht vom Zugriff auf gewöhnliche Variablen und ist allen im gleichen Modul definierten Methoden ohne Einschränkungen gestattet. Die Innensicht deckt sich also weitgehend mit der Sicht der traditionellen Programmierung. Von größerem Interesse ist die Außensicht auf ein Objekt. Visual Basic beugt sich hier inzwischen weitgehend den Regeln des COM: Property-Methoden fungieren als Schleusen für die Abfrage und das Setzen der Werte von Eigenschaften. Was der Besitzer eines Objekts als Wert einer Eigenschaft tatsächlich zu sehen bekommt, ist der Funktionswert einer Property GetFunktion, deren Bezeichner den Namen der Eigenschaft und deren Ergebnistyp den Datentyp der Eigenschaft stellt. Der Funktionswert kann zwar mit dem Wert eines Datenfelds des Objekts übereinstimmen, muss aber nicht. Mit anderen Worten, es kann genauso gut sein, dass der Wert einer Eigenschaft in Außenansicht kein Gegenstück innerhalb des Objekts in Form einer Variablen hat, sondern von der Property Get-Funktion errechnet wird. Das Gleiche, jedoch mit umgekehrten Vorzeichen, gilt für das Setzen von Eigenschaften: Die Let- oder Set-Zuweisung eines Werts an eine Eigenschaft übersetzt der Compiler in den Aufruf einer Property Let- bzw. Property Set-Methode des Objekts, die den Namen der Eigenschaft trägt. Man sieht, es lassen sich auch unterschiedliche Geltungsbereiche für das Lesen und Setzen einer Eigenschaft festlegen. Eigenschaften, für die keine Property Get-Funktion definiert (oder sichtbar) ist, sind lesegeschützt, und solche, für die keine Property Let- bzw. Property SetMethoden existieren (oder sichtbar sind), sind schreibgeschützt (vgl. auch: AmbientPropertiesObjekt). Da es den Methoden eines Objekts freigestellt ist, auch von den objekteigenen Property-Methoden Gebrauch zu machen, sind alle Eigenschaften eines Objekts auch in der Innensicht des Objekts bekannt. Anwendung
................................................... Anwendung
Angesichts dieser Tatsache stellt sich natürlich die Frage, was eigentlich geschieht, wenn eine Variable auf Modulebene als Public vereinbart wird. In der Tat vereinbart der Compiler dann die entsprechenden Property-Methoden implizit, so dass Sie prinzipiell die Wahl haben, welche der Schreibweisen Sie bevorzugen. Aufgrund eines Fehlers in Visual Basic 6.0 vereinbart der Compiler die entsprechenden Property-Methoden allerdings nicht in jedem Fall, wie die entsprechende Diskussion im Abschnitt »Parameterübergabe bei Funktionsaufrufen«, S. 574, des Praxisteils zeigt.) Da die explizite Vereinbarung von Property-Methoden einen größeren Spielraum bietet, in der Methode kann beispielsweise eine Überprüfung von Werten stattfinden, sollten Sie aber tendenziell diese Schreibweise benutzen. Welche Eigenschaften ein Objekt in welchem Geltungsbereich offenbart, liegt im Ermessen des Programmierers. Eine Richtlinie für die Programmierung mit Objekten ist jedoch, dass es so wenige wie möglich sein sollten und dass der Geltungsbereich so eng wie möglich gewählt werden sollte; das hält die Implementation übersichtlich und beugt Unsinn vor, den andere Anwendungen mit dem Objekt treiben könnten. Da eine Private-Eigenschaft (mangels Vererbung) faktisch nichts anderes ist als eine auf Modulebene vereinbarte Private-Variable – der Zugriff darauf ist ja nur mit objekteigenen Methoden möglich –, stellt sich bei der Wahl des Geltungsbereichs (zur Zeit) eigentlich nur die Frage: Friend oder Public? Friend-Deklarationen sorgen
202
Klassen als Datentypen für Objektvariablen
dafür, dass ein Bezeichner nur innerhalb des Projekts sichtbar ist, in dem die jeweilige Klasse kompiliert wird. Der Unterschied wirkt sich jedoch erst im Zusammenhang mit ActiveX-Steuerelementen und -Komponenten aus, die einer Veröffentlichung unterzogen werden. Tipp
................................................... Tipp
Angenommen, Sie schreiben ein Benutzersteuerelement und wollen eine Eigenschaft des zugrunde liegenden UserControl-Objekts übernehmen. Sie können dann den Bezeichner der Eigenschaft beibehalten, wenn Sie für eine geeignete Qualifizierung sorgen. Das verhindert den rekursiven Aufruf der Methode.
Beispiel
................................................... Beis piel
Der folgende Code zeigt die Implementation einer einfachen in einem Klassenmodul definierten Klasse Vektor und den Umgang mit einigen Eigenschaften und Methoden von Objekten dieser Klasse. ' Irgendwo in einem Formular des Projekts ... Dim a As New Vektor Dim b As New Vektor a.x = 1: a.y = 1: a.z = 1 ' Initialisierung: Eigenschaften setzen b.Init 2, 2, 2 ' Initialisierung: Init-Methode a.Addiere b Print a.Printwert Print a.SkalarProdukt(b) Print a.Betrag ' Klassenmodul: Vektor.cls Option Explicit Public x As Double Public y As Double Public z As Double Property Set Copy(v As Vektor) x = v.x: y = v.y: z = v.z End Property
' Eigenschaften, weil Public!
' Kopieren eines Werts
Property Get Betrag() As Double ' Länge des Vektors (wird errechnet!) Betrag = Sqr(x * x + y * y + z * z) End Property Function SkalarProdukt(v As Vektor) As Double SkalarProdukt = v.x * x + v.y * y + v.z * z End Function Sub x y z End
Addiere(v As Vektor) = x + v.x = y + v.y = z + v.z Sub
' Skalarprodukt
' Vektorsumme
203
Klassen als Datentypen für Objektvariablen
Property Set Picture(NewPicture As Picture) UserControl.Picture = Picture ' Qualifizierung! End Property
Klassen als Datentypen für Objektvariablen
Klassen als Datentypen für Objektvariablen
Sub x y z End
Skaliere(faktor As Double) = x * faktor = y * faktor = z * faktor Sub
Function Produkt(v As Produkt.x = y * v.z Produkt.y = z * v.x Produkt.z = x * v.y End Function
' skalares Produkt
Vektor) As Vektor ' Vektorprodukt – z * v.y – x * v.z – y * v.x
Property Get Printwert() As String ' Vektor als Zeichenfolge Printwert = "(" + CStr(x) + "," + CStr(y) + "," + CStr(z) + ")" End Property Sub Init(a As Double, b As Double, c As Double) ' Initialisierung x = a: y = b: z = c End Sub Verwandte Befehle
................................................... Verwa ndte Befehle
Let, = Verwandte Themen
................................................... Verwandte Them en
Datentypen und ihre Operationen (S. 49); »Selbst definierte Klassen« (S. 318)
Ereignisroutinen Sub SignalisierendesObjekt_Ereignis([ParamListe]) ... End Sub Event Ereignis[(ParamListe)] RaiseEvent Ereignis[(WerteListe)] [Private | Public | Dim] WithEvents Eigenschaft As Typ Beschreibung
................................................... Bes c hreibung
Der Kernmechanismus der ereignisorientierten Benutzerschnittstelle von Windows besteht darin, dass ein Objekt Ereignisroutinen bereitstellt, um spezifisch auf äußere Vorkommnisse reagieren zu können. Ein solches Vorkommnis, Ereignis genannt, kann beispielsweise ein Mausklick auf eine in einem Formular angeordnete Schaltfläche, eine Wertänderung in einem Textfeld oder schlicht eine Nachricht eines anderen Objekts sein. Der ausgeklügelte Benachrichtigungsmechanismus von Windows sieht aufseiten des Programms eine Nachrichtenschleife vor, die – grob gesagt – eine geeignete Zustellung der Ereignisse an die einzelnen Objekte vornimmt und den Aufruf bereitgestellter Ereignisroutinen bedingt. (Die von Windows verschickten Nachrichten enthalten einen Ereigniscode sowie weitere Parameter mit ereignisspezifischer Bedeutung, die eine genauere Beschreibung des Ereignisses darstellen). Der Visual-Basic-Compiler sorgt dafür, dass die Implementierung dieses Mechanismus für den Programmierer größtenteils transparent bleibt und sich nur in einigen wenigen Vorgaben bemerkbar macht, die bei der Programmierung mit Ereignissen formal zu beachten sind. Von einer wie auch immer gearteten
204
Ereignisroutinen
Anwendung
................................................... Anwendung
Die Bereitstellung einer Ereignisroutine erfolgt auf Modulebene, und zwar im Klassenmodul des Objekts, das die Behandlung des Ereignisses durchführen soll. (Außer dem Standardmodul sind alle in Visual Basic verfügbaren Modularten letztlich Klassenmodule und somit auf die Ereignisbehandlung eingerichtet.) Eine Ereignisroutine hat grundsätzlich die Form einer öffentlichen oder privaten Sub-Prozedur, deren Bezeichner nach folgendem Schema zu bilden ist: Name des signalisierenden Objekts plus Unterstrich plus Name des Ereignisses, zum Beispiel Form_Load. Die folgende Abbildung zeigt die Auswahl einer Ereignisroutine für die Implementierung in einem Formularmodul.
Auswahl einer Ereignisprozedur für die Implementierung
Man sieht, die Entwicklungsumgebung von Visual Basic kommt einem bei der Implementierung von Ereignisroutinen weitgehend entgegen: Im linken Listenelement wählt man die Objektvariable und im rechten das Ereignis aus. An Objektvariablen stehen neben dem Bezeichner der jeweiligen Basisklasse des Objekts (Form, UserControl, PropertyPage usw.) die Bezeichner sämtlicher im Entwurfsbereich platzierten Steuerelemente und ActiveX-Komponenten zur Verfügung sowie alle mit WithEvents auf Modulebene deklarierten Objektvariablen.
205
Klassen als Datentypen für Objektvariablen
»Verdrahtung« zwischen Ereignisroutinen und Nachrichten bekommt der Visual-Basic-Programmierer überhaupt nichts zu sehen, auch entzieht sie sich seiner Kontrolle. Zu den vordefinierten Objekten von Visual Basic (dazu zählen insbesondere die auf dem jeweiligen System verfügbaren Steuerelemente und ActiveX-Komponenten) gehört eine breite Vielfalt von Ereignissen, für die sich Ereignisroutinen implementieren lassen. Objekte können aber nicht nur auf Ereignisse reagieren, sie sind auch ihrerseits in der Lage, Ereignisse zu signalisieren. Damit ein Objekt ein Ereignis Ereignis mittels RaiseEvent auslösen kann, muss das zugehörige Objektmodul (Klassenmodul, Steuerelementmodul, Formularmodul oder Designermodul) eine Event-Deklaration enthalten, die das Gerüst der Ereignisroutine als Prozedur mit dem Bezeichner Ereignis deklariert. Wird für die Ereignisprozedur eine Parameterliste ParamListe vereinbart, muss bei Auslösung des Ereignisses wie bei einem Funktionsaufruf eine Werteliste für die einzelnen Parameter bereitgestellt werden. Für die Behandlung eines solchen von einem Objekt ausgelösten Ereignisses ist der Besitzer des Objekts zuständig, aber nicht verpflichtet. Er kann zu diesem Zweck eine Objektvariable für das Objekt mit dem Zusatz WithEvents deklarieren und unter ihrem Namen kombiniert mit dem Ereignisnamen eine Ereignisroutine bereitstellen, deren Struktur auf das objektseitig vorgegebene Gerüst passt.
Klassen als Datentypen für Objektvariablen
Klassen als Datentypen für Objektvariablen
Für den Mechanismus der Ereignisbehandlung macht es keinen Unterschied, wer die Benachrichtigung eines Ereignisses veranlasst hat – das Betriebssystem, ein Steuerelement, eine Komponente oder irgendein anderes Objekt. Bei benutzerdefinierten (lies: in Klassenmodulen definierten) Objektdatentypen muss man nur darauf achten, die entsprechenden Objekte auf Modulebene unter Angabe von WithEvents zu deklarieren, damit der Besitzer deren Ereignisse auch zu sehen bekommt. Für Benutzersteuerelemente, die sich in die Werkzeugsammlung einfügen lassen, erledigt das Visual Basic von sich aus, sobald ein Objekt dieses Typs auf die Entwurfsfläche gezogen wird. Wie aber sieht die Bearbeitungsreihenfolge aus, wenn sich Ereignisse häufen? Das Laufzeitsystem entnimmt die Ereignisse einer Ereigniswarteschlange unter Beachtung verschiedener Priorisierungsregeln (so kommen Timer- und MouseMove-Ereignisse beispielsweise nur zur Behandlung, wenn keine anderen Ereignisse warten) und ruft die dafür vorgesehenen Ereignisroutinen nacheinander auf. Mit anderen Worten: solange die Bearbeitung eines Ereignisses andauert, kommt kein weiteres Ereignis zum Zuge. Tipps
................................................... Tipps Bei der Implementierung neuer Ereignisse empfiehlt es sich, das Ereignis zuerst im Modul des signalisierenden Objekts zu deklarieren (Event) und sich dann im Modul des Objekts, das auf das Ereignis reagieren soll, das Gerüst der Ereignisroutine von Visual Basic einfügen zu lassen. Die Reaktivität eines Programms hängt sehr stark davon ab, wie viel an Laufzeit eine Ereignisroutine für die Behandlung des ihm zugeordneten Ereignisses benötigt. Guter Programmierstil ist es, nicht zu viel in eine Ereignisroutine hineinzupacken. Sollten aufwändigere Berechnungen anfallen, tut die Routine gut daran, zwischendurch immer mal wieder die Prozedur/Funktion DoEvents aufzurufen, damit die Behandlung inzwischen aufgelaufener Ereignisse erfolgen kann. Sobald die Warteschlange abgearbeitet ist, erhält die Routine, die den DoEvents-Aufruf abgesetzt hat, die Kontrolle wieder zurück und kann ihre Aufgabe zu Ende bringen. So gut das klingt, problemfrei ist diese Programmiertechnik nicht. Sie wirft im Wesentlichen zwei Probleme auf: Reentranz und Rekursion. Mit Ersterem ist gemeint, dass eine derartige Routine erneut zum Aufruf kommen kann, noch bevor sie fertig ist, und mit Letzerem, dass die Routine ihrerseits Ereignisse anstoßen kann, die wiederum zum Aufruf ihrer selbst führen, noch bevor sie fertig ist. Liegen Rekursion und Reentranz vor, wird das früher oder später zur Auslösung eines Stack-Überlauffehlers führen, wenn die Routine – vergleichbar mit einer Endlosschleife – in der Regel öfter betreten als verlassen wird. Rekursion alleine ist kein Problem, wenn gewährleistet ist, dass die Routine fertig ist, bevor das von ihr angestoßene Ereignis wieder zu ihrem Aufruf führt – mithin, wenn die Latenzzeit des Ereignisses größer ist als die Ausführungszeit. Reentranz für sich genommen ist auch kein Problem, solange sie nicht zur Regel wird und zu einer immer tieferen Verschachtelung der Aufrufe führt. Allerdings muss die Routine speziell darauf ausgelegt sein, damit es nicht zu unerwünschten Seiteneffekten kommt, etwa im Zusammenhang mit statischen und globalen Variablen. Um der Kumulation reentranter Aufrufe vorzubeugen, empfiehlt es sich, einen Rekursionszähler (statische Zählvariable) einzuführen und den DoEvents-Aufruf mit dem Zählerstand zu koppeln. Einer Rekursion beugt das zwar nicht vor, einem Stacküberlauf jedoch schon – gleichwohl wird die Anwendung weiterhin auf den Benutzer reagieren, auch wenn es zu Beeinträchtigungen der Reaktivität kommen kann. Beispiel
................................................... Beis piel
Das Projekt FormMitEreignis stellt eine Wrapper-Klasse für ein Formularobjekt des Typs BasisForm vor. Ein Objekt dieser Klasse, das eine Formularvariable als Eigenschaft besitzt, ergänzt die Funktionalität des dieser Variablen zugeordneten Formulars um die Behandlung eines zusätzliches Ereignisses namens FeldwertGeändert. Das Formular BasisForm enthält zwei Textfelder namens Text1 und Text2 sowie eine Statusleiste namens StatusLeiste1. Die Signali-
206
Standardereignisse
sierung des Ereignisses erfolgt, wenn sich der Wert eines der Textfelder ändert. Die für die Behandlung des Ereignisses zuständige Methode des Wrapper-Objekts ist mBasisForm_FeldwertGeändert. ' Klasse: clsFormMitEreignis Option Explicit Private WithEvents mBasisForm As BasisForm ' Formular ist Eigenschaft
Standardereignisse
' Eigenschaft veröffentlichen Public Property Get BasisForm() As BasisForm Set BasisForm = mBasisForm ' Wert der Eigenschaft bekannt geben End Property Public Property Set BasisForm(ByVal newBasisForm As BasisForm) Set mBasisForm = newBasisForm ' Wert der Eigenschaft setzen End Property ' (Formularobjekt zuordnen) ' Von mBasisForm signalisiertes Ereignis behandeln Private Sub mBasisForm_FeldwertGeändert() mBasisForm.StatusLeiste1.SimpleText = "Feldwert hat sich geändert" End Sub ' Formular: BasisForm Option Explicit Event FeldwertGeändert() ' Signalisiertes Ereignis deklarieren Private obj As New clsFormMitEreignis ' Objektvariable vereinbaren Private Sub Command1_Click() End End Sub
' Schaltfläche beendet Programm ' (Objekt wird automatisch abgebaut)
Private Sub Form_Load() StatusLeiste1.Style = sbrSimple ' Stilattribut der Statusleiste Set obj.BasisForm = Me ' Objekt generieren, als Wert der End Sub ' Eigenschaft sich selbst setzen Private Sub Text1_Change() RaiseEvent FeldwertGeändert End Sub
' Textfeld löst Ereignis aus
Private Sub Text2_Change() RaiseEvent FeldwertGeändert End Sub
' Textfeld löst Ereignis aus
Standardereignisse Ein Visual-Basic-Programm, das nicht nur aus Standardmodulen besteht, wird von Beginn an in eine »ereignisreiche Welt hineingeboren«. Als ereignisorientierte Systemumgebung hält Windows die in Ausführung befindlichen Programme ständig über alle sie betreffenden Vorkommnisse auf dem Laufenden – dazu zählen: Informationen über den eigenen Ausführungszustand (Programmstart, Programmende, Herunterfahren des Systems etc.), über geänderte Systembe-
207
Standardereignisse
dingungen (Bildschirmmodus, Sprachtreiber etc.) und natürlich über alle an sie gerichteten Benutzeraktionen. Darüber hinaus melden sich Objekte auch gegenseitig die verschiedensten Vorkommnisse oder leiten Meldungen einfach weiter (Delegation). Wer von der prozeduralen Programmierung her kommt, für den ist es erst einmal ungewohnt, sich in diesem Kanon von Ereignissen zurechtzufinden, weil der Programmablauf als Ganzes nicht mehr so ohne Weiteres ersichtlich ist. Vielmehr zerfällt das Programm in eine Fülle von Ereignisroutinen, deren Koordination »hinter den Kulissen« vom Visual-Basic-Laufzeitsystem betrieben wird, ohne dass vordergründig etwas zu sehen ist. Letztlich machen die Ereignisse die Programmierung aber auch wieder einfach, da man sich um die Ablaufsteuerung keine großen Gedanken machen muss.
Standardereignisse
Beschreibung
................................................... Bes c hreibung
Die folgende Tabelle gibt einen Überblick über die wichtigsten vordefinierten Ereignisse, die ein Objekt in Abhängigkeit von seinem Objektdatentyp zu Gesicht bekommen kann. Details zu den einzelnen Ereignissen finden Sie in den anschließenden Unterabschnitten. Beachten Sie, dass die Liste nicht vollständig ist, da jedes Steuerelement und jeder Objektdatentyp seine eigenen Ereignisse definieren kann. Schlagen Sie dazu unter dem jeweiligen Steuerelement bzw. Objektdatentyp nach. Ereignis
Beschreibung
Betroffene Objekte
Activate
Signalisiert, dass das Fenster des Objekts zum aktiven Fenster wird
Form, DataReport, MDI-Form
Click
Signalisiert die Ausführung eines Mausklicks im Fensterbereich des Objekts
Die meisten Steuerelemente, Form, MDI-Form, UserControl, UserDocument, PropertyPage
DblClick
Signalisiert die Ausführung eines Doppelklicks im Fensterbereich des Objekts
Die meisten Steuerelemente, Form, MDI-Form, UserControl, UserDocument, PropertyPage
Deactivate
Signalisiert, dass das Fenster des Objekts nicht mehr aktives Fenster ist
Form, DataReport, MDI-Form
DragDrop
Signalisiert dem Objekt das Ende einer Drag&Drop-Operation: Ein anderes Objekt wurde in den Bereich des Objekts abgelegt.
Die meisten Steuerelemente, Form, MDI-Form, UserControl, UserDocument, PropertyPage
DragOver
Tritt während einer Drag&DropOperation auf und signalisiert dem Objekt, dass ein anderes Objekt in seinen Bereich gezogen wird
Die meisten Steuerelemente, Form, MDI-Form, UserControl, UserDocument, PropertyPage
GotFocus
Signalisiert dem Objekt, dass es den Die meisten Steuerelemente, Form, Eingabefokus erhalten hat MDI-Form, UserControl, UserDocument, Extender, PropertyPage
Initialize
DataReport, DHTMLPageDesigner, Signalisiert dem Objekt, dass es soeben generiert wurde und den ein- Form, MDIForm, PropertyPage, Usermaligen Initialisierungscode ausfüh- control, UserDocument, WebClass ren kann (beim Formularobjekt kommt Initialize vor Load)
208
Standardereignisse
Beschreibung
Betroffene Objekte
KeyDown
Signalisiert dem Objekt (i.a. dem, das den Fokus besitzt) das Niederdrücken einer beliebigen Taste der Tastatur
Die meisten Steuerelemente, Form, MDIForm, PropertyPage, UserControl, UserDocument
KeyPress
Die meisten Steuerelemente, Form, Signalisiert dem Objekt (i.a. dem, das den Fokus besitzt), dass ein Zei- MDIForm, PropertyPage, UserControl, chen mit ANSI-Code über die Tasta- UserDocument tur eingegeben wurde
KeyUp
Die meisten Steuerelemente, Form, Signalisiert dem Objekt (i.a. dem, das den Fokus besitzt) das Loslassen MDIForm, PropertyPage, UserControl, einer beliebigen Taste der Tastatur UserDocument
LinkClose
Signalisiert dem Objekt das Ende einer DDE-Verbindung
Label, PictureBox, Form, MDIForm, TextBox
LinkError
Signalisiert dem Objekt das Auftreten eines Fehlers während einer DDE-Verbindung
Label, PictureBox, Form, MDIForm, TextBox
LinkExecute
Signalisiert dem Objekt eine DDEKommandozeichenfolge während einer DDE-Verbindung
Label, PictureBox, Form, MDIForm, TextBox
LinkOpen
Signalisiert dem Objekt, dass eine Zielanwendung eine DDE-Verbindung eröffnen will
Label, PictureBox, Form, MDIForm, TextBox
Load
signalisiert dem Objekt, dass es soeben geladen wurde
Form, MDIForm, PropertyPage
LostFocus
Signalisiert dem Objekt, dass es den Die meisten Steuerelemente, Form, Fokus verloren hat MDI-Form, UserControl, UserDocument, Extender, PropertyPage
MouseDown
Signalisiert dem Objekt das Nieder- Die meisten Steuerelemente, Form, drücken einer Maustaste MDI-Form, UserControl, UserDocument, PropertyPage
MouseMove
Signalisiert dem Objekt, dass die Maus über ihm bewegt wurde
Die meisten Steuerelemente, Form, MDI-Form, UserControl, UserDocument, PropertyPage
MouseUp
Signalisiert dem Objekt das Loslassen einer Maustaste
Die meisten Steuerelemente, Form, MDI-Form, UserControl, UserDocument, PropertyPage
OLECompleteDrag
Signalisiert der Quellkomponente einer OLE-Drag&Drop-Operation die Durchführung der Operation
Die meisten Steuerelemente, Form, MDI-Form, UserControl, UserDocument, PropertyPage
OLEDragDrop
Signalisiert der Zielkomponente einer OLE-Drag&Drop-Operation den Abschluss der Aktion
Die meisten Steuerelemente, Form, MDI-Form, UserControl, UserDocument, PropertyPage
209
Standardereignisse
Ereignis
Standardereignisse
Standardereignisse
Ereignis
Beschreibung
Betroffene Objekte
OLEDragOver
Tritt während einer OLEDie meisten Steuerelemente, Form, Drag&Drop-Operation auf und sig- MDI-Form, UserControl, UserDocunalisiert der Zielkomponente das ment, PropertyPage Betreten oder Verlassen ihres Bereichs
OLEGiveFeedback
Signalisiert der Quellkomponente Die meisten Steuerelemente, Form, während einer OLE-Drag&DropMDI-Form, UserControl, UserDocuOperation, dass sie gerade über eine ment, PropertyPage Zielkomponente gezogen wird und dort das OLEDragOver-Ereignis ausgelöst hat
OLESetData
Die meisten Steuerelemente, Form, Signalisiert der Quellkomponente nach Annahme einer OLEMDI-Form, UserControl, UserDocuDrag&Drop-Operation die Art des ment, PropertyPage von der Zielkomponente erwünschten Datenformats
Paint
Signalisiert dem Objekt, seinen Fensterinhalt neu zu zeichnen
QueryUnload
Signalisiert dem Objekt, dass es ent- Form, MDI-Form, PropertyPage laden werden soll – was es aber ablehnen kann.
Resize
Signalisiert dem Objekt Änderungen PictureBox, CoolBar, DataReport, seiner Fenstergröße Data, Form, MDIForm, OLE, UserControl, UserDocument
Terminate
Signalisiert dem Objekt, dass es abgebaut wird, weil keine Verweise mehr auf es existieren (folgt i.a. auf Unload)
Unload
Signalisiert dem Objekt, dass es ent- Form, MDI-Form, PropertyPage laden wird und seinen Aufräumcode ausführen kann; Ablehnen ist nicht möglich (folgt auf QueryUnload)
Validate
Signalisiert einem Objekt, dass es dabei ist, den Fokus zu verlieren, und die Benutzereingabe auf ihre Gültigkeit hin prüfen soll
Die meisten Steuerelemente, Form, MDI-Form, UserControl, UserDocument, PropertyPage
DataReport, DHTMLDesigner, Form, MDIForm, PropertyPage, UserControl, UserDocument, WebClass
Steuerelemente, die den Fokus erhalten können
Anwendung
................................................... Anwendung
Der Programmierer kann sich darauf verlassen, dass sein Programm in bestimmten Situationen bestimmte Ereignisse übermittelt bekommt, was letztlich einer Ablaufsteuerung gleichkommt. So sieht das Ablaufmodell eines Formularobjekts vor, dass dieses im Zuge seiner Entstehung zuerst das Initialize-, dann das Load- und schließlich das Resize-Ereignis zu sehen bekommt. Was an weiteren Ereignissen eintrudelt, hängt von der Aufrufsituation und den Benutzer-
21 0
Standardereignisse
Beispiel
................................................... Beis piel
Das folgende Programm verdeutlicht die Ereignisreihenfolge für den Aufruf und Abbau eines Formularobjekts: ' Formularmodul zum Testen der Ereignisreihenfolge Option Explicit Private Sub Form_Activate() Debug.Print "Activate" End Sub Private Sub Form_Deactivate() Debug.Print "Deactivate" End Sub Private Sub Form_GotFocus() Debug.Print "GotFocus" End Sub Private Sub Form_Initialize() Debug.Print "Initialize" End Sub Private Sub Form_Load() Debug.Print "Load" End Sub Private Sub Form_LostFocus() Debug.Print "LostFocus" End Sub Private Sub Form_Paint() Debug.Print "Paint" End Sub Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer) Debug.Print "QueryUnload"
21 1
Standardereignisse
aktionen ab. Die drei letzten Ereignisse, die ein Formular vor dem Ableben zu sehen bekommt, sind QueryUnload, Unload und Terminate. Das Schöne an Visual Basic ist, dass man sich nicht viele Gedanken darüber machen muss, in welchem Rahmen mit welchen Ereignissen zu rechnen ist (über die Ereignisse selbst sollte man sich natürlich schon Gedanken machen). Der Code-Editor ist intelligent genug, für jedes auf Modulebene bekannte WithEvents-Objekt sowie für das dem Modul an sich untergelegte Objekt genau die Ereignisse anzubieten, die es signalisiert bekommt. Was nicht im Angebot ist, muss entweder – wie im Falle von Benutzersteuerelement- oder Klassenmodulen – erst aufseiten des Objektdatentyps implementiert oder irgendwie anderweitig gelöst werden. Programmierer, die es gewöhnt sind, mit dem Source Development Kit (SDK) oder der Microsoft-Klassenbibliothek (MFC) zu programmieren, werden mit Sicherheit so manche Windows-Nachricht vermissen, die Visual Basic nicht in ein Ereignis ummünzt, sondern einfach unter den Tisch kehrt. Man kann mit Visual Basic zwar viel machen, aber eben nicht alles.
Standardereignisse
End Sub Private Sub Form_Resize() Debug.Print "Resize" End Sub
Standardereignisse
Private Sub Form_Terminate() Debug.Print "Terminate" End Sub Private Sub Form_Unload(Cancel As Integer) Debug.Print "Unload" End Sub
Wird das Formular sofort nach dem Erscheinen wieder geschlossen, lautet die Ausgabe:
Ereignisprotokoll für Programmstart und Programmende
Wie man sieht, tritt weder ein LostFocus- noch ein Deactivate-Ereignis auf. Aber auch das GotFocus-Ereignis trifft nur dann ein, wenn sich auf dem Formular kein Steuerelement befindet, das den Fokus annehmen kann. Und das Paint-Ereignis tritt nur auf, wenn die Eigenschaft AutoRedraw des Formulars auf False gesetzt ist.
Activate- Ereignis und Deactivate- Ereignis Sub Objekt_Activate () Sub Objekt_Deactivate () Betroffene Objekte
................................................... Betro ffene Objekte Form, MDIForm, DataReport Beschreibung
................................................... Bes c hreibung
Das Activate-Ereignis geht dem GotFocus-Ereignis voraus und signalisiert, dass das Fenster des Objekts das aktive Fenster der Anwendung geworden ist. Es tritt nur auf, wenn die VisibleEigenschaft des Objekts auf True gesetzt und bereits ein Show-Aufruf erfolgt ist. Weiterhin tritt es nur auf, wenn der Fokus innerhalb der Anwendung auf ein anderes Fenster verschoben wird. Erhält das Fenster den Fokus von einer anderen Anwendung zurück, bleibt das Ereignis aus. Umgekehrt signalisiert das Deactivate-Ereignis, dass das Fenster des Objekts den Fokus an ein anderes Fenster der Anwendung abgegeben hat. Dabei folgt es unmittelbar auf das LostFocusEreignis. LostFocus und Deactivate bleiben aus, wenn das Objekt und sein Fenster abgebaut werden.
21 2
Activate- Ereignis und Deactivate- Ereignis
Anwendung
................................................... Anwendung
Die Behandlung des Activate-Ereignisses kann sinnvoll sein, wenn das Objekt den Fokus gleich an ein anderes Fenster weitergeben soll. Deactivate kann dagegen ausgenutzt werden, um die Aktivierung in einer bestimmten Reihenfolge zu erzwingen. Hinweis
................................................... Hinweis
Beispiel
................................................... Beis piel
Der folgende Code stellt ein Formular vor, das keine weiteren Steuerelemente enthält, und auf ein Click-Ereignis hin eine weitere Instanz seiner selbst zur Anzeige bringt. Die Ereignisse Activate, Deactivate, GotFocus und LostFocus werden im Direktfenster protokolliert. ' Formularmodul zum Testen der Aktivierungsreihenfolge Option Explicit Dim f As New Form1 Private Sub Form_Load() AutoRedraw = True End Sub Private Sub Form_Click() f.Show End Sub Private Sub Form_Activate() Debug.Print "Activate" End Sub Private Sub Form_Deactivate() Debug.Print "Deactivate" End Sub Private Sub Form_GotFocus() Debug.Print "GotFocus" End Sub Private Sub Form_LostFocus() Debug.Print "LostFocus" End Sub
Startet man das Programm, klickt einmal auf das Formular und beendet dann das Programm durch Schließen beider Fenster, sieht die Ausgabe so aus:
21 3
Standardereignisse
Zeigt ein Formular aus einer von Visual Basic generierten Exe-Datei heraus ein Dialogfeld an, das aus einer gleichfalls mit Visual Basic generierten DLL stammt, erhält das Formular regulär die Ereignisse Deactivate und LostFocus. Das Deactivate-Ereignis kann jedoch in bestimmten Konstellationen auch ausbleiben: wenn das Objekt eine prozessexterne Komponente ist; wenn das Dialogfeldobjekt nicht in Visual Basic programmiert wurde; wenn der Aufruf der DLL innerhalb der Entwicklungsumgebung erfolgt.
Standardereignisse
Standardereignisse
Ereignisprotokoll für die Aktivierung eines Fensters Verwandte Ereignisse
................................................... Verwandte Ereignis s e GotFocus, LostFocus
Change- Ereignis Sub Objekt_Change([Index As Integer]) Betroffene Objekte
................................................... Betro ffene Objekte ComboBox, DataCombo, DataGrid, DateTimePicker, DirListBox, DriveListBox, FlatScrollBar, HScrollBar, ImageCombo, Label, MaskEdBox, PictureBox, RichTextBox, Slider, TextBox, ToolBar, UserControl, UserDocument, UpDown, VScrollBar Beschreibung
................................................... Bes c hreibung
Das Change-Ereignis wird ausgelöst, wenn sich der Standardwert des Steuerelements geändert hat. Die genaue Ursache für das Ereignis hängt von der Natur des einzelnen Steuerelements ab: Beim Textfeld und Kombinationsfeld ist es die Änderung des Werts nach Eingabe eines weiteren Zeichens, bei Bildlaufleisten eine Positionsänderung der Bildlaufmarke und bei listenfeldbasierten Steuerelementen die Auswahl eines Werts durch den Benutzer. Das Ereignis wird auf jeden Fall auch dann ausgelöst, wenn der Wert der Standardeigenschaft explizit durch Code gesetzt wird. Anwendung
................................................... Anwendung
Das Change-Ereignis eignet sich gut dafür, mehrere voneinander abhängige Steuerelemente zu synchronisieren, so etwa die bekannte Kaskadensteuerung der Steuerelemente DriveListBox, DirListBox und FileListBox (vgl. »Laufwerklistenfeld-Steuerelement (DriveListBox)«, S. 410). Warnung
................................................... Wa rnung
Die leichtfertige Verwendung des Change-Ereignisses kann zur Auflösung von Ereignislawinen führen, wenn sich zwei Steuerelement gegenseitig oder mehrere Steuerelemente zyklisch aktualisieren. Ein häufiges Problem im Zusammenhang mit der Behandlung dieses Ereignisses ist auch der rekursive Aufruf, der entsteht, wenn die Routine ihren eigenen Standardwert zu ändern versucht. Beispiel
................................................... Beis piel
Der folgende Code ist reentrant, weil die hier nicht näher ausgeführte Routine LookupFullValue den Wert des Textfelds vervollständigt, wenn sie in ihrer Datenbasis einen passenden Wert findet. Da das ein weiteres Change-Ereignis auslöst, kommt es zum Wiedereintritt in die Routine, wobei die statische Variable Reenter den erneuten Aufruf von LookupFullValue verhindert. Verzichtet man auf die statische Variable, muss die Routine LookupFullValue so implementiert sein,
21 4
Click- Ereignis
dass sie den Wert des Textfelds nicht ändert, wenn der gefundene Eintrag bereits vollständig eingegeben wurde. In diesem Fall bricht die Rekursion dann spätestens nach dem zweiten Aufruf ab.
' Wert ggf. vervollständigen
Click- Ereignis Sub MDIForm_Click() Sub Form_Click() Sub Objekt_Click([Index As Integer]) Betroffene Objekte
................................................... Betro ffene Objekte Animation, CheckBox, ComboBox, CommandButton, CoolBar, DataGrid, DBCombo, DBGrid, DBList, DirListBox, FileListBox, Form, Frame, Grid, Image, Label, ListBox, ListView, MDIForm, Menu, OLE, OptionButton, PictureBox, ProgressBar, PropertyPage, RichTextBox, Slider, SSTab, StatusBar, TabStrip, TextBox, ToolBar, TreeView, UserControl, UserDocument Beschreibung
................................................... Bes c hreibung
Das Click-Ereignis wird ausgelöst, wenn der Benutzer über einem (nicht gesperrten) Steuerelement oder in den Client-Bereich eines Formulars einen Mausklick ausführt. Falls das Objekt in der Lage ist, den Fokus zu erhalten und diesen noch nicht besitzt, geht dem Click-Ereignis das GotFocus-Ereignis voran. Form-Objekte sowie die meisten Steuerelemente signalisieren das Ereignis bei rechtem oder linkem Mausklick. Objekte des Typs CheckBox, CommandButton, ListBox und OptionButton beachten dagegen nur die linke Maustaste. Zudem gibt es eine Reihe von Situationen, in denen Steuerelemente das Click-Ereignis auch unabhängig von einer Betätigung der Maustaste signalisieren. So, wenn: ●
●
●
●
●
●
der Benutzer in einem ComboBox- oder ListBox-Objekt per Tastatur oder Pfeilschaltfläche ein anderes Element auswählt der Benutzer die Leertaste drückt und ein OptionButton-, CommandButton- oder CheckBoxObjekt gerade den Fokus hat der Benutzer die Eingabetaste drückt und ein CommandButton-Objekt existiert, dessen Default-Eigenschaft auf True gesetzt ist der Benutzer die Taste (Esc) drückt und ein CommandButton-Objekt existiert, dessen Cancel-Eigenschaft auf True gesetzt ist der Benutzer die Zugriffstaste ((Alt) + unterstrichenes Zeichen in der Beschriftung) für ein Steuerelement drückt sich die Value-Eigenschaft eines Steuerelements ändert und diese den Typ Boolean trägt
Der Parameter Index gibt den Index des signalisierenden Steuerelements wieder, wenn es Element eines Steuerelemente-Arrays ist.
21 5
Standardereignisse
Private Sub Text1_Change() Static Reenter As Boolean If Not Reenter Then Reenter = True LookupFullValue Text1 Reenter = False End If End Sub
Standardereignisse
Anwendung
................................................... Anwendung
Das Click-Ereignis wird entweder als Auslöser für einen beliebigen Befehl (CommandButton) genutzt oder für das Treffen von Einstellungen, die das Verhalten der Anwendung in der einen oder anderen Weise in einem komplexeren Zusammenhang nachhaltig verändern (CheckBox, OptionButton). Warnung
Standardereignisse
................................................... Wa rnung
Das Click-Ereignis wird auch bei einem Doppelklick signalisiert und geht dem DblClick-Ereignis grundsätzlich voraus. Falls die Click-Behandlungsroutine jedoch ein Objekt mit eigener Nachrichtenschleife (Formular oder Dialog: beispielsweise Msgbox) aufruft, kommt es trotz Doppelklicks nicht zu einem DblClick-Ereignis! Tipp
................................................... Tipp
Das Click-Ereignis beinhaltet keinerlei Informationen darüber, wo der Mausklick aufgetreten ist und welche Maustaste dafür benutzt wurde. Wenn diese Informationen für die Programmlogik eine Rolle spielen, muss eines der Ereignisse MouseDown oder MouseUp, die unmittelbar vor bzw. nach Click auftreten, ausgewertet werden. Beispiel
................................................... Beis piel
Der folgende Code stellt ein Formular vor, das die Ereignisse MouseDown, MouseUp, Click und DblClick im Direktfenster protokolliert. ' Formularmodul zum Testen der Mausereignisreihenfolge Option Explicit Private Sub Form_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single) Debug.Print "MouseUp" End Sub Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single) Debug.Print "MouseDown" End Sub Private Sub Form_Click() Debug.Print "Click" End Sub Private Sub Form_DblClick() Debug.Print "DblClick" End Sub
Die Ereignisfolge für einen einfachen Mausklick sieht so aus:
Ereignisprotokoll für das Click-Ereignis
21 6
DblClick- Ereignis
Verwandte Ereignisse
................................................... Verwandte Ereignis s e MouseDown, MouseUp, MouseMove, DblClick
DblClick- Ereignis Sub MDIForm_DblClick() Sub Form_DblClick() Sub Objekt_DblClick([Index As Integer]) Betroffene Objekte
Animation, ComboBox, DataGrid, DBCombo, DBGrid, DBList, FileListBox, Form, Frame, Grid, Image, Label, ListBox, ListView, MDIForm, Menu, OLE, OptionButton, PictureBox, PropertyPage, StatusBar, TextBox, ToolBar, TreeView, UserControl, UserDocument Beschreibung
................................................... Bes c hreibung
Das DblClick-Ereignis wird ausgelöst, wenn der Benutzer über einem (nicht gesperrten) Steuerelement oder in den Client-Bereich eines Formulars doppelklickt. Form-Objekte sowie die meisten Steuerelemente signalisieren das Ereignis bei rechtem oder linkem Mausklick. Objekte des Typs ListBox und OptionButton beachten dagegen nur die linke Maustaste. ComboBox-Objekte signalisieren einen Doppelklick nur, wenn sie die Form eines einfachen Kombinationsfelds haben, das heißt, wenn die Eigenschaft Style den Wert 1 hat. Der Parameter Index gibt den Index des signalisierenden Steuerelements wieder, wenn es Element eines Steuerelemente-Arrays ist. Anwendung
................................................... Anwendung
Der Doppelklick hat für grafisch orientierte Benutzerschnittstellen traditionell die Bedeutung der Auswahl eines Elements mit gleichzeitiger Bestätigung der Standardaktion. Da dem DblClick-Ereignis immer ein Click-Ereignis vorausgeht, überlässt man die Elementauswahl üblicherweise der Click-Behandlungsroutine und reagiert auf das DblClick-Ereignis nur mit der Bestätigung, beispielsweise durch Aufruf der Click-Routine der Standardschaltfläche (CommandButton-Objekt, dessen Default-Eigenschaft auf True gesetzt ist). Tipp
................................................... Tipp
Um zu erfahren, ob ein linker oder rechter Doppelklick erfolgt ist und an welcher Position er aufgetreten ist, können Sie die Parameter der Ereignisse MouseDown oder MouseUp, die jedem DblClick-Ereignis vorangehen, auswerten. Warnungen
................................................... Wa rnungen Ein Doppelklick ist nur dann ein Doppelklick, wenn der Benutzer mit derselben Maustaste innerhalb der in der Systemsteuerung festgelegten Zeit zweimal klickt. Wird diese Zeit überschritten, signalisiert das betreffende Objekt zweimal hintereinander die Ereignisfolge für das Click-Ereignis. Man sollte daher bei der Gestaltung der Logik der Benutzerschnittstelle darauf achten, dass dies keine Probleme bereitet. So wäre es beispielsweise wenig sinnvoll, eine Löschoperationen an das Click-Ereignis zu binden, während das DblClick-Ereignis eine Auswahl mit impliziter Bestätigung trifft. Falls die Behandlungsroutine für das Click-Ereignis ein Formular oder einen Dialog (beispielsweise mit Msgbox) aufruft, kommt es trotz Doppelklicks nicht zu einem DblClick-Ereignis.
21 7
Standardereignisse
................................................... Betro ffene Objekte
Standardereignisse
Beispiel
................................................... Beis piel
Standardereignisse
Die Ereignisfolge für einen Doppelklick sieht so aus (Programmcode vgl. »Click-Ereignis«, S. 215):
Ereignisprotokoll für das DblClic k- Ereignis
Verwandte Ereignisse
................................................... Verwandte Ereignis s e MouseDown, MouseMove, MouseUp, Click
DragDrop- Ereignis Sub MDIForm_DragDrop(Source As Control, X As Single, Y As Single) Sub Form_DragDrop(Source As Control, X As Single, Y As Single) Sub Objekt_DragDrop([Index As Integer], Source As Control, _ X As Single, Y As Single) Betroffene Objekte
................................................... Betro ffene Objekte Adodc, Animation, CheckBox, ComboBox, CommandButton, Coolbar, Data, DataGrid, DataList, DataRepeater, DateTimePicker, DBCombo, DBGrid, DBList, DirListBox, DriveListBox, FileListBox, FlatScrollBar, Form, Frame, Grid, HScrollBar, Image, ImageCombo, Label, ListBox, ListView, MaskEdBox, MDIForm, MMControl, MonthView, MSFlexGrid, MSHFlexGrid, OLE, OptionButton, PictureBox, ProgressBar, PropertyPage, RemoteData, RichTextBox, Slider, StatusBar, TabStrip, TextBox, ToolBar, TreeView, UpDown, UserControl, UserDocument, VScrollBar, WebBrowser Beschreibung
................................................... Bes c hreibung
Das DragDrop-Ereignis signalisiert den Abschluss einer Drag&Drop-Operation – ein Vorgang, bei dem ein als Quellobjekt bezeichnetes Steuerelement (Source) mit Hilfe der Maus über ein anderes als Zielobjekt bezeichnetes Steuerelement (Objekt) oder ein Formular (Form, MDIForm) gezogen und dort abgelegt wird. Da das Zielobjekt dabei eine Objektreferenz auf das Quellobjekt erhält, steht es ihm frei, die Aktion nach Belieben zu interpretieren und von den Eigenschaften und Methoden des Quellobjekts Gebrauch zu machen. Die Parameter X und Y drücken die Zielkoordinaten des Steuerelements im Koordinatensystem des jeweiligen Zielobjekts aus. Der Parameter Index gibt den Index des signalisierenden Steuerelements (Zielobjekt) wieder, wenn es Element eines Steuerelemente-Arrays ist. Anwendung
................................................... Anwendung
Im Gegensatz zu OLE-Drag&Drop-Operationen funktionieren Drag&Drop-Operationen nur innerhalb einer Anwendung (bzw. innerhalb ein und derselben Exe-Datei). Bei einer OLE-
21 8
DragDrop- Ereignis
Beispiel
................................................... Beis piel
Das Beispielprojekt DragDrop demonstriert den typischen Ablauf einer programmseitig eingeleiteten Drag&Drop-Operation. Das Formular enthält eine Schalfläche Command1 sowie ein Bildfeld Picture1. Ein Klick auf die Schaltfläche öffnet eine weitere Instanz des Formulars und lädt ein Bild in das Bildfeld. Das Bild kann nun via Drag&Drop zwischen den Bildfeldern der beiden Formulare hin und her verschoben werden. Ein Klick auf die Schaltfläche Command1 der zweiten Instanz öffnet eine dritte Instanz, woraufhin sich das Bild zwischen allen drei Bildfeldern verschieben lässt. Der Code pflegt zudem die DragIcon-Eigenschaft des Quellobjekts, indem er ihm je nach Situation die Symbole DropOK, NoDrop und Dragging zuordnet. Sie stammen aus der umfangreichen zu Visual Basic gehörigen Grafiksammlung \Programme\Microsoft Visual Studio\Common\Graphics. Die folgende Abbildung zeigt die verschiedenen Gesichter des Mauszeigers während einer Drag&Drop-Operation (siehe folgende Abbildung). Für den vorliegenden Code ist es kein Problem, wenn Quell und Zielobjekt übereinstimmen. Das Steuerelement behandelt dann sozusagen die hausgemachten DragOver- und Drag-Ereignisse. (Das ist auch der Grund, warum man sofort nach Beginn der Operation erst einmal das DropOK-Symbol sieht.) ' Projekt: DragDrop-Demo Dim Form2 As New Form1 Public NoDrop As Picture Public Dragging As Picture Public DropOK As Picture
' Ablegen nicht möglich ' Drag&Drop-Operation ' Ablegen möglich
21 9
Standardereignisse
Drag&Drop-Operation handelt es sich um einen dokumentenorientierten Vorgang, der den Datentransport zwischen Objekten (Steuerelementen, Komponenten) über DataObject-Objekte abwickelt, ohne dass das Quellobjekt als solches in Erscheinung tritt. Da sie auch anwendungsübergreifend funktioniert – letztlich war genau dies das Ziel, das zu ihrer Entwicklung geführt hat –, ist ihre Handhabung schwieriger und mit wesentlich mehr Overhead verbunden als die Handhabung der Drag&Drop-Operation, bei der das Quellobjekt offen zu Tage tritt (weil es zum gleichen Prozess gehört). Ein in einer Drag&Drop-Operation befindliches Steuerelement folgt – ohne weiteres Zutun des Programms – als schwebender Umriss den Mausbewegungen. Gelangt der Mittelpunkt des Umrisses in den Fensterbereich eines anderen Steuerelements, beginnt dieses mit der Signalisierung von DragOver-Ereignissen. (Das als Parent-Objekt des Steuerelements fungierende FormObjekt signalisiert somit laufend DragOver-Ereignisse). Ein DragOver-Ereignis übermittelt einen der Aktionscodes vbEnter (0), vbLeave (1), vbOver (2). Eine Behandlungsroutine für das DragOver-Ereignis wird im Allgemeinen die DragIcon-Eigenschaft des Quellobjekts ändern, um dem Benutzer anhand der Mauszeigerform zu bedeuten, ob die Operation von Erfolg gekrönt sein kann oder nicht – zuweilen findet man aber auch das Verhalten, dass das Zielobjekt seine Anzeige invertiert. Damit ein Steuerelement eine Drag&Drop-Operation als Quellobjekt eingeht, muss entweder seine DragMode-Eigenschaft den Wert vbAutomatic (1) aufweisen oder ein expliziter Aufruf seiner Drag-Methode mit Aktionscode vbBeginDrag erfolgen. Im ersteren Fall leitet das Objekt die Operation bei Niederdrücken der Maustaste von sich aus ein, so dass man sich als Programmierer eigentlich nur um die Behandlung des DragDrop- sowie gegebenenfalls des DragOver-Ereignisses auf der Zielseite kümmern muss. Wenn als Quelle auch mehrere Steuerelemente zulässig sein sollen oder die Steuerung des Vorgangs sonstwie durch Programmcode gegeben sein soll (beispielsweise über Tastaturereignisse oder unter Berücksichtigung von Funktionstasten), bleibt nicht anderes übrig, als die Eigenschaft DragMode auf vbManual zu belassen und die Operation explizit mit der Methode Drag unter Angabe des entsprechenden Aktionscodes einzuleiten (vbBeginDrag), zu beenden (vbEndDrag) oder abzubrechen (vbCancel).
Standardereignisse
Standardereignisse
Der Mauszeiger ändert sich während einer Drag&Drop- Operation Private Sub Command1_Click() Picture1 = LoadPicture(App.Path + "\phone.wmf") ' Bild laden Form2.Left = Left + Width ' Nächstes Formular neben dieses Form2.Top = Top Form2.Show End Sub Private Sub Command1_DragOver(Source Static AltesIcon As Picture Select Case State Case vbEnter Set AltesIcon = Source.DragIcon Source.DragIcon = NoDrop Case vbLeave Source.DragIcon = AltesIcon End Select End Sub
As Control, X As Single, Y As Single, State As Integer) ' Aktionscode analysieren ' Altes Symbol speichern ' Ablegen nicht möglich ' Altes Symbol wiederherstellen
Private Sub Form_Unload(Cancel As Integer) Unload Form2 End Sub Private Sub Picture1_DragOver(Source Y As Single, State As Integer) Static AltesIcon As Picture Select Case State Case vbEnter Set AltesIcon = Source.DragIcon Source.DragIcon = DropOK Case vbLeave Source.DragIcon = AltesIcon End Select End Sub
As Control, X As Single, _
' Aktionscode analysieren ' Altes Symbol speichern ' Ablegen möglich ' Altes Symbol wieder herstellen
Private Sub Form_Load() Set NoDrop = LoadPicture(App.Path + "\noDrop02.cur") Set Dragging = LoadPicture(App.Path + "\drag1pg.ico") Set DropOK = LoadPicture(App.Path + "\drop1pg.ico") End Sub
220
DragOver- Ereignis
Private Sub Picture1_MouseDown(Button As Integer, Shift As Integer, _ X As Single, Y As Single) Set Picture1.DragIcon = Dragging ' Symbol für Drag-Operation Picture1.Drag (vbBeginDrag) ' Drag-Operation starten End Sub As Control, X As Single, _ ' ' ' '
Da Drag&Drop auf sich selbst möglich, muss Bildreferenz zwischengespeichert werden!
Standardereignisse
Private Sub Picture1_DragDrop(Source Y As Single) Dim Bild As Picture Set Bild = Source.Picture Set Source.Picture = Nothing Set Picture1 = Bild End Sub Verwandte Ereignisse
................................................... Verwandte Ereignis s e DragOver, OLECompleteDrag, OLEDragDrop, OLEDragOver, OLEGiveFeedBack, OLESetData Verwandte Themen
................................................... Verwandte Them en
Transparenz und Drag&Drop (S. 606)
DragOver- Ereignis Sub MDIForm_DragOver(Source As Control, X As Single, Y As Single, _ State As Integer) Sub Form_DragOver(Source As Control, X As Single, Y As Single, _ State As Integer) Sub Objekt_DragOver([Index As Integer], Source As Control, _ X As Single, Y As Single, State As Integer) Betroffene Objekte
................................................... Betro ffene Objekte Adodc, Animation, CheckBox, ComboBox, CommandButton, Coolbar, Data, DataGrid, DataList, DataRepeater, DateTimePicker, DBCombo, DBGrid, DBList, DirListBox, DriveListBox, FileListBox, FlatScrollBar, Form, Frame, Grid, HScrollBar, Image, ImageCombo, Label, ListBox, ListView, MaskEdBox, MDIForm, MonthView, MSFlexGrid, MSHFlexGrid, OLE, OptionButton, PictureBox, ProgressBar, PropertyPage, RemoteData, RichTextBox, Slider, StatusBar, TabStrip, TextBox, ToolBar, TreeView, UpDown, UserControl, UserDocument, VScrollBar, WebBrowser Beschreibung
................................................... Bes c hreibung
Das DragOver-Ereignis signalisiert, dass der Umriss des als Quellobjekt fungierenden Steuerelements Source im Verlauf einer Drag&Drop-Operation gerade über den Fensterbereich von Objekt (Steuerelement oder Formular) gezogen wird und somit als mögliches Ziel der Operation in Frage kommt. Da Objekt eine Objektreferenz auf das Quellobjekt erhält, steht es ihm frei, die Aktion nach Belieben zu interpretieren und von den Eigenschaften und Methoden des Quellobjekts Gebrauch zu machen – das heißt insbesondere dessen Eigenschaft DragIcon zu manipulieren, um dem Benutzer ein visuelles Feedback über den möglichen Erfolg oder Misserfolg der Operation zu geben. Die Parameter X und Y drücken die aktuellen Koordinaten des Steuerelements im Koordinatensystem von Objekt aus.
221
Standardereignisse
Der Parameter State enthält einen Aktionscode, der über die Tendenz der Operation Aufschluss gibt. Sein Wert ist: vbEnter (0), wenn der Mittelpunkt des Umrisses von Source gerade in den Fensterbereich von Objekt gelangt ist; vbOver (2), wenn der Mittelpunkt des Umrisses von Source im Fensterbereich von Objekt ist und darin bewegt wird; vbLeave (1), wenn der Mittelpunkt des Umrisses von Source den Fensterbereich von Objekt verlässt. Der Parameter Index gibt den Index des signalisierenden Steuerelements (mögliches Zielobjekt der Operation) wieder, wenn es Element eines Steuerelemente-Arrays ist. Anwendung
Standardereignisse
................................................... Anwendung
Ein in einer Drag&Drop-Operation befindliches Steuerelement folgt – ohne weiteres Zutun des Programms – als schwebender Umriss den Mausbewegungen. Gelangt der Mittelpunkt des Umrisses in den Fensterbereich eines anderen Steuerelements, beginnt dieses mit der Signalisierung von DragOver-Ereignissen. (Das als Parent-Objekt des Steuerelements fungierende FormObjekt signalisiert somit laufend DragOver-Ereignisse). Bei der Behandlung des DragOver-Ereignisses wird meist nur zwischen den Aktionscodes vbEnter (0) und vbLeave (1) unterschieden, um die Eigenschaft DragIcon des Quellobjekts geeignet zu setzen. Der Aktionscode vbOver kann dann von Relevanz sein, wenn der Bereich des Steuerelements noch einmal in Unterbereiche aufgeteilt ist. Beispiel
................................................... Beis piel
Die folgende Behandlungsroutine entstammt dem vollständigen Beispiel für das DragDrop-Ereignis. Auf den Aktionscode vbEnter hin speichert die Routine zunächst den Wert von DragIcon des Quellobjekts und ändert dann die Eigenschaft geeignet. Tritt der Aktionscode vbLeave auf, stellt die Routine das ursprüngliche Symbol wieder her. Private Sub Picture1_DragOver(Source Y As Single, State As Integer) Static AltesIcon As Picture Select Case State Case vbEnter Set AltesIcon = Source.DragIcon Source.DragIcon = DropOK ' Source.DragIcon = NoDrop Case vbLeave Source.DragIcon = AltesIcon End Select End Sub
As Control, X As Single, _
' Aktionscode analysieren ' Altes Symbol speichern ' Ablegen möglich ' Ablegen nicht möglich ' Altes Symbol wieder herstellen
Verwandte Ereignisse
................................................... Verwandte Ereignis s e DragDrop, OLECompleteDrag, OLEDragDrop, OLEDragOver, OLEGiveFeedBack, OLESetData
GotFocus- Ereignis und LostFocus- Ereignis Sub MDIForm_GotFocus() Sub Form_GotFocus() Sub Objekt_GotFocus ([Index As Integer]) Sub MDIForm_LostFocus() Sub Form_LostFocus() Sub Objekt_LostFocus ([Index As Integer])
222
GotFocus- Ereignis und LostFocus- Ereignis
Betroffene Objekte
................................................... Betro ffene Objekte Adodc, Animation, CheckBox, ComboBox, CommandButton, DataGrid, DataList, DataRepeater, DateTimePicker, DBCombo, DBGrid, DBList, DirListBox, DriveListBox, FileListBox, Form, Grid, ImageCombo, ListBox, ListView, MaskEdBox, MDIForm, MonthView, MSFlexGrid, MSHFlexGrid, OLE, OptionButton, PictureBox, PropertyPage, RichTextBox, Slider, TabStrip, TextBox, TreeView, UpDown, UserControl, UserDocument, VScrollBar, WebBrowser Beschreibung
................................................... Bes c hreibung
Anwendung
................................................... Anwendung
Beim Show-Aufruf eines Formulars erhält das Steuerelement den Eingabefokus, das unter allen in Frage kommenden den niedrigsten TabIndex-Wert besitzt, sprich: in der Tabulatorreihenfolge möglichst weit am Anfang steht. Den Eingabefokus erhalten können alle Steuerelemente, die den Eingabefokus unterstützen (das Image-Steuerelement tut das beispielsweise nicht) und deren Visible- sowie Enabled-Eigenschaften auf True gesetzt sind. Ist ein Steuerelement im Besitz des Eingabefokus, wird das aus seiner Darstellung ersichtlich: Ein Textfeld zeigt eine blinkende Eingabemarke an, die Beschriftung einer Schaltfläche erhält eine gepunktete Umrahmung usw. Für die Weitergabe des Eingabefokus kommt eine von drei Ursachen in Betracht. Erstens, der Benutzer drückt die Taste (Tab) bzw. die Tastenkombination (Umschalt)+(Tab). In diesem Fall rückt der Eingabefokus unter den in Frage kommenden Steuerelementen entlang der Tabulatorreihenfolge vorwärts bzw. rückwärts. Zweitens, der Benutzer klickt mit der Maus auf ein anderes Steuerelement. Falls das Steuerelement den Eingabefokus annehmen kann, erhält es ihn, ansonsten verbleibt er bei dem Steuerelement, in dessen Besitz er ist. Drittens, der Fokus wird einem Steuerelement programmseitig durch Aufruf der SetFocus-Methode des zugehörigen Objekts explizit zugeordnet. Falls das Steuerelement den Fokus nicht annehmen kann, etwa weil es gesperrt oder nicht sichtbar ist, kommt es zu einem Laufzeitfehler. Geht der Eingabefokus von einem ersten Steuerelement auf ein zweites Steuerelement weiter, signalisieren beide Steuerelemente ein Ereignis: Das erste Steuerelement signalisiert ein LostFocus-Ereignis und das zweite ein GotFocus-Ereignis. Das GotFocus-Ereignis wird in der Regel dazu benutzt, den für die Eingabe erforderlichen Kontext einzustellen, das heißt, den Zustand anderer Steuerelemente zu aktualisieren, wechselseitige Abhängigkeiten mit anderen Steuerelementen zu berücksichtigen oder Vorgabewerte bereitzustellen. Das LostFocus-Ereignis lässt sich dazu benutzen, den Fokus abweichend von der Tabulatorreihenfolge explizit einem bestimmten Steuerelement zuzuschanzen, Aktualisierungen anderer Steuerelemente oder Objekte vorzunehmen, deren Zustände in irgendeiner Form vom Wert des signalisierenden Steuerelements abhängen, oder Daten zurückzuschreiben bzw. anzufordern. Kurz gesagt, die Behandlung des LostFocus-Ereignisses ermöglicht es, den aktuellen Wert des signalisierenden Steuerelements in den Zustand der Anwendung einzuflechten. Ist die CausesValidation-Eigenschaft sowohl des Steuerelements, das den Fokus verliert, als auch des Steuerelements, das den Fokus erhält, auf True gesetzt, geht dem LostFocus-Ereignis
223
Standardereignisse
Das GotFocus-Ereignis signalisiert einem Objekt, dass es den Eingabefokus erhalten, das LostFocus-Ereignis, dass es ihn verloren hat. Der Eingabefokus legt fest, welches Objekt die Tastatureingaben des Benutzers zu sehen bekommt. Mit anderen Worten: Hat ein Objekt den Eingabefokus, erhält es auch die Ereignisse KeyDown usw. Ein Formular erhält den Eingabefokus nur, wenn es keine Steuerelemente enthält, die diesen erhalten könnten. (Nicht alle Steuerelemente unterstützen einen Eingabefokus, und gesperrte sowie unsichtbare Steuerelemente nehmen ihn nicht an.) Der Parameter Index gibt den Index des Steuerelements wieder, das den Fokus erhalten bzw. verloren hat, wenn es Element eines Steuerelemente-Arrays ist.
Standardereignisse
noch das Validate-Ereignis voraus, das eine Gültigkeitsprüfung des Eingabewerts ermöglicht. Zur Weitergabe des Fokus kommt es in diesem Fall nur, wenn der vom Ereignis übermittelte ByRef-Parameter Cancel im Verlauf der Validate-Behandlung nicht auf True gesetzt wird. Warnung
Standardereignisse
................................................... Wa rnung
Vom Prinzip her könnte man auch versuchen, eine Gültigkeitsprüfung im Zuge von LostFocus zu implementieren. Das kann aber Probleme verursachen, da das Steuerelement zu dem Zeitpunkt, an dem das Ereignis auftritt, den Fokus bereits abgegeben hat. Es wäre also ein SetFocus-Aufruf erforderlich, der aber wiederum dem neuen Inhaber des Fokus diesen sofort wieder entreißt und ihm das LostFocus-Ereignis beschert. Falls dieses Steuerelement gleichfalls nicht bereit ist, den Fokus herzugeben und seinerseits einen SetFocus-Aufruf durchführt, kommt es zu einer Ereignisflut, die das System destabilisieren kann. Benutzen Sie für dieses Unterfangen besser das Validate-Ereignis, das speziell auf diesen Zweck ausgelegt ist. Tipp
................................................... Tipp
Um einer anderen Anwendung oder Instanz der gleichen Anwendung den Anwendungsfokus zuzuschanzen, lässt sich die AppActivate-Anweisung verwenden: Sub AppActivate (title As String[, wait As Boolean]) Die Parameter der Anweisung sind benannt. Für title gibt man den Fenstertitel der zu aktivierenden Anwendung vollständig oder nur die ersten paar signifikanten Zeichen an. Hat der optionale Parameter wait den Wert True, erfolgt die Aktivierung der im ersten Parameter spezifizierten Anwendung erst, wenn das Programm, das die AppActivate-Anweisung ausführt, selbst den Anwendungsfokus erhält, ansonsten erfolgt die Fokusweitergabe unabhängig davon, welche der aktuell in Ausführung befindlichen Anwendungen ihn gerade besitzt. Verwandte Ereignisse
................................................... Verwandte Ereignis s e Validate, Activate, Deactivate
Initialize- Ereignis Sub Objekt_Initialize() Betroffene Objekte
................................................... Betro ffene Objekte Class, DataReport, DHTMLPageDesigner, Form, MDIForm, PropertyPage, UserControl, UserDocument, WebClass Beschreibung
................................................... Bes c hreibung
Das Initialize-Ereignis ist das erste Ereignis, das ein frisch generiertes Objekt Objekt zu sehen bekommt. Es geht dem Load-Ereignis voran und gibt dem Objekt die Möglichkeit zur Initialisierung seiner Bestandteile. Anwendung
................................................... Anwendung
Im Gegensatz zum Load-Ereignis kann das Initialize-Ereignis nur einmal im Leben eines Objekts auftreten. Es tritt auf, wenn das Laufzeitsystem eine neue Objektinstanz anlegt, das heißt bei Ausführung einer impliziten oder expliziten New-Anweisung oder bei Aufruf der CreateObject-Funktion. Es eignet sich dafür, Elemente und Eigenschaften des frisch ins Leben gerufenen Objekts zu initialisieren, die keine erneute Initialisierung benötigen, wenn das Fenster des Objekts (Formular oder Steuerelement) zwischendurch entladen wird. Dazu zählen beispiels-
224
KeyDown- Ereignis und KeyUp- Ereignis
weise auf Modulebene deklarierte Variablen. Einem Formularobjekt untergeordnete Steuerelemente sollten ihre Initialisierung im Zuge des Load-Ereignisses erfahren, da sie, wenn eine Unload-Anweisung mit nachfolgender impliziter oder expliziter Load-Anweisung auftritt, zusammen mit dem Formular entladen und wieder geladen werden. Tipp
................................................... Tipp
Kommt das
Load-Ereignis
etwa vor dem
Sub Form_Initialize() AutoRedraw = True Print "Initialize" a = 1 End Sub
Initialize-Ereignis?
' Wenn False oder fehlt, Laufzeitfehler!
Private Sub Form_Load() Print "Load" End Sub
Sieht man sich den Programmablauf aber im Einzelschrittmodus (Taste (F8)) des Debuggers an, stellt man fest, dass das Load-Ereignis im Verlauf von Form_Initialize – nämlich beim Setzen der AutoRedraw-Eigenschaft – getriggert wird. Beispiel
................................................... Beis piel
Private Names() As String Const StartGrößeNames = 10 Sub Form_Initialize() Redim Names(StartGrößeNames) End Sub Verwandte Ereignisse
................................................... Verwandte Ereignis s e Load, Terminate, Unload
KeyDown- Ereignis und KeyUp- Ereignis Sub Form_KeyDown(KeyCode As Integer, Shift As Integer) Sub Objekt_KeyDown([Index As Integer], KeyCode As Integer, _ Shift As Integer)
225
Standardereignisse
Achten Sie darauf, dass ein Objekt bei Eintreffen des Initialize-Ereignisses zwar existiert, aber noch nicht ganz fertig konstruiert ist. Insbesondere lassen sich zu diesem Zeitpunkt noch nicht alle Methoden des Objekts aufrufen. (So kann es beispielsweise in einem Formular mit dem Print-Aufruf Schwierigkeiten geben.) Nicht zuletzt aus diesem Grund wird der Code für die Initialisierung eines Formulars meist in die Behandlungsroutine für das Load-Ereignis gepackt, das unmittelbar nach dem Initialize-Ereignis (bzw. während der Behandlung des InitializeEreignisses) auftritt. Der folgende Code liefert beispielsweise eine zunächst einmal verblüffende Ausgabe:
Standardereignisse
Sub Form_KeyUp(KeyCode As Integer, Shift As Integer) Sub Objekt_KeyUp([Index As Integer], KeyCode As Integer, _ Shift As Integer) Betroffene Objekte
................................................... Betro ffene Objekte
Standardereignisse
CheckBox, ComboBox, CommandButton, DataCombo, DataGrid, DataList, DBCombo, DBGrid, DBList, DirListBox, DriveListBox, FileListBox, Form, Grid, ListBox, ListView, MaskEdBox, MonthView, OLE, OptionButton, PictureBox, PropertyPage, RichTextBox, Slider, TabStrip, TextBox, TreeView, UserControl, UserDocument, VScrollBar Beschreibung
................................................... Bes c hreibung
Die Ereignisse KeyDown und KeyUp werden von dem Steuerelement signalisiert, das den Eingabefokus besitzt. KeyDown zeigt an, dass der Benutzer die Taste mit dem Tastaturcode KeyCode niedergedrückt hat, KeyUp, dass der Benutzer die Taste mit dem Tastaturcode KeyCode wieder losgelassen hat. Der Aufzählungstyp KeyCodeConstants definiert für die meisten Tasten eine eigene Konstante nach dem Muster: vbKey0 für die Taste (0), vbKeyA für die Taste (A), vbKeyF1 für die Taste (F1) usw. Die vollständige Liste finden Sie im Objektkatalog (aufzurufen mit (F2)) der Visual-Basic-Entwicklungsumgebung. Der Parameter Shift ist ein Bitvektor, dessen gesetzte Bits 0 bis 2 (vbShiftMask, vbCtrlMask, vbAltMask) anzeigen, ob die Funktionstasten (Umschalt), (Strg) oder (Alt) bei Auslösung des Ereignisses gedrückt (gesetztes Bit) bzw. nicht gedrückt (gelöschtes Bit) waren. Hat Shift beispielsweise den Wert 5 (vbShiftMask+vbAltMask), bedeutet das, dass die Taste in Kombination mit (Umschalt)+(Alt) niedergedrückt bzw. losgelassen wurde. Der Parameter Index gibt den Index des signalisierenden Steuerelements wieder, wenn es Element eines Steuerelemente-Arrays ist. Anwendung
................................................... Anwendung
Wer sich auf die Verarbeitung von Tastatureingaben auf der Ebene der KeyUp- und KeyDownEreignisse einlässt, beweist Mut zum Detail. Da Steuerelemente, die auf diese Ereignisse reagieren, bereits eine fertige Tastaturschnittstelle mitbringen, die festlegt, wie das Steuerelement auf jede einzelne Taste bzw. Tastenkombination reagiert, besteht wenig Veranlassung für massivere Eingriffe, die über die Ergänzung oder Änderung spezifischer Tastaturbefehle hinausgehen. Bei Implementierung eines Benutzersteuerelements wird man dagegen schon eher in die Verlegenheit geraten, selbst eine Tastaturschnittstelle gestalten zu müssen. Überlegen Sie sich dabei aber gut, was sie wollen, denn nichts führt schneller in des Teufels Küche als eine naiv drauflosprogrammierte Tastaturschnittstelle. Bereits bei der Konzeption der Schnittstelle gibt es einige Dinge zu beachten: Erstens schlucken das Betriebssystem und das Laufzeitsystem von Visual Basic gewisse Tastenkombinationen einfach weg, für die bereits auf höherer Ebene eine Interpretation existiert. Da sind schon einmal die von Windows reservierten Tastenkombinationen für die Zwischenablage ((Strg)+(C), (Strg)+(V), (Strg)+(X), (Strg)+(Einfg), (Strg)+(Entf), (Umschalt)+(Einfg)) sowie die für den Schnellstart von Desktop-Objekten auf dem jeweiligen System vereinbarten Tastenkombinationen. Aufseiten des Formulars kommen dann noch die Zugriffstasten für die Steuerelemente ((Alt) + unterstrichener Buchstabe in der Beschriftung des Steuerelements) und die ShortCut-Tasten für die Menü- und Symbolleistenbefehle ((Ctrl) + Buchstabe) hinzu. Ist bei einem der Steuerelemente auf dem Formular die Eigenschaft Default bzw. Cancel auf True gesetzt (CommandButton, OLE usw.), löst die Eingabetaste bzw. die Taste (Esc) dessen ClickEreignis aus. Nur was da noch übrig bleibt, führt zu einem KeyUp- und/oder KeyDown-Ereignis und lässt sich individuell verarbeiten.
226
KeyDown- Ereignis und KeyUp- Ereignis
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer) Print KeyCode, Shift End Sub Hinweis
................................................... Hinweis Ein Formularobjekt erhält diese Ereignisse nur, wenn keines seiner Steuerelemente den Eingabefokus hat oder wenn die Eigenschaft KeyPreview auf True gesetzt ist. Im letzteren Fall sieht das Formularobjekt jedes Tastaturereignis als Erstes (bis auf spezielle Ereignisse, die einzelne Steuerelemente wegfangen: ein CommandButton-Steuerelement mit der Default-Eigenschaft True fängt beispielsweise die Eingabetaste ab und ein ListBox-Steuerelement die Tasten (Oben) und (Unten)). Sobald das Formularobjekt mit der Behandlung eines Tastaturereignisses fertig ist, kommt die Behandlungsroutine des Steuerelements, das den Fokus besitzt, zum Aufruf – es sei denn, das Formularobjekt setzt den ByRef-Parameter KeyCode auf 0, dann unterbleibt der Aufruf. Dieser Fall gilt analog für alle Objekte, die als Container für Steuerelemente fungieren können und über die KeyPreview-Eigenschaft verfügen (PropertyPage, UserControl, UserDocument). Beispiel
................................................... Beis piel
Das Projekt KeyUpDown zeigt die Implementation einer Tastaturschnittstelle für ein PictureBox-Steuerelement. Das Programm ermöglicht es, das Bildfeld Picture1 mittels der Cursortasten in dem Formular zu bewegen (Pacman lässt grüßen). Private MaxX, MaxY, StepX, StepY Private PosX, PosY Private Sub Form_Load() Picture1 = LoadPicture(App.Path + "\Phone.wmf") StepX = Picture1.Width / 10 StepY = Picture1.Height / 10 MaxX = ScaleWidth – Picture1.Width MaxY = ScaleHeight – Picture1.Height PosX = Picture1.Left PosY = Picture1.Top End Sub Private Sub Picture1_KeyDown(KeyCode As Integer, Shift As Integer) Select Case KeyCode Case vbKeyUp PosY = PosY – StepX
227
Standardereignisse
Zweitens: Der von den Ereignissen gelieferte Wert für den Parameter KeyCode ist mitnichten der ASCII-Code (wie bei dem Ereignis KeyPress), sondern vielmehr der Tastaturcode der gedrückten Taste. Die Unterscheidung zwischen Klein- und Großschreibung ist ebenso anhand des im Parameter Shift gelieferten Bitvektors zu treffen wie die Abfrage der Funktionstasten Strg und Alt. Drückt der Benutzer also die Kombination (Umschalt)+(A), ergibt sich für KeyCode der Wert vbKeyA und für Shift der Wert vbShiftMask. Auch jede Funktionstaste für sich liefert KeyUp- und KeyDown-Ereignisse. Die Tastaturcodes der Tasten (Umschalt), (Strg) und (Alt) sind vbKeyShift, vbKeyCtrl und vbKeyAlt. Eine Unterscheidung zwischen rechts und links gelegenen Tasten gibt es nicht, auch der Bitvektor Shift hilft da nicht weiter. Damit nicht genug: Nach einer gewissen Zeit, setzt die Wiederholfunktion der Tastatur ein, so dass beispielsweise bereits eine etwas länger gehaltene Taste (Umschalt) zu einer Flut von KeyUp- und KeyDown-Ereignissen führt. All das gibt es zu berücksichtigen. Am besten, Sie sehen sich auf einem leeren Formular selbst an, was passiert:
Standardereignisse
Standardereignisse
If PosY < 0 Then PosY = 0 Case vbKeyDown PosY = (PosY + StepY) Mod MaxY Case vbKeyLeft PosX = PosX – StepX If PosX < 0 Then PosX = 0 Case vbKeyRight PosX = (PosX + StepX) Mod MaxX Case vbKeyHome PosX = 0: PosY = 0 Case vbKeyEnd PosX = MaxX: PosY = MaxY End Select Picture1.Left = PosX Picture1.Top = PosY End Sub Verwandte Befehle
................................................... Verwa ndte Befehle
KeyPress, MouseDown, MouseUp
KeyPress- Ereignis Sub Form_KeyPress(KeyAscii As Integer) Sub Objekt_KeyPress([Index As Integer], KeyAscii As Integer) Betroffene Objekte
................................................... Betro ffene Objekte CheckBox, ComboBox, CommandButton, DataCombo, DataGrid, DataList, DBCombo, DBGrid, DBList, DirListBox, DriveListBox, FileListBox, Form, Grid, ListBox, ListView, MaskEdBox, MonthView, OLE, OptionButton, PictureBox, PropertyPage, RichTextBox, Slider, TabStrip, TextBox, TreeView, UserControl, UserDocument, VScrollBar Beschreibung
................................................... Bes c hreibung
Das Ereignis KeyPress wird von dem Steuerelement signalisiert, das den Eingabefokus besitzt. Es zeigt an, dass der Benutzer eine Taste der Tastatur gedrückt hat, die das Zeichen mit dem ANSI-Code KeyAscii generiert. Groß- und Kleinschreibung werden unterschieden. Der Parameter Index gibt den Index des signalisierenden Steuerelements wieder, wenn es Element eines Steuerelemente-Arrays ist. Anwendung
................................................... Anwendung
Wofür sich das KeyPress-Ereignis einsetzen lässt, dürfte klar sein: überall da, wo das konkrete über die Tastatur eingegebene ANSI-Zeichen von Interesse ist. So könnte man etwa auf die Taste (Esc) hin den ursprünglichen Wert eines TextBox-Steuerelements wieder herstellen: Private OldText1 As String Private Sub Text1_GotFocus() OldText1 = Text1 End Sub Private Sub Text1_KeyPress(KeyAscii As Integer) If KeyAscii = vbKeyEscape Then
228
KeyPress- Ereignis
Text1 = OldText1 End If End Sub
Übrigens, zur Menge der ANSI-Zeichen gehören bekanntlich auch die Tastenkombinationen (Strg)+(A) bis (Strg)+(Z) mit den ASCII-Codes 1 bis 26. ((Strg)+(V) und (Strg)+(X) schnappt sich allerdings das Betriebssystem, ebenso (Strg)+(C), wenn ein Bereich markiert ist; weitere Kombinationen können für Menübefehle vereinbart sein.) Hinweis
................................................... Hinweis
Beispiel
................................................... Beis piel
Der folgende einfache Code dokumentiert den Ablauf der drei Ereignisse KeyDown, KeyUp und KeyPress im Direktfenster. Die Abbildung zeigt die Ausgabe für die Tastenfolge (Alt)+(A); (A).
Ausgabe im Direktfenster Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer) Debug.Print "KeyDown"; KeyCode; Shift End Sub Private Sub Form_KeyPress(KeyAscii As Integer) Debug.Print "KeyPress"; KeyAscii; Chr(KeyAscii) End Sub Private Sub Form_KeyUp(KeyCode As Integer, Shift As Integer) Debug.Print "KeyUp"; KeyCode; Shift End Sub Verwandte Ereignisse
................................................... Verwandte Ereignis s e MouseDown, MouseUp, KeyDown, KeyUp
229
Standardereignisse
Ein Formularobjekt erhält dieses Ereignis nur, wenn keines seiner Steuerelemente den Eingabefokus hat oder wenn die Eigenschaft KeyPreview auf True gesetzt ist. Im letzteren Fall sieht das Formularobjekt jedes Tastaturereignis als Erstes (bis auf spezielle Ereignisse, die einzelne Steuerelemente wegfangen: ein CommandButton-Steuerelement mit der Default-Eigenschaft True fängt beispielsweise die Eingabetaste ab und ein ListBox-Steuerelement die Tasten (Oben) und (Unten). Sobald das Formularobjekt mit der Behandlung eines Tastaturereignisses fertig ist, kommt die Behandlungsroutine des Steuerelements, das den Fokus besitzt, zum Aufruf – es sei denn, das Formularobjekt setzt den ByRef-Parameter KeyCode auf 0, dann unterbleibt der Aufruf. Dieser Fall gilt analog für alle Objekte, die als Container für Steuerelemente fungieren können und über die KeyPreview-Eigenschaft verfügen (PropertyPage, UserControl, UserDocument).
Standardereignisse
LinkClose- Ereignis Sub Form_LinkClose() Sub MDIForm_LinkClose() Sub Objekt_LinkClose([Index As Integer]) Betroffene Objekte
................................................... Betro ffene Objekte Form, Label, MDIForm, PictureBox, TextBox
Standardereignisse
Beschreibung
................................................... Bes c hreibung
Das LinkClose-Ereignis tritt auf beiden Seiten einer bestehenden DDE-Verbindung auf, wenn die Verbindung von einer der beiden Seiten beendet wird. Da aufseiten der Quelle das als »Thema« der Verbindung fungierende Objekt (meist ein Formularobjekt) alle Anforderungen bedient, erhält dieses auch das LinkClose-Ereignis, und nicht das in LinkItem spezifizierte Quellobjekt. Der Parameter Index gibt den Index des signalisierenden Steuerelements wieder, wenn es Element eines Steuerelemente-Arrays ist. Anwendung
................................................... Anwendung
Eine DDE-Verbindung ermöglicht die Verknüpfung von Datenfeldern und die Übermittlung von Kommandos zwischen einem Ziel und einer Quelle, die jeweils unterschiedlichen Prozessen (lies: Laufzeitinstanzen von Anwendungen) angehören. Zielobjekt ist ein Steuerelement, Quellobjekt ein Formular. Das Zielobjekt initiiert den Verbindungsaufbau. Bei einer DDE-Verknüpfung liefert gewöhnlich das Quellobjekt den Wert eines seiner Elemente (lies: Steuerelemente) an das Zielobjekt, obwohl eine Aktualisierung auch in der anderen Richtung möglich ist, wenn die Methode LinkPoke zum Einsatz kommt. Ein Zielobjekt kann eine bestehende DDE-Verbindung auch für die Übermittlung von Kommandozeichenfolgen an das Quellobjekt nutzen. Ein Quellobjekt kann mit mehreren Zielobjekten eine DDE-Verbindung unterhalten, ein Zielobjekt jedoch immer nur mit einem Quellobjekt. Eine DDE-Verbindung wird beendet, wenn das Zielobjekt oder das Quellobjekt seine Eigenschaft LinkMode auf vbLinkNone (0) setzt. Das Quellobjekt kann immer nur alle von ihm gehaltenen Verbindungen auf einmal beenden, nicht jedoch gezielt eine einzelne Verbindung zu einem bestimmten Zielobjekt. Die gewöhnliche Reaktion auf das LinkClose-Ereignis besteht darin, die Zustandsvariable für den Verbindungsstatus zu pflegen und gegebenenfalls dem Benutzer die Beendigung der DDEVerbindung mitzuteilen oder sonstwie anzuzeigen. Beispiel
................................................... Beis piel
' im Modul des Zielobjekts Private Text1Verbunden As Boolean ... Text1.LinkMode = vbLinkNone ...
' DDE-Verbindung beenden
Sub Text1_LinkClose() Text1Verbunden = False MsgBox "DDE-Verbindung beendet" End Sub
230
LinkError- Ereignis
LinkError- Ereignis Sub Form_LinkError(LinkErr As Integer) Sub MDIForm_LinkError(LinkErr As Integer) Sub Objekt_LinkError([Index As Integer], LinkErr As Integer) Betroffene Objekte
................................................... Betro ffene Objekte Form, Label, MDIForm, PictureBox, TextBox Beschreibung
Das LinkError-Ereignis signalisiert der Quelle und dem Ziel einer DDE-Verbindung, dass ein Fehler während der DDE-Kommunikation aufgetreten ist. Der Parameter LinkErr übermittelt eine Fehlernummer, die Aufschluss über die Art des Fehlers gibt. Der Aufzählungstyp LinkErrorConstants definiert dafür die folgenden Konstanten: Fehler
Beschreibung
vbWrongFormat (1)
Die andere Anwendung hat Daten im falschen Format angefordert. Dieser Fehler kann mehrmals aufeinander folgend auftreten, während Visual Basic versucht, ein Format zu finden, das von der anderen Anwendung erkannt wird
vbSourceClosed (6)
Das Zielobjekt hat versucht, die Kommunikation fortzusetzen, nachdem die Quelle die Verbindung bereits geschlossen hat.
vbTooManyLinks (7)
Die Quelle kann nicht mehr als 128 Verbindungen handhaben.
vbDataTransferFailed (8)
Zielobjekt einer automatischen Verknüpfung oder eines expliziten LinkRequest-Aufrufs konnte nicht aktualisiert werden. Oder: Quellobjekt konnte durch LinkPoke-Aufruf nicht aktualisiert werden.
Der Parameter Index gibt den Index des signalisierenden Steuerelements wieder, wenn es Element eines Steuerelemente-Arrays ist. Anwendung
................................................... Anwendung
Eine DDE-Verbindung ermöglicht die Verknüpfung von Datenfeldern und die Übermittlung von Kommandos zwischen einem Ziel und einer Quelle, die jeweils unterschiedlichen Prozessen (lies: Laufzeitinstanzen von Anwendungen) angehören. Zielobjekt ist ein Steuerelement, Quellobjekt ein Formular. Das Zielobjekt initiiert den Verbindungsaufbau. Bei einer DDE-Verknüpfung liefert gewöhnlich das Quellobjekt den Wert eines seiner Elemente (lies: Steuerelemente) an das Zielobjekt, obwohl eine Aktualisierung auch in der anderen Richtung möglich ist, wenn die Methode LinkPoke zum Einsatz kommt. Ein Zielobjekt kann eine bestehende DDE-Verbindung auch für die Übermittlung von Kommandozeichenfolgen an das Quellobjekt nutzen. Ein Quellobjekt kann mit mehreren Zielobjekten eine DDE-Verbindung unterhalten, ein Zielobjekt jedoch immer nur mit einem Quellobjekt. Das LinkError-Ereignis sollte eigentlich nicht auftreten, wenn Ziel und Quelle gut aufeinander abgestimmt sind. Tritt es dennoch auf, ist die Situation nach einer Analyse der Fehlerursache häufig noch zu retten, etwa indem nicht mehr benötigte Verbindungen geschlossen werden, ein anderes Datenformat benutzt oder ein Versuch unternommen wird, eine gestorbene Verbindung erneut zu eröffnen.
23 1
Standardereignisse
................................................... Bes c hreibung
Standardereignisse
LinkExecute- Ereignis Sub Form_LinkExecute(CmdStr As String, Cancel As Integer) Sub MDIForm_LinkExecute(CmdStr As String, Cancel As Integer) Betroffene Objekte
................................................... Betro ffene Objekte Form, MDIForm Beschreibung
Standardereignisse
................................................... Bes c hreibung
Das LinkExecute-Ereignis tritt im Verlauf einer DDE-Verbindung aufseiten des Quellobjekts auf und übermittelt eine Kommandozeichenfolge des Zielobjekts. Anwendung
................................................... Anwendung
Eine DDE-Verbindung ermöglicht die Verknüpfung von Datenfeldern und die Übermittlung von Kommandos zwischen einem Ziel und einer Quelle, die jeweils unterschiedlichen Prozessen (lies: Laufzeitinstanzen von Anwendungen) angehören. Zielobjekt ist ein Steuerelement, Quellobjekt ein Formular. Das Zielobjekt initiiert den Verbindungsaufbau. Bei einer DDE-Verknüpfung liefert gewöhnlich das Quellobjekt den Wert eines seiner Elemente (lies: Steuerelemente) an das Zielobjekt, obwohl eine Aktualisierung auch in der anderen Richtung möglich ist, wenn die Methode LinkPoke zum Einsatz kommt. Ein Zielobjekt kann eine bestehende DDE-Verbindung auch für die Übermittlung von Kommandozeichenfolgen an das Quellobjekt nutzen. Ein Quellobjekt kann mit mehreren Zielobjekten eine DDE-Verbindung unterhalten, ein Zielobjekt jedoch immer nur mit einem Quellobjekt. Zum Versand eines DDE-Kommandos ruft das Zielobjekt seine LinkExecute-Methode unter Angabe einer Kommandozeichenfolge auf. Empfänger des Kommandos ist immer das Quellobjekt, nicht das für die Verknüpfung benannte Datenelement. Die gewöhnliche Reaktion auf das LinkExecute-Ereignis besteht in der Interpretation des Kommandos. Da es keinen feststehenden Kommandosatz für DDE-Verbindungen gibt, unterliegt die Ausgestaltung der Kommandos sowie deren Interpretation allein der Fantasie des Programmierers. Wenn die Behandlungsroutine mit dem Kommando etwas anfangen kann, muss sie den Parameter Cancel explizit auf False setzen, sonst tritt in der Zielanwendung der Laufzeitfehler 285: »Andere Anwendung führt die DDE-Methode oder -Operation nicht aus« auf. Beispiel
................................................... Beis piel
' Im Modul des Zielobjekts ... Text1.LinkExecute("Beenden") ... ' Im Modul der Quellobjekts Private Sub Form_LinkExecute(CmdStr As String, Cancel As Integer) If CmdStr = "Beenden" Then Cancel = False ' Kommando erkannt! Unload Me ' Kommando ausführen End If End Sub
232
LinkNotify- Ereignis
LinkNotify- Ereignis Sub Objekt_LinkNotify([Index As Integer]) Betroffene Objekte
................................................... Betro ffene Objekte Label, PictureBox, TextBox Beschreibung
................................................... Bes c hreibung
Anwendung
................................................... Anwendung
Eine DDE-Verbindung ermöglicht die Verknüpfung von Datenfeldern und die Übermittlung von Kommandos zwischen einem Ziel und einer Quelle, die jeweils unterschiedlichen Prozessen (lies: Laufzeitinstanzen von Anwendungen) angehören. Zielobjekt ist ein Steuerelement, Quellobjekt ein Formular. Das Zielobjekt initiiert den Verbindungsaufbau. Bei einer DDE-Verknüpfung liefert gewöhnlich das Quellobjekt den Wert eines seiner Elemente (lies: Steuerelemente) an das Zielobjekt, obwohl eine Aktualisierung auch in der anderen Richtung möglich ist, wenn die Methode LinkPoke zum Einsatz kommt. Ein Zielobjekt kann eine bestehende DDE-Verbindung auch für die Übermittlung von Kommandozeichenfolgen an das Quellobjekt nutzen. Ein Quellobjekt kann mit mehreren Zielobjekten eine DDE-Verbindung unterhalten, ein Zielobjekt jedoch immer nur mit einem Quellobjekt. Ein Zielobjekt hat beim Aufbau einer DDE-Verbindung für eine Verknüpfung die Wahl zwischen drei Arbeitsmodi: »Automatisch«, »Manuell« oder »Manuell mit Benachrichtigung«. Im Modus »Automatisch« findet eine automatische Synchronisation zwischen Quelle und Ziel statt. Im Modus »Manuell« kann das Zielobjekt die Synchronisation durch explizite Aufrufe der Methode LinkRequest nach Bedarf aufrechterhalten. Im Modus »Manuell mit Benachrichtigung« erhält das Zielobjekt jedes Mal ein LinkNotify-Ereignis, wenn sich der verknüpfte Wert aufseiten der Quelle ändert. Die Behandlungsroutine für das Ereignis wird im Allgemeinen einen LinkRequest-Aufruf enthalten, der die Synchronisation betreibt. Ein Quellobjekt kann durch Aufruf der LinkSend-Methode des als Datenlieferant für eine DDEVerknüpfung fungierenden Steuerelementobjekts LinkNotify-Ereignisse auch außer der Reihe anstoßen, was beispielsweise nach Manipulation der Picture-Eigenschaft eines Bildfelds erforderlich ist.
LinkOpen- Ereignis Sub Form_LinkOpen(Cancel As Integer) Sub MDIForm_LinkOpen(Cancel As Integer) Sub Objekt_LinkOpen([Index As Integer], Cancel As Integer) Betroffene Objekte
................................................... Betro ffene Objekte Form, Label, MDIForm, PictureBox, TextBox Beschreibung
................................................... Bes c hreibung
Das LinkOpen-Ereignis signalisiert den Aufbau einer DDE-Verbindung durch das Zielobjekt. Es tritt auf beiden Seiten der Verbindung auf. Der Ergebnisparameter Cancel hat als Voreinstellung den Wert 0. Zielobjekt und Quellobjekt haben die Möglichkeit, die Verbindung abzulehnen, indem sie Cancel auf einen Wert ungleich 0 setzen.
23 3
Standardereignisse
Das LinkNotify-Ereignis signalisiert dem Zielobjekt einer im Modus »Manuell mit Benachrichtigung« aufgebauten DDE-Verknüpfung, dass sich der Inhalt des Datenlieferanten aufseiten der Quelle geändert hat. Zur Auffrischung seiner Daten kann das Zielobjekt seine Methode LinkRequest ausführen.
Standardereignisse
Anwendung
Standardereignisse
................................................... Anwendung
Eine DDE-Verbindung ermöglicht die Verknüpfung von Datenfeldern und die Übermittlung von Kommandos zwischen einem Ziel und einer Quelle, die jeweils unterschiedlichen Prozessen (lies: Laufzeitinstanzen von Anwendungen) angehören. Zielobjekt ist ein Steuerelement, Quellobjekt ein Formular. Das Zielobjekt initiiert den Verbindungsaufbau. Bei einer DDE-Verknüpfung liefert gewöhnlich das Quellobjekt den Wert eines seiner Elemente (lies: Steuerelemente) an das Zielobjekt, obwohl eine Aktualisierung auch in der anderen Richtung möglich ist, wenn die Methode LinkPoke zum Einsatz kommt. Ein Zielobjekt kann eine bestehende DDE-Verbindung auch für die Übermittlung von Kommandozeichenfolgen an das Quellobjekt nutzen. Ein Quellobjekt kann mit mehreren Zielobjekten eine DDE-Verbindung unterhalten, ein Zielobjekt jedoch immer nur mit einem Quellobjekt. Voraussetzung für einen erfolgreichen Verbindungsaufbau ist, dass die Eigenschaft LinkMode des Quellobjekts bereits beim Entwurf auf den Wert vbLinkSource gesetzt wurde. Die Eigenschaften LinkTopic und LinkItem des Zielobjekts spezifizieren das Quellobjekt sowie das Verknüpfungselement. Der Wert für LinkTopic setzt sich aus einem »Anwendungsnamen« und einem »Thema« zusammen und hat die Syntax: Objekt.LinkTopic = "Anwendung|Thema"
Der »Anwendungsname« entspricht dem Wert der Eigenschaft App.Title aufseiten der Quelle und stimmt meist mit dem Namen der EXE-Datei des Programms überein, jedoch nicht immer. Als »Thema« ist der Wert der LinkTopic-Eigenschaft des Quellobjekts anzugeben, der meist (jedoch nicht zwingend) mit dem Fenstertitel des Formulars übereinstimmt. Für die Eröffnung einer reinen Kommandoverbindung reicht es, nur die LinkTopic-Eigenschaft des Zielobjekts geeignet zu setzen. Für die Eröffnung einer Datenverknüpfung zu einem Steuerelement des Quellobjekts muss weiterhin die LinkItem-Eigenschaft auf den Bezeichner des Steuerelements gesetzt werden: Objekt.LinkItem = "Element"
Um beispielsweise mit dem Steuerelement Text1 des Formulars Form1 (lies: dessen LinkTopicEigenschaft den Wert »Form1« hat) eines Programms, dessen Anwendungsname »DDE-Demo« ist, eine Verknüpfung aufzubauen, lautet die Verbindungsinformation für das Zielobjekt ZielElement: ZielElement.LinkTopic = "DDE-Demo|Form1" ZielElement.LinkItem = "Text1"
Der Verbindungsaufbau erfolgt, sobald die Eigenschaft LinkMode des Zielobjekts auf einen Wert ungleich 0 gesetzt wird: ZielElement.LinkMode = vbLinkAutomatic
Umgekehrt erfolgt der Verbindungsabbau, sobald die Eigenschaft LinkMode des Zielobjekts oder des Quellobjekts auf den Wert 0 gesetzt wird: ZielElement.LinkMode = vbLinkNone
Über eine bereits bestehende DDE-Verknüpfung lassen sich gleichfalls Kommandos an das Quellobjekt schicken, meist erfolgt der Versand von Kommandos aber über eine eigene Kommandoverbindung. Aufseiten des Ziels tritt dann ein weiteres Zielobjekt in Aktion, ohne dass dies an der Logik der Quelle etwas ändert. Reine Kommandoverbindungen werden im Verbindungsmodus »Manuell« oder »Manuell mit Benachrichtigung« geöffnet.
234
LinkOpen- Ereignis
ZielElement.LinkMode = vbLinkManual
oder ZielElement.LinkMode = vbLinkNotify
Beispiel
................................................... Beis piel
Das folgende Codefragment zeigt prototypisch den Aufbau einer DDE-Verknüpfung mit einem Steuerelement in einer anderen Anwendung: Static ZielementVerbunden As Boolean ... If Not ZielementVerbunden Then ZielElement.LinkTopic = "DDE-Demo|Form1" ' Anwendung und Formular ZielElement.LinkItem = "Text1" ' Quelle festlegen! On Error GoTo StarteAnwendung ZielElement.LinkMode = vbLinkAutomatic ZielElementVerbunden = True End If ... Exit Sub StarteAnwendung: MsgBox (Err.Description) ...
Hier der gleiche Code, wenn die Verbindung mit der Zelle B5 einer Excel-Tabelle namens Tabelle1.xls aufgebaut werden soll: Static ZielementVerbunden As Boolean ... If Not ZielementVerbunden Then ZielElement.LinkTopic = "Excel|Tabelle1.xls" ' Anwendung und Formular ZielElement.LinkItem = "Z2S5" ' Quelle festlegen! On Error GoTo StarteAnwendung ZielElement.LinkMode = vbLinkAutomatic ZielElementVerbunden = True End If ... Exit Sub StarteAnwendung: If Shell("C:\Microsoft Office\Office\Excel.exe C:\Tabelle1.xls") Then Resume Else
23 5
Standardereignisse
Für reine Kommandoverbindungen macht es keinen Unterschied, welcher der beiden Verbindungsmodi benutzt wird, für eine Datenverknüpfung jedoch schon. Im Modus »Manuell« muss das Zielobjekt einer Datenverknüpfung jede Aktualisierung der Verknüpfungsinformation explizit durch einen Aufruf der Methode LinkRefresh anleiern, ohne zu wissen, ob sich aufseiten der Quelle etwas geändert hat oder nicht. Im Modus »Manuell mit Benachrichtigung« schickt das Quellobjekt dem Zielobjekt dagegen bei jeder Änderung das Ereignis LinkNotifiy, woraufhin dieses mit LinkRefresh reagieren kann, aber nicht muss.
Standardereignisse
Exit Sub End If End Sub Verwandte Themen
................................................... Verwandte Them en
DDE-Verbindungen (S. 495)
Load- Ereignis
Standardereignisse
Sub Objekt_Load() Betroffene Objekte
................................................... Betro ffene Objekte Form, MDIForm, PropertyPage Beschreibung
................................................... Bes c hreibung
Das Load-Ereignis folgt auf das Initialize-Ereignis und tritt als zweites Ereignis im noch jungen Leben eines Formulars auf. Zu diesem Zeitpunkt wird das Formularobjekt zwar noch nicht angezeigt, ist aber bereits vollständig konstruiert und ermöglicht die sichere Initialisierung von Steuerelementen und globalen Variablen sowie das Laden von Ressourcen. Im Gegensatz zum Initialize-Ereignis kann das Load-Ereignis im Leben eines Objekts mehrmals auftreten. Anwendung
................................................... Anwendung
Für das Eintreten eines Load-Ereignisses kommen drei Anlässe in Betracht: Erstens, das Formular ist als Startobjekt einer Anwendung spezifiziert. Zweitens, das Formular wird durch ein anderes Objekt explizit mittels einer Load-Anweisung geladen. Drittens, ein anderes Objekt referiert auf ein Element, eine Eigenschaft oder eine Methode des ungeladenen Formulars. Für den zweiten und dritten Fall muss die verwendete Objektvariable mit dem Schlüsselwort New deklariert oder zuvor ein Unload-Aufruf für das Formular erfolgt sein. Auch erfolgt keine automatische Anzeige des Formulars. Vielmehr ist es der Show-Aufruf, der ein geladenes oder ungeladenes Formular zur Anzeige bringt. Es reicht somit zu schreiben: Private MeinForm As New Form1 ... ' in einer Funktion/Prozedur MeinForm.Show ' Lädt das Formular und zeigt es an
Wird ein Formular geladen, dessen MDIChild-Eigenschaft beim Entwurf auf True gesetzt wurde, lädt Visual Basic implizit auch das übergeordnete MDI-Formular, falls dieses noch nicht geladen ist. Für ein untergeordnetes Formular ist kein Show-Aufruf erforderlich, da dieses nach dem Load-Ereignis automatisch angezeigt wird. Hinweis
................................................... Hinweis Von MsgBox oder InputBox angezeigte Dialogfelder erfordern weder eine Deklaration noch einen Load- oder Show-Aufruf. Beispiel
................................................... Beis piel
Der folgende Code demonstriert den Unterschied zwischen dem Load- und dem InitializeEreignis, indem es die Ereignisse durch MsgBox-Aufrufe gewissermaßen sichtbar macht. Das Formular enthält ein Steuerelement namens Text1 und initialisiert dieses während der Behandlung von Initialize (!). Beim Start der Anwendung kommen nacheinander das Initialize- und das
236
MouseDown- Ereignis und MouseUp- Ereignis
Load-Ereignis zum Zuge. Ein Klick auf das Formular entlädt dieses zunächst, gibt eine Meldung aus und lädt es dann gleich wieder. Wie man sieht, hat Text1 danach seine Initialisierung vergessen. ' Demo für den Unterschied zwischen dem Load- und Initialize-Ereignis Private Sub Form_Initialize() MsgBox "Form_Initialize" Text1 = "Initialisiert" End Sub
Standardereignisse
Private Sub Form_Load() MsgBox "Form_Load" End Sub Private Sub Form_Click() Unload Me MsgBox "Formular entladen" Show End Sub Verwandte Ereignisse
................................................... Verwandte Ereignis s e Initialize, Terminate, Unload
MouseDown- Ereignis und MouseUp- Ereignis Sub Form_MouseDown(Button As Integer, Shift As Integer, _ X As Single, Y As Single) Sub MDIForm_MouseDown(Button As Integer, Shift As Integer, _ X As Single, Y As Single) Sub Objekt_MouseDown([Index As Integer,] Button As Integer, _ Shift As Integer, X As Single, Y As Single) Sub Form_MouseUp(Button As Integer, Shift As Integer, _ X As Single, Y As Single) Sub MDIForm_MouseUp (Button As Integer, Shift As Integer, _ X As Single, Y As Single) Sub Objekt_MouseUp ([Index As Integer,] Button As Integer, _ Shift As Integer, X As Single, Y As Single) Betroffene Objekte
................................................... Betro ffene Objekte Adodc, Animation, CheckBox, CommandButton, DataGrid, DataList, DataRepeater, DateTimePicker, DBCombo, DBGrid, DBList, DirListBox, FileListBox, Form, Grid, HScrollBar Image, ListBox, ListView, MDIForm, MonthView, MSFlexGrid, MSHFlexGrid, OLE, OptionButton, PictureBox, ProgressBar, PropertyPage, RichTextBox, Slider, StatusBar, TabStrip, TextBox, UpDown, UserControl, UserDocument, VScrollBar Beschreibung
................................................... Bes c hreibung
Das MouseDown-Ereignis signalisiert einem Objekt, dass der Benutzer über seinem Fenster eine Maustaste niedergedrückt hat, während das MouseUp-Ereignis das Loslassen derselben verkün-
23 7
Standardereignisse
Standardereignisse
det. Im Gegensatz zu den Ereignissen Click und DblClick übermitteln MouseDown und MouseUp genügend Informationen, um die aktuelle Mausposition, die verantwortliche Maustaste sowie auch den Status der Funktionstasten ermitteln zu können. Der im Parameter Button übergebene Bitvektor identifiziert die benutzte Maustaste. Es ist immer nur ein Bit gesetzt: Bit 0 steht für die linke Maustaste, Bit 1 für die rechte und Bit 2 für die mittlere. Der Aufzählungstyp MouseButtonConstants definiert drei Konstanten dafür, die sich als Masken für die Abfrage der Maustaste benutzen lassen: vbLeftButton (1), vbRightButton (2) und vbMiddleButton (4). Durch Auswertung des Shift-Parameters, gleichfalls ein Bitvektor lässt sich ermitteln, ob und welche Funktionstasten bei Eintritt des Ereignisses gedrückt waren. Bit 0 steht für (Umschalt), Bit 1 für (Strg) und Bit 2 für (Alt). Der Aufzählungstyp ShiftConstants definiert drei Konstanten dafür, die sich als Masken für die Abfrage der Funktionstasten benutzen lassen: vbShift (1), vbCtrl (2) und vbAlt (4). Die Werte der Parameter X und Y geben die aktuelle Mausposition zum Zeitpunkt des Ereignisses als Koordinaten im aktuellen Koordinatensystem des Objekts an. Der Parameter Index gibt den Index des betroffenen Steuerelements wieder, wenn es Element eines Steuerelemente-Arrays ist. Anwendung
................................................... Anwendung
Ein häufiges Problem besteht darin, dass es dem Benutzer möglich sein soll, mit der Maus einen Bereich auszuwählen oder die Größe eines Elements zu verändern (vgl. Drag&Drop). In diesem Fall muss die Programmlogik so ausgelegt sein, dass das MouseDown-Ereignis den Start der Operation, die bis zum MouseUp-Ereignis auftretenden MouseMove-Ereignisse den Verlauf der Operation und das MouseUp-Ereignis schließlich den Abschluss der Operation steuern. Der Zusammenhang lässt sich durch Einsatz einer geeignet zu pflegenden Zustandsvariable herstellen. Reine Positionsverschiebungen lassen sich auch allein auf Basis des MouseMove-Ereignisses bewerkstelligen. Drückt der Benutzer mehrere Maustasten gleichzeitig, sieht das Objekt für jede der Maustasten ein eigenes Ereignis. Wenn es eine Rolle spielt, ob nur eine oder mehrere Maustasten gedrückt bzw. losgelassen wurden, müssen die Behandlungsroutinen auch in diesem Fall eine Zustandsvariable pflegen. Da in dem Bitvektor Shift mehrere Bits gesetzt sein können, ist zur Abfrage einer einzelnen Funktionstaste eine And-Operation mit der Maskenkonstanten erforderlich: If Shift And vbShift Then
Kombinationen von Funktionstasten lassen sich einzeln oder durch Addition der entsprechenden Maskenkonstanten ermitteln: If (Shift And (vbShift + vbAlt)) = (vbShift + vbAlt) Then
bzw. If (Shift And vbShift) And (Shift And vbAlt) Then
Die Klammerung ist hier wichtig, da And zweimal als bitweiser und einmal als logischer Operator auftritt. Warnungen
................................................... Wa rnungen Für Verwirrung sorgt oft die Behandlung von Mausereignissen für Steuerelemente. Da Steuerelemente ein unveränderliches Koordinatensystem mit Ursprung in der linken oberen Ecke und der Maßeinheit Twips benutzen, beziehen sich die von den Ereignissen MouseDown und MouseUp gelieferten Koordinaten auf dieses Koordinatensystem, auch wenn im übergeordneten Formular
238
MouseMove- Ereignis
ein anderes Koordinatensystem herrscht. Für die Umrechnung von Koordinaten zwischen verschiedenen Koordinatensystemen lassen sich die Methoden ScaleX und ScaleY einsetzen. Das Eintreffen des MouseUp-Ereignisses ist nicht in jedem Falle garantiert. In speziellen Situationen kann es auch einmal unter den Tisch fallen – beispielsweise im Zuge der Ausführung der Stop-Anweisung oder wenn die Programmausführung einen Unterbrechungspunkt erreicht. Hinweis
................................................... Hinweis Beachten Sie, dass jedes MouseUp-Ereignis ein Click-Ereignis sowie gegebenenfalls ein DblClickEreignis nach sich zieht.
................................................... Tipp
Eine geeignete Logik bei der Behandlung des MouseMove-Ereignisses kann die Behandlung von MouseDown und MouseUp obsolet machen. Verwandte Befehle
................................................... Verwa ndte Befehle
Click, DblClick, KeyDown, KeyUp, MouseMove Beispiel
................................................... Beis piel
Vgl. das Beispiel in »MouseMove-Ereignis« (S. 239). Verwandte Themen
................................................... Verwandte Them en
Gummiband – Bereiche interaktiv auswählen (S. 492)
MouseMove- Ereignis Sub Form_MouseMove(Button As Integer, Shift As Integer, _ X As Single, Y As Single) Sub MDIForm_MouseMove(Button As Integer, Shift As Integer, _ X As Single, Y As Single) Sub Objekt_MouseMove([Index As Integer,] Button As Integer, _ Shift As Integer, X As Single, Y As Single) Betroffene Objekte
................................................... Betro ffene Objekte Adodc, Animation, CheckBox, CommandButton, DataGrid, DataList, DataRepeater, DateTimePicker, DBCombo, DBGrid, DBList, DirListBox, FileListBox, Form, Grid, HScrollBar Image, ListBox, ListView, MDIForm, MonthView, MSFlexGrid, MSHFlexGrid, OLE, OptionButton, PictureBox, ProgressBar, PropertyPage, RichTextBox, Slider, StatusBar, TabStrip, TextBox, UpDown, UserControl, UserDocument, VScrollBar Beschreibung
................................................... Bes c hreibung
Während die Ereignisse MouseDown und MouseUp nur dann auftreten, wenn der Benutzer die Maustasten betätigt, tritt das MouseMove-Ereignis auf, sobald der Benutzer die Maus im Fensterbereich des Objekts bewegt. Im Gegensatz zu den Ereignissen Click und DblClick übermittelt MouseMove Informationen, die einer Behandlungsroutine Aufschluss über die aktuelle Mausposition sowie den Status der Maus- und Funktionstasten geben. Der im Parameter Button übergebene Bitvektor identifiziert die benutzten Maustasten. Es können auch mehrere Bits gesetzt sein: Bit 0 steht für die linke Maustaste, Bit 1 für die rechte und Bit 2 für die mittlere. Der Aufzählungstyp MouseButtonConstants definiert drei Konstanten
23 9
Standardereignisse
Tipp
Standardereignisse
Standardereignisse
dafür, die sich als Masken für die Abfrage der Maustaste benutzen lassen: vbLeftButton (1), vbRightButton (2), vbMiddleButton (4). Durch Auswertung des Shift-Parameters, gleichfalls ein Bitvektor lässt sich ermitteln, ob und welche Funktionstasten bei Eintritt des Ereignisses gedrückt waren. Bit 0 steht für (Umschalt), Bit 1 für (Strg) und Bit 2 für (Alt). Der Aufzählungstyp ShiftConstants definiert drei Konstanten dafür, die sich als Masken für die Abfrage der Funktionstasten benutzen lassen: vbShift (1), vbCtrl (2), vbAlt (4). Die Werte der Parameter X und Y geben die aktuelle Mausposition zum Zeitpunkt des Ereignisses als Koordinaten im aktuellen Koordinatensystem des Objekts an. Der Parameter Index gibt den Index des betroffenen Steuerelements wieder, wenn es Element eines Steuerelemente-Arrays ist. Anwendung
................................................... Anwendung
Eine Behandlung des MouseMove-Ereignisses ist sinnvoll, wenn die Bewegung des Mauszeigers im Verlauf einer Operation von Interesse ist – zum Beispiel für die Anzeige eines visuellen Feedbacks für den Benutzer, wenn dieser eine Ziehoperation mit der Maus durchführt. Tipp
................................................... Tipp
Da das Formularobjekt (aber auch großflächige Steuerelemente) im Allgemeinen einer ständigen Flut von MouseDown-Ereignissen ausgesetzt ist, sollten Sie bei Implementierung einer Behandlungsroutine für dieses Ereignis stets auf gute Laufzeiteigenschaften achten, damit die Reaktivität der Benutzerschnittstelle keine Beeinträchtigung erfährt. Am besten, Sie fragen gleich zu Beginn der Routine eine Zustandsvariable ab, deren Wert über die Notwendigkeit einer ernsthaften Behandlung mit Auswertung der Ereignisparameter entscheidet. Damit vermeiden Sie vorauseilende Operationen und Berechnungen, deren Ergebnisse ohnehin erst im Verlauf einer konkreten Mausoperation von Interesse sind. Beispiel
................................................... Beis piel
Das folgende kleine Programm wertet die Ereignisse MouseDown, MouseUp und MouseMove aus, um eine Ziehoperation für ein Steuerelement zu implementieren, bei der das Steuerelement (im Gegensatz zur Drag&Drop-Operation) sichtbar bleibt. Das Szenario besteht schlicht aus einem Formular und einem Steuerelement namens figur. Um die Angelegenheit realistischer zu machen, geht die Implementation erschwerend davon aus, dass in Formular und Steuerelement unterschiedliche Koordinatensysteme gelten. Private StartX As Single Private StartY As Single Private Sub Form_Load() ScaleMode = vbPixels End Sub Private Sub figur_MouseDown(Button As Integer, Shift As Integer, _ X As Single, Y As Single) StartX = X ' Startkoordinaten merken, Operation einleiten StartY = Y End Sub Private Sub figur_MouseMove(Button As Integer, Shift As Integer, _ X As Single, Y As Single)
240
OLECompleteDrag- Ereignis
If StartX Then figur.Left = figur.Left – ScaleX((StartX – X), vbTwips, vbPixels) figur.Top = figur.Top – ScaleY((StartY – Y), vbTwips, vbPixels) End If End Sub
Ein Wort zu der nicht ganz trivialen Strategie, die der Code verfolgt. Die globale Variable StartX nimmt eine Doppelfunktion ein: Sie ist Zustandsvariable für die Steuerung der MouseMove-Behandlung und bunkert gleichzeitig den X-Offset des Mauszeigers relativ zum Ursprung des Steuerelements. StartY speichert den Y-Offset. Beide Werte beziehen sich auf den Beginn der Ziehoperation. Da alle von MouseMove gelieferten Koordinaten auf das Koordinatensystem des Steuerelements bezogen sind, gilt es, bei jedem MouseMove-Ereignis die Position des Steuerelements so zu verändern, dass der Offset des Mauszeigers im Steuerelement wieder hergestellt wird. Die Distanz für die Verschiebung ergibt sich somit als Differenz zwischen dem Offset und der aktuellen Mausposition im Steuerelement. Da im Formular ein anderes Koordinatensystem als im Steuerelement gilt, muss die Distanz für die Verschiebung des Steuerelements im Formular noch in dessen Koordinatensystem umgerechnet werden. Verwandte Befehle
................................................... Verwa ndte Befehle
Click, DblClick, KeyDown, KeyUp, MouseDown, MouseUp Verwandte Themen
................................................... Verwandte Them en
Gummiband – Bereiche interaktiv auswählen (S. 492)
OLECompleteDrag- Ereignis Private Sub Objekt_OLECompleteDrag(Effect As Long) Betroffene Objekte
................................................... Betro ffene Objekte Animation, CoolBar, DataList, DateTimePicker, DBCombo, DBList, DirListBox, FileListBox, FlatScrollBar, Grid, Image, ImageCombo, ListBox, ListView, MaskEdBox, MSChart, MSFlexGrid, MSHFlexGrid, OLE, PictureBox, PropertyPage, RichTextBox, SSTab, TabStrip, TextBox, TreeView, ToolBar, UserControl, UserDocument Beschreibung
................................................... Bes c hreibung
Das OLECompleteDrag-Ereignis tritt als letztes Ereignis in der Ereignisfolge einer OLEDrag&Drop-Operation auf, wenn die Quellkomponente im manuellen Modus (OLEDragMode = vbDragManual) betrieben wird. Es teilt ihr mit, welche Operation die Zielkomponente auf das OLEDragDrop-Ereignis hin letztlich durchgeführt hat – vorausgesetzt, diese hat den Parameter Effect entsprechend gepflegt. Vom Wert her ist Effect ein Bitvektor, dessen einzelne Bits für die möglichen Operationen stehen. Ist kein Bit gesetzt, fand keine Operation statt. Der Aufzählungstyp OLEDropEffectConstants definiert eine Reihe von Konstanten für die einzelnen Opera-
241
Standardereignisse
Private Sub figur_MouseUp(Button As Integer, Shift As Integer, _ X As Single, Y As Single) If StartX Then ' Steuerelement ablegen figur.Left = figur.Left – ScaleX((StartX – X), vbTwips, vbPixels) figur.Top = figur.Top – ScaleY((StartY – Y), vbTwips, vbPixels) StartX = 0 End If End Sub
Standardereignisse
tionen: vbDropEffectNone (0) steht für »keine Operation«, vbDropEffectCopy (1) für »Kopieroperation« und vbDropEffectMove (2) für »Verschiebeoperation«. Anwendung
Standardereignisse
................................................... Anwendung
Zum Abschluss einer OLE-Drag&Drop-Operation kann aufseiten der Quellkomponente eine Nachbereitung erforderlich sein, beispielsweise wenn eine Verschiebeoperation stattgefunden hat und die Komponente den verschobenen Inhalt nicht von alleine löscht oder gegebenenfalls belegte Ressourcen nicht freigibt. Falls die Quellkomponente in einer der Behandlungsroutinen für OLEDragStart oder OLEGiveFeedBack einen eigenen Mauscursor setzt, um dem Benutzer das Ergebnis der Operation besser vor Augen zu bringen, muss in jedem Fall eine Behandlung von OLECompleteDrag erfolgen, um den ursprünglichen Mauscursor wieder herzustellen. Warnung
................................................... Wa rnung
Auch wenn der Visual Basic Editor das Ereignis bei vielen Steuerelementen und Objekten anbietet (so beispielsweise für Form und MMControl), betrifft es im Allgemeinen nur solche Objekte, die darauf eingerichtet sind, als Quellkomponente in einer OLE-Drag&Drop-Operation zu fungieren. Zudem tritt das Ereignis auch bei diesen Objekten nur im »manuellen Modus« auf, das heißt, wenn die Eigenschaft OLEDragMode den Wert vbOLEDragManual (1) hat. Beispiel
................................................... Beis piel
Private Sub Quelle_OLECompleteDrag(Effect As Long) Screen.MouseIcon = AltesScreenIcon ' Alte Form setzen Screen.MousePointer = AlterScreenPointer ' Alten Zeiger setzen End Sub Verwandte Ereignisse
................................................... Verwandte Ereignis s e OLEDragDrop, OLEDragOver, OLEGiveFeedback, OLESetData, OLEStartDrag Verwandte Themen
................................................... Verwandte Them en
OLE-Drag&Drop (S. 501)
OLEDragDrop- Ereignis Private Sub Form_OLEDragDrop(Data As DataObject, Effect As Long, _ Button As Integer, Shift As Integer, X As Single, Y As Single) Private Sub Objekt_OLEDragDrop([Index As Integer,] Data As DataObject, _ Effect As Long, Button As Integer, Shift As Integer, X As Single, _ Y As Single) Betroffene Objekte
................................................... Betro ffene Objekte Animation, CheckBox, CommandButton, CoolBar, DataList, DateTimePicker, DBCombo, DBList, DirListBox, FileListBox, FlatScrollBar, Frame, Form, Grid, Image, ImageCombo, ListBox, ListView, MaskEdBox, MDIForm, MMControl, MSChart, MSFlexGrid, MSHFlexGrid, OLE, OptionButton, PictureBox, ProgressBar, PropertyPage, RichTextBox, Slider, SSTab, StatusBar, TabStrip, TextBox, TreeView, ToolBar, UpDown, UserControl, UserDocument
242
OLEDragDrop- Ereignis
Beschreibung
................................................... Bes c hreibung
Anwendung
................................................... Anwendung
Bevor eine Zielkomponente mit der Ausführung einer OLE-Drag&Drop-Operation beginnen kann, ist eine genauere Analyse der Situation erforderlich. Erstens gilt es, anhand des Zustands der Funktions- und Maustasten (Shift und Button) herauszufinden, welche Operation der Benutzer ausführen will. Zweitens ist zu klären, ob die Quellkomponente diese Operation auch unterstützt (Effect). Drittens empfiehlt sich eine Analyse des Datenformats (Data.GetFormat), in dem der übermittelte Inhalt vorliegt – das ist zwar kein Muss, erleichtert aber die Sache, wenn die Zielkomponente mit mehreren Datenformaten zurechtkommt oder das angebotene Format nicht unterstützt. Die folgende Tabelle gibt einen Überblick über die von Visual Basic unterstützten (teils auch automatisch erkannten) OLE-Formate und die dafür vom Aufzählungstyp ClipBoardConstants definierten Konstanten. Formatkonstante
Beschreibung des Formats
vbCFText (1)
Data enthält Zeichenfolge im Format TXT
vbCFBitmap (2)
Data enthält Bitmap im Format BMP
vbCFMetafile (3)
Data enthält Grafik im Zwischendateiformat WMF
vbCFEMetafile (14)
Data enthält Grafik im erweiterten Zwischendateiformat EMF
vbCFDIB (8)
Data enthält geräteunabhängige Bitmap im DIB-Format
vbCFPalette (9)
Data enthält Palette im Format PAL
vbCFFiles (15)
Data enthält Files-Auflistung
vbCFRTF (-16639)
Data enthält Zeichenfolge im RTF-Format
Standardmäßige Datenformate für Inhalte eines DataObject- Objekts
Vom Prinzip her lassen sich auch eigene Formate in einer OLE-Operation übermitteln (mehr zur Vorgehensweise im Abschnitt »OLE-Drag&Drop«, S. 501, des Praxisteils).
243
Standardereignisse
Das OLEDragDrop-Ereignis bedeutet der Zielkomponente einer OLE-Drag&Drop-Operation, dass der Benutzer den Inhalt abgelegt hat und die gewählte Operation ausführen will. Der Parameter Effect ist ein von der Quellkomponente in Antwort auf OLEStartDrag oder OLEGiveFeedback gesetzter Bitvektor, der ausdrückt, welche Operationen diese unterstützt. Der Aufzählungstyp OLEDropEffectConstants definiert eine Reihe von Konstanten für die einzelnen Operationen: vbDropEffectNone (0) steht für »keine Operation«, vbDropEffectCopy (1) für »Kopieroperation« und vbDropEffectMove (2) für »Verschiebeoperation«. Button ist gleichfalls ein Bitvektor, der den Zustand der Maustasten zum Zeitpunkt des Ereignisses wiedergibt. Der Aufzählungstyp MouseButtonConstants definiert dafür die Maskenkonstanten vbLeftButton (1), vbRightButton (2) und vbMiddleButton (4). Auch Shift ist ein Bitvektor, der ausdrückt, welche Funktionstasten der Benutzer zum Zeitpunkt des Ereignisses gedrückt hält. Der Aufzählungstyp ShiftConstants definiert dafür die Maskenkonstanten vbShift (1), vbCtrl (2) und vbAlt (4). X und Y enthalten die aktuelle Position des Mauszeigers zum Zeitpunkt des Ereignisses im Koordinatensystem der Zielkomponente. Der Parameter Data verweist auf ein Objekt vom Typ DataObject, das als Container für den zu übermittelnden Inhalt fungiert und Informationen über die unterstützten Inhaltsformate bereitstellt.
Standardereignisse
Wenn die Operation und das Format geklärt sind, kann der Zugriff auf den Inhalt mittels GetData erfolgen. Hat die Quellkomponente das spezifizierte Format nur angemeldet, ohne einen Inhalt dafür bereitzustellen – der Aufruf dafür sieht so aus:
Standardereignisse
Data.SetData , Format
stößt der GetData-Aufruf für das Format implizit ein OLESetData-Ereignis aufseiten der Quellkomponente an, damit diese den Inhalt bereitstellen kann. GetData löst regulär den Laufzeitfehler 461 aus, wenn die Quellkomponente das durch den Parameter spezifizierte Format zwar angeblich unterstützt, aber letztlich keinen Inhalt dafür bereitstellen kann. Wenn der Inhalt erfolgreich entgegengenommen werden konnte, sollte die Zielkomponente der Quellkomponente über den Parameter Effect die ausgeführte Operation »mitteilen«. Die Mitteilung erreicht die Quellkomponente schließlich in Form des OLECompleteDrag-Ereignisses, das unmittelbar auf OLEDragDrop folgt. Hinweis
................................................... Hinweis Manche Steuerelemente deklarieren für den Data-Parameter eine eigene Variante des Datentyps DataObject. Am Umgang mit dem Parameter ändert das nichts. Tipp
................................................... Tipp
Da die Analyse des Maus- und Funktionstastenstatus auch bei der Behandlung des OLEDragOverEreignisses zu gebrauchen ist, empfiehlt es sich, die gesamte Logik dafür in eine eigene Prozedur zu packen. Beispiel
................................................... Beis piel
Das folgende kleine Programm gibt den Inhalt einer Textdatei aus, die aus einem Fenster des Windows Explorer auf das Formular gezogen wird: Private Sub Form_Load() OLEDropMode = vbOLEDropManual AutoRedraw = True End Sub Private Sub Form_OLEDragDrop(Data As DataObject, Effect As Long, _ Button As Integer, Shift As Integer, x As Single, Y As Single) Dim fs As Object Dim DOF As DataObjectFiles If (Effect And vbDropEffectCopy > 0) And Data.GetFormat(vbCFFiles) _ Then Set DOF = Data.Files If UCase(Right(DOF(1), 4)) = ".TXT" Then Set fs = CreateObject("Scripting.FileSystemobject") On Error Resume Next ' Falls Datei inzwischen unbekannt ... Print fs.OpenTextfile(DOF(1)).ReadAll ' Datei en bloc ausgeben End If Effect = vbDropEffectCopy Else Effect = vbDropEffectNone End If End Sub
244
OLEDragOver- Ereignis
Verwandte Ereignisse
................................................... Verwandte Ereignis s e OLECompleteDrag, OLEDragOver, OLEGiveFeedback, OLESetData, OLEStartDrag Verwandte Themen
................................................... Verwandte Them en
OLE-Drag&Drop (S. 501)
OLEDragOver- Ereignis
Private Sub Objekt_OLEDragOver([Index As Integer,] Data As DataObject, _ Effect As Long, Button As Integer, Shift As Integer, X As Single, _ Y As Single, State As Integer) Betroffene Objekte
................................................... Betro ffene Objekte Animation, CheckBox, CommandButton, CoolBar, DataList, DateTimePicker, DBCombo, DBList, DirListBox, FileListBox, FlatScrollBar, Frame, Form, Grid, Image, ImageCombo, ListBox, ListView, MaskEdBox, MDIForm, MMControl, MSChart, MSFlexGrid, MSHFlexGrid, OLE, OptionButton, PictureBox, ProgressBar, PropertyPage, RichTextBox, Slider, SSTab, StatusBar, TabStrip, TextBox, TreeView, ToolBar, UpDown, UserControl, UserDocument Beschreibung
................................................... Bes c hreibung
Das OLEDragOver-Ereignis bedeutet einer Komponente, dass sie als mögliches Ziel einer OLEDrag&Drop-Operation in Frage kommt oder nicht mehr in Frage kommt. Ursächlich für das Ereignis ist, dass der Mauscursor im Verlauf einer OLE-Drag&Drop-Operation in den Fensterbereich der Komponente eintritt, darin verschoben wird oder den Fensterbereich verlässt. Der Parameter Effect ist ein von der Quellkomponente in Antwort auf OLEStartDrag oder OLEGiveFeedback gesetzter Bitvektor, der ausdrückt, welche Operationen diese unterstützt. Der Aufzählungstyp OLEDropEffectConstants definiert eine Reihe von Konstanten für die einzelnen Operationen: vbDropEffectNone (0) steht für »keine Operation«, vbDropEffectCopy (1) für »Kopieroperation« und vbDropEffectMove (2) für »Verschiebeoperation«. Button ist gleichfalls ein Bitvektor, der den Zustand der Maustasten zum Zeitpunkt des Ereignisses wiedergibt. Der Aufzählungstyp MouseButtonConstants definiert dafür die Maskenkonstanten vbLeftButton (1), vbRightButton (2) und vbMiddleButton (4). Auch Shift ist ein Bitvektor, der besagt, welche Funktionstasten der Benutzer zum Zeitpunkt des Ereignisses gedrückt hält. Der Aufzählungstyp ShiftConstants definiert dafür die Maskenkonstanten vbShift (1), vbCtrl (2) und vbAlt (4). X und Y enthalten die aktuelle Position des Mauszeigers zum Zeitpunkt des Ereignisses im Koordinatensystem der Zielkomponente. Der Parameter Data verweist auf ein Objekt vom Typ DataObject, das als Container für den zu übermittelnden Inhalt fungiert und Informationen über die unterstützten Inhaltsformate bereitstellt. State zeigt an, dass die Maus: den Bereich der Komponente eben erst betreten hat (vbEnter); ihre Position innerhalb des Bereichs verändert hat (vbOver); den Bereich verlässt (vbLeave). Im letzten Fall haben die Parameter X und Y den Wert 0.
245
Standardereignisse
Private Sub Form_OLEDragOver(Data As DataObject, _ Effect As Long, Button As Integer, Shift As Integer, X As Single, _ Y As Single, State As Integer)
Standardereignisse
Anwendung
Standardereignisse
................................................... Anwendung
Die Aufgabe bei der Behandlung des OLEDragOver-Ereignisses besteht darin, eine genauere Analyse der Situation vorzunehmen und der Quellkomponente über den Wert des ByRef-Parameters Effect mitzuteilen, welche der im Angebot stehenden Operationen der Benutzer voraussichtlich ausführen wird. (Die Quellkomponente bekommt das Ergebnis mit dem unmittelbar folgenden OLEGiveFeedback-Ereignis zugestellt.) Die Analyse umfasst: erstens die Zustände der Funktionsund Maustasten (Shift und Button), über die der Benutzer die Operation auswählt; zweitens die von der Quellkomponente unterstützten Operationen (Effect); drittens das Datenformat des Inhalts (Data.GetFormat). Eine Aufstellung der Datenformate finden Sie im Abschnitt »OLEDragDrop-Ereignis«. In besonderen Fällen wird auch eine Auswertung des State-Parameters nötig sein, beispielsweise wenn die Komponente das Betreten und Verlassen ihres Bereichs durch die Maus wie ein Hot-spot (lies: über eine Veränderung der eigenen Darstellung) anzeigen soll. Hinweis
................................................... Hinweis Manche Steuerelemente deklarieren für den Data-Parameter eine eigene Variante des Datentyps DataObject. Am Umgang mit dem Wert selbst ändert das nichts. Warnung
................................................... Wa rnung
Eine Auswertung des Inhalts sollte auf dieses Ereignis hin tunlichst unterbleiben, da insbesondere bei späterer Bereitstellung des Inhalts im Rahmen von OLESetData der Aufwand beträchtlich sein kann und das System unnötig belastet – schließlich kommt ein OLEDragOver-Ereignis selten allein. Tipp
................................................... Tipp
Nachdem die Analyse des Maus- und Funktionstastenstatus auch bei der Behandlung des OLEDragDrop-Ereignisses zu gebrauchen ist, empfiehlt es sich, die gesamte Logik dafür in eine eigene Prozedur zu packen. Beispiel
................................................... Beis piel
Private Sub Form_OLEDragOver(Data As DataObject, Effect As Long, _ Button As Integer, Shift As Integer, x As Single, Y As Single, _ State As Integer) ' Stimmt das Datenformat? If Not (Data.GetFormat(vbCFBitmap) Or Data.GetFormat(vbCFDIB)) Then Effect = vbDropEffectNone ' leider nicht Exit Sub End If ' Hotspot-Darstellung If State = vbEnter Then InvertiereDarstellung(1) If State = vbLeave Then InvertiereDarstellung(0) Effect = vbDropEffectNone Exit Sub End If ' Kopieren? If Effect And vbDropEffectCopy > 0 And Shift = 0 Then Effect = vbDropEffectCopy
246
OLEGiveFeedback- Ereignis
End If ' Verschieben? If Effect And vbDropEffectMove > 0 And Shift = vbShiftMask Then Effect = vbDropEffectMove End If End Sub Verwandte Ereignisse
................................................... Verwandte Ereignis s e OLECompleteDrag, OLEDragDrop, OLEGiveFeedback, OLESetData, OLEStartDrag
................................................... Verwandte Them en
OLE-Drag&Drop (S. 501)
OLEGiveFeedback- Ereignis Private Sub Objekt_OLEGiveFeedback(Effect As Long, _ DefaultCursors As Boolean) Betroffene Objekte
................................................... Betro ffene Objekte Animation, CoolBar, DataList, DateTimePicker, DBCombo, DBList, DirListBox, FileListBox, FlatScrollBar, Grid, Image, ImageCombo, ListBox, ListView, MaskEdBox, MSChart, MSFlexGrid, MSHFlexGrid, OLE, PictureBox, PropertyPage, RichTextBox, SSTab, TabStrip, TextBox, TreeView, ToolBar, UserControl, UserDocument Beschreibung
................................................... Bes c hreibung
Das OLEGiveFeedback-Ereignis tritt nach jedem OLEDragOver-Ereignis aufseiten der Quellkomponente auf, wenn diese im manuellen Modus (OLEDragMode = vbDragManual) betrieben wird. Der Parameter Effect ist ein von der Zielkomponente nach Auswertung des Maus- und Funktionstastenstatus sowie gegebenenfalls des Inhaltsformats gesetzter Bitvektor, der die anvisierte Operation ausdrückt. Der Aufzählungstyp OLEDropEffectConstants definiert eine Reihe von Konstanten für die einzelnen Operationen: vbDropEffectNone (0) steht für »keine Operation«, vbDropEffectCopy (1) für »Kopieroperation« und vbDropEffectMove (2) für »Verschiebeoperation«. DefaultCursors ist ein Ausgabeparameter, den die Quellkomponente auf True setzen kann, um die über die Eigenschaften MouseIcon und MousePointer des Screen-Objekts spezifizierte Mauszeigerform einzustellen. Anwendung
................................................... Anwendung
Mit diesem Ereignis erhält die Quellkomponente Gelegenheit, dem Benutzer eine geeignete Rückmeldung für die von ihm anvisierte Operation zukommen zu lassen. Da Komponenten mit OLE-Unterstützung standardmäßig für jede der möglichen Operationen eine andere, vom System her bereitgestellte Mauszeigerform anzeigen, lohnt sich der Austausch der Mauszeigerform meist nur in speziellen Fällen. Eine Behandlung des Ereignisses kann aber auch die Ausgabe einer kurzen Befehlsbeschreibung in der Statusleiste zum Ziel haben oder gar, bei Verwendung einer One-Shot-Logik, eine akustische Rückmeldung. Wenn Sie mit eigenen Mauszeigerformen arbeiten, sollten Sie die alte Mauszeigerform auf jeden Fall speichern und im Zuge der Behandlung von OLEDragComplete wieder herstellen.
247
Standardereignisse
Verwandte Themen
Standardereignisse
Beispiel
................................................... Beis piel
Standardereignisse
Der folgende Code zeigt, wie man für die unterschiedlichen Operationen eigene Cursorformen zur Anzeige bringt. Er geht davon aus, dass die Formen in den EffectOperationIcon-Variablen bereitstehen. Private Sub File1_OLEGiveFeedback(Effect As Long, _ DefaultCursors As Boolean) Select Case Effect Case vbDropEffectCopy ' Kopieren Set Screen.MouseIcon = EffectCopyIcon ' Eigene Form setzen Case vbDropEffectMove ' Verschieben Set Screen.MouseIcon = EffectMoveIcon ' Eigene Form setzen Case vbDropEffectNone ' Operation nicht möglich Set Screen.MouseIcon = EffectNoneIcon ' Eigene Form setzen Case Else DefaultCursors = True End Select DefaultCursors = False ' Eigene Form verwenden End Sub Verwandte Ereignisse
................................................... Verwandte Ereignis s e OLECompleteDrag, OLEDragDrop, OLEDragOver, OLESetData, OLEStartDrag Verwandte Themen
................................................... Verwandte Them en
OLE-Drag&Drop (S. 501)
OLESetData- Ereignis Private Sub Objekt_OLESetData(Data As DataObject, DataFormat As Integer) Betroffene Objekte
................................................... Betro ffene Objekte Animation, CoolBar, DataList, DateTimePicker, DBCombo, DBList, DirListBox, FileListBox, FlatScrollBar, Grid, Image, ImageCombo, ListBox, ListView, MaskEdBox, MSChart, MSFlexGrid, MSHFlexGrid, OLE, PictureBox, PropertyPage, RichTextBox, SSTab, TabStrip, TextBox, TreeView, ToolBar, UserControl, UserDocument Beschreibung
................................................... Bes c hreibung
Das OLESetData-Ereignis fordert die Quellkomponente einer OLE-Drag&Drop-Operation auf, den Inhalt in dem Format DataFormat bereitzustellen. Voraussetzung für das Auftreten des Ereignisses ist allerdings, dass die Quellkomponente das Format zuvor durch einen SetDataAufruf ohne Angabe eines Inhalts (Value) gesetzt hat. Anwendung
................................................... Anwendung
Vater dieses Ereignisses ist die Strategie der »späten Bereitstellung« von OLE-Inhalten. Diese Strategie verfolgt das Ziel, den Ressourcenbedarf für eine OLE-Operation so gering wie möglich zu halten. Würde man umfangreiche Inhalte sozusagen »in vorauseilendem Gehorsam« bereits zu Beginn einer OLE-Drag&Drop-Operation, also auf das OLEDragStart-Ereignis hin, in allen unterstützten Formaten bereitstellen, wäre das mitunter mit einem erheblichen Speicherbedarf verbunden. Da ist es natürlich weitaus sinnvoller, den Inhalt erst dann in einem spezifischen Format bereitzustellen, wenn er in diesem Format tatsächlich benötigt wird.
248
OLESetData- Ereignis
Eine Aufstellung der von Visual Basic für die verschiedenen ActiveX-Steuerelemente direkt unterstützten Standardformate finden Sie im Abschnitt »OLEDragDrop-Ereignis«. Sie haben aber auch die Möglichkeit, eigene Formate in OLE-Operationen zu verwenden. Einzelheiten zur Vorgehensweise entnehmen Sie dem Abschnitt »OLE-Drag&Drop«, S. 501, des Praxisteils. Warnung
................................................... Wa rnung
Ein GetData-Aufruf löst bei der Zielkomponente regulär den Laufzeitfehler 461 aus, wenn die Quellkomponente das spezifizierte Format zwar angeblich unterstützt, aber letztlich keinen Inhalt dafür bereitstellt.
................................................... Beis piel
Das folgende kleine Programm demonstriert die »späte Bereitstellung« sowie die Abfolge der Ereignisse. Das verwendete Formular enthält ein Textfeld, dessen Inhalt sich in das Formular ziehen lässt, dort jedoch in umgekehrter Zeichenfolge ankommt.
Das Textfeld arbeitet mit später Bereitstellung und dreht die Zeichenfolge um Private Sub Form_Load() Text1.OLEDragMode = vbOLEDragAutomatic OLEDropMode = vbOLEDropManual AutoRedraw = True End Sub Private Sub Form_OLEDragDrop(Data As DataObject, Effect As Long, _ Button As Integer, Shift As Integer, X As Single, Y As Single) Print "Form_OLEDragDrop" Print Data.GetData(vbCFText) End Sub Private Sub Text1_OLESetData(Data As DataObject, DataFormat As Integer) Print "Text1_OLESetData" Data.SetData StrReverse(Text1), vbCFText End Sub Private Sub Text1_OLEStartDrag(Data As DataObject, _ AllowedEffects As Long) Print "Text1_OLEStartDrag" Data.SetData , vbCFText ' Inhalt wird später bereitgestellt End Sub
249
Standardereignisse
Beispiel
Standardereignisse
Verwandte Ereignisse
................................................... Verwandte Ereignis s e OLECompleteDrag, OLEDragDrop, OLEDragOver, OLEGiveFeedback, OLEStartDrag Verwandte Themen
................................................... Verwandte Them en
OLE-Drag&Drop (S. 501)
OLEStartDrag- Ereignis
Standardereignisse
Sub Objekt_OLEStartDrag(Data As DataObject, AllowedEffects As Long) Betroffene Objekte
................................................... Betro ffene Objekte Animation, CoolBar, DataList, DateTimePicker, DBCombo, DBList, DirListBox, FileListBox, FlatScrollBar, Grid, Image, ImageCombo, ListBox, ListView, MaskEdBox, MSChart, MSFlexGrid, MSHFlexGrid, OLE, PictureBox, PropertyPage, RichTextBox, SSTab, TabStrip, TextBox, TreeView, ToolBar, UserControl, UserDocument Beschreibung
................................................... Bes c hreibung
Das OLEStartDrag-Ereignis tritt als erstes Ereignis gleich zu Anfang einer OLE-Drag&DropOperation auf. Es informiert die Quellkomponente über den Beginn der Operation und gibt ihr Gelegenheit, die unterstützten Operationen sowie Inhalte und Formate zu setzen. Der Parameter Data verweist auf ein Objekt vom Typ DataObject, das als Container für die zu übermittelnden Inhalte und die dazugehörigen Formatinformationen fungiert. Der Ausgabeparameter AllowedEffects kommt bei der Zielkomponente als Effect-Parameter an und wird dort als Bitvektor interpretiert, dessen gesetzte Bits die möglichen Operationen anzeigen. Der Aufzählungstyp OLEDropEffectConstants definiert eine Reihe von Konstanten dafür: vbDropEffectNone (0) steht für »keine Operation«, vbDropEffectCopy (1) für »Kopieroperation« und vbDropEffectMove (2) für »Verschiebeoperation«. Anwendung
................................................... Anwendung
Die auf OLE basierende Drag&Drop-Operation ermöglicht den dokumentenorientierten Austausch von Daten zwischen einer Quellkomponente und einer Zielkomponente. Medium des Austauschs ist aus der Sicht von Visual Basic das DataObject-Objekt, ein im System verankertes Containerobjekt, dessen Dienste allen OLE-tauglichen Anwendungen für den anwendungsübergreifenden, aber auch -internen Transport von Daten zur Verfügung stehen. Grundlegender Gedanke bei dieser Art von Kommunikation ist, dass die Daten in einem anwendungsunabhängigen Format gehalten sind. Damit kann eine Komponente jederzeit Inhalte von einer anderen Komponente entgegennehmen, sofern sich beide auf ein gemeinsames Format »einigen« können. Für die wichtigsten Standardformate definiert der Aufzählungstyp ClipBoardConstants Formatkonstanten: Formatkonstante
Beschreibung des Formats
vbCFText (1)
Data enthält Zeichenfolge im Format TXT
vbCFBitmap (2)
Data enthält Bitmap im Format BMP
vbCFMetafile (3)
Data enthält Grafik im Zwischendateiformat WMF
vbCFEMetafile (14)
Data enthält Grafik im erweiterten Zwischendateiformat EMF
Standardmäßige Datenformate eines DataObject-Objekts
250
OLEStartDrag- Ereignis
Formatkonstante
Beschreibung des Formats
vbCFDIB (8)
Data enthält geräteunabhängige Bitmap im DIB-Format
vbCFPalette (9)
Data enthält Palette im Format PAL
vbCFFiles (15)
Data enthält Files-Auflistung
vbCFRTF (-16639)
Data enthält Zeichenfolge im RTF-Format
Standardmäßige Datenformate eines DataObject-Objekts
Data.SetData , vbCFBitmap
Im automatischen Modus (OLEDragMode = vbOLEDragAutomatic) überträgt eine Steuerelementkomponente ihren Standardwert noch vor dem OLEStartDrag-Ereignis in das Containerobjekt – im manuellen Modus passiert das nicht, und das ist oft auch erwünscht. Um ein DataObjectObjekt in einen definierten Zustand zu versetzen, rufen Sie die Clear-Methode auf. Sie löscht alle Formatinformationen und Inhalte. Unterbleibt der SetData-Aufruf, kann Visual Basic von sich aus zwischen den Formaten BMP, WMF, EMF und TXT unterscheiden, mehr jedoch nicht. Alle anderen Formate müssen explizit gesetzt werden, damit kein Laufzeitfehler aufseiten der Zielkomponente auftritt. Bei Verwendung eines Formats, das nicht zu den von Visual Basic anerkannten Standardformaten zählt, müssen Sie Inhalte in Form von Byte-Arrays bereitstellen. Außerdem sollte jedes Array Längeninformationen beinhalten, da der OLE-Mechanismus die Daten ohne Längenangabe in einem »hinreichend großen« Puffer übermittelt, der auch größer sein kann als das Array – und es in der Regel auch ist. Beispiel
................................................... Beis piel
Das folgende Beispielprojekt OLEDragStart ist eine etwas kompliziertere Version des Beispiels zu OLESetData, wenngleich das Szenario (ohne dass dies eine Rolle spielt) recht einfach gewählt ist: Der Benutzer kann den Inhalt eines Textfelds mittels OLE-Drag&Drop auf das Formular verschieben. Quell- und Zielkomponente arbeiten jedoch mit zwei unterschiedlichen Formaten: zum einen mit dem standardmäßigen Format vbCFText, zum anderen mit dem eigenen Format »Mein Textformat«.
251
Standardereignisse
Das Containerobjekt ist speziell darauf eingerichtet, Inhalte in verschiedenen Formaten entgegennehmen zu können. Da die Einhaltung der Formate für die sichere Kommunikation eine wichtige Rolle spielt, sind die Formate mit einer eindeutigen Nummer (Handle) assoziiert und in der Systemregistrierung niedergelegt. Damit ist es einer Anwendung prinzipiell möglich, die Nummer für ein registriertes Format in Erfahrung zu bringen und auch eigene Formate zu registrieren. Allerdings bietet Visual Basic dafür keine Funktionen an, so dass man in diesem Fall die entsprechenden Routinen der Win32-API direkt aufrufen muss (RegisterClipBoardFormat und GetClipFormatName). Vom Prinzip her lassen sich Inhalte auch in nicht registrierten Formaten übermitteln, sofern deren Nummern auf beiden Seiten bekannt sind. Das birgt allerdings gewisse Gefahren, wenn unter einer verwendeten Nummer ein anderes Format registriert ist. Eine Quellkomponente reagiert auf das OLEStartDrag-Ereignis, indem sie den AllowedEffectsParameter mit einem Bitvektor versorgt und für jedes unterstützte Format die SetData-Methode des DataObject-Objekts aufruft. Falls der Speicherbedarf für die zugehörigen Inhalte erträglich ist, spricht nichts dagegen, diese bereits in dieser Phase der SetData-Methode über den ValueParameter mitzugeben. Ab einer bestimmten Datenmenge empfiehlt es sich jedoch, mit der Bekanntgabe des Inhalts auf das OLESetData-Ereignis zu warten und die SetData-Methode vorerst nur mit einer Formatkonstanten zu versorgen (späte Bereitstellung).
Standardereignisse
Standardereignisse
Nachdem das Textfeld als Quellkomponente seinen Wert bereits von sich aus im vbCFText-Format in das Containerobjekt einfügt, kann sich die OLEDragStart-Behandlungsroutine darauf beschränken, das eigene Format zu registrieren und zu setzen. Für die Registrierung ist der Aufruf der Win32-API-Funktion RegisterClipboardFormat notwendig, die das Format mit einer Nummer identifiziert, wie sie der SetData-Aufruf erwartet. Unglücklicherweise besteht SetData auf einen Wert vom Typ Integer, während das Add-In API-Viewer für die Funktion den Rückgabetyp Long deklariert. Die einfachste Lösung besteht darin, den Rückgabetyp manuell auf Integer zu ändern, was keinerlei weitere Probleme aufwerfen sollte, da das System Formatkonstanten als 16-Bit-Werte behandelt. Das war die Sicht der Quellkomponente. Die Zielkomponente muss nun ihrerseits bei der Behandlung von OLEDragDrop die Funktion RegisterClipboardFormat aufrufen (im Allgemeinen werden Quelle und Ziel ja nicht demselben Prozess angehören), um die Nummer des Formats in Erfahrung zu bringen. Der Rest ist zwar nicht elegant, aber doch geradlinig: Da das DataObjectObjekt bei eigenen Formaten auf Byte-Arrays besteht, liefert die Methode GetData auch ein solches, was nicht nur aufseiten der Zielkomponente ein gewisses Geschick bei der Konvertierung erfordert (das Beispiel verzichtet der Einfachheit halber auf die Einarbeitung einer Längeninformation und bedient sich für die Konvertierung einer Variant-Variablen). ' Formularmodul: OLEDragStart-Demo Option Explicit Private Declare Function RegisterClipboardFormat Lib "user32" Alias _ "RegisterClipboardFormatA" (ByVal lpString As String) As Integer Private MeinFormat As Integer Private Sub Form_Load() OLEDropMode = vbOLEDropManual Text1.OLEDropMode = vbOLEDragAutomatic AutoRedraw = True End Sub Private Sub Form_OLEDragDrop(Data As DataObject, Effect As Long, Button As Integer, Shift As Integer, X As Single, Y As Single) ' Zuerst im gewöhnlichen Textformat If Data.GetFormat(vbCFText) Then Print Data.GetData(vbCFText) ' Nun im eigenen Format If Data.GetFormat(RegisterClipboardFormat("Mein Textformat")) Then Print Data.GetData(MeinFormat) End If End Sub Private Sub Text1_OLESetData(Data As DataObject, DataFormat As Integer) Dim Daten As Variant ' Zur einfachen Typ-Konversion Dim ByteArray() As Byte ' Für eigenes Format bei SetData If DataFormat = MeinFormat Then ' MeinFormat? Daten = " " + StrReverse(Text1) ' Daten zuerst in Variant ByteArray = Daten ' Typkonversion Data.SetData ByteArray, MeinFormat ' Byte-Array als Inhalt setzen End If End Sub
252
Paint- Ereignis
Private Sub Text1_OLEStartDrag(Data As DataObject, AllowedEffects _ As Long) AllowedEffects = vbDropEffectMove ' Eigenes Format registrieren und setzen MeinFormat = RegisterClipboardFormat("Mein Textformat") If MeinFormat Then Data.SetData , MeinFormat End Sub Verwandte Ereignisse
................................................... Verwandte Ereignis s e
Standardereignisse
OLECompleteDrag, OLEDragDrop, OLEDragOver, OLEGiveFeedback, OLESetData Verwandte Themen
................................................... Verwandte Them en
OLE-Drag&Drop (S. 501)
Paint- Ereignis Sub Form_Paint() Sub Objekt_Paint([Index As Integer]) Betroffene Objekte
................................................... Betro ffene Objekte Form, PictureBox, PropertyPage, UserControl, UserDocument Beschreibung
................................................... Bes c hreibung
Das Paint-Ereignis tritt auf, wenn der Fensterbereich des Objekts teilweise oder vollständig neu gezeichnet werden muss, weil er (teilweise) verdeckt, unsichtbar oder minimiert war respektive vergrößert oder explizit durch einen Aufruf der Refresh-Methode für ungültig erklärt wurde. Voraussetzung für das Ereignis ist aber, dass die AutoRedraw-Eigenschaft den Wert False hat. Durch Behandlung dieses Ereignisses erhält das Objekt Gelegenheit, seinen Fensterbereich aufzufrischen oder – gegebenenfalls mit anderem Inhalt – komplett neu zu zeichnen. Beim Start eines Formulars herrscht die Ereignisreihenfolge: Load, Resize, Paint, GotFocus usw. Anwendung
................................................... Anwendung
Ein Objekt, das auf das Paint-Ereignis reagieren kann, verfügt über die beiden Eigenschaften AutoRedraw und ClipControls. AutoRedraw hat als Voreinstellung den Wert False und ClipControls den Wert True. Wird AutoRedraw auf True gesetzt, speichert Visual Basic den Fensterinhalt des Objekts als Bitmap und restauriert ungültig gewordene Bereiche in eigener Regie, wann immer dies erforderlich ist. Für die meisten Anwendungsfälle dürfte dies die praktischste Einstellung sein, da man sich als Programmierer eigentlich um nichts kümmern muss und an jeder beliebigen Stelle Ausgaben vornehmen kann. Wie alle Automatismen hat er aber auch Nachteile: Er verzehrt Ressourcen, kostet Zeit und lässt einiges an Flexibilität vermissen. Die Eigenschaft ClipControls wirkt sich nur aus, wenn AutoRedraw auf False gesetzt ist. Sie regelt, ob Visual Basic für Steuerelemente Bildausschnitte bei der Restaurierung des Objektbereichs berechnet oder nicht. Ohne diese Berechnung ist die Grafikausgabe schneller, es kann jedoch zu Ungereimtheiten bei der Anzeige sich überlappender Steuerelemente kommen. Visual Basic unterscheidet beim Zeichnen eines Objekts drei Ebenen: 1. Die hintere Ebene – in dieser Ebene landet die Bitmap, die über die Picture-Eigenschaft des Objekts festgelegt ist, über die Print-Methode ausgegebener Text sowie alle Ausgaben der Grafikmethoden Line, Circle und PSet.
253
Standardereignisse
2. Die mittlere Ebene – in dieser Ebene erscheinen fensterlose Steuerelemente, wie das Bezeichnungsfeld (Label), die grafischen Steuerelemente (Shape, Line) sowie alle UserControl-Steuerelemente, deren Windowless-Eigenschaft auf True gesetzt wurde. 3. Die obere Ebene – in dieser Ebene erscheinen alle Steuerelemente, denen ein eigenes Fenster zugeordnet ist. Das sind alle Steuerelemente bis auf die unter 2. genannten.
Standardereignisse
Die folgende Tabelle gibt einen Überblick über die Wirkung der Eigenschaften AutoRedraw und ClipControls, wenn die Grafikausgabe innerhalb oder außerhalb der Paint-Behandlung stattfindet. AutoRedraw
ClipControls
Effekt
True
True
Paint-Ereignis bleibt aus und die Grafikausgabe unterscheidet drei Ebenen.
True
False
Beschleunigte Grafikausgabe in drei Ebenen, da keine Bildausschnitte für Steuerelemente berechnet werden. Platzierte Steuerelemente sollten sich nicht überlappen.
False (Voreinstellung)
True (Voreinstellung)
Grafikausgabe findet in drei Ebenen statt, sofern Grafikmethoden ausschließlich innerhalb der Paint-Routine aufgerufen werden. Beim Aufruf von Grafikmethoden außerhalb von Paint kann das Ergebnis fehlerhaft sein, da die Abfolge beim Zeichnen der mittleren Ebene nicht mehr garantiert ist.
False
False
Die Grafikausgabe findet in drei Ebenen statt. Neu gezeichnet werden nur Rechteckbereiche, die bis zum Auftreten des nächsten Paint-Ereignisses für ungültig erklärt wurden, etwa weil sie verdeckt waren. Beim Aufruf von Grafikmethoden außerhalb von Paint kann das Ergebnis fehlerhaft sein, da die Abfolge der Ebenen nicht mehr garantiert ist.
Warnung
................................................... Wa rnung
Wenn Sie mit einer Paint-Routine arbeiten, sollten alle expliziten Grafikausgaben innerhalb dieser Routine erfolgen. Tipp
................................................... Tipp
Innerhalb einer Ebene legt die Tabulatorordnung fest, in welcher Reihenfolge überlappende Steuerelemente einander verdecken. Um eine eigene Reihenfolge festzulegen, können Sie die ZOrder-Methode der beteiligten Steuerelemente aufrufen, um diese nacheinander an die oberste Position zu bringen. Der folgende Code aus dem Beispielprojekt Schach1 arbeitet mit einem Array fensterloser Steuerelemente. Nach jeder Verschiebung einer Figur wird die Reihenfolge wieder hergestellt: For i = 31 To 0 Step -1 figur(i).ZOrder (0) Next i
' Steuerelement ganz nach oben
Wenn Sie den Fensterbereich eines Objekts explizit restaurieren wollen, zum Beispiel nach einem Resize-Ereignis, sollten Sie die Refresh-Methode aufrufen.
254
Paint- Ereignis
Beispiel
................................................... Beis piel
Der folgende in zwei Varianten vorgestellte Code zeichnet eine Ellipse, die immer exakt den Client-Bereich des Formulars ausfüllt. Die erste Variante verlässt sich darauf, dass Visual Basic den Formularbereich automatisch neu zeichnet, die zweite verwendet eine Paint-Routine. Ändert man die Größe des Formulars, flackert die Anzeige im ersten Fall ein wenig, im zweiten Fall jedoch nicht.
Standardereignisse
' Erste Variante Private Sub Form_Load() AutoRedraw = True End Sub Private Sub Form_Resize() Dim Radius If ScaleWidth > ScaleHeight Then Radius = ScaleWidth / 2 Else Radius = ScaleHeight / 2 End If Cls Circle (ScaleWidth / 2, ScaleHeight / 2), Radius, , , , ScaleHeight / ScaleWidth End Sub
Die Ellipse füllt das Formular in jeder Größe ganz aus ' Zweite Variante Private Sub Form_Load() AutoRedraw = False End Sub Private Sub Form_Paint() Dim Radius If ScaleWidth > ScaleHeight Then Radius = ScaleWidth / 2 Else Radius = ScaleHeight / 2 End If Cls Circle (ScaleWidth / 2, ScaleHeight / 2), Radius, , , , ScaleHeight / ScaleWidth End Sub
255
Standardereignisse
Private Sub Form_Resize() Refresh End Sub
QueryUnload- Ereignis Form_QueryUnload(Cancel As Integer, UnloadMode As Integer) MIDForm_QueryUnload(Cancel As Integer, UnloadMode As Integer) Betroffene Objekte
Standardereignisse
................................................... Betro ffene Objekte Form, MDIForm Beschreibung
................................................... Bes c hreibung
Dieses dem Unload-Ereignis vorangehende Ereignis teilt einem Formular mit, dass es geschlossen werden soll. Falls die gesamte Anwendung beendet werden soll, schickt Windows dieses Ereignis der Reihe nach an alle geöffneten Formulare. QueryUnload stellt eine Anfrage dar, die ein einzelnes Formular (im Namen aller Formulare) auch ablehnen kann, indem es den Ausgabeparameter Cancel auf einen Wert ungleich 0 setzt. MDI-Formulare sehen das Ereignis vor ihren untergeordneten Formularen (beim Unload-Ereignis ist die Reihenfolge dagegen genau andersherum). Der Parameter UnloadMode liefert den Grund für das Ereignis. Die folgende Tabelle gibt einen Überblick über die verschiedenen Gründe und die über den Aufzählungstyp QueryUnloadConstants dafür definierten Konstanten. Konstante
Grund
vbFormControlMenu (0)
Der Benutzer beendet die Anwendung über das Systemmenü.
vbFormCode (1)
Eine Unload-Anweisung wurde für das Formular ausgeführt.
vbAppWindows (2)
Windows soll heruntergefahren werden.
vbAppTaskManager (3)
Der Task-Manager fordert die Anwendung zum Abbrechen auf.
vbFormMDIForm (4)
Das übergeordnete MDI-Formular soll geschlossen werden.
vbFormOwner (5)
Das übergeordnete Formular soll geschlossen werden.
Anwendung
................................................... Anwendung
Eine Anwendung kann dieses Ereignis behandeln, um sicherzustellen, dass alle Operationen in allen Formularen abgeschlossen sind und alle Daten gespeichert wurden. Andernfalls sollte der Benutzer die Möglichkeit erhalten, dies nachzuholen. Beispiel
................................................... Beis piel
Der folgende Code zeigt die typische Reaktion auf QueryUnload. Private Textgeändert As Boolean ... Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer) If Textgeändert Then Select Case MsgBox("Datei speichern?", vbExclamation + vbYesNoCancel) Case vbCancel ' Programmabbruch vereiteln Cancel = True
256
Resize- Ereignis
Case vbYes mnuDateiSpeichern_Click Cancel = False Case vbNo Cancel = False End Select End If End Sub
' Datei speichern, dann Unload
' Datei nicht speichern, sofort Unload
Verwandte Ereignisse
................................................... Verwandte Ereignis s e
Standardereignisse
Unload
Resize- Ereignis Sub Form_Resize() Sub Objekt_Resize([HeightNew As Single, WidthNew As Single]) Betroffene Objekte
................................................... Betro ffene Objekte CoolBar, Data, DataReport, Form, MDIForm, OLE, PictureBox, UserControl, UserDocument Beschreibung
................................................... Bes c hreibung
Das Resize-Ereignis zeigt einem Objekt an, dass sich seine Größe geändert hat. Die Ursache dafür kann entweder eine entsprechende Benutzeraktion wie »Vergrößern«, »Verkleinern«, »Maximieren« oder »Wiederherstellen« sein, aber auch die Ausführung einer Anweisung, die Einfluss auf die Größe oder Sichtbarkeit des Objekts hat. Für ein Formular tritt das Ereignis erstmals nach dem Load-Ereignis auf, wenn es automatisch gestartet wurde, oder im Zuge eines Show-Aufrufs, der es sichtbar macht. Ansonsten ist es Seiteneffekt, wenn eine der Eigenschaften WindowState, Height, Width einen neuen Wert erhält oder die Visible-Eigenschaft auf True gesetzt wird. Die Parameter HeightNew und WidthNew geben die neuen Abmessungen im aktuellen Koordinatensystem an. Anwendung
................................................... Anwendung
Ein Objekt behandelt dieses Ereignis, um auf Größenänderungen reagieren zu können und seinen Bereich gegebenenfalls neu zu zeichnen oder zu organisieren. Häufig wird in einer ResizeRoutine auch das Koordinatensystem via ScaleHeight, ScaleWidth, ScaleLeft, ScaleTop geändert, um die Grafikausgaben von der Bereichsgröße unabhängig zu machen. Falls die AutoRedraw-Eigenschaft eines Formulars auf False gesetzt wurde, sollte die ResizeRoutine einen Refresh-Aufruf enthalten, wenn sich das Koordinatensystem geändert hat. Beispiel
................................................... Beis piel
Die folgende Resize-Routine reagiert auf Änderungen der Formularabmessungen, indem sie eine Periode der Sinuskurve ausgibt, die immer genau in den Client-Bereich des Formulars eingepasst ist. Der Einfachheit halber ändert der Code das Koordinatensystem, nicht die Parameter der Kurve. Private Sub Form_Resize() Dim Winkel As Single Dim Pi As Single Pi = 4 * Atn(1)
257
Standardereignisse
ScaleHeight = -2 ' Positive Koordinaten oben! Me.ScaleTop = 1 ' Ursprung in die Mitte ScaleWidth = 2 * Pi ' reicht für eine volle Sinusperiode Cls ' Bereich löschen ' Sinuskurve zeichnen For Winkel = 0 To 2.1 * Pi Step Pi / 30 Line -(Winkel, Sin(Winkel)) Next Winkel End Sub
Standardereignisse
Verwandte Themen
................................................... Verwandte Them en
Bildlauf – ein kleiner Betrachter für große Bilder (S. 545); HexView – eine schnelle Textansicht für große Dateien (S. 551)
Terminate- Ereignis Sub Objekt_Terminate() Betroffene Objekte
................................................... Betro ffene Objekte ClassModule, DataReport, DHTMLPageDesigner, Form, MDIForm, PropertyPage, UserControl, UserDocument, WebClass Beschreibung
................................................... Bes c hreibung
Terminate ist das letzte Ereignis, das ein Objekt ereilt. Es tritt ein, sobald keine Verweise auf das Objekt mehr existieren, das heißt, wenn das Fenster des Objekts entladen wurde und der Geltungsbereich der letzten Objektvariable, die noch auf das Objekt verwiesen hat, erloschen ist oder auf den Wert Nothing gesetzt wurde. Für Objekte mit eigenem Fenster folgt das TerminateEreignis unmittelbar auf das Unload-Ereignis, nicht jedoch für Objekte, die von einer auf ClassModule aufsetzenden Klasse abstammen. Für diese Objekte fungiert Terminate als alleiniger Destruktor. Anwendung
................................................... Anwendung
Die Ereignisse Terminate und Initialize treten beide nur ein einziges Mal auf. Aus diesem Grund sollte eine Terminate-Routine dazu verwendet werden, genau die Ressourcen wieder freizugeben, die während Initialize angefordert wurden. Warnungen
................................................... Wa rnungen Formularvariablen sollten nicht ohne vorherigen Unload-Aufruf auf Nothing gesetzt werden, da sie sonst geladen bleiben (und ebenso alle Objekte, die dem Formular untergeordnet sind, das heißt: alle Steuerelemente sowie gegebenenfalls weitere Formulare). Terminate tritt nicht auf, wenn ein Formular oder die Instanz einer Klasse aus dem Hauptspeicher entfernt, weil die Anwendung nicht ordnungsgemäß beendet wurde, oder wenn die Anwendung durch eine End-Anweisung beendet wird, ohne dass zuvor alle bestehenden Instanzen der Klasse oder des Formulars aus dem Hauptspeicher entfernt wurden. Beispiel
................................................... Beis piel
Private Sub Form_Terminate Redim MeinArray(0) ' Arrayelemente freigeben End Sub
258
Unload- Ereignis
Verwandte Ereignisse
................................................... Verwandte Ereignis s e Initialize, Load, Unload
Unload- Ereignis Sub Objekt_UnLoad(Cancel As Integer) Betroffene Objekte
................................................... Betro ffene Objekte Form, MDIForm, PropertyPage
................................................... Bes c hreibung
Das Unload-Ereignis tritt auf, wenn ein Formularobjekt sein Fenster sowie alle darin enthaltenen Steuerelemente freigibt bzw. entlädt – das Objekt selbst bleibt bestehen. Ursache des Ereignisses kann eine Unload-Anweisung sein, aber auch eine Benutzeraktion, die das Schließen des Fensters oder die Beendigung des gesamten Programms zum Ziel hat – beispielsweise ein Klick auf die Schaltfläche SCHLIEßEN in der Titelleiste des Fensters oder der Aufruf eines entsprechenden Menübefehls. Der Ausgabeparameter Cancel lässt sich auf einen Wert ungleich 0 oder auf True setzen, um das Entladen zu verhindern. Ein Herunterfahren von Windows wird dadurch aber nicht abgebrochen, das kann nur bei der Behandlung von QueryUnload geschehen. Anwendung
................................................... Anwendung
Erhält ein Objekt das Unload-Ereignis, heißt das noch lange nicht, dass es selbst auch abgebaut wird. Das ist erst der Fall, wenn ein Terminate-Ereignis folgt. Es zeugt sogar von gutem Programmierstil, Formulare zwischendurch mit der Unload-Anweisung zu entladen, wenn sie gerade nicht benötigt werden. Das Objekt selbst bleibt dabei erhalten, und das Fenster lässt sich jederzeit über einen Load- oder besser Show-Aufruf erneut laden und zur Anzeige bringen, ja, es reicht auch ein Zugriff auf die Visible-Eigenschaft. Das spart Ressourcen, kann aber zu Verzögerungen bei der erneuten Anzeige des Formulars führen, weil das Fenster sowie alle darauf befindlichen Steuerelemente neu initialisiert werden müssen. Warnungen
................................................... Wa rnungen Visual Basic entlädt das Fenster eines Formularobjekts nicht automatisch, wenn die letzte Referenz auf das Objekt auf Nothing gesetzt wird. Das Entladen muss explizit durch den Benutzer oder durch den Programmcode geschehen. Erfolgt für ein entladenes Formularfenster ein Hide-Aufruf, bringt dieser – entgegen aller Logik – das Fenster zur Anzeige. Tipp
................................................... Tipp
Um ein Fenster verschwinden zu lassen, können Sie auch die Hide-Methode des Formularobjekts ausführen. In diesem Fall wird das Fenster zwar unsichtbar, bleibt aber geladen. Das Fenster selbst ist dann inaktiv und kann insbesondere den Fokus nicht erhalten. Um das Fenster wieder sichtbar zu machen, kann die Visible-Eigenschaft auf True gesetzt werden oder ein ShowAufruf erfolgen. Beispiel
................................................... Beis piel
Private Sub Form_Unload(Cancel As Integer) If vbYes = MsgBox("Sind Sie sicher?", vbYesNo, "Fenster schließen") _
259
Standardereignisse
Beschreibung
Standardereignisse
Then Cancel = 0 Else Cancel = 1 End If End Sub Verwandte Ereignisse
................................................... Verwandte Ereignis s e
Standardereignisse
Initialize, Load, Terminate
Validate- Ereignis Sub Objekt_Validate(Cancel As Boolean) Betroffene Objekte
................................................... Betro ffene Objekte Adodc, Animation, CheckBox, ComboBox, CommandButton, Data, DataGrid, DataList, DataRepeater, DateTimePicker, DBCombo, DBGrid, DBList, DirListBox, DriveListBox, FileListBox, HScrollBar, ListBox, ListView, MaskEdBox, MMControl, MonthView, MSChart, MSFlexGrid, MSHFlexGrid, MSTab, OptionButton, PictureBox, PropertyPage, RemoteData, RichTextBox, Slider, TabStrip, TextBox, TreeView, VScrollBar Beschreibung
................................................... Bes c hreibung
Das unmittelbar vor LostFocus eintreffende Validate-Ereignis kündigt einem Steuerelement an, dass es den Fokus abgeben soll. Setzt das Steuerelement den Ausgabeparameter Cancel auf True, kann es die Weitergabe des Fokus unterbinden – etwa, wenn sein aktueller Wert gegen eine Gültigkeitsregel verstößt. Voraussetzung für das Auftreten dieses Ereignisses ist allerdings, dass die CausesValidation-Eigenschaft sowohl des betroffenen Steuerelements als auch des Steuerelements, auf das der Fokus übergehen soll, den Wert True hat. Anwendung
................................................... Anwendung
Die normale Reaktion auf das Validate-Ereignis ist eine Gültigkeitsprüfung des Werts bzw. des aktuellen Zustands des Steuerelements. Stellt sich der Wert als »ungültig« heraus, ist kein besonderes Verhalten vorgeschrieben: Die Behandlungsroutine kann eine belehrende Fehlermeldung ausgeben und die Fokusabgabe verweigern, sie kann aber auch den Wert schlichtweg korrigieren und der Weitergabe des Fokus nichts in den Weg legen. Falls auch nur ein Steuerelement auf einem Formular eine Gültigkeitsprüfung durchführt, wird die CausesValidation-Eigenschaft der anderen als Empfänger des Fokus in Frage kommenden Steuerelemente im Allgemeinen auf True gesetzt sein. In besonderen Fällen kann es jedoch Vorteile bringen, die CausesValidation-Eigenschaft bestimmter Steuerelemente – etwa einer ABBRECHEN- oder HILFE-Schaltfläche – auf False zu setzen. Der Benutzer kann dann den Fokus auf diese Steuerelemente verschieben, ohne das Validate-Ereignis des verlassenen Steuerelements auszulösen. Sobald der Fokus jedoch auf ein Steuerelement weitergehen soll, dessen CausesValidation-Eigenschaft auf True gesetzt wurde, tritt zunächst einmal das Validate-Ereignis des ursprünglichen Steuerelements auf. Beachten Sie aber, dass dem Benutzer auf diese Weise gegebenenfalls das Schließen des Formulars möglich werden kann, ohne dass die Gültigkeitsprüfung des zuletzt verlassenen Steuerelements zum Zuge kommt. Die Unload-Behandlung für das Formular sollte daher einen ValidateControls-Aufruf enthalten. Die Behandlung des Validate-Ereignisses ist angezeigt, wenn der Benutzer ein Datenfeld ausfüllen muss oder wenn er nur bestimmte Daten in ein Datenfeld eintragen darf.
260
Global- Objekt
Warnung
................................................... Wa rnung
Vom Prinzip her könnte man auch versuchen, eine Gültigkeitsprüfung im Zuge von LostFocus zu implementieren. Das kann aber Probleme verursachen, da das Steuerelement zu dem Zeitpunkt, an dem das Ereignis auftritt, den Fokus bereits abgegeben hat. Es wäre also ein SetFocus-Aufruf erforderlich, der aber wiederum dem neuen Inhaber des Fokus diesen sofort wieder entreißt und ihm das LostFocus-Ereignis beschert. Falls dieses Steuerelement gleichfalls nicht bereit ist, den Fokus herzugeben und seinerseits einen SetFocus-Aufruf durchführt, kommt es zu einer Ereignisflut, die das System destabilisieren kann. Beispiel
Global- Objekt
................................................... Beis piel
Private Sub Text1_Validate(Cancel As Boolean) Cancel = Not IsDate(Text1) End Sub Verwandte Ereignisse
................................................... Verwandte Ereignis s e LostFocus, GotFocus
Global- Objekt Die Einbettung der traditionellen Standardfunktionen von Basic in das inzwischen vollkommen objektorientierte Programmiermodell von Visual Basic geschieht formal gesehen über ein Global-Objekt, das den übergeordneten Kontext für alle Module einer Anwendung bereitstellt. Damit sind die in der Klasse Global definierten Methoden größtenteils »alte Bekannte«, die (nach wie vor) den operativen Kern der Sprache selbst ausmachen. Mit den Eigenschaften des Global-Objekts verhält es sich anders; sie sind ihrerseits Objekte und verkapseln die Standardumgebung, in die jedes Visual-Basic-Programm sozusagen beim Start »hineingeboren« wird. Diese Objekte müssen (und können) nicht eigens instanziiert werden, da sie von Visual Basic automatisch im Verlauf des Programmstarts angelegt werden und ihre Bezeichner global bekannt sind. Eine Qualifikation mit dem Bezeichner »Global« ist weder nötig noch möglich.
................................................... Eigens c ha ften App, Calendar, ClipBoard, Date, Forms, Now, Printer, Printers, Screen, Time, Timer Die folgende Tabelle gibt einen Überblick über die nicht an anderer Stelle vorgestellten globalen Eigenschaften: Eigenschaft
Beschreibung
App
App-Objekt, das die aktuelle Laufzeitinstanz repräsentiert
Calendar
Integerwert, der den Kalender für alle Datumsfunktionen festlegt. Voreinstellung ist vbCalGreg (0) für den Gregorianischen Kalender. Alternative ist der Hijri-Kalender: vbCalHijri (1)
ClipBoard
ClipBoard-Objekt, das die OLE-Zwischenablage und ihre Operationen repräsentiert
Err
ErrObject-Objekt, das den Fehlerkontext der Anwendung repräsentiert
261
Global- Objekt
Eigenschaft
Beschreibung
Forms
Object-Auflistung aller von der aktuellen Laufzeitinstanz geladenen Formularinstanzen
Printer
Printer-Objekt, das den aktuellen Standarddrucker repräsentiert
Printers
Object-Auflistung aller auf dem System installierten Drucker
Screen
Screen-Objekt, das die Grafikanzeige auf dem Bildschirm repräsentiert
................................................... Methoden
Global- Objekt
Methoden
Abs, AppActivate, Asc, AscB, AscW, Atn, Beep, CallByName, CBool, CByte, CCur, CDate, CDbl, CDec, ChDir, ChDrive, Choose, Chr, ChrB, ChrW, CInt, CLng, Command, Cos, CreateObject, CSng, CStr, CurDir, CVar, CVDate, CVErr, DateAdd, DateDiff, DatePart, DateSerial, DateValue, Day, DDB, DeleteSetting, Dir, DoEvents, Environ, EOF, Err, Error, Exp, FileAttr, FileCopy, FileDateTime, FileLen, Filter, Fix, Format, FormatCurrency, FormatDateTime, FormatNumber, FormatPercent, FreeFile, FV, GetAllSettings, GetAttr, GetObject, GetSetting, Hex, Hour, IIF, InputBox, InStr, InStrB, InStrRev, Int, IPmt, IRR, IsArray, IsDate, IsEmpty, IsError, IsMissing, IsNull, IsNumeric, IsObject, Join, Kill, LCase, Left, LeftB, Len, Load, LoadPicture, LoadResData, LoadResPicture, LoadResString, Loc, LOF, Log, LTrim, Mid, MidB, Minute, MIRR, MkDir, Month, MonthName, MsgBox, NPer, NPV, Oct, Partition, Pmt, PPmt, PV, QBColor, Randomize, Rate, Replace, Reset, RGB, Right, RightB, RmDir, Rnd, Round, RTrim, SavePicture, SaveSetting, Second, Seek, SendKeys, SetAttr, Sgn, Shell, Sin, SLN, Space, Split, Sqr, Str, StrComp, StrConv, String, StrReverse, Switch, SYD, Tan, TimeSerial, TimeValue, Trim, TypeName, UCase, Unload, Val, VarType, Weekday, WeekdayName, Year Die folgende Tabelle gibt einen Überblick über die nicht an anderer Stelle vorgestellten Methoden. Die Prototypen lauten: Function CallByName(Object As Object, ProcName As String, _ CallType As VbCallType, Args()) As Variant Sub CallByName(Object As Object, ProcName As String, _ CallType As VbCallType, Args()) Function Command() As String Function CreateObject(Class As String, [ServerName As String]) As Object Sub DeleteSetting(AppName As String, [Section As String], _ [Key As String]) Sub DoEvents() Function GetAllSettings(AppName As String, Section As String) _ As String()() Function GetObject([PathName As String], _ [AnwName.ObjektTyp As String]) As Object Function GetSetting(AppName As String, Section As String, _ Key As String, [Default]) As String Function InputBox(Prompt, Title, Default, XPos, YPos, HelpFile, _ Context) As String
262
Global- Objekt
Function Load(Object As Object) Function LoadPicture([FileName], [Size], [ColorDepth], [X], [Y]) _ As Picture Function LoadResData(Id, ResType As Integer) As Byte() Function LoadResPicture(Id, ResType As Integer) As Picture
Function LoadResString(Id As Long) As String Function QBColor(Color As Integer) As Long
Global- Objekt
Function RGB(Red As Integer, Green As Integer, Blue As Integer) As Long Sub SavePicture(Picture As Picture, FileName As String) Sub SaveSetting(AppName As String, Section As String, _ Key As String, Setting As String)
Sub SendKeys(String As String, [Wait As Boolean]) Sub Unload(Object) Methode
Beschreibung
CallByName
Ermöglicht den Aufruf einer Methode oder Eigenschaft ProcName eines zur Laufzeit gebundenen Automatisierungsobjekts Object unter Angabe des Bezeichners ElementName als Zeichenfolge, gefolgt von der Art des Elements (vbGet, vbLet, vbMethod, vbSet) und der Parameterliste Params, die auch als Variant-Array übergeben werden kann. Der Aufruf kann in Funktions- oder Prozedurform erfolgen.
Command
Liefert die Kommandozeilenargumente, die beim Aufruf der Anwendung angegeben wurden
CreateObject
Erzeugt eine neue Instanz eines ActiveX-Objekts optional unter Spezifikation eines Servers und liefert einen Verweis darauf zurück
DeleteSetting
Löscht persistente Einstellungen eines Programms aus der Systemregistrierung. Die Methode wird unter Angabe des Anwendungsnamens als Schlüssel, eines Abschnittsbezeichners als Unterschlüssel (optional) und eines Bezeichners (optional) aufgerufen und löscht den entsprechenden Eintrag, Unterschlüssel oder Schlüssel.
DoEvents
Unterbricht die Ausführung der aktuellen Routine, um die Behandlung gegebenenfalls anstehender Ereignisse zu ermöglichen, und setzt die Routine danach fort
GetAllSettings
Ermittelt persistente Einstellungen eines Programms aus der Systemregistrierung. Die Funktion wird unter Angabe des Anwendungsnamens als Schlüssel sowie eines Abschnittsbezeichners als Unterschlüssel aufgerufen und liefert ein zweidimensionales String-Array, das in der ersten Dimension alle in dem Abschnitt gespeicherten Bezeichner und in der zweiten die dazugehörigen Werte wiedergibt.
GetObject
Liefert eine Object-Referenz auf ein ActiveX-Objekt
263
Global- Objekt
Global- Objekt
Methode
Beschreibung
GetSetting
Ermittelt eine persistente Einstellung eines Programms aus der Systemregistrierung. Die Funktion wird unter Angabe des Anwendungsnamens als Schlüssel, eines Abschnittsbezeichners als Unterschlüssel sowie eines Bezeichners aufgerufen und liefert einen String-Wert, der den unter dem Bezeichner abgelegten Wert wiedergibt.
InputBox
Ruft ein Dialogfeld auf, das den Benutzer zur Eingabe eines String-Werts auffordert und diesen als Ergebnis liefert
Load
Lädt ein Formular oder Steuerelement für die spätere Anzeige in den Hauptspeicher. Die Methode lässt sich insbesondere für die dynamische Erweiterung von Steuerelemente-Arrays verwenden.
LoadPicture
Lädt eine Bitmap aus einer Datei und liefert sie als Objekt vom Typ Picture. Die Methode unterstützt die gängigen Grafikformate BMP, JPG, JPEG, GIF, WMF, EMF, ICO und CUR.
LoadResData
Lädt eine Ressource aus einer Ressourcendatei (RES) und liefert deren Daten in Form eines Byte-Arrays
LoadResPicture
Lädt eine Bildressource (Bitmap, Cursor, Symbol) aus einer Ressourcendatei (RES) und liefert sie als Picture-Objekt
LoadResString
Lädt eine Zeichenfolgenressource aus einer Ressourcendatei (RES) und liefert sie als String-Wert
QBColor
Liefert den zu einer QBasic-Farbe gehörigen Farbwert als Long
RGB
Komponiert einen Farbwert aus den Farbanteilen Rot, Grün und Blau und liefert diesen als Long
SavePicture
Speichert ein als Picture-Objekt vorliegendes Bild in einer Datei
SaveSetting
Speichert persistente Einstellungen des Programms in der Systemregistrierung. Die Anweisung wird unter Angabe des Anwendungsnamens, eines Abschnittsbezeichners, eines Schlüssels sowie eines Schlüsselwerts aufgerufen.
SendKeys
Sendet eine Folge von Tastaturereignissen an das aktive Fenster (Formular, Steuerelement im Formular) der im Vordergrund ausgeführten Anwendung und ermöglicht so deren Fernsteuerung
Shell
Ermöglicht den Aufruf einer anderen Anwendung und gibt optional einen Fensterstil dafür vor
Unload
Entfernt ein Formular oder Steuerelement Object aus dem Hauptspeicher
App- Objekt Public App As App Beschreibung
................................................... Bes c hreibung
Dem App-Objekt liegt als Datentyp die in der Bibliothek VB definierte gleichnamige Klasse App zugrunde. Visual Basic vereinbart dieses Objekt automatisch in jeder Anwendung mit globalem Geltungsbereich. Es repräsentiert die Laufzeitinstanz bei Ausführung der Anwendung und speichert eine Reihe von Informationen über die Anwendung, darunter die Versions- und Herstel-
264
App- Objekt
lerinformationen und den Pfad der ausführbaren Datei sowie diverse Informationen über die konkrete Laufzeitinstanz (so beispielsweise die Tatsache, ob bereits eine weitere Instanz der gleichen Anwendung in Ausführung ist) sowie über das Verhalten der Anwendung im Zusammenspiel mit OLE-Servern. Eigenschaften
................................................... Eigens c ha ften Ein guter Teil der Eigenschaften (insbesondere die zur Laufzeit schreibgeschützten) des AppObjekts lassen sich interaktiv im Eigenschaftsfenster PROJEKTEIGENSCHAFTEN des zugehörigen Projekts setzen. Die folgende Tabelle gibt einen Überblick: Beschreibung
Comments
Zur Laufzeit schreibgeschützte Zeichenfolge für Kommentare zur aktuellen Version
CompanyName
Zur Laufzeit schreibgeschützte Zeichenfolge für Firmenname. Vorbesetzt mit Firmenname aus der Visual-Basic-Lizenz.
EXEName
Zur Laufzeit schreibgeschützte Zeichenfolge mit dem Anwendungsnamen ohne Erweiterung
FileDescription
Zur Laufzeit schreibgeschützte Zeichenfolge mit Dateibeschreibung
HelpFile
Zur Laufzeit schreibgeschützte Zeichenfolge; enthält Pfad für die zur Anwendung gehörige kontextbezogene Hilfedatei.
hInstance
Zur Laufzeit schreibgeschützter Handle der Laufzeitinstanz (für Aufrufe der Win32-API)
LegalCopyright
Zur Laufzeit schreibgeschützte Zeichenfolge mit Copyrightinformationen
LegalTrademarks
Zur Laufzeit schreibgeschützte Zeichenfolge mit Warenzeicheninformationen
LogMode
Zur Laufzeit schreibgeschützter Protokollmodus; wird von StartLogging-Methode gesetzt. Der Wert dieser Eigenschaft ist ein Vektor und kann eine Kombination aus Werten des Aufzählungstyps LogEventTypeConstants sein: vbLogAuto (0) – Protokoll wird unter Windows 9x in durch LogPath spezifizierte Datei ausgegeben, unter Windows NT in Ereignisprotokoll (Quelle ist »VBRunTime«, Title wird als genauere Beschreibung der Instanz ausgegeben). vbLogOff (1) – keine Protokollierung mehr, LogEvent-Aufrufe bleiben ohne Effekt vbLogToFile (2) – Ausgabe in durch LogPath spezifizierte Datei; ein ungültiger Dateiname erzwingt vbLogOff; falls LogPath leere Zeichenfolge, ist vbevents.log die Vorgabe vbLogToNT (3) – Ausgabe in NT-Ereignisprotokoll; erzwingt vbLogOff unter Windows 9x vbLogOverwrite (16) – legt Protokolldatei bei jedem Start neu an vbLogThreadID (32) – Meldungen werden um Thread-ID ergänzt (bei Multithreaded-Anwendung); erzwingt vbLogOff bei Singlethreaded-Anwendung
265
Global- Objekt
Eigenschaft
Global- Objekt
Global- Objekt
Eigenschaft
Beschreibung
LogPath
Zur Laufzeit schreibgeschützte Zeichenfolge mit Namen der Protokolldatei, wird von StartLogging-Methode gesetzt
Major
Zur Laufzeit schreibgeschützter Wert für primäre Versionsnummer
Minor
Zur Laufzeit schreibgeschützter Wert für sekundäre Versionsnummer
NonModalAllowed
Laufzeiteigenschaft vom Typ Boolean – ungebundene Anzeige
OLERequestPendingMsgText
Laufzeiteigenschaft vom Typ String für die Spezifikation einer eigenen Meldung im Zusammenhang mit einer Zeitüberschreitung bei der Bearbeitung einer Anforderung durch einen ActiveX-Server (weiteres unter OLEServerBusyRaiseError)
OLERequestPendingMsgTimeOut
Laufzeiteigenschaft für die Anzahl der Millisekunden (Vorgabewert: 5 Sekunden), welche die Instanz auf die Anwort eines ActiveX-Servers wartet; gilt auch für ActiveX-Dokumente, die via OLE-Container-Steuerelement eingebettet bzw. verknüpft sind
OLERequestPendingMsgTitle
Laufzeiteigenschaft vom Typ String; Vorgabewert ist die TitleEigenschaft des Objekts
OLEServerBusyMsgText
Laufzeiteigenschaft vom Typ String für die Spezifikation einer eigenen Meldung im Zusammenhang mit einer Zeitüberschreitung bei einer Anforderung an einen ActiveX-Server (Weiteres unter OLEServerBusyRaiseError)
OLEServerBusyMsgTitle
Laufzeiteigenschaft vom Typ String; Vorgabewert ist die TitleEigenschaft des Objekts
OLEServerBusyRaiseError
Laufzeiteigenschaft vom Typ Boolean – bei Auftreten eines Timeouts im Verlauf einer Anforderung an einen ActiveX-Server bestimmt der Vorgabewert True dieser Eigenschaft, dass umgehend der Automatisierungsfehler -2147418111 (&H80010001) ausgelöst werden soll. Bei False erscheint entweder der standardmäßige Dialog KOMPONENTE BESCHÄFTIGT (bzw. KOMPONENTENANFORDERUNG WIRD BEARBEITET) oder ein mit den Werten der Eigenschaften OLEServerBusyMsgText und OLEServerBusyMsgTitle (bzw. OLERequestPendingMsgText und OLERequestPendingMsgTitle) zusammengestellter Dialog – je nachdem, ob der Wert der Eigenschaft OLEServerBusyMsgText (bzw. OLERequestPendingMsgText) die leere Zeichenfolge ist oder nicht. Jeden der Dialoge kann der Benutzer mit OK oder mit ABBRECHEN beenden. Ein Abbruch löst den genannten Automatisierungsfehler aus.
OLEServerBusyRaiseTimeOut
Laufzeiteigenschaft für die Anzahl der Millisekunden (Vorgabewert: 10 Sekunden), welche die Instanz auf die Anwort eines ActiveX-Servers wartet; gilt auch für ActiveX-Dokumente, die via OLE-Container-Steuerelement eingebettet bzw. verknüpft sind
Path
Zur Laufzeit schreibgeschützte Zeichenfolge, die den vollständigen Pfad der VBP-Projektdatei bzw. der EXE-Datei wiedergibt.
266
App- Objekt
Beschreibung
PrevInstance
Zur Laufzeit schreibgeschützter Boolean-Wert – True besagt, dass eine andere Instanz der gleichen Anwendung in Ausführung ist, False das Gegenteil
ProductName
Zur Laufzeit schreibgeschützte Zeichenfolge für Produktnamen
RetainedProject
Zur Laufzeit schreibgeschützter Boolean-Wert – True besagt, Laufzeitinstanz bleibt im Speicher geladen, False (Vorgabe), das Gegenteil
Revision
Zur Laufzeit schreibgeschützter Wert für Revisionsanteil der Versionsnummer
StartMode
Zur Laufzeit schreibgeschützter Wert, der besagt, ob eine Laufzeitinstanz im Stand-alone-Modus (vbSModeStandalone = 0) oder als ActiveX-Komponente (vbSModeAutomation = 1) gestartet wurde
TaskVisible
Boolean-Wert, der bestimmt, ob die Laufzeitinstanz in der Taskliste von Windows als Eintrag erscheint (True) oder nicht (False)
ThreadID
Long-Wert mit der Thread-ID der Laufzeitinstanz (für Aufrufe der Win32-API)
Title
String-Wert, der den Namen der Laufzeitinstanz für die Ausgabe in der Taskliste bestimmt
UnAttendedApp
Zur Laufzeit schreibgeschützter Boolean-Wert; wenn False (Vorgabe), erhält die Anwendung eine Benutzeroberfläche, sonst nicht
................................................... Methoden Methoden
Sub App.StartLogging( _ LogTarget As String = "vbevents.log", LogModes As Long) Sub App.LogEvent( _ LogBuffer As String, EventType As LogEventTypeConstants) Die zwei Methoden des App-Objekts dienen der Protokollierung von Ereignissen. Sie verrichten Ihren Dienst allerdings nur, wenn das Programm als eigenständige Exe-Datei ausgeführt wird – bei Ausführung der Projektdatei innerhalb der Entwicklungsumgebung sollte die LogEvent-Ausgabe laut Spezifikation in das Direktfenster stattfinden, was jedoch in der Version 6.0 (SP3) nicht passiert. Methode
Beschreibung
StartLogging
Legt LogTarget als Protokolldatei und LogModes als Protokollmodus für die Ereignisaufzeichnung mittels LogEvent fest (vgl. LogModus-Eigenschaft)
LogEvent
Protokolliert den in LogBuffer enthaltenen Text als Meldung in der Protokolldatei LogTarget der Anwendung. (Unter Windows NT schreibt die Methode in das NT-Ereignisprotokoll.) Der Parameter EventType kategorisiert die Meldung in »Fehler« (vbLogEventTypeError oder 1), »Warnung« (vbLogEventTypeWarning oder 2) bzw. »Information« (vbLogEventTypeInformation oder 4).
267
Global- Objekt
Eigenschaft
Global- Objekt
Anwendung
Global- Objekt
................................................... Anwendung
Als Laufzeitinstanz eines Programms ist das App-Objekt zwar das »Objekt der Objekte«, doch bei der Programmierung spielt es eher eine untergeordnete Rolle. Am häufigsten dürften die Eigenschaften PrevInstance und Path von Interesse sein. PrevInstance gibt Aufschluss darüber, ob bereits eine andere Instanz der Anwendung in Ausführung ist, und Path gibt Aufschluss über den Pfad der Exe-Datei. (Der Name der Exe-Datei ergibt sich aus der Eigenschaft Title.) Ab und an sind aber auch die Möglichkeiten der Ereignisprotokollierung des App-Objekts von Nutzen, zum Beispiel wenn es darum geht, die Aktivitäten eines Servers zu protokollieren. Seltener dagegen wird das App-Objekt für die Gestaltung von Time-outs und Abbrüchen bei Anforderungen an ActiveX-Server herangezogen. (Das Vorgabeverhalten ist hier in den meisten Fällen ausreichend. In speziellen Fällen, etwa wenn ActiveX-Server im Netzwerk verteilt sind, kann eine explizite Definition der entsprechenden Eigenschaften nötig sein.) Warnung
................................................... Wa rnung
Die Time-out-Intervalle des App-Objekts lassen sich zwar beliebig ausdehnen, da die entsprechenden Eigenschaften den Typ Long tragen, doch ein zu langes Zeitintervall kann die Anwendung lahmlegen, wenn der kontaktierte Server nicht reagiert. Sollten Sie für eine bestimmte Aufgabe die Time-outs des App-Objekts ändern müssen, empfiehlt es sich, die Werte nach Erledigung der Aufgabe umgehend wieder zu restaurieren, wenn die Anwendung mit mehr als einem ActiveX-Objekt arbeitet. Beispiel
................................................... Beis piel
Der folgende Code verhindert den mehrfachen Start der Anwendung. Die Beschränkung auf eine Laufzeitinstanz ist im Zusammenhang mit MDI-Anwendungen zwar nicht zwingend, aber doch weit verbreitet. Private Sub Form_Load() If App.PrevInstance Then MsgBox ("Es gibt eine ältere Instanz der Anwendung") End End If End Sub
Der folgende Codeauszug demonstriert die Ereignisprotokollierung in eine Datei namens C:\Test.log. (Achtung: Kompilierung erforderlich). Option Explicit Dim Nr As Long Private Sub Form_Load() App.StartLogging "C:\Test.log", 0 End Sub Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single) Nr = Nr + 1 App.LogEvent "Mousedown " + Str(Nr), vbLogEventTypeError End Sub
Die Ausgabe in die Datei c:\test.log hat folgende Gestalt: Error Application C:\Test.log: Thread ID: -513437 ,Logged: Mousedown 1 Error Application C:\Test.log: Thread ID: -513437 ,Logged: Mousedown 2
268
CallByName- Methode
CallByName- Methode Function CallByName(Object As Object, ProcName As String, _ CallType As VbCallType, Args()) As Variant Sub CallByName(Object As Object, ProcName As String, _ CallType As VbCallType, Args()) Beschreibung
................................................... Bes c hreibung
Anwendung
................................................... Anwendung
Der Visual-Basic-Compiler versucht, Objekte nach Möglichkeit bereits zur Übersetzungszeit zu binden und Verweise auf ihre Eigenschaften und Methoden möglichst umgehend aufzulösen. Man spricht in diesem Zusammenhang von früher Bindung. Diese Art der Bindung ist aber bei COM-Objekten nicht immer möglich, etwa wenn das Objekt keine duale Schnittstelle besitzt (oder die Typbibliothek dafür nicht bekannt ist), sondern nur eine Automatisierungsschnittstelle (IDispatch) anbietet. Für den Zugriff auf das Objekt bleibt dann nur noch die späte Bindung, das heißt, die Bindung zur Laufzeit. Das ist beispielsweise der Fall, wenn der Programmierer mit Object-Verweisen hantiert und Automatisierungsobjekte zur Laufzeit via CreateObject instanziiert. Hinter der CallByName-Methode steckt übrigens nichts weiter als ein Aufruf der Invoke-Funktion der IDispatch-Schnittstelle des Automatisierungsobjekts. Die CallByName-Methode ermöglicht es nun, gewissermaßen in letzter Sekunde auf Eigenschaften eines Objekts zuzugreifen, ohne dass diese bei der Kompilierung des Visual-Basic-Programms bekannt waren oder gar existiert haben. Auf diese Weise kann ein Programm mit maximaler Flexibilität vom jeweils aktuellen Dienstangebot des Automatisierungsobjekts profitieren. Beispiel
................................................... Beis piel
Da für das Scripting-Objekt FileSystemObject eine Typbibliothek sowie eine duale Schnittstelle zur Verfügung stehen, kann der Compiler die Methoden des Objekts selbst auflösen. Wer will, kann aber die Methoden auch über die Automatisierungsschnittstelle mit CallByName ansprechen. Die folgenden drei Print-Anweisungen leisten genau dasselbe, sie geben den gesamten Inhalt der Textdatei DateiName aus. (Da ReadAll die gesamte Datei ausliest, müssen immer zwei Print-Anweisungen auskommentiert sein.) Set fs = CreateObject("Scripting.FileSystemObject") ' Print fs.OpenTextfile(Dateiname).ReadAll ' Datei en bloc ausgeben ' Print CallByName(fs, "OpenTextFile", VbMethod, Dateiname).ReadAll Print CallByName(CallByName(fs, "OpenTextFile", VbMethod, Dateiname), _ "ReadAll", VbMethod) Verwandte Methoden
................................................... Verwa ndte Metho den CreateObject, GetObject
269
Global- Objekt
Die globale CallByName-Methode ermöglicht den Zugriff auf eine Funktion (Eigenschaft oder Methode) eines zur Laufzeit gebundenen Automatisierungsobjekts Object. Zur Spezifikation der Funktion ist ihr Bezeichner als Zeichenfolge ProcName anzugeben. Der Parameter CallType legt fest, ob die Funktion als Get, Set oder Let Property deklariert ist oder als gewöhnliche Methode. Der Aufzählungstyp VbCallType definiert dafür die Konstanten: vbMethod (1), vbGet (2), vbLet (4), vbSet (8). Etwaige Argumente für den Aufruf lassen sich über den Args-Parameter in Form eines Variant-Arrays oder als schlichte Parameterliste übergeben.
Global- Objekt
Clipboard- Objekt Public Clipboard As Clipboard Beschreibung
Global- Objekt
................................................... Bes c hreibung
Das Clipboard-Objekt vermittelt den Zugriff auf die von allen Anwendungen gemeinsam für den Datenaustausch genutzte Zwischenablage des Systems. Es gehört der in der Bibliothek VB definierten gleichnamigen Klasse an und wird von Visual Basic in jeder Anwendung automatisch als globales Objekt vereinbart. Das Objekt besitzt zwei voneinander unabhängige Puffer, von denen der eine für den Transport von Zeichenfolgen als Text, Text im RTF-Format oder DDE-Nachrichten für OLE-Objekte gedacht ist, während der andere die Übermittlung von Daten unterschiedlichen Formats (Grafiken oder Dateien) ermöglicht. Eine zusätzliche Formatinformation gibt Aufschluss über die Art der in den Puffern enthaltenen Daten. Methoden
................................................... Metho den Function Clipboard.Clear() Function Clipboard.GetData( _ Format As ClipBoardConstants) As IPictureDisp Function Clipboard.GetFormat( _ Format As ClipBoardConstants) As Boolean Function Clipboard.GetText( _ [Format As ClipBoardConstants = vbCFText]) As String Sub Clipboard.SetData(Picture As IPictureDisp, _ [Format As ClipBoardConstants]) Sub Clipboard.SetText(Str As String, _ [Format As ClipBoardConstants = vbCFText]) Die sechs Methoden des Clipboard-Objekts ermöglichen Zugriffe und Manipulationen der Zwischenablage (das heißt: der Pufferinhalte sowie der Formatinformationen). Die folgende Tabelle gibt einen Überblick über die Aufgaben der einzelnen Methoden: Methode
Beschreibung
Clear
Löscht alle Inhalte der Zwischenablage
GetData
Liefert Grafikdaten aus der Zwischenablage
GetFormat
Testet, ob die Zwischenablage Daten eines bestimmten Formats enthält, und liefert True, wenn die Daten in der Zwischenablage das im Parameter bezeichnete Format tragen, ansonsten False
GetText
Liefert Text (als Zeichenfolge) aus der Zwischenablage
SetData
Überträgt Grafikdaten und Formatbeschreibung in die Zwischenablage
SetText
Überträgt Text (als Zeichenfolge) in die Zwischenablage
Die folgende Tabelle gibt einen Überblick über die in Visual Basic für das Clipboard-Objekt definierten Formatkonstanten:
27 0
Clipboard- Objekt
Wert
Beschreibung
vbCFLink
&HFFFFBF00
Zwischenablage enthält Informationen für DDE-Verbindung
vbCFRTF
&HFFFFBF01
Zwischenablage enthält eine RTF-Datei (Rich Text Format)
vbCFText
1
Zwischenablage enthält TXT-Datei (ASCII-Text)
vbCFBitmap
2
Zwischenablage enthält eine Grafik vom Typ BMP (Bitmap)
vbCFMetafile
3
Zwischenablage enthält eine Grafik vom Typ WMF (Windows Metafile = Windows-Zwischendatei)
vbCFDIB
8
Zwischenablage enthält eine Grafik vom Typ DIB (geräteunabhängige Bitmap)
vbCFPalette
9
Zwischenablage enthält eine Farbpalette
vbCFEMetafile
14
Zwischenablage enthält eine Grafik vom Typ EMF (Enhanced Windows Metafile = erweiterte WindowsZwischendatei)
vbCFFiles
15
Zwischenablage enthält eine Dateiliste des Windows Explorers (wird vom Clipboard-Objekt zwar als Format angezeigt, jedoch nicht weiter unterstützt)
Anwendung
................................................... Anwendung
Über den Einsatz der Methode Clear muss man nicht viele Worte verlieren: Sie sollte immer dann zum Aufruf kommen, wenn der Inhalt der Zwischenablage nicht mehr vonnöten ist. Sie sorgt für die Freigabe der von dem Objekt belegten Ressourcen (die im Falle von Grafiken beträchtlich sein können). Die mit dem Ergebnistyp Boolean sicherlich etwas ungeschickt implementierte Methode GetFormat testet, ob die Daten in der Zwischenablage ein bestimmtes Format tragen. Nachdem die Zwischenablage allen Anwendungen gleichermaßen zur Verfügung steht, ist eine solche Prüfung in der Praxis unerlässlich, um Laufzeitfehlern aufgrund falscher Datenformate vorzubeugen. Die Methoden GetText und SetText dienen dem Transport von Daten im einfachen Textformat (TXT; Voreinstellung bei fehlendem Parameter Format), im Rich Text Format (RTF) sowie von Informationen, die im Zusammenhang mit DDE-Verbindungen übertragen werden. Der Imund Export von Text ist auf den Datentyp String ausgerichtet. Die Methoden GetData und SetData dienen dem Transport von Grafiken und binären Daten, die in verschiedenen Formaten vorliegen können. Der Im- und Export von Bitmaps ist auf den Datentyp Picture ausgerichtet und die Anzeige erfordert ein Steuerelement mit Picture-Eigenschaft. Bei dem im Prototyp der beiden Methoden genannten Datentyp IPictureDisp handelt es sich um die Automatisierungsschnittstelle des Picture-Objekts. Er stimmt in Visual Basic mit dem Datentyp Picture überein (diesen Typ liefert die TypeName-Funktion auch als Ergebnistyp der Methode GetData). Tipp
................................................... Tipp
Das Clipboard-Objekt lässt sich auch für die Übermittlung anderer auf dem System registrierter Datenformate heranziehen. Analoges dazu finden Sie im Abschnitt »OLEStartDrag-Ereignis« (S. 250). Allerdings unterstützt das Clipboard-Objekt von Visual Basic nicht alle Operationen
27 1
Global- Objekt
Formatkonstante
Global- Objekt
der OLE-Zwischenablage. So zeigt es zwar an, wenn in der Zwischenablage Dateien enthalten sind (vbCFFiles), es besitzt aber keine Methoden für den Austausch, da es (im Gegensatz zu einem DataObject-Objekt) keine Files-Eigenschaft verfügbar macht. Eine in der Zwischenablage befindliche Datei lässt sich aber in ein RTF-Steuerelement sowie in ein OLE-ContainerSteuerelement als Inhalt einfügen. Beispiel
Global- Objekt
................................................... Beis piel
Das folgende für seinen Funktionsumfang wirklich kurz geratene Projekt ZwischenAblage demonstriert recht eindrucksvoll den unkomplizierten Einsatz der Zwischenablage. Das Formular enthält ein RTF-Steuerelement (RICHTX32.OCX) namens RichTextBox, ein BildausschnittSteuerelement (PICCLP32.OCX) namens PicClip1, ein Bildfeld-Steuerelement (PictureBox) namens Picture1 sowie ein Menü mit den beiden Befehlen EINFÜGEN und KOPIEREN. Importiert man Text oder Text mit RTF-Format über die Zwischenablage in das Programm, macht der Befehl EINFÜGEN diesen im RTF-Steuerelement sichtbar. (Da RTF-Steuerelemente aber auch von sich aus mit der Zwischenablage des Systems interagieren, wenn sie den Fokus haben, ist eine explizite Implementation der Zwischenablagenfunktionen eher unnötig – zumal der Funktionsumfang des Clipboard-Objekts in Visual Basic Einschränkungen aufweist.) Importiert man in das Programm dagegen eine Grafik (beispielsweise einen mittels der Taste (Druck) erstellten Bildschirmabschuss), macht der Befehl EINFÜGEN diese im Bildfeld sichtbar. Der Befehl KOPIEREN ist dagegen so implementiert, dass er nur einen Ausschnitt des im Bildausschnitt-Steuerelement gespeicherten Bildes, nämlich 100×100 Bildpunkte von links oben gesehen, in die Zwischenablage überträgt. (Falls das Bild keine 100×100 Bildpunkte hat, wird auf ein Viertel verkleinert.) EINFÜGEN bringt das Ergebnis dieser Operation zutage. Private Sub mnuEinfügen_Click() If Clipboard.GetFormat(vbCFText) Then ' Text? RichTextBox1.Text = Clipboard.GetText End If If Clipboard.GetFormat(vbCFRTF) Then ' RTF-Text? RichTextBox1.TextRTF = Clipboard.GetText(vbCFRTF) End If If Clipboard.GetFormat(vbCFDIB) Then ' Grafik? PicClip1.Picture = Clipboard.GetData(vbCFDIB) Picture1 = PicClip1.Picture End If End Sub Private Sub mnuKopieren_Click() If PicClip1.Picture Then ' Bild zugewiesen? PicClip1.ClipHeight = 100 ' Bildausschnitt definieren PicClip1.ClipWidth = 100 Clipboard.SetData PicClip1.Clip, vbCFDIB ' In Zwischenablage End If End Sub Verwandte Themen
................................................... Verwandte Them en
OLE-Drag&Drop (S. 501)
27 2
Command- Methode
Command- Methode Public Command As String Beschreibung
................................................... Bes c hreibung
Die globale Command-Methode liefert die beim Aufruf der Anwendung angegebenen Kommandozeilenparameter als Zeichenfolge. Anwendung
................................................... Anwendung
MeinProgramm.exe Param1 Param2 Param3
Falls das Programm nicht kompiliert ist, lautet der Aufruf für Visual Basic 6.0, der den sofortigen Start der Anwendung bewirkt und Kommandozeilenparameter bereitstellt, so: Vb6 projekt1 /run /cmd Param1 Param2 Param3
Wenn Verzeichnisse für Vb6.Exe und Projekt1.vbp nicht zum Suchpfad von Windows gehören (vgl. Umgebungsvariable PATH), müssen Sie natürlich die entsprechenden Pfade angeben: "C:\Programme\Microsoft Visual Studio\VB98\Vb6" (fortgesetzt) "Vb60 Programme\Projekt1" /run /cmd Param1 Param2 Param3
In jedem der genannten Fälle liefert die Command-Methode als Wert die Zeichenfolge: "Param1 Param2 Param3"
CreateObject- Methode Function CreateObject(Class As String, [ServerName As String]) As Object Beschreibung
................................................... Bes c hreibung
Die CreateObject-Methode generiert eine neue Instanz des ActiveX-Objekts Class für die Automatisierung und liefert einen Verweis vom Typ Object darauf zurück. Läuft die für das Objekt zuständige Serveranwendung zum Zeitpunkt des Aufrufs noch nicht, startet die Methode den Server. Um das Objekt (via DCOM) auf einem bestimmten Server im Netzwerk auszuführen, ist dessen Name optional über den Parameter ServerName zu spezifizieren. Anwendung
................................................... Anwendung
Visual Basic kann mittels CreateObject eine Bindung zur Laufzeit mit allen COM-Objekten herstellen, die über eine Automatisierungsschnittstelle verfügen – und daher Automatisierungsobjekte genannt werden. Diese Schnittstelle stellt einen standardisierten Mechanismus dar, der es einer Host-Anwendung (lies: einem Visual-Basic-Programm) ermöglicht, auf eigens für die Automatisierung exponierte Methoden und Eigenschaften des Objekts zuzugreifen, wobei die Bindung erst zur Laufzeit erfolgt. Man spricht dann von später Bindung. Späte Bindung hat den Vorteil, dass zur Übersetzungszeit außer den Bezeichnern für das Objekt, seine Methoden und Eigenschaften nichts weiter über das Objekt bekannt sein muss – alle Informationen lassen sich zur Laufzeit ermitteln. Der Nachteil ist allerdings, dass sie einiges an Laufzeit verschlingt. Dim fs As Object Set fs = CreateObject("Scripting.FileSystemObject")
27 3
Global- Objekt
Seit Windows sind die Kommandozeile sowie die darin übergebenen Aufrufparameter zunehmend in den Hintergrund geraten. Von einer MS-DOS-Kommandozeile aus oder über den Befehl AUSFÜHREN im START-Menü von Windows können Sie eine als EXE-Datei vorliegende Anwendung aber jederzeit unter Angabe zusätzlicher Kommandozeilenparameter, Schalter etc. aufrufen. Die Kommandozeile lautet:
Global- Objekt
Global- Objekt
Die meisten Automatisierungsobjekte besitzen aber eine duale Schnittstelle und ermöglichen so auch eine Anbindung über die für die Klasse definierte Standardschnittstelle. Für diese Art der Bindung, die bereits zur Übersetzungszeit (bis auf einen einzigen Verweis auf eine zentrale Zeigertabelle) vollständig vorbereitet werden kann und somit als frühe Bindung bezeichnet wird, muss dem Compiler allerdings die gesamte Objektdeklaration in Form einer Objektbibliothek (OLB) vorliegen. Um dem Compiler eine bestimmte Objektbibliothek zugänglich zu machen, rufen Sie das Dialogfeld VERWEISE über das Menü PROJEKT/VERWEISE auf und setzen ein Häkchen vor den gewünschten Objekttyp. Für das Objekt Scripting.FileSystemObject wäre das die Bibliothek Microsoft Scripting Runtime. Über den Aufbau des Scripting-Moduls, also die darin definierten Konstanten, Eigenschaften und Methoden, sowie die Objektstruktur der Eigenschaften, also wiederum deren Konstanten, Eigenschaften und Methoden etc., können Sie sich dann im Objektkatalog (aufzurufen über ANSICHT/OBJEKTKATALOG) bis in alle Einzelheiten informieren.
Zum Scripting-Modul gehörige Objektbibliothek Bei Verwendung der frühen Bindung deklarieren Sie die Objektvariable wie gewohnt unter Angabe eines konkreten Datentyps: Dim fs As New Scripting.FileSystemObject
Vielfach spielt die Ausführungsgeschwindigkeit aber keine so große Rolle, so dass der geringere Overhead sowie die große Flexibilität, die CreateObject ermöglicht, nicht zu übersehende Vorteile bietet. Visual Basic ermöglicht es auch, eigene ActiveX-Server zu implementieren, um anderen Anwendungen Automatisierungsobjekte zur Verfügung zu stellen. Hierfür gibt es die speziellen Projekttypen »ActiveX-DLL« und »ActiveX-EXE«. Beispiel
................................................... Beis piel
Der folgende Code gibt den Inhalt einer Textdatei unter Verwendung des FileSystemObjectObjekts en bloc aus.
27 4
DeleteSetting- Methode
Dim fs As Object Set fs = CreateObject("Scripting.FileSystemObject") Print fs.OpenTextfile(DateiName).ReadAll ' Datei en bloc ausgeben Verwandte Methoden
................................................... Verwa ndte Metho den CallByName, GetObject
DeleteSetting- Methode Beschreibung
................................................... Bes c hreibung
Die Methode DeleteSetting ermöglicht es, persistente Einstellungen eines Programms aus der Systemregistrierung wieder zu entfernen, die mittels der SaveSetting-Methode als Unterschlüssel des Schlüssels HKEY_CURRENT_USER\Software\VB and VBA Program Settings\ gespeichert wurden. Es gilt: AppName ist Unterschlüssel von "VB and VBA Program Settings" und steht für den Programmnamen, Section ist Unterschlüssel von AppName und steht für den Abschnitt und Key ist Bezeichner eines Werts in Section. Falls der zu löschende Schlüssel, Unterschlüssel oder Wert nicht existiert, löst die Methode den Laufzeitfehler 5 »Ungültiger Prozeduraufruf oder ungültiges Argument« aus. Anwendung
................................................... Anwendung
Früher mussten Windows-Anwendungen ihre persistenten Einstellungen noch in je eigenen INIDateien ablegen. Spätestens seit Windows 95 (aber auch schon zuvor) gibt es eine Systemregistrierung, in der sich solche Daten an zentraler Stelle speichern lassen. Für den Zugriff auf die Systemregistrierung von Visual Basic aus stehen im Wesentlichen vier Methoden zur Verfügung: GetSetting, GetAllSettings, SaveSetting und DeleteSetting, deren Funktion bereits durch die Bezeichner deutlich wird. Um auf andere Schlüssel zuzugreifen, müssen Sie die entsprechenden Funktionen der Win32-API (RegDeleteKey, RegDeleteValue etc.) benutzen. Beispiel
................................................... Beis piel
On Error Resume Next ' Fehler ignorieren DeleteSetting "MeineAnwendung" ' Alle Einträge des Programms entfernen DeleteSetting "MeineAnwendung1", "Metrik" ' Abschnitt entfernen On Error Goto 0' Verwandte Methoden
................................................... Verwa ndte Metho den GetSetting, GetAllSettings, SaveSetting Verwandte Themen
................................................... Verwandte Them en
Registrierung (S. 561)
27 5
Global- Objekt
Sub DeleteSetting(AppName As String, [Section As String], _ [Key As String])
Global- Objekt
DoEvents- Methode Sub DoEvents() Beschreibung
................................................... Bes c hreibung
Die DoEvents-Methode unterbricht die Ausführung der aktuellen Routine temporär, um die Behandlung gegebenenfalls anstehender Ereignisse zu ermöglichen, und setzt die Routine danach wieder fort. Die Methode lässt sich als Prozedur und als Funktion verwenden. Als Funktion aufgerufen, liefert sie die Anzahl der geöffneten Formulare.
Global- Objekt
Anwendung
................................................... Anwendung
Die DoEvents-Methode ist in Programmen mit nur einem einzigen Ausführungspfad (Thread) ein wichtiges Mittel, um die Reaktivität der Benutzerschnittstelle auch im Verlauf länger andauernder Operationen aufrechtzuerhalten. Da die in der Ereigniswarteschlange bereitstehenden Ereignisse immer nacheinander abgearbeitet werden, kann eine zeitintensive Routine die Behandlung anderer wartender Ereignisse spürbar (wenn nicht gar ewig) verzögern, was der Benutzer als »zähe Reaktion« auf seine Aktivitäten zu spüren bekommt. Damit zeitaufwändige Routinen quasi im Hintergrund ablaufen, fügen Sie an passender Stelle (nicht zu häufig, nicht zu selten; eine Faustregel ist: ca. jede Zehntelsekunde auf einem durchschnittlichen System) einen DoEvents-Aufruf ein. Tipp
................................................... Tipp
In manchen Fällen lassen sich längere Wartezeiten für den Benutzer nicht vermeiden. Um ihm aber dennoch das Gefühl zu geben, dass etwas passiert, können Sie eine Fortschrittsleiste anzeigen oder zumindest die Mauszeigerform auf »Sanduhr« umschalten. Warnung
................................................... Wa rnung
Wenn eine durch DoEvents unterbrochene Routine erneut aufgerufen wird, kommt es zur Rekursion. Rekursion an sich bereitet keine Probleme, solange Sie bei der Programmierung darauf achten, dass keine statischen oder globalen Variablen im Spiel sind. Wird die Rekursion jedoch zu tief, kommt es zum Laufzeitfehler »Stapelüberlauf«. Ein weiteres Problem im Zusammenhang mit DoEvents sind die so genannten »Stehaufmännchen«, die auftreten können, wenn Unload innerhalb einer DoEvents-Unterbrechung zur Ausführung kommt. In diesem Fall wird Visual Basic das Formularobjekt beim nächsten Zugriff auf eine Eigenschaft oder Methode automatisch erneut laden, und das ganze Spiel beginnt von vorne. Schlimmer noch: wenn das einmal passiert ist, lässt sich das Formular nicht mehr beenden, weil jeder Versuch es zu beenden eine neue Instanz hervorzaubert, selbst wenn keine DoEvents-Unterbrechung mehr vorliegt. (Wenn Sie es ausprobieren, müssen Sie das Programm über das Entwicklungssystem beenden.) Sie umgehen das Problem, indem Sie den von DoEvents gelieferten Funktionswert auswerten. Dieser ist nämlich 0, wenn der Benutzer inzwischen das Formular geschlossen hat, ansonsten ungleich 0 (detailliertere Informationen dazu entnehmen Sie dem Abschnitt »ApfelmannZoom – eine Fahrt durch die Mandelbrotmenge«, S. 478 im Praxisteil). Beispiel
................................................... Beis piel
Die folgende Routine ist eine Variante des »Apfelmännchens«, die mit regelmäßigen DoEventsAufrufen während der Bildberechnung die Benutzerschnittstelle am »Leben« erhält. Private Sub Apfel(m_x1, m_x2, m_y1, m_y2, step_x, step_y) Dim r1 As Double, re As Double, im As Double Dim zr As Double, zi As Double, it As Integer
27 6
Err- Objekt
Global- Objekt
For zr = m_x1 To m_x2 Step step_x ' alle Spalten in x-Richtung For zi = m_y1 To m_y2 Step step_y ' alle Punkte in Spalte zr re = 0 ' Realteil initialisieren im = 0 ' Imaginärteil initialisieren For it = 0 To cMaxIterat ' Iteration für Punkt r1 = re * re – im * im + zr im = 2 * re * im + zi re = r1 If re < -cGrenze Or re > cGrenze Or _ im < -cGrenze Or im > cGrenze Then PSet (zr, zi), it * 16 ' Punkt ausgeben Exit For End If Next it Next zi if DoEvents = 0 Then Exit Sub Next zr End Sub
Err- Objekt Public Err As ErrObject Beschreibung
................................................... Bes c hreibung
Für den Umgang mit Laufzeitfehlern besitzt Visual Basic ein altes und eine neues Informationskonzept. Für das alte steht die Error-Anweisung, für das neue das Err-Objekt. Das von Visual Basic in jeder Anwendung automatisch als globales Objekt vereinbarte Err-Objekt gehört der Klasse ErrObject aus der Bibliothek VBA an. Seine Aufgabe besteht darin, Laufzeitfehler zu signalisieren und spezifische Fehlerinformationen für deren Behandlung bereitzustellen. Eigenschaften
................................................... Eigens c ha ften Das Err-Objekt verfügt über eine Reihe von Eigenschaften für die nähere Spezifikation eines Fehlers und dessen Behebung. Die folgende Tabelle gibt einen Überblick über alle Eigenschaften des Objekts: Eigenschaft
Beschreibung
Description
Zeichenfolge mit dem aktuellen Fehlertext
HelpContext
Zeichenfolge mit der Kontextkennung für das aktuelle Hilfethema in Hilfedatei. Wenn leer, wird die Kontextkennung über den Wert von Number ermittelt.
HelpFile
Zeichenfolge mit dem Zugriffspfad für die Hilfedatei. Wenn leer, wird die Visual-Basic-Hilfedatei benutzt.
LastDllError
zur Laufzeit schreibgeschützter Long-Wert, der den Rückgabewert der zuletzt aufgerufenen DLL-Routine übermittelt. Die Bedeutung des Rückgabewerts ist der Dokumentation der entsprechenden Funktion zu entnehmen (vgl. »Routinen aus DLLs und der Windows-API einsetzen«, S. 185).
Number
Wert vom Typ Long mit der aktuellen Fehlernummer
27 7
Global- Objekt
Eigenschaft
Beschreibung
Source
Zeichenfolge mit dem Klassennamen des den Fehler signalisierenden Objekts (gegebenenfalls Objekt-ID) oder dem Namen des Programmmoduls; hilfreich, wenn keine modulinterne Behandlung des Fehlers erfolgt
Ein explizites Setzen der Eigenschaften erübrigt sich, da alle Eigenschaften im Zuge des RaiseAufrufs mit einem Wert versorgt werden können.
Global- Objekt
Methoden
................................................... Metho den Sub Err.Clear () Sub Err.Raise(Number, _ [Source], [Description], [HelpFile], [HelpContext]) Die Methode Clear ermöglicht es, das Err-Objekt in den Grundzustand zurück zu versetzen. Das ist beispielsweise im Zusammenhang mit der Anweisung On Error Resume Next erforderlich, wenn eine Inline-Fehlerbehandlung unmittelbar im Anschluss an eine Anweisung erfolgt. Die Methode Raise löst einen Laufzeitfehler mit der Nummer Number aus. Die benannten Parameter der Methode setzen die gleichnamigen Eigenschaften des Objekts. Der Wertebereich für Visual-Basic-Fehler liegt zwischen 0 und 65.535, wobei der Bereich zwischen 0 und 512 für Systemfehler reserviert ist, so dass für benutzerdefinierte Fehler der Bereich zwischen 513 und 65.535 übrig bleibt. In Klassenmodulen arbeitet man mit Fehlernummern, die den Offset vbObjectError haben. Anwendung
................................................... Anwendung
Das Err-Objekt stellt gegenüber dem traditionellen Fehlermechanismus von Visual Basic eine Verfeinerung dar, weil es eine bessere Kontrolle über den Kontext eines Fehlers bietet. Am Fehlerbehandlungsmechanismus von Visual Basic ändert das Err-Objekt jedoch nichts (Einzelheiten dazu im Abschnitt »Fehlerbehandlung«, S. 43). Mit Ausnahme der Eigenschaft LastDllError, die nur im Zusammenhang mit Aufrufen der Win32-API eine Bedeutung hat, lassen sich die Eigenschaften des Fehlerobjekts an beliebiger Stelle in einer Funktion/Prozedur setzen. Im Allgemeinen wird man die Werte für die diese Eigenschaften bei einem Raise-Aufruf bereitstellen. Beachten Sie aber, dass die Anweisungen Exit Function, Exit Sub, Exit Property sowie Resume Next implizit einen Clear-Aufruf des Err-Objekts veranlassen, so dass die Eigenschaften jedes Mal neu gesetzt werden müssen. Tipp
................................................... Tipp
Die Fehlerbehandlung sollte bei der Entwicklung eines Programms von Anfang an einen sehr hohen Stellenwert haben. Testen Sie Ihre Fehlerroutinen ausgiebig auf alle Spezialfälle und schließen Sie aus, dass eine Fehlerbehandlungsroutine in die Verlegenheit kommt, Fehler zu behandeln, für die sie nicht ausgelegt ist. Beispiel
................................................... Beis piel
Vgl. das Beispiel zu dem Thema »Fehlerbehandlung« (S. 43) Verwandte Themen
................................................... Verwandte Them en
Fehlerbehandlung (S. 43)
27 8
Forms- Auflistung
Forms- Auflistung Forms As Object Beschreibung
................................................... Bes c hreibung
Anwendung
................................................... Anwendung
Sie können die Auflistung Forms mit einem For Each-Konstrukt durchlaufen, um festzustellen, welche Formulare es gibt, oder um beispielsweise den Anwendungsfokus an ein bestimmtes Formular weiterzureichen. Als Datentyp für die Laufvariable vereinbaren Sie Object oder besser Form. Beispiel
................................................... Beis piel
Dim Formular As Form For Each Formular In Forms Print Formular.Caption Next Verwandte Themen
................................................... Verwandte Them en
Auflistungen und Collection-Objekte (S. 304)
GetAllSettings- Methode Function GetAllSettings(AppName As String, Section As String) _ As String()() Beschreibung
................................................... Bes c hreibung
Die Methode GetAllSettings ermöglicht die Abfrage aller zum Abschnitt (Unterschlüssel) Section gehörigen persistenten Einstellungen eines Programms AppName, die mittels der SaveSetting-Methode als Unterschlüssel des Schlüssels HKEY_CURRENT_USER\Software\VB and VBA Program Settings\ gespeichert wurden. Die Methode liefert ihr Ergebnis in einem zweidimensionalen Zeichenfolgenarray mit nullbasiertem Index, wobei in der ersten Dimension die Bezeichner der Werte und in der zweiten Dimension die Daten zu den Werten enthalten sind. Falls ein Schüssel nicht existiert oder keine Werte enthält, liefert die Methode den Wert Empty. Anwendung
................................................... Anwendung
Früher mussten Windows-Anwendungen ihre persistenten Einstellungen noch in je eigenen INIDateien ablegen. Spätestens seit Windows 95 (aber auch schon zuvor) gibt es eine zentrale Systemregistrierung, in der sich solche Daten an zentraler Stelle speichern lassen. Für den Zugriff auf die Systemregistrierung von Visual Basic aus stehen im Wesentlichen vier Methoden zur
27 9
Global- Objekt
Die nicht mit dem Datentyp Collection kompatible Forms-Auflistung enthält eine Liste der aktuell von der Anwendung instanziierten Formularobjekte – gewöhnliche Formulare, MDIFormulare, MDI-Formularen untergeordnete Formulare. Elementtyp ist der Klassenname (Formularname) der jeweiligen Instanz. Einzige Eigenschaft der Auflistung ist Count, über die sich die Anzahl Listenelemente in Erfahrung bringen lässt. Um die Pflege der Auflistung kümmert sich das Laufzeitsystem (im Rahmen von Load), so dass sie jederzeit auf dem neuesten Stand ist.
Global- Objekt
Verfügung: GetSetting, GetAllSettings, SaveSetting und DeleteSetting, deren Funktion bereits durch die Bezeichner deutlich wird. Um auf andere Schlüssel zuzugreifen, müssen Sie die entsprechenden Funktionen der Win32-API (RegOpenKeyEx etc.) benutzen. Beispiel
................................................... Beis piel
Global- Objekt
Der folgende Code fügt vier Werte unter dem Schlüssel »Abschnitt« in die Registrierung ein. Danach liest er sie wieder mittels GetAllSettings, gibt sie aus und löscht sie schließlich en bloc. Dim a, i SaveSetting "MeineAnwendung", "Abschnitt", "Wert", 10 SaveSetting "MeineAnwendung", "Abschnitt", "Wert1", 20 SaveSetting "MeineAnwendung", "Abschnitt", "Höhe", 100 SaveSetting "MeineAnwendung", "Abschnitt", "Breite", 100 a = GetAllSettings("MeineAnwendung", "Abschnitt") If not IsEmpty(a) Then For i = 0 To UBound(a) Print a(i, 0) & " = " & a(i, 1) Next i End If DeleteSetting "MeineAnwendung" Verwandte Methoden
................................................... Verwa ndte Metho den DeleteSetting, GetSetting, SaveSetting Verwandte Themen
................................................... Verwandte Them en
Registrierung (S. 561)
GetObject- Methode Function GetObject([PathName As String], _ [AnwName.ObjektTyp As String]) As Object Beschreibung
................................................... Bes c hreibung
Die GetObject-Methode liefert die Referenz auf ein ActiveX-Objekt (Automatisierungsobjekt) mit registrierter Klasse. Existiert das Objekt bereits als Datei, lässt es sich unter Angabe des entsprechenden Dateinamens (PathName) öffnen, was dazu führt, dass die für das Objekt zuständige (lies: für den Objekttyp registrierte) Serveranwendung automatisch gestartet wird. Optional lässt sich über den zweiten String-Parameter die Klasse des zu aktivierenden Objekts noch genauer spezifizieren, falls in der Datei mehrere Objekte enthalten sind. Ist Pathname die leere Zeichenfolge, muss der zweite Parameter in jedem Fall angegeben sein, da die Methode dann eine Referenz auf ein neues Objekt dieser Klasse liefert. Beispiel
................................................... Beis piel
Der folgende Code organisiert eine Referenz auf ein Word-Dokument sowie auf eine CorelDRAW-Zeichnung: Dim Wdoc As Object, CorelDoc As Object Set Wdoc = GetObject("\Eigene Dateien\Basic.doc") Set Wdoc = GetObject("Zeichnung.cdr", "CorelDRAW.Graphic.7")
280
GetSetting- Methode
Einige Anwendungen ermöglichen es auch, einzelne Eigenschaften eines gegebenen Objekts zu aktivieren. Der Name der Eigenschaft wird dann durch ein Ausrufezeichen abgetrennt hinten angefügt.
GetSetting- Methode Function GetSetting(AppName As String, Section As String, _ Key As String, [Default]) As String Beschreibung
................................................... Bes c hreibung
Beispiel
................................................... Beis piel
Print GetSetting ("MeineAnwendung", "Abmessungen", "Breite", 100) Verwandte Methoden
................................................... Verwa ndte Metho den DeleteSetting, GetAllSettings, SaveSetting Verwandte Themen
................................................... Verwandte Them en
Registrierung (S. 561)
InputBox- Methode Function InputBox(Prompt, [Title], [Default], [XPos, YPos], _ [HelpFile], [Context]) As String Beschreibung
................................................... Bes c hreibung
Die Methode InputBox ruft ein Dialogfeld mit den Schaltflächen OK und ABBRECHEN auf, in dessen Client-Bereich die Eingabeaufforderung Prompt sowie ein einzeiliges Textfeld für die Werteingabe erscheinen. Als Ergebnis liefert Sie den Wert des Textfeldes als Zeichenfolge. Standardmäßige Beschriftung des Dialogfelds ist der Name der Anwendung, sie lässt sich aber über den optionalen Parameter Title frei gestalten. Ist ein Wert für Default angegeben, erscheint dieser markiert als Vorgabewert im Textfeld des Dialogs. Bricht der Benutzer den Dialog ab, resultiert die leere Zeichenfolge, unabhängig von der Existenz eines Vorgabewerts. Über XPos und YPos kann die Position des Dialogfelds relativ zum linken oberen Punkt der Bildschirmanzeige (Screen) gesetzt werden. Fehlen diese Parameter, setzt Windows das Dialogfeld leicht nach oben verschoben in die Mitte des Bildschirms. Darüber hinaus lassen sich optional ein Hilfethema HelpFile sowie eine zuständige Hilfedatei Context angeben. Anwendung
................................................... Anwendung
Obwohl der Anwendungsbereich der Methode vom Prinzip her den gesamten Bereich der zeilenorientierten Benutzereingabe abdeckt, wird sie eigentlich nur in Situationen eingesetzt, wo sich der Entwurf eines eigenen Dialogfelds nicht lohnt – so in provisorischen Programmentwürfen oder wenn bei der Reaktion auf einen Fehler interaktive Eingaben erforderlich sind.
281
Global- Objekt
Die GetSetting-Methode entspricht von der Funktionalität her der GetAllSettings-Methode, mit dem Unterschied, dass sie nur die Daten zu einem bestimmten Wert Key des Abschnitts (Unterschlüssels) Section liefert. Als Vorgabe für den Fall, dass der Eintrag Key nicht existiert, kann der optionale Parameter Default angegeben werden.
Global- Objekt
Beispiel
................................................... Beis piel
Do a = InputBox("Bitte eine Zahl zwischen 10 und 100 eingeben", _ "Bereich einstellen") Loop Until IsNumeric(a) And Val(a) <= 100 And Val(a) >= 10 Verwandte Methoden
................................................... Verwa ndte Metho den
Global- Objekt
MsgBox
Load- Methode Function Load(Object As Object) Beschreibung
................................................... Bes c hreibung
Die Load-Methode lädt das Objekt Object in den Hautspeicher und initialisiert es, ohne es anzuzeigen. Das Objekt kann ein Formular, ein MDI-Formular oder ein Steuerelement sein. Anwendung
................................................... Anwendung
Normalerweise ist es unnötig, ein Formular oder ein zur Entwurfszeit eingefügtes Steuerelement explizit zu laden, denn jeder Verweis auf das Formular (mit Ausnahme von TypeOf- und SetKonstrukten) veranlasst Visual Basic dazu, dieses samt aller ihm untergeordneten Steuerelemente implizit zu laden. Für ein Formular, das nicht Startobjekt des Projekts ist und somit zum Programmstart auch nicht automatisch geladen wird, genügt ein Show-Aufruf, um es zu laden und anzeigen. Die explizite Verwendung von Load vor Show kann aber den Vorteil haben, dass das Objekt dann, wenn es benötigt wird, bereits geladen ist und nur noch die Visible-Eigenschaft mittels Show oder explizit zu setzen ist, um es ohne weitere Wartezeiten anzuzeigen. Explizit ist der Load-Aufruf eigentlich nur erforderlich, wenn Sie mit einem SteuerelementeArray arbeiten und in dieses zur Laufzeit eine neue Instanz des Steuerelements einfügen wollen. Visual Basic erweitert das Array dann automatisch, initialisiert die Visible-Eigenschaft jedoch mit False, so dass Sie diese Eigenschaft extra auf True setzen müssen, damit das neue Steuerelement sichtbar wird. Tipp
................................................... Tipp
Um Ressourcen zu sparen, können Sie selten benötigte Formulare oder monströse Steuerelemente mittels Unload umgehend nach Gebrauch wieder entladen. Das erneute Laden erfolgt dann implizit mit dem nächsten Show-Aufruf. Diese Technik führt dazu, dass auch alle Steuerelemente auf dem Formular neu initialisiert werden – ein Vorgang, der natürlich auf Kosten der Laufzeit geht. Beispiel
................................................... Beis piel
Load Form1 Verwandte Befehle
................................................... Verwa ndte Befehle
Unload, Show Verwandte Themen
................................................... Verwandte Them en
Schach – klare Sicht auf Hintergründliches (S. 607)
282
LoadPicture- Methode
LoadPicture- Methode Function LoadPicture([FileName As String], _ [Size As LoadPictureSizeConstants], _ [ColorDepth As LoadPictureColorConstants], _ [X], [Y]) _ As Picture Beschreibung
................................................... Bes c hreibung
Konstante
Beschreibung
vbLPSmall (0)
Kleines Systemsymbol (Voreinstellung)
vbLPLarge (1)
Großes Systemsymbol
vbLPSmallShell (2)
Größe von »Titelleistensymbol«, wie im Systemdialog EIGENANZEIGE auf der Registerkarte DARSTELLUNG festgelegt SCHAFTEN VON
vbLPLargeShell (3)
Größe von Symbol, wie im Systemdialog EIGENSCHAFTEN ANZEIGE auf der Registerkarte DARSTELLUNG festgelegt
vbLPCustom (4)
Benutzerdefinierte Größe
VON
Für den optionalen Parameter ColorDepth gibt es drei Werte, für die der Aufzählungstyp LoadPictureColorConstants Konstanten definiert. Konstante
Beschreibung
vbLPDefault (0)
System wählt beste Übereinstimmung mit Anzeige
vbLPMonochrom (1)
Schwarz/Weiß
vbLPVGAColor (2)
16 Grundfarben
vbLPColor (3)
256 Farben
Wird die Methode ohne Parameter aufgerufen, liefert Sie den Wert Empty und kann dafür benutzt werden, die Picture-Eigenschaft eines Steuerelements oder Formulars auf »Kein Bild« zu setzen. Beispiel
................................................... Beis piel
Set Picture = LoadPicture(Dateiname) Set Picture1.Picture = LoadPicture(Dateiname1)
283
Global- Objekt
Die LoadPicture-Methode lädt eine Grafikdatei FileName, die in einem der Formate DIB, BMP, RLE, ICO, CUR, GIF, JPG, WMF oder EMF vorliegt, und liefert eine Referenz vom Typ Picture dafür. Falls die Datei eines der Formate ICO oder CUR trägt, können Sie für den optionalen Parameter Size zwischen verschiedenen Symbolgrößen auswählen (sofern in der Datei verfügbar). Der Aufzählungstyp LoadPictureSizeConstants definiert dafür die folgenden Konstanten:
Global- Objekt
LoadResData- Methode, LoadResPicture- Methode, LoadResString- Methode Function LoadResData(Id, ResType As Integer) As Byte() Function LoadResPicture(Id, ResType As Integer) As Picture Function LoadResString(Id As Long) As String Beschreibung
Global- Objekt
................................................... Bes c hreibung
Die Methoden LoadResData, LoadResPicture und LoadResString ermöglichen das Laden von binären Ressourcen, Bildressourcen oder Zeichenfolgenressourcen aus einer Ressourcendatei (RES). Für den Parameter Id ist die Ressourcenkennung anzugeben, wie im Ressourcen-Editor festgelegt. Die Methoden LoadResData und LoadResPicture erwarten noch einen weiteren Parameter, ResType, der den Typ der zu ladenden Ressource genauer spezifiziert. LoadResPicture akzeptiert für diesen Parameter die drei Werte vbResBitmap (0), vbResIcon (1) vbResCursor (2), während LoadResData den Zugriff auf alle Arten von Ressourcen (also auch auf Zeichenfolgen und Bitmaps) ermöglicht, diese aber in Form eines Byte-Arrays zurückliefert. Der Parameter ResType kann für diese Methode folgende Werte tragen: Wert für ResType
Art der Ressource
1
Cursor
2
Bitmap
3
Symbol
4
Menü
5
Dialogfeld
6
Zeichenfolge
7
Schriftartverzeichnis
8
Schriftart
9
Zugriffstasten (Accelerator)
10
benutzerdefiniert
12
Gruppen-Cursor
14
Gruppensymbol
Printer- Objekt Public Printer As Printer Beschreibung
................................................... Bes c hreibung
Das in jeder Anwendung als globales Objekt bekannte Printer-Objekt entstammt der gleichnamigen in der Bibliothek VB beheimateten Klasse und ermöglicht Druckerausgaben aus einer Anwendung heraus. Bei Start einer Anwendung ist das Printer-Objekt auf den aktuellen Standarddrucker des Systems gesetzt, und seine Eigenschaften spiegeln die dafür in der Systemsteuerung getroffenen Standardeinstellungen wider. Die Eigenschaften des Printer-Objekts lassen sich ohne Auswirkungen für andere Anwendungen individuell ändern. Um die Druckerausgabe gezielt auf einen bestimmten Drucker zu leiten (bzw. über einen bestimmten Druckertreiber auszugeben), kann dem Printer-Objekt jederzeit ein anderes Element aus der Printers-Auflistung zugewiesen werden. In dieser Auflistung sind alle auf dem jeweiligen System installierten Drucker enthalten.
284
Printer- Objekt
Eigenschaften
................................................... Eigens c ha ften Das Printer-Objekt verfügt über eine stattliche Anzahl von Eigenschaften, auf deren Basis der für den jeweiligen Drucker installierte Druckertreiber die Druckausgabe durchführt. Da nicht alle Drucker alle Eigenschaften unterstützen, können einzelne Eigenschaften auch ohne Wirkung bleiben oder verschiedene Einstellungen die gleiche Wirkung haben (insbesondere »Transparenz« kann ein Problem sein). Einstellungen außerhalb der zulässigen Wertebereiche können einen Laufzeitfehler verursachen – müssen aber nicht. Genaue Informationen über die Wirkung der einzelnen Werte bezogen auf eine spezielle Konstellation aus Drucker und Treiber finden Sie in den jeweiligen Herstellerdokumentationen. Beschreibung
ColorMode
Wert vom Typ Integer, der den Farbmodus des Druckers wiedergibt bzw. festlegt. Der Aufzählungstyp PrinterObjectConstants definiert dafür zwei Konstanten: vbPRCMColor (1) – Ausdruck nach Möglichkeit in Farbe; vbPRCMMonochrom (2) – Ausdruck in Graustufen.
Copies
Wert vom Typ Integer, der die Anzahl der Kopien eines Ausdrucks festlegt
CurrentX CurrentY
Werte vom Typ Single, die die aktuelle Position auf der Ausgabefläche für die nächste Zeichenoperation wiedergeben bzw. festlegen. Standardmäßig gilt die Einheit Twips, es sei denn, über die Eigenschaft ScaleMode wurde eine andere Einheit festgelegt.
DeviceName
Schreibgeschützter Wert vom Typ String, der den Gerätenamen des dem Printer-Objekt aktuell zugewiesenen Druckers wiedergibt
DrawMode
Wert vom Typ Integer, der den Zeichenmodus für Zeichenoperationen sowie für das Zeichnen von Figur- und Liniensteuerelementen festlegt. Der Aufzählungstyp DrawModeConstants definiert dafür eine Reihe von Konstanten: Schwarzintensität vbBlackness (1) vbNotMergePen (2)
Stift mischen invers (invers zu 15)
vbMaskNotPen (3)
inversen Stift maskieren; Kombination der Farben, die der Hintergrund mit der invertierten Stiftfarbe gemeinsam hat
vbNotCopyPen (4)
Stift kopieren invers (invers zu 13)
vbMaskPenNot (5)
Stift und inverse Anzeige maskieren; Kombination der Farben, die der Stift mit der invertierten Anzeigefarbe gemeinsam hat
vbInvert (6)
Anzeigefarbe invers
vbXorPen (7)
Stift Xor; Kombination der Farben, die im Stift und in der Anzeigefarbe, aber nicht in beiden vorhanden sind
vbNotMaskPen (8)
Stift maskieren invers (invers zu 9)
vbMaskPen (9)
Stift maskieren; Kombination der Farben, die Stift und Anzeigefarbe gemeinsam haben (invers zu 10)
vbNotMaskPen (10)
Stift maskieren invers (invers zu 9)
285
Global- Objekt
Eigenschaft
Global- Objekt
Global- Objekt
Eigenschaft
Beschreibung
DrawMode (Forts.)
vbNop (11)
Keine Operation, Stift zeichnet nicht
vbMergeNotPen (12)
inversen Stift mischen – Kombination der Anzeigefarbe und der invertierten Stiftfarbe
vbCopyPen (13)
Stift kopieren (Voreinstellung); Stiftfarbe ist durch Eigenschaft ForeColor gegeben (invers zu 4)
vbMergePenNot (14)
Stift und inverse Anzeige mischen; Kombination der Stiftfarbe und der invertierten Anzeigefarbe
vbMergePen (15)
Stift mischen; Kombination aus Stift- und Anzeigefarbe (invers zu 2)
vbWhiteNess (16)
Weißintensität
DrawStyle
Wert vom Typ Integer, der den Linienstil für Zeichenoperationen festlegt. Der Aufzählungstyp DrawStyleConstants definiert dafür eine Reihe von Konstanten: vbSolid (0) Durchgezogen (Voreinstellung) vbDash (1)
Gestrichelt
vbDot (2)
Gepunktet
vbDashDot (3)
Folge aus Strichen und Punkten
vbDashDotDot (4)
Folge aus Strichen und zwei Punkten
vbInvisible (5)
Transparent
vbInsideSolid (6)
Innen ausgefüllt
DrawWidth
Wert vom Typ Integer, der die Linienbreite in Bildpunkten für Zeichenoperationen festlegt. Der Wert kann zwischen 1 (Haarlinie; Voreinstellung) und 32767 liegen. Ist der Wert ungleich 1, erzeugen die Werte 0 bis 4 für die DrawStyle-Eigenschaft eine durchgezogene Linie.
DriverName
Zur Laufzeit schreibgeschützter Wert vom Typ String mit dem Gerätenamen des Druckers
Duplex
Wert vom Typ Integer für einseitiges oder beidseitiges Drucken. Der Aufzählungstyp DrawStyleConstants definiert dafür drei Konstanten: vbPRDPSimplex (1) Einseitiges Drucken
FillColor
286
vbPRDPHorizontal (2)
Beidseitiges Drucken mit horizontalem Seitenwechsel
vbPRDPVertical (3)
Beidseitiges Drucken mit vertikalem Seitenwechsel
Farbwert vom Typ Long, der die Füllfarbe für das Ausmalen von Figuren angibt. Standardwert ist 0 (vbBlack). Beliebige Farben lassen sich aus ihren Komponentenanteilen über die Funktion RGB komponieren. Die Funktion QBColor liefert den Farbwert einer der 16 Grundfarben. Zudem stehen die Elemente des Aufzählungstyps ColorConstants als vordefinierte Konstanten zur Verfügung.
Printer- Objekt
Eigenschaft
Beschreibung
FillStyle
Wert vom Typ Integer, der das Füllmuster für das Ausmalen von Figuren angibt. Der Aufzählungstyp FillStyleConstants definiert dafür acht Konstanten: vbFSSolid (0) Ausgefüllt Transparent (Voreinstellung)
VbHorizontalLine (2)
Horizontales Linienmuster
vbVerticalLine (3)
Vertikales Linienmuster
vbUpwardDiagonal (4)
Aufwärtsdiagonales Linienmuster
vbDownwardDiagonal (5)
Abwärtsdiagonales Linienmuster
vbCross (6)
Kreuzmuster
vbDiagonalCross (7)
Diagonalkreuzmuster
Font
Objekt vom Typ Font, das die Schrift für Ausgaben auf das Gerät repräsentiert
FontBold FontItalic FontStrikethru FontUnderline
Werte vom Typ Boolean, die als Attribute für die Schriftstile Fett, Kursiv, Durchgestrichen und Unterstrichen fungieren
FontCount
Schreibgeschützter Wert von Typ Integer, der die Anzahl der dem Printer-Objekt bekannten Schriften wiedergibt
FontName
Wert vom Typ String, der den Namen der aktuell eingestellten Schriftart wiedergibt bzw. festlegt
FontTransparent
Wert vom Typ Boolean, der festlegt, ob die Textausgabe mit durchscheinendem Hintergrund oder in einem Kasten mit Hintergrundfarbe erfolgt. Ein Ändern der Eigenschaft während der Druckerausgabe wirkt sich nur auf nachfolgende Textausgaben aus.
Fonts
Schreibgeschützte Auflistung der dem Printer-Objekt bekannten Schriften
FontSize
Wert vom Typ Single, der die Schriftgröße wiedergibt bzw. festlegt
ForeColor
Farbwert vom Typ Long, der die Vordergrundfarbe für Zeichenoperationen im Modus 13 (vbCopyPen) wiedergibt bzw. festlegt. Beliebige Farben lassen sich aus ihren Komponentenanteilen über die Funktion RGB komponieren. Die Funktion QBColor liefert den Farbwert einer der 16 Grundfarben. Zudem stehen die Elemente des Aufzählungstyps ColorConstants als vordefinierte Konstanten zur Verfügung.
hDC
Zur Laufzeit schreibgeschützter Wert vom Typ Long, der auf den Gerätekontext des Ausgabegerätes verweist und nur im Zusammenhang mit Funktionen der Win32-API benötigt wird
Height Width
Werte vom Typ Long, die die Höhe bzw. Breite der Ausgabefläche in Twips beschreiben. Sie richten sich im Allgemeinen nach der Eigenschaft PaperSize. Setzt man einen dieser Werte, ändert sich gleichzeitig der Wert von PaperSize auf 256. (Bei A4 ist die Ausgabefläche 16.834 × 11.909 Twips, was bei 600 dpi 7.014 × 4.962 Bildpunkten entspricht.)
287
Global- Objekt
vbFSTransparent (1)
Global- Objekt
Eigenschaft
Beschreibung
Orientation
Wert vom Typ Integer, der die Orientierung des Papiers beim Ausdruck festlegt. Der Aufzählungstyp PrinterObjectConstants definiert dafür zwei Konstanten: vbPRORPortrait (1) Hochformat (Standard)
Global- Objekt
vbPRORLandscape (2)
Querformat
Page
Zur Laufzeit schreibgeschützter Wert vom Typ Integer, der die aktuelle Seitennummer seit dem letzten Aufruf von EndDoc bzw. dem Programmstart wiedergibt. Der mit 1 initialisierte Wert erhöht sich mit jedem Aufruf von NewPage um 1, aber auch, wenn die Print-Methode den unteren Papierrand erreicht hat.
PaperBin
Wert vom Typ Integer, der die Papierzufuhr für die Druckerausgabe festlegt. Der Aufzählungstyp PrinterObjectConstants definiert dafür folgende Konstanten: vbPRBNUpper (1) Oberer Behälter
PaperSize
288
vbPRBNLower (2)
Unterer Behälter
vbPRBNMiddle (3)
Mittlerer Behälter
vbPRBNManual (4)
Manueller Einzug
vbPRBNEnvelope (5)
Umschlagspapiereinzug automat.
vbPRBNEnvManual (6)
Umschlagspapiereinzug manuell
vbPRBNAuto (7)
Standardbehälter (Voreinstellung)
vbPRBNTractor (8)
Traktor-Einzug
vbPRBNSmallFmt (9)
Kleiner Papierbehälter
vbPRBNLargeFmt (10)
Großer Papierbehälter
vbPRBNLargeCapacity (11)
Einzug mit großem Papiervorrat
vbPRBNCassette (14)
Kassetten-Cartridge
Wert vom Typ Integer, der die Papiergröße und damit die Größe der Ausgabefläche festlegt. Ein Setzen dieses Werts ändert gleichzeitig die Werte der Eigenschaften Height und Width. Der Aufzählungstyp PrinterObjektConstants definiert dafür folgende Konstanten: vbPRPSLetter (1) US Letter: 8½×11 Zoll vbPRPSLetterSmall (2)
US Letter Small: 8½×11 Zoll
vbPRPSTabloid (3)
US Tabloid: 11×17 Zoll
vbPRPSLedger (4)
US Ledger: 17×11 Zoll
vbPRPSLegal (5)
US Legal: 8½×14 Zoll
vbPRPSStatement (6)
US Statement: 5½×8½ Zoll
vbPRPSExecutive (7)
Executive: 7½×10½ Zoll
vbPRPSA3 (8)
DIN A3: 297×420 mm
vbPRPSA4 (9)
DIN A4: 210×297 mm
vbPRPSA4Small (10)
DIN A4: 210×297 mm
vbPRPSA5 (11)
DIN A5: 148×210 mm
Printer- Objekt
Eigenschaft
Beschreibung
PaperSize (Forts.)
vbPRPSB4 (12)
DIN B4: 250×354 mm
vbPRPSB5 (13)
DIN B5: 182×257 mm
vbPRPSFolio (14)
Folio: 8½×13 Zoll
vbPRPSQuarto (15)
Quarto: 215×275 mm
vbPRPS10x14 (16)
10×14 Zoll
vbPRPS11x17 (17)
11×17 Zoll US Note: 8½×11 Zoll Umschlag 9: 37/8×87/8 Zoll
vbPRPSEnv10 (20)
Umschlag 10: 41/8×9½ Zoll
vbPRPSEnv11 (21)
Umschlag 11: 4½×103/8 Zoll
vbPRPSEnv12 (22)
Umschlag 12: 4½×11 Zoll
vbPRPSEnv14 (23)
Umschlag 14: 5×11½ Zoll
vbPRPSCSheet (24)
Blatt in C-Größe
vbPRPSDSheet (25)
Blatt in D-Größe
vbPRPSESheet (26)
Blatt in E-Größe
vbPRPSEnvDL (27)
Umschlag DL: 110×220 mm
vbPRPSEnvC5 (28)
Umschlag C5: 162×229 mm
vbPRPSEnvC3 (29)
Umschlag C3: 324×458 mm
vbPRPSEnvC4 (30)
Umschlag C4: 229×324 mm
vbPRPSEnvC6 (31)
Umschlag C6: 114×162 mm
vbPRPSEnvC65 (32)
Umschlag C65:114×229 mm
vbPRPSEnvB4 (33)
Umschlag B4: 250×353 mm
vbPRPSEnvB5 (34)
Umschlag B5: 176×250 mm
vbPRPSEnvB6 (35)
Umschlag B: 176×125 mm
vbPRPSEnvItaly (36)
Umschlag: 110×230 mm
vbPRPSEnvMonarch (37)
Umschlag Monarch: 37/8×7½ Zoll
vbPRPSEnvPersonal (38)
Umschlag, 35/8×6½ Zoll
vbPRPSFanfoldUS (39)
U.S. Stand. Fanfold: 147/8×11 Zoll
vbPRPSFanfoldStdGerman (40)
Standard-Durchschlag: 8½×12 Zoll
vbPRPSFanfoldLglGerman (41)
Legal-Durchschlag: 8½×13 Zoll
vbPRPSUser (256)
benutzerdefiniert
Global- Objekt
Port
vbPRPSNote (18) vbPRPSEnv9 (19)
Schreibgeschützter Wert vom Typ String, der den Namen der Schnittstelle (zum Beispiel: »LPT1«) oder Netzwerkfreigabe wiedergibt, über die die Druckausgabe an den Drucker geleitet wird
289
Global- Objekt
Eigenschaft
Beschreibung
PrintQuality
Wert vom Typ Integer, der die Auflösung als Qualitätsstufe (negativer Wert) oder in dpi (positiver Wert, z.B.: 600) wiedergibt bzw. festlegt. Der Aufzählungstyp PrinterObjectConstants definiert vier Konstanten für die Wahl der Qualitätsstufe: vbPRPQDraft (-1) Entwurfsqualität
Global- Objekt
vbPRPQLow (-2)
Geringe Qualität
vbPRPQMedium (-3)
Mittlere Qualität
vbPRPQHigh (-4)
Hohe Qualität
RightToLeft
Schreibgeschützter Wert vom Typ Boolean, der angibt, ob das Gerät Ausgaben in umgekehrter Leserichtung ermöglicht (nur im Zusammenhang mit bidirektionalen Systemen von Interesse)
ScaleHeight ScaleWidth
Werte vom Typ Single, die die Höhe bzw. Breite der für die Druckausgabe zur Verfügung stehenden Fläche in der jeweils geltenden Maßeinheit (vgl. ScaleMode) und Vergrößerungsstufe (vgl. Zoom) wiedergeben. Jede Änderung dieser Werte wirkt sich als Skalierung des logischen Koordinatensystems aus, die physikalischen Abmessungen der für die Druckausgabe zur Verfügung stehenden Fläche bleiben dabei unverändert (vgl. PaperSize). Setzt man beispielsweise ScaleHeight auf den Wert 100, ordnet das der physikalischen Höhe der Druckausgabefläche (die im metrischen System beispielsweise 30 cm beträgt) im logischen Koordinatensystem das Maß 100 zu. Jede Wertänderung zieht implizit eine Änderung der Eigenschaft ScaleMode auf den Wert 0 (benutzerdefiniert) nach sich.
ScaleLeft ScaleTop
Werte vom Typ Single, die die Koordinaten der linken bzw. oberen Ecke der Druckausgabefläche wiedergeben (vgl. auch ScaleMode und Zoom). Jede Änderung dieser Werte wirkt sich als Translation des logischen Koordinatensystems aus. Die physikalischen Abmessungen der für die Druckausgabe zur Verfügung stehenden Fläche bleiben dabei unverändert (vgl. PaperSize). Setzt man beispielsweise ScaleLeft sowie ScaleTop auf den Wert -100 und ScaleWidth sowie ScaleHeight auf 200, wird die Druckausgabefläche durch das von den Punkten (-100, -100) und (100, 100) aufgespannte Rechteck beschrieben.
ScaleMode
Wert vom Typ Integer, der das für die Druckausgabe geltende Koordinaten- und Maßsystem beschreibt. Ist der Wert ungleich 0, liegt der Ursprung des Koordinatensystems links oben. Der Aufzählungstyp ScaleModeConstants definiert mehrere Konstanten für die Wahl des Maßsystems: vbUser (0) Benutzerdefiniertes Koordinaten- und Maßsystem (wird implizit gesetzt, wenn mindestens eine der Eigenschaften ScaleHeight, ScaleWidth, ScaleLeft und ScaleTop einen benutzerdefinierten Wert erhält)
290
vbTwips (1)
Maßeinheit ist Twips (567 Twips entsprechen einem Zentimeter und 1440 Twips einem Zoll)
vbPoints (2)
Maßeinheit ist Punkt (72 Punkt entsprechen einem Zoll)
Printer- Objekt
Beschreibung
ScaleMode (Forts.)
vbPixels (3)
Maßeinheit ist Bildpunkt (kleinste Einheit, die in der geltenden Auflösung auf dem Drucker dargestellt werden kann)
vbCharacters (4)
Maßeinheit ist Zeichen (horizontal: 120 Twips je Einheit; vertikal: 240 Twips je Einheit)
vbInches (5)
Maßeinheit ist Zoll
vbMillimeters (6)
Maßeinheit ist Millimeter
vbCentimeters (7)
Maßeinheit ist Zentimeter
vbHimetric (8)
Maßeinheit ist HiMetric (0,01 mm)
TrackDefault
Wert von Typ Boolean, der festlegt, ob das Printer-Objekt vonseiten des Betriebssystems veranlasste Änderungen des Standarddruckers zur Laufzeit des Programms mitmacht (True; Voreinstellung) oder nicht (False). Eine Änderung dieser Eigenschaft während der Druckausgabe erzwingt implizit den Aufruf der Methode EndPage.
TwipsPerPixelX TwipsPerPixelY
Zur Laufzeit schreibgeschützte Werte vom Typ Single, die ausdrücken, wie viele Twips bei der aktuell eingestellten Auflösung einem Bildpunkt in horizontaler (X) und vertikaler Richtung (Y) entsprechen (bei 600 dpi entsprechen 2,4 Twips einem Bildpunkt)
Width
Siehe Height
Zoom
Wert vom Typ Long, der den Vergrößerungsfaktor für die Druckausgabe in Prozent enthält. (Um von A4 auf A5 zu verkleinern, ist beispielsweise der Vergrößerungsfaktor 71 einzustellen.) Bei Druckern, die keine Vergrößerung unterstützen, ist dieser Wert 0, Standardvorgabe ist 100.
................................................... Methoden Methoden
Sub Printer.Circle (x As Single, y As Single, Radius As Single, _ [Color As Long, Start As Single, End As Single, Aspect As Single] Sub Printer.EndDoc() Sub Printer.KillDoc() Sub Printer.Line (Flags As Integer, X1 As Single, Y1 As Single, _ X2 As Single, Y2 As Single, Color As Long) Sub Printer.NewPage() Sub Printer.PaintPicture(pic As Picture, X1 As Single, _ Y1 As Single, [Width1 As Single], [Height1 As Single], [X2 As Single], [Y2 As Single], [Width2 As Single], _ [Height2 As Single], [Opcode]) Sub Printer.Print (Ausdr1[Trennz [Ausdr2] ... ]]) Sub Printer.PSet( X As Single, Y As Single, Color As Long) Function Printer.ScaleX(Width As Single, [FromScale], [ToScale]) _ As Single
291
Global- Objekt
Eigenschaft
Global- Objekt
Function Printer.ScaleY(Width As Single, [FromScale], [ToScale]) _ As Single Function Printer.TextHeight(Str As String) As Single
Global- Objekt
Function Printer.TextWidth(Str As String) As Single Das Printer-Objekt hat zwölf Methoden mit benannten Argumenten. Drei davon (EndDoc, NewPage und KillDoc) sind originäre Methoden des Printer-Objekts und dienen der Drucksteuerung. Die anderen Methoden finden sich auch bei anderen Objekten, die über einer Ausgabefläche operieren (Form, PictureBox, PropertyPage, UserControl, UserDocument), und ermöglichen Berechnungen sowie Operationen, die mittelbar oder unmittelbar mit dem Zeichnen im Zusammenhang stehen. Die Textausgabe erfolgt mit der Print-Anweisung, die Grafikausgabe mit PaintPicture, und gezeichnet wird mit Line, Circle und PSet. Die Syntax für den Aufruf der Methoden Circle, Line und PSet ist traditionell spezifisch: Circle [Step] (x, y), Radius, [Color], [Start], [End], [Aspect] Line [Step] (x1, y1) – [Step] (x2, y2), [Color], [B[F]] PSet [Step] (x, y), [Color] Ist das optionale Schlüsselwort Step angegeben, wird das unmittelbar folgende Koordinatenpaar als relativer Offset bzgl. der aktuellen Position (CurrentX, CurrentY) interpretiert, ansonsten als absolute Position – jeweils bezogen auf das geltende Koordinatensystem. Standardmäßig zeichnen die Methoden in der durch die Eigenschaft ForeColor spezifizierten Farbe (Standardwert ist Schwarz: vbBlack). Beliebige Farben lassen sich aus ihren Komponentenanteilen über die Funktion RGB zu einem Long-Wert für den optionalen Parameter Color komponieren. Definierte Farben liefert die Funktion QBColor (16 Grundfarben) sowie der Aufzählungstyp ColorConstants. Füllfarbe ist FillColor, sofern FillStyle ungleich 1 ist. Methode
Beschreibung
Circle
Zeichnet einen Kreis um den Mittelpunkt (x, y) mit Radius Radius und der optionalen Stiftfarbe Color. Sind die optionalen Parameter Start und End angegeben, zeichnet die Methode nur einen Teilkreis. Start und End bezeichnen den Start- und Endwinkel (im Bogenmaß). Ist der Parameter Aspect mit einem Wert ungleich 1 bzw. -1 angegeben, zeichnet die Methode eine Ellipse, die dem Kreis einbeschrieben (positive Werte) bzw. umbeschrieben (negative Werte) ist. Aspect drückt das Verhältnis zwischen Höhe und Breite des begrenzenden Rechtecks aus.
EndDoc
Schließt den aktuellen Druckjob ab und gibt gegebenenfalls zuvor noch die aktuelle Druckseite für den Ausdruck frei, wenn bereits Ausgabeoperationen darauf erfolgt sind
KillDoc
Bricht den aktuellen Druckjob ab
Line
Zeichnet eine Linie zwischen den Punkten (x1, y1) und (x2, y2). Ist der optionale Zusatz »B« gegeben, zeichnet die Methode das Rechteck, das die beiden Punkte definieren. Ist der optionale Zusatz »BF« gegeben, zeichnet die Methode das Rechteck und füllt es mit der Füllfarbe FillColor aus.
NewPage
Schließt die aktuelle Druckseite ab und gibt sie für den Ausdruck frei
292
Printer- Objekt
Beschreibung
PaintPicture
Zeichnet das im Datentyp Picture vorliegende Bild pic an der Position (X1, Y1), die den linken oberen Bildpunkt beschreibt. Alle Werte sind in der aktuellen Maßeinheit (ScaleMode) anzugeben. Sind die optionalen Parameter Height1 und Width1 angegeben, wird das Bild auf diese Maße skaliert, ansonsten errechnet sich die Methode diese Werte selbst und gibt das Bild in originaler Größe aus. Sind die optionalen Parameter X2 und Y2 sowie Width2 und Height2 vorhanden, beschreiben diese einen Bildausschnitt innerhalb des Bildes, der (nach impliziter Skalierung) in dem durch X1, Y1, Width1 und Height1 spezifizierten Rechteck ausgegeben wird. (Achtung: die Eigenschaften Height und Width eines PictureObjekts sind in der Maßeinheit »HiMetric« gehalten und müssen gegebenenfalls mittels ScaleX und ScaleY in die geltende Maßeinheit – Vorgabe ist Twips – umgewandelt werden.) Negative Werte für Height und/oder Width führen zu einer spiegelverkehrten und/oder um 180° gedrehten Ausgabe. Koordinaten für Ausschnitte sind auf den Ursprung des aktuellen Koordinatensystems zu beziehen!
Print
Gibt die in der Parameterliste genannten Werte unter Berücksichtigung etwaiger Trennzeichen (Semikolon, Komma, Tab(n)) in der gegebenen Schrift (Font-Eigenschaft) als Zeichenfolge an der aktuellen Position aus. Dabei beschreiben die Eigenschaften CurrentX und CurrentY den linken oberen Punkt des begrenzenden Rechtecks. Der Platzbedarf lässt sich mittels TextHeight und TextWidth ermitteln.
PSet
Zeichnet einen Punkt an der Position (x, y) in der Farbe Color
ScaleX ScaleY
Rechnet die Koordinate Width für die X-Achse (bzw. Y-Achse) vom Koordinatensystem FromScale in das Koordinatensystem ToScale um und liefert die neue Koordinate als Single. Fehlt der Parameter für ein Koordinatensystem, bezieht sich die Methode auf das aktuell geltende Koordinatensystem. Für die verschiedenen Koordinatensysteme sind Integer-Konstanten definiert (vgl. »ScaleMode-Eigenschaft«, S. 369)
TextHeight
Liefert die für die Ausgabe der Zeichenfolge Str benötigte Höhe in der aktuellen y-Maßeinheit des aktuellen Koordinatensystems
TextWidth
Liefert die für die Ausgabe der Zeichenfolge Str benötigte Breite in der aktuellen x-Maßeinheit des aktuellen Koordinatensystems
Anwendung
................................................... Anwendung
Das Druckmodell von Visual Basic ist relativ einfach, erlegt dem Programmierer leider aber auch gewisse Beschränkungen auf. Ein Visual-Basic-Programm muss damit auskommen, was auf dem jeweiligen System im Angebot ist, da es weder möglich ist, ein neues Printer-Objekt ins Leben zu rufen, noch die Printers-Auflistung zu erweitern. Das heißt auch, es kann zu einem Zeitpunkt immer nur einen Drucker ansprechen, nämlich den, auf den das PrinterObjekt gesetzt ist. (Die parallele Ausgabe auf mehreren Druckern ist zwar vom Prinzip her möglich, wenn man direkt auf Funktionen des GDI zurückgreift, die Angelegenheit wird dann jedoch recht aufwändig. (Vgl. auch den Tipp) Das Printer-Objekt verfügt über die nötige Ausstattung, die Druckausgabe durchzuführen. Die Ausgabe selbst ist seitenorientiert: Die Anwendung zeichnet (ohne weitere Vorbereitungen) jeweils den Inhalt einer Seite unter Verwendung der entsprechenden Methoden des Printer-
293
Global- Objekt
Methode
Global- Objekt
Global- Objekt
Objekts (darunter: Line, Circle, Print, PSet, Print TextHeight und TextWidth) und gibt diese Seite dann nach Fertigstellung explizit durch Aufruf der Methode NewPage für den Ausdruck frei. Die Freigabe der ersten Seite eröffnet einen Druckjob in der Warteschlange des dem Printer-Objekt zugeordneten Druckers. Nachfolgende Ausgaben erscheinen dann – bis zum nächsten NewPage-Aufruf – auf der nächsten Seite. NewPage pflegt darüber hinaus die Page-Eigenschaft des Objekts, die somit (beginnend mit 1) immer die aktuelle Seitennummer wiedergibt. Der Zeichenvorgang an sich unterscheidet sich nicht vom Zeichnen in ein Formular oder ein Steuerelement. Falls der eingestellte Drucker keine Farben unterstützt, setzt der Druckertreiber die Farbwerte je nach Voreinstellung im Eigenschaftsfenster in Grauwerte um. Zum Abschluss eines Druckjobs ist ein Aufruf der Methode EndDoc erforderlich. (Visual Basic führt diesen Aufruf bei Programmende zwar von sich aus durch, darauf zu bauen, wäre aber schlechter Programmierstil und würde insbesondere auch den Drucker bis zum Programmende blockieren.) Falls zwischen dem letzten Aufruf von NewPage und EndDoc Ausgaben stattgefunden haben, gibt EndDoc erst die angefangene Seite zum Ausdruck frei und beendet dann den Druckjob. Mit Beendigung eines Druckjobs erhält Page wieder den Wert 1. Die Methode KillDoc erlaubt es, einen noch nicht abgeschlossenen Druckjob unmittelbar abzubrechen (je nach Zustand der Warteschlange des eingestellten Druckers kann dies auch während des Ausdrucks sein, im Drucker gepufferte vollständige Seiten kommen aber noch zum Ausdruck). Zur Gestaltung der Ausgabe können die Eigenschaften des Printer-Objekts jederzeit geändert werden, etwa um eine andere Schriftart oder Schriftgröße einzustellen oder ein Attribut zu ändern. Eine Änderung wirkt sich erst für die nächste Ausgabeoperation aus, hat also keinen Rückbezug auf bereits geschehene Ausgabeoperationen für die jeweilige Seite. Tipps
................................................... Tipps In manchen Fällen ist der mit dem Betriebssystem verbundene und verallgemeinernde Overhead bei der Druckerausgabe eher eine Last als eine Erleichterung, so beispielsweise, wenn der installierte Treiber das Ausgabegerät nicht im gewünschten Umfang unterstützt oder wenn Lösungen für ganz spezifische Problemstellungen gefragt sind. Ein Programm kann einen Drucker (oder ein sonstiges Ausgabegerät) dann gegebenenfalls direkt über die jeweilige Geräteschnittstelle ansprechen – die klassische Methode der Druckerausgabe also. Dazu öffnet und schließt man die Schnittstelle wie eine Datei (Open "LPT1:" For Binary …) und versorgt das Gerät via Print# mit geeigneten Daten. Wenn Sie ein Formular ausdrucken wollen, wie es auf dem Bildschirm erscheint, führt an der Methode PrintForm des jeweiligen Formularobjekts kein Weg vorbei. Diese Methode gibt den Inhalt des dem Formular untergelegten Fensters als eigenständige Seite auf das Printer-Objekt (unter Beachtung des aktuellen Zustands) aus – die Ausgabe auf eine bereits angefangene Seite nötigt das Printer-Objekt, einen Laufzeitfehler auszulösen. Aber Achtung, alle Inhalte müssen in Steuerelementen untergebracht sein. Wie das Beispielprogramm zeigt, berücksichtigt PrintForm nämlich nur die auf dem Formular befindlichen Steuerelemente (samt Inhalten), nicht jedoch sonstige, direkt in das Formular gezeichnete Inhalte, seien es Texte, Grafiken oder Bilder. Zur Abhilfe sollte man solche Inhalte in ein PictureBox-Steuerelement zeichnen. Mit diesem Steuerelement lässt sich auch eine (allerdings nicht sehr befriedigende) Druckvorschau implementieren. Man legt ein Formular an, das nichts weiter als ein PictureBox-Steuerelement enthält, und zeichnet alle auszudruckenden Inhalte in dieses Steuerelement. Zu beachten gilt es lediglich, dass die AutoRedraw-Eigenschaft des Formularobjekts auf True gesetzt ist. Der Ausdruck selbst ist dann mit einem einfachen PrintForm-Aufruf erledigt. Leider werden nur die Inhalte eines Formulars gedruckt, die bei der gegebenen Auflösung auf dem Bildschirm Platz haben (wenn Teile des Formulars verdeckt sind, spielt das keine Rolle). Zudem lässt die Qualität von Grafiken wegen der kleineren Auflösung des Bildschirms (96 dpi) stark zu wünschen übrig.
294
Printer- Objekt
Beispiel
................................................... Beis piel
Formular des Projektes PrinterDemo Das Formular enthält zwei Schaltflächen: Die eine (cmdDemoAusdruck) demonstriert die Druckausgabe unter Verwendung der verschiedenen Methoden des Printer-Objekts, die andere (cmdFormularAusdruck) zeigt die Ausgabe des Formulars mittels der PrintForm-Methode des Form-Objekts. Option Explicit ' Mit etwas Unterstützung der WinApi ... Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
295
Global- Objekt
Das Projekt PrinterDemo zeigt die wichtigsten Aspekte im Umgang mit dem Printer-Objekt. Der Code initialisiert das Kombinationsfeld-Steuerelement Combo1 (dessen Style-Eigenschaft beim Entwurf auf 1 oder 2 gesetzt wurde) mit den Namen der auf dem System verfügbaren Drucker und zeigt den Standarddrucker als aktuelle Auswahl im Kombinationsfeld an. Verliert das Kombinationsfeld den Fokus, erhält das Printer-Objekt – entsprechend der im Kombinationsfeld getroffenen Auswahl – gegebenenfalls einen neuen Wert. Um den Zustand des Kombinationsfelds mit dem aktuellen Wert des Printer-Objekts zu synchronisieren, bedient sich der Code der SendMessage-Funktion aus der Win32-API mangels einer geeigneten Methode seitens des Steuerelements. Diese Funktion ermöglicht es, dem Steuerelement eine Nachricht zu senden, die es anweist, nach einem bestimmten Eintrag zu suchen und dessen Index zu liefern. Die folgende Abbildung zeigt das Fenster der Anwendung:
Global- Objekt
(ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _ lParam As Any) As Long Private Const CB_FINDSTRING = &H14C
Global- Objekt
Private Sub cmdDemoAusdruck_Click() Demo Printer Printer.EndDoc End Sub
' Druckjob abschließen
Private Sub cmdFormularAusdruck_Click() PrintForm End Sub Private Sub Combo1_Lostfocus() If Printer.DeviceName <> Combo1.Text Then ' Hat sich was geändert? Set Printer = Printers(Combo1.ListIndex) End If End Sub Private Sub Form_Load() Dim p As Printer, lIndex As Long For Each p In Printers ' Kombifeld initialisieren Combo1.AddItem p.DeviceName Next ' Standarddrucker auswählen. Suchen lassen lIndex = SendMessage(Combo1.hwnd, CB_FINDSTRING, -1, _ ByVal Printer.DeviceName) Combo1.ListIndex = lIndex ' Eintrag auswählen End Sub Sub Demo(obj As Object) Dim x As Single obj.ScaleMode = vbMillimeters obj.ScaleLeft = -obj.ScaleWidth / 2 obj.ScaleTop = -obj.ScaleHeight / 2
' Millimeter als Maßeinheit ' Ursprung in die Mitte
obj.Line (-70, 0)-(70, 0), vbBlack ' Koordinatenkreuz obj.Line (0, -70)-(0, 70), vbBlack obj.Line (-70, -70)-(70, 70), vbBlack, B ' Quadrat drum herum For x = 10 To 70 Step 10 obj.Circle (0, 0), x, vbBlack ' Kreise drum herum Next x ' Überschrift obj.FontName = "Times New Roman" obj.FontSize = 30 obj.FontBold = True
' Schrift für Beschriftung
obj.CurrentX = -obj.TextWidth("Demo-Ausdruck") / 2 obj.CurrentY = -70 – obj.TextHeight("Demo-Ausdruck") obj.Print "Demo-Ausdruck"
296
Printers- Auflistung
' Beschriftung obj.FontName = "Courier New" obj.FontSize = 8.5
' Schrift für Beschriftung
Global- Objekt
For x = -70 To 70 Step 10 ' Skala horizontal obj.CurrentX = x – obj.TextWidth(CStr(x)) / 2 obj.CurrentY = 0 obj.Print CStr(x) ' Skala vertikal obj.CurrentX = -obj.TextWidth(CStr(x)) obj.CurrentY = x – obj.TextHeight(CStr(x)) / 2 obj.Print CStr(x) Next x End Sub Private Sub Form_Paint() Demo Me End Sub Private Sub Form_Resize() Refresh End Sub Verwandte Objekte
................................................... Verwandte Objekte Form, PictureBox Verwandte Themen
................................................... Verwandte Them en
Standarddialoge-Steuerelement (CommonDialog) (S. 444)
Printers- Auflistung Printers As Object Beschreibung
................................................... Bes c hreibung
Die nicht mit dem Auflistungstyp Collection kompatible Printers-Auflistung stellt eine Liste mit allen auf dem System installierten Druckern bereit. Elementdatentyp ist Printer. Einzige Eigenschaft der Auflistung ist Count, über die sich die Anzahl der Listenelemente in Erfahrung bringen lässt. Anwendung
................................................... Anwendung
Sie können die Auflistung mit einem For Each-Konstrukt und einer Zählvariablen des Typs Printer durchlaufen. Für den direkten Zugriff auf ein Element verwenden Sie die übliche ArraySchreibweise unter Angabe eines Index (Zählung beginnt bei 0). Visual Basic initialisiert das Printer-Objekt grundsätzlich mit dem aktuellen Standarddrucker des Systems. Um auf einem anderen Drucker Ausgaben anzufertigen, weisen Sie dem PrinterObjekt das zugehörige Element der Auflistung über eine Set-Anweisung zu. Beispiel
................................................... Beis piel
Vgl. das Beispiel zu »Printer-Objekt« (S. 284).
297
Global- Objekt
Verwandte Themen
................................................... Verwandte Them en
Auflistungen und (CommonDialog) (S. 444)
Collection-Objekte
(S. 304);
Standarddialoge-Steuerelement
QBColor- Methode Function QBColor (Color As Integer) As Long Beschreibung
Global- Objekt
................................................... Bes c hreibung
Die QBColor-Funktion liefert die zur Farbnummer Color gehörige RGB-Farbe als Long-Farbwert zurück. Anwendung
................................................... Anwendung
Bei älteren noch auf DOS basierenden Basic-Versionen (etwa QBasic), musste sich der Programmierer (mittels der Screen-Anweisung) noch explizit um die Steuerung der Grafikanzeige (CGA, EGA, VGA, PVGA etc.) kümmern und konnte je nach vorhandener Hardware zwischen verschiedenen Bildschirmauflösungen und Farbtiefen wählen. Mit Windows und der Philosophie der geräteunabhängigen Ausgabe ist die Angelegenheit einfacher geworden: Da die Ausgabe unter Windows fensterbasiert ist, ist sie gewissermaßen auflösungsunabhängig geworden, und für die Farbausgabe wird inzwischen einheitlich der TrueColor-Farbraum des 24-Bit-Farbmodells zugrunde gelegt. Die Farben des 16-farbigen EGA/VGA-Modus spielen aber bei der Programmierung als Grundfarben nach wie vor eine wichtige Rolle, da sie eine klar zu unterscheidende Farbauswahl darstellen. Da die QBColor-Funktion den indizierten Farbraum des 16-farbigen EGA/VGA-Modus in den TrueColor-Farbraum des 24-Bit-Farbmodells abbildet, ist sie ein unverzichtbares Werkzeug für die Umsetzung älterer Basic-Programme nach Visual Basic. Die folgende Tabelle gibt einen Überblick über die Zuordnung zwischen den Farbnummern und den Farben: Farbnummer
Farbe
Farbnummer
Farbe
0
Schwarz
8
Grau
1
Blau
9
Hellblau
2
Grün
10
Hellgrün
3
Zyan
11
Hellzyan
4
Rot
12
Hellrot
5
Magenta
13
Hellmagenta
6
Gelb
14
Hellgelb
7
Weiß
15
Leuchtend Weiß
Beispiel
................................................... Beis piel
Line (0, 0)-(1000, 1000), QBColor(6) Verwandte Befehle
................................................... Verwa ndte Befehle
RGB
298
RGB- Methode
RGB- Methode Function RGB(Red As Integer, Green As Integer, Blue As Integer) As Long Beschreibung
................................................... Bes c hreibung
Die RGB-Methode komponiert eine RGB-Farbe für den TrueColor-Farbraum aus den Werten für die einzelnen Farbkomponenten Rot, Grün und Blau und gibt diese als Long-Farbwert zurück. Die Funktion unterscheidet die Werte 0 bis 255 für die drei Parameter Red, Green und Blue. (Größere Werte verursachen keinen Laufzeitfehler, sondern werden auf 255 abgerundet.) Anwendung
Der Einsatz der RGB-Methode ist immer dann angesagt, wenn eine Farbe aus dem TrueColorFarbraum komponentenweise zusammengestellt werden muss. Die Parameter der Methode drücken die zwischen 0 und 255 abgestuften Helligkeitsanteile der einzelnen Farbkomponenten aus, so dass der Aufruf RGB(0, 0, 0) den Farbwert für Schwarz und RGB(255, 255, 255) den Farbwert für Weiß liefert. Die Methode macht eigentlich nichts weiter, als die Komponentenwerte in die unteren drei Bytes des Farbwerts umzumünzen nach der Formel RGBWert = (Rot Mod 256) + (Green Mod 256) * 256 + (Blue Mod 256) * 65536 Beispiel
................................................... Beis piel
Der folgende Code gibt einen so genannten Graukeil aus: ScaleMode = vbPixels For i = 0 To 255 Line (i, 0)-(i, 1000), RGB(i, i, i) Next i
SavePicture- Methode Sub SavePicture(Picture As Picture, FileName As String) Beschreibung
................................................... Bes c hreibung
Die SavePicture-Methode speichert ein Bild Picture in der Datei FileName. Bilder der Formate BMP, JPG, und GIF werden dabei unabhängig von ihrem ursprünglichen Format im BMP-Format abgespeichert, während Bilder der Formate ICO, WMF, EMF und CUR ihr ursprüngliches Format beibehalten. Anwendung
................................................... Anwendung
Ein interessanter Einsatzbereich dieser Methode ist das Abspeichern des aktuellen Zustands der Zeichenfläche eines Formulars, die über die Image-Eigenschaft verfügbar ist. Beispiel
................................................... Beis piel
Der folgende Code speichert den von Grafikmethoden (PSet, Circle, Line, Print) gezeichneten Inhalt eines Formulars im BMP-Format. (Beachten Sie, dass dazu die AutoRedraw-Eigenschaft des Formulars auf True gesetzt sein muss!) SavePicture Image, "Forminhalt.bmp"
299
Global- Objekt
................................................... Anwendung
Global- Objekt
SaveSetting- Methode Sub SaveSetting(AppName As String, Section As String, _ Key As String, Setting As String) Beschreibung
................................................... Bes c hreibung
Die Methode SaveSetting ermöglicht es, persistente Einstellungen eines Programms in Systemregistrierung persistent als Unterschlüssel des Schlüssels
Global- Objekt
HKEY_CURRENT_USER\Software\VB and VBA Program Settings\ zu speichern. Für den Aufruf gilt: AppName steht für den Anwendungsnamen und ist Unterschlüssel von »VB and VBA Program Settings«; Section steht für den Abschnitt und ist Unterschlüssel von AppName; Key ist Eintrag unter Section und steht für den Namen des zu speichernden Werts; Setting ist der Key zugeordnete Datenwert. Anwendung
................................................... Anwendung
Früher mussten Windows-Anwendungen ihre persistenten Einstellungen noch in je eigenen INIDateien ablegen. Spätestens seit Windows 95 (aber auch schon zuvor) gibt es eine zentrale Systemregistrierung, in der sich solche Daten an zentraler Stelle speichern lassen. Für den Zugriff auf die Systemregistrierung von Visual Basic aus stehen im Wesentlichen vier Methoden zur Verfügung: GetSetting, GetAllSettings, SaveSetting und DeleteSetting, deren Funktion bereits durch die Bezeichner deutlich wird. Um auf andere Schlüssel zuzugreifen, müssen Sie die entsprechenden Funktionen der Win32-API (RegSaveKey etc.) benutzen. Beispiel
................................................... Beis piel
Vgl. »GetAllSettings-Methode« (S. 279).
Screen- Objekt Public Screen As Screen Beschreibung
................................................... Bes c hreibung
Das aus der Bibliothek VB stammende Screen-Objekt ist in jeder Anwendung als globales Objekt bekannt. Es repräsentiert den gesamten Bildschirm als Ausgabegerät, ohne jedoch selbst Methoden für die Ausgabe zu besitzen. Seine Eigenschaften geben Aufschluss über die physikalische Auflösung sowie die Abmessungen des eingestellten Bildschirmmodus, die auf dem System installierten Schriften und darüber, welches Formular und Steuerelement der Anwendung gerade aktiv ist (lies: im Besitz des Fokus ist). Weiterhin erlauben sie die Kontrolle über den für alle Formulare und Steuerelemente der Anwendung geltenden Mauszeiger. Die folgende Tabelle gibt einen Überblick über die Eigenschaften des Screen-Objekts. Eigenschaft
Beschreibung
ActiveControl
Zur Laufzeit schreibgeschützter Wert, der vom System gepflegt wird und eine Objektreferenz auf das Steuerelement des aktiven Formulars darstellt, das augenblicklich den Fokus besitzt; der Datentyp der Objektreferenz richtet sich nach dem Steuerelement (als Datentyp für die Parameterübergabe ist daher Control zu wählen)
3 00
Screen- Objekt
Beschreibung
ActiveForm
Zur Laufzeit schreibgeschützter Wert, der vom System gepflegt wird und eine Objektreferenz auf das aktive Formular darstellt. Ist ein MDI-Formular aktiv, bezeichnet diese Eigenschaft das aktive, dem MDI-Formular untergeordnete Unterformular.
FontCount
Zur Laufzeit schreibgeschützter Wert vom Typ Integer, der die Anzahl der auf dem System installierten Schriftarten wiedergibt
Fonts
Auflistung mit dem Elementtyp Font und Indexbereich: 0 bis FontCount – 1 für die auf dem System installierten Schriftarten
Height Width
Zur Laufzeit schreibgeschützte Werte vom Typ Single, die die Höhe bzw. Breite des Bildschirms in Twips ausdrücken (1 Zentimeter entspricht ca. 567 Twips, 1 Zoll entspricht 1440 Twips)
MouseIcon
Wert von Typ Picture. Lässt sich zur Laufzeit auf ein CUR- oder ICO-Bild (nicht jedoch ANI) setzen. Ermöglicht die Anzeige eines benutzerdefinierten Mauszeigers im Bereich des aktiven Formulars, wenn MousePointer den Wert 99 hat.
MousePointer
Wert vom Typ MousePointerConstants, der festlegt, welcher Mauszeiger im Bereich des aktiven Formulars angezeigt wird. vbDefault (0) objektspezifisch (Voreinstellung)
TwipsPerPixelX TwipsPerPixelY
vbArrow (1)
Pfeilsymbol
vbCrosshair (2)
Kreuz
vbIbeam (3)
I-Symbol
vbIconPointer (4)
Symbol (Quadrat in Quadrat)
vbSizePointer (5)
Pfeilkreuz Nord/Süd/West/Ost
vbSizeNESW (6)
Doppelpfeil Nord-Ost/Süd-West
vbSizeNS (7)
Doppelpfeil Nord/Süd
vbSizeNWSE (8)
Doppelpfeil Nord-West/Süd-Ost
vbSizeWE (9)
Doppelpfeil West/ Ost
vbUpArrow (10)
Pfeil aufwärts zeigend
vbHourglass (11)
Sanduhr
vbNoDrop (12)
nicht ablegen
vbArrowHourglass (13)
Sanduhr mit Pfeil
vbArrowQuestion (14)
Pfeil mit Fragezeichen
vbSizeAll (15)
Größenänderung alle
vbCustom (99)
benutzerdefiniertes Symbol, wie in MouseIcon definiert
Zur Laufzeit schreibgeschützte Werte vom Typ Single, die ausdrücken, wie viele Twips bei der aktuell eingestellten Bildschirmauflösung einem Bildpunkt in horizontaler (X) und vertikaler Richtung (Y) entsprechen
3 01
Global- Objekt
Eigenschaft
Global- Objekt
Anwendung
Global- Objekt
................................................... Anwendung
Eine wichtige Aufgabe des Screen-Objekts ist die Kontrolle des Mauszeigers für die gesamte Anwendung. Solange Screen.MousePointer den Wert vbDefault hat, kann jedes Formular und Steuerelement den Mauszeiger für seinen Fensterbereich individuell über seine eigene MousePointer-Eigenschaft kontrollieren. Jeder andere Wert für Screen.MousePointer erklärt den entsprechenden Mauszeiger als allgemeingültig und setzt die objektspezifischen Einstellungen für die Fenster der Anwendung (temporär) außer Kraft. Auf diese Weise kann eine Anwendung beispielsweise auf die Sanduhr umschalten, wenn eine zeitaufwändige Operation die Reaktivität der Benutzerschnittstelle beeinträchtigt. Eine wichtige Informationsquelle für die Bildschirmausgabe ist auch die Fonts-Auflistung des Screen-Objekts. Sie gibt einen Überblick über die auf dem System installierten Schriften. Beispiel
................................................... Beis piel
Das folgende Codefragment überträgt die Namen der auf dem System installierten Schriftarten in die Liste eines Kombinationsfeld-Steuerelements namens Combo1. Dim f As Integer For f = 0 To Screen.FontCount -1 Combo1.AddItem Screen.Fonts(f) Next
Sendkeys- Methode Sub SendKeys(String As String, [Wait As Boolean]) Beschreibung
................................................... Bes c hreibung
Die SendKeys-Methode interpretiert den Wert des Parameters String als Tastenfolge und sendet entsprechende Tastaturereignisse an das aktive Fenster (lies: an das Element, das den Fokus besitzt), so als ob der Benutzer dort die Tastenfolge per Tastatur eingegeben hätte. Wird der Parameter Wait mit dem Wert True versorgt, wartet die Anwendung, bis die Tastenfolge von der Zielanwendung tatsächlich verarbeitet wurde (Synchronbetrieb), ansonsten findet die Verarbeitung asynchron statt. Anwendung
................................................... Anwendung
Die SendKeys-Methode macht es möglich, das Fenster einer beliebigen Anwendung, das den Fokus besitzt, durch die Simulation von Tastaturereignissen gewissermaßen »fernzusteuern«. In der Tat ist dieser einfache Mechanismus seit jeher ein probates Mittel, die Prozessgrenze zu fremden Anwendungen zu überwinden und deren Steuerung zu übernehmen – eine primitive Form der Automatisierung also. Eine einfache Syntax mit einer Reihe von Schlüsselwörtern für die Bildung des Parameterwerts stellt sicher, dass sich alle Tasten und Tastenkombinationen ausdrücken lassen. Die Sonderzeichen »+«, »^« und »%« stehen für die Funktionstasten (Umschalt), (Strg) und (Alt) und wirken sich auf das folgende Zeichen bzw. die folgende durch einfache Klammern ausgezeichnete Gruppierung aus: SendKeys "+abs" SendKeys "+(abs)"
' sendet "Abs" ' sendet "ABS"
Um mehrere Funktionstasten auf ein Zeichen oder eine Zeichengruppe anzuwenden, ist keine Klammerung erforderlich. Für Zeichen, die sich in Großschreibung notieren lassen, ist die Angabe von »+« nicht erforderlich – aber auch nicht schädlich: SendKeys "ABS"
3 02
' sendet "ABS"
Unload- Methode
Zu den weiteren Sonderzeichen zählen »~« für die Eingabetaste sowie die geschweiften Klammern »{Schlüsselwort}« zur Notation der Sonderzeichen »+^%~(){}« in literaler Bedeutung sowie von Schlüsselwörtern für Tasten, die selbst kein Zeichen hervorbringen. SendKeys "{{ }" SendKeys "erste Zeile+~zweite Zeile"
' sendet "{" ' Zeilenwechsel in Multiline' Textfeld SendKeys "nähmlich{Left 5}{Backspace}{Right 4}" ' Eingabe der Zeichen' folge "nämlich"
Symbol
Bedeutung
Symbol
Bedeutung
+
(Umschalt)
^
(Strg)
%
(Alt)
~
(Eingabe)
(
Zeichengruppierung
)
Zeichengruppierung
{
Schlüsselwort folgt
}
Schlüsselwort zu Ende
{Backspace} {BS}, {Bksp}
(Rücktaste)
{Delete} {Del}
(Entf)
{Capslock}
(Feststelltaste)
{Break}
(Pause)
{Down}
(Unten)
{End}
(Ende)
{Enter}
(Eingabe)
{Esc}
(Esc)
{Help}
(Hilfe)
{Home}
(Pos1)
{Insert}, {Ins}
(Einf)
{Left}
(Links)
{Numlock}
(Num)
{PgDn}
(BildAb)
{PgUp}
(BildAuf)
{PrtSc}
(Druck)
{Scrolllock}
(Rollen)
{Tab}
(Tab)
{Up}
(Oben)
{F1} bis {F16}
(F1) bis (F16)
Beispiel
................................................... Beis piel
SendKeys "%{F4}"
' beendet aktives Fenster
Unload- Methode Sub Unload(Object) Beschreibung
................................................... Bes c hreibung
Die Unload-Methode entlädt ein Formular oder ein zuvor mittels Load in ein SteuerelementeArray geladenes Steuerelement Object. Der Entladevorgang zerstört zwar das Fenster der Instanz, nicht jedoch das in der Visual-Basic-Umgebung dafür existierende Objekt. (Dieses wird erst abgebaut, wenn sein Geltungsbereich erloschen ist und das Terminate-Ereignis eintritt!) Für ein Formular bewirkt der Unload-Aufruf der Reihe nach die Ereignisse QueryUnload und Unload. Das Formular kann im Zuge der Behandlung dieser Ereignisse den Entladevorgang unterbinden, indem es den Cancel-Parameter auf True setzt. In diesem Fall bleibt der UnloadAufruf ohne weitere Wirkung.
3 03
Global- Objekt
Die folgende Tabelle gibt einen Überblick über alle von der Methode erkannten Schlüsselwörter und Symbole sowie deren Bedeutung:
Auflistungen und Collection- Objekte
Anwendung
................................................... Anwendung
Ein einmal entladenes Steuerelement oder Formular kann jederzeit mit Load erneut geladen und durch Setzen der Visible-Eigenschaft auf True (bzw. einen Show-Aufruf) erneut angezeigt werden. Das Fenster des Objekts wird dann neu initialisiert. Beachten Sie jedoch, dass sich Steuerelemente, die bereits zur Entwurfzeit auf einem Formular platziert wurden, nicht entladen lassen. Beispiel
Auflistungen und Collection- Objekte
................................................... Beis piel
Ein Formular enthalte zum Beenden seiner selbst eine Befehlsschaltfläche namens cmdBeenden. Der Code dafür ist denkbar einfach: Private Sub cmdBeenden_Click() Unload Me End Sub Verwandte Befehle
................................................... Verwa ndte Befehle
Load
Auflistungen und Collection- Objekte Dim cols As Collection Objekt.Controls, Printers, Objekt.ListImages
Forms,
Screen.Fonts,
Printer.Fonts,
DataObject.Files,
Beschreibung
................................................... Bes c hreibung
Bei Auflistungen handelt es sich um spezielle Datentypen, die ähnlich einem dynamischen Array beliebige Elemente eines Datentyps speichern können und den indexsequenziellen Zugriff darauf ermöglichen. Trotz der rein – funktional gesehen – engen Verwandtschaft mit dynamischen Arrays sind Auflistungen Objekte mit Eigenschaften und Methoden, und der zugehörige allgemeine Objektdatentyp Collection ist so etwas wie eine »objektorientierte Implementation dynamischer Arrays«. Wie für jeden Objektdatentyp lassen sich auch für den Typ Collection Objektvariablen vereinbaren, die auf bestehende Auflistungen verweisen können. Neue Auflistungsobjekte werden wie üblich mit dem New-Operator generiert. Anders als bei dynamischen Arrays ist der Elementdatentyp einer Auflistung grundsätzlich Variant, so dass Auflistungen als Elementwerte alles »schlucken«, was sich auch einer Variablen des Typs Variant zuweisen lässt – Werte mit benutzerdefinierten Datentypen, für die eine Type-Deklaration vorliegt, gehören somit nicht dazu. Ein Collection-Objekt besitzt die vier Methoden Add, Remove, Count und Item. Die Methode Add fügt ein neues Element in die Auflistung ein. Ihr Prototyp lautet: Sub Liste.Add (Item, [Key As String], [Before As Long], [After As Long])
Der Parameter Item stellt den Wert den neuen Elements. Über den Zeichenfolgenparameter Key lässt sich optional ein Schlüssel für den assoziativen Zugriff auf das Element festlegen. Er muss eindeutig sein. Die optionalen Parameter Before bzw. After erlauben die Angabe eines Positionswerts größer 0, vor bzw. nach dem das Element für den indexsequenziellen Zugriff eingefügt wird. Ohne Positionsangabe fügt das Objekt neue Elemente grundsätzlich hinten an.
3 04
Auflistungen und Collection- Objekte
Umgekehrt ermöglicht die Methode Remove das Entfernen eines Elements aus der Auflistung. Sie wird unter Angabe der Position Index oder des für das Element vereinbarten Schlüssels Key aufgerufen: Sub Liste.Remove (Index As Long | Key As String)
Function Liste[.Item](Index As Long | Key As String)
Äquivalent sind folgende Schreibweisen: Element = MeineListe.Item(i) Element = MeineListe(i)
Falls das Element i seinerseits eine Auflistung ist, mutet die reduzierte Schreibweise für den Zugriff auf deren j-tes Element ein wenig seltsam an, sie ist aber korrekt: Element = MeineListe(i)(j) Anwendung
................................................... Anwendung
Die Bezeichner von Eigenschaften und Variablen, die als Auflistung definiert sind, tragen in Visual Basic traditionell das Plural-«s« der englischen Sprache. Allerdings ist nicht überall, »wo Auflistung draufsteht, auch Collection drin«. Das liegt im Wesentlichen daran, dass Windows selbst eine Reihe verschiedener Objektdatentypen für spezifische Auflistungen verwendet, für die andere Eigenschaften und Methoden als für Collection definiert sind. Mithin ist es also nicht möglich, einer Collection-Variablen jede in Visual Basic anzutreffende Auflistung zuzuordnen, so dass in vielen Fällen ein spezieller Typ für die Variable vereinbart oder auf den allgemeinen Datentypen Object zurückgegriffen werden muss. Als Programmierer bekommt man hier allerdings schnell den Eindruck des Wildwuchses, denn was hier eindeutig fehlt, ist eine klare Linie. So ist es beispielsweise nicht gerade sehr instruktiv, dass manche Auflistungen mit dem Index 0 beginnen, andere dagegen mit dem Index 1, und dass Auflistungen wie Fonts der Objekte Screen und Printer oder die List-Eigenschaft von Listenfeldern weder einer Objektvariablen noch einer Array-Variablen als Wert zugeordnet werden können – mit dem dummen Effekt, dass sie sich auch nicht als Parameter bei Funktionsaufrufen übergeben lassen. Immerhin ist die Schreibweise für den Zugriff auf einzelne Elemente einheitlich: Es kann durchgehend die Array-Notation unter Angabe eines Positionsindex verwendet werden – was übrigens nur deshalb geht, weil Item als Standardmethode für Auflistungen vereinbart ist und nicht notiert werden muss. Darüber hinaus erweist sich die Möglichkeit, neben dem Positionsindex auch Bezeichner für die Elemente einer Auflistung zu vereinbaren, als höchst praktisch, wenn es eine Sortierordnung gibt oder mit einer Fluktuation der Elemente zu rechnen ist. Nachdem die Remove-Methode dafür sorgt, dass keine Lücken in der fortlaufenden Zählung der Elemente entstehen, verkleinern sich die Positionsindizes aller Elemente nach einem gelöschten Element um 1. Umgekehrt erhöht die Add-Methode die Positionsindizes nachfolgender Elemente um 1, wenn sie ein Element an einer bestimmten Position einfügt. Während sich gewöhnliche Arrays elementweise ausschließlich in For-Zählschleifen mit Zählindex durchlaufen lassen, gibt es für Auflistungen als Alternative die For Each-Kontrollstruktur,
3 05
Auflistungen und Collection- Objekte
Die parameterlose Methode Count liefert die Anzahl der Elemente in der Auflistung und zugleich – da Auflistungen 1-basiert sind – die Position des letzten Elements. Solange eine Auflistung leer ist, lautet das Ergebnis 0. Die Methode Item wird unter Angabe eines Positionswerts Index oder Schlüssels Key aufgerufen und liefert das entsprechende Element. Da es sich bei dieser Methode um die Standardmethode des Objekts handelt, muss ihr Bezeichner nicht unbedingt notiert werden, der Parameter aber sehr wohl. Der Prototyp lautet:
Formulare
die kein Problem damit hat, wenn sich die Anzahl der Elemente während der Aufzählung ändert, und die auch die Ergebnisse von Add und Remove berücksichtigt, sofern sich diese auf noch nicht aufgezählte Positionen beziehen. Dim Drucker As Printer For Each Drucker In Printers List1.AddItem Drucker.DeviceName Next Beispiel
Formulare
................................................... Beis piel
Der folgende Code arbeitet mit drei verschiedenen Auflistungstypen. Er instanziiert ein neues Auflistungsobjekt und fügt die Schriftnamen des zweiten (!) auf dem System installierten Druckers als Einträge ein. Dim l As Collection Set l = New Collection For i = 0 To Printers(1).FontCount – 1 ' Printers beginnt bei Index 0 l.Add Printers(1).Fonts(i) ' Fonts beginnt bei Index 0 Next Print TypeName(l(1)) ' Ausgabe: "String"
Formulare Wenn es so etwas wie einen Standardzugang für die Programmierung mit Visual Basic gibt, dann ist dies das Formular. Es vereinigt zunächst einmal alles unter sich, was für die Umsetzung traditioneller Basic-Programme benötigt wird: die zeichenorientierte Ein- und Ausgabe sowie eine grafische Ausgabefläche in Form eines Fensters. Damit aber nicht genug. Hinzu kommt die mühelose Integration nicht nur aller klassischen Elemente der Windows-Benutzerschnittstelle: Menü, Symbolleiste, Statusleiste und der üblichen Steuerelemente (Kontrollkästchen, Optionsfeld, Listenfeld, Textfeld, Schaltfläche etc.), sondern auch beliebiger im COM-Standard gehaltener ActiveX-Steuerelemente und -Komponenten. Wer unter Visual Basic ein neues Projekt anlegt und »Standard EXE« als Projekttyp auswählt, erhält den Rahmen für ein einfaches im Stand-alone-Betrieb ausführbares Programm, in dem sich als Vorgabe ein einzelnes Formularmodul mit dem Namen Form1 befindet. Aus Sicht der objektorientierten Programmierung stellt ein Formularmodul einen eigenständigen Objektdatentyp (bzw. Klasse) dar, der auf die Klasse Form aufsetzt und für den sich nach Belieben Instanzen ins Leben rufen sowie Objektvariablen vereinbaren lassen. Ist ein Formular (wie für den Projekttyp »Standard-EXE« bereits voreingestellt) im Dialogfeld PROJEKTEIGENSCHAFTEN auf der Seite ALLGEMEIN als Startobjekt für das Projekt angegeben, sorgt der Compiler dafür, dass beim Programmstart implizit eine Instanz dieses Formulars generiert wird, zur Anzeige kommt und die Kontrolle erhält (lies: initialisiert wird und den Fokus erhält). Ein neues Projekt des Typs »Standard EXE« lässt sich somit bereits ohne weiteres Zutun des Programmierers sofort kompilieren und stellt das Gerüst für ein vollständiges Programm im Sinne der Windows-Programmierung dar: ein Fenster (als visuelle Komponente des Formulars) mit Systemmenü, das sich vergrößern, verkleinern, verschieben und wieder schließen lässt. Im Programmiermodell von Windows ist ein Fenster mehr als nur eine Ausgabefläche. Ausgestattet mit einer Schnittstelle für den Empfang von Nachrichten, die das Fenster über die Aktivitäten des Benutzers (Mausereignisse, Tastatureingaben) sowie über sonstige Ereignisse (Initialisierungsphasen, Mitteilungen anderer Fenster) informieren, wird es zu einer Art Kom-
306
Form- Objekt (Basisobjekt für Formulare)
Form- Objekt (Basisobjekt für Formulare) Dim Formular As Form Beschreibung
................................................... Bes c hreibung
Der Datentyp Form ist Grundlage aller Formulare und stattet diese mit einer reichhaltigen Sammlung an Eigenschaften, Methoden und Ereignissen aus. Es lassen sich zwar Variablen dieses Datentyps vereinbaren, die Referenzen auf beliebige Formularobjekte (konkrete Instanzen von Formulardatentypen) enthalten können, das hinter dem Objektdatentyp selbst steckende Objekt lässt sich jedoch weder instanziieren noch ansprechen. Das liegt daran, dass es sich bei Form (ebenso wie bei MDIForm, UserControl, UserDocument, PropertyPage und ClassModul) um einen Schnittstellendatentyp (abstrakte Klasse) handelt, der als Vorlage für konkrete Klassen fungiert, jedoch nicht dafür gedacht ist, eigenständige Objekte hervorzubringen. Vielmehr tritt das Form-Objekt als Basisobjekt und somit als invarianter Kern konkreter Formulare auf, die nicht nur Objekte (nämlich Dialogfelder) hervorbringen können, weil sie eigenständige Datentypen bzw. Klassen darstellen, sondern auch in je eigenen Modulen organisiert sind. Da sich die Eigenschaften, Methoden des Form-Objekts auf die konkreten Formulardatentypen übertragen und das Form-Objekt dem jeweils umhüllenden konkreten Objekt Ereignisse signalisiert, die dieses behandeln kann, aber nicht muss, erhält man ein Aggregat, das landläufig als Formularobjekt bezeichnet wird – ohne dass sprachlich ein Unterschied zwischen dem Formulardatentyp und der konkreten Instanz des Formulardatentyps (dem »Formular«, das als Fens-
3 07
Formulare
mandozentrale für alle das Fenster als visuellen Bereich (gegebenenfalls mit Binnenstruktur) und als eigenständige operationale Einheit (Objekt mit untergeordneten Objekten) betreffenden Vorkommnisse. In der Dokumentation zu Visual Basic wird selten ein Unterschied zwischen einem Formularobjekt und dem dazugehörigen Fenster gemacht – eine Sicht, die in den meisten Fällen wohl ausreichend ist, doch vielfach auch Verständnisschwierigkeiten bereitet, was die wahre Natur des Formulars betrifft. Genau genommen ist ein Fenster nämlich ein vom Betriebssystem als Ressource zur Verfügung gestelltes Objekt mit visueller Repräsentation und der besagten Nachrichtenschnittstelle, während das Formularobjekt eine aufseiten von Visual Basic gelegene Instanz einer Klasse mit Eigenschaften, Methoden und Ereignissen ist, die das Fenster gewissermaßen umhüllt. Mithin sollte man also nicht sagen: »ein Formular ist ein Fenster«, sondern: »ein Formular hat ein Fenster«. (Deutlich wird der Unterschied, wenn man sich vor Augen hält, dass das Fenster eines Formularobjekts mittels Unload jederzeit abgebaut werden kann, während das Objekt weiter existiert und sich jederzeit über einen Load- bzw. Show-Aufruf ein neues Fenster organisieren kann.) Um das genannte Programmgerüst weiter auszugestalten, fügt man zum einen in den Entwurfsbereich des Formulars Steuerelemente ein, die eine geeignete Umsetzung der gewünschten spezifischen Ein-/Ausgabefunktionalität gestatten, und definiert zum anderen im Codefenster des Formulars geeignete Behandlungsroutinen für die verschiedenen von den Steuerelementen und dem Fenster signalisierten Ereignisse. Bei der Programmierung mit Formularen unterscheidet man zwischen SDI-Formularen (Single Document Interface) und MDI-Formularen (Multiple Document Interface). SDI-Formularen liegt der Datentyp Form zugrunde. Sie verfügen über eine reichhaltige Ausstattung an Eigenschaften und Methoden für die Grafik- und Textausgabe und können als Container für Steuerelemente und sonstige Komponenten fungieren, jedoch nicht für weitere Formulare. MDI-Formularen liegt hingegen der Datentyp MDIForm zugrunde. Sie kennen keine Grafikmethoden, erlauben nur die Platzierung von bestimmten Steuerelementen (nämlich solchen, die als Leisten definiert sind) und sind ansonsten speziell auf die Verwaltung und Darstellung untergeordneter SDI-Formulare im eigenen Client-Bereich eingerichtet.
Formulare
ter auf dem Bildschirm erscheint) gemacht wird. Dennoch sollte man sich immer darüber im Klaren sein, dass zu unterschiedlichen Formularen unterschiedliche Formulardatentypen gehören, die auf ein und dieselbe Standardschnittstelle Form aufbauen, und dass jeder Formulardatentyp beliebig viele Instanzen des gleichen Formulars hervorbringen kann, deren Bildschirmdarstellungen (Fenster) sich aber je nach Objektzustand erheblich voneinander unterscheiden können. Eigenschaften
Formulare
................................................... Eigens c ha ften ActiveControl, Appearance, AutoRedraw, BackColor, BorderStyle, Caption, ClipControls, ControlBox, Controls, Count, CurrentX, CurrentY, DrawMode, DrawStyle, DrawWidth, Enabled, FillColor, FillStyle, Font, FontBold, FontItalic, FontName, FontStrikethru, FontTransparent, FontUnderline, ForeColor, HasDC, hDC, Height, HelpContextID, hWnd, Icon, Image, KeyPreview, Left, LinkMode, LinkTopic, MaxButton, MDIChild, MinButton, MouseIcon, Mousepointer, Moveable, Name, OLEDropMode, Palette, PaletteMode, Picture,RightToLeft, ScaleHeight, ScaleLeft, ScaleMode, ScaleTop, ScaleWidth, ShowInTaskbar, StartupPosition, Tag, Top, Visible, WhatsThisButton, WhatsThisHelp, WhatsThisMode, Width, WindowState Die folgende Tabelle gibt einen Überblick über die nicht anderer Stelle vorgestellten Eigenschaften des Formulars: Eigenschaft
Beschreibung
ActiveControl
Zur Laufzeit schreibgeschützter Object-Verweis auf das Steuerelement des Formulars, das im Besitz des Fokus ist; der Wert ist Nothing, wenn kein Steuerelement, sondern das Formular den Fokus hat
AutoRedraw
Boolean-Wert, der bestimmt, ob das Formularobjekt den Client-Bereich automatisch neu zeichnet oder dazu eine Paint-Routine verwendet (vgl. »Paint-Ereignis«, S. 253)
ClipControls
Boolean-Wert, der bestimmt, ob Visual Basic für Steuerelemente Bildausschnitte bei der Restaurierung des Objektbereichs berechnet oder nicht
ControlBox
Zur Laufzeit schreibgeschützter Boolean-Wert, der bestimmt, ob das Formularfenster zur Laufzeit ein Systemmenü besitzt.
Controls
Schreibgeschützte Object-Auflistung, die alle Steuerelemente des Formulars als Elemente enthält. Beim Entwurf zuletzt eingefügte Steuerelemente kommen zuerst.
Count
Schreibgeschützter Integer-Wert, der die Anzahl der in der Controls-Auflistung des Formulars befindlichen Steuerelemente wiedergibt.
CurrentX CurrentY
Single-Werte, die die Startkoordinaten für die Zeichenoperationen Print, Circle, Pset, Line im aktuellen Koordinatensystem (vgl. ScaleMode) beschreiben und von diesen Methoden auch aktualisiert werden
KeyPreview
Boolean-Wert, der bestimmt, ob das Formular Tastaturereignisse, die seinen Steuerelementen gelten, gleichfalls behandeln kann (True) – und zwar vor diesen – oder nicht (False). Diese Eigenschaft ermöglicht beispielsweise die zentrale Tastatursteuerung auf Formularebene.
MaxButton MinButton
Boolean-Werte, die bestimmen, ob das Formularfenster die Schaltflächen MINIMIEREN, MAXIMIEREN und WIEDERHERSTELLEN anzeigt (True) oder nicht (False)
308
Form- Objekt (Basisobjekt für Formulare)
Beschreibung
MDIChild
Zur Laufzeit schreibgeschützter Boolean-Wert, der bestimmt, ob das Formular einem MDI-Formular untergeordnet ist (True) oder nicht (False)
Moveable
Boolean-Wert, der bestimmt, ob der Benutzer das Formular verschieben kann (True) oder nicht (False)
Palette
Picture-Wert, der auf ein Bild mit Palette verweist. Voreinstellung der Eigenschaft ist ein Verweis auf die Standardpalette des Systems, in der die unteren und oberen 20 Einträge mit den Systemfarben besetzt sind. Die Palette definiert einen indizierten Farbraum für die Darstellung von Bildern und Grafik in palettenorientierten Anzeigemodi (beispielsweise der Anzeigemodus »256-Farben«). Die Verwendung der Palette ist vom Wert der Eigenschaft PaletteMode abhängig
PaletteMode
Integer-Wert, der bestimmt, welche Palette das Formular (oder Benutzersteuerelement) für die Anzeige seiner Steuerelemente verwendet. Der Aufzählungsdatentyp PaletteModeConstants definiert Konstanten für die verschiedenen zur Auswahl stehenden Paletten: vbPaletteModeHalftone (0) Systempalette verwenden (Voreinstellung) vbPaletteModeUseZOrder (1) Palette des zuvorderst gezeichneten Steuerelements verwenden vbPaletteModeCustom (2)
Über Palette-Eigenschaft definierte Palette verwenden
vbPaletteModeContainer (3) Über Palette-Eigenschaft des Container-Objekts definierte Palette verwenden, sofern bekannt vbPaletteModeNone (4)
Keine Palette verwenden (betrifft nur Benutzersteuerelemente)
vbPaletteModeObject (5)
Palette des ActiveX-Designers verwenden. (Betrifft nur ActiveX-Designer, die eine Palette enthalten.)
ShowInTaskbar
Boolean-Wert, der bestimmt, ob das Formular in der Taskleiste erscheint (True) oder nicht (False)
StartupPosition
Integer-Wert, der die Position qualitativ beschreibt, an der das Formular unmittelbar nach dem Laden angezeigt wird. Der Aufzählungsdatentyp StartupPositionConstants definiert Konstanten für die verschiedenen zur Auswahl stehenden Positionen: vbStartupManual (0) Keine Vorgabe, Windows gibt die Position nach eigenen Kriterien vor vbStartupOwner (1)
Fenstermitte; das Formular erscheint zentriert im übergeordneten Fenster (MDIFormular)
vbStartUpScreen (2)
Bildschirmmitte; das Formular erscheint zentriert im Fenster des Screen-Objekts (Bildschirm)
vbStartUpWindowsDefault (3)
Das Formular erscheint in der linken oberen Ecke im Fenster des Screen-Objekts (Bildschirm)
3 09
Formulare
Eigenschaft
Formulare
Eigenschaft
Beschreibung
WhatsThisButton
Boolean-Wert, der bestimmt, ob das Formular die Schaltfläche HILFE in der Titelleiste erhält (True) oder nicht (False). Achtung: Damit die Anzeige erfolgen kann, müssen MinButton und MaxButton auf False gesetzt sein.
WindowState
Integer-Wert, der den Anzeigezustand des Formularfensters zur Laufzeit bestimmt – zur Auswahl stehen die Werte: vbNormal (0); vbMinimized (1); vbMaximized (2).
................................................... Methoden
Formulare
Methoden
Circle, Cls, Line, Hide, Move, OLEDrag, PaintPicture, Print, Point, PopupMenu, Pset, PrintForm, Refresh, Scale, ScaleX, ScaleY, SetFocus, Show, TextHeight, TextWidth, ValidateControls, WhatsThisMode, ZOrder Die folgende Tabelle gibt einen Überblick über die nicht an anderer Stelle vorgestellten Methoden des Formulars. Die Prototypen lauten: Sub Circle [Step] (x, y), r, [color As Long], [start, end], [ratio] Sub Cls() Sub Line [Step] (x1, y1) – [Step] (x2, y2), [color As Long], [B[F]] Sub Hide() Sub PaintPicture(Pic As Picture, x1, y1, Height1, Width1, x2, y2, _ Height2, Width2) Function Point(x, y) As Long Sub PopupMenu(Menu As Object, Flags, x, y, DefaultMenu) Sub Print Wert1 [,|;] Wert2 [,|;] ... Sub PrintForm() Sub PSet [Step] (x, y), [Color As Long] Sub Scale(x1, y1) – (x2, y2) Function ScaleX(Width, [FromScale], [ToScale]) As Single Function ScaleY(Height, [FromScale], [ToScale]) As Single Sub Show() Function TextHeight(Str As String) As Single Function TextWidth(Str As String) As Single Sub ValidateControls() Sub WhatsThisMode()
31 0
Form- Objekt (Basisobjekt für Formulare)
Kurzbeschreibung
Circle
Zeichnet einen Kreis um den Mittelpunkt (x, y) mit Radius Radius und der optionalen Stiftfarbe Color. Es gilt das aktuelle Koordinatensystem des Formulars. Sind die optionalen Parameter Start und End angegeben, zeichnet die Methode nur einen Teilkreis. Start und End bezeichnen den Startund Endwinkel (im Bogenmaß). Ist der Parameter Aspect mit einem Wert ungleich 1 bzw. -1 angegeben, zeichnet die Methode eine Ellipse, die dem Kreis einbeschrieben (positive Werte) bzw. umbeschrieben (negative Werte) ist. Aspect drückt das Verhältnis zwischen Höhe und Breite des begrenzenden Rechtecks aus.
Cls
Löscht den Client-Bereich, indem es diesen mit der Hintergrundfarbe BackColor füllt
Line
Zeichnet eine Linie zwischen den Punkten (x1, y1) und (x2, y2). Ist der optionale Zusatz »B« gegeben, zeichnet die Methode das Rechteck, das die beiden Punkte definieren. Ist der optionale Zusatz »BF« gegeben, zeichnet die Methode das Rechteck und füllt es mit der Füllfarbe FillColor aus.
Hide
Setzt die Visible-Eigenschaft auf False und macht das Formular unsichtbar
PaintPicture
Zeichnet das im Datentyp Picture vorliegende Bild Pic an der Position (x1, y1), die den linken oberen Bildpunkt beschreibt. Alle Werte sind in der aktuellen Maßeinheit des für das Formular geltenden Koordinatensystems (ScaleMode) anzugeben. Sind die optionalen Parameter Height1 und Width1 vorhanden, wird das Bild auf diese Maße skaliert, ansonsten errechnet sich die Methode diese Werte selbst und gibt das Bild in originaler Größe aus. Sind die optionalen Parameter x2 und y2 sowie Width2 und Height2 angegeben, beschreiben diese einen Bildausschnitt innerhalb des Bildes, der (nach impliziter Skalierung) in dem durch x1, y1, Width1 und Height1 spezifizierten Rechteck ausgegeben wird. (Achtung, die Eigenschaften Height und Width eines Picture-Objekts sind in der Maßeinheit »HiMetric« gehalten und müssen gegebenenfalls mittels ScaleX und ScaleY in die geltende Maßeinheit – Vorgabe ist Twips – umgewandelt werden.) Negative Werte für Height und/oder Width führen zu einer spiegelverkehrten und/oder um 180° gedrehten Ausgabe. Koordinaten für Ausschnitte sind auf den Ursprung des aktuellen Koordinatensystems zu beziehen!
Point
Liefert den Farbwert des Punkts mit den Koordinaten (x, y) als Long-Wert
PopupMenu
Zeigt das Popup-Menü Menu als Kontextmenü an der aktuellen Mausposition an. Der optionale Parameter Flags ermöglicht die Angabe eines Bitvektors, der die Position sowie das Verhalten des Menüs bestimmt. Für die einzelnen Bits sind folgende Konstanten definiert. vbPopupMenuLeftAlign (0) links an der Position (x, y) ausgerichtet (Voreinstellung) vbPopupMenuCenterAlign (4) mittig zur Position (x, y) ausgerichtet vbPopupMenuRightAlign (8)
rechts an der Position (x, y) ausgerichtet
vbPopupMenuLeftButton (0)
linke Maustaste aktiviert Menübefehl (Voreinstellung)
vbPopupMenuRightButton (2) rechte Maustaste aktiviert Menübefehl
31 1
Formulare
Methode
Formulare
Formulare
Methode
Kurzbeschreibung
PopupMenu (Forts.)
Die optionalen Parameter x und y ermöglichen die Angabe einer Position (x, y) im Koordinatensystem des Formulars, relativ zu der das Menü ausgerichtet wird. Fehlen beide Parameter, verwendet die Methode die aktuellen Mauskoordinaten. Über den optionalen Parameter DefaultMenu lässt sich schließlich der Bezeichner für den als Standard vorgeschlagenen Menübefehl spezifizieren. Der entsprechende Befehl erscheint dann im Menü fett.
Print
Gibt die in der Parameterliste befindlichen Werte in textueller Repräsentation an der Position (CurrentX, CurrentY) und mit den aktuellen Schrifteinstellungen (Font) aus. Als Trennzeichen für die Parameter sind das Semikolon und das Komma zulässig. Folgt ein Semikolon auf einen Parameter, wird der nächste Wert (ggf. auch im Rahmen der nächsten PrintAnweisung) ohne Zwischenraum dahinter gesetzt. Folgt ein Komma auf einen Parameter, wird der nächste Wert (ggf. auch im Rahmen der nächsten Print-Anweisung) an der nächsten Tabulatorposition ausgegeben. Print-Anweisungen, deren Parameterliste nicht mit einem Trennzeichen endet, bewirken einen Zeilenvorschub. Ein automatischer Zeilenumbruch findet ansonsten nicht statt.
PrintForm
Gibt die auf dem Formular befindlichen Steuerelemente samt Inhalte auf dem Standarddrucker aus. Direkt in den Client-Bereich des Formulars gezeichnete Ausgaben werden nicht ausgegeben, auch keine Hintergrundbilder (Picture).
PSet
Zeichnet einen Punkt an der Position (x, y) in der Farbe Color
Scale
Setzt ein Koordinatensystem für den Client-Bereich des Formulars, bei dem der linke obere Punkt die Koordinaten (x1, y1) und der rechte untere Punkt die Koordinaten (x2, y2) hat
ScaleX ScaleY
Rechnet die Koordinate Width für die X-Achse (bzw. Y-Achse) vom Koordinatensystem FromScale in das Koordinatensystem ToScale um und liefert die neue Koordinate als Single. Fehlt einer der optionalen Parameter für ein Koordinatensystem, bezieht sich die Methode auf das aktuell geltende Koordinatensystem. Für die verschiedenen Koordinatensysteme sind Integer-Konstanten definiert (vgl. »ScaleMode-Eigenschaft«, S. 369)
Show
Die Methode zeigt das Formular an, indem sie die Visible-Eigenschaft auf True setzt. Falls das Formular noch nicht geladen ist, die Formularvariable aber unter Angabe von New deklariert wurde, findet implizit ein Load-Aufruf statt.
TextHeight
Liefert die für die Ausgabe der Zeichenfolge Str benötigte Höhe in der aktuellen y-Maßeinheit des aktuellen Koordinatensystems
TextWidth
Liefert die für die Ausgabe der Zeichenfolge Str benötigte Breite in der aktuellen x-Maßeinheit des aktuellen Koordinatensystems
ValidateControls Sorgt dafür, dass die Inhalte aller Steuerelemente auf dem Formular gültig sind (Validate-Ereignis), bevor das Formular geschlossen wird WhatsThisMode
31 2
Aktiviert den Direkthilfemodus (Mauszeiger wird zu Pfeil mit Fragezeichen)
MDIForm- Objekt
Die Syntax für den Aufruf der Methoden Circle, Line und PSet ist traditionell spezifisch: Ist das optionale Schlüsselwort Step angegeben, wird das unmittelbar folgende Koordinatenpaar als relativer Offset bzgl. der aktuellen Position (CurrentX, CurrentY) interpretiert, sonst als absolute Position – jeweils bezogen auf das geltende Koordinatensystem. Standardmäßig zeichnen die Methoden in der durch die Eigenschaft ForeColor spezifizierten Farbe (Standardwert ist Schwarz: vbBlack). Beliebige Farben lassen sich aus ihren Komponentenanteilen über die Funktion RGB zu einem Long-Wert für den optionalen Parameter Color komponieren. Definierte Farben liefert die Funktion QBColor (16 Grundfarben) sowie der Aufzählungstyp ColorConstants. Füllfarbe ist FillColor, sofern FillStyle ungleich 1 ist. Anwendung
Die Möglichkeiten und Anlässe für die Anwendung von Formularobjekten sind so vielfältig und unerschöpflich, dass es wenig Sinn hat, sich darüber im Einzelnen auszulassen. Allein dieses Buch dürfte um die hundert unterschiedliche Konzeptionen für Formulare bzw. formularbasierte Programme vorstellen, und letztlich lässt sich fast der gesamte Text des Buches in dem Kontext »für den Einsatz auf/mit/in einem Formular« lesen. Mit dem Objektdatentyp Form verhält es sich anders. Dass es überhaupt möglich ist, Variablen dieses Typs zu vereinbaren und ihnen Formularinstanzen (wohlgemerkt: Instanzen, nicht Formulare) als Wert zuzuordnen, resultiert aus dem von Visual Basic verwendeten PolymorphieKonzept. Da die einzelnen Formulardatentypen eines Projekts nichts weiter als spezifische Implementationen ein und derselben der Form-Schnittstelle darstellen und somit dieselbe Grundstruktur besitzen, ist es zulässig, den gleichen Zeiger für Referenzen auf unterschiedliche Datentypen zu verwenden. Beispiel
................................................... Beis piel
Im folgenden Codeauszug vertritt eine Variable des Typs Form Instanzen zweier unterschiedlicher Formulare. Dim f As Form Dim erstesFormular As New Form1 ... Set f = erstesFormular ' Form1 wird implizit instanziiert! f.Show ' Instanz wird angezeigt ... Set f = New Form2 ' Form2 wird explizit instanziiert! f.Show ' Form2-Instanz wird angezeigt Verwandte Themen
................................................... Verwandte Them en
Vollbildanzeige (S. 536); DiaProjektor – SDI-Formulare synchronisieren (S. 511)
MDIForm- Objekt Dim mdiFormVar As MDIForm Dim mdiFormTypVar As MDIFormTyp Beschreibung
................................................... Bes c hreibung
Was die formale Natur des MDIForm-Objekts als Datentyp betrifft, so gilt das Gleiche wie für das Form-Objekt (vgl. dort, insbesondere die Klärung zur sprachlichen Verwendung des Wortes »MDI-Formularobjekt«).
31 3
Formulare
................................................... Anwendung
Formulare
Formulare
Im Gegensatz zu gewöhnlichen Formularobjekten sind MDI-Formularobjekte in der Lage, Formulare in ihrem Client-Bereich anzuzeigen. Zudem bieten sie einen gewissen Komfort für die Verwaltung untergeordneter Formulare. Methoden für eigene Grafikoperationen (die über ein Setzen der vorhandenen Picture-Eigenschaft hinausgehen) kennt das MDI-Formular jedoch nicht. MDI-Formulare werden daher meist als Grundlage für die Implementation von Anwendungen verwendet, die mit mehreren Dokumentenfenstern arbeiten. Damit ein gewöhnliches Formular einem MDI-Formular untergeordnet werden kann, muss seine MDIChild-Eigenschaft (bereits zur Entwurfszeit) auf True gesetzt werden. Neben Formularen lassen sich auf einem MDI-Formular nur solche Steuerelemente platzieren, die im engeren oder weiteren Sinne als Leiste definiert sind: StatusBar, ProgressBar, Data, RemoteData, Adodc, ToolBar und PictureBox (erscheint als Leiste in voller Breite des Client-Bereichs). Darüber hinaus kann ein MDI-Formular ein Menü besitzen und Kontextmenüs zur Anzeige bringen. Eigenschaften
................................................... Eigens c ha ften ActiveControl, ActiveForm, Appearance, AutoShowChildren, BackColor, Caption, Controls, Count, Enabled, Height, HelpContextID, hWnd, Icon, Left, LinkMode, LinkTopic, MouseIcon, Mousepointer, Moveable, Name, NegotiateToolsbars, OLEDropMode, Picture,RightToLeft, ScaleHeight, ScaleWidth, ScrollBars, StartupPosition, Tag, Top, Visible, Width, WindowState Die folgende Tabelle gibt einen Überblick über die nicht anderer Stelle vorgestellten Eigenschaften des MDI-Formulars (vgl. insbesondere: Form-Objekt): Eigenschaft
Beschreibung
ActiveForm
Verweis auf das untergeordnete Formular, das im Besitz des Fokus ist (Datentyp entspricht dem Objektdatentyp des jeweiligen Formulars)
AutoShowChildren
Boolean-Wert, der bestimmt, ob nach dem Load-Aufruf für ein untergeordnetes Formular implizit ein Show-Aufruf erfolgt (True; Voreinstellung) oder nicht (False)
Controls
Schreibgeschützte Object-Auflistung, die alle Steuerelemente (nicht Formulare!) des MDI-Formulars als Elemente enthält. Beim Entwurf zuletzt eingefügte Steuerelemente kommen zuerst. Beachten Sie, dass sich nicht alle Arten von Steuerelementen in ein MDI-Formular einfügen lassen.
Count
Schreibgeschützter Integer-Wert, der die Anzahl der in der ControlsAuflistung für das Formular geführten Steuerelemente (nicht Formulare!) wiedergibt.
NegotiateToolbars
Boolean-Wert, der bestimmt, ob die Symbolleisten eines untergeordneten Objekts im MDI-Formular angezeigt werden, solange das Objekt aktiv ist
ScrollBars
Boolean-Wert, der bestimmt, ob das MDI-Formular automatisch Bildlaufleisten anzeigt, wenn der Client-Bereich für die vollständige Anzeige eines untergeordneten Formulars nicht ausreicht
Methoden
................................................... Metho den Arrange, Hide, Move, OLEDrag, PopupMenu, SetFocus, Show, ValidateControls, WhatsThisMode, ZOrder
31 4
MDIForm- Objekt
Die einzige nicht an anderer Stelle vorgestellte Methode des MDI-Formulars ist Arrange. Ihr Prototyp lautet: Sub Arrange (Arrange As FormArrangeConstants) Für den Aufruf der Methode definiert der Aufzählungstyp FormArrangeConstants drei Konstanten: Beschreibung
vbCascade (0)
Die Methode ordnet alle dem MDI-Formular untergeordneten sichtbaren und nicht minimierten Formulare als Kaskade an – das heißt: überlappend, mit zunehmender Verschiebung von links oben nach rechts unten, so dass von hinteren Fenstern die Titelleisten lesbar bleiben.
vbTileHorizontal (1)
Die Methode ordnet alle dem MDI-Formular untergeordneten sichtbaren und nicht minimierten Formulare in einer Reihe untereinander an, so dass sie den Client-Bereich der Breite und der Höhe nach vollständig ausfüllen.
vbTileVertical (2)
Die Methode ordnet alle dem MDI-Formular untergeordneten sichtbaren und nicht minimierten Formulare in einer Reihe nebeneinander an, so dass sie den Client-Bereich der Breite und der Höhe nach vollständig ausfüllen.
Anwendung
................................................... Anwendung
Wer MDI von anderen Programmiersprachen her kennt, wird die von Visual Basic vorgestellte MDI-Konzeption als denkbar einfach empfinden: Man füge dem Projekt ein neues Modul des Typs »MDI-Formular« hinzu (wobei je Projekt maximal ein MDI-Formular zulässig ist), das die Implementation für das MDI-Formular enthalten wird, und füge dem Projekt weiterhin für jeden dem MDI-Formular untergeordneten Formulartyp ein gewöhnliches Formularmodul hinzu, in dem die Eigenschaft MDIChild auf True gesetzt ist. Das war bereits alles; den Rest erledigt Visual Basic für Sie. Ist ein untergeordnetes Formular im Dialog PROJEKTEIGENSCHAFTEN als Startobjekt festgelegt, lädt Visual Basic das übergeordnete MDI-Formular automatisch noch vor dem untergeordneten Formular und zeigt es dann im Client-Bereich des MDI-Formulars an. Ist dagegen das MDI-Formular als Startobjekt definiert, kommt dieses mit leerem ClientBereich zur Anzeige und kann sich (beispielsweise im Rahmen seiner Load-Behandlung) selbst um die Anzeige untergeordneter Formulare kümmern. Die Ereignisreihenfolge für ein als Startobjekt festgelegtes MDI-Formular sieht so aus: Initialize Load Resize Activate ... QueryLoad Unload Terminate
Beim Entladen eines MDI-Formulars trifft das QueryUnload-Ereignis zuerst beim MDI-Formular und dann der Reihe nach bei den untergeordneten Formularen ein. Falls nur ein einziges Formular die Cancel-Eigenschaft auf True setzt, unterbricht das den Entladevorgang für alle Formulare. Ansonsten folgt das Unload-Ereignis, das diesmal zuerst aber alle untergeordneten
31 5
Formulare
Konstante
Formulare
Formulare
Formulare erreicht und erst zum Schluss das MDI-Formular. Falls ein untergeordnetes Formular bei der Behandlung des Unload-Ereignisses die Cancel-Eigenschaft auf True setzt, unterbricht das den Entladevorgang für alle folgenden Formulare (und damit auch für das MDI-Formular). Die Ereignisreihenfolge für ein automatisch über ein untergeordnetes Formular geladenes (und später wieder geschlossenes) MDI-Formular sieht damit so aus: Form.Initialize MDIForm.Initialize MDIForm.Load Form.Load MDIForm.Resize Form.Resize MDIForm.Activate Form.Activate Form.GotFocus ... MDIForm.QueryLoad Form.QueryLoad Form.Unload MDIForm.Unload Form.Terminate MDIForm.Terminate
Untergeordnete Formulare sind im Client-Bereich des MDI-Formulars eingesperrt, lassen sich darin aber bequem mittels Arrange auf drei verschiedene Arten anordnen. Automatisch vom MDIForm-Objekt gepflegte Bildlaufleisten sorgen dafür (ScrollBars-Eigenschaft), dass der Benutzer auch mit Formularen vernünftig arbeiten kann, die nicht vollständig in den Client-Bereich passen. Ein wichtiges Mittel für die Arbeit mit untergeordneten Formularen ist die Eigenschaft ActiveControl. Sie ermöglicht es, Operationen immer auf das Formular zu beziehen, das im Besitz des Fokus ist, ohne eigens darüber Buch führen zu müssen, welches Formular dies gerade ist. Um beispielsweise den Namen des aktiven Steuerelements im aktiven Formular zu ermitteln, schreiben Sie: sName = ActiveForm.ActiveControl.Name
Wie das Beispiel zu diesem Abschnitt zeigt, kann es einem diese Eigenschaft sogar ersparen, Objektvariablen auf geladene untergeordnete Variablen zu halten. Es versteht sich, dass die Eigenschaft nur dann einen von Nothing unterschiedlichen Wert aufweist, wenn es ein aktives untergeordnetes Formular gibt. Da ein Formular weder über eine Container-Eigenschaft noch über eine Parent-Eigenschaft verfügt, haben untergeordnete Formulare umgekehrt jedoch keine Information über das MDI-Formular, dem sie angehören. Beispiel
................................................... Beis piel
Mit dem Projekt MDIForm liegt die einfache Implementation eines MDI-Formulars vor, das beliebig viele Bilder in untergeordneten Fenstern anzeigen kann. Das Formular verfügt über eine Menüleiste mit den beiden Menüs DATEI und ANORDNEN. Ersteres enthält die Kommandos ÖFFNEN, um ein weiteres Bild in ein neues Fenster zu laden, SCHLIEßEN, um das aktive untergeordnete Fenster zu schließen, und BEENDEN, um das MDI-Formular samt aller untergeordneter Fenster zu schließen und das Programm zu beenden. Die Routine mnuDateiÖffnen_Click für die Behandlung von ÖFFNEN speichert das neue Formularobjekt in einer lokalen Variablen und demonstriert so recht schön, dass das Formularobjekt erhalten
31 6
MDIForm- Objekt
bleibt, obwohl im Programm keine Referenz mehr darauf existiert – allerdings hält das MDIFormular implizit noch eine Referenz darauf, nicht zuletzt um im Zuge des Unload-Aufrufs im Rahmen der Behandlung von BEENDEN automatisch alle untergeordneten Formulare entladen zu können. Die ebenso elegante wie undurchsichtige Implementation des zweiten Menüs ANORDNEN profitiert davon, dass die Menüeinträge als Array vereinbart sind. Sie nutzt den Parameter Index der Behandlungsroutine schlicht als Argument für Arrange. Klickt der Benutzer in den Client-Bereich des MDI-Formulars, wird dieses Menü auch als Kontextmenü angezeigt.
Formulare
' Modul MDIForm1: Formular zeigt Bilder in untergeordneten Formularen an Private Sub MDIForm_Load() mnuDateiÖffnen_Click ' Erstes Fenster öffnen End Sub Private Sub MDIForm_Click() PopupMenu mnuAnordnen End Sub Private Sub mnuDateiBeenden_Click() Unload Me ' Programm beenden End Sub Private Sub mnuDateiÖffnen_Click() ' Nächstes Fenster öffnen Dim SDIForm As New Form1 CommonDialog1.Filter = "Bilder|*.jpg;*.gif;*.bmp;*.wmf;*.emf;*.ico;*.cur" CommonDialog1.ShowOpen If CommonDialog1.filename <> "" Then SDIForm.ShowPic (CommonDialog1.filename) End If End Sub Private Sub mnuDateiSchließen_Click() Unload ActiveForm ' Fenster schließen End Sub Private Sub mnuOrdnen_Click(Index As Integer) Arrange (Index) ' Fenster anordnen End Sub
Das Formularmodul, das den Objektdatentyp für die untergeordneten Formulare definiert, ist recht geradlinig implementiert. Die Routine ShowPic lädt das Bild und baut ansonsten auf die Resize-Routine, die das Bild in geeigneter Skalierung zentriert mit Hilfe von PaintPicture in den Client-Bereich des Formulars zeichnet. ' Modul Form1: Formular lädt Bild und zeigt es in passender Größe an Private Bild As Picture Private Sub Form_Resize() Dim w, h ' Bild geeignet zentrieren w = Bild.Width: h = Bild.Height If w < Width Or w > ScaleWidth Then
31 7
Selbst definierte Klassen
Selbst definierte Klassen
w = ScaleWidth h = h * w / Bild.Width End If If h > ScaleHeight Then h = ScaleHeight w = Bild.Width * h / Bild.Height End If Cls PaintPicture Bild, (ScaleWidth – w) / 2, (ScaleHeight – h) / 2, w, h End Sub Public Sub ShowPic(filename As String) On Error GoTo Fehler Set Bild = LoadPicture(filename) ' Bild Laden On Error GoTo 0 Form_Resize ' Bild zeichnen Caption = Mid(filename, InStrRev(filename, "\") + 1) Exit Sub Fehler: Print "Dateiformat nicht erkannt" End Sub
Selbst definierte Klassen Wer schon das eine oder andere Formular oder gar Benutzersteuerelement implementiert hat, steckt bereits tiefer in der objektorientierten Programmierung, als er vermutet – und hat vom Prinzip her eigentlich schon das Wesentliche begriffen, was man für die Implementierung eigener Klassen benötigt. Jedes Formular oder Benutzersteuerelement verkörpert in Visual Basic nämlich einen eigenständigen Datentyp bzw. eine eigene Klasse, so dass die Programmiertechnik für den Umgang mit Formularen und Steuerelementen fundamental die gleiche ist wie für den Umgang mit Objekten selbst definierter Klassen – und auch die Implementation unterliegt den gleichen Gesetzmäßigkeiten und Prinzipien. Ja, selbst definierte Klassen sind sogar um einiges »pflegeleichter« als Formulare und Benutzersteuerelemente, da sie zunächst einmal weder eine visuelle Repräsentation in Form eines Fensters besitzen noch auf eine vergleichbar komplexe Schnittstelle aufsetzen. Was die Form-Schnittstelle für das Formular und die UserControl-Schnittstelle für das Benutzersteuerelement, das ist die ClassModule-Schnittstelle für eine selbst definierte Klasse. Sie stattet eine Instanz der selbst definierten Klasse mit dem Nötigsten aus, was ein Objekt benötigt: Konstruktor und Destruktor. Beide lassen sich als Ereignisbehandlungsroutinen implementieren und fügen sich somit nahtlos in das ereignisorientierte Programmiermodell ein. Definition Für die Definition einer eigenen Klasse fügen Sie dem Projekt über das Menü PROJEKT ein Klassenmodul hinzu und legen im Eigenschaftsfenster den Namen der Klasse fest. Die Implementation nehmen Sie dann wie gewohnt im Codefenster vor. Der erste Schritt bei der Implementation einer neuen Klasse wird die Definition der Datenstruktur sein, die ein Objekt der Klasse verkörpern soll – mit anderen Worten: die Deklaration der Datenelemente der Klasse. (Wie im Abschnitt »Klassen als Datentypen für Objektvariablen«, S. 196, näher ausgeführt, gibt es hier eine Innensicht und eine Außensicht.) Der nächste Schritt ist die Initialisierung der Datenelemente. Sie erfolgt im Konstruktor der Klasse, also im Rahmen der Behandlungsroutine für das Initialize-Ereignis. Im dritten Schritt implementieren Sie die Eigenschaften der Klasse in Form
31 8
Selbst definierte Klassen
1. Aktivieren Sie das Codefenster der Klasse und öffnen Sie das Dialogfeld PROZEDURATTRIBUTE über das Menü EXTRAS/PROZEDURATTRIBUTE. 2. Wählen Sie im Kombinationsfeld NAME den Bezeichner des Elements, das Sie als Standardelement vereinbaren wollen, und zeigen Sie über die Schaltfläche WEITERE die detaillierte Form des Dialogfelds an. 3. Wählen Sie im Kombinationsfeld PROZEDUR-ID den Eintrag (Voreinstellung) oder tragen Sie den Wert 0 als ID ein.
Die Eigenschaft Betrag als Standardeigenschaft festlegen Von nun an können Sie auf das Standardelement (hier: Betrag) referieren, indem Sie schlicht den Objektbezeichner notieren – und zwar als Rechtswert oder Linkswert, vorausgesetzt, die entsprechenden Methoden (meist: Property Set/Let und/oder Property Get) sind vorhanden. Schnittstellen implementieren Für die fortgeschrittene Programmierung mit Klassen besteht weiterhin die Möglichkeit, eine Klasse durch Verwendung des Schlüsselworts Implements als Implementation einer bestimmten Schnittstelle zu deklarieren. Bei der Schnittstelle kann es sich um eine Standardschnittstelle handeln, aber auch um eine selbst definierte sog. abstrakte Klasse, die Methoden als Rümpfe deklariert, ohne eine Implementation dafür bereitzustellen. Letzteres ist dann Aufgabe der Klasse, die die Schnittstelle implementiert – sie muss für jede in der Schnittstelle deklarierte Methode eine Implementation bereitstellen.
31 9
Selbst definierte Klassen
von Property-Methoden (vgl. »Eigenschaften«; S. 201) und die sonstigen Methoden der Klasse. Im letzten Schritt müssen Sie gegebenenfalls noch für den Abbau aller durch ein Objekt dieser Klasse angeforderter Ressourcen sorgen, die Visual Basic nicht von sich aus mit dem Objekt zusammen abbaut – so beispielsweise: Formularinstanzen. Den Code dafür setzen Sie in die Behandlungsroutine für das Terminate-Ereignis. Standardeigenschaft Von der Programmierung mit Steuerelementen her sind Sie es sicher gewohnt: Ein Objekt kann ein so genanntes Standardelement (Standardeigenschaft) besitzen, das heißt eine Eigenschaft oder eine Methode, deren Wert bzw. Aufruf sich durch alleinige Notation des Objektbezeichners (also unter Weglassung des zugehörigen Elementbezeichners) ergibt. Um eine Methode (eine Eigenschaft ist ja nichts anderes als eine Property Get-Methode) als Standardelement zu vereinbaren, gehen Sie wie folgt vor:
Selbst definierte Klassen
Eine ausführlichere Diskussion dieses Konzepts führt jedoch sehr schnell in die komplexe Welt des COM und der Programmierung von und mit ActiveX-Komponenten. Beispiel
................................................... Beis piel
Ein Beispiel für die Implementation einer einfachen selbst definierten Klasse finden Sie im Abschnitt »Eigenschaften« (S. 201).
Selbst definierte Klassen
ClassModul- Schnittstelle Bei der ClassModule-Schnittstelle handelt es sich um eine rudimentäre Schnittstelle, die eine selbst definierte Klasse mit den beiden Ereignissen Initialize und Terminate sowie den Eigenschaften DatabindingBehavior und DataSourceBehavior ausstattet. Weitere »Erblasten« gibt es zunächst einmal nicht. Anwendung
................................................... Anwendung
Im Gegensatz zu Schnittstellen wie Form oder UserControl steht ClassModule nicht als Objektdatentyp für Objektvariablen zur Verfügung. Wer eine generische Objektvariable benötigt (und nichts anderes wäre eine Variable des Typs ClassModul) muss also mit dem Datentyp Object vorlieb nehmen. Das Initialize-Ereignis tritt für jedes Objekt einer gegebenen selbst definierten Klasse ein einziges Mal auf, nämlich bei der Instanziierung. Eine Behandlung dieses Ereignisses gibt dem Objekt Gelegenheit, seine Elemente zu initialisieren, Ressourcen anzufordern usw., kurz gesagt: sich zu konstruieren. Da die Datenstruktur eines Objekts seinerseits Elemente mit Objektdatentypen enthalten kann, stellt sich die Frage nach der Reihenfolge der Initialize-Ereignisse. Die Antwort lautet: Visual Basic instanziiert Objekte top-down, also zuerst das Objekt und dann seine Elemente. Das ist nicht verwunderlich, da Visual Basic Objekte generell erst dann konstruiert, wenn es (implizit oder explizit) eine New-Anweisung ausführt. Das Terminate-Ereignis ist das Gegenstück zu Initialize und tritt gleichfalls nur einziges Mal auf, nämlich beim Ableben des Objekts. Es gibt dem Objekt umgekehrt Gelegenheit, angeforderte Ressourcen wieder freizugeben. Ein expliziter Abbau von Elementen mit Objektdatentyp ist allerdings nicht erforderlich, da Visual Basic diese automatisch abbaut. Die Reihenfolge des Abbaus ist gleichfalls top-down, das heißt, die Elemente eines Objekts werden nach dem Objekt abgebaut (was natürlich sinnvoll ist, denn das Objekt sollte bei der Behandlung von Terminate noch über alle Elemente verfügen). Die nur zur Entwurfszeit verfügbaren Eigenschaften DatabindingBehavior und DataSourceBehavior legen fest, ob (und wenn ja wie) das Objekt an eine Datenquelle gebunden werden kann bzw. ob es selbst als Datenquelle für andere Objekte auftreten kann. Sie sind nur von Interesse, wenn Sie die Klasse für die Implementation von Datenbankverbindungen verwenden.
320
Steuerelemente Was das Lenkrad, der Tacho, die Instrumente, Schalter, Regler, Hebel und Pedale für ein Auto oder ein Flugzeug, das sind die Steuerelemente für ein Programm. Steuerelemente sind nach ergonomischen Gesichtspunkten gestaltete Ein- und Ausgabemedien für die Interaktion zwischen Benutzer und Programm. Dabei vereint jedes Steuerelement (genauer: die meisten Steuerelemente) in sich eine visuelle Repräsentation seiner Zustände sowie eine operationale Repräsentation der mit diesen Zuständen verbundenen Befehle und Aktionen. Gut 80 Prozent der Programmierung mit Visual Basic hat etwas mit Steuerelementen zu tun. Das ist kein Wunder, denn das Erfolgsrezept von Visual Basic war seit jeher der unkomplizierte Umgang mit Steuerelementen sowie die Möglichkeit, in kürzester Zeit selbst komplexeste Formulare nicht nur zusammenstellen, sondern auch ans Laufen bringen zu können. Während der visuell orientierte Entwurf von Benutzeroberflächen sehr schnell auch in die Entwicklungsumgebungen anderer Programmiersprachen Einzug gehalten hat, ist die mit Visual Basic vorliegende Integration der Codeentwicklung in die interaktive visuelle Gestaltung der Benutzeroberfläche nach wie vor außerordentlich. Das liegt daran, dass auch für den gesamten modularen Aufbau eines Programms bzw. einer Komponente das gleiche Baukastenprinzip zur Anwendung kommt: Der Entwickler zieht ein Steuerelement bzw. eine Komponente in den Entwurfsbereich eines Formulars (bzw. der zu entwickelnden Komponente) und eröffnet sich damit den gesamten Funktionsumfang des dahinter steckenden Bibliotheksmoduls. Eine zum Modul gehörige und von der Entwicklungsumgebung automatisch konsultierte Typbibliothek gibt zudem Auskunft über die Eigenschaften, Methoden und Ereignisse, die für das Steuerelement definiert sind. (Rufen Sie doch einmal mit (F2) den Objektkatalog auf.) Ein Steuerelement muss nicht unbedingt als sichtbares Element der Benutzeroberfläche mit eigenem Fensterbereich in Erscheinung treten. Visual Basic fasst den Begriff des Steuerelements wesentlich weiter. Hat man erst einmal verstanden, dass in Visual Basic (fast) alles, was von »außen« kommt, als Steuerelement auf den Entwurfsbereich eines Formulars oder einer Komponente platziert wird, lebt es sich leicht: »Sage mir, was Du machen willst, und ich besorge Dir ein Steuerelement dafür«. Die Sparte VB-Steuerelemente ist inzwischen zu einem Boombereich auf dem Softwaremarkt geworden, und frei nach dem Motto »es gibt nichts, was es nicht gibt«, reduziert sich so manches Problem durch Einsatz einer fertigen kommerziellen Komponente zu einer trivialen Angelegenheit. Umgekehrt steht einem Programmierer aber auch nichts im Wege, sich seine eigenen Bibliotheken als Benutzersteuerelemente (ActiveX-Steuerelemente oder ActiveX-Komponenten) zusammenzustellen und sich gegebenenfalls sogar seine Nische in dem breiten Markt zu erobern. In der Dokumentation wird selten zwischen einem Steuerelement als Fenster, das einem anderen Fenster (etwa dem eines Formulars) untergeordnet ist, und einem Steuerelement als Objekt, das die operationale Repräsentation dieses Fensters innerhalb eines Programms vornimmt, unterschieden. Gerade für Anfänger im Bereich der Windows-Programmierung ist diese Unterscheidung für ein besseres Verständnis aber sehr wichtig, weil sie die über eine Schnittstelle vermittelte Dualität sichtbares GDI-Objekt, als Fensterobjekt einer bei Windows registrierten Fensterklasse, und operatives Visual-Basic-Objekt, das auf Nachrichten des Fensterobjekts reagiert und bemüht ist, seinen Zustand mit dem des Fensterobjekts zu synchronisieren, unterstreicht. Mit diesem Wissen im Hinterkopf erklärt sich auch, warum ein Formular (und alle darauf befindlichen Steuerelemente) geladen und wieder entladen werden kann, obwohl das Formularobjekt (respektive die Steuerelementobjekte) erhalten bleibt. Einen kleinen Nachteil hatte der visuelle Ansatz, bevor die Version 6.0 eine Lösung anbot: Im Gegensatz zu anderen Programmiersprachen war es Visual Basic bisher nicht so ohne Weiteres möglich, ein Steuerelement zur Laufzeit zu generieren, das nicht bereits zur Entwurfszeit wenigstens in einer Instanz als »Samen« auf dem jeweiligen Formular platziert worden war. Darüber hinaus mussten dynamisch generierte Steuerelementinstanzen in Steuerelemente-
3 21
Selbst definierte Klassen
Selbst definierte Klassen
Arrays verwaltet werden, was die Bezeichnerwahl natürlich einschränkte. Die Version 6.0 behebt dieses Manko nun mit Mitteln der Automatisierung, indem sie die Möglichkeit bietet, der Controls-Auflistung eines Formulars über die Methode Add dynamisch neue, noch nicht als »Samen« existierenden Steuerelemente hinzuzufügen – um den Preis einer späten Bindung zur Laufzeit. Mit der Version 4.0 hat Visual Basic nicht nur den Sprung in den 32-Bit-Welt vorgenommen, sondern sich auch konzeptuell dem COM (Component Object Model) verschrieben. Steuerelemente sind seither Komponenten im Sinne des COM. Traditionell gibt es die Unterscheidung zwischen den Standardsteuerelementen, die den Kern der von Windows unterstützten Steuerelemente ausmachen, in der grundlegenden Werkzeugsammlung von Visual Basic zu finden sind und sich ohne weiteres Zutun sofort einsetzen lassen, und den ActiveX-Steuerelementen, die über den Menübefehl PROJEKT/KOMPONENTEN ((Strg)+(T)) bei Bedarf explizit in die Werkzeugsammlung übernommen werden müssen. Die ActiveX-Steuerelemente zerfallen wiederum in verschiedene Gruppen, die aber im Allgemeinen die Bibliothekszugehörigkeit widerspiegeln. Die wichtigsten darunter sind die Standarddialoge (COMDLG32.OCX) und die WindowsStandardsteuerelemente (MSCOMCTL.OCX, MSCOMCT2.OCX, MSCOM332.OCX). Die folgende Tabelle gibt einen Überblick, in welchem OCX-Modul welches Steuerelement zu finden ist: Steuerelement
Komponentenname
Dateiname
Adodc (ADO DatenSteuerelement)
Microsoft ADO Data Control 6.0
MSADODC.OCX
MSChart
Microsoft Chart Control 5.5
MSCHART.OCX
MSComm
Microsoft Comm Control 6.0
MSCOMM32.OCX
CommonDialog
Microsoft Common Dialog Control 6.0
COMDLG32.OCX
DBGrid
Microsoft Data Bound Grid Control 5.0
DBGRID32.OCX
DBCombo, DBList
Microsoft Data Bound List Controls 6.0
DBLIST32.OCX
DataRepeater
Microsoft Data Repeater Control 6.0
MSDATREP.OCX
DataGrid
Microsoft Data Grid Control 6.0
MSDATGRD.OCX
DataList, DataCombo
Microsoft Data List Controls 6.0
MSDATLST.OCX
MSFlexGrid
Microsoft FlexGrid Control 6.0
MSFLXGRD.OCX
Grid
Microsoft Grid Control
GRID32.OCX
MSHFlexGrid
Microsoft Hierarchical Flex Grid Control 6.0
MSHFLXGD.OCX
Inet (Internet Trans- Microsoft Internet Transfer Control 6.0 fer-Steuerelement)
MSINET.OCX
MAPIMessages, MAPISession
Microsoft MAPI Controls 6.0
MSMAPI32.OCX
MaskEdBox
Microsoft MaskedEdit Control 6.0
MSMASK32.OCX
MMControl
Microsoft Multimedia Control 6.0
MCI32.OCX
PictureClip
Microsoft PictureClip Control 6.0
PICCLP32.OCX
RemoteData
Microsoft RemoteData Control 6.0
MSRDC20.OCX
RichTextBox
Microsoft RichTextBox Control 6.0
RICHTX32.OCX
Komponentenzugehörigkeit der ActiveX- Steuerelemente
322
Gemeinsame Eigenschaften
Komponentenname
Dateiname
SysInfo
Microsoft SysInfo Control 6.0
SYSINFO.OCX
Tab (Microsoft TabSteuerelement)
Microsoft TabbedDialog Control 6.0
TABCTL32.OCX
ImageCombo, ImageList, ListView, ProgressBar, Slider, StatusBar, TabStrip, Toolbar, TreeView
Microsoft Windows Common Controls 6.0
MSCOMCTL.OCX (in der Version 5.0: COMCTL32.OCX)
Animation, DTPicker, FlatScrollbar, MonthView, UpDown,
Microsoft Windows Common Controls-2 6.0
MSCOMCT2.OCX (in der Version 5.0: COMCT232.OCX)
CoolBar
Microsoft Windows Common Controls-3 6.0
COMCT332.OCX
WinSock
Microsoft Winsock Control 6.0
MSWINSCK.OCX
Komponentenzugehörigkeit der ActiveX- Steuerelemente
Gemeinsame Eigenschaften Da die Standardsteuerelemente und ActiveX-Steuerelemente viele Eigenschaften mit mehr oder weniger gleicher Bedeutung – teilweise aber mit unterschiedlichen Wertebereichen – besitzen, folgt zunächst einmal eine Besprechung der wichtigsten Eigenschaften und Methoden, bevor es dann mit den einzelnen Steuerelementtypen weitergeht. Die folgende Tabelle gibt einen Überblick über die Eigenschaften, die bei der Besprechung der einzelnen Steuerelemente sowie des Formularobjekts nicht eigens diskutiert werden. Eigenschaft
Beschreibung
Alignment
Integer-Wert, der die Ausrichtung des Werts oder der Beschriftung des Steuerelements im Fensterbereich ausdrückt
Appearance
Integer-Wert, der die Darstellungsart des Steuerelements ausdrückt (2D oder 3D)
AutoSize
Boolean-Wert, der bestimmt, ob das Steuerelement seine Bereichsabmessungen automatisch anpasst, um den Standardwert vollständig darstellen zu können
BackColor
Long-Wert, der die Hintergrundfarbe des Steuerelements ausdrückt
BackStyle
Integer-Wert, der ausdrückt, ob der Hintergrund des Steuerelements durchscheint oder nicht
BorderStyle
Integer-Wert, der den Stil der Randlinie des Steuerelements ausdrückt
Caption
String-Wert für die Beschriftung des Steuerelements
Container
Objektverweis auf das Containerobjekt des Steuerelements
Die wichtigsten gemeinsamen Eigenschaften der Steuerelemente
323
Gemeinsame Eigenschaften
Steuerelement
Gemeinsame Eigenschaften
Gemeinsame Eigenschaften
Eigenschaft
Beschreibung
CausesValidation
Boolean-Wert, der ausdrückt, ob ein Validate-Ereignis bei Fokusverlust an ein anderes Steuerelement (mit auf True gesetzter CausesValidation-Eigenschaft) auftritt
DataChanged
Boolean-Wert, der ausdrückt, ob sich der Wert des Steuerelements gegenüber dem Datensatz, an den es gebunden ist, geändert hat.
DataField
String-Wert für den Namen des Datenfelds, wenn das Steuerelement an ein bestimmtes Datenfeld im aktuellen Datensatz gebunden ist
DataFormat
DataFormat-Objekt, das die Schnittstelle IStdDataFormatDisp implementiert und für eine formatierte Darstellung des Steuerelementwerts sorgt
DataMember
String-Wert für den Namen des Datenelements in einer Datenquelle, wenn das Steuerelement an eine solche gebunden ist
DataSource
String-Wert für den Namen der Datenquelle, wenn das Steuerelement an eine solche gebunden ist
DisabledPicture
Picture-Objekt mit Verweis auf Bitmap für die benutzerdefinierte Darstellung des inaktiven Steuerelements
DownPicture
Picture-Objekt mit Verweis auf Bitmap für die benutzerdefinierte Darstellung des Steuerelements im angeklickten Zustand
DragIcon
Picture-Objekt mit Verweis auf benutzerdefinierten Mauszeiger für die Anzeige während Drag&Drop-Operationen
DragMode
Integer-Wert, der das Verhalten des Steuerelements bei Drag&DropOperationen bestimmt
DrawMode
Integer-Wert, der den Zeichenmodus für Zeichenoperationen sowie für das Zeichnen von Figur- und Liniensteuerelementen festlegt
DrawStyle
Integer-Wert, der den Linienstil für Grafikmethoden bestimmt
FillColor
Long-Wert, der die Füllfarbe für die Darstellung nichttransparenter Füllbereiche des Steuerelements ausdrückt
FillStyle
Integer-Wert, der den Füllstil für die Darstellung nichttransparenter Füllbereiche des Steuerelements ausdrückt
Font
Font-Objekt, das die Schrift für die Darstellung textorientierter Werte definiert
FontBold, FontIta- Boolean-Werte für zusätzliche Schriftattribute bei der Darstellung textorientierter Werte lic, FontStrikethru, FontUnderline FontName
String-Wert, der den Namen der aktuell eingestellten Schrift bestimmt
FontSize
Single-Wert, der die Schriftgröße der aktuell eingestellten Schrift bestimmt
ForeColor
Long-Wert, der die Farbe für das Zeichnen von Vordergrundanteilen des Steuerelements ausdrückt
Die wichtigsten gemeinsamen Eigenschaften der Steuerelemente
324
Gemeinsame Eigenschaften
Beschreibung
HasDC
Schreibgeschützter Boolean-Wert, der angibt, ob das Formular oder Steuerelement einen eigenen Gerätekontext für die Ausgabe besitzt
Height
Single-Wert, der die Höhe des Steuerelements bezogen auf das aktuelle Koordinatensystem des Containers bestimmt
HelpContextID
Long-Wert, der dem Steuerelement die Nummer eines Thema in der kontextbezogenen Hilfedatei (App.HelpFile) zuordnet
hDC
Zur Laufzeit schreibgeschützter Wert vom Typ Long der auf den Gerätekontext des Ausgabegerätes verweist und nur im Zusammenhang mit Funktionen der Win32-API benötigt wird.
hWnd
Long-Wert, der die Windows-Fensternummer (Handle) des dem Steuerelement zugeordneten Fensters wiedergibt
Image
Zur Laufzeit schreibgeschützter Picture-Wert, der einen Verweis auf die beständige Bitmap im Gerätekontext des Steuerelements oder Formulars darstellt
Index
Indexnummer des Steuerelements im Steuerelemente-Array (reine Entwurfseigenschaft)
Left
Single-Wert, der die horizontale Position (linker Rand) des Steuerelements im Container bezogen auf das aktuelle Koordinatensystem des Containers ausdrückt
MaskColor
Long-Wert, der die Transparenzfarbe für die Maskierung transparenter Bereiche des Steuerelements ausdrückt (Verwendung abhängig von UseMaskColor)
MouseIcon
Picture-Objekt mit Verweis auf benutzerdefinierten Mauszeiger (Verwendung abhängig von MousePointer)
MousePointer
Integer-Wert, der das für das Steuerelement angezeigte Mauszeigersymbol auswählt
MultiLine
Boolean-Wert, der bestimmt, ob der Wert des Steuerelements für eine mehrzeilige Darstellung umbrochen werden kann
Name
String-Wert, der den Bezeichner des Steuerelementobjekts wiedergibt
OLEDropAllowed
Boolean-Wert, der bestimmt, ob das Steuerelement das Ablegen von OLE-Objekten im Rahmen von OLE-Drag&Drop-Operationen zulässt
OLEDropMode
Integer-Wert, der das Verhalten des Steuerelements bei OLEDrag&Drop-Operationen bestimmt
OLETypeAllowed
Integer-Wert, der bestimmt, ob das Steuerelement das Einbetten und/ oder das Verknüpfen von OLE-Objekten unterstützt, die im Rahmen von OLE-Drag&Drop-Operationen abgelegt werden
Parent
Verweis auf das (Formular-)Objekt, dem das Steuerelement untergeordnet ist
Die wichtigsten gemeinsamen Eigenschaften der Steuerelemente
325
Gemeinsame Eigenschaften
Eigenschaft
Gemeinsame Eigenschaften
Gemeinsame Eigenschaften
Eigenschaft
Beschreibung
Picture
Picture-Objekt mit Verweis auf das im Bereich des Steuerelements angezeigte (Hintergrund-)Bild (Aktivierung und grafischer Modus ggf. erforderlich)
RightToLeft
Boolean-Wert, der die Ausrichtung der textorientierten Darstellung auf bidirektionalen Systemen bestimmt
ScaleLeft
Single-Wert, der die Ursprungsverschiebung des Koordinatensystems in horizontaler Richtung ausdrückt
ScaleMode
Integer-Wert, der das Koordinatensystem und die darin geltenden Maßeinheiten bestimmt
ScaleTop
Single-Wert, der die Ursprungsverschiebung des Koordinatensystems in vertikaler Richtung ausdrückt
ShowTips
Boolean-Wert, der die QuickInfo ein- oder ausschaltet
Style
Integer-Wert, der Art und/oder Darstellungsform des Steuerelements ausdrückt
TabIndex
Integer-Wert, der dem Steuerelements eine Position in der Tabulatorreihenfolge für die Fokusweitergabe zuordnet
TabStop
Boolean-Wert, der festlegt, ob das Steuerelement den Fokus durch Betätigung der Tabulatortaste (entlang der Tabulatorordnung) erlangen kann
Tag
Variant-Wert für zusätzliche benutzerdefinierte Daten (Eigenschaft wird von Visual Basic nicht benutzt)
ToolTipText
String-Wert mit Hilfetext, der als QuickInfo für das Steuerelement angezeigt wird, wenn die Maus darüber eine Zeit lang verharrt
Top
Single-Wert, der die vertikale Position (oberer Rand) des Steuerelements im Container bezogen auf das aktuelle Koordinatensystem des Containers ausdrückt
UseMaskColor
Boolean-Wert, der bestimmt, ob die in MaskColor festgelegte Farbe für die transparente Darstellung des Steuerelements benutzt wird
Visible
Boolean-Wert, der die Sichtbarkeit des Steuerelements bestimmt
WhatsThisHelpID
Long-Wert, der den Index für das Hilfethema der Popup-Direkthilfe festlegt. Wenn 0, kommt die Windows-Standardhilfe unter Beachtung von HelpContextID zum Aufruf
WhatsThisHelp
Boolean-Wert, der festlegt, ob die Direkthilfe für ein Formular aktiv ist
Width
Single-Wert, der die Breite des Steuerelements bezogen auf das aktuelle Koordinatensystem des Containers bestimmt.
Die wichtigsten gemeinsamen Eigenschaften der Steuerelemente
326
Alignment- Eigenschaft
Alignment- Eigenschaft Objekt.Alignment As Integer Betroffene Objekte
................................................... Betro ffene Objekte DataGrid, Label, ColumnHeader, Function, CheckBox, OptionButton, Panel, RptFunction, RptLabel, RptText,TextBox, UpDown Beschreibung
................................................... Bes c hreibung
Konstante (Wert)
definiert für ...
Wirkung
vbLeftJustify (0)
TextBox, Label, OptionButton, CheckBox
Wert bzw. Beschriftung wird linksbündig im Bereich des Steuerelements ausgerichtet (Voreinstellung)
vbRightJustify (1)
TextBox, Label, OptionButton, CheckBox
Wert bzw. Beschriftung wird rechtsbündig in Steuerelementbereich ausgerichtet
vbCenterJustify (2)
TextBox, Label
Wert oder Beschriftung wird zentriert in Steuerelementbereich ausgerichtet
IvwColumnLeft (0)
ColumnHeader
Spaltenbeschriftung wird linksbündig ausgerichtet (Voreinstellung)
IvwColumnRight (1)
ColumnHeader
Spaltenbeschriftung wird rechtsbündig ausgerichtet
IvwColumnCenter (2)
ColumnHeader
Spaltenbeschriftung wird zentriert ausgerichtet
sbrLeft (0), hdrLeft (0)
Panel
Text erscheint linksbündig rechts neben der Bitmap (Voreinstellung)
sbrCenter (1), hdrCenter (1)
Panel
Text erscheint zentriert rechts neben der Bitmap
sbrLeft (2), hdrLeft (2)
Panel
Text erscheint rechtsbündig links neben der Bitmap
cc2AlignmentLeft (0)
UpDown
AufAb-Steuerelement erscheint links von Buddy
cc2AlignmentRight (1)
UpDown
AufAb-Steuerelement erscheint rechts von Buddy (Voreinstellung)
dbgLeft (0)
DataGrid
Wert wird linksbündig in Spalte ausgerichtet
dbgRight (1)
DataGrid
Wert wird rechtsbündig in Spalte ausgerichtet
dbgCenter (2)
DataGrid
Wert wird zentriert in Spalte ausgerichtet
3 27
Gemeinsame Eigenschaften
Die Alignment-Eigenschaft bestimmt die Ausrichtung eines Steuerelements in dem ihm zur Verfügung stehenden Bereich. Für Ausrichtung sind für die verschiedenen Steuerelemente in den jeweiligen Modulen diverse Integer-Konstanten definiert. Die folgende Tabelle gibt einen Überblick:
Gemeinsame Eigenschaften
Gemeinsame Eigenschaften
Konstante (Wert)
definiert für ...
Wirkung
dbgGeneral (3)
DataGrid
Text wird links, Zahlen werden rechts in Spalte ausgerichtet (Voreinstellung)
rptJustifyLeft (0)
RptFunction, RptLabel, Wert wird linksbündig in Bereich ausgerichtet (Voreinstellung) RptText
rptJustifyRight (1)
RptFunction, RptLabel, Wert wird rechtsbündig in Bereich ausgerichtet RptText
rptJustifyCenter (2)
RptFunction, RptLabel, Wert wird zentriert in Bereich ausgerichtet RptText
Hinweis
................................................... Hinweis Entgegen anders lautenden Informationen in der Online-Hilfe zu Visual Basic lässt sich diese Eigenschaft für Kontrollkästchen, Optionsfelder und Textfelder zur Laufzeit manipulieren. Auch lassen sich die Inhalte von Textfeldern unabhängig vom Wert der Eigenschaft MultiLine ausrichten.
Appearance- Eigenschaft Objekt.Alignment As Integer Betroffene Objekte
................................................... Betro ffene Objekte Adodc, CheckBox, ComboBox, CommandButton, Data, DataCombo, DataList, DirListBox, DriveListBox, FileListBox, FlatScrollBar, Form, Frame, Image, Label, ListBox, MDIForm, OLE, OptionButton, PictureBox, PropertyPage, RemoteData, TextBox, ToolBar, UserControl, UserDocument Beschreibung
................................................... Bes c hreibung
Die Appearance-Eigenschaft legt fest, ob das Steuerelement oder Formular im 3D-Stil von Windows 9x ausgegeben wird oder im flachen 2D-Stil von Windows 3.x. Die Eigenschaft wirkt sich nur auf das Objekt selbst und nicht auf untergeordnete Objekte aus – sie ist also für diese getrennt zu setzen. Konstante (Wert)
Definiert für ...
0
Die meisten Komponenten 3D-Darstellung
1
Die meisten Komponenten 2D-Darstellung
fsb3D (0)
FlatScrollBar
3D-Darstellung wie Windows-Bildlaufleiste
fsbFlat (1)
FlatScrollBar
Flache 2D-Darstellung (Voreinstellung)
fsbTrack3D (2)
FlatScrollBar
Bildlaufleiste ist 2D, Bildlaufmarke und Bildlaufpfeile werden 3D, wenn der Mauszeiger darüber steht
cc3D (0)
Toolbar, ListView, Tree- 3D-Darstellung View
ccFlat (1)
Toolbar, ListView, Tree- 2D-Darstellung View
328
Wirkung
AutoSize- Eigenschaft
Hinweis
................................................... Hinweis Entgegen anders lautenden Informationen in der Online-Hilfe zu Visual Basic lässt sich diese Eigenschaft zur Laufzeit ändern. Auch wirkt sich der Wert 3 für die Eigenschaft BorderStyle nicht auf die Darstellung eines Formulars aus.
AutoSize- Eigenschaft Objekt.AutoSize As Boolean Betroffene Objekte
................................................... Betro ffene Objekte Beschreibung
................................................... Bes c hreibung
Die AutoSize-Eigenschaft bestimmt, ob ein Steuerelement seinen Bereich automatisch an die für die vollständige Darstellung seines Standardwerts benötigten Abmessungen anpasst (True) oder nicht (False; Voreinstellung). Anwendung
................................................... Anwendung
Trotz der augenscheinlichen Vorzüge, die der hinter der AutoSize-Eigenschaft steckende Automatismus verspricht, sollte sein Einsatz gut überlegt werden – insbesondere bei Bezeichnungsfeldern, die mit einem Feld in einer Datenbank oder mit einer DDE-Quelle verbunden sind und ihren Wert somit automatisch ändern können. (Wie das Beispiel zeigt, ist der Automatismus in Visual Basic 6.0 zudem nicht sauber implementiert). Wenn ein Steuerelement seine Größe automatisch anpasst, kann es nämlich dazu kommen, dass es nicht mehr auf das Formular passt und abgeschnitten wird, oder schlimmer noch, sich mit anderen Steuerelementen überlappt, diese verdeckt oder selbst verdeckt wird. Beim Bildfeld-Steuerelement lässt sich das Ergebnis der automatischen Bereichsanpassung immerhin durch Behandlung des Resize-Ereignisses noch kontrollieren und gegebenenfalls nachbessern, beim Bezeichnungsfeld-Steuerelement sind »Hopfen und Malz verloren«. Beispiel
................................................... Beis piel
Das Beispielprojekt FadeOut demonstriert die Tücken der AutoSize-Eigenschaft. Ein Timer animiert ein Bezeichnungsfeld mit automatischer Größenanpassung auf einem Formular mit einem Fade-out-Effekt. Damit die Bereichsgrenzen gut zu sehen sind, ist der Hintergrund des Bezeichnungsfelds weiß. Wenn Sie das Programm starten, werden Sie feststellen, dass die Bereichsanpassung durch die AutoSize-Eigenschaft nur unvollständig funktioniert und der Bereich immer breiter wird. Einen Work-around für das Problem bieten die beiden auskommentierten Zeilen. Dim Texte() Dim i Private Sub Form_Load() Texte = Array("Rauchen", "schadet", "Ihrer", "Gesundheit") Label1 = Texte(0) Label1.AutoSize = True Label1.BackColor = vbWhite ' Hintergrund Weiß Label1.FontName = "Arial" ' TrueType, wegen Size-Änderung Timer1.Interval = 40 End Sub
3 29
Gemeinsame Eigenschaften
Label, PictureBox
Gemeinsame Eigenschaften
Gemeinsame Eigenschaften
Private Sub Timer1_Timer() Label1.Font.Size = Label1.Font.Size + 1.2 If Label1.FontSize > 60 Then Label1.FontSize = 8 ' Label1.Left = Label1.Left + Label1.Width / 2 ' Label1.Width = 1 i = (i + 1) Mod (UBound(Texte) + 1) Label1 = Texte(i) End If End Sub
BackColor- Eigenschaft und ForeColor- Eigenschaft Objekt.BackColor As Long Objekt.ForeColor As Long Betroffene Objekte
................................................... Betro ffene Objekte Adodc, AmbientProperties, Animation, Band, CheckBox, ComboBox, CommandButton, CoolBar, Data, DataCombo, DataList, DirListBox, DriveListBox, FileListBox, Form, Frame, Image, ImageCombo, ImageList, Label, ListBox, MDIForm, OLE, OptionButton, RptFunction, RptImage, RptLabel, RptText, RptShape, PictureBox, Printer, PropertyPage, RemoteData, RichTextBox, Shape, SSTab, TextBox, UserControl, UserDocument Beschreibung
................................................... Bes c hreibung
Die Eigenschaften BackColor und ForeColor sind Farbwerte vom Typ Long, die die Hintergrundfarbe bzw. die Vordergrundfarbe (oder Stiftfarbe) für ein Objekt festlegen. Die Voreinstellungen dieser Eigenschaften sind je nach Komponententyp verschieden, entsprechen aber den standardmäßig dafür in der Systemsteuerung festgelegten Systemfarben. Zur Auswahl stehen neben den Systemfarben auch beliebige andere Farben, die sich entweder zur Entwurfszeit aus der Palette wählen oder zur Laufzeit über die Funktion RGB komponentenweise zu einem Farbwert komponieren lassen. Die Funktion QBColor liefert den Farbwert einer der 16 Grundfarben. Zudem definiert der Aufzählungstyp SystemColorConstants die folgenden Konstanten für die Systemfarben: Konstante
Wert
Systemfarbe für ...
vbScrollBars
0x80000000
Farbe der Bildlaufleiste
vbDesktop
0x80000001
Farbe des Desktop
vbActiveTitleBar
0x80000002
Farbe der Titelleiste des aktiven Fensters
vbInactiveTitleBar
0x80000003
Farbe der Titelleiste des inaktiven Fensters
vbMenuBar
0x80000004
Hintergrundfarbe für Menüs
vbWindowBackground
0x80000005
Hintergrundfarbe für Fenster
vbWindowFrame
0x80000006
Rahmenfarbe für Fenster
vbMenuText
0x80000007
Farbe für Menütext
vbWindowText
0x80000008
Textfarbe in Fenstern
vbTitleBarText
0x80000009
Farbe für Titeltext
330
BackStyle- Eigenschaft
Wert
Systemfarbe für ...
vbActiveBorder
0x8000000A
Rahmenfarbe für aktives Fenster
vbInactiveBorder
0x8000000B
Rahmenfarbe für inaktives Fenster
vbApplicationWorkspace
0x8000000C
Hintergrundfarbe von MDI-Anwendungen
vbHighlight
0x8000000D
Hintergrundfarbe für ausgewählte Elemente oder Bereiche des Steuerelements
vbHighlightText
0x8000000E
Textfarbe für ausgewählte Elemente oder Bereiche des Steuerelements
vbButtonFace
0x8000000F
Farbe für Oberfläche von Schaltflächen
vbButtonShadow
0x80000010
Farbe für Schattierung von Schaltflächen
vbGrayText
0x80000011
Textfarbe für deaktiviertes Steuerelement
vbButtonText
0x80000012
Farbe für Schaltflächenbeschriftung
vbInactiveCaptionText
0x80000013
Titeltextfarbe für inaktives Fenster
vb3DHighlight
0x80000014
Farbe für markierten Zustand von 3D-Elementen
vb3DDKShadow
0x80000015
Farbe für dunkelsten Schatten von 3D-Elementen
vb3DLight
0x80000016
Farbe für helle Bereiche in 3D-Elementen
vbInfoText
0x80000017
Textfarbe für QuickInfo
vbInfoBackground
0x80000018
Hintergrundfarbe für QuickInfo
Anwendung
................................................... Anwendung
Der Wertebereich für RGB-Farben liegt zwischen 0 und 16.777.215 (&H00FFFFFF), so dass das höchstwertige Byte des Long-Werts immer gleich 0 ist. Die drei niederwertigen Bytes bestimmen (in aufsteigender Reihenfolge) jeweils den Rot-, Grün- und Blauanteil, dargestellt durch einen Wert zwischen 0 und 255 (&HFF). Ist das höchstwertige Byte 128 (&H80), drückt das niederwertige Byte eine der 25 reservierten Systemfarben aus (vgl. Tabelle). Beispiel
................................................... Beis piel
Form1.BackColor = RGB(124, 12, 120) Form1.ForeColor = RGB(255, 255, 255)
' Lila Hintergrund ' Weiße Textfarbe
BackStyle- Eigenschaft Objekt.Alignment As Integer Betroffene Objekte
................................................... Betro ffene Objekte Animation, Label, RptFunction, RptImage, RptLabel, RptText, RptShape, OLE, Shape, UserControl Beschreibung
................................................... Bes c hreibung
Die BackStyle-Eigenschaft bestimmt, ob ein Steuerelement mit durchsichtigem Hintergrund angezeigt wird oder nicht.
331
Gemeinsame Eigenschaften
Konstante
Gemeinsame Eigenschaften
Gemeinsame Eigenschaften
Konstante (Wert)
Definiert für ...
Wirkung
0
Label, OLE, Shape, UserControl
transparenter Hintergrund
1
Label, OLE, Shape, UserControl
Hintergrund in Hintergrundfarbe
cc2BackStyleTransparent (0)
Animation
transparenter Hintergrund
cc2BackStyleTransparent (1)
Animation
Hintergrund in Hintergrundfarbe
rptBkOpaque (0)
RptFunction, RptImage, RptLabel, RptText, RptShape
transparenter Hintergrund
rptBkTransparent (1)
RptFunction, RptImage, RptLabel, RptText, RptShape
Hintergrund in Hintergrundfarbe
Hinweis
................................................... Hinweis Diese Eigenschaft muss auf den Wert 0 gesetzt sein, damit die Eigenschaft MaskColor ihre Wirkung entfalten kann. Verwandte Themen
................................................... Verwandte Them en
Transparenz und Drag&Drop (S. 606)
BorderStyle- Eigenschaft Objekt.BorderStyle As Integer Betroffene Objekte
................................................... Betro ffene Objekte Form, Frame, Image, Label, Line, ListView, MonthView, MSChart, OLE, ProgessBar, RptFunction, RptImage, RptLabel, RptLine, RptShape, PictureBox, ProgressBar, Shape, Slider, TextBox, ToolBar, TreeView, UserControl Beschreibung
................................................... Bes c hreibung
Für ein Steuerelement bestimmt die BorderStyle-Eigenschaft, mit welchem Linientyp es selbst oder seine Umrandung gezeichnet wird, und für ein Formular, welchen Fenstertyp dieses erhält. Konstante (Wert)
Definiert für ...
Wirkung
vbBSNone (0)
Frame, Form, Image, Label, OLE, Text, UserControl
«Keine« – Darstellung ohne Randlinie oder Umrandung; bei Formularen fehlt zudem eine Titelleiste
vbFixedSingle (1)
Frame, Form, Image, Label, OLE, Text, UserControl
«Fest Einfach« – Steuerelemente erhalten eine Umrandung; für UserControl-Steuerelemente ist dieser Wert nur definiert, wenn die Windowless-Eigenschaft auf False gesetzt ist; bei Formular kann der Benutzer die Größe nur mittels der Schaltflächen MINIMIEREN, MAXIMIEREN und WIEDERHERSTELLEN ändern
332
BorderStyle- Eigenschaft
Definiert für ...
Wirkung
vbSizable (2)
Form
«Änderbar« (Voreinstellung) – der Benutzer kann die Größe ändern
vbFixedDouble (3)
Form
«Fester Dialog« – Formular kann Systemmenü, nicht jedoch die Schaltflächen MINIMIEREN, MAXIMIEREN und WIEDERHERSTELLEN enthalten. Die Größe ist nicht änderbar.
vbFixedToolWindow (4)
Form
«Festes Werkzeugfenster« – Fenster mit schmaler Titelleiste, ohne Systemmenü jedoch mit Schaltfläche SCHLIEßEN. Das Formular wird nicht in der Taskleiste von Windows 95 angezeigt und seine Größe ist nicht änderbar.
vbSizableToolWindow (5) Form
«Änderbares Werkzeugfenster« – größenveränderliches Fenster mit schmaler Titelleiste, ohne Systemmenü, jedoch mit der Schaltfläche SCHLIEßEN. Das Formular wird nicht in der Taskleiste von Windows 9x angezeigt.
vbTransparent (0)
Line, Shape
«Transparent« – keine Linie
vbBSSolid (1)
Line, Shape
«Ausgefüllt« – durchgezogene Linie
vbBSDash (2)
Line, Shape
«Strich« – gestrichelte Linie
vbBSDot (3)
Line, Shape
«Punkt« – gepunktete Linie
vbBSDashDot (4)
Line, Shape
«Strich-Punkt»
vbBSDashDotDot (5)
Line, Shape
«Strich-Punkt-Punkt»
vbBSInsideSolid (6)
Line, Shape
«Innen ausgefüllt« – Randlinie innerhalb der Bereichsgrenzen
rptBSTransparent (0)
RptLine, RptShape
Keine Linie
rptBSSolid (1)
RptLine, RptShape
Durchgezogene Linie
rptBSDashes (2)
RptLine, RptShape
Gestrichelte Linie
rptBSDots (3)
RptLine, RptShape
Gepunktete Linie
rptBSDashDot (4)
RptLine, RptShape
Strich-Punkt-Linie
rptBSDashDotDot (5)
RptLine, RptShape
Strich-Punkt-Punkt-Linie
cc2None (0)
MonthView, ListView, Randlos ProgressBar, Slider, ToolBar, TreeView
cc2FixedSingle (1)
MonthView, ListView, Umrandet ProgressBar, Slider, ToolBar, TreeView
vtStyleNone (0)
MSChart
Randlos
vbStyleFixedSingle (1)
MSChart
Umrandet
333
Gemeinsame Eigenschaften
Konstante (Wert)
Gemeinsame Eigenschaften
Anwendung
Gemeinsame Eigenschaften
................................................... Anwendung
Für ein Formular legt die BorderStyle-Eigenschaft einen Teil der Ausstattung des Fensters fest. Der Wert 0 steht für ein rahmenloses Fenster ohne Titelleiste, wie es beispielsweise für eine Vollbildansicht benötigt wird. Die Werte 1 und 2 stehen für ein Mehrzweckfenster wahlweise mit Systemmenü (ControlBox) und den Schaltflächen MAXIMIEREN MINIMIEREN und WIEDERHERSTELLEN (MaxButton, MinButton). Der Wert 3 (Fester Dialog) steht für das klassische Dialogfeld, dessen Größe der Benutzer nicht ändern kann. Die Einstellungen 4 (Festes Werkzeugfenster) und 5 (Änderbares Werkzeugfenster) erzeugen Fenster mit der Schaltfläche SCHLIEßEN und ohne Systemmenü, wie sie gewöhnlich für Werkzeugsammlungen verwendet werden. Einem MDI-Formular untergeordnete Formulare (MDIChild-Eigenschaft hat den Wert True) mit der Einstellung 2 (Änderbar) erscheinen innerhalb des MDI-Formulars in einer voreingestellten Größe, die durch Windows zur Laufzeit festgelegt wird. Bei allen anderen Einstellungen erscheint das Formular in der zur Entwurfszeit angegebenen Größe. Hinweise
................................................... Hinweis e Entgegen anders lautender Informationen in der Online-Hilfe zu Visual Basic ist diese Eigenschaft auch für Textfelder und Formulare zur Laufzeit nicht schreibgeschützt. Eine Änderung der Eigenschaft zur Laufzeit zeigt für Formulare jedoch keine Wirkung. Wenn Sie diese Eigenschaft für ein Formular auf eine der Einstellungen 0, 1, 3, 4, 5 setzen, ändert die Entwicklungsumgebung von Visual Basic zugleich die Eigenschaften MinButton, MaxButton und ShowInTaskbar ungefragt auf False. Allerdings spielen diese Eigenschaften ohnehin nur für die Einstellungen 1 und 2 eine Rolle. Verwandte Themen
................................................... Verwandte Them en
Vollbildanzeige (S. 536)
Caption- Eigenschaft Objekt.Caption As String Betroffene Objekte
................................................... Betro ffene Objekte Adodc, CheckBox, CommandButton, Data, Form, Frame, Label, MDIForm, Menu, OptionButton, PropertyPage, RemoteData, RptLabel, UserControl Beschreibung
................................................... Bes c hreibung
Für ein Formularobjekt bestimmt die Caption-Eigenschaft den Fenstertitel und für ein Steuerelement Objekt dessen Beschriftung. Der Wert der Eigenschaft kann zur Laufzeit jederzeit geändert werden. Anwendung
................................................... Anwendung
Die Entwicklungsumgebung setzt als Voreinstellung für die Caption-Eigenschaft immer den automatisch vergebenen Bezeichner des Steuerelements bzw. Formulars. Der Fenstertitel eines Formulars erscheint auch in der Taskleiste sowie im Task-Manager, sofern die Eigenschaft ShowInTaskBar auf True gesetzt ist. Beispiel
................................................... Beis piel
Caption = App.Title & " – " & sDateiname
334
Container- Eigenschaft
Container- Eigenschaft Objekt.Container Objekt.Container Objekt.Container Objekt.Container
As As As As
FormularTyp PictureBox Frame SSTab
Betroffene Objekte
................................................... Betro ffene Objekte Alle Steuerelemente Beschreibung
Die Eigenschaft Container ist eine reine Laufzeiteigenschaft. Ihr Wert verweist auf das Objekt, das als Container für das Steuerelement fungiert. Als Container kann ein Formularobjekt oder ein anderes Steuerelement des Typs PictureBox, Frame oder SSTab fungieren. Hinweis
................................................... Hinweis Der Container eines Steuerelements lässt sich zur Laufzeit zwar ändern, jedoch immer nur innerhalb des gleichen Formulars. Beachten Sie, dass sich die Position eines Steuerelements immer auf das Koordinatensystem des Containers bezieht. Dies kann sich mit dem Container ändern. Bei Änderung des Containers rechnet Visual Basic die Positionskoordinaten und Abmessungen allerdings automatisch um. Beispiel
................................................... Beis piel
Set Text1.Container = Form1 Form1.ScaleMode = vbInches Text1.Left = 1 Text1.Top = 1 Set Text3.Container = Picture1 Print Text1.Left Print Text1.Top
' Koordinatensystem mit Einheit Zoll
' Anderen Container setzen ' Ausgabe: 1440 ' Ausgabe: 1440
CausesValidation- Eigenschaft Objekt.CausesValidation As Boolean Betroffene Objekte
................................................... Betro ffene Objekte Adodc, Animation, CommandButton, CheckBox, ComboBox, DataCombo, DataList, DataRepeater, DirListBox, DriveListBox, FileListBox, ListBox, ListView, MonthView, MSChart, OLE, OptionBox, PictureBox, RichTextBox, Slider, TextBox, TreeView, UpDown, VBControlExtender Beschreibung
................................................... Bes c hreibung
Die CausesValidation-Eigenschaft eröffnet einem Steuerelement die Möglichkeit, seinen Wert durch Behandlung des Validate-Ereignisses einer Gültigkeitsprüfung zu unterziehen, unmittelbar bevor es den Fokus abgibt. Damit das Steuerelement allerdings das Validate-Ereignis zu sehen bekommt, muss nicht nur seine CausesValidation-Eigenschaft auf True gesetzt sein, sondern auch die des Steuerelements, auf das der Fokus übergehen soll. Verliert ein Steuerelement mit gesetzter CausesValidation-Eigenschaft den Fokus an ein Steuerelement, dessen CausesValidation-Eigenschaft auf False gesetzt ist, tritt das Validate-Ereignis nicht sofort auf, sondern erst, wenn der Fokus wieder in Besitz eines Steuerelements mit gesetzter CausesValidation-
335
Gemeinsame Eigenschaften
................................................... Bes c hreibung
Gemeinsame Eigenschaften
Eigenschaft gehen soll. Setzt ein Steuerelement bei der Behandlung des Validate-Ereignisses den Cancel-Parameter auf True, behält es den Fokus bzw. erhält es diesen zurück. Anwendung
Gemeinsame Eigenschaften
................................................... Anwendung
Die CausesValidation-Eigenschaft eines Steuerelements muss immer im Zusammenhang mit den CausesValidation-Eigenschaften der anderen Steuerelemente auf einem Formular gesehen werden. Der etwas kompliziert anmutende Mechanismus hat aber seine Vorteile: Der Benutzer kann während der Eingabe eines Werts in ein Steuerelement mit Gültigkeitsprüfung auf andere Steuerelemente zugreifen, solange für diese die CausesValidation-Eigenschaft nicht gesetzt ist, und beispielsweise Hilfe anfordern, seine Eingabe verwerfen oder den Dialog ganz abbrechen.
DataChanged- Eigenschaft Objekt.Changed As Boolean Betroffene Objekte
................................................... Betro ffene Objekte CheckBox, Column, ComboBox, DataCombo, DataGrid, DataList, DTPicker, Image, Label, ListBox, MaskEdBox, MonthView, OLE, PictureBox, RepeaterBinding, RichTextBox, TextBox, Beschreibung
................................................... Bes c hreibung
Die DataChanged-Eigenschaft eines Steuerelements drückt aus, ob sich der Wert der Standardeigenschaft inzwischen (lies: seit DataChanged zuletzt auf False gesetzt wurde) geändert hat (True) oder nicht (False). Eine besondere Rolle spielt die DataChanged-Eigenschaft, wenn das Steuerelement an eine Datenquelle gebunden ist und von dieser Daten bezieht. Jede Synchronisation des Steuerelements mit dem aktuellen Datensatz eines Recordsets setzt den Wert dieser Eigenschaft auf False, Änderungen der Standardeigenschaft durch den Benutzer oder durch Programmcode setzen ihn dagegen auf True. Im letzteren Fall kann eine automatische Wertübernahme beim nächsten Datensatzwechsel erfolgen. Anwendung
................................................... Anwendung
Ein Programm kann die DataChanged-Eigenschaft im Rahmen der Validate-Behandlung eines Steuerelements als Kriterium für die Gültigkeitsprüfung auswerten und gegebenenfalls auch auf False setzen, um Aktualisierungen des aktuellen Datensatzes mit dem Wert der Standardeigenschaft zu unterbinden. Nur wenn DataChanged auf True gesetzt ist und über die Edit-Methode des Recordset-Objekts der Bearbeitungsmodus eingeschaltet wurde, wird der Wert der Standardeigenschaft beim nächsten Datensatzwechsel oder beim expliziten Aufruf der UpdateMethode in den Datensatz zurückgeschrieben.
DataField- Eigenschaft Objekt.DataField As String Betroffene Objekte
................................................... Betro ffene Objekte CheckBox, Column, ComboBox, DataCombo, DTPicker, Image, ImageCombo, Label, ListBox, MonthView, OLE, PictureBox, RepeaterBinding, RptFunction, RptTextBox, RichTextBox, TextBox,
336
DataField- Eigenschaft
Beschreibung
................................................... Bes c hreibung
Die DataField-Eigenschaft enthält den Feldnamen eines Datenfelds einer Tabelle oder eines Recordsets, wenn das Steuerelement Objekt als Datennutzer an eine Datenquelle (DataSource) bzw. ein Datenelement (DataMember) einer Datenquelle gebunden ist. Anwendung
................................................... Anwendung
Tipp
................................................... Tipp
Um einem Steuerelement ein in einer Abfrage berechnetes Feld zuzuordnen, muss die Abfrage einen Alias (alternativen Bezeichner) für dieses Feld vereinbaren. Beispiel
................................................... Beis piel
Der folgende Code arbeitet mit einem Textfeld Text1, das zur Entwurfszeit an ein Data-Steuerelement Data1 als Datenquelle gebunden wurde. Ein Klick auf die Schaltfläche FrachtkostenAnzeigen ändert das Recordset der Datenquelle sowie die Datenfeldbindung des Textfeldes. Const Abfrage1 = "SELECT AVG(Frachtkosten) AS Fracht FROM Bestellungen" Private Sub FrachtkostenAnzeigen_Click() Text1.DataField = "" ' weil RecordSource geändert wird Data1.RecordSource = Abfrage1 Data1.Refresh ' weil RecordSource geändert wurde Text1.DataField = "Fracht" ' berechnetes Feld End Sub
ADO-Steuerelemente lassen sich auch zur Laufzeit als Datenquelle setzen. In diesem Fall könnte die Behandlungsroutine so aussehen: Private Sub FrachtkostenAnzeigen_Click() Text1.DataField = "" ' alte Bindung ggf. aufheben Adodc1.RecordSource = Abfrage1 ' Refresh nicht nötig Set Text1.DataSource = Adodc1 Text1.DataField = "Fracht" ' berechnetes Feld End Sub
Da der gemeinsame Einsatz von Data- und Adodc-Steuerelementen im Zusammenhang mit der Jet-Engine Probleme bereiten kann, ist es besser, sich für die eine oder die andere Gattung zu entscheiden.
337
Gemeinsame Eigenschaften
Das standardmäßige Szenario für den Zugriff auf eine Datenquelle (lies: Datenbank) von Visual Basic sieht so aus, dass ein Formular oder eine Komponente mit Steuerelementen bestückt ist, deren Werte über die DataField-Eigenschaft an die Feldwerte eines einzelnen Datensatzes oder einer Folge von Datensätzen gebunden sind. Die Datensätze entstammen wiederum einer Tabelle oder einem beliebigen Recordset (lies: Ergebnis einer Abfrage), das ein Datensteuerelement unter Verwendung eines Daten-Providers (lies: einer geeigneten Schnittstelle) bei einem Datenbanksystem anfordert. Als Datenquellen kommen Objekte wie Data, RemoteData, Adodc oder DataEnvironment in Frage. Datenquellen, die durch die ersten beiden Steuerelementtypen repräsentiert werden, müssen bereits zur Entwurfszeit an den Datennutzer gebunden werden, die anderen beiden Objekttypen lassen sich auch zur Laufzeit binden. Die DataField-Eigenschaft kann jedoch ohne Einschränkungen zur Laufzeit gesetzt werden, gleich an welche Art von Datenquelle ein Steuerelement gebunden ist. Falls das Steuerelement bereits an ein Feld gebunden ist, sollte die Eigenschaft DataField vor einem Wechsel der Datenquelle oder des Recordsets in der Datenquelle auf die leere Zeichenfolge gesetzt werden.
Gemeinsame Eigenschaften
Verwandte Eigenschaften
................................................... Verwa ndte Eigens c ha ften DataMember, DataFormat, DataSource Verwandte Themen
................................................... Verwandte Them en
Data-Datensteuerelement (Data) (S. 402)
DataFormat- Eigenschaft
Gemeinsame Eigenschaften
Objekt.DataFormat As IStdDataFormatDisp Betroffene Objekte
................................................... Betro ffene Objekte CheckBox, Column, ComboBox, DataCombo, DataList, DataRepeater, DBList, DBCombo, DTPikker, Image, ImageCombo, Label, ListBox, MonthView, OLE, PictureBox, RepeaterBinding, RptFunction, RptTextBox, TextBox, Beschreibung
................................................... Bes c hreibung
Die DataFormat-Eigenschaft ordnet einem Steuerelement Objekt, das an eine Datenquelle gebunden ist, ein Datenformat in Form eines Datenformatobjekts zu. Das wie ein bilateraler Filter arbeitende Datenformatobjekt stellt die von der Datenquelle gelieferten Daten in dem gewählten Format dar und übersetzt in diesem Format gehaltene Eingaben auch wieder zurück in die von der Datenquelle benutzte interne Darstellung. Anwendung
................................................... Anwendung
Visual Basic ordnet jedem Steuerelement, das an eine Datenquelle gebunden ist, ein standardmäßiges Datenformatobjekt des Typs StdDataFormat zu. Diese Zuordnung ist integraler Bestandteil der Bindung und sorgt im Allgemeinen automatisch für eine geeignete Anpassung zwischen dem von der Datenquelle gelieferten und dem von dem Steuerelement erwarteten Wert. Wenn gefordert ist, dass ein Steuerelement seinen Wert in einem bestimmten Darstellungsformat anzeigt, etwa in einem Währungsformat oder einem bestimmten Datums-/ZeitFormat, kann dieses zur Entwurfszeit in der Eigenschaftsseite des DataFormat-Objekts ausgewählt werden. Der Aufruf der Eigenschaftsseite kann über das Eigenschaftsfenster erfolgen. Darüber hinaus hat das Datenformatobjekt eine Reihe von Eigenschaften, die sich zur Laufzeit setzen bzw. abfragen lassen. Die wichtigste davon ist die Format-Eigenschaft vom Typ String, die eine Zeichenfolge mit der Formatbeschreibung enthält. Beispiel
................................................... Beis piel
' Liefert Ausgaben der Art "18:02:04 Uhr, am 5. März 1992" Text1.DataFormat.Format = "h:mm:ss U\hr, a\m d.mmmm yyyy" Verwandte Eigenschaften
................................................... Verwa ndte Eigens c ha ften DataField, DataMember, DataSource Verwandte Themen
................................................... Verwandte Them en
Data-Datensteuerelement (Data) (S. 402)
338
DataMember- Eigenschaft
DataMember- Eigenschaft Objekt.DataMember As String Betroffene Objekte
................................................... Betro ffene Objekte CheckBox, Column, ComboBox, DataCombo, DataGrid, DataList, DataRepeater, DBList, DBCombo, DTPicker, Image, ImageCombo, Label, ListBox, MonthView, OLE, PictureBox, RepeaterBinding, RptFunction, RptTextBox, RichTextBox, TextBox Beschreibung
................................................... Bes c hreibung
Anwendung
................................................... Anwendung
Die DataMember-Eigenschaft spielt für die fortgeschrittene Programmierung mit ADO-Datenobjekten (ActiveX Data Objects) ein gewisse Rolle, wenn als Datenquelle eine DataEnvironmentKomponente (oder eine vergleichbare Komponente) benutzt wird. Einer DataEnvironment-Komponente lassen sich Befehle (Command-Objekte) und gespeicherte Prozeduren (PreparedCommandObjekte) für die Auswahl spezieller Recordsets zuordnen, die sich dann über die DataMemberEigenschaft abstrakt auswählen lassen. Beispiel
................................................... Beis piel
Text1_DataMember = "Command2" Verwandte Eigenschaften
................................................... Verwa ndte Eigens c ha ften DataField, DataFormat, DataSource Verwandte Themen
................................................... Verwandte Them en
Data-Datensteuerelement (Data) (S. 402)
DataSource- Eigenschaft Objekt_DataSource = DatenquelleObjekt Betroffene Objekte
................................................... Betro ffene Objekte CheckBox, Column, ComboBox, DataCombo, DataGrid, DataList, DataRepeater, DBList, DBCombo, DTPicker, Image, ImageCombo, Label, ListBox, MaskEdBox, MonthView, OLE, PictureBox, RepeaterBinding, RptFunction, RptTextBox, RichTextBox, TextBox Beschreibung
................................................... Bes c hreibung
Die DataSource-Eigenschaft bindet ein Steuerelement an eine sog. Datenquelle. Bei einer Datenquelle handelt es sich um ein Objekt, das eine Datenbankverbindung herstellen und die Repräsentation von Datensatzmengen (Recordset) übernehmen kann. Ist ein Steuerelement an eine Datenquelle gebunden, zeigt es zum einen den von dieser Datenquelle gelieferten Wert an, kann zum anderen aber auch Änderungen des Werts zurück an die Datenquelle liefern (sofern das verwendete Recordset-Objekt dies zulässt; vgl. »Data-Datensteuerelement (Data)«; S. 402).
339
Gemeinsame Eigenschaften
Die DataMember-Eigenschaft eines gebundenen Steuerelements spezifiziert, welches Datenelement der Datenquelle die Datensätze bereitstellt. Datenelemente sind vom Prinzip her benannte Recordsets, die in dem als Datenquelle fungierenden Objekt bereits vordefiniert sind und sich über die DataMember-Eigenschaft auswählen lassen. Hat die DataMember-Eigenschaft die leere Zeichenfolge als Wert, fungiert das standardmäßige Recordset der Datenquelle als Datenelement.
Gemeinsame Eigenschaften
An vordefinierten Datenquellen stehen in die beiden älteren Steuerelemente Data, RemoteData sowie das neuere ActiveX-Steuerelement Adodc und DataEnvironment-Komponenten zur Verfügung. Datenquellen, die durch die ersten beiden Steuerelementtypen repräsentiert werden, müssen bereits zur Entwurfszeit an den Datennutzer gebunden werden, die anderen beiden Objekttypen lassen sich auch zur Laufzeit binden. Anwendung
Gemeinsame Eigenschaften
................................................... Anwendung
Die Datenbankanbindung über ein Datensteuerelement kommt der Philosophie von Visual Basic am weitesten entgegen. Da die gesamte Logik für die Errichtung und Pflege der Datenbankverbindung in dem Datensteuerelement verkapselt ist, muss der Programmierer beim Entwurf eines datenbankbasierten Formulars (bzw. Komponente) nichts weiter machen, als ein Datensteuerelement in den Entwurfsbereich zu ziehen, dieses (zur Entwurfszeit oder zur Laufzeit) geeignet zu initialisieren und es den Steuerelementen, die für die Anzeige der Informationen aus der Datenbank gedacht sind, über die Eigenschaft DataSource als Datenquelle bekanntzugeben. Den Rest macht das Datensteuerelement: Es baut die Verbindung mit der Datenbank auf, wählt eine Datensatzmenge aus, pflegt einen über die Pfeilschaltflächen des Datensteuerelements gesteuerten Datensatzzeiger und synchronisiert die angebundenen Steuerelemente mit dem jeweils aktuellen Datensatz. Jedes der drei in Visual Basic verfügbaren Datensteuerelemente geht mit einem Datenzugriffsmodell einher. Die beiden älteren Datensteuerelemente Data und RemoteData sind speziell auf DAO (Data Access Objects = Datenzugriffsobjekte für den Datenzugriff über die Microsoft Jet Engine) bzw. auf RDO (Remote Data Objects = Remote-Datenzugriffsobjekte für den Datenzugriff über ODBC) ausgelegt. Sie werden inzwischen nur noch der Kompatibilität halber unterstützt, da seit Einführung der ActiveX-Datenzugriffsobjekte (ADO = ActiveX Data Objects) mit Visual Basic 6.0 ein vollständig COM-orientiertes Datenzugriffsmodell existiert. Für einfache Datenbankanwendungen tut es jedes der drei genannten Datensteuerelemente. Bei komplexeren Anforderungen an die möglichen Formen des Datenzugriffs sowie an Datenbankinteraktionen generell empfiehlt sich die Verwendung von Datenumgebungskomponenten (DataEnvironment), die eine Verkapselung maßgeschneiderter Modelle für die fortgeschrittene Datenbankinteraktion ermöglichen. Beispiel
................................................... Beis piel
Const Abfrage1 = "SELECT AVG(Frachtkosten) AS Fracht FROM Bestellungen" Private Sub FrachtkostenAnzeigen_Click() Text1.DataField = "" ' alte Bindung ggf. aufheben Adodc1.RecordSource = Abfrage1 ' Refresh nicht nötig Set Text1.DataSource = Adodc1 Text1.DataField = "Fracht" ' berechnetes Feld End Sub Verwandte Eigenschaften
................................................... Verwa ndte Eigens c ha ften DataField, DataFormat, DataMember Verwandte Themen
................................................... Verwandte Them en
Data-Datensteuerelement (Data) (S. 402)
340
DisabledPicture- Eigenschaft
DisabledPicture- Eigenschaft Objekt.DisabledPicture As Picture Betroffene Objekte
................................................... Betro ffene Objekte CheckBox, CommandButton, OptionButton Beschreibung
................................................... Bes c hreibung
Die DisabledPicture-Eigenschaft ermöglicht es, eine Bitmap anzugeben, die den inaktiven Zustand des Steuerelements in der benutzerdefinierten Darstellung zum Ausdruck bringt.
................................................... Anwendung
Schaltflächen, Optionsfelder und Kontrollkästchen unterstützen anstelle der standardmäßigen Darstellung auch die Möglichkeit, ihre drei Zustände in Form von benutzerdefinierten Bitmaps anzuzeigen. Zur Festlegung der Bitmaps sind die Eigenschaften Picture, DownPicture und DisabledPicture zuständig. Damit ein Steuerelement die Werte dieser Eigenschaften überhaupt beachtet, muss die zur Laufzeit schreibgeschützte Style-Eigenschaft bereits beim Formularentwurf auf vbButtonGraphical (1) gesetzt werden. Die DisabledPicture-Bitmap kommt immer dann zur Anzeige, wenn das Steuerelement sichtbar und die Enabled-Eigenschaft auf False gesetzt ist. Beispiel
................................................... Beis piel
Der folgende Code entstammt einem Formular, auf dem zwei Schaltflächen Command1 und Command2 sowie ein Bildausschnitt-Steuerelement PictureClip1 platziert wurden. Er demonstriert die benutzerdefinierte Darstellung für Command2. Das Bildausschnitt-Steuerelement liefert die drei Bitmaps, die für die Unterscheidung der Anzeigezustände notwendig sind. Die Schaltfläche Command1 ist dafür da, Command2 abwechselnd zu aktivieren und zu deaktivieren (dritter Zustand): Private Sub Command1_Click() Command2.Enabled = Not Command2.Enabled End Sub
' Aktivieren/Deaktivieren
Private Sub Form_Load() Command2.Picture = PictureClip1.GraphicCell(0) Command2.DownPicture = PictureClip1.GraphicCell(1) Command2.DisabledPicture = PictureClip1.GraphicCell(2) End Sub Verwandte Eigenschaften
................................................... Verwa ndte Eigens c ha ften DownPicture, Picture
DownPicture- Eigenschaft Objekt.DownPicture As Picture Betroffene Objekte
................................................... Betro ffene Objekte CheckBox, CommandButton, OptionButton Beschreibung
................................................... Bes c hreibung
Die DownPicture-Eigenschaft ermöglicht es, eine Bitmap anzugeben, die den gedrückten Zustand des Steuerelements in der benutzerdefinierten Darstellung zum Ausdruck bringt.
3 41
Gemeinsame Eigenschaften
Anwendung
Gemeinsame Eigenschaften
Anwendung
Gemeinsame Eigenschaften
................................................... Anwendung
Schaltflächen, Optionsfelder und Kontrollkästchen unterstützen anstelle der standardmäßigen Darstellung auch die Möglichkeit, ihre drei Zustände in Form von benutzerdefinierten Bitmaps anzuzeigen. Zur Festlegung der Bitmaps sind die Eigenschaften Picture, DownPicture und DisabledPicture zuständig. Damit ein Steuerelement die Werte dieser Eigenschaften überhaupt beachtet, muss die zur Laufzeit schreibgeschützte Style-Eigenschaft bereits beim Formularentwurf auf vbButtonGraphical (1) gesetzt werden. Zur Anzeige der DownPicture-Bitmap kommt es, wenn das Steuerelement sichtbar ist, die Enabled-Eigenschaft auf True gesetzt ist und der Benutzer mit der Maus oder der Tastatur das ClickEreignis des Steuerelements auslöst. Beispiel
................................................... Beis piel
Vgl. das Beispiel in »DisabledPicture-Eigenschaft« (S. 341). Verwandte Eigenschaften
................................................... Verwa ndte Eigens c ha ften DisabledPicture, Picture
DragIcon- Eigenschaft Objekt.DragIcon As Picture Betroffene Objekte
................................................... Betro ffene Objekte Die meisten Steuerelementtypen Beschreibung
................................................... Bes c hreibung
Die DragIcon-Eigenschaft spezifiziert die Mauszeigerform, die zur Anzeige kommt, wenn das Steuerelement einer Drag&Drop-Operation unterworfen ist. Hat die Eigenschaft den Wert Nothing, bleibt die aktuelle Mauszeigerform unverändert, vielmehr erscheint aber der Umriss des gezogenen Steuerelements als visuelles Feedback. Anwendung
................................................... Anwendung
Als Werte für diese Eigenschaft sind nur Verweise auf Bitmaps geeignet, die in einem der Symbolformate CUR oder ICO vorliegen – insbesondere lassen sich also weder gewöhnliche Bilder (BMP, JPG, WMF etc.) noch animierte Cursorformen (ANI) als Mauszeigerform setzen. Beispiel
................................................... Beis piel
Text1.DragIcon = LoadPicture ("MyDragIcon.Ico") Verwandte Eigenschaften
................................................... Verwa ndte Eigens c ha ften MouseIcon, MousePointer Verwandte Themen
................................................... Verwandte Them en
OLE-Drag&Drop (S. 501); Transparenz und Drag&Drop (S. 606)
342
DragMode- Eigenschaft
DragMode- Eigenschaft Objekt.DragMode As Integer Betroffene Objekte
................................................... Betro ffene Objekte Die meisten vordefinierten Steuerelementtypen Beschreibung
................................................... Bes c hreibung
Konstante
Beschreibung
vbManual (0)
Das Steuerelement geht auf Drag&Drop-Operationen nur ein, wenn es explizit durch Aufruf der Methode Drag dazu veranlasst wird (Voreinstellung).
vbAutomatic (1)
Das Steuerelement interpretiert das Drücken der linken Maustaste als Beginn einer Drag&Drop-Operation. Zudem kann eine Drag&Drop-Operation auch explizit durch Aufruf der Methode Drag eingeleitet werden.
Anwendung
................................................... Anwendung
Drag&Drop-Operationen stellen eine auf die Grenzen eines Programms beschränkte Form der grafisch orientierten Befehlsübermittlung zwischen Steuerelementen untereinander sowie von Steuerelement zu Formular dar. Im automatischen Modus beginnt der Benutzer eine Drag&Drop-Operation, indem er mit der linken Maustaste auf ein Steuerelement (Quellobjekt) klickt und dieses mit gehaltener Maustaste über ein anderes Steuerelement und/oder Formular zieht und ablegt (Zielobjekt). Das Loslassen der Maustaste steht für das Ablegen des Steuerelements und löst aufseiten des Zielobjekts das DragDrop-Ereignis aus, dessen Behandlung die mit der Operation verbundene Wirkung hervorruft. Wie die Wirkung im Einzelnen aussieht, dafür gibt es keine Vorschriften. Da das Zielobjekt eine echte Referenz auf das Quellobjekt erhält, kann sie von der einfachen Positionsänderung des gezogenen Steuerelements über die Befehlsauswahl bis hin zur Vernichtung des Quellobjekts (»in einem Papierkorb verschieben«) reichen. Beispiel
................................................... Beis piel
Der folgende Code zeigt die Implementation einer manuellen Drag&Drop-Operation. Der Benutzer kann eine auf dem Formular befindliche Schaltfläche Command1 mit der rechten Maustaste verschieben und mit der linken Maustaste weiterhin wie gewohnt das Click-Ereignis auslösen. Private StartX As Single Private StartY As Single Private Sub Command1_MouseDown(Button As Integer, Shift As Integer, _ X As Single, Y As Single) If Button = vbRightButton Then StartX = X StartY = Y Command1.Drag vbBeginDrag End If End Sub
3 43
Gemeinsame Eigenschaften
Die DragMode-Eigenschaft legt fest, ob ein Steuerelement Drag&Drop-Operationen automatisch eingehen kann oder durch Aufruf der Drag-Methode dazu »überredet« werden muss. Die folgende Tabelle gibt einen Überblick über die möglichen Werte der Eigenschaft und deren Wirkung:
Gemeinsame Eigenschaften
Private Sub Form_DragDrop(Source As Control, X As Single, Y As Single) Source.Left = X – StartX Source.Top = Y – StartY End Sub Private Sub Form_Load() Command1.DragMode = 0 End Sub Verwandte Ereignisse
Gemeinsame Eigenschaften
................................................... Verwandte Ereignis s e DragDrop-Ereignis, DragOver-Ereignis Verwandte Themen
................................................... Verwandte Them en
Transparenz und Drag&Drop (S. 606)
DrawMode- Eigenschaft Objekt.DrawMode As Integer Betroffene Objekte
................................................... Betro ffene Objekte Form, Line, PictureBox, Printer, PropertyPage, RptLine, RptShape, Shape, UserControl, UserDocument Beschreibung
................................................... Bes c hreibung
Die DrawMode-Eigenschaft legt die Rasteroperation fest, die das Objekt für das Zeichnen von Linien und Füllbereichen anwendet. Der Aufzählungstyp DrawModeConstants definiert folgende Konstanten dafür: Konstante (Wert)
Beschreibung
vbBlackness (1)
Schwarzintensität
vbNotMergePen (2)
Stift mischen invers (invers zu 15)
vbMaskNotPen (3)
inversen Stift maskieren; Kombination der Farben, die der Hintergrund mit der invertierten Stiftfarbe gemeinsam hat
vbNotCopyPen (4)
Stift kopieren invers (invers zu 13)
vbMaskPenNot (5)
Stift und inverse Anzeige maskieren; Kombination der Farben, die der Stift mit der invertierten Anzeigefarbe gemeinsam hat
vbInvert (6)
Anzeigefarbe invers
vbXorPen (7)
Stift XOR Anzeigefarbe; Kombination der Farben, die im Stift und in der Anzeigefarbe, aber nicht in beiden vorhanden sind
vbNotMaskPen (8)
Stift maskieren invers (invers zu 9)
vbMaskPen (9)
Stift maskieren; Kombination der Farben, die Stift und Anzeigefarbe gemeinsam haben (invers zu 10)
vbNotMaskPen (10)
Stift maskieren invers (invers zu 9)
vbNop (11)
Keine Operation; Stift zeichnet nicht
vbMergeNotPen (12)
Inversen Stift mischen – Kombination der Anzeigefarbe und der invertierten Stiftfarbe
344
DrawMode- Eigenschaft
Konstante (Wert)
Beschreibung
vbCopyPen (13)
Stift kopieren (Voreinstellung); Stiftfarbe ist durch Eigenschaft ForeColor gegeben (invers zu 4)
vbMergePenNot (14)
Stift und inverse Anzeige mischen; Kombination der Stiftfarbe und der invertierten Anzeigefarbe
vbMergePen (15)
Stift mischen; Kombination aus Stift- und Anzeigefarbe (invers zu 2)
vbWhiteNess (16)
Stift zeichnet mit leuchtendem Weiß
................................................... Anwendung
Die DrawMode-Eigenschaft ist etwas für spezielle Effekte. So kann eine einfache Änderung der DrawMode-Eigenschaft recht beachtliche Veränderungen in der Ausgabe hervorrufen. Der voreingestellte Wert vbCopyPen (13) entspricht dem, was man sich landläufig unter »Zeichnen mit einem deckenden Stift in einer bestimmten Farbe« vorstellt. Die anderen Rasteroperationen verwenden teilweise eine feste oder invertierte Stiftfarbe und/oder mischen bzw. invertieren die Stiftfarbe mit der Anzeigefarbe (das ist die bestehende Farbe des Bildpunkts) – das Ergebnis ist also nicht in jedem Fall vorhersehbar, sondern hängt von den farblichen Gegebenheiten und der Logik der Rasteroperation ab. Auf Rang zwei in der Beliebtheitsskala der Rasteroperationen, gleich nach vbCopyPen, rangiert vbXorPen (7). Diese Rasteroperation ist ihre eigene Umkehroperation, das heißt, in diesem Modus versetzt ein erneuter Aufruf der genau gleichen Grafikoperation den Ausgabebereich in den Zustand vor dem ersten Aufruf zurück. Ausgaben lassen sich auf diese Weise zurücknehmen, ohne darunter gelegene Elemente zu zerstören. Diese Technik wird beispielsweise für die Programmierung mit »Gummibändern« verwendet (vgl. »Gummiband – Bereiche interaktiv auswählen«, S. 492). Beispiel
................................................... Beis piel
Das Beispielprojekt DrawModeDemo zeigt die Auswirkungen der DrawMode-Eigenschaft auf das Zeichnen von Linien und Füllbereichen sowie für Überlappungen mit bereits gezeichneten Elementen. Die Paint-Routine des Formulars zeichnet den linken Kreis mit der voreingestellten Rasteroperation vbCopyPen (13) und den rechten mit dem jeweils aktuellen Rasteroperation, die sich durch einen Klick auf den Formularbereich weiterschalten lässt. Dim DrawMode1 As Integer Private Sub Form_Click() DrawMode1 = (DrawMode1 Mod 16) + 1 ' Weiterschalten Refresh End Sub Private Sub Form_Load() Height = Width FillStyle = 0 DrawWidth = 15 DrawMode1 = 13 DrawMode1 = 1 AutoRedraw = False End Sub Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single) Dim p, r, g, b
3 45
Gemeinsame Eigenschaften
Anwendung
Gemeinsame Eigenschaften
Gemeinsame Eigenschaften
p = Point(X, Y) r = (p And &HFF) g = (p \ &H100 And &HFF) b = p \ &H10000 Caption = "DrawMode: " & DrawMode1 & ", Farbe unter Maus: (" _ & r & "," & g & "," & b & ")" End Sub Private Sub Form_Paint() ' Linker Kreis wird mit "normalen" Einstellungen gezeichnet Form_MouseMove 0, 0, 0, 0 Cls DrawMode = 13 FillColor = RGB(200, 255, 255) ForeColor = RGB(255, 0, 255) Me.Circle (ScaleHeight * 0.52, ScaleHeight * 0.5), Height * 0.4 ' Rechter Kreis wird mit verändertem Drawmode gezeichnet DrawMode = DrawMode1 FillColor = RGB(123, 145, 17) ForeColor = RGB(145, 17, 123) Me.Circle (ScaleHeight * 0.72, ScaleHeight * 0.5), Height * 0.4 End Sub
Verwandte Eigenschaften
................................................... Verwa ndte Eigens c ha ften BorderStyle, BorderWidth, DrawStyle, DrawWidth Verwandte Themen
................................................... Verwandte Them en
Gummiband – Bereiche interaktiv auswählen (S. 492)
DrawStyle- Eigenschaft Objekt.DrawStyle As Integer Betroffene Objekte
................................................... Betro ffene Objekte Form, PictureBox, Printer, PropertyPage, UserControl, UserDocument
346
Enabled- Eigenschaft
Beschreibung
................................................... Bes c hreibung
Die DrawStyle-Eigenschaft bestimmt einen von sieben Linienstilen für die Zeichenoperationen eines Objekts. Falls für eine Grafikoperation eine Linienbreite (DrawWidth) größer als 1 eingestellt ist, ignoriert das Objekt allerdings die Werte 1 bis 4 der DrawStyle-Eigenschaft und arbeitet statt dessen mit der Voreinstellung 0. Der Aufzählungstyp DrawStyleConstants definiert eine Reihe von Konstanten für die unterschiedlichen Linienstile: Beschreibung
vbSolid (0)
Durchgezogen (Voreinstellung)
vbDash (1)
Gestrichelt
vbDot (2)
Gepunktet
vbDashDot (3)
Folge aus Strichen und Punkten
vbDashDotDot (4)
Folge aus Strichen und zwei Punkten
vbInvisible (5)
Transparent
vbInsideSolid (6)
Innen ausgefüllt
Anwendung
................................................... Anwendung
Aufgrund ihrer Begrenztheit auf Linienbreiten von 1 Bildpunkt lässt sich mit der DrawStyleEigenschaft nicht allzu viel anfangen. Der Unterschied zwischen vbSolid und vbInsideSolid macht sich nur bemerkbar, wenn eine geschlossene Figur, beispielsweise ein Rechteck oder ein Kreis, gezeichnet wird. Für gewöhnliche Linien gibt es keinen Unterschied. In geschlossen Figuren sorgt vbInsideSolid dafür, dass die Linie vollständig innerhalb des angegebenen Bereichs (Höhe, Breite, doppelter Radius) verbleibt, so dass für optimierte Zeichenroutinen (Paint) jeweils mit den Bereichsgrenzen gerechnet werden kann. Verwandte Eigenschaften
................................................... Verwa ndte Eigens c ha ften BorderStyle, BorderWidth, DrawMode, DrawWidth Verwandte Themen
................................................... Verwandte Them en
Gummiband – Bereiche interaktiv auswählen (S. 492)
Enabled- Eigenschaft Objekt.Enabled As Boolean Betroffene Objekte
................................................... Betro ffene Objekte Formulare sowie die meisten vordefinierten Steuerelementtypen Beschreibung
................................................... Bes c hreibung
Die Enabled-Eigenschaft drückt den Aktivierungszustand von Objekt aus. Lautet ihr Wert False, ist das Objekt deaktiviert, das heißt, es erhält die Darstellung »deaktiviert« und nimmt den Fokus nicht mehr entgegen.
3 47
Gemeinsame Eigenschaften
Konstante (Wert)
Gemeinsame Eigenschaften
Anwendung
................................................... Anwendung
Die Enabled-Eigenschaft implementiert einen recht brauchbaren Mechanismus, der es ermöglicht, die Benutzerinteraktionen in Abhängigkeit vom jeweiligen Programmzustand auf die sinnvollen bzw. erlaubten Operationen einzuschränken. Da die meisten Steuerelemente ihre Deaktivierung auch durch eine entsprechend veränderte Darstellung anzeigen, erhält der Benutzer gleichzeitig ein visuelles Feedback für seinen Handlungspielraum.
FillColor- Eigenschaft
Gemeinsame Eigenschaften
Objekt.FillColor As Long Betroffene Objekte
................................................... Betro ffene Objekte Form, PictureBox, Printer, PropertyPage, Shape, UserControl, UserDocument Beschreibung
................................................... Bes c hreibung
Die Eigenschaft FillColor ist ein Farbwert vom Typ Long, der die Füllfarbe für den Füllbereich beim Zeichnen geschlossener Figuren festlegt. Voreinstellung für diese Eigenschaft ist die Farbe »Schwarz« mit dem Wert 0. Zur Auswahl stehen neben den Systemfarben auch beliebige andere Farben, die sich entweder zur Entwurfszeit aus der Palette wählen oder zur Laufzeit über die Funktion RGB komponentenweise zu einem Farbwert komponieren lassen. Die Funktion QBColor liefert den Farbwert einer der 16 Grundfarben. Zudem definiert der Aufzählungstyp SystemColorConstants Konstanten für die Systemfarben (vgl. »BackColor-Eigenschaft und ForeColor-Eigenschaft«, S. 330). Voraussetzung für die Anzeige der Füllfarbe ist allerdings, dass die Eigenschaft FillStyle einen Wert ungleich vbTransparent (1) aufweist. Anwendung
................................................... Anwendung
FillColor spielt insbesondere eine Rolle, wenn Sie mit grafischen Methoden wie Circle oder Line arbeiten. Beim Zeichnen ausgefüllter Rechtecke können Sie wählen, ob Sie den Füllbereich mit der Vordergrundfarbe (ForeColor) oder mit der Füllfarbe (FillColor) und dem Füllmuster (FillStyle) zeichnen wollen, indem Sie den optionalen Parameter BF oder nur B angeben. Beispiel
................................................... Beis piel
Der folgende Code zeichnet ein gefülltes Rechteck mit taubenblauem diagonal gestreiften Füllmuster und schwarzem verdickten Rand. DrawWidth = 3 ForeColor = vbBlack FillColor = RGB(24, 135, 212) FillStyle = vbDownwardDiagonal Line (100, 100)-(1000, 1000), , B Verwandte Eigenschaften
................................................... Verwa ndte Eigens c ha ften BackColor, FillStyle, ForeColor
348
FillStyle- Eigenschaft
FillStyle- Eigenschaft Objekt.FillStyle As Integer Betroffene Objekte
................................................... Betro ffene Objekte Form, PictureBox, Printer, PropertyPage, Shape, UserControl, UserDocument Beschreibung
................................................... Bes c hreibung
Konstante (Wert)
Beschreibung
vbFSSolid (0)
Ausgefüllt
vbFSTransparent (1)
Transparent (Voreinstellung)
vbHorizontalLine (2)
Horizontale Schraffur
vbVerticalLine (3)
Vertikale Schraffur
vbUpwardDiagonal (4)
Aufwärtsdiagonale Schraffur
vbDownwardDiagonal (5)
Abwärtsdiagonale Schraffur
vbCross (6)
Kreuzschraffur
vbDiagonalCross (7)
Diagonale Kreuzschraffur
Anwendung
................................................... Anwendung
Laut Voreinstellung wird die Füllfarbe ignoriert. FillStyle muss daher auf einen Wert ungleich 1 gesetzt werden, damit die Füllfarbe im Füllbereich sichtbar wird. Beispiel
................................................... Beis piel
Siehe das Beispiel zu »FillColor-Eigenschaft« (S. 348). Verwandte Befehle
................................................... Verwa ndte Befehle
BackColor, FillColor, ForeColor
Font- Eigenschaft Objekt.Font As Font Betroffene Objekte
................................................... Betro ffene Objekte Adodc, CheckBoxm, ComboBox, CommandButton, Data, DataCombo, DataGrid, DataList, DataReport, DirListBox, DriveListBox, FileListBox, Form, Frame, Label, MaskEdBox, MonthView, ImageCombo, ListBox, ListView, OptionButton, PictureBox, Printer, PropertyPage, RemoteData, RichTextBox, RptFunction, RptLabel, RptTextBox, Shape, SSTab, StatusBar, TabStrip, TextBox, TreeView, UserControl, UserDocument Beschreibung
................................................... Bes c hreibung
Die Font-Eigenschaft enthält eine Referenz auf ein Font-Objekt, das die Schrift sowie die Schriftattribute für die Textausgabe genauer spezifiziert. Font-Objekte
3 49
Gemeinsame Eigenschaften
Die Eigenschaft Fillstyle legt ein Füllmuster für Füllbereiche fest, die mit Füllfarbe (FillColor) gezeichnet werden. Der Aufzählungstyp FillStyleConstants definiert folgende Konstanten für die verschiedenen Füllmuster:
Gemeinsame Eigenschaften
Anwendung
................................................... Anwendung
Gemeinsame Eigenschaften
Um die für ein Steuerelement geltende Schrift zu verändern, können Sie über die Font-Eigenschaft auf das aktuell geltende Font-Objekt zugreifen und dessen Eigenschaften neu setzen. Es besteht aber auch die Möglichkeit, die Schriftattribute mehrerer Steuerelemente über ein einzelnes Font-Objekt zu steuern, indem Sie die Font-Eigenschaften dieser Steuerelemente auf das gleiche Font-Objekt setzen. Dazu vereinbaren Sie entweder auf Modulebene ein neues Font-Objekt: Private fntGemeinsam As New StdFont ... fntGemeinsam.Name = "Arial Black" fntGemeinsam.Size = 14 ...
oder nutzen ein bereits bestehendes Font-Objekt, das zu einem der Steuerelemente gehört. Dann setzen Sie die Font-Eigenschaften der Steuerelemente auf dieses Objekt. Set Text1.Font = fntGemeinsam Set Text2.Font = fntGemeinsam Set Text3.Font = fntGemeinsam
Änderungen an fntGemeinsam wirken sich nun auf alle betroffenen Steuerelemente aus. Hinweis
................................................... Hinweis Die Schriftattribute »Fett«, »Kursiv«, »Durchgestrichen« oder »Unterstrichen« lassen sich sowohl über die Eigenschaften FontBold, FontItalic, FontStrikethru, FontUnderline als auch über die Eigenschaften Bold, Italic, Strikethru und Underline des Font-Objekts setzen. In gleicher Weise decken sich die Eigenschaften FontName und FontSize mit den Eigenschaften Name und Size des Font-Objekts. Beispiel
................................................... Beis piel
Text1.Font.Italic = True
ForeColor- Eigenschaft Siehe »BackColor-Eigenschaft und ForeColor-Eigenschaft«, S. 330.
HasDC- Eigenschaft Objekt.HasDC As Boolean Betroffene Objekte
................................................... Betro ffene Objekte Form, MonthView, PictureBox, PropertyPage, UserControl, UserDocument Beschreibung
................................................... Bes c hreibung
Die HasDC-Eigenschaft spielt nur im Zusammenhang mit Aufrufen von Funktionen der Win32API eine Rolle. Sie beschreibt, ob Objekt über einen eigenen Gerätekontext verfügt (True) oder nicht (False). Anwendung
................................................... Anwendung
Falls ein Objekt keinen eigenen Gerätekontext besitzt, wie das beispielsweise bei fensterlosen Steuerelementen (vgl. Windowless-Eigenschaft) der Fall ist (unabhängig vom tatsächlichen Wert
350
hDC- Eigenschaft
der HasDC-Eigenschaft), benutzt es für seine Ausgaben den Gerätekontext des übergeordneten Objekts (Parent). Das kann dann zu Schwierigkeiten bis hin zu hässlichen Abstürzen bei der Ausführung von Win32-API-Funktionen führen.
hDC- Eigenschaft Objekt.hDC As Long Betroffene Objekte
................................................... Betro ffene Objekte CommonDialog, Form, MonthView, PictureBox, PropertyPage, UserControl, UserDocument
................................................... Bes c hreibung
Die hDC-Eigenschaft spielt nur im Zusammenhang mit Aufrufen von Funktionen der Win32-API eine Rolle. Ihr Wert ist eine Nummer, die den Gerätekontext von Objekt identifiziert. Eine Ausnahme bildet das CommonDialog-Steuerelement: Hier spezifiziert hDC den Gerätekontext des ausgewählten Druckers. Anwendung
................................................... Anwendung
Ein Gerätekontext ist eine Windows-Datenstruktur, die den Zustand eines Ausgabegerätes (Drucker, Fenster, Bildschirm) vollständig beschreibt. Er enthält die aktuellen Einstellungen über die verwendeten Farben, den aktuellen Stift, die Schrift usw. und im Falle des Bildschirmkontextes auch noch eine Bitmap (Image-Eigenschaft), die den aktuellen Zustand der Ausgabe repräsentiert und (bei gesetzter AutoRedraw-Eigenschaft) für die Aktualisierung der Anzeige benutzt wird. Falls ein Objekt keinen eigenen Gerätekontext besitzt (HasDC-Eigenschaft), wie das beispielsweise bei fensterlosen Steuerelementen (vgl. Windowless-Eigenschaft) der Fall ist (unabhängig vom tatsächlichen Wert der HasDC-Eigenschaft), benutzt es für seine Ausgaben den Gerätekontext des übergeordneten Objekts (Parent). Das kann jedoch zu Schwierigkeiten bis hin zu hässlichen Abstürzen bei der Ausführung von Win32-API-Funktionen führen.
Height- Eigenschaft und Width- Eigenschaft Objekt.Height As Single Objekt.Width As Single Betroffene Objekte
................................................... Betro ffene Objekte Alle Formulararten, Komponenten und Steuerelemente mit sichtbarer Darstellung Beschreibung
................................................... Bes c hreibung
Die Eigenschaften Height und Width beschreiben die vertikale und horizontale Abmessung eines Objekts. Bei Form-Objekten, Printer-Objekten und dem Screen-Objekt werden die Werte dieser Eigenschaften standardmäßig in der Maßeinheit Twips ausgedrückt. Bei Steuerelementen und Komponenten, die in einem Container (in einem Formular, Bildfeld, Rahmen etc.) platziert wurden, sind die Abmessungen immer auf die Maßeinheiten bezogen, wie sie aktuell im Koordinatensystem des Containers gelten. Anwendung
................................................... Anwendung
Die Abmessungen eines Steuerelements oder eines Formulars lassen sich nicht nur zur Entwurfszeit, sondern auch zur Laufzeit dynamisch anpassen, indem Sie die Eigenschaften Height und/oder Width auf geeignete Werte setzen. Um bei der Anpassung von Steuerelementen keine
3 51
Gemeinsame Eigenschaften
Beschreibung
Gemeinsame Eigenschaften
Gemeinsame Eigenschaften
Überraschungen zu erleben, sollten Sie allerdings im Hinterkopf behalten, dass sich die Maßeinheiten in einem Containerobjekt jederzeit durch einen Wechsel des Koordinatensystems (ScaleMode) oder durch eine Skalierung der Koordinatenachsen (ScaleHeight und ScaleWidth) ändern können. Bei Formularobjekten, deren BorderStyle-Eigenschaft auf vbSizable (2) gesetzt wurde, können sich Wertänderungen für Height und Width auch aufgrund von Benutzerinteraktionen ergeben. Jede Wertänderung dieser Eigenschaften, gleich ob sie vom Programmcode aus oder auf Veranlassung des Benutzers geschehen ist, generiert ein Resize-Ereignis. Es ist wichtig, den Unterschied zwischen den Eigenschaften Height und Width und den Eigenschaften ScaleHeight und ScaleWidth zu begreifen. Erstere sind auf die äußeren Abmessungen des Darstellungsbereichs eines Objekts bezogen, während letztere die Abmessungen des ClientBereichs, also die »inneren« Abmessungen des Objekts, ausdrücken. Mehr noch: Wertänderungen bei Height und Width ändern die äußeren und inneren Abmessungen des Objekts, Wertänderungen bei ScaleHeight und ScaleWidth dagegen nur die Maßgrundlage und nicht die Abmessungen. Beispielsweise gibt ein neuer Wert für die ScaleHeight-Eigenschaft eines Formulars an, in wie viele Einheiten der Client-Bereich fortan in vertikaler Richtung unterteilt sein soll; da dies die Metrik ändert, ändert sich auch die Eigenschaft ScaleMode implizit auf vbUser (0). Anmerkungen
................................................... Anm erkungen Beim Bildausschnittobjekt (PictureClip) sind die Eigenschaften Height und Width schreibgeschützt. Da das Bildausschnittobjekt selbst keine sichtbare Darstellung hat, drücken diese Eigenschaften die Abmessungen der dem Objekt zugeordneten Bitmap (in Bildpunkten) aus. Während die Abmessungen des PictureBox-Objekts (wie alle Steuerelemente) in den Maßeinheiten des Containerobjekts gehalten sind, liegt den Eigenschaften Height und Width eines zugewiesenen Picture-Objekts immer die Maßeinheit HiMetric (hundertstel Millimeter) zugrunde. Um ein PictureBox-Objekt an die Abmessungen eines Picture-Objekts anzupassen, müssen die Maßeinheiten mittels der Methoden ScaleX und ScaleY umgerechnet werden. Picture1.Width = ScaleX(Picture1.Picture.Height, vbHimetric, vbUser)
Für das Screen-Objekt sind die beiden Eigenschaften schreibgeschützt. Bei DriveListBox- und ComboBox-Steuerelementen, deren Style-Eigenschaft auf vbComboDropdown (0) oder auf vbComboDropdownList (2) gesetzt wurde, wird die Height-Eigenschaft vom System errechnet und ist zur Laufzeit schreibgeschützt. Beispiel
................................................... Beis piel
Häufig ergibt sich das Problem, den Client-Bereich eines Formulars auf eine bestimmte Größe trimmen zu müssen, etwa um ein Bild einzupassen. Auf direktem Wege ist das nicht möglich, da ScaleHeight und ScaleWidth ja keine Abmessungen, sondern nur Maßeinheiten ändern. Die Lösung ist simpel – wenn man sie kennt. Angenommen die Variablen sNeueHöhe und sNeuBreite enthalten die neuen Abmessungen des Client-Bereichs, dann leistet der folgende Code das Gewünschte: ScaleMode = vbTwips ' gleiche Maßeinheiten für innen und außen Height = Height – ScaleHeight + sNeueHöhe Width = Width – ScaleWidth + sNeueBreite Verwandte Eigenschaften
................................................... Verwa ndte Eigens c ha ften ScaleHeight, ScaleMode, ScaleWidth
352
HelpContextID- Eigenschaft und WhatsThisHelpID- Eigenschaft
HelpContextID- Eigenschaft und WhatsThisHelpID- Eigenschaft Objekt.HelpContextID As Long Objekt.WhatsThisHelpID As Long Betroffene Objekte
................................................... Betro ffene Objekte Formulare, Komponenten und Steuerelemente mit sichtbarer Darstellung Beschreibung
................................................... Bes c hreibung
Anwendung
................................................... Anwendung
Visual Basic unterstützt vier verschiedene Techniken, wie Sie Benutzern Ihrer Anwendungen Hilfe zukommen lassen können: 1. QuickInfo – blendet den Wert der Eigenschaft ToolTipText eines Steuerelements in einem kleinen Fensterchen ein, wenn die Maus in den Bereich des Steuerelements gelangt und dort ca. eine halbe Sekunde verweilt. 2. Statusanzeige – die Behandlungsroutine der MouseMove-Methode gibt bei Eintritt der Maus in den Steuerelementbereich einen Hilfetext in der Statusleiste (StatusBar) aus. 3. Direkthilfe – bei aktiviertem Direkthilfemodus blendet der nächste Klick auf ein Steuerelement ein Popup-Hilfefenster mit einem Hilfetext zu diesem Steuerelement ein. Der über die Eigenschaft WhatsThisHelp indizierte Hilfetext entstammt der Datei App.HelpFile. Damit sich die Direkthilfe überhaupt aktivieren lässt, muss die WhatsThisHelp-Eigenschaft des Containerformulars auf True gesetzt sein. Die Aktivierung des Direkthilfemodus kann dann durch Aufruf der WhatsThisMode-Methode des Formulars geschehen oder durch einen Klick auf die Hilfeschaltfläche in der Titelleiste des Formulars (dazu muss die Eigenschaft WhatsThisButton des Formulars auf True gesetzt sein, und es dürfen keine MINIMIEREN-/MAXIMIEREN-Schaltflächen vorhanden sein). 4. Kontextbezogene Hilfe – die Hilfetaste (F1) aktiviert das Windows-Hilfesystem und bringt darin den über die Eigenschaft HelpContextID indizierten Hilfetext aus der Datei App.HelpFile zur Anzeige. Verwandte Eigenschaften
................................................... Verwa ndte Eigens c ha ften MaxButton, MinButton, ToolTipText, WhatsThisHelp, WhatsThisButton
3 53
Gemeinsame Eigenschaften
Die Eigenschaften HelpContextID und WhatsThisHelpID ordnen einem Objekt Hilfethemen in der zur Anwendung gehörigen Hilfedatei (App.HelpFile) zu. HelpContextID spezifiziert den Index des Hilfethemas, wenn der Benutzer die kontextbezogene Hilfe (Windows-Hilfesystem) über die Taste (F1) aktiviert. WhatsThisHelpID spezifiziert den Index des Hilfethemas, wenn der Benutzer die Direkthilfe zu einem Steuerelement anfordert. Hat HelpContextID den Wert 0 (Voreinstellung), bedeutet das, dass für das Objekt kein Thema in der Allgemeinhilfe definiert ist. In diesem Fall versucht das Laufzeitsystem, das Hilfethema des Containerobjekts aufzurufen. Falls bis zum obersten Container alle HelpContextID-Eigenschaften den Wert 0 haben, kommt die SUCHEN-Funktion des Hilfesystems zum Aufruf. Hat WhatsThisHelpID den Wert 0 (Voreinstellung), bedeutet das, dass keine Direkthilfe für das Objekt verfügbar ist. In diesem Fall wird der Direkthilfemodus beendet, ohne dass etwas passiert.
Gemeinsame Eigenschaften
hWnd- Eigenschaft Objekt.hWnd As Long Betroffene Objekte
................................................... Betro ffene Objekte Formulare, Komponenten und Steuerelemente mit sichtbarer Darstellung, nicht jedoch OLE Beschreibung
................................................... Bes c hreibung
Gemeinsame Eigenschaften
Die zur Laufzeit schreibgeschützte hWnd-Eigenschaft enthält die Zugriffsnummer für das Fenster des Objekts – den sog. Fenster-Handle. Anwendung
................................................... Anwendung
Der Fenster-Handle eines Steuerelements spielt für die Visual-Basic-Programmierung nur dann eine Rolle, wenn Funktionen der Win32-API zum Aufruf kommen. Warnung
................................................... Wa rnung
Der Wert dieser Eigenschaft kann sich zur Laufzeit ändern (etwa im Zuge eines Unload-Aufrufs). Beispiel
................................................... Beis piel
Vgl. das Beispiel zu »Printer-Objekt« (S. 284). Verwandte Themen
................................................... Verwandte Them en
Routinen aus DLLs und der Windows-API einsetzen (S. 185)
Image- Eigenschaft Image As Picture Betroffene Objekte
................................................... Betro ffene Objekte Band, Button, Form, Node, PictureBox, PropertyPage, Tab, UserControl, UserDocument Beschreibung
................................................... Bes c hreibung
Die Image-Eigenschaft stellt die Beschreibung einer beständigen im Speicher gelegenen Bitmap dar. Bei Objekten mit eigenem Gerätekontext (hDC) handelt es sich dabei um die beständige Bitmap im Gerätekontext. Die Image-Eigenschaft ist ihrerseits ein Objekt und besitzt fünf Eigenschaften sowie eine Methode: Eigenschaft
Beschreibung
Handle
Long-Wert, der die zugeordnete Bitmap identifiziert
Width, Height
Long-Werte, die die Breite und Höhe der Bitmap in der Einheit HiMetric (0,01 mm) ausdrücken. Die Werte müssen meist mit ScaleX bzw. ScaleY umgerechnet werden.
hPal
Long-Wert, der einen Handle auf die Palette darstellt, die für die Bitmap verwendet wird (nur für Aufrufe der Win32-API interessant)
354
Image- Eigenschaft
Eigenschaft
Beschreibung
Type
Integer-Wert, der das Grafikformat der beschriebenen Bitmap wiedergibt. Die möglichen Werte sind: vbPicTypeNone (0) Bild ist leer vbPicTypeBitMap (1)
Bild hat BMP-Format
vbPicTypeMetaFile (2)
Bild hat WMF-Format
vbPicTypeIcon (3)
Bild hat ICO-Format
vbPicTypeEMetaFile (4)
Bild hat EMF-Format
Sub Objekt.Image.Render(hDC As Long, x As Long, y As Long, cx As Long,_ cy As Long, xSrc As Long, ySrc As Long, cxSrc As Long, _ cySrc As Long, prcWBounds As Any) Die vielen Parameter sind alle erforderlich. In hDC muss der Gerätekontext des Ausgabebereichs übergeben werden (wenn das Zielobjekt keinen eigenen hat, dann der des Containerobjekts). Die Parameter x, y, cx, cy beschreiben die Position und Abmessungen des Bildes im Ausgabebereich bezogen auf das darin geltende Koordinatensystem. Die Parameter xSrc, ySrc, cxSrc, cySrc beschreiben den zu zeichnenden Bildausschnitt (Position und Abmessungen) der über Handle zugänglichen Quell-Bitmap – als Koordinatensystem gilt hier HiMetric. Für den Parameter prcWBounds ist der Wert 0 anzugeben, wenn die Bitmap nicht im WMF-Format vorliegt – ansonsten ein Wert des Typs RECTL (eine aus vier Long-Werten bestehende Struktur, die den linken oberen und rechten unteren Punkt beschreibt), der die Standardabmessungen für die Vektorgrafik definiert. Anwendung
................................................... Anwendung
Die Image-Eigenschaft ermöglicht den Umgang mit Bildern, die in einem Gerätekontext repräsentiert sind. Bei Formularen entscheidet die AutoRedraw-Eigenschaft darüber, ob die über die ImageEigenschaft beschriebene Bitmap Ziel der Grafikoperationen Circle, Pset, Line und Print ist und für die Auffrischung des Client-Bereichs herangezogen wird oder ob die Paint-Routine die Ausgaben an der Bitmap des Gerätekontextes vorbei direkt in den Client-Bereich zeichnet. Die beständige Bitmap des Gerätekontextes existiert in jedem Fall, unabhängig vom Wert der AutoRedrawEigenschaft, nur dass sie im einen Fall sowohl die über die Picture-Eigenschaft bereitgestellte Bitmap enthält als auch Grafikausgaben und im anderen Fall nur die Bitmap. Beispiel
................................................... Beis piel
Der genannte Unterschied lässt sich leicht nachweisen, indem man die Picture-Eigenschaft des Formulars auf eine Bitmap setzt und etwa einen Kreis darüber zeichnet. Da es möglich ist, die beständige Bitmap des Gerätekontextes über die Image-Eigenschaft auszulesen, kann man sie beispielsweise der Picture-Eigenschaft eines Bildfelds als Wert zuweisen. Im ersten Fall (AutoRedraw = True) sieht man dann die Bitmap samt Kreis und im andern Fall nur den Kreis. Private Sub Form_Click() Picture = LoadPicture("Test.bmp") Circle (400, 400), 400 Refresh Picture1 = Image ' Bitmap aus Gerätekontext in Bildfeld kopieren End Sub
3 55
Gemeinsame Eigenschaften
Die Methode trägt den Bezeichner Render und hat folgenden Prototyp:
Gemeinsame Eigenschaften
Index- Eigenschaft Objektarray(Index As Integer) As Objekt Beschreibung
Gemeinsame Eigenschaften
................................................... Bes c hreibung
Bei der Index-Eigenschaft handelt es sich um eine reine Entwurfseigenschaft, die zur Laufzeit nicht als Eigenschaft, sondern gegebenenfalls als Array-Index in Erscheinung tritt. Bleibt im Eigenschaftsfenster das Feld für ihren Wert leer, legt der Entwurfseditor den unter der Eigenschaft Name spezifizierten Bezeichner für das Objekt fest, ohne die Index-Eigenschaft weiter zu beachten. Trägt man dagegen eine (nicht negative) Ganzzahl als Wert für die Index-Eigenschaft ein, legt der Entwurfseditor unter dem in der Eigenschaft Name spezifizierten Bezeichner ein dynamisches Steuerelemente-Array an, dem sich beliebig viele Steuerelemente gleichen Typs zur Entwurfszeit, aber auch noch zur Laufzeit (via Load) hinzufügen lassen. Anwendung
................................................... Anwendung
Der Entwurfseditor schlägt die Einrichtung eines Steuerelemente-Arrays von sich aus vor, wenn Sie ein Steuerelement mit noch nicht gesetzter Index-Eigenschaft über die Zwischenablage vervielfältigen. Da er in diesem Fall die Index-Eigenschaften der im Array zusammengefassten Objekte auch pflegt, sei Ihnen diese Vervielfältigungsmethode ans Herz gelegt. Das erste Steuerelement (von dem die Kopie ausging) erhält den Index 0, das zweite den Index 1 usw. Sie können ein dynamisches Steuerelemente-Array aber auch »manuell« anlegen, indem Sie für ein bereits platziertes Steuerelement einen beliebigen Startindex eintragen und dann zur Entwurfszeit oder zur Laufzeit (mittels Load) weitere Steuerelemente gleichen Typs mit gleichem Namen in denselben Container platzieren. Obwohl die Zählung üblicherweise bei 0 beginnt, gibt es keine Vorschriften für den Startindex, er darf nur nicht negativ sein. Auch darf die Zählung Lücken aufweisen – solange zur Laufzeit (außer in der Load-Anweisung) nicht auf die fehlenden Indizes verwiesen wird. Der Einsatz von Steuerelemente-Arrays erfordert zwar ein wenig zusätzliche Logik, führt aber meist zu einem kompakteren Programmcode. Das Besondere an Steuerelemente-Arrays ist nämlich, dass eine Ereignisbehandlungsroutine immer für alle Steuerelemente des Arrays zuständig ist – ihr Name wird daher auch mit dem Array-Bezeichner gebildet. Ein zusätzlicher Parameter namens Index in der Parameterliste der Prozedur gibt Auskunft darüber, welchem Steuerelement aus dem Array das Ereignis gilt. Hinweis
................................................... Hinweis Um mit einem Steuerelemente-Array arbeiten zu können, muss mindestens ein Steuerelement des gewünschten Typs zur Entwurfszeit im Bereich des Formulars (bzw. der Komponente) platziert werden. Beispiel
................................................... Beis piel
Die folgende Routine erweitert ein bestehendes Schaltflächen-Array bei jedem Aufruf um eine weitere Schaltfläche. Ausgehend von einem Formular, auf dem eine einzelne Schaltfläche mit dem Namen Command1, der Index-Eigenschaft 1 und der Caption-Eigenschaft "Befehl 1" platziert wurde, erfolgt der Aufruf der Routine interessanterweise (jedoch, ohne dass dies dem Code etwas Besonderes verleihen würde), sobald der Benutzer auf eine der bereits existierenden Schaltflächen klickt. Private Sub Command1_Click(Index As Integer) Dim i As Integer i = Command1.UBound Load Command1(i + 1)
356
Left- Eigenschaft und Top- Eigenschaft
Command1(i + 1).Top = Command1(i).Top + Command1(i).Height Command1(i + 1).Caption = "Befehl " & (i + 1) Command1(i + 1).Visible = True End Sub
Left- Eigenschaft und Top- Eigenschaft Objekt.Left As Single Objekt.Top As Single Betroffene Objekte
Formulare, Komponenten und Steuerelemente mit sichtbarer Darstellung Beschreibung
................................................... Bes c hreibung
Die Eigenschaften Left und Top bestimmen die Position der linken oberen Ecke eines Objekts in seinem Container. Bei Form-Objekten werden die Werte dieser Eigenschaften standardmäßig in der Maßeinheit Twips ausgedrückt, wobei der Ursprung des Screen-Objekts in der linken oberen Ecke des Bildschirms liegt. Bei Steuerelementen und Komponenten, die in einem Container (in einem Formular, Bildfeld, Rahmen etc.) platziert wurden, sind die Werte als Koordinaten im aktuellen Koordinatensystem des Containerobjekts zu interpretieren (vgl. »ScaleMode-Eigenschaft«, S. 369). Anwendung
................................................... Anwendung
Die Position eines Steuerelements oder eines Formulars lässt sich nicht nur zur Entwurfszeit, sondern auch zur Laufzeit dynamisch anpassen, indem Sie die Eigenschaften Left und/oder Top auf geeignete Werte setzen. Um bei der Positionierung von Steuerelementen keine Überraschungen zu erleben, sollten Sie allerdings im Hinterkopf behalten, dass sich das Koordinatensystem in einem Containerobjekt, das über die Eigenschaft ScaleMode verfügt, jederzeit ändern kann. Bei Formularobjekten können sich Wertänderungen für Left und Top auch aufgrund von Benutzerinteraktionen ergeben. Beachten Sie auch, dass das Laufzeitsystem zur Entwurfszeit gesetzte Vorgaben für diese Eigenschaften nur berücksichtigt, wenn die Eigenschaft StartUpPosition auf vbStartUpManual (0) gesetzt wurde. Bei anderen Einstellungen positioniert Windows das Fenster entweder frei (nach der internen Logik für die Fensteranordnung) oder mittig, bezogen auf das übergeordnete Fenster oder den Bildschirm. Hinweise
................................................... Hinweis e Bereits positionierte Steuerelemente behalten bei einem Wechsel des Koordinatensystems (Skalierung oder Verschiebung) ihre physikalischen Positionen und Abmessungen bei, da die Eigenschaften Left, Top, Height und Width implizit eine umgekehrte Koordinatentransformation durchmachen. Die Eigenschaften ScaleLeft und ScaleTop haben mit der Position eines Objekts nichts zu tun, sondern mit der Position des Ursprungs des Koordinatensystems. Sie drücken die Koordinaten der linken oberen Ecke des Client-Bereichs aus – ändert man ihre Werte, bewirkt das eine Ursprungsverschiebung. Beispiel
................................................... Beis piel
Das Beispielprojekt TVTennis erinnert stark an den historischen Vorläufer. Der Ball, ein ImageSteuerelement, das mit einem Smiley-Bild initialisiert wurde, bewegt sich im Formularbereich und wird an den Wänden reflektiert. Die Bewegung passt sich an Größenänderungen des Formulars an.
3 57
Gemeinsame Eigenschaften
................................................... Betro ffene Objekte
Gemeinsame Eigenschaften
Option Explicit Private StepX As Integer Private StepY As Integer
Gemeinsame Eigenschaften
Private Sub Form_Load() StepX = 400 * Rnd StepY = 400 * Rnd End Sub Private Sub Timer1_Timer() Image1.Left = (Image1.Left + StepX) Image1.Top = (Image1.Top + StepY) If Image1.Left > ScaleWidth – Image1.Width Then StepX = -StepX Image1.Left = 2 * ScaleWidth – 2 * Image1.Width – Image1.Left End If If Image1.Left < 0 Then StepX = -StepX Image1.Left = -Image1.Left End If If Image1.Top > ScaleHeight – Image1.Height Then StepY = -StepY Image1.Top = 2 * ScaleHeight – 2 * Image1.Height – Image1.Top End If If Image1.Top < 0 Then StepY = -StepY Image1.Top = -Image1.Top End If End Sub Verwandte Eigenschaften
................................................... Verwa ndte Eigens c ha ften Height, ScaleHeight, ScaleLeft, ScaleMode, ScaleTop, ScaleWidth, Width Verwandte Themen
................................................... Verwandte Them en
Bildlauf – ein kleiner Betrachter für große Bilder (S. 545), HexView – eine schnelle Textansicht für große Dateien (S. 551)
MaskColor- Eigenschaft Objekt.MaskColor As Long Betroffene Objekte
................................................... Betro ffene Objekte CheckBox, CommandButton, ImageList, OptionButton, UserControl Beschreibung
................................................... Bes c hreibung
Die Eigenschaft MaskColor ist ein Farbwert vom Typ Long, der eine Transparenzfarbe für die dem Objekt zugeordnete(n) Bitmap(s) festlegt. Zur Auswahl stehen neben den Systemfarben auch beliebige andere Farben, die sich entweder zur Entwurfszeit aus der Palette wählen oder zur Laufzeit über die Funktion RGB komponentenweise zu einem Farbwert komponieren lassen. Die Funktion QBColor liefert den Farbwert einer der 16 Grundfarben. Zudem definiert der Auf-
358
MouseIcon- Eigenschaft
zählungstyp SystemColorConstants Konstanten für die Systemfarben (vgl. »BackColor-Eigenschaft und ForeColor-Eigenschaft«, S. 330). Bei Schaltflächen, Optionsfeldern und Kontrollkästchen erscheinen transparente Bereiche der Bitmap in der für das Steuerelement gesetzten Hintergrundfarbe (BackColor); bei benutzerdefinierten Steuerelementen scheint in transparenten Bereichen durch, was dahinter liegt – beispielsweise ein anderes Steuerelement oder ein Hintergrundbild des Containers. Anwendung
................................................... Anwendung
Verwandte Eigenschaften
................................................... Verwa ndte Eigens c ha ften BackColor, BackStyle, DisabledPicture, DownPicture, MaskPicture, Picture Verwandte Themen
................................................... Verwandte Them en
Transparenz und Drag&Drop (S. 606)
MouseIcon- Eigenschaft Objekt.MouseIcon As Picture Betroffene Objekte
................................................... Betro ffene Objekte Alle Formulare, Komponenten und Steuerelemente mit sichtbarer Darstellung Beschreibung
................................................... Bes c hreibung
Die MouseIcon-Eigenschaft spezifiziert eine benutzerdefinierte Mauszeigerform für ein Objekt. Sie kommt zur Anzeige, sobald der Mauszeiger in den sichtbaren Bereich des Objekts gerät, unter der Voraussetzung dass die MousePointer-Eigenschaft auf vbCustom (99) gesetzt ist. Hat die Eigenschaft den Wert Nothing, bleibt die aktuelle Mauszeigerform unverändert.
3 59
Gemeinsame Eigenschaften
Für die Beachtung der Transparenzfarbe bei Schaltflächen, Optionsfeldern und Kontrollkästchen gibt es drei Voraussetzungen: Die Eigenschaft Style muss auf vbGraphical (1) gesetzt sein, die UseMaskColor-Eigenschaft muss auf True gesetzt sein und für die benutzerdefinierte Darstellung der Zustände (Picture, DisabledPicture, DownPicture) müssen Bitmaps angegeben sein, deren transparente Bereiche in der für MaskColor gesetzten Farbe gehalten sind. Sind diese Voraussetzungen erfüllt, machen sich Änderungen der Hintergrundfarbe des Steuerelements in den transparenten Bereichen der Bitmaps bemerkbar – es handelt sich hier also um Pseudotransparenz. Von echter Transparenz spricht man, wenn hinter dem Steuerelement gelegene Bereiche – ein Hintergrundbild und andere Steuerelemente mit niedrigerer Z-Ordnung – an den transparenten Stellen zu sehen sind. Sie lässt sich nur mit benutzerdefinierten Steuerelementen, also Komponenten des Typs UserControl, realisieren und erfordert dreierlei: Setzen der BackStyle-Eigenschaft auf vbTransparent (0); Setzen der MaskColor-Eigenschaft auf die Transparenzfarbe; Setzen der Eigenschaften Picture und MaskPicture auf eine Bitmap, deren transparente Bereiche in der für MaskColor gesetzten Farbe gehalten sind. Auch beim ImageList-Steuerelement legt die MaskColor-Eigenschaft eine Transparenzfarbe zur Erstellung einer Maske für die transparente Anzeige fest. Die Methoden Overlay und Draw arbeiten mit dieser Eigenschaft und implizit auch Steuerelemente wie TreeView und ListView (bzw. ListItem).
Gemeinsame Eigenschaften
Anwendung
................................................... Anwendung
Als Werte für diese Eigenschaft sind nur Verweise auf Bitmaps geeignet, die in einem der Symbolformate CUR oder ICO vorliegen – insbesondere lassen sich also weder gewöhnliche Bilder (BMP, JPG, WMF etc.) noch animierte Cursorformen (ANI) als Mauszeigerform setzen. Beispiel
................................................... Beis piel
Gemeinsame Eigenschaften
Command1.MouseIcon = LoadPicture ("MeinIcon.Ico") Command1.MousePointer = vbCustom Verwandte Eigenschaften
................................................... Verwa ndte Eigens c ha ften DragIcon, MousePointer
MousePointer- Eigenschaft Objekt.MousePointer As Integer Betroffene Objekte
................................................... Betro ffene Objekte Alle Formulare, Komponenten und Steuerelemente mit sichtbarer Darstellung Beschreibung
................................................... Bes c hreibung
Die MousePointer-Eigenschaft bestimmt, welche Mauszeigerform zur Anzeige kommt, wenn der Mauszeiger in den sichtbaren Bereich des Objekts gerät. Zur Auswahl stehen die 16 vom System her zur Verfügung gestellten Mauszeigerformen sowie eine benutzerdefinierte Mauszeigerform, wenn die Eigenschaft MouseIcon auf ein geeignetes Symbol verweist. Der Aufzählungstyp MousePointerConstants definiert Konstanten für die einzelnen Mauszeigerformen. Konstante (Wert)
Beschreibung
vbDefault (0)
Wie Containerobjekt (Voreinstellung)
vbArrow (1)
Mauspfeil
vbCrosshair (2)
Kreuz
vbIbeam (3)
I-Form
vbIconPointer (4)
Symbol
vbSizePointer (5)
Größenänderung (Kreuz mit Pfeilspitzen)
vbSizeNESW (6)
Größenänderung aufwärts diagonal
vbSizeNS (7)
Größenänderung vertikal
vbSizeNWSE (8)
Größenänderung abwärts diagonal
vbSizeWE (9)
Größenänderung horizontal
vbUpArrow (10)
Aufwärtspfeil
vbHourglass (11)
Sanduhr
vbNoDrop (12)
»Nicht ablegen« (Verbotsschild)
vbArrowHourglass (13)
Mauspfeil mit Sanduhr
vbArrowQuestion (14)
Mauspfeil mit Fragezeichen (Hilfepfeil)
360
MultiLine- Eigenschaft
Konstante (Wert)
Beschreibung
vbSizeAll (15)
Größenänderung alle (Kreuz mit Pfeilspitzen)
vbCustom (99)
Benutzerdefinierter Mauszeiger (MouseIcon-Eigenschaft)
Anwendung
................................................... Anwendung
Beispiel
................................................... Beis piel
Command1.MouseIcon = LoadPicture ("MeinIcon.Ico") Command1.MousePointer = vbCustom
MultiLine- Eigenschaft Objekt.MultiLine As Boolean Betroffene Objekte
................................................... Betro ffene Objekte TextBox Beschreibung
................................................... Bes c hreibung
Die MultiLine-Eigenschaft bestimmt, ob für den Wert eines Textfeldes nur eine einzelne Zeile zur Verfügung steht (False) oder ob ein Umbruch auf mehrere Zeilen möglich ist (True). Ist keine horizontale Bildlaufleiste (ScrollBars) vorhanden, führt das Textfeld im mehrzeiligen Modus einen dynamischen Zeilenumbruch durch, der sich nach der jeweils aktuellen Breite (Width) des Objekts richtet. Hinweis
................................................... Hinweis Entgegen anders lautenden Informationen in der Online-Hilfe zu Visual Basic wirkt sich der Wert der MultiLine-Eigenschaft nicht auf die Ausrichtung des Textes (Alignment) in einem Textfeld aus. Vielmehr wirkt sich der Wert der Alignment-Eigenschaft auf die Art aus, wie ein Textfeld den Umbruch vornimmt. Verwandte Befehle
................................................... Verwa ndte Befehle
ScrollBars
Name- Eigenschaft Objekt.Name As String Betroffene Objekte
................................................... Betro ffene Objekte Alle Formulare, Komponenten und Steuerelemente Beschreibung
................................................... Bes c hreibung
Die zur Laufzeit schreibgeschützte Name-Eigenschaft gibt den im Code verwendeten Bezeichner eines Objekts als Zeichenfolge wieder.
3 61
Gemeinsame Eigenschaften
Indem Sie die Mauszeigerform für ein Objekt zur Laufzeit ändern, können Sie dem Benutzer ein visuelles Feedback über momentan ablaufende Geschehnisse oder aktivierte Funktionen anzeigen. Standardoperationen sollten auch durch Standardsymbole (wie in der Tabelle aufgeführt) symbolisiert werden. In speziellen Situationen steht es Ihnen frei, die MousePointer-Eigenschaft auf vbCustom (99) zu setzen und über die MouseIcon-Eigenschaft ein eigenes Symbol bereitzustellen.
Gemeinsame Eigenschaften
Anwendung
Gemeinsame Eigenschaften
................................................... Anwendung
Der Entwurfseditor von Visual Basic benennt Komponenten, Formulare und Steuerelemente automatisch nach einem festen Schema: vereinfachte Typbezeichnung plus Ordinalzahl der Instanzenzählung. Somit erhält das erste Textfeld in einem Formular den Bezeichner »Text1«, das dritte Optionsfeld den Bezeichner »Option3« usw. Wenn Sie lieber mit aussagekräftigeren Bezeichnern arbeiten, können Sie den Wert der Name-Eigenschaft zur Entwurfszeit jederzeit auf einen anderen gültigen Variablenbezeichner setzen. Falls der Bezeichner eines Steuerelements bereits für ein anderes Steuerelement gleichen Typs vergeben wurde, schlägt Ihnen der Entwurfseditor vor, beide Steuerelemente in einem dynamischen Array zu organisieren, das den gewählten Bezeichner erhält. Die Unterscheidung der Objekte geschieht dann über die IndexEigenschaft. Zwei Formulare können dagegen zur Entwurfszeit nicht denselben Namen erhalten. Da Visual Basic ein Formularmodul aber als Datentyp behandelt, können Sie zur Laufzeit beliebig viele Instanzen davon ins Leben rufen. Wenn es ein Array sein muss, bitte sehr, dann vereinbaren Sie eben ein Array mit diesem Typ. (Ja, wenn Sie so wollen, können Sie sogar den Typbezeichner als Array-Namen vereinbaren). Wenn nun noch die erste Laufzeitinstanz des Formulars (die alle weiteren hervorbringt) zu einem Array-Element machen, haben Sie vom Konstrukt her etwas Ähnliches wie bei einem Steuerelemente-Array: ' Innerhalb des Moduls Form1 Dim Form1(2) As New Form1 Private Sub Form_Click() Set Form1(0) = Me Form1(1).Show Form1(2).Show End Sub
... ' Drei Instanzen werden gebraucht ' Vorhandene Instanz eingliedern ' Weitere Instanzen anlegen
Hinweis
................................................... Hinweis Der Entwurfseditor von Visual Basic verwendet automatisch vergebene Bezeichner auch noch für einige andere Eigenschaften als Voreinstellung – so für Caption, LinkTopic und Text. Warnung
................................................... Wa rnung
Wenn Sie im Nachhinein die Groß-/Kleinschreibung eines Formularbezeichners ändern, das Formularmodul abspeichern und später erneut für die Bearbeitung öffnen, erhalten Sie eine Namenskonflikt-Fehlermeldung, die Sie nur wieder loswerden, wenn Sie die alte Schreibweise des Namens wieder einführen oder gleich einen anderen Bezeichner wählen. Der Fehler kommt daher, dass Visual Basic Änderungen der Groß-/Kleinschreibung nicht in die Projektdatei übernimmt und beim erneuten Laden des Moduls glaubt, einen »falschen« Bezeichner vorzufinden.
OLEDropAllowed- und OLETypeAllowed- Eigenschaften Objekt.OleDropAllowed As Boolean Objekt.OleTypeAllowed As Integer Betroffene Objekte
................................................... Betro ffene Objekte OLE, UserControl
362
OLEDropMode- Eigenschaft
Beschreibung
................................................... Bes c hreibung
Die OLEDropAllowed-Eigenschaft bestimmt, ob ein Steuerelement als Ziel einer OLEDrag&Drop-Operation das Ablegen eines OLE-Objekts erlaubt (True) oder nicht (False; Voreinstellung). Die OLETypeAllowed-Eigenschaft bestimmt, welche Operation(en) das Steuerelement für das Ablegen eines OLE-Objekts unterstützt. Der Aufzählungstyp OLEContainerContants definiert drei Konstanten für die zulässigen Werte dieser Eigenschaft. Beschreibung
vbOLELinked (0)
Für abgelegtes OLE-Objekt kann nur eine Verknüpfung eingerichtet werden
vbOLEEmbedded (1)
Abgelegtes OLE-Objekt kann nur eingebettet werden
vbOLEEither (2)
Abgelegtes OLE-Objekt kann verknüpft oder eingebettet werden
Anwendung
................................................... Anwendung
Für OLE-Drag&Drop-Operationen sind zwei Szenarien zu unterscheiden: Das erste beinhaltet die gewöhnliche Datenübertragung zwischen zwei Steuerelementen oder Komponenten im Sinne einer Kopier- oder Verschiebeoperation (mehr dazu im Praxisteil unter »OLEDrag&Drop«, S. 501). Das zweite beinhaltet das Einbetten oder Verknüpfen eines von einem OLE-Server stammenden OLE-Objekts in einem OLE-Container. Um das zweite Szenario geht es hier. Wird ein Objekt, das als OLE-Container fungieren kann und OLE-Drag&Drop-Operationen zulässt, Ziel einer solchen Operation, kann es sich dafür entscheiden, das Objekt einzubetten oder nur eine Verknüpfung auf das Objekt einzurichten. Im ersten Fall verschiebt oder kopiert ((Umschalt) gedrückt) die Operation das Objekt als solches in den OLE-Container, und der Container ermöglicht fortan die Bearbeitung des Objekts »vor Ort« – also in seinem Fenster. Im zweiten Fall verbleibt das OLE-Objekt da, wo es ist, und der OLE-Container speichert nur eine Referenz darauf. In beiden Fällen wird das Objekt im OLE-Container angezeigt (was übrigens der für das OLE-Objekt zuständige OLE-Server übernimmt), nur gespeichert und bearbeitet wird es an verschiedenen Stellen. Lässt ein OLE-Container sowohl das Verknüpfen als auch das Einbetten zu, kann der Benutzer die Art der Operation mittels Funktionstasten gestalten. Drückt er keine Funktionstaste, wird das Objekt verschoben und eingebettet; drückt er (Umschalt), wird es kopiert und eingebettet; drückt er (Umschalt)+(Strg), wird es verknüpft. Verwandte Themen
................................................... Verwandte Them en
OLE-Drag&Drop (S. 501)
OLEDropMode- Eigenschaft Objekt.OleDropMode As Integer Betroffene Objekte
................................................... Betro ffene Objekte Animation, CheckBox, ComboBox, CommandButton, CoolBar, Data, DataCombo, DataList, DateTimePicker, DirListBox, DriveListBox, FileListBox, Frame, Form, Image, ImageCombo, Label, ListBox, ListView, MaskEdBox, MDIForm, MMControl, MSChart, MSFlexGrid, MSHFlexGrid, OptionButton, PictureBox, ProgressBar, PropertyPage, RichTextBox, Slider, SSTab, StatusBar, TabStrip, TextBox, TreeView, ToolBar, UpDown, UserControl, UserDocument
3 63
Gemeinsame Eigenschaften
Eigenschaft
Gemeinsame Eigenschaften
Beschreibung
................................................... Bes c hreibung
Gemeinsame Eigenschaften
Die OLEDropMode-Eigenschaft bestimmt, wie sich ein Objekt verhält, wenn es Zielobjekt einer OLE-Drag&Drop-Operation wird. Der Aufzählungstyp OLEDropConstants definiert dafür folgende Konstanten: Konstante (Wert)
Beschreibung
vbDropNone (0), ccDropNone (0) cc2DropNone (0), cc3DropNone (0) mciDropNone (0)
Das Objekt lehnt die Durchführung der Operation ab und zeigt dies durch eine wie ein Verbotsschild aussehende Mauszeigerform an.
vbDropManual (1), ccDropManual (1) cc2DropManual (1), cc3DropManual (1) mciDropManual (1),
Das Objekt geht auf den Vorgang ein und löst die OLE-Ereignisse OLEDragOver und OLEDrop für die weitere Ausgestaltung der Operation aus.
vbDropAutomatic (2) ccDropAutomatic (2) cc2DropAutomatic (2) cc3DropAutomatic (2) mciDropAutomatic (2)
Das Objekt wickelt die Operation automatisch ab, sofern ein Datenformat im Angebot steht, mit dem es etwas anfangen kann. (Dieser Wert wird nicht von allen Objekten unterstützt, die über eine OLEDropMode-Eigenschaft verfügen.)
Anwendung
................................................... Anwendung
Für die meisten der einfacheren Steuerelemente von Visual Basic (etwa Label, TextBox, PictureBox, Image etc.) bietet sich der automatische Modus an, da die den Objektwerten zugrunde liegenden Datentypen von den standardmäßigen OLE-Datentypen gut abgedeckt werden. Sollen die Daten allerdings in einem speziellen Format oder gar in mehreren Formaten bei einer OLEDrag&Drop-Operation übertragen werden, ist der manuelle Modus unverzichtbar, weil nur er die notwendige Handhabe für den programmseitigen Eingriff in die Operation bietet. Komplexere Steuerelemente wie Form, ListBox oder TreeView unterstützen gar keinen automatischen Modus, da er hier auch keinen Sinn ergeben würde. Verwandte Eigenschaften
................................................... Verwa ndte Eigens c ha ften OLEDragMode Verwandte Methoden
................................................... Verwa ndte Metho den OLEDragDrop, OLEDragOver Verwandte Themen
................................................... Verwandte Them en
OLE-Drag&Drop (S. 501)
Parent- Eigenschaft Objekt.Parent As Formular Beschreibung
................................................... Bes c hreibung
Die zur Laufzeit schreibgeschützte Parent-Eigenschaft eines Objekts enthält einen Verweis auf das übergeordnete Objekt.
364
Picture- Eigenschaft
Anwendung
................................................... Anwendung
Verwandte Eigenschaften
................................................... Verwa ndte Eigens c ha ften Container
Picture- Eigenschaft Objekt.Picture As Picture Betroffene Objekte
................................................... Betro ffene Objekte CheckBox, CommandButton, Form, MDIForm, Image, OptionButton, OLE, PictureBox, PictureClip, PropertyPage, RptImage, UserControl, UserDocument Beschreibung
................................................... Bes c hreibung
Die Picture-Eigenschaft legt ein Bild fest, das im Bereich eines Steuerelements oder Formulars zur Anzeige kommt. Für das OLE-Container-Steuerelement steht diese Eigenschaft zur Entwurfszeit nicht zur Verfügung und ist zur Laufzeit schreibgeschützt. Sie wird von der Quellanwendung, die für das eingebettete Objekt zuständig ist, gepflegt. Für Kontrollkästchen, Optionsfelder und Schaltflächen gibt die Picture-Eigenschaft die Bitmap an, die den aktivierten, nichtgedrückten (bzw. nichtmarkierten) Zustand des Steuerelements in der benutzerdefinierten Darstellung zum Ausdruck bringt. Anwendung
................................................... Anwendung
Die Anzeige eines Bildes innerhalb eines Steuerelements oder Formulars ermöglicht die visuell ansprechende Gestaltung des dem Objekt zur Verfügung stehenden Anzeigebereichs (vgl. auch »Paint-Ereignis«, S. 253). Wird die Picture-Eigenschaft (bzw. vergleichbare Eigenschaften) eines Steuerelements bereits zur Entwurfszeit gesetzt, speichert Visual Basic die entsprechende Bitmap zunächst als binären Anhang in einer separaten Datei (FRX-Datei für Formulare). Bei Erstellung der kompilierten Fassung erhält die EXE-Datei dann alle binären Anhänge als Ressourcen, so dass die Quelldatei generell nicht weiter bereitgestellt werden muss. Anders jedoch, wenn die Bitmap erst zur Laufzeit mittels LoadPicture eingelesen wird. In diesem Fall muss die entsprechende Grafikdatei natürlich dauerhaft verfügbar sein. Mit dem Bildausschnitt-Steuerelement (PictureClip) sowie dem Bildliste-Steuerelement (ImageList) stehen sehr leistungsfähige Werkzeuge für den flexiblen Umgang mit Bildern verschiedenster Formate zur Verfügung. Für Steuerelemente des Typs Image und PictureBox fungiert die Picture-Eigenschaft als Standardeigenschaft. Schaltflächen, Optionsfelder und Kontrollkästchen unterstützen anstelle der standardmäßigen Darstellung auch die Möglichkeit, ihre drei Zustände in Form von benutzerdefinierten Bitmaps
3 65
Gemeinsame Eigenschaften
Während die Container-Eigenschaft eine Aussage darüber macht, in welchem anderen Objekt ein Steuerelement angeordnet ist, regelt die Parent-Eigenschaft das Besitzverhältnis und lässt sich als »Ist-Datenfeld-von«-Eigenschaft lesen. So kann ein Textfeld beispielsweise ein Rahmenfeld (Frame) oder ein Bildfeld (PictureBox) als Container haben, während es einem Formularobjekt (Form) untergeordnet ist. Die Parent-Eigenschaft ist manchmal recht hilfreich, wenn ein Objekt eine Methode oder Eigenschaft seines übergeordneten Objekts aufrufen will, ohne dieses mit Namen zu kennen. Ein solche Situation liegt beispielsweise vor, wenn eine Funktion/Prozedur nichts weiter als eine Referenz auf ein bestehendes Objekt übergeben bekommt.
Gemeinsame Eigenschaften
anzuzeigen. Zur Festlegung der Bitmaps sind die Eigenschaften Picture, DownPicture und DisabledPicture zuständig. Damit ein Steuerelement die Werte dieser Eigenschaften überhaupt beachtet, muss allerdings bereits beim Formularentwurf die zur Laufzeit schreibgeschützte Style-Eigenschaft auf vbButtonGraphical (1) gesetzt worden sein. Die Picture-Bitmap kommt beim aktivierten Steuerelement (Enabled-Eigenschaft auf True) im Zustand »Normal« (lies: weder »gedrückt« noch »markiert«) zur Anzeige. Beispiel
Gemeinsame Eigenschaften
................................................... Beis piel
Picture = LoadPicture("Form.jpg") ' Formular Picture1 = LoadPicture("PicBox.jpg") ' PictureBox-Steuerelement Image1 = LoadPicture("Image.jpg") ' Image-Steuerelement Command1.Picture = LoadPicture("Schmiley.jpg") ' ben.def.Schaltfläche Verwandte Befehle
................................................... Verwa ndte Befehle
DisabledPicture, DownPicture Verwandte Themen
................................................... Verwandte Them en
Transparenz und Drag&Drop (S. 606)
RightToLeft- Eigenschaft Objekt.RightToLeft As Boolean Betroffene Objekte
................................................... Betro ffene Objekte CheckBox, ComboBox, CommandButton, Form, MDIForm, Label, ListBox, OptionButton, PictureBox, PropertyPage, RptTextBox, RptLabel, TextBox, UserControl, UserDocument Beschreibung
................................................... Bes c hreibung
Die RightToLeft-Eigenschaft bestimmt, ob ein Steuerelement auf einem bidirektionalen System Text von rechts nach links (True) oder von links nach rechts (False; Voreinstellung) anzeigt. Anwendung
................................................... Anwendung
Für diese Eigenschaft müssen Sie sich nur interessieren, wenn Sie internationale Anwendungen, beispielsweise für den arabischen Raum, programmieren, wo die Leserichtung von Text nicht von links nach rechts, sondern von rechts nach links ist.
ScaleLeft- Eigenschaft und ScaleTop- Eigenschaft Objekt.ScaleLeft As Single Objekt.ScaleTop As Single Betroffene Objekte
................................................... Betro ffene Objekte Form, PictureBox, Printer, PropertyPage, UserControl, UserDocument Beschreibung
................................................... Bes c hreibung
Die Eigenschaften ScaleLeft und ScaleTop beschreiben die Koordinaten des linken oberen Bildpunkts des Client-Bereichs im aktuellen Koordinatensystem des Objekts.
366
ScaleLeft- Eigenschaft und ScaleTop- Eigenschaft
Anwendung
................................................... Anwendung
Ändert man die Werte dieser Eigenschaften, bewirkt das eine Ursprungsverschiebung (Translation) des Koordinatensystems, und als Seiteneffekt erhält die ScaleMode-Eigenschaft den Wert vbUser (0). In allen vordefinierten Koordinatensystemen liegt der Ursprung in der linken oberen Ecke des Client-Bereichs, so dass umgekehrt diese Eigenschaften als Seiteneffekt den Wert 0 erhalten, wenn die ScaleMode-Eigenschaft auf einen Wert ungleich vbUser (0) gesetzt wird. Tipps
................................................... Tipps
Scale (-2 * Pi, 1) – (2 * Pi, -1) Beispiel
................................................... Beis piel
Das folgende einfache Programm gibt die Funktion der dreigliedrigen Sinusreihe für die Approximation der Rechteckkurve im Intervall [-2π, 2π] aus. Da die Paint-Routine das Koordinatensystem immer so wählt, dass der Kurvenausschnitt exakt in den Client-Bereich des Formulars passt, kann der Benutzer die Fenstergröße nach Belieben verändern. Private Sub Form_Paint() Dim Pi, x, y Pi = Atn(1) * 4 ScaleHeight = -2 ScaleWidth = 4 * Pi ScaleLeft = -2 * Pi ScaleTop = 1 For x = -2 * Pi To 2 * Pi Step Pi / 200 y = Sin(x) + 1 / 3 * Sin(3 * x) + 1 / 5 * Sin(5 * x) PSet (x, y) Next x End Sub Private Sub Form_Resize() Refresh End Sub Verwandte Eigenschaften
................................................... Verwa ndte Eigens c ha ften ScaleMode, ScaleHeight, ScaleWidth
3 67
Gemeinsame Eigenschaften
Die richtige Wahl des Koordinatensystems kann so manches Problem vereinfachen und lästige Umrechnereien ersparen. Da Zeichenoperationen meist auf ein virtuelles Koordinatensystem bezogen sind, bietet es sich an, den in diesem Koordinatensystem benutzten Ausschnitt so auf den Ausgabebereich abzubilden, dass die virtuellen Koordinaten mit den Bereichskoordinaten zusammenfallen. Dazu setzen Sie zunächst die Eigenschaften ScaleHeight, ScaleWidth auf die Ausdehnung und die Orientierung (Vorzeichen der Werte) des Ausschnitts und verschieben dann den Ursprung, in dem Sie ScaleLeft, ScaleTop geeignet setzen. Am einfachsten setzen Sie ein neues Koordinatensystem jedoch mittels der Scale-Methode. Sie übergeben der Methode einfach den linken oberen und den rechten unteren Punkt des Ausschnitts, der im Client-Bereich sichtbar sein soll. Anstelle der im Beispiel verwendeten Befehlsfolge ließe sich somit auch schreiben:
Gemeinsame Eigenschaften
Gemeinsame Eigenschaften
Approximation der
Rec htec kkurve
im
Intervall [ - 1 , 1] x[ - 2
π, π 2
]
Verwandte Methoden
................................................... Verwa ndte Metho den Scale, ScaleX, ScaleY
ScaleHeight- Eigenschaft und ScaleWidth- Eigenschaft Objekt.ScaleHeight As Single Objekt.ScaleWidth As Single Betroffene Objekte
................................................... Betro ffene Objekte Form, PictureBox, Printer, PropertyPage, UserControl, UserDocument Beschreibung
................................................... Bes c hreibung
Die Eigenschaften ScaleHeight und ScaleWidth beschreiben die Abmessungen des ClientBereichs des Objekts in Koordinaten des aktuellen Koordinatensystems. Anwendung
................................................... Anwendung
Beachten Sie, dass sich über die Scale-Eigenschaften keine Größenänderungen durchführen lassen. Ändert man die Werte dieser Eigenschaften, ändert sich lediglich die Maßeinheit für die jeweilige Richtung. Das heißt, es passiert eine Streckung bzw. Stauchung (Skalierung) der Koordinatenachsen, wobei der linke obere Punkt des Client-Bereichs Fixpunkt ist. Als Seiteneffekt erhält die ScaleMode-Eigenschaft den Wert vbUser (0). Für die vordefinierten Koordinatensysteme sind feste Maßeinheiten definiert, so dass umgekehrt ScaleHeight und ScaleWidth als Seiteneffekt neue Werte erhalten, wenn die ScaleMode-Eigenschaft auf einen Wert ungleich vbUser (0) gesetzt wird. Standardmäßig ist die vertikale Achse des Koordinatensystems eines Objekts von oben nach unten und die horizontale Achse von links nach rechts orientiert. Um die Orientierung zu ändern, müssen Sie der entsprechenden Eigenschaft nur ein anderes Vorzeichen verpassen, indem Sie ihren Wert mit -1 multiplizieren. Tipp
................................................... Tipp
Die richtige Wahl des Koordinatensystems kann so manches Problem vereinfachen und schlimmere Rechnereien ersparen. Da Zeichenoperationen meist auf ein virtuelles Koordinatensystem bezogen sind, bietet es sich an, den in diesem Koordinatensystem benutzten Ausschnitt so auf den Ausgabebereich abzubilden, dass die virtuellen Koordinaten mit den Bereichskoordinaten zusammenfallen. Dazu setzen Sie ScaleHeight und ScaleWidth auf die Ausdehnung und Orientierung des Bereichs und verschieben den Ursprung, indem Sie ScaleLeft, ScaleTop auf die Koordinaten des linken oberen Punkts setzen.
368
ScaleMode- Eigenschaft
Am einfachsten setzen Sie ein neues Koordinatensystem jedoch mittels der Scale-Methode. Sie übergeben der Methode einfach den linken oberen und den rechten unteren Punkt des Ausschnitts, der im Client-Bereich sichtbar sein soll. Scale (-2 * Pi, 1) – (2 * Pi, -1) Beispiel
................................................... Beis piel
Siehe »ScaleHeight-Eigenschaft und ScaleWidth-Eigenschaft«, S. 368. Verwandte Eigenschaften
................................................... Verwa ndte Eigens c ha ften Verwandte Methoden
................................................... Verwa ndte Metho den Scale, ScaleX, ScaleY
ScaleMode- Eigenschaft Objekt.ScaleMode As Integer Betroffene Objekte
................................................... Betro ffene Objekte Form, PictureBox, Printer, PropertyPage, UserControl, UserDocument Beschreibung
................................................... Bes c hreibung
Die ScaleMode-Eigenschaft bestimmt das Koordinatensystem eines Objekts. Zur Auswahl stehen elf Koordinatensysteme: ein benutzerdefiniertes sowie zehn vordefinierte. Die vordefinierten haben allesamt den Ursprung in der linken oberen Ecke des Client-Bereichs, sind in der vertikalen Richtung von oben nach unten orientiert und unterscheiden sich eigentlich nur in den für die Koordinatenachsen geltenden Maßeinheiten. Der Aufzählungstyp ScaleModeConstants definiert dafür die folgenden Konstanten: Konstante (Wert)
Beschreibung
vbUser (0)
Benutzerdefiniertes Koordinatensystem, das über die ScaleMethode bzw. über die Eigenschaften ScaleHeight, ScaleLeft, ScaleTop, ScaleWidth näher spezifiziert wird
vbTwips (1)
Vordefiniertes Koordinatensystem mit der Maßeinheit »Twips« (Voreinstellung); 56,69286 Twips entsprechen einem Millimeter und 0,176389 Millimeter einem Twip
vbPoints (2)
Vordefiniertes Koordinatensystem mit der Maßeinheit »Punkt«; 1 Punkt entspricht 0,3527781 mm, 2,834643 Punkte entsprechen einem Millimeter und 72 Punkt einem Zoll
vbPixels (3)
Vordefiniertes Koordinatensystem mit der Maßeinheit »Bildpunkt«; bei einer Auflösung von 1200×1068 entspricht 1 Bildpunkt logisch 0,2116669 mm, und 4,724405 Bildpunkte entsprechen einem logischen Millimeter (die physikalischen Abmessungen hängen von der Geometrie des Bildschirms ab)
3 69
Gemeinsame Eigenschaften
ScaleMode, ScaleLeft, ScaleTop
Gemeinsame Eigenschaften
Gemeinsame Eigenschaften
Konstante (Wert)
Beschreibung
vbCharacters (4)
Vordefiniertes Koordinatensystem mit der Maßeinheit »Zeichen« (die horizontale und vertikale Skalierung kann hier unterschiedlich sein und hängt im Allgemeinen von den physikalischen Gegebenheiten sowie dem Betriebsmodus des Ausgabegeräts ab)
vbInches (5)
Vordefiniertes Koordinatensystem mit der Maßeinheit »Zoll«; 1 Zoll entspricht 25,4 Millimeter und ein Millimeter 0,03937 Zoll
vbMillimeters (6)
Vordefiniertes Koordinatensystem mit der Maßeinheit »Millimeter»
vbCentimeters (7)
Vordefiniertes Koordinatensystem mit der Maßeinheit »Zentimeter»
vbHimetric (8)
Vordefiniertes Koordinatensystem mit der Maßeinheit »hundertstel Millimeter«; Visual Basic unterstützt dieses Koordinatensystem (zur Zeit noch) nicht für Steuerelemente und Formulare! Es ist aber bei Picture-Objekten (und da ausschließlich) anzutreffen (die Umrechnung mit ScaleX und ScaleY ist möglich und häufig auch erforderlich)
vbContainerPosition (9)
Wird von Visual Basic (zur Zeit noch) nicht unterstützt
vbContainerSize (10)
Wird von Visual Basic (zur Zeit noch) nicht unterstützt
Anwendung
................................................... Anwendung
Mit einem Wert ungleich 0 für die ScaleMode-Eigenschaft arbeiten Sie, wenn Sie eine bestimmte Skalierung auf einem Ausgabegerät einführen wollen. So ist beispielsweise die Skalierung Millimeter (vbMillimeters) sehr hilfreich, um maßstabsgetreue metrische Zeichnungen auf einem Drucker auszugeben. Um ein benutzerdefiniertes Koordinatensystem einzustellen, arbeiten Sie am besten mit der Methode Scale. Die Methode, die in ihren Parametern eine Beschreibung des sichtbaren Ausschnitts in Form eines Koordinatenpaars erwartet, setzt ScaleMode auf den Wert vbUser (0) und richtet das Koordinatensystem so ein, dass das angegebene Koordinatenpaar auf den linken oberen und den rechten unteren Punkt im Client-Bereich abgebildet wird. Tipp
................................................... Tipp
Da es die Druckwerke einiger Drucker meist nicht so ganz genau mit den Größenverhältnissen nehmen (Toleranzen im Bereich von 1 % sind normal), lässt sich die Skalierung im Einzelfall nachbessern, indem man ein Quadrat fester Länge ausgibt (bei A4 ist beispielsweise die Kantenlänge 190 mm gut geeignet): Printer.ScaleMode = vbMillimeters Printer.Line(0,0)-(190, 190) Printer.EndDoc
dieses dann nachmisst und unter Berücksichtigung von Korrekturfaktoren ein benutzerdefiniertes Koordinatensystem einführt: Printer.ScaleWidth = Printer.ScaleWidth * (MesswertBreiteInMM / 190) Printer.ScaleHeight = Printer.ScaleHeight * (MesswertHöheInMM / 190)
370
ShowTips- Eigenschaft
Beispiel
................................................... Beis piel
Der folgende Code gibt den Namen, die Papiergröße und die bedruckbare Fläche (ClientBereich) des Standarddruckers aus. Da sich ScaleMode nur auf den Client-Bereich auswirkt, müssen die Eigenschaften Height und Width mittels der Methoden ScaleX und ScaleY in die geltenden Maßeinheiten umgerechnet werden.
Gemeinsame Eigenschaften
With Printer .ScaleMode = vbMillimeters .PaperSize = vbPRPSA4 Print .DeviceName Print .ScaleX(.Height, vbTwips, vbMillimeters), Print .ScaleX(.Width, vbTwips, vbMillimeters) Print .ScaleHeight, .ScaleWidth End With
Papiergröße und bedruckbarer Bereich des Standarddruckers Verwandte Eigenschaften
................................................... Verwa ndte Eigens c ha ften ScaleHeight, ScaleWidth, ScaleLeft, ScaleTop Verwandte Methoden
................................................... Verwa ndte Metho den Scale, ScaleX, ScaleY
ShowTips- Eigenschaft Objekt.ShowTips As Boolean Betroffene Objekte
................................................... Betro ffene Objekte TabStrip, StatusBar, ToolBar Beschreibung
................................................... Bes c hreibung
Die ShowTips-Eigenschaft bestimmt, ob die in der Eigenschaft ToolTipText definierte Zeichenfolge als QuickInfo für das Steuerelement zur Anzeige kommt (True) oder nicht (False). Das QuickInfo-Fensterchen erscheint, wenn die Maus in den Bereich des Steuerelements gelangt und dort ca. eine halbe Sekunde verweilt. Anwendung
................................................... Anwendung
Visual Basic unterstützt vier verschiedene Techniken, um für den Benutzer einer Anwendung Hilfe bereitzustellen: QuickInfo, Statusanzeige, Direkthilfe und kontextbezogene Hilfe. Die ersten beiden Techniken bieten dem Benutzer unaufgefordert und in knapper Form Hilfestellung für den Umgang mit der Benutzerumgebung. Um eine QuickInfo für ein Steuerelement bereitzustellen, weisen Sie der Eigenschaft ToolTipText eine Zeichenfolge mit dem Hilfetext zu und setzen – sofern vorhanden – die ShowTips-Eigenschaft auf True. Die Zeichenfolge sollte nicht zu lang sein, da die Anzeige einzeilig ist.
371
Gemeinsame Eigenschaften
Style- Eigenschaft Objekt.Style As Integer Betroffene Objekte
................................................... Betro ffene Objekte Band, Button, CheckBox, CommandButton, ComboBox, DataCombo, ListBox, OptionButton, SSTab, StatusBar, TabStrip, TreeView Beschreibung
Gemeinsame Eigenschaften
................................................... Bes c hreibung
Die Style-Eigenschaft bestimmt den Darstellungsstil eines Steuerelements. Die Tabelle gibt einen Überblick über die Konstanten, die für die Stile der einzelnen Steuerelemente definiert sind. Konstante (Wert)
Gilt für Objekt
Beschreibung
cc3BandNormal (0)
Band
Der Benutzer kann Größe des Bandes ändern (Voreinstellung).
cc3BandFixedSize (1)
Band
Die Bandgröße ist unveränderlich.
sbrNormal (0)
StatusBar
Die Statusleiste enthält mehrere Bereiche in Form von Panel-Objekten (Voreinstellung).
sbrSimple (1)
StatusBar
Die Statusleiste enthält nur einen Bereich (PanelObjekt) und Text wird durch Setzen der SimpleText-Eigenschaft ausgegeben.
sbrText (0)
Panel
Der Statusleistenbereich kann Text und/oder ein Bild anzeigen (Voreinstellung).
sbrCaps (1)
Panel
Der Statusleistenbereich zeigt den Zustand der Feststellfunktion für (Umschalt) an.
sbrNum (2)
Panel
Der Statusleistenbereich zeigt den Zustand der Feststellfunktion für (Num) an.
sbrIns (3)
Panel
Der Statusleistenbereich zeigt den Zustand der Funktion Einfügen/Überschreiben an.
sbrScrl (4)
Panel
Der Statusleistenbereich zeigt den Zustand der Feststellfunktion für (Rollen) an.
sbrTime (5)
Panel
Der Statusleistenbereich zeigt die Systemzeit an.
sbrDate (6)
Panel
Der Statusleistenbereich zeigt das Systemdatum an.
sbrKana (7)
Panel
Statusleistenbereich zeigt den Zustand der Feststellfunktion für (Kana) an (nur für japanische Version des Betriebssystems relevant).
ssStyle_ TabbedDialog (0)
SSTab
Die in den Dialogfeldern mit Registerdarstellung erscheinenden Register sehen genauso aus wie in den Anwendungen von Microsoft Office für Microsoft Windows 3.1. Die Schrift im Register der aktiven Registerkarte erscheint fett (Voreinstellung).
372
Style- Eigenschaft
Gilt für Objekt
Beschreibung
ssStyle_ PropertyPage (1)
SSTab
Die in den Dialogfeldern erscheinenden Register sind im Stil von Windows 9x gehalten. Die TabMaxWidth-Eigenschaft wird ignoriert und die Beschriftung des ausgewählten Registers auch nicht fett dargestellt.
tabTabs (0)
TabStrip
Die Registerkarten erscheinen wie Register in einem Notizbuch (Voreinstellung).
tabButtons (1)
TabStrip
Die Registerkarten enthalten eine Schaltfläche als Register.
tabFlatButtons (2)
TabStrip
Die Registerkarten enthalten eine Schaltfläche als Register, die nur im gedrückten Zustand in 3DDarstellung gezeichnet werden, ansonsten in 2DDarstellung.
tbrDefault (0)
Button
Die Schaltfläche ist Befehlsschaltfläche (Voreinstellung).
tbrCheck (1)
Button
Die Schaltfläche ist Schalter.
tbrButtonGroup (2)
Button
Die Schaltfläche ist Element einer Schaltergruppe, in der immer nur ein Schalter »gedrückt« sein kann.
tbrSeparator (3)
Button
Die Schaltfläche ist Trennlinie.
tbrPlaceholder (4)
Button
Die Schaltfläche sieht aus wie eine Trennlinie, ist aber Platzhalter und von der Breite her veränderbar.
tbrDropDown (5)
Button
Der Schaltfläche ist ein Menü (MenuButtonObjekte) zugeordnet.
tbrStandard (0)
ToolBar
Symbolleiste hat standardmäßige Darstellung (Voreinstellung).
tbrTransparent (1)
ToolBar
Schaltflächen und Symbolleiste sind durchsichtig, Schaltfläche enthält Beschriftung unter Bitmap, und Hot Tracking ist aktiviert.
tbrRight (2)
ToolBar
Wie tbrTransparent, die Beschriftung erscheint jedoch rechts neben der Bitmap.
tvwTextOnly (0)
TreeView
Knotenobjekte (Node) werden als reiner Text angezeigt.
tvwPictureText (1)
TreeView
Knotenobjekte (Node) werden als Text mit Bild angezeigt.
tvwPlusMinusText (2)
TreeView
Knotenobjekte (Node) werden als Text mit Plus-/ Minussymbol (Teilbaum sichtbar/unsichtbar) angezeigt.
tvwPlusPicture Text (3)
TreeView
Knotenobjekte (Node) werden als Text mit Plus-/ Minussymbol und Bild angezeigt.
373
Gemeinsame Eigenschaften
Konstante (Wert)
Gemeinsame Eigenschaften
Gemeinsame Eigenschaften
Konstante (Wert)
Gilt für Objekt
Beschreibung
tvwTreelinesText (4)
TreeView
Knotenobjekte (Node) werden als Text mit Abhängigkeitslinien (unter Beachtung der LineStyle-Eigenschaft) angezeigt.
tvwTreelinesPicture Text (5)
TreeView
Knotenobjekte (Node) werden als Text mit Bild und Abhängigkeitslinien (unter Beachtung der LineStyle-Eigenschaft) angezeigt.
tvwTreelinesPlus MinusText (6)
TreeView
Knotenobjekte (Node) werden als Text mit Plus-/ Minussymbol und Abhängigkeitslinien (unter Beachtung der LineStyle-Eigenschaft) angezeigt
tvwTreelinesPlus MinusPictureText (7)
TreeView
Knotenobjekte (Node) werden als Text mit Plus-/ Minussymbol, Bild und Abhängigkeitslinien (unter Beachtung der LineStyle-Eigenschaft) angezeigt.
vbButtonStandard (0)
CheckBox, OptionButton, CommandButton
Das Steuerelement wird im Stil von Windows 9x dargestellt.
vbButtonGraphical (1)
CheckBox, OptionButton, CommandButton
Das Steuerelement wird mit benutzerdefinierten Grafiken (für die Zustände »normal«, »gedrückt« und »inaktiv«) dargestellt.
vbListBoxStandard (0)
ListBox
Das Listenfeld wird als einfaches Listenfeld mit Texteinträgen dargestellt.
vbListBoxCheckBox (1)
ListBox
Das Listenfeld wird als Listenfeld mit ankreuzbaren Einträgen (Kontrollkästchen neben den Texteinträgen) dargestellt.
vbComboDropDown (0) dbcDropDownCombo (0)
ComboBox, DataCombo
Das Kombinationsfeld wird als Textfeld mit Pfeilschaltfläche dargestellt. Der Benutzer kann einen Wert in das Textfeld eingeben oder die anhängende Liste öffnen und einen Eintrag daraus wählen (Voreinstellung).
vbComboSimple (1) dbcSimpleCombo (1)
ComboBox, DataCombo
Das Kombinationsfeld wird als Textfeld über einem Listenfeld dargestellt. Die Liste bleibt ständig sichtbar. Ihre Höhe wird durch die HeightEigenschaft bestimmt.
vbCombo DropDownList (2) dbcDropDownList (2)
ComboBox, DataCombo
Das Kombinationsfeld wird als Textfeld mit Pfeilschaltfläche dargestellt. Der Benutzer kann keine Eingaben in das Textfeld vornehmen, sondern muss einen Eintrag aus der anhängenden Liste wählen.
Verwandte Eigenschaften
................................................... Verwa ndte Eigens c ha ften BorderStyle
37 4
TabIndex- Eigenschaft
TabIndex- Eigenschaft Objekt.TabIndex As Integer Betroffene Objekte
................................................... Betro ffene Objekte Alle sichtbaren Steuerelemente, nicht jedoch Data, Line, Shape, Menu Beschreibung
................................................... Bes c hreibung
Anwendung
................................................... Anwendung
Die durch die TabIndex-Eigenschaft festgelegte Tabulatorordnung bestimmt die Aktivierreihenfolge der Steuerelemente, wenn der Benutzer mit (Tab) bzw. (Umschalt)+(Tab) navigiert. Der Entwurfseditor von Visual Basic legt die Tabulatorordnung nach der Reihenfolge des Einfügens der Steuerelemente in das Formular (bzw. in die Komponente) fest und setzt dabei die TabIndexEigenschaft, mit der Zählung von 0 beginnend, auf den entsprechenden Wert. Auch Steuerelemente, die den Fokus nicht entgegennehmen können – weil sie nicht dafür konzipiert sind oder ihre TabStop-Eigenschaft auf False gesetzt ist –, spielen eine wichtige Rolle für die Tabulatorordnung, da sie Zugriffstasten definieren können. Eine Zugriffstaste ((Alt) plus unterstrichenes Zeichen in der Beschriftung) ermöglicht es dem Benutzer, den Fokus per Tastatur auf ein bestimmtes Steuerelement zu verschieben. Sie definieren eine Zugriffstaste für ein Steuerelement, indem Sie dem entsprechenden Zeichen in der Beschriftung des Steuerelements (CaptionEigenschaft) ein »&«-Zeichen voranstellen. Falls ein Steuerelement, das den Fokus erhalten kann, selbst keine Beschriftung besitzt, sondern auf ein Bezeichnungsfeld (Label) angewiesen ist, lässt sich die Zugriffstaste über die Caption-Eigenschaft des Bezeichnungsfelds festlegen, sofern dessen UseMnemonic-Eigenschaft auf True gesetzt ist. Dabei ist es wichtig, dass das Bezeichnungsfeld in der Tabulatorordnung vor dem besagten Steuerelement kommt, das heißt, dass der Wert seiner TabIndex-Eigenschaft kleiner ist. (Natürlich sollte in der Tabulatorordnung zwischen dem Steuerelement und seinem Bezeichnungsfeld kein weiteres Steuerelement sitzen, das den Fokus annehmen kann). Durch Verwendung mehrerer Bezeichnungsfelder lassen sich so sogar problemlos mehrere Zugriffstasten für ein und dasselbe Steuerelement definieren. Unsichtbare oder deaktivierte Steuerelemente verbleiben in der Tabulatorreihenfolge, werden aber bei der Fokusweitergabe übersprungen. Beim Entwurf eines Formulars lohnt es sich, gleich beim Einfügen der Steuerelemente auf die richtige Reihenfolge zu achten – insbesondere also Bezeichnungsfelder vor den zugehörigen Steuerelementen zu platzieren. Natürlich lässt sich die TabIndex-Eigenschaft jederzeit auch nachträglich ändern. Dafür sollten Sie wissen, dass der Entwurfseditor von Visual Basic die TabIndex-Werte zweier Steuerelemente automatisch austauscht, wenn Sie einem Steuerelement den Wert eines anderen zuordnen. In seltenen Fällen werden Sie vielleicht die Aktivierreihenfolge zur Laufzeit ändern wollen – ein automatischer Austausch findet dann aber nicht statt, so dass Sie die TabIndex-Werte aller Steuerelemente neu setzen müssen, die eine andere Position in der Tabulatorordnung erhalten sollen. Vom Prinzip her ist es zwar unproblematisch, zwei oder mehreren Steuerelementen den gleichen TabIndex-Wert zuzuordnen, allerdings wird die Aktivierreihenfolge unter diesen Elementen dann zu einem Produkt des Zufalls. »Mut zur Lücke« bei der Tabulatorordnung wird zwar nicht bestraft, die Ordnung sollte aber nach Möglichkeit fortlaufend sein (und mit 0 oder 1 beginnen), weil nicht nur der Entwurfseditor, sondern auch das Laufzeitsystem (so beim Laden einer neuen Steuerelementinstanz mit Load) bemüht ist, eine fortlaufende Ordnung
375
Gemeinsame Eigenschaften
Die TabIndex-Eigenschaft ordnet einem Objekt eine Position in der Tabulatorordnung des Containers zu und regelt insbesondere die Fokusweitergabe bei Verwendung der Tabulatortaste. Die Zählung beginnt üblicherweise bei 0 kann aber auch mit 1 begonnen werden.
Gemeinsame Eigenschaften
durchzusetzen. Wenn Sie einem Steuerelemente-Array mittels Load ein weiteres Element hinzufügen, erhält dieses vom Laufzeitsystem automatisch die jeweils letzte Position in der Tabulatorordnung. Die ZOrder-Methode hat nichts mit der TabIndex-Eigenschaft zu tun. Sie regelt die Zeichenreihenfolge der Steuerelemente. Verwandte Eigenschaften
................................................... Verwa ndte Eigens c ha ften
Gemeinsame Eigenschaften
Caption
TabStop- Eigenschaft Objekt.TabStop As Boolean Betroffene Objekte
................................................... Betro ffene Objekte Adodc, Animation, CheckBox, ComboBox, CommandButton, DataCombo, DataGrid, DataList, DataRepeater, DateTimePicker, DirListBox, DriveListBox, FileListBox, HScrollBar, ImageCombo, ListView, ListBox, MaskEdBox, MonthView, MSChart, MSFlexGrid, MSHFlexGrid, OLE, OptionButton, PictureBox, RichTextBox, Slider, SSTab, TabStrip, TextBox, TreeView, UpDown, VScrollBar Beschreibung
................................................... Bes c hreibung
Die TabStop-Eigenschaft bestimmt, ob ein Element den Fokus durch Weiterschaltung mit (Tab) oder (Umschalt)+(Tab) oder per Zugriffstaste erhalten kann (True; Voreinstellung) oder nicht (False). Anwendung
................................................... Anwendung
Der Benutzer hat mehrere Möglichkeiten, den Eingabefokus auf ein bestimmtes Steuerelement zu setzen: per Tabulatortaste, per Zugriffstaste, per Mausklick und per Pfeiltasten. Die ersten beiden Möglichkeiten unterliegen dem Einfluss der TabStop-Eigenschaft, die anderen beiden nicht. Steuerelemente, deren Visible-Eigenschaft oder Enabled-Eigenschaft auf False gesetzt ist, können den Fokus überhaupt nicht erhalten. Verwandte Eigenschaften
................................................... Verwa ndte Eigens c ha ften Visible, Enabled
Tag- Eigenschaft Objekt.Tag As String Betroffene Objekte
................................................... Betro ffene Objekte Alle Formulare, Komponenten und Steuerelemente Beschreibung
................................................... Bes c hreibung
Die Tag-Eigenschaft wird von Visual Basic nicht beachtet und steht daher für benutzerdefinierte Zwecke zur Verfügung. Anwendung
................................................... Anwendung
Die Tag-Eigenschaft ist recht hilfreich, wenn es darum geht, ohne viel Aufwand und ohne den Wert von Eigenschaften wie Caption zu verändern, zusätzliche Informationen an ein Objekt zu »heften«. So kann man diese Eigenschaft beispielsweise dafür verwenden, unterschiedliche
37 6
ToolTipText- Eigenschaft
Instanzen eines Formulars unterschiedlich zu benennen, um sie auseinander halten zu können. Vom Prinzip her würde sich für den Zweck der reinen Unterscheidung auch die hWnd-Eigenschaft anbieten, doch diese kann sich zur Laufzeit bekanntlich auch ändern.
ToolTipText- Eigenschaft Objekt.ToolTipText As String Betroffene Objekte
................................................... Betro ffene Objekte
Beschreibung
................................................... Bes c hreibung
Die ToolTipText-Eigenschaft bestimmt den Text für die Anzeige der QuickInfo. Anwendung
................................................... Anwendung
Visual Basic unterstützt vier verschiedene Techniken, um für den Benutzer einer Anwendung Hilfe bereitzustellen: QuickInfo, Statusanzeige, Direkthilfe, und kontextbezogene Hilfe. Die ersten beiden Techniken bieten dem Benutzer unaufgefordert und in knapper Form Hilfestellung für den Umgang mit der Benutzerumgebung. Um eine QuickInfo für ein Steuerelement bereitzustellen, weisen Sie der Eigenschaft ToolTipText eine Zeichenfolge mit dem Hilfetext zu und setzen – sofern vorhanden – die ShowTips-Eigenschaft auf True. Die Zeichenfolge sollte nicht zu lang sein, da die Anzeige einzeilig ist. Verwandte Eigenschaften
................................................... Verwa ndte Eigens c ha ften HelpContextID, ShowTips, WhatsThisButton, WhatsThisHelp, WhatsThisHelpID
UseMaskColor- Eigenschaft Objekt.UseMaskColor As Boolean Betroffene Objekte
................................................... Betro ffene Objekte CheckBox, CommandButton, OptionButton, ImageList Beschreibung
................................................... Bes c hreibung
Die UseMaskColor-Eigenschaft bestimmt, ob die für die MaskColor-Eigenschaft gesetzte Farbe für die Maskierung transparenter Bereiche verwendet wird (True) oder nicht (False). Anwendung
................................................... Anwendung
Bei Schaltflächen, Optionsfeldern und Kontrollkästchen mit benutzerdefinierter Darstellung ermöglicht die UseMaskColor-Eigenschaft das Ein- und Ausschalten der Pseudotransparenz (vgl. »MaskColor-Eigenschaft«, S. 358). Gleiches gilt für Steuerelemente, die anders als die genannten Steuerelemente standardmäßig mit Bildern eines ImageList-Objekts arbeiten. Sie sind darauf angewiesen, dass die UseMaskColor-Eigenschaft des ImageList-Objekts gesetzt ist, die sich dann immer für alle Bilder in der Liste auswirkt.
377
Gemeinsame Eigenschaften
Adodc, Animation, Button, CheckBox, ComboBox, CommandButton, CoolBar, Data, DataCombo, DataGrid, DataList, DataRepeater, DateTimePicker, DirListBox, DriveListBox, FileListBox, FlatScrollBar, Frame, Image, ImageCombo, Label, ListItem, ListSubItem, ListView, ListBox, MaskEdBox, MMContol, MonthView, MSChart, MSFlexGrid, MSHFlexGrid, OptionButton, Panel, PictureBox, ProgressBar, RemoteData, RichTextBox, Slider, SSTab, StatusBar, Tab, TabStrip, TextBox, TreeView, UpDown
Gemeinsame Eigenschaften
Verwandte Eigenschaften
................................................... Verwa ndte Eigens c ha ften MaskColor
Visible- Eigenschaft Objekt.Visible As Boolean Betroffene Objekte
Gemeinsame Eigenschaften
................................................... Betro ffene Objekte Adodc, Animation, Band, Button, ButtonMenu, CheckBox, ComboBox, CommandButton, Data, DataCombo, DataGrid, DataList, DataRepeater, DateTimePicker, DirListBox, DriveListBox, FileListBox, FlatScrollBar, Form, Frame, HScrollBar, Image, ImageCombo, Label, Line, ListView, ListBox, MaskEdBox, MDIForm, MMControl, MonthView, MSChart, MSFlexGrid, MSHFlexGrid, Node, OLE, OptionButton, Panel, PictureBox, ProgressBar, RemoteData, RichTextBox, RptFunction, RptLine, RptImage, RptLabel, RptShape, RptTextBox, RptSection, Shape, Slider, SSTab, StatusBar, TabStrip, TextBox, ToolBar, TreeView, UpDown, VScrollBar Beschreibung
................................................... Bes c hreibung
Die Visible-Eigenschaft bestimmt, ob ein Objekt sichtbar ist (True) oder nicht (False). Unsichtbare Objekte können den Fokus nicht erhalten. Anwendung
................................................... Anwendung
Um ein Formular aus- oder einzublenden, können Sie wahlweise die Methoden Show und Hide aufrufen oder die Visible-Eigenschaft auf False bzw. True setzen. Wenn Sie eine neue Instanz für ein Steuerelemente-Array zur Laufzeit anlegen, »erbt« diese die meisten Eigenschaften der Instanz mit dem niedrigsten Index. hWnd erhält natürlich einen anderen Wert, und die Visible-Eigenschaft trägt standardmäßig den Wert False. Verwandte Eigenschaften
................................................... Verwa ndte Eigens c ha ften Enabled
WhatsThisHelp- Eigenschaft Objekt.WhatsThisHelp As Boolean Betroffene Objekte
................................................... Betro ffene Objekte Animation, DataGrid, Form, MDIForm, MSFlexGrid Beschreibung
................................................... Bes c hreibung
Die WhatsThisHelp-Eigenschaft bestimmt, ob die Hilfetaste (F1) die Direkthilfe in einem PopupFenster (True) oder die kontextbezogene Hilfe im Windows-Hilfesystem (False) anzeigt. Anwendung
................................................... Anwendung
Vgl. »HelpContextID-Eigenschaft und WhatsThisHelpID-Eigenschaft«, S. 353.
WhatsThisHelpID- Eigenschaft Siehe »HelpContextID-Eigenschaft und WhatsThisHelpID-Eigenschaft«, S. 353.
37 8
Width- Eigenschaft
Width- Eigenschaft Siehe »Height-Eigenschaft und Width-Eigenschaft«, S. 351.
Gemeinsame Methoden
Methode
Beschreibung
Drag
Leitet eine Drag&Drop-Operation für das Steuerelement manuell ein
LinkExecute
Versendet ein DDE-Kommando an das Quellobjekt einer bestehenden DDE-Verbindung
LinkPoke
Ändert den Wert des als Datenlieferant fungierenden Steuerelements aufseiten des Quellobjekts einer bestehenden DDE-Verbindung
LinkRequest
Fordert das Quellobjekt einer bestehenden DDE-Verbindung auf, den Wert des Zielobjekts zu aktualisieren
LinkSend
Schickt dem Zielobjekt einer bestehenden DDE-Verbindung ein LinkNotify-Ereignis, damit dieses seine LinkRequest-Methode zur erneuten Synchronisation aufruft
Move
Ändert Position und Abmessungen des Objekts innerhalb des Containers
OleDrag
Leitet eine OLE-Drag&Drop-Operation für ein Steuerelement ein
Refresh
Veranlasst das Objekt, seinen Zustand sowie seine Darstellung zu aktualisieren
SetFocus
Fordert den Fokus für das Steuerelement an
ShowWhatsThis
Zeigt die Direkthilfe für das Steuerelement an
ZOrder
Setzt das Steuerelement an die vorderste oder hinterste Position der Anzeigereihenfolge
Drag- Methode Sub Objekt.Drag(Action As Integer) Betroffene Objekte
................................................... Betro ffene Objekte Adodc, Animation, CheckBox, ComboBox, CommandButton, CoolBar, Data, DataCombo, DataGrid, DataList, DataRepeater, DateTimePicker, DirListBox, DriveListBox, FileListBox, FlatScrollBar, Frame, HScrollBar, Image, ImageCombo, Label, ListView, ListBox, MaskEdBox, MMControl, MonthView, MSChart, MSFlexGrid, MSHFlexGrid, OLE, OptionButton, PictureBox, ProgressBar, RemoteData, RichTextBox, Slider, SSTab, StatusBar, TabStrip, TextBox, ToolBar, TreeView, UpDown, VScrollBar
379
Gemeinsame Methoden
Auch bei den Methoden haben die Steuerelemente einiges an Gemeinsamkeiten zu bieten, die meisten Steuerelemente besitzen jedoch ungleich weniger Methoden als Eigenschaften. Natürlich relativiert sich diese Aussage, wenn man bedenkt, dass die Eigenschaften COM-konformer Objekte nicht als schlichte Datenelemente mit öffentlichem Geltungsbereich implementiert sind, sondern über Property-Methoden, die für den entsprechenden prozeduralen Unterbau sorgen. Mithin sind Eigenschaften also auch Methoden. Die folgende Tabelle gibt einen Überblick über die Methoden, die bei der Besprechung der einzelnen Steuerelemente sowie des Formularobjekts nicht extra diskutiert werden:
Gemeinsame Methoden
Beschreibung
................................................... Bes c hreibung
Die Drag-Methode dient der codeseitigen Steuerung von Drag&Drop-Operationen für Objekt im manuellen oder automatischen Modus (vgl. DragMode). Für den Parameter Action sind über den Aufzählungstyp DragConstants drei Werte definiert: vbBeginDrag (1) startet eine Drag&Drop-Operation; vbEndDrag (2) beendet die aktuelle Drag&Drop-Operation; vbCancel (0) bricht die aktuelle Drag&Drop-Operation ab. Wird Drag im Verlauf einer Drag&DropOperation mit dem Wert vbEndDrag aufgerufen, erhält das unter dem Mauszeiger befindliche Objekt ein DragDrop-Ereignis.
Gemeinsame Methoden
Anwendung
................................................... Anwendung
Die explizite Steuerung von Drag&Drop-Vorgängen ist im Allgemeinen nur erforderlich, wenn ein Objekt, dessen DragMode-Eigenschaft auf vbManual (0; Voreinstellung) gesetzt ist, eine Drag&Drop-Operation eingehen soll. Das kann beispielsweise der Fall sein, wenn eine Steuerung der Operation mit der Tastatur oder der rechten Maustaste erwünscht ist. Beispiel
................................................... Beis piel
Der folgende Code implementiert Drag&Drop mit der rechten Maustaste für ein Bezeichnungsfeld: Private Sub Label1_MouseDown(Button As Integer, Shift As Integer, _ X As Single, Y As Single) If Button = vbRightButton Then Label1.Drag vbBeginDrag End Sub Verwandte Methoden
................................................... Verwa ndte Metho den DragDrop, DragIcon, DragMode, DragOver Verwandte Themen
................................................... Verwandte Them en
Transparenz und Drag&Drop (S. 606)
LinkExecute- Methode Sub Objekt.LinkExecute (Command As String) As Boolean Betroffene Objekte
................................................... Betro ffene Objekte Label, PictureBox, TextBox Beschreibung
................................................... Bes c hreibung
Die Methode LinkExecute ermöglicht es dem Zielobjekt einer bestehenden DDE-Verbindung, eine Kommandozeichenfolge an das in einer anderen Anwendung gelegene Quellobjekt (Formular) zu senden. Aufseiten des Quellobjekts löst die Methode ein LinkExecute-Ereignis aus. Der erste Parameter des Ereignisses übermittelt die Zeichenfolge, den zweiten Parameter muss das Quellobjekt bei erfolgreicher Behandlung auf 0 setzen, sonst tritt beim Zielobjekt ein Laufzeitfehler auf. Anwendung
................................................... Anwendung
Um die LinkExecute-Methode ausführen zu können, muss zuvor eine DDE-Verbindung mit der Quellanwendung eröffnet worden sein. Für den Verbindungsaufbau ist es nötig, die Eigenschaft LinkTopic mit einer geeigneten Verbindungsinformation zu versorgen und danach die Eigen-
380
LinkPoke- Methode
schaft LinkMode auf einen Wert ungleich 0 zu setzen. Falls kein Laufzeitfehler 282 auftritt, kann das Zielobjekt davon ausgehen, dass die Verbindung besteht, und mit der Übermittlung von DDE-Kommandos beginnen. Da es keinen feststehenden Kommandosatz für DDE-Verbindungen gibt, unterliegt die Ausgestaltung der Kommandos sowie deren Interpretation allein der Fantasie des Programmierers. Beispiel
................................................... Beis piel
' Im Modul der Quellobjekts Private Sub Form_LinkExecute(CmdStr As String, Cancel As Integer) If CmdStr = "Beenden" Then Cancel = False ' Kommando erkannt! Unload Me ' Kommando ausführen End If End Sub Verwandte Methoden
................................................... Verwa ndte Metho den LinkClose, LinkError, LinkExecute, LinkItem, LinkMode, LinkNotify, LinkOpen, LinkPoke, LinkRequest, LinkSend, LinkTopic Verwandte Themen
................................................... Verwandte Them en
DDE-Verbindungen (S. 495)
LinkPoke- Methode Sub Objekt.Poke() Betroffene Objekte
................................................... Betro ffene Objekte Label, PictureBox, TextBox Beschreibung
................................................... Bes c hreibung
Die Methode LinkPoke ermöglicht es dem Zielobjekt einer bestehenden DDE-Verbindung, den Wert seiner Standardeigenschaft dem als Datenlieferant fungierenden Steuerelement aufseiten der Quellanwendung aufzuoktroyieren – lies: eine Synchronisation in umgekehrter Richtung vorzunehmen. Anwendung
................................................... Anwendung
In der Regel übermittelt bei einer DDE-Verbindung der Datenlieferant des Quellobjekts seinen Wert an das Zielobjekt. In speziellen Situation kann es jedoch erforderlich sein, diese Beziehung umzukehren und LinkPoke einzusetzen. Beachten sie aber, dass nicht alle DDE-Serveranwendungen den Einsatz von LinkPoke akzeptieren. Die Standardeigenschaften der betroffenen Steuerelemente sind: Text für das Textfeld, Picture für das Bildfeld und Caption für das Bezeichnungsfeld.
3 81
Gemeinsame Methoden
' Im Modul des Zielobjekts ... Text1.LinkExecute("Beenden") ...
Gemeinsame Methoden
Verwandte Methoden
................................................... Verwa ndte Metho den LinkClose, LinkError, LinkExecute, LinkItem, LinkMode, LinkNotify, LinkOpen, LinkRequest, LinkSend, LinkTopic Verwandte Themen
................................................... Verwandte Them en
DDE-Verbindungen (S. 495)
Gemeinsame Methoden
LinkRequest- Methode Sub Objekt.LinkRequest() Betroffene Objekte
................................................... Betro ffene Objekte Label, PictureBox, TextBox Beschreibung
................................................... Bes c hreibung
Die Methode LinkRequest fordert die Quellanwendung einer bestehenden DDE-Verbindung auf, den Wert des Zielobjekts mit dem Wert des Datenlieferanten zu synchronisieren. Anwendung
................................................... Anwendung
Der Aufruf der LinkRequest-Methode findet im Allgemeinen in Reaktion auf das LinkNotifyEreignis statt. Je nach Wert der LinkMode-Eigenschaft des Zielobjekts gibt es verschiedene Szenarien für die Synchronisation zwischen Quelle und Ziel. Hat LinkMode den Wert vbLinkAutomatic (1) und handelt es sich bei dem zu synchronisierenden Wert um eine Zeichenfolge, sorgt die Quellanwendung für die automatische Synchronisation, sobald sich aufseiten der Quelle eine Wertänderung ergibt – ein Aufruf von LinkRequest ist dann nur erforderlich, wenn der Wert aufseiten des Zielobjekts etwa durch Benutzeraktivitäten verloren gegangen ist. Wenn es sich im Automatikmodus bei dem zu synchronisierenden Wert um ein Bild handelt oder LinkMode den Wert vbLinkNotify (3) hat, schickt die Quellanwendung dem Zielobjekt ein LinkNotifyEreignis, damit dieses je nach Bedarf die Synchronisation explizit mittels LinkRequest vornehmen kann. Hat LinkMode den Wert vbLinkManual (2), liegt es allein am Zielobjekt, die Synchronisation mit LinkRequest anzustoßen; eine Benachrichtigung über Wertänderungen findet nicht statt. Verwandte Methoden
................................................... Verwa ndte Metho den LinkClose, LinkError, LinkExecute, LinkItem, LinkMode, LinkNotify, LinkOpen, LinkPoke, LinkSend, LinkTopic Verwandte Themen
................................................... Verwandte Them en
DDE-Verbindungen (S. 495)
LinkSend- Methode Sub Objekt.LinkSend() Betroffene Objekte
................................................... Betro ffene Objekte Label, PictureBox, TextBox
382
Move- Methode
Beschreibung
................................................... Bes c hreibung
Die LinkSend-Methode ermöglicht es dem Quellobjekt einer bestehenden DDE-Verbindung, das Zielobjekt über eine Wertänderung des als Datenlieferant fungierenden Steuerelements zu informieren. Die Benachrichtigung erfolgt in Form des LinkNotify-Ereignisses. Anwendung
................................................... Anwendung
Verwandte Methoden
................................................... Verwa ndte Metho den LinkClose, LinkError, LinkExecute, LinkItem, LinkMode, LinkNotify, LinkOpen, LinkPoke, LinkSend, LinkTopic Verwandte Themen
................................................... Verwandte Them en
DDE-Verbindungen (S. 495)
Move- Methode Sub Objekt.Move(Left, [Top], [Width], [Height]) Betroffene Objekte
................................................... Betro ffene Objekte Alle sichtbaren Steuerelemente und Formularobjekte Beschreibung
................................................... Bes c hreibung
Die Move-Methode erlaubt es, die Position sowie die Abmessungen des Steuerelements zu verändern. Der Parameter Left ist obligatorisch, die anderen drei Parameter sind optional. Anwendung
................................................... Anwendung
Vom Prinzip her macht es keinen Unterschied, ob Sie die Eigenschaften Left, Top, Width oder Height explizit auf andere Werte setzen oder die Move-Methode dafür verwenden, die MoveMethode wird allerdings etwas schneller ausgeführt, wenn mehr als eine Eigenschaft einen neuen Wert erhalten soll.
OLEDrag- Methode Sub Objekt.OLEDrag() Betroffene Objekte
................................................... Betro ffene Objekte Animation, CheckBox, ComboBox, CommandButton, CoolBar, Data, DataCombo, DataList, DateTimePicker, DirListBox, DriveListBox, FileListBox, Form, Frame, Image, ImageCombo, Label, ListView, ListBox, MaskEdBox, MDIForm, MMControl, MonthView, MSChart, MSFlexGrid, MSHFlexGrid, OptionButton, PictureBox, ProgressBar, PropertyPage, RichTextBox, Slider, SSTab, StatusBar, TabStrip, TextBox, ToolBar, TreeView, UpDown, UserControl, UserDocument
3 83
Gemeinsame Methoden
Änderungen in Textwerten signalisiert das Quellobjekt automatisch. Der Einsatz der LinkSendMethode ist also nur im Zusammenhang mit Bildern notwendig, deren ständige Synchronisation nur in seltenen Fällen wirklich erwünscht ist. Das Zielobjekt reagiert auf das von der LinkSend-Methode ausgelöste LinkNotify-Ereignis bei Bedarf mit einem LinkRequest-Aufruf, der schließlich die Synchronisation bewirkt.
Gemeinsame Methoden
Beschreibung
................................................... Bes c hreibung
Die OLEDrag-Methode ermöglicht die codeseitige Einleitung von OLE-Drag&Drop-Operationen im manuellen oder automatischen Modus (vgl. OLEDragMode) und löst insbesondere aufseiten des Quellobjekts das OLEStartDrag-Ereignis aus. Anwendung
Gemeinsame Methoden
................................................... Anwendung
Im Gegensatz zur Drag-Methode ist der Aufruf von OLEDrag parameterlos, da die weitere Steuerung der Operation durch OLE-Ereignisse geschieht und ein Abbruch oder gezieltes Beenden der Operation vonseiten des Quellobjekts her nicht vorgesehen ist. Das Quellobjekt kann die Operation allenfalls »sabotieren«, indem es auf das OLESetData-Ereignis hin keine Daten bereitstellt. Da eine begonnene OLE-Drag&Drop-Operation durch Loslassen der Maustaste abgeschlossen wird, dürfte es wenig Sinn machen, OLEDrag in einem anderen Szenario als während der Behandlung eines MouseDown-Ereignisses aufzurufen – schließlich muss der Benutzer die Maustaste irgendwann loslassen, was dann den Abschluss der Operation sicherstellt. Beispiel
................................................... Beis piel
Private Sub Text1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single) StatusBar1.SimpleText = "OLE-Drag gestartet" Text1.OLEDrag StatusBar1.SimpleText = "" End Sub Verwandte Methoden
................................................... Verwa ndte Metho den OLECompleteDrag, OLEDragMode, OLEDragOver, OLEDropMode, OLEGiveFeedback, OLESetData, OLEStartDrag Verwandte Themen
................................................... Verwandte Them en
OLE-Drag&Drop (S. 501)
Refresh- Methode Sub Objekt.Refresh() Betroffene Objekte
................................................... Betro ffene Objekte Adodc, CheckBox, ComboBox, CommandButton, CoolBar, Data, DataCombo, DataGrid, DataList, DataRepeater, DataReport, DateTimePicker, DBGrid, DBCombo, DBList, DirListBox, DriveListBox, FlatScrollBar, FileListBox, Form, Frame, HScrollBar, Image, ImageCombo, Label, Line, ListView, ListBox, MaskEdBox, MMControl, MonthView, MSChart, MSFlexGrid, MSHFlexGrid, OLE, OptionButton, PictureBox, ProgressBar, PropertyPage, RemoteData, RichTextBox, Shape, Slider, StatusBar, TabStrip, TextBox, ToolBar, TreeView, UserControl, UserDocument, VScrollBar Beschreibung
................................................... Bes c hreibung
Die Refresh-Methode aktualisiert die Darstellung eines Steuerelements bzw. Formulars. Bei Datensteuerelementen bewirkt Refresh eine explizite Aktualisierung des Recordset-Objekts sowie aller abhängigen Steuerelemente.
384
SetFocus- Methode
Anwendung
................................................... Anwendung
Warnung
................................................... Wa rnung
Der Einsatz von Refresh innerhalb einer Paint-Routine kann zu endlosen Ereignisketten führen (zur Rettung eines Projekts für den Fall der Fälle, vgl. »Schleifen«, S. 40). Beispiel
................................................... Beis piel
Die folgende Routine demonstriert den Unterschied für ein Listenfeld List1. Wenn Sie den Refresh-Aufruf auskommentieren, erfolgt die Aktualisierung der Anzeige erst nach Beendigung der Schleife. Private Sub Form_Click() Print Time For i = 1 To 4 List1.AddItem "Eintrag" & i, 0 Next i List1.Refresh ' umgehende Anzeige For i = 1 To 10000000 ' Zeitverzögerung a = Sqr(1) Next Print Time End Sub
SetFocus- Methode Sub Objekt.SetFocus() Betroffene Objekte
................................................... Betro ffene Objekte Adodc, Animation, CheckBox, ComboBox, CommandButton, DataCombo, DataGrid, DataList, DataRepeater, DataReport, DateTimePicker, DBGrid, DBCombo, DBList, DirListBox, DriveListBox, FlatScrollBar, FileListBox, Form, HScrollBar, ImageCombo, ListView, ListBox, MaskEdBox, MMControl, MonthView, MSChart, MSFlexGrid, MSHFlexGrid, OLE, OptionButton, PictureBox, PropertyPage, RichTextBox, Slider, TabStrip, TextBox, TreeView, UpDown, UserControl, UserDocument, VScrollBar
3 85
Gemeinsame Methoden
Im Allgemeinen frischen Steuerelemente nach einer Wertänderung ihre Darstellung von selbst auf, sobald sich die Gelegenheit dafür ergibt. Die Priorität für diesen Vorgang ist allerdings nicht sehr hoch. Wenn jedoch wichtigere Ereignisse auf Behandlung warten (und das sind fast alle, außer Timer-Ereignissen) oder die Behandlung des aktuellen Ereignisses noch einiges an Laufzeit beanspruchen wird, lässt sich mittels Refresh eine umgehende Aktualisierung der Darstellung erreichen. Bei Formularen, deren AutoRedraw-Eigenschaft nicht auf True gesetzt wurde, bewirkt Refresh unter anderem die Ausführung der Paint-Routine. Die Methode sollte daher immer zum Aufruf kommen, wenn der Client-Bereich komplett neu gezeichnet werden soll, beispielsweise nach einer Änderung des Koordinatensystems oder im Zuge einer Resize-Behandlung. Für die Datensteuerelemente Data, RemoteData und Adodc sind Refresh-Aufrufe jedes Mal dann notwendig, wenn sich eine der Eigenschaften DatabaseName, RecordSource, ReadOnly, Exclusive oder Connect geändert hat und ein neues Recordset erstellt werden muss.
Gemeinsame Methoden
Beschreibung
................................................... Bes c hreibung
Die SetFocus-Methode gibt den Fokus ungeachtet der Tabulatorreihenfolge direkt an das Steuerelement Objekt weiter und löst dabei folgende Ereigniskette aus: Validate (abhängig vom Wert der CausesValidation-Eigenschaft) und LostFocus bei dem Objekt, das den Fokus verliert, sowie GotFocus bei Objekt. Anwendung
Gemeinsame Methoden
................................................... Anwendung
Das Objekt, das den Fokus abgeben soll, kann die Weitergabe verhindern, indem es bei der Validate-Behandlung den Cancel-Parameter auf True setzt. Umgekehrt resultiert ein Laufzeitfehler, wenn die Enabled-Eigenschaft des Empfängerobjekts auf False gesetzt ist. Beachten Sie jedoch, dass der Fokusempfang trotz gesetzter Locked-Eigenschaft möglich ist, denn auch wenn der Benutzer den Wert nicht ändern kann, Markierungen kann er vornehmen. Warnung
................................................... Wa rnung
Der unvorsichtige Einsatz von SetFocus kann zu endlosen Ereignisketten führen, wenn sich Steuerelemente den Fokus wechselseitig im Rahmen der GotFocus-Behandlung zuschanzen. (Zur Rettung des Projekts für den Fall der Fälle: vgl. »Schleifen«, S. 40.)
ShowWhatsThis- Methode Sub Objekt.ShowWhatsThis() Betroffene Objekte
................................................... Betro ffene Objekte Alle sichtbaren Steuerelemente Beschreibung
................................................... Bes c hreibung
Zeigt die Direkthilfe für das Steuerelement im Popup-Hilfefenster an. Der Hilfetext ist über die Eigenschaft WhatsThisHelpID indiziert und entstammt der in App.HelpFile spezifizierten Datei. Damit die Methode in Aktion tritt, muss die WhatsThisHelp-Eigenschaft des Formulars auf True gesetzt sein.
................................................... Verwa ndte Eigens c ha ften HelpContextId, App.HelpFile
WhatsThisButton,
WhatsThisHelp,
WhatsThisHelpID,
WhatsThisMode,
Verwandte Themen
................................................... Verwandte Them en
HelpContextID-Eigenschaft und WhatsThisHelpID-Eigenschaft (S. 353)
ZOrder- Methode Sub Objekt.ZOrder([Position]) Betroffene Objekte
................................................... Betro ffene Objekte Alle Steuerelemente mit sichtbarer Darstellung, Formulare, MDI-Formulare Beschreibung
................................................... Bes c hreibung
Unter Z-Ordnung versteht man die Reihenfolge, in der die Objekte eines Containers innerhalb der jeweiligen Grafikebene gezeichnet werden. Die ZOrder-Methode setzt Objekt an die oberste oder unterste Position in der Z-Ordnung des zugehörigen Containers. Fehlt der Parameter
386
Standardsteuerelemente
Position oder ist er 0, platziert die Methode das Objekt ganz nach vorn, so dass es potenziell andere verdecken kann. Ist der Wert 1, platziert die Methode das Objekt nach hinten, so dass es potenziell von allen anderen verdeckt werden kann. Anwendung
................................................... Anwendung
Beispiel
................................................... Beis piel
' Z-Ordnung wiederherstellen For i = 31 To 0 Step -1 figur(i).ZOrder Next i Verwandte Themen
................................................... Verwandte Them en
Schach – klare Sicht auf Hintergründliches (S. 607)
Standardsteuerelemente CheckBox, ComboBox, CommandButton, Data, DirListBox, DriveListBox, FileListBox, Frame, HScrollBar, Image, Label, Line, ListBox, OLE, OptionButton, PictureBox, Shape, TextBox, Timer, VScrollBar Beschreibung
................................................... Bes c hreibung
Die Standardsteuerelemente sind in der standardmäßigen Werkzeugsammlung von Visual Basic enthalten und lassen sich ohne weitere Vorbereitung direkt für den Entwurf von Formularen, Benutzersteuerelementen, Eigenschaftsseiten und Benutzerdokumenten einsetzen – vorausgesetzt, die Werkzeugsammlung wurde über den Menübefehl ANSICHT/WERKZEUGSAMMLUNG eingeblendet. Um ein Steuerelement in den Entwurfsbereich etwa eines Formulars zu platzieren, klicken Sie zuerst auf das entsprechende Symbol in der Werkzeugleiste und ziehen dann im Entwurfsfenster mit der Maus einen Bereich auf, den das Steuerelement später einnehmen soll.
Auswahl eines Steuerelementtyps in der Werkzeugsammlung von Visual Basic
387
Standardsteuerelemente
Für die Steuerelemente eines Formulars sind drei Reihenfolgen zu unterscheiden: die Tabulatorreihenfolge, die Einfügereihenfolge und die Z-Ordnung oder Anzeigereihenfolge. Die Tabulatorreihenfolge ergibt sich aus der TabIndex-Eigenschaft der Steuerelemente, die den Fokus erhalten können, und regelt die Weitergabe des Fokus bei Betätigung der Tabulatortaste. Die Einfügereihenfolge ergibt sich aus der Reihenfolge, in der die Steuerelemente auf dem Formular (Container) platziert wurden, unter Berücksichtigung aller nachträglichen Vertauschungen über das Menü FORMAT/REIHENFOLGE. Sie bestimmt die Reihenfolge, in der Visual Basic die einzelnen Steuerelemente deklariert, und spiegelt sich (umgekehrt) in der Elementreihenfolge der Auflistung Controls wider. Die nur zur Laufzeit existierende Z-Ordnung ergibt sich zunächst aus der Einfügereihenfolge und regelt, in welcher Reihenfolge ein Formular (Container) die Steuerelemente zeichnet. Falls sich die Bereiche zweier Steuerelemente überschneiden, wird dasjenige mit der niedrigeren Z-Ordnung zuerst gezeichnet – und damit von dem anderen ganz oder teilweise verdeckt.
Standardsteuerelemente
Standardsteuerelemente
Abgesehen von dem Zeitgeber-Steuerelement (Timer), das zur Laufzeit nicht sichtbar ist, legen Sie über den Bereich die spätere Größe und Position des Steuerelements fest. Um Größe oder Position eines bereits eingefügten Steuerelements danach noch einmal zu ändern, klicken Sie das betreffende Steuerelement einfach an und ziehen den Bereich an die gewünschte Position bzw. in die gewünschte Größe. Die folgende Abbildung zeigt die Entwurfsansicht eines Formulars, auf dem (ein wenig wahllos) alle Standardsteuerelemente zu sehen sind.
Formular, auf dem (wahllos) alle Standardsteuerelemente platziert wurden
Die Steuerelemente auf dem Formular sind in drei Spalten organisiert. In der linken Spalte sind zwei Kontrollkästchen (CheckBox) in einem Figur-Steuerelement (Shape) enthalten, darunter drei Optionsfelder (OptionButton) in einem Rahmensteuerelement (Frame), dann ein Datensteuerelement (Data) und schließlich ein Zeitgeber-Steuerelement (Timer), das zur Laufzeit nicht sichtbar ist und folglich an beliebiger Stelle platziert sein kann. Die mittlere Spalte beginnt mit zwei Textfeldern (TextBox), darunter ein Kombinationsfeld (ComboBox) sowie ein Listenfeld (ListBox), daneben eine vertikale Bildlaufleiste (VScrollBar), darunter eine horizontale Bildlaufleiste (HScrollBar) und schließlich ein Anzeige-Steuerelement (Image). Die optische Abtrennung der nächsten Spalte übernimmt ein Liniensteuerelement (Line). Die rechte Spalte beginnt mit einem Beschriftungssteuerelement (Label) über einem Bildfeld-Steuerelement (PictureBox) neben einer Schaltfläche (CommandButton) über einem OLE-Container-Steuerelement (OLE). Darunter dann ein Laufwerklistenfeld (DriveListBox), ein Verzeichnislistenfeld (DirListBox) und schließlich ein Dateilistenfeld (FileListBox). Steuerelement
Objektdatentyp
Beschreibung
Anzeige
Image
Ermöglicht die Anzeige eines Bildes
Befehlsschaltfläche
CommandButton
Ermöglicht die Ausführung einer Aktion bzw. eines Befehls
Bezeichnungsfeld
Label
Ermöglicht Beschriftungen
Bildfeld
PictureBox
Stellt ein Fenster für die Anzeige von Bildern sowie die Ausgabe von Text und Grafik bereit
388
Standardsteuerelemente
Objektdatentyp
Beschreibung
Bildlaufleiste horizontal
HScrollBar
Übernimmt die analoge Darstellung der Position und des prozentualen Anteils eines horizontalen Ausschnitts bezogen auf ein größeres Ganzes und ermöglicht die Veränderung der horizontalen Position über eine Bildlaufmarke
Bildlaufleiste vertikal
VScrollBar
übernimmt die analoge Darstellung der Position und des prozentualen Anteils eines vertikalen Ausschnitts bezogen auf ein größeres Ganzes und ermöglicht die Veränderung der vertikalen Position über eine Bildlaufmarke
Dateilistenfeld
FileListBox
Zeigt eine Liste der Dateien im gegebenen Verzeichnis an und ermöglicht die Auswahl
Daten
Data
Ermöglicht den Datenbankzugriff auf Basis der Microsoft Jet Engine oder der ODBC-Schnittstelle
Figur
Shape
Ermöglicht die Anzeige verschiedener geometrischer Figuren – Kreis, Ellipse, Rechteck, Quadrat, abgerundetes Rechteck, abgerundetes Quadrat
Kombinationsfeld
ComboBox
Kombination aus Textfeld und Listenfeld
Kontrollkästchen
CheckBox
Ermöglicht die Auswahl einer Option
Laufwerklistenfeld
DriveListBox
Zeigt eine Liste der logischen Laufwerke des Systems an und ermöglicht die Auswahl
Linie
Line
Ermöglicht die Anzeige einer Linie
Listenfeld
ListBox
Ermöglicht die Anzeige einer Zeichenfolgenliste sowie die Auswahl daraus
OLE-Container
OLE
Ermöglicht die Anzeige und Bearbeitung eingebetteter oder verknüpfter Objekte anderer Anwendungen
Optionsfeld
OptionButton
Ermöglicht die Auswahl einer Option unter vielen
Rahmen
Frame
Ermöglicht die sichtbare Gruppierung von Steuerelementen
Textfeld
TextBox
Ermöglicht die Ein- und Ausgabe von Text
Verzeichnislistenfeld
DirListBox
Zeigt das hierarchische Umfeld des eingestellten Verzeichnisses für ein gegebenes Laufwerk an und ermöglicht die Auswahl eines beliebigen Verzeichnisses auf diesem Laufwerk
Zeitgeber
Timer
Ermöglicht die zeitgebundene, periodische Ausführung von Code
Visual Basic vereinbart für jedes Steuerelement implizit ein Objekt und schlägt dafür einen Bezeichner in der Name-Eigenschaft vor. Sofern das Steuerelement eine Beschriftung oder eine
389
Standardsteuerelemente
Steuerelement
Standardsteuerelemente
Standardsteuerelemente
Zeichenfolge als Wert hat, setzt Visual Basic den gewählten Bezeichner dafür ein – das zeigt die Abbildung recht schön. Die meisten Eigenschaften eines Steuerelementobjekts lassen sich bereits zur Entwurfszeit im Eigenschaftenfenster definieren (viele der Entwurfseigenschaften sind zur Laufzeit sogar schreibgeschützt), manche aber auch erst zur Laufzeit. Die Abbildung zeigt das Eigenschaftenfenster eines Textfeldes.
Eigenschaftenfenster mit den Entwurfseigenschaften eines Textfeldes
Anzeige- Steuerelement (Image) Image1 As Image Beschreibung
................................................... Bes c hreibung
Das Anzeige-Steuerelement dient der Anzeige von Bildern, die als Bitmap in einem der Formate BMP, JPG, JPEG, GIF, WMF, EMF, ICO oder CUR vorliegen. Die Standardeigenschaft des Steuerelements ist Picture.
390
Anzeige- Steuerelement ( Image)
Eigenschaften
................................................... Eigens c ha ften Appearance, BorderStyle, Container, DataChanged, DataField, DataFormat, DataMember, DataSource, DragIcon, DragMode, Enabled, Height, Index, Left, MouseIcon, MousePointer, Name, OLEDragMode, OLEDropMode, Parent, Picture, Stretch, Tag, ToolTipText, Top, Visible, WhatsThisHelpID, Width Methoden
................................................... Metho den Drag, Move, OLEDrag, Refresh, ShowWhatsThis, ZOrder Ereignisseza
Click, DblClick, DragDrop, DragOver, MouseDown, MouseMove, MouseUp, OLECompleteDrag, OLEDragDrop, OLEDragOver, OLEGiveFeedback, OLESetData, OLEStartDrag Anwendung
................................................... Anwendung
Bestimmung dieses Steuerelements ist die ressourcenschonende und schnelle Anzeige von Bildern in verschiedenen Grafikformaten. Hat die Eigenschaft Stretch den Wert False, behält das Bild seine vordefinierte Größe, und es hängt von der Bereichsgröße des Steuerelements ab, ob es vollständig oder nur teilweise (von links oben aus gesehen) dargestellt werden kann. Andernfalls wird das Bild unter Inkaufname einer horizontalen Streckung oder Stauchung in den Bereich des Steuerelements eingepasst. Wenn Sie der Eigenschaft Picture zur Entwurfszeit ein Bild zuordnen, speichert Visual Basic eine Kopie des Bildes als binären Anhang der Programmdatei bzw. in der kompilierten Version als Teil der ausführbaren Datei. Wenn Sie eine aktuelle Fassung des Bildes zur Laufzeit laden wollen (Verknüpfung), müssen Sie das Bild mit der LoadPicture-Funktion laden und dem Anzeige-Steuerelement zuweisen. Warnung
................................................... Wa rnung
Den Eigenschaften Height und Width des Datentyps Picture liegt die Maßeinheit vbHiMetric zugrunde. Wenn Sie die Bereichsgröße eines Anzeige-Steuerelements an die Abmessungen eines Bildes anpassen wollen, müssen Sie die Methoden ScaleX und ScaleY für die Umrechnung der Maße in die Maßeinheiten des Containers bemühen. Beispiel
................................................... Beis piel
Der folgende Code demonstriert, wie man ein Bild in ein Anzeige-Steuerelement lädt und dessen Bereichsgröße an die Abmessungen der Bitmap anpasst. Dim pic As Picture Set pic = LoadPicture(App.Path + "\MeinBild.bmp") Image1.Width = ScaleX(pic.Width, vbHimetric, ScaleMode) Image1.Height = ScaleY(pic.Height, vbHimetric, ScaleMode) Image1 = pic Verwandte Steuerelemente
................................................... Verwandte Steuerelemente ImageList, PictureBox, RptImage
3 91
Standardsteuerelemente
................................................... Ereignis s e
Standardsteuerelemente
Befehlsschaltfläche- Steuerelement (CommandButton) Command1 As CommandButton
Beschreibung
Standardsteuerelemente
................................................... Bes c hreibung
Das Befehlsschaltfläche-Steuerelement ist als Visualisierung für die Funktion des einfachen beschrifteten Tasters gedacht. Klickt der Benutzer darauf oder betätigt er die Schaltfläche mit der Tastatur, ändert sich für die Zeitdauer der Betätigung seine Darstellung sowie der Wert der Value-Eigenschaft, und das Click-Ereignis tritt auf. Eigenschaften
................................................... Eigens c ha ften Appearance, BackColor, CausesValidation, Cancel, Caption, Container, Default, DisabledPicture, DownPicture, DragIcon, DragMode, Enabled, Font, FontBold, FontItalic, FontName, FontSize, FontStrikeThru, FontUnderline, ForeColor, Height, HelpContextID, hWnd, Index, Left, MaskColor, MouseIcon, MousePointer, Name, OLEDropMode, Parent, Picture, RightToLeft, Style, TabIndex, TabStop, Tag, ToolTipText, Top, UseMaskColor, Value, Visible, WhatsThisHelpID, Width Methoden
................................................... Metho den Drag, Move, OLEDrag, Refresh, SetFocus, ShowWhatsThis, ZOrder Ereignisse
................................................... Ereignis s e
Click, DragDrop, DragOver, GotFocus, KeyDown, KeyUp, KeyPress, LostFocus, MouseDown, MouseMove, MouseUp, OLECompleteDrag, OLEDragDrop, OLEDragOver, OLEGiveFeedback, OLESetData, OLEStartDrag, Validate Anwendung
................................................... Anwendung
Für gewöhnlich wird die Befehlsschaltfläche dafür genutzt, Aktionen bzw. Befehle explizit durch einen Klick mit der Maus auszulösen. So für die Befehle »OK«, »Abbrechen« und »Übernehmen« in Dialogfeldern, aber auch für andere formularspezifische Kommandos. Vom Prinzip her ließen sich dafür natürlich auch andere Steuerelemente verwenden, die eine Click-Behandlung ermöglichen, doch die Befehlsschaltfläche ist speziell auf diese Aufgabe zugeschnitten. Insbesondere besitzt sie die Eigenschaften Default und Cancel, deren Werte darüber entscheiden, ob auch die Tasten (Eingabe) oder (Esc) das Click-Ereignis auslösen können. Für die beiden Eigenschaften gelten folgende Regeln: 1. In einem Formular kann immer nur die Default-Eigenschaft einer einzigen Befehlsschaltfläche auf True gesetzt sein. Das macht sie zur Standardschaltfläche. Analoges gilt für die Cancel-Eigenschaft. 2. Setzt man die Default-Eigenschaft einer Schaltfläche auf True, wird sie zur Standardschaltfläche und gleichzeitig ändert sich die Default-Eigenschaft der bisherigen Standardschaltfläche auf False. Analoges gilt für die Cancel-Eigenschaft. 3. Es kann immer nur eine Befehlsschaltfläche in einem Formular auf die Eingabetaste reagieren. Das ist entweder die Schaltfläche, die den Fokus besitzt, oder, falls keine der Schaltflächen in dem Formular den Fokus besitzt, die Standardschaltfläche. Analoges gilt für die
392
Bezeichnungsfeld- Steuerelement (Label)
Cancel-Eigenschaft. Den Fokusbesitz zeigt eine Schaltfläche durch einen zusätzlichen gestrichelten Innenrand an. Ist keine der Schaltflächen im Besitz des Fokus, lässt sich die Standardschaltfläche an einer verdickten Randlinie (vgl. Abbildung) erkennen.
Beispiel
................................................... Beis piel
Die Click-Routinen der Schaltflächen Abbrechen und Beenden schließen beide das Formular. OK ist Standardschaltfläche und reagiert auf die Eingabetaste, während Abbrechen auf die Taste (Esc) reagiert. OK_Click verwertet im Gegensatz zu Abbrechen_Click die Eingaben des Benutzers. ... OK.Default = True Abbrechen.Cancel = True ... Private Sub OK_Click() VerwerteEingaben Unload Me End Sub Private Sub Abbrechen_Click() Unload Me End Sub
' Eingaben des Benutzers verwerten
' Eingaben werden verworfen
Verwandte Steuerelemente
................................................... Verwandte Steuerelemente Button, CheckBox, OptionButton
Bezeichnungsfeld- Steuerelement (Label) Label1 As Label Beschreibung
................................................... Bes c hreibung
Das Bezeichnungsfeld-Steuerelement hat als Standardeigenschaft eine Zeichenfolge, die es in seinem Bereich gegebenenfalls umbrochen anzeigt. Eine Bearbeitung der Zeichenfolge durch Benutzer ist nicht möglich.
3 93
Standardsteuerelemente
Die Caption-Eigenschaft ermöglicht die Beschriftung der Schaltfläche. Um eine Zugriffstaste für das Steuerelement zu vereinbaren, stellen Sie dem entsprechenden Buchstaben in der Beschriftung das Zeichen »&« voran. Er wird zur Laufzeit durch Unterstreichung kenntlich gemacht (vgl. Abbildung). Befehlsschaltflächen kennen zwei unterschiedliche Darstellungen: die Standarddarstellung im Stil von Windows 9x und die benutzerdefinierte Darstellung. Um dem Steuerelement ein benutzerdefiniertes Aussehen zu verleihen, können Sie den Eigenschaften Picture und DownPicture sowie gegebenenfalls DisabledPicture geeignete Bilder zuordnen. Falls Sie eine pseudotransparente Darstellung wünschen, bei der die über eine Transparenzfarbe (MaskColor) definierten transparenten Bereiche der Bilder in Hintergrundfarbe erscheinen, müssen Sie die Eigenschaft UseMaskColor auf True setzen. Die Style-Eigenschaft entscheidet darüber, welche der Darstellungen zur Anzeige kommt. (Ein Beispiel dafür findet sich im Abschnitt »DisabledPictureEigenschaft«, S. 341.)
Standardsteuerelemente
Eigenschaften
................................................... Eigens c ha ften Alignment, Appearance, AutoSize, BackColor, BackStyle, BorderStyle, Caption, Container, DataChanged, DataField, DataFormat, DataMember, DataSource, DragIcon, DragMode, Enabled, Font, FontBold, FontItalic, FontName, FontSize, FontStrikeThru, FontUnderline, ForeColor, Height, Index, Left, LinkItem, LinkMode, LinkTimeout, LinkTopic, MouseIcon, MousePointer, Name, OLEDropMode, Parent, RightToLeft, TabIndex, Tag, ToolTipText, Top, UseMnemonic, Visible, WhatsThisHelpID, WordWrap Methoden
Standardsteuerelemente
................................................... Metho den Drag, LinkExecute, LinkPoke, LinkRequest, LinkSend, Move, OLEDrag, Refresh, ShowWhatsThis, ZOrder Ereignisse
................................................... Ereignis s e
Change, Click, DblClick, DragDrop, DragOver, GotFocus, KeyDown, KeyUp, KeyPress, LinkClose, Link, Error, LinkNotify, LinkOpen, MouseDown, MouseMove, MouseUp, OLECompleteDrag, OLEDragDrop, OLEDragOver, OLEGiveFeedback, OLESetData, OLEStartDrag Anwendung
................................................... Anwendung
Das Bezeichnungsfeld-Steuerelement ist als Mittel für die »externe« Beschriftung von Steuerelementen gedacht, wenn diese selbst keine Möglichkeit dafür vorsehen, also keine Caption-Eigenschaft besitzen. Zur Gestaltung der Textausgabe können Sie die Schriftattribute über die jeweiligen Font-Eigenschaften setzen. Für die Beschriftung von Bildern (beispielsweise in Image-Steuerelementen) kann es vorteilhaft sein, einen durchsichtigen Bereichshintergrund für das Bezeichnungsfeld einzustellen. Setzen Sie dazu die Eigenschaft BackStyle auf vbTransparent (0). Der Wert des Steuerelements lässt sich zur Laufzeit nach Belieben und mit sofortiger Wirkung durch einfache Zuweisung einer anderen Zeichenfolge ändern. Dies kann auch automatisch passieren, wenn Sie das Steuerelement über die Link-Eigenschaften zum Ziel einer DDE-Verknüpfung erklären oder es über die Data-Eigenschaften an ein Feld in einer Datenbank binden. Im Zuge einer Wertänderung kann es allerdings passieren, dass nicht die gesamte Zeichenfolge im Bereich des Steuerelements darstellbar ist – restliche Zeichen werden dann abgeschnitten, wenn die AutoResize-Eigenschaft nicht auf True gesetzt ist. Sie müssen den Bereich daher entweder von vornherein großzügig bemessen oder Platz für die automatische Ausdehnung einkalkulieren. Zudem lässt sich die WordWrap-Eigenschaft auf True setzen, um einen automatischen Umbruch zu erhalten. Beachten Sie aber, dass sich der Bereich des Feldes dann in beide Richtungen (nach unten und nach rechts/links, bevorzugt aber nach unten) ausdehnen kann, je nachdem wie der Umbruch zustande kommt. Oft ist es dann besser, ein Textfeld mit Bildlaufleisten zu benutzen. Textfelder lassen sich übrigens hervorragend als Bezeichnungsfelder »verkleiden«, wenn man die Möglichkeit der Bearbeitung mittels der Locked-Eigenschaft unterbindet und die Eigenschaften BorderStyle und BackColor geeignet setzt. Nur ein transparenter Hintergrund lässt sich so nicht realisieren. Beispiel
................................................... Beis piel
Das folgende kleine Programm benutzt ein Bezeichnungsfeld Label1 und ein Timer-Steuerelement Timer1 für die animierte Wiedergabe einer Textsequenz mit Fade-out-Effekt. (Das vollständige Beispielprojekt FadeOut wird im Abschnitt »AutoSize-Eigenschaft«, S. 329, diskutiert.)
394
Bildfeld- Steuerelement (PictureBox)
Dim Texte() Dim i Private Sub Form_Load() Texte = Array("Rauchen", "schadet", "Ihrer", "Gesundheit") Label1 = Texte(0) End Sub
Standardsteuerelemente
Private Sub Timer1_Timer() Label1.Font.Size = Label1.Font.Size + 1.1 If Label1.FontSize > 40 Then Label1.FontSize = 8 i = (i + 1) Mod (UBound(Texte) + 1) Label1 = Texte(i) End If End Sub Verwandte Steuerelemente
................................................... Verwandte Steuerelemente TextBox, RptLabel, RptTextBox
Bildfeld- Steuerelement (PictureBox) Picture1 As PictureBox Beschreibung
................................................... Bes c hreibung
Die Standardeigenschaft des Bildfeld-Steuerelements ist Picture. Damit ist es für die Anzeige von Bildern prädestiniert. Neben der Anzeige von Bildern in beliebiger Skalierung (Verzerrung des Seitenverhältnisses möglich) kennt es aber auch die gleichen Grafikmethoden wie das Formular und ermöglicht insbesondere die Ausgabe von Text. Darüber hinaus kann es als Container für andere Steuerelemente fungieren. Eigenschaften
................................................... Eigens c ha ften Align, Appearance, AutoRedraw, AutoSize, BackColor, BorderStyle, ClipControls, CausesValidation, Container, CurrentX, CurrentY, DataChanged, DataField, DataFormat, DataMember, DataSource, DragIcon, DragMode, DrawMode, DrawStyle, DrawWidth, Enabled, FillColor, FillStyle, Font, FontBold, FontItalic, FontName, FontSize, FontStrikeThru, FontTransparent, FontUnderline, ForeColor, hDC, Height, HelpContextID, hWnd, Image, Index, Left, LinkItem, LinkMode, LinkTimeout, LinkTopic, MouseIcon, MousePointer, Name, OLEDragMode, OLEDropMode, Parent, Picture, RightToLeft, ScaleHeight, ScaleLeft, ScaleMode, ScaleTop, ScaleWidth, TabIndex, TabStop, Tag, ToolTipText, Top, Visible, WhatsThisHelpID, Width Methoden
................................................... Metho den Circle, Cls, Drag, Line, LinkExecute, LinkPoke, LinkRequest, LinkSend, Move, OLEDrag, PaintPicture, Point, PSet, Print, Refresh, Scale, ScaleX, ScaleY, SetFocus, ShowWhatsThis, TextHeight, TextWith, ZOrder Ereignisse
................................................... Ereignis s e
Change, Click, DblClick, DragDrop, DragOver, GotFocus, KeyDown, KeyUp, KeyPress, LinkClose, LinkError, LinkNotify, LinkOpen, LostFocus, MouseDown, MouseMove, MouseUp, OLECompleteDrag, OLEDragDrop, OLEDragOver, OLEGiveFeedback, OLESetData, OLEStartDrag, Paint, Resize, Validate
3 95
Standardsteuerelemente
Anwendung
Standardsteuerelemente
................................................... Anwendung
Das Bildfeld-Steuerelement besitzt im Wesentlichen die gleiche Ausstattung wie das Formular – darunter insbesondere die Methoden für die Ausgabe von Bildern (PaintPicture), Text (TextHeight, TextWidth, Print) und Grafik (Circle, Line, PSet, Point) sowie natürlich auch ein eigenes Koordinatensystem (Scale, ScaleX, ScaleY), nicht zu vergessen die Kombination PaintMethode und AutoRedraw-Eigenschaft sowie das Modell der Ausgabe auf drei Ebenen (vgl. »Paint-Ereignis«, S. 253). Auch kann das Bildfeld als Container für andere Steuerelemente fungieren, etwa für die Gruppierung von Optionsfeldern und/oder Kontrollkästchen. Obwohl das Bildfeld wirklich viel vom Formular hat, bleibt es doch ein Steuerelement und kann als Quelle für OLE-Drag&Drop-Operationen oder DDE-Verbindungen fungieren. Trotz dieser reichhaltigen Ausstattung ist das Bildfeld aber – wie der Name schon vermuten lässt – in erster Linie als Steuerelement für die flexible Ausgabe von Bildern gedacht. Während das Anzeige-Steuerelement (Image) nur auf die reine, dafür aber schnelle und ressourcenschonende Ausgabe von Bildern spezialisiert ist und auf die Möglichkeiten, die sich durch Skalierung, Streckung oder Rasteroperationen eröffnen, verzichten muss, bietet das Bildfeld all diesen Komfort. Das Bildfeld-Steuerelement lässt sich als einziges Standardsteuerelement neben dem Datensteuerelement (Data) auf einem MDI-Formular platzieren. Es verhält sich dann wie eine Leiste und klebt zusammen mit anderen Leisten (ToolBar, CoolBar, ProgressBar, nicht jedoch StatusBar) in voller Breite am oberen Rand des Client-Bereichs. Sie können es dann beispielsweise dafür benutzen, sich eine benutzerdefinierte Symbolleiste zu basteln. Beispiel
................................................... Beis piel
Das für seinen Leistungsumfang eher zierlich geratene Projekt BilderViewer lässt sich als vollwertiger Bildbetrachter verwenden und wartet sogar mit OLE-Funktionalität auf. Im Formular finden Sie zwei Steuerelemente: ein Bildfeld Picture1, das die zentrierte und – in Bezug auf die aktuelle Formulargröße – maximierte Bildausgabe übernimmt, und ein Standarddialoge-Steuerelement CommonDialog1 für die unkomplizierte Eingabe eines Dateinamens. Neue Bilder lassen sich entweder über den Menübefehl DATEI/ÖFFNEN oder per OLE-Drag&Drop einfügen. Bei Größenänderungen des Formulars passt sich der Inhalt entsprechend an. ' Projekt: Viewer für Bilder Dim Pic As Picture Private Sub Form_Load() CommonDialog1.Filter = "Bilder (JPG, GIF, BMP)|*.jpg;*.gif;*.bmp" Picture1.Left = 0 Picture1.Top = 0 Picture1.Width = ScaleWidth Picture1.Height = ScaleHeight mnuDateiÖffnen_Click End Sub Private Sub Form_Resize() Picture1.Refresh End Sub
' Bildfeld neu einpassen
Private Sub mnuDateiÖffnen_Click() CommonDialog1.ShowOpen ' Bilddatei in Erfahrung bringen If CommonDialog1.FileName <> "" Then
396
Bildfeld- Steuerelement (PictureBox)
Set Pic = LoadPicture(CommonDialog1.FileName) Picture1.Refresh ' Bild und Bildfeld einpassen End If End Sub
Standardsteuerelemente
Private Sub Picture1_OLEDragDrop(Data As DataObject, Effect As Long, _ Button As Integer, Shift As Integer, X As Single, Y As Single) ' Datei von Explorer-Fenster? If Data.GetFormat(vbCFFiles) Then Set Pic = LoadPicture(Data.Files(1)) Picture1.Refresh End If ' Gewöhnliches Bildobjekt? If Data.GetFormat(vbCFDIB) Or Data.GetFormat(vbCFBitmap) Then Set Pic = Data.GetData(vbCFDIB) End If End Sub Private Sub Picture1_Paint() Dim PicWidth, PicHeight If Not Pic Is Nothing Then ' Bildfeld maximieren und zentrieren und ' Bild in maximaler Größe verzerrungsfrei einpassen PicWidth = ScaleX(Pic.Width, vbHimetric, vbTwips) PicHeight = ScaleY(Pic.Height, vbHimetric, vbTwips) If PicHeight / PicWidth > ScaleHeight / ScaleWidth Then Picture1.Height = ScaleHeight ' Maximieren Picture1.Width = ScaleHeight / PicHeight * PicWidth Picture1.Left = (ScaleWidth – Picture1.Width) / 2 ' Zentrieren Picture1.Top = 0 Else Picture1.Width = ScaleWidth Picture1.Height = ScaleWidth / PicWidth * PicHeight Picture1.Top = (ScaleHeight – Picture1.Height) / 2 Picture1.Left = 0 End If ' Bild einpassen Picture1.PaintPicture Pic, 0, 0, Picture1.ScaleWidth, _ Picture1.ScaleHeight End If End Sub Verwandte Steuerelemente
................................................... Verwandte S teuerelemente Image, Form, RptImage, UserControl, UserDocument Verwandte Themen
................................................... Verwa ndte Them en
Formulare (S. 306)
3 97
Standardsteuerelemente
Standardsteuerelemente
Fenster der Anwendung BilderViewer
Bildlaufleisten- Steuerelemente (HScrollBar, VScrollBar) VScroll1 As VScrollBar HScroll1 As HScrollBar Beschreibung
................................................... Bes c hreibung
Es gibt eine 16- und eine 32-Implementation der Bildlaufleiste, und Visual Basic 6.0 arbeitet (leider) noch mit der 16-Bit-Version, was gewisse Einschränkungen mit sich bringt. Die Bildlaufleisten-Steuerelemente HScrollBar und VScrollBar sind speziell für die analoge Visualisierung und Manipulation von Ausschnittsdarstellungen eingerichtet. Standardeigenschaft der Bildlaufleiste ist die Value-Eigenschaft. Sie ist vom Typ Integer und bestimmt die aktuelle Position der Bildlaufmarke. Den Wertebereich für die Bildlaufmarke legen die Eigenschaften Min und Max fest. Die Eigenschaften sind vom Typ Integer und müssen im Intervall 0 bis 32767 liegen, wobei Min kleiner als Max zu wählen ist. Darüber hinaus gestatten die Eigenschaften LargeChange und SmallChange die Definition von Werten für den Vorschub der Bildlaufmarke alternativ zur Ziehoperation. LargeChange ist für den Bildvorschub zuständig, der mit den Tasten (Bild-Auf) und (Bild-Ab) sowie durch Mausklicks in den Schaftbereich der Leiste zwischen Bildlaufmarke und Pfeilsymbole ausgelöst wird. SmallChange gibt den Wert für den »kleinen Vorschub« (Zeilenvorschub) mittels der Pfeiltasten (Auf), (Ab), (Links) und (Rechts) sowie der Pfeilsymbole an. Die Ausdehnung der Bildlaufmarke hängt von LargeChange ab, gibt aber das Größenverhältnis zwischen Ausschnitt und vollem Bereich (leider) nicht proportional wieder. Während der Benutzer die Bildlaufmarke auf eine andere Position zieht, signalisiert das Steuerelement Scroll-Ereignisse. Am Ende des Ziehvorgangs sowie bei diskreten Positionsänderungen per Tastatur oder Mausklick tritt das Change-Ereignis auf. Eigenschaften
................................................... Eigens c ha ften CausesValidation, Container, DragIcon, DragMode, Enabled, Height, HelpContextID, hWnd, Index, LargeChange, Left, Max, Min, MouseIcon, MousePointer, Name, Parent, RightToLeft, SmallChange, TabIndex, TabStop, Tag, Top, Value, Visible, WhatsThisHelpID, Width Methoden
................................................... Metho den Drag, Move, Refresh, SetFocus, ShowWhatsThis, ZOrder Ereignisse
................................................... Ereignis s e
Change, DragDrop, DragOver, GotFocus, KeyDown, KeyUp, KeyPress, LostFocus, Scroll, Validate
398
Bildlaufleisten- Steuerelemente ( HScrollBar, VScrollBar)
Anwendung
................................................... Anwendung
1. Definition des Wertebereichs – wählen Sie den Wertebereich (über die Eigenschaften Min und Max) nach Möglichkeit so, dass er den Wertebereich der Problemstellung entweder direkt wiedergibt oder zumindest entweder durch reine Translation (lies: Verschiebung) oder durch reine Skalierung (lies: Streckung/Stauchung) auf den Problembereich abzubilden ist. Skalierung und Translation sind mit viel Rechnerei verbunden. 2. Definition der Seitengröße – setzen Sie die Eigenschaft LargeChange auf die Breite des sichtbaren Ausschnitts, oder bei Verwendung des Steuerelements als Schieberegler auf eine geeignete Intervalllänge. Ein guter Wert für SmallChange ist in den meisten Fällen ein Zehntel von LargeChange. 3. Interpretation des Standardwerts – der Standardwert (Value) drückt die Position der Bildlaufmarke zwischen Min und Max aus, wobei die Breite der Marke sozusagen »herausgelogen« wird. Aus diesem Grund müssen Sie für die Definition von Max von der oberen Bereichsgrenze die Seitengröße LargeChange abziehen! Wird das Steuerelement dagegen wie ein Schieberegler benutzt, setzen Sie Max auf die obere Bereichsgrenze, ohne etwas abzuziehen. 4. Positionierung und Größenanpassung der Bildlaufleiste – wird die Bildlaufleiste in einer größenveränderlichen Komponente platziert, muss sie in den meisten Fällen in der verantwortlichen Resize-Routine oder dem entsprechenden Äquivalent gepflegt werden. Bei Größenänderungen einer Bildlaufleiste (Width und Height) bleibt die Breite der Bildlaufmarke prozentual erhalten. Insbesondere bleiben die Eigenschaften Min, Max, SmallChange und LargeChange sowie Value unverändert. 5. Pflege des Standardwerts – der Standardwert, der die Position der Bildlaufmarke bestimmt, wird vom Steuerelement selbst gepflegt. Seine Auswertung geschieht im Rahmen der ChangeBehandlung. Sie können den Wert mit sofortiger Wirkung auch explizit unter Beachtung von Min und Max setzen (jede Änderung des Werts generiert ein Change-Ereignis), nur nicht innerhalb der Scroll-Behandlung, da das nächste Scroll-Ereignis das Ergebnis sofort wieder zunichte macht. Beispiel
................................................... Beis piel
Die folgende weitgehend dem Projekt HexView entstammende Resize-Routine sorgt dafür, dass die Bildlaufleisten VScroll1 und HScroll1 am rechten bzw. unteren Rand im Client-Bereich des Formulars verbleiben und gegebenenfalls ausgeblendet werden, wenn der Client-Bereich für die Anzeige des Dokuments ausreicht. Die globalen Variablen DocWidth und DocHeight geben hier die Abmessungen des gesamten Dokuments wieder: Private Sub Form_Load() VScroll1.Top = 0 HScroll1.Left = 0 ...
' oben ankleben ' links ankleben
Private Sub Form_Resize()
3 99
Standardsteuerelemente
Der Einsatz der Bildlaufleiste bietet sich überall da an, wo es um die Auswahl eines Werts aus einem Wertebereich mit analoger Darstellung geht. Das kann eine Tonhöhe (Frequenzwert aus einem gegebenen Frequenzbereich) sein, eine Lautstärke oder ein Winkel für die perspektivische Darstellung. Beachten Sie aber, dass Sie mit dem Schieberegler-Steuerelement (Slider) ein Steuerelement zur Verfügung haben, das auf Analogregelung spezialisiert ist. Die Bildlaufleiste hat keine Skala, ist aber im Gegensatz zum Schieberegler in der Lage, die Ausdehnung des angezeigten Teilbereichs über die Breite der Bildlaufmarke zu visualisieren. Die Programmierung der Bildlaufleiste (gleich ob vertikal oder horizontal) hat mehrere Aspekte:
Standardsteuerelemente
Standardsteuerelemente
If Not WindowState = vbMinimized Then Height = IIf(Height < 1200, 1200, Height) Width = IIf(Width < 1000, 1000, Width) HScroll1.Visible = ScaleWidth < DocWidth ' Sichtbarkeit VScroll1.Visible = ScaleHeight < DocHeight ' Sichtbarkeit ' Abmessungen der Bildlaufleisten VScroll1.Left = ScaleLeft + ScaleWidth – VScroll1.Width VScroll1.Height = ScaleHeight + HScroll1.Height * HScroll1.Visible HScroll1.Top = ScaleTop + ScaleHeight – HScroll1.Height HScroll1.Width = ScaleWidth + VScroll1.Width * VScroll1.Visible ' vertikale Bildlaufparameter anpassen If VScroll1.Visible Then VScroll1.Max = DocHeight – ScaleHeight VScroll1.LargeChange = ScaleHeight * 0.95 VScroll1.SmallChange = VScroll1.LargeChange / 10 End If ' horizontale Bildlaufparameter anpassen If HScroll1.Visible Then HScroll1.Max = DocWidth – ScaleWidth HScroll1.LargeChange = ScaleWidth * 0.95 HScroll1.SmallChange = HScroll1.LargeChange / 10 End If Refresh End If End Sub Verwandte Steuerelemente
................................................... Verwandte Steuerelemente Slider, UpDown Verwandte Themen
................................................... Verwandte Them en
HexView – eine schnelle Textansicht für große Dateien (S. 551); Bildlauf – ein kleiner Betrachter für große Bilder (S. 545)
Dateilistenfeld- Steuerelement (FileListBox) File1 As FileListBox
Beschreibung
................................................... Bes c hreibung
Bei dem Dateilistenfeld-Steuerelement handelt es sich um ein Listenfeld, das darauf spezialisiert ist, den Inhalt des über die Path-Eigenschaft spezifizierten Verzeichnisses unter Beachtung eines Namensfilters (Pattern) sowie der über die Eigenschaften Archive, ReadOnly, System, Hidden und Normal spezifizierten Attribute in seiner Liste anzuzeigen. Über die Eigenschaft MultiSelect lässt sich zur Laufzeit festlegen, ob der Benutzer nur einen einzelnen Dateinamen (vbMultiSelectNone = 0), mehrere Dateinamen in einem Block
400
Dateilistenfeld- Steuerelement ( FileListBox)
(vbMultiSelectSimple = 1) oder mehrere (vbMultiSelectExtended = 2) auswählen kann.
einzelne
Dateinamen
bzw.
Blöcke
Eigenschaften
................................................... Eigens c ha ften
Methoden
................................................... Metho den Drag, Move, OLEDrag, Refresh, SetFocus, ShowWhatsThis, ZOrder Ereignisse
................................................... Ereignis s e
Click, DblClick, DragDrop, DragOver, GotFocus, KeyDown, KeyPress, KeyUp, LostFocus, MouseDown, MouseMove, MouseUp, OLECompleteDrag, OLEDragDrop, OLEDragOver, OLEGiveFeedback, OLESetData, OLEStartDrag, PathChange, PatternChange, Scroll, Validate Anwendung
................................................... Anwendung
Das Dateilistenfeld ist wie das Verzeichnislistenfeld (DirListBox) und das Laufwerklistenfeld (DriveListBox) noch ein Relikt aus der Zeit, als Anwendungen den Benutzer mit eigenen Varianten für Dialoge wie »Datei öffnen«, »Speichern unter« usw. auf die Probe stellten. Inzwischen dürfte es nur noch in ganz besonderen Problemfeldern zum Einsatz kommen, da die seit Windows 9x vorliegende Implementation der Standarddialoge wenig Wünsche offen lässt, was die interaktive Dateiauswahl betrifft. Freilich wird eine Anwendung, die sich beispielsweise auf die Konvertierung von Dateien im Batch-Modus spezialisiert, noch in irgendeiner Form vom Dateilistenfeld profitieren können. Von der Programmierung eigener Versionen der Standarddialoge sei aber eher abgeraten, da es der Benutzer sicher nicht danken wird. Um die im Listenfeld angezeigte Dateiauswahl zu spezifizieren, setzen Sie zunächst die Eigenschaft Path auf das gewünschte Verzeichnis. Dieses muss existieren und wird meist von einem Verzeichnislistenfeld-Steuerelement geliefert. Im zweiten Schritt setzen Sie die Eigenschaft Pattern auf ein Suchmuster im Stil von »*.bmp« oder »a??0012.dat«. Alternativ können Sie die komplette Spezifikation (Pfad plus Suchmuster) auch über die Eigenschaft FileName setzen. In diesem Fall extrahiert das Steuerelement die Werte für Path und Pattern und setzt FileName auf den Namen der ersten Datei in der Auswahl. Ist die Dateiauswahl jedoch leer, tritt ein Laufzeitfehler auf. Jede Änderung von Path bzw. Pattern tut das Steuerelement über die Ereignisse PathChange bzw. PatternChange kund. Wenn Sie wollen, dass der Benutzer mehrere Einträge im Listenfeld auswählen kann, setzen Sie MultiSelect auf 1 oder 2. Die Analyse der Auswahl des Benutzers wird meist im Zuge der Behandlung von Validate oder LostFocus vorgenommen und beinhaltet die Auswertung des Boolean-Arrays Selected (Basis 0), das so viele Elemente hat, wie es Einträge im Listenfeld gibt, und für jedes Element die Zugehörigkeit zur aktuellen Auswahl anzeigt – True bedeutet: das Element gehört zur Auswahl. Die zugehörigen Einträge lassen sich unter dem jeweiligen Index über die List-Eigenschaft gewinnen. Warnung
................................................... Wa rnung
Der verkapselte Teil der OLESetData-Behandlung durch das Steuerelement ist fehlerhaft implementiert. Als Quellkomponente in einer OLE-Drag&Drop-Operation setzt die Komponente in
401
Standardsteuerelemente
Appearance, Archive, BackColor, CausesValidation, Container, DragIcon, DragMode, Enabled, FileName, Font, FontBold, FontItalic, FontName, FontSize, FontStrikethru, FontUnderline, ForeColor, Height, HelpContextID, Hidden, hWnd, Index, Left, List, ListCount, ListIndex, Locked, MouseIcon, MousePointer, MultiSelect, Name, Normal, OLEDragMode, OLEDropMode, Parent, Path, Pattern, ReadOnly, Selected, System, TabIndex, TabStop, Tag, Top, ToolTipText, TopIndex, Visible, WhatsThisHelpID, Width
Standardsteuerelemente
der Files-Auflistung nämlich einen fehlerhaften Pfad, wenn die Auswahl dem Wurzelverzeichnis eines Laufwerks angehört: Aus irgendeinem Grund enthält er einen Schrägstrich zu viel – zum Beispiel »C:\\«. Beispiel
................................................... Beis piel
Der folgende Code demonstriert die Auswertung einer Mehrfachauswahl:
Standardsteuerelemente
Private Sub Form_Load() File1.FileName = "c:\Windows\*.dll" End Sub Private Sub File1_LostFocus() For i = 0 To File1.ListCount – 1 If File1.Selected(i) Then Print File1.List(i) End If Next End Sub Verwandte Steuerelemente
................................................... Verwandte Steuerelemente CommonDialog, DirListBox, DriveListBox, ListBox Verwandte Themen
................................................... Verwandte Them en
Listenfeld-Steuerelement (ListBox) (S. 412)
Data- Datensteuerelement (Data) Data1 As Data
Beschreibung
................................................... Bes c hreibung
Das Datensteuerelement Data bildet neben den Steuerelementen RemoteData und Adodc die Grundlage für automatisierte Verbindungen zwischen Steuerelementen und Datenbanken. Aus Sicht eines gewöhnlichen Steuerelements kann Data als Datenquelle auftreten, die es automatisch mit Werten aus einer Datenbank versorgt und Eingaben des Benutzers zurück an die Datenbank leitet. Data legt ein Datenbankobjekt (Database) an, um die Datenbankverbindung zu öffnen, sowie ein Recordset-Objekt für die Repräsentation der Datensätze, sobald es für ein anderes Steuerelement als Datenquelle in Aktion treten muss. Einen Überblick über die für den Datenbankzugriff spezifischen Eigenschaften und Ereignisse des Datensteuerelements entnehmen Sie den folgenden Tabellen. Eigenschaften
................................................... Eigens c ha ften Align, Appearance, BackColor, BOF, Caption, Connect, Database, DatabaseName, DefaultCursorType, DefaultType, DragIcon, DragMode, EditMode, Enabled, EOF, ForeColor, Exclusive, Font, FontBold, FontItalic, FontName, FontSize, FontStrikeThru, FontTransparent, FontUnderline, ForeColor, Height, Index, Left, MouseIcon, MousePointer, Name, OLEDrop-
402
Data- Datensteuerelement (Data)
Mode, Options, Parent, ReadOnly, Recordset, RecordsetType, RecordSource, RightToLeft, Tag, ToolTipText, Top, Visible, WhatsThisHelpID, Width Beschreibung
Database
Verweis auf Database-Objekt, das das Steuerelement unter Beachtung der Eigenschaften DataBaseName, Connect, Exclusive und ReadOnly für eine Verbindung generiert
DatabaseName
Zeichenfolge mit dem Namen der Datenbank
Connect
Zeichenfolge mit Informationen über den Datenbanktyp sowie (durch ein Semikolon getrennt) gegebenenfalls erforderliche zusätzliche Parameter. Zur Auswahl stehen: "Access" (Voreinstellung), "dBASE III;", "dBASE IV;", "dBASE 5;", "Excel 3.0;", "Excel 4.0;", "Excel 5.0;", "Excel 8.0;", "Lotus WK1;", "Lotus WK3;", "Lotus WK4;", "FoxPro 2.0;", "FoxPro 2.5;", "FoxPro 2.6;", "FoxPro 3.0;", "Paradox 3.x;", "Paradox 4.x;", "Paradox 5.x;" und "Text;"
BOF
Boolean-Wert, der anzeigt, ob die Position des aktuellen Datensatzes vor dem ersten Datensatz im Recordset-Objekt liegt
EOF
Boolean-Wert, der anzeigt, ob die Position des aktuellen Datensatzes hinter dem letzten Datensatz im Recordset-Objekt liegt
DefaultCursorType
Integer-Wert der bestimmt, welchen Cursortyp das Recordset-Objekt verwendet. Zur Auswahl stehen vbUseDefaultCursor (0; Voreinstellung; Auswahl bleibt dem Treiber überlassen), vbUseODBC (1; Verwendung der ODBC-Cursorbibliothek) und vbUseServerSideCursor (2, Verwendung von Server-Cursorn)
DefaultType
Integer-Wert, der bestimmt, ob die Microsoft Jet-Engine oder die ODBC-Treiber für den Datenzugriff verwendet werden sollen. Voreinstellung ist dbUseJet (2). Wenn Sie dbUseODBC (1) einstellen, müssen Sie die Connect-Eigenschaft auf "Text;" gefolgt von der Verbindungszeichenfolge für die ODBC-Verbindung setzen.
EditMode
Integer-Wert, der den Bearbeitungsstatus des aktuellen Datensatzes anzeigt. Folgende Werte stehen zur Auswahl: dbEditNone (0) für »keine Bearbeitung« (Voreinstellung), dbEditInProgress (1) für »Datensatz wird bearbeitet« und dbEditAdd (2) für »Datensatz wird gerade hinzugefügt«. Die Eigenschaft ist bei der Behandlung von Gültigkeitsfehlern nützlich.
Exclusive
Boolean-Wert, der bestimmt, ob die Datenbank im exklusiven Einbenutzermodus (True) oder im Mehrbenutzermodus (False; Voreinstellung) geöffnet wird
ReadOnly
Boolean-Wert, der bestimmt, ob die Datenbank für den reinen Lesezugriff (True) oder für den Schreib-/Lesezugriff (False; Voreinstellung) geöffnet wird
Recordset
Verweis auf das Recordset-Objekt, das die Datensätze für die Datenbankverbindung zusammenstellt, verwaltet und repräsentiert
RecordsetType
Integer-Wert, der den Typ des Recordset-Objekts bestimmt. Zur Auswahl stehen: vbRSTypeTable (0), vbRCTypeDynaset (1) und vbRSTypeSnapshot.
403
Standardsteuerelemente
Eigenschaft
Standardsteuerelemente
Eigenschaft
Beschreibung
RecordSource
Zeichenfolge mit dem Namen der Tabelle, Abfrage oder einer SQLAnweisung, die Datensätze für das Recordset-Objekt liefert.
Methoden
................................................... Metho den
Standardsteuerelemente
Drag, Move, OLEDrag, Refresh, ShowWhatsThis, UpdateControls, UpdateRecord, ZOrder Methode
Beschreibung
UpdateControls
Überträgt die Werte aus dem aktuellen Datensatz des Recordset-Objekts in die mit dem Datensteuerelement verbundenen Steuerelemente. Der explizite Aufruf dieser Methode ermöglicht es, die Steuerelemente auf die ursprünglichen Werte des Datensatzes zurückzusetzen, wenn die Eingaben des Benutzers verworfen werden sollen.
UpdateRecord
Überträgt die Werte der mit dem Datensteuerelement verbundenen Steuerelemente in den aktuellen Datensatz des Recordset-Objekts und speichert diesen in der Datenbank. Falls das Recordset-Objekt vom Typ Snapshot ist, kann die Methode die Werte nicht zurück in die Datenbank schreiben, sondern synchronisiert die Steuerelemente mittels UpdateControls wieder mit dem ursprünglichen Datensatz.
Ereignisse
................................................... Ereignis s e
DragDrop, DragOver, Error, MouseDown, MouseMove, MouseUp, OLECompleteDrag, OLEDragDrop, OLEDragOver, OLEGiveFeedback, OLESetData, OLEStartDrag, Reposition, Resize, Validate Ereignis
Beschreibung
Validate
Tritt auf, bevor der Datensatzzeiger des Recordsets auf einen anderen Datensatz gesetzt wird
Reposition
Tritt auf, nachdem der Datensatzzeiger des Recordsets auf einen anderen Datensatz gesetzt wurde.
Anwendung
................................................... Anwendung
Im Lauf der Zeit hat Microsoft verschiedene Schnittstellenstandards für die datenbankgestützte Programmierung vorgestellt: Data Access Objects (DAO), Remote Data Objects (RDO) und ActiveX Data Objects (ADO). Jede Schnittstelle hat ihre eigene Philosophie, ihre eigene Programmierwelt und – nicht zuletzt – ihr eigenes Datensteuerelement. Traditionell ist das DataSteuerelement für die DAO-Schnittstelle der Microsoft Jet-Engine (bis DAO 3.51) zuständig, während das RemoteData-Steuerelement den rein SQL-orientierten Datenbankzugriff über die ODBC-Schnittstellen-Treiber nach dem Client/Server-Modell abwickelt. Seit Einführung der ActiveX Data Objects und des dazu gehörigen ADO-Steuerelements mit Visual Basic 6.0 empfiehlt Microsoft allerdings die Umstellung auf die ADO-Technologie, da diese nicht nur die anderen beiden Technologien umfasst, sondern auch weiterentwickelt und künftig sogar ersetzen soll. Trotz der Zukunftspläne von Microsoft wird das Data-Steuerelement aber so schnell nicht von der Bildfläche verschwinden; dafür ist es zu bequem und zu weit verbreitet. Datensteuerelemente stellen gewissermaßen den »Königsweg« für die Entwicklung datenbankgestützter Anwendungen dar. Sie können als Datenquellen für andere Steuerelemente (etwa
404
Data- Datensteuerelement (Data)
Beispiel
................................................... Beis piel
Der folgende Code demonstriert den Umgang mit dem Datensteuerelement Data1 anhand des Textfeldes Text1, das zur Entwurfszeit mit diesem gebunden wurde. Ein Klick auf die Schaltfläche FrachtkostenAnzeigen ändert das Recordset der Datenquelle sowie die Datenfeldbindung des Textfeldes.
405
Standardsteuerelemente
TextBox, Image oder ListBox) fungieren und ermöglichen die einfache Steuerung der Datensatzauswahl. Aus Sicht der Programmierung verkapseln sie alles, was mit Datenbankkommunikation als solches zu tun hat. Sie stellen die Verbindung mit dem Datenbanksystem her und kümmern sich um die Abwicklung datenbankbezogener Kommandos. Insbesondere fordern sie Datensatzmengen (Recordsets) an, pflegen Sperren für Datensätze sowie einen Datensatzzeiger und betreiben das Zurückschreiben geänderter Datensätze. Von der äußeren Gestalt her nehmen sich die drei Datensteuerelemente nichts. Von der programmtechnischen Seite her gibt es aber doch einige Unterschiede. Ein Data-Steuerelement muss den Steuerelementen, die es versorgt, bereits zur Entwurfszeit als Datenquelle (DataSource) zugeordnet werden – ein Nachteil, den das ADO-Steuerelement nicht mehr hat. Damit das Datensteuerelement seinen Dienst aufnehmen kann, benötigt es zum einen in der Eigenschaft DataBaseName den Dateinamen der Datenbank, aus der es seine Datensätze beziehen soll, und zum anderen in der Eigenschaft RecordSource eine Spezifikation der zusammenzustellenden Datensatzmenge – jeweils als Wert vom Typ String. Die Aufgabe der Repräsentation der zusammengestellten Datensatzmenge fällt dem Recordset-Objekt des Steuerelements zu. Es kann einen von drei Typen haben, der über die RecordsetType-Eigenschaft spezifiziert wird: Tabelle, Dynaset oder Snapshot. Für ein Recordset-Objekt vom Typ Tabelle setzen Sie RecordsetType auf vbRSTypeTable (0) und RecordSource auf den Namen einer Tabelle, die in der Datenbank als lokale Tabelle oder als eingebundene externe Tabelle bekannt ist. Einer der größten Vorteile dieses Objekttyps ist, dass er die jeweils vorhanden Indizes der Tabellen ausnutzen und damit schneller als die anderen Typen sortieren und filtern kann. Für die Objekttypen Dynaset und Snapshot setzen Sie RecordsetType auf vbRSTypeDynaset (1; Voreinstellung) bzw. auf vbRSTypeSnapshot (2) und RecordSource auf den Namen einer in der Datenbank bekannten Abfrage oder auf eine explizite SQLAnweisung, die eine Datensatzmenge als Ergebnis liefert. Der Unterschied zwischen diesen beiden Objekttypen besteht im Wesentlichen darin, dass ein »Snapshot« eine statische Ansammlung von Datensätzen darstellt, die nach ihrer Zusammenstellung keine Verbindung zu den untergelegten Tabellen mehr unterhält und somit weder eine Aktualisierung der Tabellen ermöglicht noch spätere Aktualisierungen der Tabellen widerspiegelt. Ein »Dynaset« hält die Verbindung mit den untergelegten Tabellen aufrecht und reicht Aktualisierungen (in beiden Richtungen) durch. Das Datensteuerelement baut die Datenbankverbindung auf, sobald es der ersten Aktualisierungsanforderung eines mit ihm verbundenen Steuerelements nachkommen muss. Falls die Datenbankbindung bereits zur Entwurfszeit vollständig spezifiziert wurde, passiert dies gleich beim Laden des Formulars, auf dem die beiden Steuerelemente platziert wurden. Ansonsten sorgt ein expliziter Aufruf der Refresh-Methode dafür. Der weitere Umgang mit dem Datensteuerelement hängt natürlich von der Aufgabenstellung ab. Er ist unkritisch, solange nichts weiter passiert als die Aktualisierung der verbundenen Datenfelder (UpdateControls) oder des aktuellen Datensatzes (UpdateRecord) sowie die Durchsetzung von Gültigkeitsregeln im Rahmen der Validate-Behandlung. Auch eine Änderung des Recordsets (vgl. Beispiel) oder ein Wechsel der Datenbank zur Laufzeit ist nicht schwierig, führt aber bereits in das Fahrwasser der DAO-Programmierung. Weitergehende Kenntnisse im Bereich des Datenbank-Designs sind gefordert, wenn es um die Zusammenstellung von SQLAnweisungen geht, die über reine SELECT-Abfragen hinausgehen.
Standardsteuerelemente
Const Abfrage1 = "SELECT AVG(Frachtkosten) AS Fracht FROM Bestellungen" Private Sub FrachtkostenAnzeigen_Click() Text1.DataField = "" ' Weil RecordSource geändert wird Data1.RecordSource = Abfrage1 Data1.Refresh ' Weil RecordSource geändert wurde Text1.DataField = "Fracht" ' Berechnetes Feld End Sub Verwandte Steuerelemente
Standardsteuerelemente
................................................... Verwandte Steuerelemente Adodc, RemoteData
Figur- Steuerelement (Shape) Shape1 As Shape
Beschreibung
................................................... Bes c hreibung
Das Figur-Steuerelement verkörpert ein grafisches Element, das es ermöglicht, Verzierungen in Form von Steuerelementen auf einem Formular oder Benutzersteuerelement anzubringen. Die Art der Verzierung wird über die Eigenschaft Shape spezifiziert und kann die Form eines Rechtecks, Quadrats, Kreises, Ellipse, Rechtecks mit abgerundeten Ecken oder Quadrats mit abgerundeten Ecken haben. Der Aufzählungstyp ShapeConstants definiert dafür die Konstanten: vbShapeRectangle (0); vbShapeSquare (1); vbShapeCircle (3); vbShapeOval (2); vbShapeRoundedRectangle (4); vbShapeRoundedSquare (5). Die Form kann ausgefüllt oder transparent sein und lässt sich über eine Reihe von Eigenschaften (Linienstil, Linienfarbe, Füllmuster, Füllfarbe etc.) gestalten. Unterbrochene Linien, wie sie ein Wert größer 1 für die BorderStyle-Eigenschaft hervorbringt, zeichnet das Steuerelement allerdings durchgezogen, wenn die BorderWidth-Eigenschaft einen Wert ungleich 1 hat. Eigenschaften
................................................... Eigens c ha ften BackColor, BackStyle, BorderColor, BorderStyle, BorderWidth, Container, DrawMode, FillColor, FillStyle, ForeColor, Height, Index, Left, Name, Parent, Shape, Tag, Visible, Width Methoden
................................................... Metho den Move, Refresh, ZOrder Ereignisse
................................................... Ereignis s e
Keine Anwendung
................................................... Anwendung
Der Umgang mit dem Steuerelement ist unkompliziert, da seine Gestaltung bereits zur Entwurfszeit erfolgen kann und so Ergebnis eines interaktiven Vorgangs ist. Kreise und Rechtecke
406
Kombinationsfeld- Steuerelement ( ComboBox)
könnte man natürlich auch über die Paint-Routine des Parent-Objekts zeichnen, bei den abgerundeten Formen wird es bereits etwas schwieriger. In jedem Fall hilft das Steuerelement, den Code zu reduzieren. Verwandte Steuerelemente
................................................... Verwandte Steuerelemente Line, RptLine, RptShape Verwandte Themen
................................................... Verwandte Them en
Paint-Ereignis (S. 253)
Combo1 As ComboBox
Beschreibung
................................................... Bes c hreibung
Bei dem Kombinationsfeld-Steuerelement handelt es sich um ein Textfeld mit anhängendem Listenfeld. Der Wert des Textfelds (Text) ist gleichzeitig der Standardwert des Steuerelements. Darstellung und Verhalten des Steuerelements richten sich nach der zur Laufzeit schreibgeschützten Style-Eigenschaft. Der Aufzählungstyp ComboBoxConstants definiert dafür drei Konstanten: Konstante (Wert)
Beschreibung
vbComboDropdown (0) (Voreinstellung)
Dropdown-Kombinationsfeld – der Benutzer muss das Listenfeld über die Pfeilschaltfläche des Textfelds aufklappen, um darin eine Auswahl zu treffen. Das Textfeld lässt beliebige Eingaben zu (vgl. Abbildung links).
vbComboSimple (1)
Einfaches Kombinationsfeld – das Listenfeld ist ständig unterhalb des Textfelds sichtbar, und das Textfeld lässt beliebige Eingaben zu (vgl. Abbildung: Mitte).
vbComboDropdownList (2)
Dropdown-Liste – der Benutzer muss das Listenfeld über die Pfeilschaltfläche des Textfelds aufklappen, und das Textfeld nimmt nur Listeneinträge als Eingaben an (vgl. Abbildung rechts).
Vom Textfeld erhält das Steuerelement unter anderem die Eigenschaften DataChanged, Locked, SelLength, SelStart, SelText sowie insbesondere das Changed-Ereignis, und vom Listenfeld stammen die Eigenschaften List, ListCount, ListIndex, Sorted, die Methoden AddItem, Clear, RemoveItem sowie das Scroll-Ereignis – mehr darüber in den Abschnitten »Textfeld-Steuerelement (TextBox)« (S. 427) und »Listenfeld-Steuerelement (ListBox)« (S. 412).
407
Standardsteuerelemente
Kombinationsfeld- Steuerelement (ComboBox)
Standardsteuerelemente
Eigenschaften
................................................... Eigens c ha ften Appearance, BackColor, CausesValidation, Container, DataChanged, DataField, DataFormat, DataMember, DragIcon, DragMode, DataSource, Enabled, Font, FontBold, FontItalic, FontName, FontSize, FontStrikethru, FontUnderline, ForeColor, Height, HelpContextID, hWnd, Index, Left, List, ListCount, ListIndex, Locked, MouseIcon, MousePointer, Name, NewIndex, OLEDragMode, OLEDropMode, Parent, RightToLeft, SelLength, SelStart, SelText, Sorted, Style, TabIndex, TabStop, Tag, Text, Top, ToolTipText, TopIndex, Visible, WhatsThisHelpID, Width
Standardsteuerelemente
Methoden
................................................... Metho den AddItem, Clear, Drag, Move, OLEDrag, Refresh, RemoveItem, SetFocus, ShowWhatsThis, ZOrder Ereignisse
................................................... Ereignis s e
Change, Click, DblClick, DragDrop, DragOver, DropDown, GotFocus, KeyDown, KeyPress, KeyUp, LostFocus, OLECompleteDrag, OLEDragDrop, OLEDragOver, OLEGiveFeedback, OLESetData, OLEStartDrag, Scroll, Validate Anwendung
................................................... Anwendung
Das Einsatzgebiet des Kombinationsfelds ist groß. Wo immer die Eingabe von Text bei vorhandener Nachschlageliste stattfinden soll, ist das Kombinationsfeld die richtige Wahl. Die Pflege und der Umgang mit der Nachschlageliste sieht nicht anders aus als beim Listenfeld. Gleiches gilt auch für das Textfeld, mit dem einzigen Unterschied, dass dieses im Stil Dropdown-Liste jeden Tastenanschlag des Benutzers als Anfangsbuchstabe des auszuwählenden Listeneintrags interpretiert und den ersten passenden Listeneintrag als Wert übernimmt. Eine Wiederholung des Tastenanschlags ändert die Auswahl in den gegebenenfalls nächsten passenden Listeneintrag mit dem gleichen Anfangsbuchstaben. Das Textfeld sowie die Listeneinträge sind auf eine Länge von 1024 Zeichen beschränkt. Zur Auswertung der Benutzereingaben implementieren Sie am besten Behandlungsroutinen für die Ereignisse LostFocus und/oder Validate. Beispiel
................................................... Beis piel
Die folgende LostFocus-Behandlung sorgt dafür, dass sich das Kombinationsfeld Combo1 in seiner Liste immer die letzten fünf unterschiedlichen Eingaben merkt. Const MaxElements = 5 Private Sub Combo1_LostFocus() For i = 0 To Combo1.ListCount – 1 If Combo1.List(i) = Combo1 Then Exit Sub Next i Combo1.AddItem Combo1, 0 If Combo1.ListCount >= MaxElements + 1 Then Combo1.RemoveItem Combo1.ListCount – 1 End If End Sub Verwandte Steuerelemente
................................................... Verwandte Steuerelemente DirListBox, DriveListbox, ImageCombo, ListBox, TextBox
408
Kontrollkästchen- Steuerelement (CheckBox)
Kontrollkästchen- Steuerelement (CheckBox) Check1 As CheckBox
Beschreibung
................................................... Bes c hreibung
Eigenschaften
................................................... Eigens c ha ften Alignment, Appearance, BackColor, Caption, CausesValidation, Container, DataChanged, DataField, DataFormat, DataMember, DataSource, DisabledPicture, DownPicture, DragIcon, DragMode, Enabled, Font, FontBold, FontItalic, FontName, FontSize, FontStrikeThru, FontUnderline, ForeColor, Height, HelpContextID, hWnd, Index, Left, MaskColor, MouseIcon, MousePointer, Name, OLEDragMode,OLEDropMode, Parent, Picture, RightToLeft, Style, TabIndex, TabStop, Tag, ToolTipText, Top, UseMaskColor, Value, Visible, WhatsThisHelpID, Width Methoden
................................................... Metho den Drag, Move, OLEDrag, Refresh, SetFocus, ShowWhatsThis, ZOrder Ereignisse
................................................... Ereignis s e
Click, DragDrop, DragOver, GotFocus, KeyDown, KeyUp, KeyPress, LostFocus, MouseDown, MouseMove, MouseUp, OLECompleteDrag, OLEDragDrop, OLEDragOver, OLEGiveFeedback, OLESetData, OLEStartDrag, Validate Anwendung
................................................... Anwendung
Ursprüngliche Bestimmung dieses Steuerelements ist die optische Repräsentation von Werten des Typs Boolean. Zwischen diesen beiden Werten schaltet das Steuerelement auch hin und her, wenn der Benutzer mit der Maus darauf klickt. Inzwischen hat sich das Steuerelement durch Hinzunahme eines dritten Werts zu einem TriState-Steuerelement gemausert. Mit welcher Interpretation Sie den dritten Wert, der nur vom Programmcode aus gesetzt werden kann, versehen, bleibt Ihnen überlassen. Von der Intuition her steht der dritte Zustand für ein »Jein«. Gängige Interpretationen sind: »Standardeinstellung wird übernommen«, »Nicht alle abhängigen Optionen sind angekreuzt«, »Zustand hat keine Wirkung«. Jede Änderung der Value-Eigenschaft, gleich ob mit der Maus oder vom Programmcode aus, bringt das Click-Ereignis hervor. Da Value die Standardeigenschaft des CheckBox-Objekts ist, muss nur der Objektbezeichner notiert werden, um diese Eigenschaft anzusprechen. Um eine Zugriffstaste für das Steuerelement zu vereinbaren, stellen Sie dem entsprechenden Buchstaben in der Beschriftung (Caption) das Zeichen »&« voran. Er wird zur Laufzeit durch Unterstreichung kenntlich gemacht. Das Kontrollkästchen kennt zwei unterschiedliche Darstellungen: die Standarddarstellung im Stil von Windows 9x (vgl. Abbildung) und eine benutzerdefinierte Darstellung. Die Style-
409
Standardsteuerelemente
Das Kontrollkästchen-Steuerelement visualisiert die Funktion eines Schalters. Es besteht aus einem Kästchen, das sich mit einem Häkchen versehen (»ankreuzen«) lässt, sowie einer Beschriftung. Für die Standardeigenschaft Value des Objekts sind drei Werte definiert: vbFalse (0), vbTrue (1) und vbUseDefault (2).
Standardsteuerelemente
Standardsteuerelemente
Eigenschaft entscheidet darüber, welche der Darstellungen zur Anzeige kommt. In der benutzerdefinierten Darstellung sieht das Kontrollkästchen zunächst wie eine Befehlsschaltfläche aus, hält aber im Gegensatz zu dieser den gedrückten Zustand bis zum nächsten Mausklick. Um dem Steuerelement ein benutzerdefiniertes Aussehen zu verleihen, können Sie den Eigenschaften Picture und DownPicture sowie gegebenenfalls DisabledPicture geeignete Bilder zuordnen. Falls Sie eine pseudotransparente Darstellung wünschen, bei der die über eine Transparenzfarbe (MaskColor) definierten transparenten Bereiche der Bilder in Hintergrundfarbe erscheinen, müssen Sie die Eigenschaft UseMaskColor auf True setzen. (Ein analoges Beispiel für Schaltflächen findet sich im Abschnitt »DisabledPicture-Eigenschaft«, S. 341.) In Datenbankanwendungen lässt sich das Steuerelement über seine Data-Eigenschaften an ein Feld des Typs Boolean binden. Beispiel
................................................... Beis piel
Der folgende Code arbeitet mit einer Befehlsschaltfläche Command1 und einem Kontrollkästchen Check1. Jeder Klick auf die Schaltfläche oder auf das Kontrollkästchen ändert den Zustand des Kontrollkästchens. Der TriState-Zustand lässt sich nur über die Schaltfläche hervorbringen. Private Sub Check1_Click() MsgBox "klicke de klicke de klick" End Sub Private Sub Command1_Click() Check1 = (Check1 + 1) Mod 3 End Sub Verwandte Steuerelemente
................................................... Verwandte Steuerelemente Button, CommandButton, OptionButton
Laufwerklistenfeld- Steuerelement (DriveListBox) Drive1 As DriveListBox
Beschreibung
................................................... Bes c hreibung
Das Laufwerklistenfeld-Steuerelement ermöglicht die Auswahl eines logischen Laufwerks des Systems. Standardeigenschaft des Steuerelements ist Drive, eine Zeichenfolge, die den ausgewählten Texteintrag wiedergibt. Ändert sich die Standardeigenschaft aufgrund einer Benutzerinteraktion, tritt das Ereignis Change auf. Eigenschaften
................................................... Eigens c ha ften Appearance, BackColor, CausesValidation, Container, DragIcon, DragMode, Drive, Enabled, Font, FontBold, FontItalic, FontName, FontSize, FontStrikethru, FontUnderline, ForeColor, Height, HelpContextID, hWnd, Index, Left, List, ListCount, ListIndex, MouseIcon, MousePointer, Name, OLEDragMode, OLEDropMode, Parent, TabIndex, TabStop, Tag, Top, ToolTipText, TopIndex, Visible, WhatsThisHelpID, Width
41 0
Linie- Steuerelement (Line)
Methoden
................................................... Metho den Drag, Move, OLEDrag, Refresh, SetFocus, ShowWhatsThis, ZOrder Ereignisse
................................................... Ereignis s e
Change, DragDrop, DragOver, DropDown, GotFocus, KeyDown, KeyPress, KeyUp, LostFocus, OLECompleteDrag, OLEDragDrop, OLEDragOver, OLEGiveFeedback, OLESetData, OLEStartDrag, Scroll, Validate Anwendung
................................................... Anwendung
Beispiel
................................................... Beis piel
Der folgende Code zeigt, wie einfach die Synchronisation zwischen Laufwerklistenfeld, Verzeichnislistenfeld und Dateilistenfeld ist: Private Sub Dir1_Change() File1 = Dir1 ' File1.Path = Dir1.Path End Sub Private Sub Drive1_Change() Dir1 = Drive1 ' Dir1.Path = Drive1.Drive End Sub Verwandte Befehle
................................................... Verwa ndte Befehle
DirListBox, FileListBox, ImageCombo, ListBox, TextBox
Linie- Steuerelement (Line) Line1 As Line Beschreibung
................................................... Bes c hreibung
Das Linie-Steuerelement verkörpert ein grafisches Element, das es ermöglicht, Linienverzierungen auf einem Formular oder Benutzersteuerelement anzubringen. Das Erscheinungsbild der Linie wird durch die Eigenschaften BorderColor, BorderStyle, BorderWidth und DrawMode bestimmt, die Endpunkte im Koordinatensystem des Containers durch die Eigenschaften X1, Y1, X2, Y2. Unterbrochene Linien, wie sie ein Wert größer 1 für die BorderStyle-Eigenschaft hervorbringt, kennt das Steuerelement nur, wenn die BorderWidth-Eigenschaft den Wert 1 hat. Eigenschaften
................................................... Eigens c ha ften BorderColor, BorderStyle, BorderWidth, Container, DrawMode, Parent, Tag, Visible, X1, X2, Y1, Y2
41 1
Standardsteuerelemente
Genau genommen müsste dieses Steuerelement die Bezeichnung »DriveCombo« tragen, da es nicht vom Listenfeld abstammt, sondern vom Kombinationsfeld- bzw. ImageCombo-Steuerelement. Die Standardeigenschaft Drive lässt sich ohne Nachbearbeitung direkt der Standardeigenschaft Path eines DirListBox-Steuerelements zuweisen, da dieses Steuerelement (genau wie das DriveListBox-Steuerelement) den Laufwerksbuchstaben automatisch extrahiert (sofern ein Leerzeichen und ein Text in eckigen Klammern folgen) und auf das für dieses Laufwerk geltende aktuelle Verzeichnis umschaltet. Die Standardeigenschaft Path eines Verzeichnislistenfelds lässt sich wiederum in die Path-Eigenschaft eines Dateilistenfelds übernehmen, so dass sich die drei Steuerelemente über eine einfache Aktualisierungskette bequem synchronisieren lassen.
Standardsteuerelemente
Methoden
................................................... Metho den Refresh, ZOrder Ereignisse
................................................... Ereignis s e
Keine Anwendung
Standardsteuerelemente
................................................... Anwendung
Der Umgang mit dem Steuerelement ist unkompliziert, da seine Gestaltung bereits zur Entwurfszeit erfolgen kann und so Ergebnis eines interaktiven Vorgangs ist. Natürlich könnte man Linien mit den Eigenschaften des Steuerelements auch über die Paint-Routine des ParentObjekts zeichnen, doch die Verwendung des Steuerelements ist zumindest für statische Linien, die als Verzierung gedacht sind, erheblich einfacher und spart zudem Code. Verwandte Steuerelemente
................................................... Verwandte Steuerelemente Shape, RptShape Verwandte Themen
................................................... Verwandte Them en
Paint-Ereignis (S. 253)
Listenfeld- Steuerelement (ListBox) List1 As ListBox
Beschreibung
................................................... Bes c hreibung
Das Listenfeld-Steuerelement zeigt eine Liste mit Texteinträgen an, in der ein Benutzer seine Auswahl treffen kann. Wird die Sorted-Eigenschaft zur Entwurfszeit auf True gesetzt, fügt das Steuerelement neue Einträge in aufsteigender alphabetischer Ordnung in die über die ListEigenschaft zugängliche Liste ein (und zeigt sie auch so an). Je nach Wert der MultiSelectEigenschaft erlaubt das Listenfeld die Einfach- oder Mehrfachauswahl von Einträgen. Standardeigenschaft des Steuerelements ist Text. Sie ist vom Typ String und hat immer den Wert des zuletzt markierten Listenelements. Zur Laufzeit gibt die ListIndex-Eigenschaft immer die Position des zuletzt markierten Eintrags in der Liste wieder. Eigenschaften
................................................... Eigens c ha ften Appearance, BackColor, CausesValidation, Columns, Container, DataChanged, DataField, DataFormat, DataMember, DataSource, DragIcon, DragMode, Enabled, Font, FontBold, FontItalic, FontName, FontSize, FontStrikethru, FontUnderline, ForeColor, Height, HelpContextID, hWnd, Index, IntegralHeight, ItemData, Left, List, ListCount, ListIndex, MouseIcon, MousePointer, MultiSelect, Name, NewIndex, OLEDragMode, OLEDropMode, Parent, RightToLeft, Selected, SelCount, Sorted, Style, TabIndex, TabStop, Tag, Text, Top, ToolTipText, TopIndex, Visible, WhatsThisHelpID, Width
41 2
Listenfeld- Steuerelement ( ListBox)
Beschreibung
Columns
Integer-Wert, der bestimmt, welche Art von Bildlauf das Listenfeld unter Anzeige der entsprechenden Bildlaufleisten durchführt – horizontal oder vertikal – und in wie viele Spalten es seine Anzeige aufteilt. Voreinstellung ist der Wert 0 für den vertikalen Bildlauf, jeder andere Wert größer 0 hat den horizontalen Bildlauf zur Folge und bestimmt die Spaltenanzahl (vgl. in der Abbildung links und Mitte). Die Eigenschaft kann zur Laufzeit nur verändert werden, wenn sie zur Entwurfszeit auf einen Wert größer 0 gesetzt wurde.
IntegralHeight
Boolean-Wert, der bestimmt, ob das Steuerelement Einträge am unteren Ende abschneiden kann (False) oder ob es nur vollständige Einträge anzeigt (True; Voreinstellung)
ItemData
Long-Array, das es ermöglicht, jedem Eintrag einen benutzerdefinierten Long-Wert zuzuordnen – etwa für die Indizierung von Elementen in anderen Listen oder Arrays
List
Objekt, das die Listeneinträge speichert und den Zugriff darauf nach dem Prinzip des Array-Zugriffs ermöglicht. Die Array-Elemente lassen sich als Rechts- und Linkswerte verwenden, das List-Objekt selbst jedoch weder noch. Die Methoden AddItem und RemoveItem erweitern bzw. verkürzen die Liste, und Clear löscht sie.
ListCount
Integer-Wert, der ausdrückt, wie viele Einträge die Liste enthält
ListIndex
Integer-Wert, der den Index des zuletzt ausgewählten Listeneintrags wiedergibt
MultiSelect
Zur Laufzeit schreibgeschützter Integer-Wert, der bestimmt, ob der Benutzer nur einen einzelnen Eintrag (vbMultiSelectNone = 0), einen Block von Einträgen (vbMultiSelectSimple = 1) oder mehrere einzelne Einträge bzw. Blöcke (vbMultiSelectExtended = 2) auswählen kann
NewIndex
Zur Laufzeit schreibgeschützter Integer-Wert, der wiedergibt, welches Element als letztes in die Liste eingefügt wurde
SelCount
Integer-Wert, der ausdrückt, wie viele Einträge der Benutzer markiert hat
Selected
Boolean-Array, dessen Elemente den einzelnen Einträgen in der Liste zugeordnet sind und beschreiben, ob der zugehörige Listeneintrag markiert ist (True) oder nicht (False). Hat Style den Wert vbListBoxCheckBox (1), bestimmt diese Eigenschaft, ob das Kontrollkästchen des Eintrags ein Häkchen enthält.
Sorted
Zur Laufzeit schreibgeschützter Boolean-Wert, der bestimmt, ob das Listenfeld seine Einträge aufsteigend sortiert (True) oder nicht (False). Deutsche Umlaute werden nicht richtig eingeordnet, da das Steuerelement ungeachtet der Ländereinstellungen die amerikanische Sortierordnung verwendet. Beachten Sie auch, dass der Aufruf der AddItem-Methode bei expliziter Angabe eines Index die Sortierung durcheinander bringen kann.
Style
Zur Laufzeit schreibgeschützter Integer-Wert, der bestimmt, ob vor den Einträgen der Liste Kontrollkästchen erscheinen (vbListBoxStandard = 0; Voreinstellung) oder nicht (vbListBoxCheckBox = 1). Wenn diese Eigenschaft auf 1 gesetzt ist, muss die MultiSelect-Eigenschaft den Wert 0 haben (vgl. in der Abbildung links und rechts).
41 3
Standardsteuerelemente
Eigenschaft
Standardsteuerelemente
Standardsteuerelemente
Eigenschaft
Beschreibung
TopIndex
Integer-Wert, der den Index des Eintrags bestimmt, welcher als oberstes Element in der vertikal orientierten (Columns = 0) Liste erscheint – vorausgesetzt, es folgen noch genügend Einträge, die den sichtbaren Teil des Listenfensters füllen. Hat die Columns-Eigenschaft einen Wert größer 0, bewirkt die TopIndex-Eigenschaft nur, dass das entsprechende Element sicher angezeigt wird, es ist im Allgemeinen aber nicht der oberste Eintrag in der Darstellung.
Methoden
................................................... Metho den AddItem, Clear, Drag, Move, OLEDrag, Refresh, RemoveItem, SetFocus, ShowWhatsThis, ZOrder Ereignisse
................................................... Ereignis s e
Click, DblClick, DragDrop, DragOver, GotFocus, ItemCheck, KeyDown, KeyPress, KeyUp, LostFocus, MouseDown, MouseMove, MouseUp, OLECompleteDrag, OLEDragDrop, OLEDragOver, OLEGiveFeedback, OLESetData, OLEStartDrag, Scroll, Validate Anwendung
................................................... Anwendung
Das Listenfeld-Steuerelement visualisiert den Inhalt eines Zeichenfolgenarrays und ermöglicht, je nach Wert der MultiSelect-Eigenschaft, die Auswahl einer oder mehrerer Zeichenfolgen durch einfaches Markieren mit der Maus oder der Tastatur. Wurde die Style-Eigenschaft beim Entwurf auf »1 – Kontrollkästchen« gesetzt (was erfordert, dass MultiSelect den Wert 0 hat), geschieht die Auswahl über das Setzen von Häkchen in die links neben den Listeneinträgen erscheinenden Kontrollkästchen. Dieser Modus erlaubt grundsätzlich die Mehrfachauswahl. Um die Liste zu erweitern oder zu verkürzen, benutzen Sie die Methoden AddItem und RemoveItem. Die Clear-Methode löscht die gesamte Liste. Die Prototypen dieser Methoden lauten: Sub Objekt.AddItem (Eintrag As String[, Index As Integer]) Sub Objekt.RemoveItem (Index As Integer) Sub Objekt.Clear
Fehlt der Parameter Index beim Aufruf von AddItem, hängt die Methode einen neuen Eintrag in Abhängigkeit von der Sorted-Eigenschaft als letztes Element an die unsortierte Liste oder sortiert ihn ein. Ist ein Index spezifiziert, fügt die Methode das neue Element an der Position Index ein und verschiebt die nachfolgenden Einträge um eine Position nach hinten, ohne Rücksicht auf eine gegebenenfalls bestehende Sortierung. Die Angabe der Position 0 bewirkt, dass der neue Eintrag zum ersten Element in der Liste wird. Zugänglich werden die Einträge über die List-Eigenschaft. Der Zugriff erfolgt wie bei einem Zeichenfolgenarray unter Angabe eines nullbasierten Indexwerts – »en bloc«-Zuweisungen, bei denen die List-Eigenschaft selbst als Linkswert oder als Rechtswert auftritt, sind jedoch nicht möglich. Über die Eigenschaft ListCount erfahren Sie, wie viele Elemente die Liste hat. Laut Voreinstellung hat die MultiSelect-Eigenschaft den Wert »0 – Kein«, so dass der Benutzer zu einem Zeitpunkt immer nur einen Eintrag markieren kann. Die Reaktion auf Benutzereingaben ist in diesem Fall über die Click-Behandlung möglich, da das Steuerelement dieses Ereignis grundsätzlich bei jeder Änderung der Markierung signalisiert, gleich ob diese auf das Konto der Maus oder der Tastatur geht. Wenn Sie wollen, dass der Benutzer mehrere Einträge im Listenfeld auswählen kann, setzen Sie MultiSelect auf 1 oder 2. Die Analyse der Auswahl des Benutzers wird dann meist im Zuge der
41 4
OLE- Container- Steuerelement (OLE)
Beispiel
................................................... Beis piel
Der folgende Code initialisiert ein Listenfeld List1 mit den Namen der auf dem System installierten Schriften und zeigt das Bezeichnungsfeld Label1 in der jeweils ausgewählten Schrift an. Private Sub Form_Load() For i = 0 To Screen.FontCount List1.AddItem Screen.Fonts(i) Next End Sub Private Sub List1_Click() Label1.FontName = List1.Text End Sub Verwandte Steuerelemente
................................................... Verwandte Steuerelemente ComboBox, CommonDialog, DirListBox, DriveListBox, ImageCombo, ListView
OLE- Container- Steuerelement (OLE) OLE1 As OLE Beschreibung
................................................... Bes c hreibung
Das OLE-Container-Steuerelement eröffnet einem Visual-Basic-Programm die gesamte Funktionalität OLE-fähiger Anwendungen. Es fungiert als Container für OLE-Objekte und kann verknüpfte oder eingebettete Objekte darstellen und für die Bearbeitung aktivieren. Bei einem verknüpften Objekt arbeitet das Steuerelement nur mit einem Verweis auf den Speicherort für das Objekt und kommt mit dessen aktuellen Datenstrukturen überhaupt nicht in Berührung. Für seine Darstellung im Fenster des Steuerelements liefert das Objekt ein im Metafile-Format gehaltenes Bild, und die Bearbeitung kann nur in einem Fenster der für das Objekt zuständigen OLE-Quellanwendung stattfinden. Zudem speichert die Methode SaveToFile nur die Verknüpfungsinformation, das Objekt selbst muss von der Quellanwendung aus gespeichert werden.
41 5
Standardsteuerelemente
Behandlung von Validate oder LostFocus vorgenommen. Sie beinhaltet die Auswertung des Boolean-Arrays Selected (Basis 0), das so viele Elemente hat, wie es Einträge im Listenfeld gibt, und für jedes Element die Zugehörigkeit zur aktuellen Auswahl anzeigt – True bedeutet: das Element gehört zur Auswahl. Die zugehörigen Einträge lassen sich unter dem jeweiligen Index über die List-Eigenschaft gewinnen. Der Wert von SelCount informiert darüber, wie viele Einträge markiert sind. Ein Listenfeld-Steuerelement blendet automatisch eine Bildlaufleiste ein, wenn nicht alle Einträge im Listenfenster auf einmal darstellbar sind (vgl. Abbildung). Wurde die Columns-Eigenschaft beim Entwurf auf 0 gesetzt, führt das Steuerelement einen vertikalen Bildlauf durch. Dabei pflegt es die Eigenschaft TopIndex, so dass deren Wert immer den Index des zuoberst gelegenen Eintrags wiedergibt. Indem Sie diese Eigenschaft auf die Position eines bestimmten Eintrags setzen, erzwingen Sie einen entsprechenden Bildlauf. An spezifischen Ereignissen signalisiert das Steuerelement: ItemCheck und Scroll. ItemCheck kann nur auftreten, wenn die Style-Eigenschaft auf 1 gesetzt wurde. Das Ereignis hat einen Integer-Parameter, der die Position eines Listeneintrags ausdrückt und besagt, dass der Benutzer das Kontrollkästchen des Eintrags gesetzt oder gelöscht hat. Das parameterlose ScrollEreignis tritt auf, wenn der Benutzer oder das Setzen der TopIndex-Eigenschaft einen Bildlauf verursacht hat.
Standardsteuerelemente
Standardsteuerelemente
Ein eingebettetes Objekt ist dagegen mit allen seinen Datenstrukturen im OLE-Container enthalten und kann von diesem beispielsweise auch komplett mittels SaveToFile gespeichert werden. Für die Bearbeitung des Objekts stehen zwei Modi zur Auswahl: die Bearbeitung in einem Fenster der OLE-Quellanwendung und die Vorortbearbeitung im Fenster des OLE-ContainerSteuerelements. Im ersten Modus, der mit dem Verb Öffnen (-2) aktiviert wird, erhält der Benutzer ein neues Fenster der Quellanwendung mit dem Objekt und kann dieses darin bearbeiten und gegebenenfalls sogar (ohne Auswirkung auf das Objekt im Steuerelement) als Kopie extern speichern. Wenn er das Fenster schließt, übernimmt das Steuerelement im Allgemeinen automatisch alle vorgenommenen Änderungen und zeigt diese auch an. Im zweiten Modus, der mit dem Verb Bearbeiten (-1) – und meist auch mit dem Standardverb – aktiviert wird, kann der Benutzer das Objekt vor Ort, das heißt im Fenster des Steuerelements, bearbeiten, wobei die OLE-Quellanwendung die Kontrolle über das Fenster des Steuerelements übernimmt, sein Menü im Formular anzeigt usw. (vgl. Abbildung). Eigenschaften
................................................... Eigens c ha ften Action, Appearance, AppIsRunning, AutoActivate, AutoVerbMenu, BackColor, BorderStyle, CausesValidation, Class, Container, Data, DataChanged, DataField, DataText, DisplayType, DragIcon, DragMode, Enabled, FileNumber, Format, ForeColor, Height, HelpContextID, HostName, hWnd, Index, Left, lpOleObject, MiscFlags, MouseIcon, MousePointer, Name, Object, ObjectAcceptFormats, ObjectAcceptFormatsCount, ObjectGetFormats, ObjectGetFormatsCount, ObjectVerbFlags, ObjectVerbs, ObjectVerbsCount, OLEDropAllowed, OLEType, OLETypeAllowed, Parent, PasteOK, Picture, SizeMode, SourceDoc, SourceItem, TabIndex, TabStop, Tag, Top, UpdateOptions, Verb, Visible, WhatsThisHelpID, Width
41 6
OLE- Container- Steuerelement (OLE)
Eigenschaft
Beschreibung
Action
Integer-Wert, der eine Operation des Steuerelements auslöst, sobald er gesetzt wird. Die Aktivierung von Operationen über die Action-Eigenschaft unterstützt das Steuerelement nur noch zum Erhalt der Kompatibilität. Inzwischen steht für jede Operation eine Methode zur Verfügung. Folgende Werte lösen folgende Operationen aus (in Klammern die äquivalente Methode): 0 (CreateEmbed) Eingebettetes Objekt erstellen (SourceDoc benennt gegebenenfalls eine Datei, Class den Objekttyp) Verknüpftes Objekt aus dem Inhalt einer Datei erstellen (SourceDoc benennt Datei, SourceItem gegebenenfalls Element/Bereich innerhalb des Objekts)
4 (Copy)
Objekt in Zwischenablage kopieren
5 (Paste)
Kopie des Objekts in Zwischenablage in Steuerelement einfügen
6 (Update)
Darstellung des Objekts (als Bitmap) im Steuerelement aktualisieren
7 (DoVerb)
Objekt für Operation öffnen (Verb spezifiziert Operation)
9 (Close)
Objekt schließen und Verbindung mit OLE-Server-Anwendung trennen
10 (Delete)
Objekt löschen und von diesem belegte Ressourcen freigeben
11 (SaveToFile)
Objekt in der geöffneten Binärdatei mit der Dateinummer FileNumber speichern
12 (ReadFromFile)
Objekt aus der geöffneten Binärdatei mit der Dateinummer FileNumber laden
14 (InsertObjDlg)
Dialog OBJEKT EINFÜGEN ausführen
15 (PasteSpecialDlg) Dialog EINFÜGEN ausführen 17 (FetchVerbs)
ObjectVerbs und ObjectVerbsCount aktualisieren
18 (SaveToOle1File) Objekt im OLE-1.0-Format in geöffneter Binärdatei mit Dateinummer FileNumber speichern AppIsRunning
Boolean-Wert, der eine Aussage darüber macht, ob die für das im Container enthaltene OLE-Objekt zuständige Quellanwendung in Ausführung ist (True) oder nicht (False). Ein Setzen der Eigenschaft bewirkt, dass die Anwendung explizit gestartet bzw. beendet wird.
AutoActivate
Integer-Wert, der bestimmt, auf welche Art das im Container enthaltene OLE-Objekt (sowie die dafür zuständige Server-Anwendung) aktiviert wird. Der Aufzählungstyp VBOLEContainerConstants definiert dafür die Konstanten: vbOLEActivateManual (0) Das Objekt lässt sich nur vom Code aus durch Aufruf der DoVerb-Methode aktivieren.
41 7
Standardsteuerelemente
1 (CreateLink)
Standardsteuerelemente
Eigenschaft
Beschreibung
AutoActivate (Forts.)
vbOLEActivateGetFocus (1)
Standardsteuerelemente
vbOLEActivateDoubleclick (2)
vbOLEActivateAuto (3)
Falls das Objekt die Aktivierung per Mausklick unterstützt, wird es automatisch aktiviert, sobald das OLE-Steuerelement den Fokus erhält. (Voreinstellung) Das Objekt wird durch einen Doppelklick oder (falls das OLE-Steuerelement den Fokus besitzt) durch die Taste (Eingabe) aktiviert. Das OLE-Steuerelement sieht das DblClick-Ereignis nicht! Das Objekt wird automatisch aktiviert, wenn das OLE-Steuerelement den Fokus erhält oder der Benutzer in das Steuerelement doppelklickt. (Das OLE-Steuerelement sieht das DblClick-Ereignis nicht!)
AutoVerbMenu
Boolean-Wert, der bestimmt, ob das Objekt auf einen rechten Mausklick hin ein Kontextmenü mit den für es definierten Verben (lies: Befehlen) anzeigt (True; Voreinstellung) oder nicht (False)
Class
Zeichenfolge, die den Klassennamen des eingebetteten Objekts zurückgibt
Data
Long-Wert als Handle für Speicherobjekt, das für die Datenübermittlung im spezifizierten Format (Format-Eigenschaft) herangezogen wird
DataText
Zeichenfolge, die als bidirektionaler Puffer für die textorientierte Kommunikation mit dem OLE-Objekt fungiert
DisplayType
Integer-Wert, der angibt, ob das OLE-Objekt als Inhalt (vbOLEDisplayContent = 0; Voreinstellung) oder als Symbol (vbOLEDisplayIcon = 1) im Steuerelement angezeigt wird. Der Wert dieser Eigenschaft bestimmt die Voreinstellung des Kontrollkästchens ALS SYMBOL ANZEIGEN in den Dialogfeldern INHALTE EINFÜGEN und OBJEKT EINFÜGEN, wenn diese zur Laufzeit mittels der Methoden InsertObjDlg bzw. PasteSpecialDlg angezeigt werden. Falls Sie ein OLE-Objekt mit den Methoden CreateEmbed oder CreateLink zur Laufzeit erstellen, legen Sie mit der DisplayType-Eigenschaft (unwiderruflich) fest, ob das Objekt als Inhalt oder als Symbol im Steuerelement angezeigt werden soll.
FileNumber
Integer-Wert, der für die Werte 11, 12, 18 der Action-Eigenschaft die Dateinummer einer im binären Modus geöffneten Datei spezifiziert. Wird nur von Action verwendet, die Methoden SaveToFile, ReadFromFile, SaveToOle1 machen davon keinen Gebrauch.
Format
Zeichenfolge, die das Datenformat für den Versand oder Erhalt objektspezifischer Daten beschreibt. Die als nullbasierte Zeichenfolgen-Arrays definierten Eigenschaften ObjectAcceptFormats (Empfang) und ObjectGetFormats (Versand) stellen die von dem jeweiligen Objekttyp unterstützten Formate zur Auswahl.
HostName
Zeichenfolge, die auf den Namen der Visual-Basic-Anwendung gesetzt werden kann, um der OLE-Quellanwendung die Ausgabe dieses Namens in der Titelleiste zu ermöglichen (was jedoch nicht alle OLE-Server tun)
LpOLEObject
Adresse des OLE-Objekts (wird von verschiedenen in ActiveX-DLLs gelegenen Routinen verlangt)
41 8
OLE- Container- Steuerelement (OLE)
Beschreibung
MiscFlags
Integer-Wert, der als Bitvektor generelle Verhaltensattribute des OLEObjekts steuert. Das Bit vbOLEMiscFlagMemStorage (1) verhindert, dass das Objekt nur im Hauptspeicher und nicht in einer temporären Datei gespeichert wird, und das Bit vbOLEMiscFlagDisableInPlace (2) bewirkt, dass das Objekt keine Vorortbearbeitung im Fenster des Steuerelements unterstützt.
ObjectAcceptFormatsCount
Schreibgeschützter Integer-Wert, der die Elementanzahl der Eigenschaft ObjectAcceptFormats wiedergibt
ObjectGetFormats
Schreibgeschütztes Zeichenfolgen-Array (nullbasiert), dessen Elemente die Bezeichnungen der von dem OLE-Objekt lieferbaren Datenformate wiedergeben. Für ein Bitmap-Objekt sind das beispielsweise: »CF_METAFILEPICT«, »CF_DIB«; »CF_BITMAP"
ObjectGetFormatsCount
Schreibgeschützter Integer-Wert, der die Elementanzahl der Eigenschaft ObjectGetFormats wiedergibt
ObjectVerbFlags
Long-Array mit Bitvektoren, die die Anzeigezustände der im Array ObjectVerbs enthaltenen Menüeinträge beschreiben. Der Aufzählungstyp VBOLEContainerConstants definiert dafür die Flags: vbOLEFlagGrayed (1) Menüelement ist abgeblendet vbOLEFlagDisabled (2)
ObjectVerbs
Menüelement ist deaktiviert
vbOLEFlagChecked (8)
Menüelement trägt Häkchen
vbOLEFlagSeparator (2048)
Menüelement ist Trennlinie
schreibgeschütztes Zeichenfolgen-Array (nullbasiert), dessen Elemente die Menüeinträge für die von dem Objekt (im jeweiligen Zustand) unterstützten Verben (lies: Kommandos) darstellen. OLE-Objekte unterstützen darüber hinaus eine Reihe von Standardverben, die eine Entsprechung durch einen Menüeintrag haben können, aber nicht müssen. Der Aufzählungstyp VBOLEContainerConstants definiert dafür die Konstanten: vbOLEPrimary (0) Standardaktion des Objekts vbOLEShow (-1)
Objekt wird zum Bearbeiten angezeigt und aktiviert (nach Möglichkeit für die Vorortbearbeitung)
vbOLEOpen (-2)
Öffnet Objekt in separatem Fenster der OLEQuellanwendung
vbOLEHide (-3)
bei eingebetteten Objekten wird die OLE-Quellanwendung ausgeblendet
vbOLEUIActivate (-4)
Aktiviert die Benutzeroberfläche der OLE-Quellanwendung des Objekts (nach Möglichkeit für die Vorortbearbeitung in der Zielanwendung)
vbOLEInPlaceActivate (-5)
Macht das Objekt für die Vorortberarbeitung verfügbar, so dass der Benutzer in das Objekt klicken und es sofort bearbeiten kann
vbOLEDiscardUndoState (-6) Bringt die OLE-Quellanwendung des Objekts dazu, alle gespeicherten Bearbeitungsschritte rückgängig zu machen
41 9
Standardsteuerelemente
Eigenschaft
Standardsteuerelemente
Eigenschaft
Beschreibung
ObjectVerbsCount
Schreibgeschützter Integer-Wert, der die Elementanzahl der Eigenschaft ObjectVerbs wiedergibt
OLEDropAllowed Boolean-Wert, der bestimmt, ob das Steuerelement das Ablegen von Objekten im Rahmen von OLE-Drag&Drop-Operationen in seinem Fenster erlaubt (True) oder nicht (False)
Standardsteuerelemente
OLEType
Schreibgeschützter Integer-Wert, der eine Aussage über das im Steuerelement befindliche Objekt macht. vbOLELinked (0) Objekt ist verknüpft vbOLEEmbedded (1) Objekt ist eingebettet vbOLENone (3) Steuerelement enthält kein Objekt
OLETypeAllowed Integer-Wert, der bestimmt, welche Art von OLE-Objekten das Steuerelement unterstützt. vbOLELinked (0) verknüpfte OLE-Objekte vbOLEEmbedded (1) eingebettete OLE-Objekte vbOLEEither (2) beide Arten (der Befehl EINFÜGEN sollte mittels PasteSpecialDlg das Dialogfeld EINFÜGEN aufrufen) PasteOK
Boolean-Wert, der bestimmt, ob der Inhalt der Zwischenablage in das Steuerelement eingefügt werden kann (True) oder nicht (False) (Der Aktivierungszustand des Menübefehls EINFÜGEN sollte sich nach dem Wert dieser Eigenschaft richten.)
SizeMode
Integer-Wert, der den Zusammenhang zwischen den Abmessungen des Steuerelements und denen des OLE-Objekts festlegt. vbOLESizeClip (0) OLE-Objekt wird in der originalen Größe angezeigt, die Darstellung wird jedoch am Rand des Steuerelements beschnitten (Voreinstellung) vbOLESizeStretch (1) Darstellung des OLE-Objekts wird gestreckt oder gestaucht (und dabei gegebenenfalls verzerrt), damit sie den Fensterbereich des Steuerelements vollständig ausfüllt vbOLESizeAutoSize (2) Abmessungen des Steuerelements werden automatisch an die orignale Größe des OLE-Objekts angepasst (das Steuerelement-Objekt erhält ein Resize-Ereignis!) vbOLESizeZoom (3) Darstellung des OLE-Objekts wird gestreckt oder gestaucht (dabei jedoch nicht verzerrt), damit sie den Fensterbereich des Steuerelements möglichst vollständig ausfüllt
SourceDoc
Zeichenfolge, die für den Wert 1 der Action-Eigenschaft (Objekt verknüpfen) den Dateinamen der Verknüpfung bereitstellt und für den Wert 0 (Objekt einbetten) den Dateinamen der Datei mit dem einzubettenden OLE-Objekt. Im Zusammenhang mit den inzwischen verfügbaren Methoden CreateLink bzw. CreateEmbed hat der Wert von SourceDoc jedoch keine Bedeutung mehr. Zur Laufzeit gibt die SourceItem-Eigenschaft eine leere Zeichenfolge zurück, während die SourceDoc-Eigenschaft den vollständigen Pfad zu der verknüpften Datei, gefolgt von einem Ausrufezeichen (!) und dem Wert des SourceItem-Parameters enthält. Beispiel: "C:\MeinText!Textmarke1"
420
OLE- Container- Steuerelement (OLE)
Beschreibung
SourceItem
Zeichenfolge, die für den Wert 1 der Action-Eigenschaft (Objekt verknüpfen) das einzufügende Element (bzw. Elementbereich) des Objekts benennt, ansonsten (insbesondere im Zusammenhang mit den inzwischen verfügbaren Methoden CreateLink bzw. CreateEmbed) jedoch keine Funktion mehr hat (vgl. SourceDoc)
UpdateOptions
Integer-Wert, der bestimmt, wie ein verknüpftes Objekt aktualisiert wird, wenn sich seine Daten aufgrund von Fremdeinwirkungen ändern: vbOLEAutomatic (0) Automatisch (Voreinstellung). Die Darstellung des Objekts wird sofort aktualisiert, sobald sich die verknüpften Daten ändern
Verb
vbOLEFrozen (1)
Die Darstellung des Objekts ist gegenüber Änderungen des Objektzustands eingefroren. Eine Aktualisierung findet nur statt, wenn das Objekt von der Quellanwendung aus gespeichert wird
vbOLEManual (2)
Für die Aktualisierung der Darstellung des Objekts ist ein Aufruf der Update-Methode erforderlich
Integer-Wert, der für den Wert 7 der Action-Eigenschaft die auszuführende Operation angibt; wird von DoVerb ignoriert und dient nur noch dem Erhalt der Kompatibilität
................................................... Methoden Methoden
Close, Copy, CreateEmbed, CreateLink, Delete, DoVerb, Drag, FetchVerbs, InsertObjDlg, Move, Paste, PasteSpecialDlg, ReadFromFile, Refresh, SaveToFile, SaveToOle1File, SetFocus, ShowWhatsThis, Update, ZOrder Die Prototypen der nicht an anderer Stelle vorgestellten Methoden des Steuerelements sind: Sub Objekt.Copy() Sub Objekt.CreateEmbed(SourceDoc As String, [Class As String]) Sub Objekt.CreateLink(SourceDoc As String, [SourceItem As String]) Sub Objekt.Delete() Sub Objekt.DoVerb(Verb As Integer) Sub Objekt.FetchVerbs Sub Objekt.InsertObjDlg Sub Objekt.Paste Sub Objekt.PasteSpecialDlg Sub Objekt.ReadFromFile (FileNumber As Integer) Sub Objekt.SaveToFile (FileNumber As Integer) Methode
Beschreibung
Copy
Fügt eine Kopie des in der Zwischenablage befindlichen Objekts als OLEObjekt in das Steuerelement ein
421
Standardsteuerelemente
Eigenschaft
Standardsteuerelemente
Standardsteuerelemente
Methode
Beschreibung
CreateEmbed
Fügt ein OLE-Objekt als eingebettetes Objekt in das Steuerelement ein. Der obligatorische Parameter SourceDoc spezifiziert die Dokumentdatei, in der das Objekt gespeichert ist. Um ein neues Objekt anzulegen, erhält SourceDoc beim Aufruf die leere Zeichenfolge und der Class-Parameter den Namen der dem Objekt zugrunde zu legenden (registrierten) OLE-Klasse.
CreateLink
Fügt das in der Dokumentdatei mit dem Namen SourceDoc befindliche Objekt als verknüpftes Objekt in das Steuerelement ein. Über den optionalen Parameter SourceItem lässt sich das Objekt auf eines seiner Elemente bzw. einen Elementbereich einschränken. Nach dem erfolgreichen Öffnen des Objekts hinterlegt die Methode die vollständige Verknüpfungsinformation in der Eigenschaft SourceDoc nach dem Muster "C:\TEXT.DOC!MARKE1" für ein Word-Dokument oder "C:\TABS\LOHN.XLS!Z2S1:Z22S5" für eine Excel-Tabelle. Der Darstellung des Objekts im Steuerelement liegt das Metafile-Format zugrunde, da die Quelldaten dafür nicht verfügbar sind. Gespeichert wird nicht das Objekt selbst (dieses kann nur im Bearbeitungsmodus über die Quellanwendung gespeichert werden), sondern nur die in der Eigenschaft SourceDoc enthaltene Verknüpfungsinformation.
Delete
Löscht das dem Steuerelement zugeordnete OLE-Objekt explizit und gibt insbesondere die von diesem belegten Ressourcen frei. Erfolgt der DeleteAufruf für das Objekt nicht, wird es zusammen mit dem Steuerelementobjekt beim Schließen des Formulars abgebaut.
DoVerb
Weist das Objekt an, die Aktion Verb auszuführen. Als Werte für Verb stehen die Standardverben sowie die zu dem jeweiligen Objekt (und seinem aktuellen Zustand) gehörigen Verben zur Verfügung (vgl. ObjectVerbs). Verben aus dem ObjectVerbs-Array sind über ihren Index anzugeben.
FetchVerbs
Aktualisiert die Eigenschaft ObjectVerbs
InsertObjDlg
Ruft das Dialogfeld OBJEKT EINFÜGEN auf, das es dem Benutzer ermöglicht, interaktiv ein OLE-Objekt als verknüpftes oder eingebettetes Objekt in das Steuerelement einzufügen. Der Funktionsumfang des Dialogfelds richtet sich nach dem Wert der Eigenschaft OLETypeAllowed.
Paste
Kopiert das in dem Steuerelement enthaltene OLE-Objekt in die Zwischenablage
PasteSpecialDlg
Ruft das Dialogfeld EINFÜGEN auf, das es ermöglicht, den Inhalt der OLE-Zwischenablage als eingebettetes oder verknüpftes OLE-Objekt in das Steuerelement einzufügen. Der Funktionsumfang des Dialogfelds richtet sich nach dem Wert der Eigenschaft OLETypeAllowed.
ReadFromFile
Liest ein OLE-Objekt aus der bereits mit der Dateinummer FileNumber geöffneten Binärdatei und fügt es in das Steuerelement als eingebettetes oder verknüpftes Objekt ein. Das Objekt muss zuvor (mit der Methode SaveToFile) im OLE-Format in der Binärdatei gespeichert worden sein, sonst kommt es zu einem Laufzeitfehler.
422
OLE- Container- Steuerelement (OLE)
Methode
Beschreibung
SaveToFile
Speichert das im Steuerelement enthaltene OLE-Objekt in einer zuvor im binären Modus geöffneten Datei mit der Dateinummer FileNumber. Wenn es sich bei dem Objekt um ein verknüpftes Objekt handelt (OLEType = 0), speichert die Methode nur den in der Eigenschaft SourceDoc enthaltenen Verweis auf das Objekt.
SaveToOle1File
Wie SaveToFile, speichert das Objekt jedoch im OLE 1.0-Format
Ereignisse
Click, DblClick, DragDrop, DragOver, GotFocus, KeyDown, KeyPress, KeyUp, LostFocus, MouseDown, MouseMove, MouseUp, ObjectMove, Resize, Updated Private Sub Objekt_ObjectMove(Left As Single, Top As Single, _ Width As Single, Height As Single) Das Ereignis ObjectMove tritt auf, wenn der Benutzer eine Größenänderung beim aktiviertem OLE-Objekt vornimmt. Sie können das Ereignis behandeln, um die Größe des Steuerelements an die des Objekts anzupassen. Falls keine Behandlungsroutine existiert oder diese nicht die vom Objekt angeforderten Abmessungen einstellt, passt das Objekt seine Abmessungen wieder an die des Steuerelements an. Anwendung
................................................... Anwendung
Das OLE-Container-Steuerelement zählt zu den anspruchsvolleren Steuerelementen, deren Möglichkeiten schon beinahe ins Unerschöpfliche gehen, weil es einem Visual-Basic-Programm gewissermaßen die gesamte Welt des OLE 2.0 erschließt. Der Zugang zu diesem Steuerelement ist dennoch einfach. Platziert man es im Entwurfsmodus beispielsweise auf einem Formular, konfrontiert einen Visual Basic (nach einer kurzen »Bedenkzeit«) mit dem Dialogfeld OBJEKT EINFÜGEN, das eine Auswahl des für das Steuerelement vorgesehenen verknüpften Objekts bzw. des Typs für ein neu zu erstellendes eingebettetes Objekt gestattet. Sie können den Dialog auch abbrechen, wenn Sie vorhaben, dem Steuerelement erst zur Laufzeit ein OLE-Objekt zuzuordnen – etwa über die Zwischenablage. Um dem Steuerelement zur Laufzeit ein OLE-Objekt zuzuordnen, haben Sie verschiedene Möglichkeiten. Zunächst einmal können Sie den alten, von Microsoft jedoch nicht mehr empfohlenen Weg über die Action-Eigenschaft gehen: Setzen Sie dazu im ersten Schritt die Eigenschaften SourceDoc, SourceItem sowie erforderlichenfalls Class auf geeignete Werte. Im zweiten Schritt setzen Sie die Action-Eigenschaft auf den Wert 0, um ein eingebettetes OLE-Objekt aus der Datei SourceDoc zu laden bzw. ein neues Objekt des Typs Class zu generieren, oder auf den Wert 1, um ein verknüpftes OLE-Objekt einzufügen, das in der Datei SourceDoc gespeichert und gegebenenfalls durch SourceItem genauer spezifiziert ist. Der von Microsoft empfohlene Weg sieht dagegen den Aufruf der Methoden CreateEmbed und CreateLink vor, deren Parameter die gleichen Namen wie die von Action benutzten Eigenschaften tragen und auch die gleichen Funktionen haben, so dass die Eigenschaften unbeachtet bleiben. Einen dritten Weg eröffnet die Methode InsertObjDlg (respektive der Code 14 für die Action-Eigenschaft). Sie ruft das (bereits vom Entwurfsmodus her bekannte) Dialogfeld OBJEKT EINFÜGEN auf, in dem der Benutzer das OLE-Objekt interaktiv als eingebettetes oder verknüpftes Objekte genauer spezifizieren kann. Um zu erfahren, welche Art von Objekt der Benutzer in dem Dialog eingefügt hat, empfiehlt sich eine Auswertung der OLEType-Eigenschaft. Ein anderer Weg, wie ein OLE-Steuerelement zu einem OLE-Objekt kommt, ist der über die Zwischenablage. Wenn Sie den Kopiervorgang mit der Paste-Methode (im Standardformat)
423
Standardsteuerelemente
................................................... Ereignis s e
Standardsteuerelemente
Standardsteuerelemente
abwickeln wollen, sollten Sie prüfen, ob der Aufruf erfolgreich sein kann. Dazu stellen Sie erst einmal klar, ob die Zwischenablage das gewünschte Format liefern kann (vgl. ClipBoardObjekt), setzen dann die OLETypeAllowed-Eigenschaft auf die gewünschte Objektart und rufen schließlich Paste in Abhängigkeit vom Wert der Eigenschaft PasteOK auf. Sie können aber auch die Methode PasteDlgSpecial aufrufen, um dem Benutzer im Rahmen des Dialogfelds EINFÜGEN die Wahl zu überlassen, in welcher Form (und gegebenenfalls in welchem Format) er das Objekt einfügen möchte. Umgekehrt können Sie mittels Copy natürlich auch eine Kopie des OLE-Objekts in die Zwischenablage übertragen. Völlig automatisch geht die Geschichte, wenn der Benutzer das Objekt im Rahmen einer OLEDrag&Drop-Operation in das Steuerelement zieht. Einzige Voraussetzung: OLEDropAllowed muss True sein. Zu guter Letzt können Sie das Objekt aber auch mittels ReadFromFile direkt aus einer bereits im binären Modus geöffneten Datei einlesen. Die Kombination SaveToFile und ReadFromFile ermöglicht es, OLE-Objekte in eigenen Dateien zu speichern, um diese gegebenenfalls in einer späteren Sitzung wieder einzulesen. Um dem OLE-Objekt ein Kommando aus der Liste der Standardverben sowie seiner eigenen Verben (ObjectVerbs) zu übermitteln, benutzen Sie die Methode DoVerb. Wenn Sie das OLEObjekt des Steuerelements OLE1 beispielsweise für die Vorortbearbeitung aktivieren wollen, schreiben Sie: OLE1.DoVerb -4
Anstelle von -4 könnten Sie auch den Index des Befehls »Bearbeiten« im Kontextmenü des Objekts (ObjectVerbs) angeben. Dieser ist meist 0, da er im Allgemeinen die Standardoperation des Objekts darstellt. Natürlich kann der Benutzer die Aktivierung auch wie üblich auch per Doppelklick vornehmen (vgl. AutoActivate) Beispiel
................................................... Beis piel
OLE1.AutoActivate = 3 OLE1.OLETypeAllowed = vbOLEEmbedded If OLE1.PasteOK Then OLE1.PasteSpecialDlg ... If OLE1.OLEType <> 3 Then OLE1.DoVerb -4
' nur einbetten ' Aus Zwischenablage ... ' Objekt vorort bearbeiten
................................................... Verwandte Objekte ClipBoard Verwandte Themen
................................................... Verwandte Them en
OLE-Drag&Drop (S. 501)
Optionsfeld- Steuerelement (OptionButton) Option1 As OptionButton
424
Optionsfeld- Steuerelement (OptionButton)
Beschreibung
................................................... Bes c hreibung
Das Optionsfeld-Steuerelement visualisiert die Auswahl einer spezifischen Option unter mehreren möglichen Optionen. Im selben Container situierte Optionsfelder bilden automatisch eine Optionsgruppe, in der sich die Mitglieder gegenseitig auslösen, so dass maximal immer nur ein Optionsfeld markiert und damit auch ausgewählt sein kann. Standardeigenschaft des Optionsfelds ist Value, ein Boolean-Wert mit der Voreinstellung False. Eigenschaften
................................................... Eigens c ha ften
Methoden
................................................... Metho den Drag, Move, OLEDrag, Refresh, SetFocus, ShowWhatsThis, ZOrder Ereignisse
................................................... Ereignis s e
Click, DblClick, DragDrop, DragOver, GotFocus, KeyDown, KeyPress, KeyUp, LostFocus, MouseDown, MouseMove, MouseUp, OLECompleteDrag, OLEDragDrop, OLEDragOver, OLEGiveFeedback, OLESetData, OLEStartDrag, Validate Anwendung
................................................... Anwendung
Das Optionsfeld bildet aus logischer Sicht das Gegenstück zum Kontrollkästchen. Während letzteres als Schalter für logisch voneinander unabhängige Attribute fungiert, ist das Optionsfeld für die Darstellung sich gegenseitig ausschließender Attribute konzipiert. Es hat daher wenig Sinn, einzelne Optionsfelder isoliert zu verwenden. Vielmehr werden Optionsfelder immer gruppiert. Falls ein Formular oder eine Komponente mehrere logisch voneinander unabhängige Gruppierungen anzeigen soll, müssen die Gruppierungen in je eigenen ContainerSteuerelementen (Frame, PictureBox, SSTab) platziert sein (vgl. Abbildung). Je Gruppierung sollten Sie eine Standardoption festlegen, ein Optionsfeld, dessen Value-Eigenschaft Sie am besten bereits zur Entwurfszeit auf True setzen. Jede Änderung der Value-Eigenschaft, gleich ob mit der Maus, mit einer Zugriffstaste oder vom Programmcode aus, bringt das Click-Ereignis hervor. Da Value die Standardeigenschaft des OptionButton-Objekts ist, muss nur der Objektbezeichner notiert werden, um diese Eigenschaft anzusprechen. Um eine Zugriffstaste für das Steuerelement zu vereinbaren, stellen Sie dem entsprechenden Buchstaben in der Beschriftung (Caption) das Zeichen »&« voran. Er erscheint zur Laufzeit unterstrichen. Zur einfachen Auswertung vereinbaren Sie Optionsfeldgruppen am besten als SteuerelementeArrays. Der Index-Parameter des Click-Ereignisses liefert dann nämlich vorteilhafterweise den Array-Index des betroffenen Optionsfelds: Option1_Click(Index As Integer) Select Case Index Case 0: ' Erstes Attribut ... Case 1: ' Zweites Attribut ... End Select End Sub
425
Standardsteuerelemente
Alignment, Appearance, BackColor, Caption, CausesValidation, Container, DataFormat, DisabledPicture, DownPicture, DragIcon, DragMode, Enabled, Font, FontBold, FontItalic, FontName, FontSize, FontStrikethru, FontUnderline, ForeColor, Height, HelpContextID, hWnd, Index, Left, MaskColor, MouseIcon, MousePointer, Name, OLEDropMode, Parent, RightToLeft, Style, TabIndex, TabStop, Tag, Top, ToolTipText, UseMaskColor, Value, Visible, WhatsThisHelpID, Width
Standardsteuerelemente
Standardsteuerelemente
Das Optionsfeld kennt zwei unterschiedliche Darstellungen: die Standarddarstellung im Stile von Windows 9x (vgl. Abbildung) und eine benutzerdefinierte Darstellung. Die Style-Eigenschaft entscheidet darüber, welche der Darstellungen zur Anzeige kommt. In der benutzerdefinierten Darstellung sieht das Optionsfeld zunächst wie eine Befehlsschaltfläche aus, hält aber im Gegensatz zu dieser den gedrückten Zustand, bis es von einem anderen Optionsfeld ausgelöst wird. Um dem Steuerelement ein benutzerdefiniertes Aussehen zu verleihen, können Sie den Eigenschaften Picture und DownPicture sowie gegebenenfalls DisabledPicture geeignete Bilder zuordnen. Falls Sie eine pseudotransparente Darstellung wünschen, bei der die über eine Transparenzfarbe (MaskColor) definierten transparenten Bereiche der Bilder in Hintergrundfarbe erscheinen, müssen Sie die Eigenschaft UseMaskColor auf True setzen. (Ein analoges Beispiel für Schaltflächen findet sich im Abschnitt »DisabledPicture-Eigenschaft«, S. 341.) Beispiel
................................................... Beis piel
Im folgenden Beispiel dient eine Optionsfeldgruppe zur Auswahl der Form für das Figur-Steuerelement Shape1. Private Sub Option1_Click(Index As Integer) Shape1.Shape = Index End Sub
Verwandte Befehle
................................................... Verwa ndte Befehle
Button, CheckBox, CommandButton
Rahmen- Steuerelement (Frame) Frame1 As Frame Beschreibung
................................................... Bes c hreibung
Das Rahmen-Steuerelement ist eines der drei Steuerelemente, die als Container für andere Steuerelemente auftreten können. Die beiden anderen sind: PictureBox und SSTab. Es kann daher nicht nur für die visuelle, sondern insbesondere auch für die funktionelle Gruppierung anderer Steuerelemente benutzt werden. Standardeigenschaft des Steuerelements ist die Caption-Eigenschaft, die der Entwurfseditor mit dem automatisch generierten Namen des Steuerelements vorbesetzt. Eigenschaften
................................................... Eigens c ha ften Appearance, BackColor, Caption, ClipControls, Container, DragIcon, DragMode, Enabled, Font, FontBold, FontItalic, FontName, FontSize, FontStrikethru, FontUnderline, ForeColor, Height, HelpContextID, hWnd, Index, Left, MouseIcon, MousePointer, Name, OLEDropMode, Parent, RightToLeft, TabIndex, Tag, Top, ToolTipText, Visible, WhatsThisHelpID, Width
426
Textfeld- Steuerelement ( TextBox)
Methoden
................................................... Metho den Drag, Move, OLEDrag, Refresh, ShowWhatsThis, Refresh, ZOrder Ereignisse
................................................... Ereignis s e
Click, DblClick, DragDrop, DragOver, MouseDown, MouseMove, MouseUp, OLECompleteDrag, OLEDragDrop, OLEDragOver, OLEGiveFeedback, OLESetData, OLEStartDrag Anwendung
................................................... Anwendung
Verwandte Steuerelemente
................................................... Verwandte Steuerelemente PictureBox, SSTab
Textfeld- Steuerelement (TextBox) Text1 As TextBox Beschreibung
................................................... Bes c hreibung
Das Textfeld-Steuerelement ist auf die interaktive Ein- und Ausgabe von Text eingerichtet. Die Standardeigenschaft Text des Steuerelements ist vom Typ String und kann (nahezu) beliebig lange Zeichenfolgen enthalten. Um die Länge zu beschränken, lässt sich über die MaxLengthEigenschaft eine maximale Zeichenanzahl setzen. Mittels der Locked-Eigenschaft lässt sich das Steuerelement für Benutzereingaben sperren. Neben der voreingestellten einzeiligen Anzeige unterstützt das Steuerelement auch die mehrzeilige Anzeige mit automatischem Umbruch auf der Basis von Leerzeichen. Dazu ist die MultiLine-Eigenschaft beim Entwurf auf True zu setzen. Falls der Fensterbereich des Textfelds für die Anzeige des Werts nicht ausreicht, lassen sich zur besseren Navigation über die ScrollBars-Eigenschaft Bildlaufleisten hinzufügen. In einer eingeschränkten Fassung ist das Textfeld auch als Bestandteil der Steuerelemente ComboBox, DBCombo, DataGrid sowie als »Partner« (Buddy) des UpDown-Steuerelements anzutreffen. Über seine Link-Eigenschaften, -Methoden und -Ereignisse kann das Steuerelement als Quelloder Zielsteuerelement für DDE-Verbindungen benutzt werden – mehr dazu unter den entsprechenden Eigenschaften und Ereignissen. Die Data-Eigenschaften ermöglichen die Anbindung des Textfelds an ein Datenbankfeld – mehr dazu unter »Data-Datensteuerelement (Data)«, S. 402. Eigenschaft
Beschreibung
HideSelection
Boolean-Wert, der bestimmt, ob eine Markierung bei Fokusverlust sichtbar bleibt (False) oder nicht (True; Voreinstellung)
427
Standardsteuerelemente
Eine der Regeln, die der Entwurfseditor auferlegt, lautet: Ein Steuerelement kann seinen Container nicht wechseln. Um ein Steuerelement in einem Rahmen zu platzieren, müssen Sie das Steuerelement also bereits im Rahmen anlegen. Zur Laufzeit sieht das anders aus: Indem Sie die Container-Eigenschaft eines Steuerelement auf ein Frame-Objekt setzen, platzieren Sie es in dessen Rahmen (beachten Sie aber, dass sich dadurch das für das Steuerelement geltende Koordinatensystem ändern kann). Die Hauptanwendung des Rahmen-Steuerelements ist und bleibt aber die Gruppierung von Optionsfeldern. Da in einem Container immer nur ein Optionsfeld markiert sein kann, weil sich alle in einem Container befindlichen Optionsfelder gegenseitig auslösen, müssen funktional voneinander unabhängige Gruppierungen auf verschiedene Container verteilt werden. Unter den verfügbaren Container-Steuerelementen ist das Rahmen-Steuerelement die perfekte Wahl, weil es zudem eine passende Beschriftung der Gruppierung ermöglicht.
Standardsteuerelemente
Standardsteuerelemente
Eigenschaft
Beschreibung
Locked
Boolean-Wert, der bestimmt, ob der Benutzer Eingaben in das Textfeld vornehmen kann (False; Voreinstellung) oder nicht (True). Ein für die Eingabe gesperrtes Textfeld nimmt zwar den Fokus an und erlaubt die Navigation im Text, reagiert aber nicht auf Tastatureingaben, die den aktuellen Wert ändern würden.
MaxLength
Long-Wert, der bestimmt, wie viele Zeichen der Wert des Textfelds maximal enthalten darf. Wird die Eigenschaft auf dem voreingestellten Wert 0 belassen, kann Text wie jeder andere Wert vom Typ String (nahezu) beliebig lang werden. Wird die MaxLength-Eigenschaft im Nachhinein kleiner als die Länge des aktuellen Text-Werts gesetzt, ändert dies die Anzeige nicht, und der Benutzer kann nach wie vor den gesamten Wert bearbeiten. Die neue Beschränkung tritt jedoch alsbald in Kraft, wenn der Benutzer Zeichen löscht.
MultiLine
Zur Laufzeit schreibgeschützter Boolean-Wert, der bestimmt, ob die Darstellung des Standardwerts auf eine Zeile beschränkt bleibt (False; Voreinstellung) oder auch mehrere Zeilen umfassen kann. Falls das Textfeld eine horizontale Bildlaufleiste besitzt (ScrollBars-Eigenschaft), richtet sich der Umbruch nach dem Vorkommen von Zeilenvorschubzeichen (vbCrLf), ansonsten führt das Steuerelement einen automatischen Umbruch auf der Basis von Leerzeichen durch.
PasswordChar
String-Wert der Länge 1, der das Platzhalterzeichen für die Kennworteingabe bestimmt. Wird die Eigenschaft auf einen Wert ungleich der leeren Zeichenfolge (Voreinstellung) gesetzt, aktiviert dies den Modus »Kennworteingabe«: Der Benutzer sieht dann für jedes eingegebene Zeichen ein Platzhalterzeichen und kann den Wert nicht mehr in die Zwischenablage übernehmen. Das Textfeld ignoriert diese Eigenschaft allerdings bei mehrzeiliger Anzeige (vgl. MultiLine).
ScrollBars
Integer-Wert, der bestimmt, ob das Textfeld im mehrzeiligen Modus (vgl. MultiLine) Bildlaufleisten anzeigt und automatisch verwaltet. Der Aufzählungstyp ScrollBarConstants definiert dafür die Konstanten: vbSBNone (0) für keine Bildlaufleisten; vbHorizontal (1) für waagrechte Bildlaufleiste; vbVertical (2) für senkrechte Bildlaufleiste; vbBoth (3) für waagrechte und senkrechte Bildlaufleiste. Bei Anzeige der waagrechten Bildlaufleiste erfolgt kein automatischer Umbruch auf der Basis von Leerzeichen.
SelLength
Long-Wert, der die Länge der Markierung ab der Position SelStart beschreibt. Ist der Wert größer als die Anzahl der restlichen Zeichen ab SelLength, korrigiert das Steuerelement den Wert auf die Anzahl der restlichen Zeichen.
SelStart
Long-Wert, der die Position des Zeichens in Text beschreibt, an der die Markierung beginnt. Ein Ändern dieser Eigenschaft setzt SelLength auf 0. Ist der neue Wert für SelStart größer als die Länge von Text, korrigiert ihn das Steuerelement auf die aktuelle Länge des Textes, so dass er auf die Position hinter dem letzten Zeichen verweist.
428
Textfeld- Steuerelement ( TextBox)
Eigenschaft
Beschreibung
SelText
String-Wert, der den markierten Teil von Text wiedergibt oder neu definiert. Setzt man diese Eigenschaft, passiert Folgendes: Ist die aktuelle Länge der Markierung 0 (SelLength), wird der neue Wert an der Position SelStart eingefügt. Ansonsten ersetzt der neue Wert die Markierung, ohne dass die Längen eine Rolle spielen.
Eigenschaften
................................................... Eigens c ha ften
Methoden
................................................... Metho den Drag, LinkExecute, LinkPoke, LinkRequest, LinkSend, Move, OLEDrag, Refresh, SetFocus, ShowWhatsThis, ZOrder Ereignisse
................................................... Ereignis s e
Change, Click, DblClick, DragDrop, DragOver, GotFocus, KeyDown, KeyPress, KeyUp, LinkClose, LinkError, LinkNotify, LinkOpen, LostFocus, MouseDown, MouseMove, MouseUp, OLECompleteDrag, OLEDragDrop, OLEDragOver, OLEGiveFeedback, OLESetData, OLEStartDrag, Validate Anwendung
................................................... Anwendung
Das Textfeld-Steuerelement ersetzt die klassische Input-Anweisung von Basic. Es eignet sich für die Ein- und Ausgabe beliebiger Werte mit String-Repräsentation und erfreut durch eine brauchbare Implementation der Tastaturschnittstelle. Um den Wert des Textfelds zu setzen oder abzufragen, genügt es, den Bezeichner des Textfeldobjekts zu notieren. Visual Basic nimmt nach Möglichkeit eine automatische Typumwandlung vor. Mit jeder Änderung von Text tritt das Change-Ereignis auf. Es zeigt an, dass eine Tastatureingabe, eine DDE-Aktualisierung, eine Datensatzaktualisierung oder eine Wertänderung vom Programmcode aus geschehen ist. Wenn Sie eine Gültigkeitsprüfung für den Fall des Fokusverlusts etablieren wollen, behandeln Sie das Validate-Ereignis. Dazu muss jedoch die CausesValidation-Eigenschaft nicht nur des Textfelds, sondern auch des Steuerelements, das den Fokus erhält, auf True gesetzt sein. Wenn Sie ein Textfeld für die Kennworteingabe verwenden wollen, setzen Sie die Eigenschaft PasswordChar auf ein Zeichen, das als Platzhalterzeichen für eingegebene Zeichen erscheinen soll. Tipp
................................................... Tipp
Anstelle eines Textfelds können Sie einmalige Eingaben auch über die InputBox-Funktion abwickeln: a$ = InputBox ("Bitte Startwert eingeben")
429
Standardsteuerelemente
Alignment, Appearance, BackColor, BorderStyle, CausesValidation, Container, DataChanged, DataField, DataFormat, DataMember, DataSource, DragIcon, DragMode, Enabled, Font, FontBold, FontItalic, FontName, FontSize, FontStrikethru, FontUnderline, ForeColor, Height, HelpContextID, HideSelection, hWnd, Index, Left, LinkItem, LinkMode, LinkTimeout, LinkTopic, Locked, MaxLength, MouseIcon, MousePointer, MultiLine, Name, OLEDragMode, OLEDropMode, Parent, PasswordChar, RightToLeft, ScrollBars, SelLength, SelStart, SelText, TabIndex, TabStop, Tag, Text, Top, ToolTipText, Visible, WhatsThisHelpID, Width
Standardsteuerelemente
Beispiel
................................................... Beis piel
Standardsteuerelemente
Das Beispielprojekt EuroDMTestProjekt zeigt die Implementation eines Benutzersteuerelements mit zwei Textfeldern, die sich gegenseitig im Rahmen ihrer Change-Behandlung aktualisieren. Der Zweck der auf Modulebene vereinbarten Zustandsvariable Changing besteht darin, einer endlosen Rekursion vorzubeugen, die ansonsten aufgrund der gegenseitigen Wertänderung der Steuerelemente resultieren würde.
Fenster eines
Formulars,
auf
dem
das
Benutz ersteuerelement
' Projekt: Euro/DM-TestProjekt Const sKurs = 1.95583 Dim Changing As Boolean Property Let Euro(Value As Currency) tfEuro = Value End Property Property Get Euro() As Currency Euro = tfEuro End Property Property Let DM(Value As Currency) tfDM = Value End Property Property Get DM() As Currency DM = tfDM End Property Private Sub tfDM_Change() If Not Changing Then Changing = True tfEuro = Round(Val(tfDM) / sKurs, 2) Else Changing = False End If End Sub Private Sub tfEuro_Change() If Not Changing Then Changing = True tfDM = Round(Val(tfEuro) * sKurs, 2) Else Changing = False End If End Sub
430
EuroDM platz iert
wurde
Verzeichnislistenfeld- Steuerelement ( DirListBox)
Verwandte Steuerelemente
................................................... Verwandte Steuerelemente ComboBox, DataList, DBCombo, MaskEDBox, RichTextBox, RptTextBox
Verzeichnislistenfeld- Steuerelement (DirListBox) Dir1 As DirListBox
................................................... Bes c hreibung
Das Verzeichnislistenfeld-Steuerelement visualisiert die hierarchische Einbettung eines Verzeichnisses im Verzeichnisbaum des jeweiligem Laufwerks und bietet die Möglichkeit der interaktiven Auswahl eines jeden beliebigen Verzeichnisses auf dem gegebenen Laufwerk. Standardeigenschaft des Steuerelements ist Path. Eigenschaften
................................................... Eigens c ha ften Appearance, BackColor, CausesValidation, Container, DragIcon, DragMode, Enabled, Font, FontBold, FontItalic, FontName, FontSize, FontStrikethru, FontUnderline, ForeColor, Height, HelpContextID, hWnd, Index, Left, List, ListCount, ListIndex, MouseIcon, MousePointer, MultiSelect, Name, OLEDragMode, OLEDropMode, Parent, Path, TabIndex, TabStop, Tag, Top, ToolTipText, TopIndex, Visible, WhatsThisHelpID, Width Methoden
................................................... Metho den Drag, Move, OLEDrag, Refresh, SetFocus, ShowWhatsThis, ZOrder Ereignisse
................................................... Ereignis s e
Change, Click, DragDrop, DragOver, GotFocus, KeyDown, KeyPress, KeyUp, LostFocus, MouseDown, MouseMove, MouseUp, OLECompleteDrag, OLEDragDrop, OLEDragOver, OLEGiveFeedback, OLESetData, OLEStartDrag, Scroll, Validate Anwendung
................................................... Anwendung
Das Verzeichnislistenfeld (DirListBox) ist wie das Dateilistenfeld und das Laufwerklistenfeld (DriveListBox) noch ein Relikt aus der Zeit, als Anwendungen den Benutzer mit eigenen Varianten für Dialoge wie »Datei öffnen«, »Speichern unter« usw. auf die Probe stellen mussten, weil es die Standarddialoge (CommonDialog) noch nicht gab. Seine aktuelle Daseinsberechtigung bezieht das Verzeichnislistenfeld allerdings aus der Tatsache, dass das Standarddialoge-Steuerelement (COMLDG32.OCX) den Benutzer dazu zwingt, eine Datei auszuwählen, und die reine Verzeichnisauswahl nicht zulässt. Visual Basic initialisiert die Path-Eigenschaft des Steuerelements mit dem aktuellen standardmäßigen Arbeitsverzeichnis, das im Allgemeinen mit App.Path übereinstimmt, beim Aufruf aber auch explizit gesetzt worden sein kann (vgl. auch »CurDir-Funktion«, S. 134). Der Laufwerksanteil von Path lässt sich nur mit Programmcode ändern. Sie können den Wert wahlweise als Laufwerkspfad oder als UNC-Pfad angeben, um Netzwerkressourcen ohne Laufwerkszuordnung darzustellen. Sehr weit kommt man damit jedoch nicht, da es schwierig ist, in Visual Basic
43 1
Standardsteuerelemente
Beschreibung
Standardsteuerelemente
Standardsteuerelemente
Netzwerkfreigaben in Erfahrung zu bringen, die keinem Netzlaufwerk zugeordnet sind – ein Grund mehr, letztlich doch mit den Standarddialogen zu arbeiten. Um dem Benutzer die Möglichkeit der interaktiven Änderung zu geben, können Sie in der Nähe des Verzeichnislistenfelds zusätzlich ein Laufwerklistenfeld (DriveBox) platzieren und dessen Change-Behandlung die Path-Eigenschaft des Verzeichnislistenfelds ändern lassen. Die Implementation der Steuerelemente ist aufeinander abgestimmt, so dass die geklammerte Volumenbezeichnung des Laufwerks im Wert von Drive herausgefiltert wird. Private Sub Drive1_Change() Dir1 = Drive1 ' Volumenbezeichnung wird gefiltert End Sub
Eine Möglichkeit für das Herausfiltern von Verzeichnissen mit bestimmten Attributen bietet das Steuerelement nicht. Da über die Eigenschaften List, ListCount, ListIndex jedoch alle Einträge für die angezeigten Verzeichnisse in Form absoluter Pfade verfügbar sind, lassen sich die Attribute recht einfach gesondert über die Funktion GetAttr ermitteln. Mit dem Unterschied, dass die List-Eigenschaft schreibgeschützt ist und auch sonst keine Möglichkeiten gegeben sind, die Einträge des Listenfelds zu ändern, verhält sich das Laufwerklistenfeld ansonsten wie ein gewöhnliches Listenfeld, insbesondere was die Mehrfachauswahl (MultiSelect) und den Bildlauf betrifft. Änderungen im Verzeichnissystem spiegelt das Steuerelement nicht automatisch wider, vielmehr ist ein Refesh-Aufruf dafür erforderlich. Warnung
................................................... Wa rnung
Der Wert "\\." für Path bringt das Steuerelement sowie die gesamte Entwicklungsumgebung von Visual Basic sicher zum Absturz. Beispiel
................................................... Beis piel
Private Sub Dir1_Change() CurDir = Dir1 ' standardmäßiges Arbeitsverzeichnis setzen End Sub Verwandte Befehle
................................................... Verwa ndte Befehle
DriveListBox, FileListbox, ListBox, ListView
Zeitgeber- Steuerelement (Timer) Timer1 As Timer Beschreibung
................................................... Bes c hreibung
Unter den Standardsteuerelementen ist das Zeitgeber-Steuerelement das einzige, das keine sichtbare Darstellung besitzt. Vielmehr implementiert das Steuerelement einen einfachen Mechanismus, der periodisch das Timer-Ereignis produzieren kann. Die Periodendauer wird über die Eigenschaft Interval gesetzt. Hat diese Eigenschaft den Wert 0, löst das Steuerelement keine Timer-Ereignisse aus, ansonsten legt der Wert die Anzahl der Millisekunden zwischen zwei Timer-Ereignissen fest. Eigenschaften
................................................... Eigens c ha ften Enabled, Index, Interval, Name, Parent, Tag
432
ActiveX- Steuerelemente (OCX) Windows- Standardsteuerelemente
Methoden
................................................... Metho den Keine Ereignisse
................................................... Ereignis s e
Timer Anwendung
................................................... Anwendung
Beispiel
................................................... Beis piel
Vgl. das Beispiel in »AutoSize-Eigenschaft« (S. 329). Verwandte Themen
................................................... Verwandte Them en
Date-Funktion und Date-Anweisung (S. 116); Time-Funktion und Time-Anweisung (S. 123); Timer-Funktion (S. 124)
ActiveX- Steuerelemente (OCX) WindowsStandardsteuerelemente Beschreibung
................................................... Bes c hreibung
Die Standardsteuerelemente stellen einen durchaus geglückten Versuch von Microsoft dar, die Benutzeroberflächen und -schnittstellen von Windows-Anwendungen weitgehend zu standardisieren. Bei den Windows-Standardsteuerelementen (Common Controls) handelt es sich gewissermaßen um den zweiten Aufguss, der inzwischen (seit Windows 95) nicht nur in 32-Bit-Implementation vorliegt und komplett an das Design von Windows 9x angepasst wurde, sondern auch mit dem Component Object Model (COM) kompatibel ist. Windows-Standardsteuerelemente sind somit echte ActiveX-Steuerelemente – während die Standardsteuerelemente schlicht ein Teil von Windows sind. Da die Benutzeroberfläche von Windows, der Explorer, der Internet-Explorer, alle Systemprogramme sowie die gesamte Palette der Microsoft-Produkte massiv von diesen Steuerelementen Gebrauch machen, sind sie gewissermaßen zum unverzichtbaren Bestandteil für die gesamte Welt der Windows-Programmierung geworden – frei nach dem Motto, »sag's mit Steuerelementen«. Kopieraktionen oder andere zeitaufwändige Operationen
43 3
ActiveX- Steuerelemente (OCX) – Windows- Standardsteuerelemente
Die Anwendungsmöglichkeiten des Zeitgeber-Steuerelements sind so vielseitig, wie die Problemstellungen, die etwas mit Zeit und periodischen Aufgaben zu tun haben. Erwarten Sie aber nicht zu viel von dem Steuerelement. Zunächst einmal ist das Zeitintervall nicht sehr genau. Das liegt zum einen daran, dass das Steuerelement als Zeitbasis den Systemzeitgeber verwendet, der unter Windows 9x nur 18,2-mal pro Sekunde und unter Windows NT 50-mal pro Sekunde tickt, eine Altlast aus der Zeit von MS-DOS. (Es hat also wenig Sinn, den Wert kleiner als 6 unter Windows 9x bzw. kleiner als 2 unter Windows NT zu setzen.) Zum anderen können sich Timer-Ereignisse verspäten oder auch verloren gehen, da Windows anderen Ereignissen gegenüber Timer-Ereignissen einen gewissen Vorrang einräumt und Timer-Ereignisse sich grundsätzlich nicht ansammeln. Zum dritten: Obwohl die Eigenschaft Interval vom Typ Long ist, akzeptiert das Steuerelement dafür keine Werte größer als 65.535 (16 Bit), was das maximale Zeitintervall auf ca. 65 Sekunden beschränkt. Um längere Zeiträume zu implementieren, müssen Sie eine Logik verwenden, bei der die Timer-Routine in regelmäßigen Abständen die Systemzeit ausliest oder ihre eigenen Aufrufe zählt. Ein Beispiel dafür finden Sie im Abschnitt »LongTimer – der Timer mit Ausdauer« (S. 599) des Praxisteils.
ActiveX- Steuerelemente (OCX) – Windows- Standardsteuerelemente
ActiveX- Steuerelemente ( OCX) Windows- Standardsteuerelemente
verkürzen dem Benutzer die Wartezeit durch Animationen und Fortschrittsanzeigen. Listenansichten und Strukturansichten standardisieren die Darstellung komplexer Informationen und ihrer Zusammenhänge. Statusleisten und Symbolleisten dürfen inzwischen in ernstzunehmenden Programmen ebenso wenig fehlen wie Eigenschaftsdialoge. Da ist es nicht verwunderlich, dass Microsoft in regelmäßigen Abständen Aktualisierungen an der Implementation sowie im Design der Steuerelemente vorstellt und die Sammlung zunehmend erweitert. Unter Visual Basic 6.0 sind die Windows-Standardsteuerelemente aktuell in zwei unterschiedlichen Versionen vertreten: in der Version 5.0 sowie in der Version 6.0. Der Grund dafür liegt natürlich an der Aufrechterhaltung der Kompatibilität mit älteren Anwendungen und Projekten. Für neue Projekte sollten Sie ausschließlich mit der neueren Version arbeiten. Wenn Sie ein älteres, noch mit Visual Basic 5.0 erstelltes Projekt laden, legt Ihnen Visual Basic 6.0 die automatische Aktualisierung der Windows-Standardsteuerelemente ans Herz – ein Vorgang, der problemfrei über die Bühne geht. Die Aktualisierungsaufforderung unterbleibt, wenn im Eigenschaftsfenster des Projekts EIGENSCHAFTEN VON Projektname das Kontrollkästen ACTIVEX-STEUERELEMENTE AKTUALISIEREN deaktiviert ist. Um zu prüfen, welche Version der Steuerelemente ein Projekt verwendet, rufen Sie das Dialogfeld KOMPONENTEN über den Menübefehl PROJEKT/KOMPONENTEN auf und schauen, welche Komponenten ein Häkchen auf der Registerkarte STEUERELEMENTE haben. Um einzelne Windows-Standardsteuerelemente in einem neuen Projekt nutzen zu können, rufen Sie das Dialogfeld KOMPONENTEN über den Menübefehl PROJEKT/KOMPONENTEN auf und setzen auf der Registerkarte STEUERELEMENTE vor die entsprechende Komponente ein Häkchen. (Beachten Sie den OCX-Dateinamen der Komponente unterhalb des Listenfelds für die markierte Komponente.)
Auswahl der Windows- Standardsteuerelemente
Sie müssen nicht alle drei Komponenten auswählen. Wenn Sie beispielsweise nur mit dem CoolBar-Steuerelement arbeiten wollen, reicht die Auswahl des Eintrags Microsoft Windows Common Controls-3 6.0 (SP3). Nachdem Sie die Auswahl übernommen bzw. das Dialogfeld geschlossen haben, finden Sie für jedes Steuerelement eine zusätzliche Schaltfläche in der Werkzeugleiste und können damit genauso wie mit den Standardsteuerelementen von Visual Basic arbeiten.
43 4
ActiveX- Steuerelemente (OCX) Windows- Standardsteuerelemente
Werkzeugsammlung nac h Auswahl der Windows- Standardsteuerelemente
Steuerelement
Komponentendatei (Version)
Bestimmung des Steuerelements
ImageList
COMCTL32.OCX (5.0) MSCOMCTL.OCX (6.0)
Abbildungsliste, die anderen Steuerelementen (beispielsweise ImageCombo, TreeView, ToolBar) Bilder für die Anzeige bereitstellt
Animation
COMCT232.OCX (5.0) MSCOMCTL2.OCX (6.0)
Spielt unkomprimierte AVI-Dateien ohne Ton ab
CoolBar
MSCOMCTL3.OCX (6.0)
Konfigurierbare Symbolleiste im Stile des Internet Explorer (erfordert Installation des Internet Explorer mindestens in der Version 3.0)
DTPicker
MSCOMCTL2.OCX (6.0)
Ermöglicht die interaktive Datums-/Zeitauswahl
UpDown
COMCT232.OCX (5.0) MsComctl2.ocx (6.0)
Drehfeld (auch AufAb-Steuerelement genannt) mit Pfeilschaltflächen für das Hoch- oder Herunterzählen eines Werts im anhängenden Textfeld (Buddy)
FlatScrollBar
MSCOMCTL2.OCX (6.0)
Flache Bildlaufleiste, die nur bei Aktivierung eine 3D-Darstellung aufweist, ansonsten »flach« aussieht
ProgressBar
COMCTL32.OCX (5.0) MSCOMCTL.OCX (6.0)
Fortschrittsleiste für die animierte Fortschrittsanzeige bei längeren Operationen
ImageCombo
MSCOMCTL.OCX (6.0)
Kombinationsfeld mit bebilderten Texteinträgen
MonthView
MSCOMCTL2.OCX (6.0)
Kalender-Steuerelement für die interaktive Datums- und Zeitraumauswahl
ListView
COMCTL32.OCX (5.0) MSCOMCTL.OCX (6.0)
Listenansicht für die Anzeige von Texteinträgen mit zugeordneten Bildern (Symbolen) in vier unterschiedlichen Ansichten und sortierbaren Spalten
TabStrip
COMCTL32.OCX (5.0) MSCOMCTL.OCX (6.0)
Element für die Darstellung von Registerkarten in Eigenschaftsdialogen
43 5
ActiveX- Steuerelemente (OCX) – Windows- Standardsteuerelemente
In der Version 5.0 waren zehn Windows-Standardsteuerelemente im Angebot, die in zwei OCX-Dateien (ComCtl32.ocx, ComCt232.ocx) enthalten waren und die Unterstützung einer DLL (ComCtl32.dll) benötigten. In der Version 6.0 sind es bereits 15 Steuerelemente, die sich auf drei OCX-Dateien (MsComctl.ocx, MsComct2.ocx, MsComct3.ocx) verteilen, und eine DLL ist nicht mehr erforderlich. Die folgende Tabelle gibt einen Überblick über die Steuerelemente.
ActiveX- Steuerelemente (OCX) – Windows- Standardsteuerelemente
ActiveX- Steuerelemente ( OCX) Windows- Standardsteuerelemente
Steuerelement
Komponentendatei (Version)
Bestimmung des Steuerelements
Slider
COMCTL32.OCX (5.0) MSCOMCTL.OCX (6.0)
Schieberegler mit Skala für die Visualisierung analoger Einstellungen
StatusBar
COMCTL32.OCX (5.0) MSCOMCTL.OCX (6.0)
Statusleiste für die Anzeige von Statusinformationen am unteren Rand eines Formulars
TreeView
COMCTL32.OCX (5.0) MSCOMCTL.OCX (6.0)
Strukturansicht für die Darstellung hierarchischer Abhängigkeiten und Strukturen
ToolBar
COMCTL32.OCX (5.0) MSCOMCTL.OCX (6.0)
Symbolleiste für die Gruppierung von Befehlsschaltflächen am (meist oberen) Formularrand
Die vorliegende Ausgabe dieser Referenz stellt hier gewissermaßen exemplarisch nur das Windows-Standardsteuerelement ImageList ausführlicher vor, da es als Bildlieferant für verschiedene Szenarien fungieren kann.
Abbildungsliste- Steuerelement (ImageList) ImageList1 As ImageList Beschreibung
................................................... Bes c hreibung
Das Abbildungsliste-Steuerelement hat wie das Zeitgeber-Steuerelement zur Laufzeit keine sichtbare Darstellung, sondern dient in erster Linie anderen Steuerelementen, die über eine ImageList-Eigenschaft verfügen, als Lieferant von Bildressourcen. Als Struktur für die Speicherung der Bilder verwendet das Steuerelement eine ListImages-Auflistung mit dem Elementtyp ListImage. Zur Erweiterung der Auflistung gibt es eine AddMethode und zum Löschen die Methoden Remove bzw. Clear. Der Zugriff bzw. Verweis auf ein einzelnes Bild erfolgt wahlweise unter Angabe des in der Index-Eigenschaft des entsprechenden ListImage-Objekts festgelegten Index (Basis ist 1) oder des in der Key-Eigenschaft vom Typ String festgelegten Namens. Die Overlay-Methode des Steuerelements generiert zusammengesetzte Symbole durch Übereinanderblendung zweier Bilder der Bildliste unter Berücksichtigung einer Transparenzfarbe. Eigenschaften
................................................... Eigens c ha ften BackColor, hImageList, ImageHeigth, ImageWidth, Index, ListImages, MaskColor, Name, Object, Parent, Tag, UseMaskColor Eigenschaft
Beschreibung
BackColor
Wird von der Methode Overlay sowie von ListImage.Draw als Hintergrundfarbe für resultierende transparente Bereiche verwendet, wenn UseMaskColor und MaskColor passend gesetzt sind
hImageList
Zur Laufzeit schreibgeschützter Long-Wert mit dem Handle des Steuerelements (wird nur für Win32-API-Aufrufe benötigt)
ImageHeight ImageWidth
Integer-Werte, die die Breite und Höhe (in Bildpunkten) der Symbole festlegt, die mit ListImage.DrawIcon, ListImage.Draw und Overlay generiert werden. Die Werte sind schreibgeschützt, wenn die Bildliste Einträge enthält.
43 6
Abbildungsliste- Steuerelement ( ImageList)
Beschreibung
ListImages
Auflistung mit Elementtyp ListImage und Basis 1
MaskColor
Long-Wert, der die Transparenzfarbe für die Maskierung transparenter Bereiche ausdrückt. Wird von Overlay, ListImage.Draw und ListImage.DrawIcon verwendet, wenn UseMaskColor auf True gesetzt ist.
Object
Verweis auf das nackte (Steuerelement-)Objekt, das dem Visual-BasicObjekt zugrunde gelegt ist. Wird in speziellen Fällen für die Auflösung von Namensbereichkonflikten benötigt, wenn das Visual-Basic-Objekt eine Eigenschaft oder Methode des untergelegten Objekts durch eine gleichnamige Eigenschaft oder Methode »verdeckt«.
UseMaskColor
Boolean-Wert, der bestimmt, ob die in MaskColor festgelegte Farbe als Transparenzfarbe für Berechnung von Symbolen durch Overlay, ListImage.Draw und ListImage.DrawIcon verwendet wird
................................................... Methoden Methoden
Function Objekt.Overlay(Index1 | Key1, Index2 | Key2) As Picture Sub Objekt.ListImages.Add (Index As Integer, Key As String, _ Picture As Picture) Sub Objekt.ListImages.Remove(Index | Key) Sub Objekt.ListImages.Clear() Sub Objekt.ListImage(Index | Key).ExtractIcon() Methode
Beschreibung
Overlay
Liefert eine Komposition aus zwei Bildern der Bildliste als Symbol. Zur Auswahl der Bilder kann wahlweise die Index- oder Key-Eigenschaft der Bilder übergeben werden, wobei das im zweiten Parameter genannte Bild über das andere gelegt wird. Die Methode beachtet die Eigenschaften ImageHeight, ImageWidth sowie insbesondere, falls UseMaskColor auf True gesetzt ist, MaskColor und BackColor.
Remove
Entfernt das über die Index- oder Key-Eigenschaft spezifizierte Bild aus der Abbildungsliste
Add
Fügt das Bild Picture in die Abbildungsliste ein und ordnet ihm den Namen Key zu. Ist für Index kein Wert spezifiziert, wird das Bild als letztes Bild in die Liste eingefügt, ansonsten an der durch Index spezifizierten Position (Zählung beginnt bei 1).
Clear
Entfernt alle Bilder aus der Abbildungsliste
ExtractIcon
Liefert das Listenelement als Symbol unter Beachtung der Eigenschaften ImageHeight, ImageWidth sowie UseMaskColor. Als Transparenzfarbe fungiert die Farbe MaskColor. Falls UseMaskColor auf False gesetzt ist, generiert die Methode das Symbol im markierten Zustand (Hintergrundfarbe invertiert). Die maximale Symbolgröße, mit der die Methode zurechtkommt, ist 144 Bildpunkte.
43 7
ActiveX- Steuerelemente (OCX) – Windows- Standardsteuerelemente
Eigenschaft
ActiveX- Steuerelemente ( OCX) Windows- Standardsteuerelemente
Anwendung
ActiveX- Steuerelemente (OCX) – Windows- Standardsteuerelemente
................................................... Anwendung
Es gibt fünf Windows-Standardsteuerelemente, die speziell auf die Zusammenarbeit mit einem ImageList-Objekt ausgelegt sind und von diesem ihre Bilder beziehen: CoolBar, ImageCombo, TabStrip, TreeView und Toolbar. Sie können das Steuerelement aber auch für eigene Zwecke einsetzen, wenn sie mehrere Bilder verwalten müssen. Am einfachsten ist der Umgang mit einer Abbildungsliste, wenn Sie die Eigenschaften bereits zur Entwurfszeit interaktiv gestalten. Öffnen Sie dazu den Eigenschaftsdialog des im Entwurfsbereich platzierten Steuerelements über das Kontextmenü und aktivieren Sie die Registerkarte ABBILDUNGEN. Bilder, die Sie zur Entwurfszeit einfügen, speichert Visual Basic in der unkompilierten Fassung automatisch in einer binären Anhangsdatei (FRX, CTX etc.) und in der kompilierten Fassung als Ressource in der Exe-Datei. Wenn Sie die Eigenschaften ImageHeight und ImageWidth nicht explizit vorher (auf der Registerkarte ALLGEMEIN) setzen, richten sie sich nach den Abmessungen (in Bildpunkten) des zuerst eingefügten Bildes – und lassen sich später auch nicht mehr ändern. Um ein Bild zur Laufzeit mit einem Namen ansprechen zu können (was im Zusammenspiel mit anderen Steuerelementen recht vorteilhaft ist), setzen Sie die Key-Eigenschaft des jeweiligen Listenelements.
Eigensc haftsdialog eines Abbildungsliste- Steuerelements
Das Abbildungsliste-Steuerelement akzeptiert diverse Grafikformate (DIB, BMP, ICO, CUR, GIF, JPG) und hat vom Prinzip her auch keine Probleme damit, wenn die Bilder unterschiedliche Größen aufweisen. Im Zusammenspiel mit den genannten Windows-Standardsteuerelementen sorgt nämlich die Methode ExtractIcon für die Umwandlung in das Symbolformat, passt die Größe automatisch auf ImageHeight und ImageWidth an und nimmt gegebenenfalls eine Maskierung vor, wenn UseMaskColor den Wert True hat und MaskColor auf eine geeignete Transparenzfarbe gesetzt wurde. Beachten Sie, dass die Symbolgröße auf 144×144 Bildpunkte limitiert ist. Das Steuerelement lässt sich natürlich auch erst zur Laufzeit initialisieren. Sie können die Bilder für die Listeneinträge dann einzelnen aus bereitgestellten Grafikdateien laden, sollten es aber nicht versäumen, die Eigenschaften ImageHeight und ImageWidth vorher passend zu setzen. With ImageList1 .ImageHeight = 50 .ImageWidth = 150 .ListImages.Add , "Bild01", LoadPicture(App.Path & "Bild01.jpg") .ListImages.Add , "Bild02", LoadPicture(App.Path & "Bild02.jpg") ' ... usw.
43 8
Weitere ActiveX- Steuerelemente
.ListImages.Add , "Bild21", LoadPicture(App.Path & "Bild21.jpg") .MaskColor = vbWhite .UseMaskColor = True .BackColor = vbRed End With
PictureClip1.Rows = 3 PictureClip1.Cols = 7 PictureClip1.Picture = LoadPicture(App.Path & "Bilder.jpg") ImageList1.ImageHeight = 50 ImageList1.ImageWidth = 150 Dim Key As String, i As Integer For i = 0 To 20 Key = "Bild" & Right("0" & CStr(i + 1), 2) ImageList1.ListImages.Add , Key, PictureClip1.GraphicCell(i) Next i
Für den Zugriff auf ein einzelnes Bild gibt es mehrere Möglichkeiten. Um das Bild in der ursprünglichen Form wiederzugewinnen, schreiben Sie: Picture1 = ImageList1.ListImages(Index).Picture
Um das Bild als Symbol mit den Abmessungen ImageHeight und ImageWidth zu erhalten, schreiben Sie: Picture1 = ImageList1.ListImages(Index).ExtractIcon
Um das Bild als Symbol mit den Abmessungen ImageHeight und ImageWidth zu erhalten, das mit einem zweiten Bild überlagert ist, schreiben Sie: Picture1 = ImageList1.Overlay (Index, IndexZweitesBild)
Um das Bild in Symbolgröße in ein Formular Form1 an die Position 1000, 1000 zu zeichnen, schreiben Sie: Picture1 = ImageList1.ListImages(Index).Draw Form1.hDC, 1000, 1000 Verwandte Steuerelemente
................................................... Verwandte S teuerelemente PictureClip
Weitere ActiveX- Steuerelemente Neben den Windows-Standardsteuerelementen gehört zum Lieferumfang der Professional Edition von Visual Basic noch eine beachtliche Sammlung weiterer Steuerelemente, von denen jedes für sich eine eigene mehr oder weniger komplexe Programmierwelt mit sich bringt. So steckt beispielsweise hinter dem ADO-Steuerelement die gesamte Welt der ActiveX Data Objects, der neuen Datenbankschnittstelle von Microsoft, die dem Modell des verallgemeinerten unternehmensweiten Datenzugriffs (Universal Data Access) entsprungen ist und eine Erweiterung des
43 9
Weitere ActiveX- Steuerelemente
Eine bewährte Technik ist es, das Bildausschnitt-Steuerelement (PictureClip) als Bildquelle für das Abbildungsliste-Steuerelement einzusetzen. Gerade, wenn eine größere Anzahl von Symbolen oder Bitmaps gleicher Größe benötigt wird, lassen sich die Bildressourcen dann sehr vorteilhaft in einer einzigen externen Datei zusammenfassen, die auch im Nachhinein noch geändert werden kann:
Weitere ActiveX- Steuerelemente
Weitere ActiveX- Steuerelemente
ODBC-Standards für relationale und nichtrelationale Datenbanksysteme. Das Steuerelement unterstützt neben OLE DB auch den Datenzugriff über die älteren Datenbankschnittstellen ODBC und DAO. Hinzu kommt natürlich noch die schier unüberschaubare Fülle an ActiveX-Steuerelementen, die von Drittherstellern vertrieben werden und deren Programmierschnittstellen von beachtlicher Komplexität sein können. Früher als OLE-Steuerelemente bezeichnet, laufen diese Steuerelemente heute unter der Bezeichnung ActiveX-Steuerelemente und genügen dem COM-Standard, so dass sie sich nicht nur als Benutzeroberflächenobjekte für das gewöhnliche Design von Visual-Basic-Formularen und -Benutzersteuerelementen mit den üblichen Möglichkeiten der Datenbankanbindung eignen, sondern darüber hinaus für die Belebung und Ausgestaltung von HTML-Seiten (die im Internet-Explorer angezeigt werden) zur Verfügung stehen. Um ein ActiveX-Steuerelement in die Werkzeugsammlung zu laden, rufen Sie das Dialogfeld KOMPONENTEN über den Menübefehl PROJEKT/KOMPONENTEN AUF und setzen auf der Registerkarte STEUERELEMENTE vor den Namen der Komponentendatei ein Häkchen. Die folgende Tabelle gibt einen Überblick über die wichtigsten ActiveX-Steuerelemente: Steuerelement
Komponentendatei
Bestimmung des Steuerelements
Adodc
Microsoft ADO Data Control 6.0 (SP3) (OLE DB) MSADODC.OCX
Datensteuerelement für Datenbankanbindungen an OLE-Datenbankdateien auf Basis der ActiveX Data Objects (ADO-Schnittstelle). Die verschiedenen OLE-DB-Provider machen das Steuerelement zum vollständigen Ersatz für die älteren Datensteuerelemente Data und RemoteData.
CommonDialog
Microsoft Common Dialog Control 6.0 (SP3) COMDLG.OCX
Steuerelement ohne sichtbare Darstellung, das die Windows-Standarddialoge Öffnen, Speichern unter, Drucken, Schriftart, Farbe als eigenständige Dialogfelder anzeigen kann.
DataCombo
Microsoft DataList Controls 6.0 (SP3) (OLE DB) MSDATLST.OCX
Für ADO optimierte, codekompatible Variante von DBCombo
DataGrid
Microsoft DataGrid Control 6.0 (SP3) (OLE DB) MSDATGRD.OCX
Für ADO optimierte, codekompatible Variante von DBGrid
DataList
Microsoft DataList Controls 6.0 (SP3) (OLE DB) MSDATLST.OCX
Für ADO optimierte, codekompatible Variante von DBList
DataRepeater
Microsoft DataRepeater Control 6.0 (OLE DB) MSDATREP.OCX
Für ADO konzipiertes Steuerelement, das die ressourcensparende Wiederholung eines Steuerelements (meist Benutzersteuerelement) mit Datenbankbindung zur Anzeige fortlaufender Datensätze eines Recordset-Objekts einer Datenquelle (Adodc, Data, RemoteData) ermöglicht
440
Weitere ActiveX- Steuerelemente
Komponentendatei
Bestimmung des Steuerelements
DBCombo
Microsoft Data Bound List Controls 6.0 DBLIST32.OCX
Speziell auf die Arbeit mit Datenbankinformationen optimierte Variante des Kombinationsfelds (vgl. ComboBox), dessen Listeneinträge automatisch von einem Recordset-Objekt einer Datenquelle (Data, RemoteData) mit den Werten eines einzelnen Datenfelds aufgefüllt und gepflegt werden
DBGrid
Microsoft Data Bound Speziell auf die Arbeit mit Datenbankinformationen Grid Control 5.0 (SP3) ausgelegtes Datenblatt-Steuerelement, das die Datensätze eines Recordset-Objekts einer DatenDBGRID32.OCX quelle (Adodc, Data, RemoteData) als Tabellenblatt darstellt und die interaktive Bearbeitung ermöglicht
DBList
Microsoft Data Bound List Controls 6.0 DBLIST32.OCX
Speziell auf die Arbeit mit Datenbankinformationen optimierte Variante des Listenfelds (vgl. ListBox), dessen Listeneinträge automatisch von einem Recordset-Objekt einer Datenquelle (Adodc, Data, RemoteData) mit den Werten eines einzelnen Datenfelds aufgefüllt und gepflegt werden
Inet
Microsoft Internet Transfer Control 6.0 MSINET.OCX
Steuerelement ohne sichtbare Darstellung, das eine Implementation der beiden wichtigsten Internetprotokolle HTTP und FTP für das Abrufen von HTML-Dokumenten aus dem Internet sowie die Ausführung von FTP-Befehlen bereitstellt
MaskEdBox
Microsoft Masked Edit Textfeld (vgl. TextBox), das auf die Eingabe von Werten mit spezifischen Formaten spezialisiert ist. Control 6.0 (SP3) Die Formatbeschreibung erfolgt als Zeichenfolge MSMASK32.OCX mit Formatsymbolen
MMControl
Microsoft Multimedia Control 6.0 (SP3) MCI32.OCX
Ermöglicht die Steuerung von Aufnahme- und Wiedergabegeräten über die MCI-Schnittstelle des Systems
MSChart
Microsoft Chart Control 6.0 (SP3) (OLEDB) MSCHRT20.OCX
Ermöglicht die grafische Darstellung von Diagrammen auf Basis eines DataGrid-Steuerelements
MSComm
Microsoft Comm Con- Ermöglicht die direkte Kommunikation über die seriellen Schnittstellen des Systems trol 6.0 MSCOMM32.OCX
MSFlexGrid
Microsoft FlexGrid Control 6.0 (SP3) MSFLXGRD.OCX
Tabellenblatt für die tabellarische Anzeige von Daten (Text und Bilder) in verschiedenen Sortierungen und Formaten
441
Weitere ActiveX- Steuerelemente
Steuerelement
Weitere ActiveX- Steuerelemente
Weitere ActiveX- Steuerelemente
Steuerelement
Komponentendatei
Bestimmung des Steuerelements
MSHFlexGrid
Microsoft Hierachical FlexGrid Control 6.0 (SP3) MSHFLXGD.OCX
Tabellenblatt für die tabellarische Anzeige hierarchisch gruppierter Daten (Text und Bilder) in verschiedenen Sortierungen und Formaten. Das Steuerelement ist speziell für die Zusammenarbeit mit dem ADO-Steuerelement konzipiert und ermöglicht die Anzeige relational abhängiger Datensatzgruppen.
PictureClip
Microsoft PictureClip Control
Bildausschnitt-Steuerelement, das die Extraktion von Teilbildern einer gegebenen Bitmap unter Angabe eines Reihen-/Spaltenindex oder eines Begrenzungsrechtecks unterstützt
RichTextBox
Microsoft Internet Control SHDOCVW.DLL
Erweiterte Variante des Textfelds (TextBox) für die Darstellung und Bearbeitung von formatierten Texten im RTF-Format.
WebBrowser
Microsoft Internet Control SHDOCVW.DLL
Browser-Fenster mit einem großen Teil der Funktionalität des Internet Explorer, das die Navigation im Internet, Intranet sowie auf dem lokalen System ermöglicht
WinSock
Microsoft Winsock Control 6.0 MSWINSCK.OCX
Steuerelement ohne sichtbare Darstellung, das den Port-basierten Verbindungsaufbau mit im TCP/IPNetzwerk gelegenen Servern und den Datenaustausch über Datagramme (UDP) sowie verbindungsorientiert (TCP) ermöglicht.
Die vorliegende Ausgabe dieser Referenz stellt in der Folge nur die zwei sehr grundlegenden ActiveX-Steuerelemente PictureClip und CommonDialog ausführlicher vor.
Bildausschnitt- Steuerelement (PictureClip) ImageList1 As ImageList Beschreibung
................................................... Bes c hreibung
Das zur Laufzeit unsichtbare Bildausschnitt-Steuerelement bietet zwei verschiedene Techniken an, Teilbilder als eigenständige Bitmaps aus einer größeren Bitmap zu gewinnen. Zum einen lässt sich die Bitmap über die Eigenschaften Rows und Cols gleich einem Gitter in gleich große Grafikzellen unterteilen, die das Steuerelement dann über die Array-Eigenschaft GraphicCell verfügbar macht. Zum anderen liefert die Clip-Eigenschaft frei definierbare Teilbilder der Bitmap. Schließlich ermöglicht das Steuerelement auch noch die Skalierung der Grafikzellen bzw. Teilbilder auf frei definierbare Abmessungen. Eigenschaften
................................................... Eigens c ha ften CellHeight, CellWidth, Clip, ClipHeight, ClipWidth, ClipX, ClipY, Cols, GraphicCell, Height, hWnd, Index, Name, Object, Parent, Picture, Rows, StretchX, StretchY, Tag, Width,
442
Bildausschnitt- Steuerelement ( PictureClip)
Beschreibung
CellHeight CellWidth
Integer-Werte, die Höhe und die Breite des Bildausschnitts (Clip-Eigenschaft) in Bildpunkten ausdrücken. Diese Werte werden bei Änderung der Eigenschaften Cols bzw. Rows automatisch aktualisiert und sind zur Laufzeit schreibgeschützt.
Clip
Picture-Wert, der den durch ClipX, ClipY, ClipHeight, ClipWidth beschriebenen Bildausschnitt liefert
ClipHeight ClipWidth
Höhe und Breite des von Clip gelieferten Bildausschnitts
ClipX ClipY
Koordinaten der linken oberen Ecke des von Clip gelieferten Bildausschnitts in Bildpunkten
Cols
Integer-Wert für die Anzahl der Spalten, in die das Steuerelement das Gesamtbild (Picture) für die Bildung von Zellen aufteilt
GraphicCell
eindimensionales Array (0-basiert) mit Elementtyp Picture, das die implizit über Rows und Cols definierten Grafikzellen liefert
Picture
Gesamtbild, aus dem das Steuerelement seine Ausschnitte liefert
Rows
Integer-Wert für die Anzahl der Zeilen, in die das Steuerelement das Gesamtbild (Picture) für die Bildung von Zellen aufteilt
StretchX StretchY
Breite und Höhe in Bildpunkten, auf die das Steuerelement alle GraphicCell-Elemente skaliert. Die Voreinstellungen für diese Werte sind die Werte von CellHeight und CellWidth.
Anwendung
................................................... Anwendung
Wer in seinen Programmen viel mit Bildern zu tun hat, wird ein Lied davon singen können, wie aufwändig und lästig die Bereitstellung von Bildressourcen sein kann, gleich ob dies zur Entwurfszeit oder zur Laufzeit geschieht. Wenn zig Einzeldateien nicht nur einzeln geladen, sondern auch gepflegt sein wollen, ist es meist günstiger, mehrere Bilder in einer einzelnen Bilddatei zusammenzufassen und erst zur Laufzeit wieder zu extrahieren – zumal wenn die Bilder wie im Falle von Symbolleisten, Puzzle-Elementen oder Spielfiguren, um nur einige Anwendungsbeispiele zu nennen, noch die gleichen Abmessungen besitzen. Wird die Picture-Eigenschaft des Steuerelements bereits zur Entwurfszeit über das Eigenschaftsfenster gesetzt, speichert Visual Basic die entsprechende Bitmap zunächst als binären Anhang in einer separaten Datei (FRX-Datei für Formulare). In der kompilierten Fassung enthält die EXE-Datei alle binären Anhänge als Ressourcen, so dass die Quelldatei nicht weiter bereitgestellt werden muss. Anders jedoch, wenn die Bitmap erst zur Laufzeit mittels LoadPicture eingelesen wird. In diesem Fall muss die entsprechende Grafikdatei weiterhin verfügbar sein. PictureClip1.Picture = LoadPicture(App.Path + "\figuren.bmp")
Die Aufteilung in einzelne Zellen können Sie zur Entwurfszeit oder zur Laufzeit vornehmen, indem Sie die Eigenschaften Rows und Cols geeignet setzen. Wenn die Bitmap nebeneinander 13 Einzelbilder für die Abbildungsliste einer Symbolleiste enthält, schreiben Sie: PictureClip1.Rows = 1 PictureClip1.Cols = 13
443
Weitere ActiveX- Steuerelemente
Eigenschaft
Weitere ActiveX- Steuerelemente
Falls die zu extrahierenden Teilbilder noch einer Skalierung bedürfen, können Sie StretchX und StretchY auf die gewünschte Zielgröße in Bildpunkten setzen, bevor der Zugriff über die GraphicCell-Eigenschaft erfolgt (bei Skalierung ist es generell besser, wenn verkleinert wird).
Weitere ActiveX- Steuerelemente
PictureClip1.StretchX = 16 PictureClip1.StretchY = 16 For i = 0 To 12 ImageList1.ListImages.Add , , PictureClip1.GraphicCell(i) Toolbar1.Buttons(i).Image = i ' gleich zu ordnen Next i
Um einen beliebigen Bildausschnitt zu erhalten, arbeiten Sie mit der Clip-Eigenschaft, nachdem Sie die Koordinaten des linken oberen Punkts sowie die Abmessungen des gewünschten Teilbildes gesetzt haben. With PictureClip1 .ClipHeight = ch: .ClipWidth = cw: .ClipLeft = cl: .ClipTop = ct Object.Picture = PictureClip1.Clip End With Beispiel
................................................... Beis piel
Der folgende Codeauszug aus dem Beispielprojekt Schach des Praxisteils zeigt die Initialisierung von Steuerelementen mit den Bildern der Schachfiguren. With PictureClip1 .Picture = LoadPicture(App.Path + "\Schachfiguren.bmp") .Rows = 2 .Cols = 6 End With For i = 0 To 31 ' Element mit Index 0 ist "Same" If i > 0 Then Load figur(i) Set figur(i).MaskPicture = PictureClip1.GraphicCell(FigIdx(i)) Set figur(i).Picture = PictureClip1.GraphicCell(FigIdx(i)) Next i Verwandte Steuerelemente
................................................... Verwandte Steuerelemente ImageList
Standarddialoge- Steuerelement (CommonDialog) CommonDialog1 As CommonDialog Beschreibung
................................................... Bes c hreibung
Das Standarddialoge-Steuerelement enthält die Implementation der wichtigsten Dialoge für die Windows-Programmierung: Öffnen, Speichern unter, Schriftart, Farbauswahl Drucken und Drucker einrichten. Entsprechend verfügt das Steuerelement für den Aufruf eines jeden Dialogs eine eigene Show-Methode (einzig Drucken und Drucker einrichten werden über die gleiche Methode aufgerufen) sowie eine Fülle von Eigenschaften, die zum einen die Konfiguration der Dialoge vor dem Aufruf und zum anderen die Entgegennahme der Benutzereingaben nach dem Aufruf ermöglichen. Die Voreinstellungen sind so gewählt, dass die Vorbereitungen für den Aufruf der Dialoge minimal sind. Darüber hinaus gestattet es die Flags-Eigenschaft, jeden ein-
444
Standarddialoge- Steuerelement ( CommonDialog)
zelnen Dialog vor dem Aufruf speziell zu konfigurieren sowie die spezifische Einstellungen des Benutzers in Erfahrung zu bringen, um den vollen Funktionsumfang des Steuerelements auszunutzen. Eigenschaften
................................................... Eigens c ha ften
Eigenschaft
Beschreibung
Action
Aus Gründen der Abwärtskompatibilität nach wie vor unterstützte Eigenschaft, deren Setzen die Anzeige eines Standarddialogs bewirkt; die möglichen Werte sind: 0 – keine Aktion; 1 – Öffnen; 2 – Speichern unter; 3 – Farbe; 4 – Schriftart; 5 – Drucker; 6 – Aufruf des Hilfesystems
CancelError
Boolean-Wert, der bestimmt, ob das Steuerelement einen Fehler signalisiert, wenn der Benutzer die Schaltfläche ABBRECHEN anklickt (True) oder nicht (False; Voreinstellung)
Color
Long-Wert, der die aktuelle Farbauswahl des Standarddialogs Farbe vorgibt bzw. liefert
Copies
Integer-Wert, der die Kopienanzahl des Standarddialogs Drucker vorgibt bzw. liefert
DefaultExt
String-Wert für die standardmäßige Dateierweiterung des von den Standarddialogen Öffnen, Schriftart und Speichern unter gelieferten Dateinamens; das Steuerelement ergänzt die Erweiterung, falls sie fehlt
DialogTitle
String-Wert für den Titel des aufgerufenen Standarddialogs
FileName
String-Wert, der den ausgewählten Dateinamen samt Pfad für die Dialoge Öffnen, Schriftart und Speichern unter vorgibt bzw. liefert
FileTitle
Schreibgeschützter String-Wert, der den über die Dialoge Öffnen, Schriftart und Speichern unter ausgewählten Dateinamen ohne Pfad liefert
Filter
String-Wert, der einen Dateiauswahlfilter für die Dialogfelder Öffnen, Schriftart und Speichern unter vorgibt. Ein Filter setzt sich aus einem Filtereintrag für die Liste DATEITYP und einer Suchspezifikation (Platzhalter * und ? erlaubt) getrennt durch das Zeichen »|« zusammen. Um mehrere Filter für einen Eintrag zur Auswahl zu stellen, sind die Suchspezifikationen durch ein Semikolon zu trennen. Beispiel: "Bilder (JPG, GIF, BMP)|*.jpg;*.gif;*.bmp". Mehrere Filter werden gleichfalls durch das Zeichen »|« getrennt. Beispiel: "JPG|*.jpg|GIF|*.gif|BMP|*.bmp".
445
Weitere ActiveX- Steuerelemente
Action, CancelError, Color, Copies, DefaultExt, DialogTitle, FileName, FileTitle, Filter, FilterIndex, Flags, Font, FontBold, FontItalic, FontName, FontSize, FontStrikeThru, FontUnderline, FromPage, hDC, HelpCommand, HelpContext, HelpFile, HelpKey, Index, InitDir, Left, Max, Min, MaxFileSize, Name, Object, Orientation, Parent, PrinterDefault, Top, ToPage
Weitere ActiveX- Steuerelemente
Weitere ActiveX- Steuerelemente
Eigenschaft
Beschreibung
FilterIndex
Integer-Wert, der einen in der Eigenschaft Filter definierten Filter über seine Position auswählt bzw. den Index des vom Benutzer verwendeten Filters liefert; Voreinstellung ist 1; ungültige Indizes interpretiert das Steuerelement gleichfalls als 1
Flags
Long-Wert, der als Bitvektor Optionen für die einzelnen Dialoge festlegt bzw. liefert. Die einzelnen Flags ermöglichen eine zusätzliche Konfiguration der Dialoge, insbesondere der Zustände der Kontrollkästchen und Optionsfelder. (Die Bedeutung der einzelnen Flags entnehmen Sie der Online-Hilfe zu Visual Basic.)
FontBold, FontItalic, FontName, FontSize, FontStrikeThru, FontUnderline
Schrifteigenschaften für den Dialog Schriftart
FromPage ToPage
Integer-Werte, die den Seitenbereich für den Dialog Drucken vorgeben bzw. liefern (Achtung: Die Vorgaben für den Dialog müssen mit den Eigenschaften Min und Max vereinbar sein, sonst meldet der Dialog einen Initialisierungsfehler.)
HelpCommand
Integer-Wert, der als Bitvektor Kommandos für einen eventuellen Aufruf des Hilfesystems vorgibt bzw. liefert (die Bedeutung der einzelnen Flags entnehmen Sie der Online-Hilfe zu Visual Basic)
HelpContext
Long-Wert, der eine Kontext-Kennung für den Aufruf des Hilfesystems ausgibt (Voraussetzung: HelpCommand wird auf cdlHelpContext gesetzt)
HelpFile
String-Wert mit dem Zugriffspfad für die Hilfedatei. Wenn leer, wird die Visual-Basic-Hilfedatei benutzt.
HelpKey
String-Wert, der ein Stichwort für den Aufruf des Hilfesystems spezifiziert (Voraussetzung: HelpCommand wird auf cdlHelpKey gesetzt)
InitDir
String-Wert, der das Anfangsverzeichnis für die Auswahl eines Dateinamens bestimmt. Wenn leer, wird das standardmäßige Arbeitsverzeichnis (CurDir) verwendet. Um eine Änderung des Standardverzeichnisses durch den Dialog zu verhindern, setzen Sie das Flag cdlOFNNoChangeDir (&H8).
Max Min
Integer-Werte, die die maximale/minimale Seitenzahl für den Dialog Drucken bzw. die maximale Zeichengröße für den Dialog Schriftart spezifizieren. Wenn 0, kann keine Seitenauswahl im Dialog Drucken stattfinden
MaxFileSize
Integer-Wert, der die maximale Länge des Dateinamens für die Eigenschaft FileName festlegt. Vorgabe ist 256. Der Wert lässt sich zwischen 1 und 32.768 wählen.
Orientation
Integer-Wert, der die Papierausrichtung im Dialog DRUCKER EINRICHTEN vorgibt bzw. liefert: cdlPortrait (1) steht für Hochformat, cdlLandscape (2) für Querformat
446
Standarddialoge- Steuerelement ( CommonDialog)
Eigenschaft
Beschreibung
PrinterDefault
Boolean-Wert, der bestimmt, ob der Dialog Drucken den ausgewählten Drucker zum Standarddrucker des Systems erklärt (True) oder nicht (False)
Methoden
................................................... Metho den AboutBox, ShowColor, ShowFont, ShopHelp, ShowOpen, ShowPrinter, ShowSave Beschreibung
AboutBox
Zeigt den Infodialog des Standarddialogs an
ShowColor
Zeigt den Standarddialog Farbe an. Nach dem Aufruf enthält die Eigenschaft Color die Farbauswahl des Benutzers.
ShowFont
Zeigt den Standarddialog Schriftart an. Der erfolgreiche Aufruf setzt voraus, dass eines der Flags cdlCFScreenFonts (&H1), cdlCFBoth (&H3) oder cdlCFPrinterFonts (&H29) gesetzt wurde. Nach dem Aufruf enthält die Eigenschaft Font die Schriftauswahl des Benutzers.
ShowHelp
Ruft das Windows-Hilfesystem mit der über die Eigenschaft HelpFile gesetzten Hilfedatei auf
ShowOpen
Zeigt den Standarddialog Öffnen an. Nach dem Aufruf enthalten die Eigenschaften FileName sowie FileTitle den vom Benutzer gewählten Dateinamen. Der Wert des Kontrollkästchens SCHREIBSCHUTZ lässt sich über das Flag cdlOFNNoReadOnlyReturn ermitteln
ShowPrinter
Zeigt den Standarddialog Drucken an. Nach dem Aufruf enthält hDC den Gerätekontext des gewählten Druckers, FromPage und ToPage enthalten den zu druckenden Seitenbereich, Orientation enthält die Papierausrichtung, und Copies enthält die Anzahl der zu druckenden Kopien. Die Werte der Optionsfelder sowie Kontrollkästchen lassen sich über die Flags-Eigenschaft setzen und in Erfahrung bringen. Wird die Flags-Eigenschaft vor dem Aufruf auf cdlPDPrintSetup (&H40) gesetzt, erscheint nicht der Dialog Drucken, sondern Drucker einrichten.
ShowSave
Zeigt den Standarddialog Speichern unter an. Nach dem Aufruf enthalten die Eigenschaften FileName sowie FileTitle den vom Benutzer gewählten Dateinamen.
Anwendung
................................................... Anwendung
Die Standarddialoge dürften zweifelsohne zu den segensreichsten Werkzeugen gehören, die Visual Basic einem Programmierer an die Hand gibt. Man erkennt das allein schon daran, dass es kaum Programme gibt, die nicht in der einen oder anderen Form davon Gebrauch machen. Wer von dem Steuerelement nichts Ungewöhnliches erwartet, es also nicht bis auf das Letzte ausreizen will, wird überrascht sein, wie pflegeleicht die Programmierung damit ist. Der Perfektionist hingegen, der die völlige Kontrolle anstrebt, wird nicht umhinkommen, sich mit der Flags-Eigenschaft, der Fülle der für jeden einzelnen Dialog definierten Bitwerte und der mit Bitvektoren üblicherweise verbundenen Bitkrämerei herumschlagen zu müssen.
447
Weitere ActiveX- Steuerelemente
Methode
Weitere ActiveX- Steuerelemente
Weitere ActiveX- Steuerelemente
Öffnen Um die Standarddialoge Öffnen bzw. Speichern unter anzuzeigen, rufen Sie die Methode ShowOpen bzw. ShowSave auf oder setzen die Eigenschaft Action auf 1 bzw. 2. Beide Dialoge geben das von CurDir gelieferte aktuelle Arbeitsverzeichnis vor, falls über die Eigenschaft InitDir kein anderes Verzeichnis spezifiziert ist. Den ausgewählten Dateinamen samt Pfad finden Sie nach dem Aufruf in der Eigenschaft FileName, während FileTitle nur den nackten Dateinamen wiedergibt. Wenn Sie nicht wollen, dass die Dialoge das aktuelle Arbeitsverzeichnis ändern, setzen Sie in der Flags-Eigenschaft das Flag cdlOFNNoChangeDir (&H8). Das Kontrollkästchen für die Schreibschutzeinstellung wird man über das Flag cdlOFNHideReadOnly (&H4) los, was für den Dialog Speichern unter geschehen sollte, um Verwirrungen des Benutzers vorzubeugen. Um sicherzustellen, dass der Benutzer eine bestehende Datei auswählt, setzen Sie das Flag cdlOFNFileMustExist (&H1000), was gleichzeitig die Erstellung eines neuen Verzeichnisses (cdlOFNPathMustExist = &H800) über den Dateinamen unterbindet (nichtsdestotrotz kann der Benutzer weiterhin über das Symbol NEUER ORDNER Verzeichnisse erstellen). CommonDialog1.Flags = cdlOFNNoChangeDir + _ cdlOFNHideReadOnly + cdlOFNFileMustExist CommonDialog1.ShowOpen Dateiname = CommonDialog1.FileName
Soll der Benutzer mehrere Dateien auswählen können, setzen Sie das Flag cdlOFNAllowMultiselect (&H200). Hierbei ergibt sich aber aus historischen Gründen ein kleines Problem: Da das Steuerelement das Ergebnis einer Mehrfachauswahl gleichfalls in der Eigenschaft FileName zurückliefert und dabei zur Aufrechterhaltung der Kompatibilität den Pfad und die ausgewählten Dateinamen jeweils durch ein Leerzeichen getrennt hintereinander setzt, erhalten Sie eine veraltete Version des Dialogfelds, die nur kurze Dateinamen anzeigt und liefert (ansonsten würden sich Fehler einschleichen, wenn ein Dateiname Leerzeichen enthält). Auch müssen Sie explizit einen Filter vorgeben, damit der Benutzer überhaupt eine Auswahl sieht. In der gewohnten Gestalt des Dialogfelds (also mit langen Dateinamen) lässt sich die Mehrfachauswahl vornehmen, wenn zusätzlich das Flag cdlOFNExplorer (&H80000) gesetzt wird. Im Textfeld DATEINAME des Dialogs erscheinen die Dateinamen bei Mehrfachauswahl dann in Anführungszeichen, und im Wert der Eigenschaft FileName sind Pfad und Dateinamen jeweils durch Nullzeichen voneinander getrennt. Für die Analyse splitten Sie den Wert am besten mit der Funktion Split in ein Array auf. Das Array-Element mit dem Index 0 enthält dann den Pfadnamen und die restlichen Array-Elemente die pfadlosen langen Dateinamen der Mehrfachauswahl: Dim Dateinamen() As String CommonDialog1.Flags = cdlOFNAllowMultiselect + cdlOFNExplorer CommonDialog1.ShowOpen Dateinamen = Split(CommonDialog1.FileName, Chr(0))
Leider ist der Dialog für die Mehrfachauswahl nicht sauber genug implementiert: der Puffer für die Eigenschaft FileName kann auch bei Mehrfachauswahl nicht viel mehr als 200 Zeichen aufnehmen, was bei größeren Auswahlen schnell zu Pufferfehlern führt, die das Dialogfeld dummerweise nicht immer meldet! Bleibt zu hoffen, dass sich dies im nächsten Updates ändert.
Drucken Der Aufruf des Standarddialogs Drucken erfolgt über die Methode ShowPrinter oder durch Setzen der Action-Eigenschaft auf 5. In dem Dialogfeld kann der Benutzer wählen, ob er alle Seiten des fraglichen Dokuments, die aktuelle Auswahl oder einen bestimmten Seitenbereich drucken will. Voreinstellung der Flags-Eigenschaft ist cdlPDAllPages (alle Seiten). Da diese Konstante
448
Standarddialoge- Steuerelement ( CommonDialog)
Schriftart Für den Aufruf des Standarddialogs Schriftart sorgt die Methode ShowFont bzw. das Setzen der Action-Eigenschaft auf 4. Zur Vorbereitung des Aufrufs ist es erforderlich, dem Dialog über die Flags-Eigenschaft kundzutun, welche Schriftarten im Angebot stehen sollen. Für die primäre Auswahl ist eines der folgenden Flags obligatorisch: cdlCFScreenFonts (1) für die Auswahl der Bildschriftarten; cdlCFPrinterFonts (2) für die Auswahl der Druckerschriftarten, die mit dem Gerätekontext des aktuellen Standarddruckers kompatibel sind; cdlCFBoth (3) für alle auf dem System installierten Schriften. Für die weitere Verfeinerung der Auswahl setzen Sie zusätzliche Flags wie: cdlCFFixedPitchOnly (&H4000) für nichtproportionale Schriften; cdlCFTTOnly (&H40000) für TrueType-Schriften; cdlCFScaleableOnly (&H20000) für frei skalierbare Schriften; cdlCFANSIOnly (&H400) für Schriften, die mit dem Windows-Zeichensatz konform sind; cdlCFNoVectorFonts (&H800) für Bitmap-Schriften; cdlCFWYSIWYG (&H8000) für Schriften, die für die Bildschirm- und die Druckerausgabe geeignet sind; cdlCFNoSimulation für originale, nicht von der GDI generierte Schriften. Beachten Sie, dass die Attribute sich teilweise gegenseitig ausschließen, so dass es zum Laufzeitfehler 24574 »Keine Schriftarten vorhanden« kommen kann, wenn die Auswahl leer ist. Zudem können Sie das Flag cdlCFForceFontExist (&H10000) setzen, damit das Dialogfeld die Existenz der vom Benutzer eingegebenen Schrift überprüft. Für eine Gültigkeitsprüfung der Schriftgröße setzen Sie die Eigenschaften Min und Max auf geeignete Werte sowie das Flag cdlCFLimitSize (&H2000). Zu guter Letzt können Sie über die Flags cdlCFNoFaceSel (&H80000), cdlCFNoSizeSel (&H200000) und cdlCFNoStyleSel
449
Weitere ActiveX- Steuerelemente
den Wert 0 hat, kann sie nicht für die Maskierung der Flags-Eigenschaft verwendet werden, sondern ergibt sich daraus, dass weder das Bit cdlPDPageNums (2) für den Seitenbereich noch cdlPDSelection (1) für die Auswahl gesetzt ist. Um dem Benutzer die Seitenauswahl zu ermöglichen, müssen allerdings die Eigenschaften Min und Max vor dem Aufruf auf einen Wert größer 0 gesetzt werden. Das Dialogfeld stellt dann sicher, dass der vom Benutzer angegebene Seitenbereich gültig ist, und liefert die Eingaben in den Eigenschaften FromPage und ToPage zurück. Den Wert des Drehfelds KOPIEN gibt die Copies-Eigenschaft wieder, während der Zustand des Kontrollkästchens SORTIEREN nur durch Maskierung der Flags-Eigenschaft mit cdlPDCollate (&H10) in Erfahrung zu bringen ist und der des Kontrollkästchens AUSDRUCK IN DATEI durch Maskierung mit cdlPDPrintToFile (&H20). Da das Dialogfeld auch die Auswahl des Druckers ermöglicht, stellt sich die Frage, wie damit zu verfahren ist. Die Antwort fällt für den Visual-Basic-Programmierer eher ernüchternd aus: Unerfreulicherweise gibt es keine Eigenschaft, die den Namen des gewählten Druckers preisgeben würde. Lediglich ein Gerätekontext bzw. Informationskontext lässt sich ihm über die Eigenschaft hDC entlocken, vorausgesetzt die Flags-Eigenschaft wurde vor dem Aufruf auf cdlPCReturnDC (&H100) bzw. cdlPCReturnIC (&H100) gesetzt. Beide Informationen führen in die Tiefen der GDI und erfordern gute Kenntnisse im Bereich der Win32-API-Programmierung. Dennoch, es bleibt ein Ausweg, ein unschöner, aber durchaus praktikabler: Setzt man die Eigenschaft DefaultPrinter des Steuerelements auf True, erklärt der Druckerdialog den ausgewählten Drucker automatisch zum Standarddrucker. Vorausgesetzt die TrackDefault-Eigenschaft des Printer-Objekts wurde nicht auf False gesetzt, macht das Printer-Objekt diese Änderung mit und gestattet fortan den Ausdruck auf dem gewählten Gerät. Um den ursprünglichen Standarddrucker wieder zurückzusetzen, ist allerdings ein weiterer Aufruf des Druckerdialogs nötig, sonst bleibt der gewählte Drucker dem System erhalten – unschön, wie gesagt. Anstelle von Drucken kann ShowPrinter auch den Dialog Drucker einrichten aufrufen, der es dem Benutzer ermöglicht, die Druckereinstellungen der auf dem System installierten Drucker interaktiv zu ändern. Dazu muss das Flag cdlPDPrintSetup (&H40) gesetzt werden. Die Wirkung ist gleichfalls systemweit, sofern die DefaultPrinter-Eigenschaft auf True gesetzt ist.
Weitere ActiveX- Steuerelemente
Weitere ActiveX- Steuerelemente
(&H100000) vorgeben, ob das Dialogfeld die aktuellen Werte der Eigenschaften FontName, FontSize, FontItalic und FontBold als Vorgabe markiert oder nicht. Die Eingaben des Benutzers finden sich nach dem Aufruf in den FontXX-Eigenschaften. With CommonDialog1 .FontName = FontName .FontSize = FontSize .Flags = cdlCFScreenFonts .ShowFont FontName = .FontName FontSize = .FontSize Print FontName End With
Da das Steuerelement unverständlicherweise nicht über eine Font-Eigenschaft verfügt, lassen sich verschiedene Schriftstile, die das Dialogfeld für die eine oder andere Schrift anbietet, in Visual Basic leider gar nicht auswerten.
Farbauswahl Der Standarddialog Farbe wird über die Methode ShowColor bzw. durch Setzen der ActionEigenschaft auf 3 angezeigt. Das Dialogfeld hat zwei Ansichten: eine Standardansicht sowie eine erweiterte Ansicht. Sind die Flags cdlCCFullOpen (&H2) und cdlCCPreventFullOpen (&H4) nicht gesetzt, erscheint das Dialogfeld zunächst in Standardansicht, gestattet dem Benutzer aber das Umschalten in die erweiterte Ansicht (was sich durch cdlCCPreventFullOpen verhindern lässt). In der Standardansicht kann der Benutzer seine Farbauswahl aus 48 vordefinierten Grundfarben sowie 16 benutzerdefinierten Farben treffen. In der erweiterten Ansicht hat er die Möglichkeit, selbst Farben aus dem vollen TrueColor-Spektrum zu definieren. Damit das Dialogfeld den Wert der Color-Eigenschaft als Vorgabe für die Farbauswahl kenntlich macht, setzen Sie das Flag cdlCCRGBInit (&H1). Nach dem Aufruf enthält die Color-Eigenschaft die Farbauswahl des Benutzers. With CommonDialog1 .Color = RGB(0, 128, 0) .Flags = cdlCCFullOpen + cdlCCRGBInit .ShowColor BackColor = .Color End With
450
Teil 2: Praxisteil
451
Ältere Basic- Programme nach Visual Basic portieren Wer ein älteres in Basic geschriebenes Programm unter Visual Basic zur Ausführung bringen will, wird nicht umhinkommen, einige Anpassungen vorzunehmen, um das Programm überhaupt erst einmal ans Laufen zu bringen. Und bis es dann wirklich in jeder Hinsicht das Gewünschte macht, ist zuweilen noch einiges an Schweiß erforderlich. Vom Kern her ist Visual Basic der Sprache BASIC treu geblieben – wie sollte es auch anders sein. Natürlich, Visual Basic ist von der Ausdruckskraft her um einiges reicher geworden als etwa die mit QuickBasic oder QBasic vorliegenden Implementationen der Sprache. Verloren gegangen ist aber nichts, sieht man davon ab, dass verschiedene Konzepte im Fahrwasser der Windows-Programmierung generell einen anderen Zugang benötigen, wie beispielsweise die Ein- und Ausgabe, der Umgang mit Farben, mit langen Dateinamen oder die Unicode-Darstellung von Zeichenfolgen. Insbesondere aber an den Kontrollstrukturen sowie den Datentypen und ihren Operationen hat sich außer allfälligen Komplettierungen und der Bequemlichkeit dienenden Erweiterungen nichts geändert. Mit anderen Worten, von der algorithmischen Seite her ist für die Portierung sicher am allerwenigsten zu befürchten. Auch die traditionellen Funktionen der Sprache Basic für die Manipulation von Zeichenketten sowie für mathematische Berechnungen machen verhältnismäßig wenig Schwierigkeiten, wenngleich die Umwandlung von Zeichenketten in Zahlen und zurück aufgrund der von Visual Basic beachteten länderspezifischen Zahlendarstellung sicher Ärger machen wird. Der schwierigste Teil ist aber unbestritten die Bildschirmausgabe gleich gefolgt von der Eingabe. Die folgende Tabelle bietet einen Überblick über das Problempotenzial spezifischer Bereiche. Bereich
Macht Probleme?
Kontrollstrukturen und Algo- Keine Probleme zu befürchten rithmen Konstanten, Variablen, Bezeichner
Weitgehend problemlos; globale Variablen bedürfen der Überarbeitung und implizit deklarierte lokale Variablen der Kontrolle auf Namenskonflikte
Datentypen und Operationen Kaum Probleme zu befürchten Mathematische Funktionen
Problemlos, neue Funktionen machen vieles einfacher, zum Beispiel die Rundung, finanzmathematische Funktionen
Selbst definierte Funktionen und Prozeduren
Weitgehend problemfrei; Shared-Vereinbarungen (globale Variablen) müssen überarbeitet werden; Namenskonflikte bei impliziter Variablendeklaration möglich
Zeichenfolgenfunktionen
Problembehaftet, da Visual Basic länderspezifische Darstellung bei Zahlenwandlung beachtet, Unicode vs. ASCII-Code, auch die Codetabellen für die Zeichenkodierung sind leicht unterschiedlich
Dateioperationen
Weitgehend problemlos, zuweilen bereitet jedoch die Verarbeitung langer Dateinamen Schwierigkeiten
Eingabe
Problematisch – bei der Portierung ist einiges an konzeptuellen Änderungen einzuarbeiten
453
Wie importiert man den Quelltext?
Wie importiert man den Quelltext?
Bereich
Macht Probleme?
Zeichenausgabe
Sehr problematisch – Print-Anweisung verändert; Print Using fehlt, Zeilenorientierung, Cursorplatzierung anders, Schriftdefinitionen, Farbmodell anders
Grafik
Problematisch, da Veränderungen bei Koordinatensystem und Farbmodell einzuarbeiten sind und die Grafikausgabe die Position der Textausgabe beeinflusst
Fehlerbehandlung
Problemlos, weil unverändert.
Wie importiert man den Quelltext? Legen Sie ein neues Projekt des Typs »Standard-EXE« an und laden Sie den Quelltext des zu portierenden Programms als Standardmodul. Das geht entweder über den Menübefehl PROJEKT/MODUL HINZUFÜGEN oder schlicht durch Ziehen der BAS-Datei von einem Explorer-Fenster in das Projektfenster. Um keine Verfälschungen der Zeichencodes bei der Konversion vom DOS-Textformat nach Windows zu erhalten, können Sie den Programmtext über ein Textverarbeitungsprogramm dezidiert als MSDOS-Text importieren und über die Zwischenablage nach Visual Basic transferieren.
Einfache Programme In keiner anderen Programmiersprache werden mehr Programmskizzen geschrieben, die irgendwelche kleinen Berechnungen anstellen und das Ergebnis ausgegeben, als in Basic. Meist fehlt einfach ein passender Interpreter, um diese Programme wieder zum Leben zu erwecken. Auch wenn man diese Programme vom Prinzip her wie komplexe Programme nach Visual Basic portieren könnte, lohnt sich der Aufwand in den meisten Fällen eher nicht – und mit kleineren Abweichungen im Bereich der Ein- und Ausgabe lässt sich sicher leben. Wenn es Ihnen reicht, dass Print-Ausgaben im Direktfenster (aufzurufen über (Strg)+(G)) erscheinen und Eingaben über kleine Fenster via InputBox getätigt werden, ist die Portierung in wenigen Augenblicken erledigt. Betrachten wir folgendes einfache QBasic-Programm, das eine Zahl abfragt und darüber Auskunft gibt, ob es sich bei der Zahl um eine Primzahl handelt oder nicht. Nach dem Import des Quelltextes zeigt das Codefenster folgenden Inhalt: DECLARE FUNCTION Prim% (a&) Rem Primzahltester INPUT "Bitte Zahl eingeben: ", a& If Prim(a&) Then k$ = "keine " Print a&; "ist "; k$; "Primzahl" Function Prim%(a&) For i% = 2 To Sqr(a&) If a& Mod i% = 0 Then Prim% = i% Exit Function End If Next End Function
454
Einfache Programme
Visual Basic kann weder mit der DECLARE- noch mit der INPUT-Anweisung etwas anfangen, was der Editor durch Ausgabe der entsprechenden Zeilen in roter Farbe kundtut. Aber auch die Print-Anweisung macht Probleme. Um Code wie diesen ans Laufen zu kriegen, gehen Sie wie folgt vor:
Nach der Portierung sieht der Quelltext so aus: Rem Primzahltester Dim a&, k$ Sub Main() a& = InputBox("Bitte Zahl eingeben: ") If Prim(a&) Then k$ = "keine " Debug.Print a&; "ist "; k$; "Primzahl" End Sub Function Prim%(a&) For i% = 2 To Sqr(a&) If a& Mod i% = 0 Then Prim% = i% Exit Function End If Next End Function
Wer damit nicht zufrieden ist und die Implementation lieber in ein Formularmodul packen will, muss schon einiges mehr an Aufwand betreiben – und sollte sich bei dem einfachen Programm vielleicht sogar überlegen, ob er es nicht lieber gleich neu aufbaut. Ein generelles Problem bei der Umsetzung älterer Basic-Programme nach Visual Basic ist nämlich die Umsetzung des (synchronen) Programmablaufs, wenn Eingaben ins Spiel kommen. Da sich Visual Basic das ereignisorientierte Programmiermodell von Windows zu eigen macht, ist für Visual-Basic-Programme zunächst einmal kein fester Programmablauf vorgeschrieben: Das Programm reagiert vielmehr auf die einzelnen Benutzereingaben. Mit anderen Worten: Das, was die Flexibilität und Bedienfreundlichkeit von Windows-Programmen eigentlich ausmacht, wird einem bei der Portierung leider zum Hemmschuh. Sehen wir uns die Sache einmal genauer am Beispiel der INPUT-Anweisung an. Solange man auf InputBox ausweicht, bleibt zwar der synchrone Programmablauf gewahrt, die Eingabe in einem eigenen Dialogfeld stellt aber schon eine erhebliche Einschränkung dar. Der Versuch, solche Eingaben über ein Steuerelement, beispielsweise über ein einfaches Textfeld, zu realisieren,
455
Einfache Programme
1. Löschen Sie DECLARE-Anweisungen. Sie sind unter Visual Basic für modulinterne Funktionen/ Prozeduren nicht nötig. (Visual Basic benutzt Declare in anderer Syntax für den Import von Bibliotheksroutinen, die in DLLs beheimatet sind.) 2. Fassen Sie »losen Code«, das heißt den Code, der keiner Prozedur/Funktion zugeordnet ist, in einer Prozedur namens Main zusammen und deklarieren Sie alle Shared-Variablen aus den Funktionen/Prozeduren als globale Variablen. Lokale Variablen gleichen Namens müssen explizit deklariert werden, um Fehlern durch Namenskonflikte vorzubeugen. (Eine Option Explicit-Anweisung zu Beginn des Moduls deckt implizite Deklarationen auf.) 3. Ersetzen Sie Input-Anweisungen durch InputBox-Anweisungen, wobei Sie jeden via Input gelesenen Wert mit einer eigenen InputBox-Anweisung bedenken müssen. 4. Ersetzen Sie jede Print-Anweisung durch eine Debug.Print-Anweisung. 5. Definieren Sie die Methode Main im Fenster PROJEKTEIGENSCHAFTEN als Startobjekt. 6. Öffnen Sie das Direktfenster ((Str)+(G)) und starten Sie das Programm.
Einfache Programme
Einfache Programme
wirft sofort die Frage nach dem Programmablauf auf: Ein traditionelles Basic-Programm, wie unser Beispiel, erwartet an einer bestimmten Stelle im Programm eine Eingabe, um im weiteren Programmverlauf darauf rekurrieren zu können – ein Visual-Basic-Programm kann zwar auf Eingaben, die ein Benutzer in einem Textfeld vornimmt, reagieren, etwa wenn es das ValidateEreignis behandelt, den Benutzer aber zu zwingen, dies an einer bestimmten Stelle im Programmablauf zu tun, erfordert schon einige Kunstgriffe – zumal wenn mehrere (gegebenenfalls aufeinander aufbauende) Eingaben zu koordinieren sind. Im Sinne einer einfachen Portierung ist das sicher nicht.
Implementation einer eigenen Input- Routine Einen Ausweg stellt die folgende Implementation InputQB der Input-Anweisung dar: Wie InputBox arbeitet InputQB mit einem zweiten Formular, das jedoch nichts weiter als ein Textfeld enthält und an passender Position ohne Rahmen als modales Dialogfeld zum Aufruf kommt. InputQB gibt wie Input den Text EingabeAufforderung an der aktuellen Ausgabeposition aus und bereitet dann das zweite Formular für den Aufruf entsprechend vor: Schrift und Farben des Textfelds werden an das aktuelle Formular angeglichen, die Position des Formulars wird so berechnet, dass dieses an der richtigen Stelle zu liegen kommt, und die Abmessungen werden genau so getrimmt, dass es für eine einzeilige Eingabe bis zum Ende des aktuellen Formulars reicht. Sub InputQB(EingabeAufforderung As String, Param) Dim f As New InputForm ' Eingabeformular Dim RandOffsOben As Integer Dim RandOffsLinks As Integer Dim Params() As String Print EingabeAufforderung; ' Eingabeformular für die Eingabe an (CurrentX, CurrentY) vorbereiten f.Text1.BorderStyle = vbBSNone f.Text1.BackColor = BackColor f.Text1.ForeColor = ForeColor RandOffsOben = Height – ScaleHeight – (Width – ScaleWidth) / 2 RandOffsLinks = (Width – ScaleWidth) / 2 f.Left = CurrentX + Left + RandOffsLinks f.Top = CurrentY + Top + RandOffsOben f.Width = ScaleWidth – CurrentX f.Font = Font f.Height = f.TextHeight("Test") f.Text1.Height = f.Height f.Text1.Width = ScaleWidth – CurrentX f.Show vbModal, Me ' Formular aufrufen, gibt bei ' Hide die Kontrolle zurück Param = f.Text1 ' Wert übernehmen Print Param End Sub
Ein wenig verzwickt ist die Berechnung der Formularposition, da Visual Basic seine Position relativ zum Ursprung des Screen-Objekts (linker oberer Bildschirmpunkt) ausgedrückt haben will – und nicht relativ zum Formular des rufenden Codes (das ist nur bei MDI-Formularen so). Die Berechnung lebt übrigens von der Annahme, dass der untere Formularrand (Abstand zwischen der Unterkante des Client-Bereichs und der Unterkante des Formulars) genauso breit ist wie der rechte und linke Formularrand (genauer: die Hälfte der Summe).
456
Anspruchsvollere Programme
Die Implementation des zweiten Formulars InputForm sieht recht einfach aus. Sie setzt jedoch ein Textfeld Text1 an der Position (0, 0) voraus und baut darauf, dass die Eigenschaft BorderStyle zur Entwurfszeit auf »0 – Kein« gesetzt wurde – anders ist ein rahmenloser Aufruf des Formulars nicht zu bewerkstelligen. ' Modul: InputForm Private Sub Text1_KeyPress(KeyAscii As Integer) If KeyAscii = vbKeyReturn Then Hide End Sub
PrimzahlTester im neuen Gewand Das Projekt PrimzahlTester ist eine etwas anspruchsvollere Portierung des zuvor vorgestellten QBasic-Programms in ein Formularmodul. Es demonstriert zum einen den Einsatz der Funktion InputQB und zum anderen, wie man den losen Programmcode im Formularmodul platziert. Private Sub Form_Load() AutoRedraw = True Show DoProgram ' Unload Me ' Bei Bedarf End Sub Sub DoProgram() InputQB "Bitte Zahl eingeben: ", Eingabe If Eingabe = "" Then Eingabe = "0" a& = Eingabe If Prim(a&) Then k$ = "keine " Print a&; "ist "; k$; "Primzahl" End Sub
Anspruchsvollere Programme Anspruchsvollere Programme – und diese dürften gut 90 Prozent des zu portierenden Codes ausmachen – verlangen nach einem Formular, das den Ausgabebildschirm im Textmodus oder im Grafikmodus simuliert. Seien Sie aber darauf gefasst, dass Ihnen hier ein gutes Stück Arbeit bevorsteht: Gerade was die Ausgabe betrifft, sind die Unterschiede und Ungereimtheiten zwischen Visual Basic und älteren Basic-Varianten nämlich am größten. Die erfolgreiche Portierung mit vertretbarem Aufwand setzt da mitunter eine erhebliche Kompromissbereitschaft voraus.
457
Anspruchsvollere Programme
Wie man sieht, tut das Formular nichts weiter, als sich nach erfolgter Eingabe in das Textfeld Text1 unsichtbar zu machen. Damit hat es folgende Bewandtnis: Da der Show-Aufruf des Formulars modal erfolgt, wird der rufende Code erst fortgesetzt, wenn das gerufene Formular die Kontrolle abgibt. Das passiert beim Ableben des Formulars, aber auch, wenn es auf »unsichtbar« gesetzt wird – zum Glück, denn anders könnte der rufende Code die Eingaben des Benutzers in das Textfeld nicht in Erfahrung bringen. Die »Parameterübergabe« lebt also davon, dass das InputForm-Objekt noch existiert, wenn die Kontrolle zurückkommt – es stirbt erst mit Beendigung der Routine. Natürlich ist die hier vorgestellte Implementation kein äquivalenter Ersatz für Input, zumal die traditionelle Syntax für diese Anweisung immer schon recht ausladend war. Die weitere Ausgestaltung dürfte aber keine Schwierigkeiten mehr bereiten.
Anspruchsvollere Programme
Anspruchsvollere Programme
Nachdem Visual Basic einige beispielsweise unter QBasic noch unverzichtbare Funktionen und Anweisungen schlicht nicht hat, steht man häufig vor der Wahl, passende »Prothesen« zu implementieren, die den alten Code »herüberretten«, oder den inkompatiblen Code mit Mitteln von Visual Basic neu zu schreiben. Ersteres kann sich lohnen, aber auch recht schnell in ein Dickicht führen, das sich durch einen beherzteren Eingriff in den Code vielleicht vermeiden ließe. Mit beiden Herangehensweisen lassen sich bis zu einem gewissen Grad befriedigende Ersatzlösungen finden, eine Äquivalenz jedoch in den wenigsten Fällen. Bedenkt man jedoch, dass ein älteres, noch aus DOS-Zeiten stammendes Programm heutzutage kaum wegen seiner überzeugenden Bildschirmdarstellung und grafischen Gimmicks portiert wird, sondern wegen seines algorithmischen Kerns, dürfte eine gewisse »Entstellung« der Benutzeroberfläche aufgrund vereinfachender Ersatzlösungen nicht so stark ins Gewicht fallen. Bevor es um die konkrete Implementation einiger Standardlösungen für die wichtigsten fehlenden Funktionen und Prozeduren geht, noch ein paar Regeln dafür, wie Sie dem Code generell zu Leibe rücken sollten: 1. Laden Sie den Code, wie gehabt, am besten in ein (neues) Standardmodul und kopieren Sie ihn von da aus häppchenweise in das Formularmodul des Projekts. 2. Entfernen Sie alle DECLARE-Anweisungen ersatzlos und verschaffen Sie sich einen Überblick über den Code. Zeilen, die der Editor in roter Farbe ausgibt, enthalten eine oder mehrere Anweisungen, mit denen Visual Basic nichts anfangen kann. Unternehmen Sie erst einmal noch nichts dagegen. Sie können diese Anweisungen später gegebenenfalls als selbst definierte Funktionen/Prozeduren nachliefern und müssen dann nur die Aufrufsyntax anpassen (hierzu gleich mehr). 3. Kopieren Sie den Code aus dem Hauptmodul in die Load-Routine des Formulars oder in eine von dieser aufgerufene Hilfsprozedur. 4. Kopieren Sie alle Funktionen und Prozeduren in das Formularmodul und gehen Sie jede einzelne Funktion/Prozedur daraufhin durch, ob sie Shared-Vereinbarungen enthält. (Shared-Variablen haben in QBasic so etwas ähnliches wie einen globalen Geltungsbereich, müssen aber von jeder Funktion/Prozedur, die damit arbeitet, eigens über Shared-Anweisungen importiert bzw. exportiert werden.) Deklarieren Sie die entsprechenden Variablen im Bereich Allgemein des Formulars als globale Variablen. Bevor Sie die Shared-Anweisungen für eine Variable entfernen, sollten Sie noch die Funktionen/Prozeduren ohne Shared-Vereinbarungen danach durchgehen, ob sie nicht zufällig die gleichen Variablenbezeichner mit impliziter Deklaration (und somit als lokale Variable) verwenden. Wenn ja, ergänzen Sie lokale Deklarationen. Ansonsten fangen Sie sich aufgrund von Namenskonflikten schwer aufzufindende Fehler ein. 5. Lokalisieren Sie die für die Eingabe sowie die für die Ausgabe zuständigen Routinen und analysieren Sie die Benutzerschnittstelle des Programms. Von diesen Routinen haben Sie am meisten Ärger zu erwarten. Treffen Sie eine Entscheidung, ob Sie Eingabeschleifen und Menüs des Programms im Stile von Windows neu implementieren oder ob Sie versuchen, den vorhandenen Code durch Bereitstellung einer geeigneten Infrastruktur zu emulieren. 6. Bereinigen Sie Ungereimtheiten bei der Zeichenfolgendarstellung: beim Import nach Windows entstellte Umlaute, Punkt-/Kommaproblematik bei der Wandlung von Zeichenfolgen in Zahlenwerte (und umgekehrt) sowie veränderte Tastaturcodes. 7. Implementieren Sie Funktionen/Prozeduren und Voraussetzungen, die Visual Basic nicht unterstützt, und passen Sie die betroffenen Aufrufe an die neue Syntax bzw. Semantik an. 8. Spannen Sie den Compiler für die Überprüfung der syntaktischen Kompatibilität ein und schreiben Sie nicht emulierbare, inkompatible Codesequenzen um. Geizen Sie nicht mit Kommentaren. 9. Testen Sie die semantische Äquivalenz des portierten Codes schrittweise durch geeigneten Testcode und schreiben Sie Codesequenzen, die nicht das gewünschte Ergebnis (meist Ausgabe) liefern, um.
458
Implementation von Inkey$ Natürlich wird jedes komplexere Programm seine eigenen Fallstricke enthalten, wenn es nicht absolut sauber implementiert und kommentiert wurde oder gar den einen oder anderen »Trick« benutzt. Generelle Regeln lassen sich für die Umsetzung der verschiedenen Programmiertechniken kaum aufstellen. Die folgenden Abschnitte stellen verschiedene Routinen vor, die sich bei der Portierung schon so mancher QBasic-Programme bewährt haben. Sie sind im Projekt QBEmulation als Standardmodul zusammengefasst. Das Projekt lässt sich gut als Ausgangspunkt für eigene Portierungen sowie Emulationen verwenden.
Implementation von Inkey$
Private InkeyChar As String Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer) If Shift = 0 Then ' Funktionstasten ignorieren InkeyChar = Chr(0) + Chr(KeyCode) ' Tastaturcode merken End If End Sub Private Sub Form_KeyPress(KeyAscii As Integer) InkeyChar = Chr(KeyAscii) ' Zeichen merken End Sub Function InKey$() DoEvents InKey$ = InkeyChar InkeyChar = "" End Function
Die Implementation ist zwar geradlinig, hat aber einen Haken: Die Tastaturcodes von Windows sind anders als die von DOS. So hat beispielsweise die Taste (Rechts) unter DOS den Code 77 und unter Windows den Code 39 (oder vbKeyRight). Es wird Ihnen also nicht erspart bleiben, Tastatursteuerungen komplett zu überarbeiten. Die Tastencodes entnehmen Sie dem Aufzählungstyp KeyCodeConstants. Aufgrund des DoEvents-Aufrufs ist die Implementation auch gegen Endlosschleifen gefeit (wäre da nicht das »Stehaufmännchen«-Problem, wie noch ausführlich im Abschnitt »Ereignisbehandlung mit DoEvents, ein komplexes Problemfeld«, S. 485 erläutert). Somit stellt die klassische INKEY$-Schleife – wie hier in der Implementation einer Schreibmaschine – keine Hürde mehr dar: a$ = InKey$ While a$ <> Chr$(27) Print a$; a$ = InKey$ Wend
459
Anspruchsvollere Programme
Neben Input fehlt Visual Basic auch die Funktion INKEY$. Hierfür lässt sich jedoch relativ einfach Ersatz finden, wenn man die Ereignisse KeyDown und KeyPress des Formulars behandelt und eine globale Variable einsetzt, um den jeweils letzten Tastendruck zu speichern. (Falls das Formular ein Steuerelement besitzt, das den Fokus erhalten kann, müssen Sie die Eigenschaft KeyPreview auf True setzen, sonst bekommt das Formular die Eingaben nicht zu sehen.) Bekanntlich liefert INKEY$ ASCII-Zeichen als Zeichenfolge der Länge 1 und alle anderen Zeichen als Tastaturcodes mit vorangestelltem Nullbyte. Da das KeyPress-Ereignis nur auftritt, wenn ein ASCII-Zeichen eingegeben wurde, arbeiten die beiden Routinen mit derselben globalen Variable.
Anspruchsvollere Programme
Implementation von LOCATE, POS, CSRLIN und COLOR
Anspruchsvollere Programme
Nicht wenige Basic-Programme sind auf einen Textbildschirm mit 80 Spalten und 25 Zeilen zugeschnitten und machen massiv von der Anweisung Locate Gebrauch, die es erlaubt, die Ausgabeposition für die Print-Anweisung frei zu setzen. Zur Abfrage der aktuellen Position findet man weiterhin die Funktionen POS und CSRLIN. Alle drei Routinen arbeiten mit dem jeweils aktuellen Koordinatensystem und lassen sich recht einfach nachrüsten. Zeilen- und Spaltenzählung sind Eins-basiert. Sub Locate(Optional Zeile = 0, Optional Spalte = 0) If Zeile Then CurrentX = ScaleLeft + (Zeile – 1) * TextWidth("A") If Spalte Then CurrentY = ScaleTop + (Spalte – 1) * TextHeight("A") End Sub Function Pos(dummy) As Integer Pos = CInt((CurrentX – ScaleLeft) / TextWidth("A") + 1) End Function Function CsrLin() As Integer CsrLin = (CurrentY – ScaleTop) / TextHeight("A") + 1 End Function
Damit das funktioniert, ist allerdings eine gewisse Vorbereitung notwendig: das Setzen einer nichtproportionalen Schrift sowie geeigneter Formularabmessungen. Private Sub Form_Load() AutoRedraw = True FontName = "Courier New" FontSize = 8 Width = Width – ScaleWidth + 80 * TextWidth("T") Height = Height – ScaleHeight + 25 * TextHeight("T") ' Testen der Methoden Locate 10, 10 Print CsrLin; Locate, 20 Print Csrlin; End Sub
Ein gravierendes Problem dürfte allerdings der fehlende automatische Umbruch am Zeilenende bei der Print-Ausgabe darstellen. Hiergegen scheint aufseiten von Visual Basic kein Kraut gewachsen zu sein, es sei denn, man implementiert die Print-Anweisung neu. Angesichts des zusätzlichen Aufwands, den das für die Portierung bedeutet, wird es meist besser sein, die entsprechenden Stellen »von Hand« zu bereinigen, als alle Print-Ausgaben umzuschreiben. Auch die Color-Anweisung lässt sich nicht völlig äquivalent implementieren, da sich eine Änderung der Hintergrundfarbe immer gleich auf das gesamte Fenster bezieht und nicht nur auf die Texthöhe und Textbreite der kommenden Print-Ausgaben. Für die korrekte Semantik müsste man gleichermaßen eine andere Implementation der Print-Anweisung bereitstellen. Sub Color(Vordergrund%, Optional Hintergrund% = 0) BackColor = QBColor(Hintergrund%) ForeColor = QBColor(Vordergrund%) End Sub
460
Koordinatensystem und Grafikmodus
Koordinatensystem und Grafikmodus
Sub Screen(Modus) Select Case Modus Case 10 ' 640x350 Width = Width – ScaleWidth + ScaleX(640, vbPixels, vbTwips) Height = Height – ScaleHeight + ScaleX(350, vbPixels, vbTwips) ScaleMode = vbPixels Case 11, 12 ' 640x480 Width = Width – ScaleWidth + ScaleX(640, vbPixels, vbTwips) Height = Height – ScaleHeight + ScaleX(480, vbPixels, vbTwips) ScaleMode = vbPixels Case Else Err.Raise 1000, , "Bildschirmmodus nicht implementiert" End Select End Sub
Lassen Sie sich nicht von den Eigenschaften Height und Width ärgern! Diese Eigenschaften erwarten unabhängig vom eingestellten Koordinatensystem ihre Werte immer in Twips. Eine generelle Implementation der SCREEN-Funktion ist wahrlich »kein Zuckerschlecken« und dürfte zu sehr umständlichen Lösungen führen. Am besten, Sie schreiben das zu portierende Programm nach Möglichkeit um. Einfacher haben Sie es mit der WINDOW-Anweisung. Sie lässt sich (bis auf die Geschichte mit der Orientierung) unverändert in die Scale-Methode überführen. Damit wäre der Grafikmodus »eingeschaltet« und das Koordinatensystem gesetzt, so dass man mit den Methoden Line, PSet und Circle ungestraft zu Werke gehen kann. Na, sagen wir fast ungestraft: gegebenenfalls vorhandene Farbe-Parameter bedürfen noch einer Übersetzung in den TrueColor-Farbraum, was sich am einfachsten mittels der QBColor-Methode erledigen lässt. Der folgende Code zeichnet ein rotes Rechteck im VGA-Modus 12: Screen 12 Line (10, 10)-(630, 470), QBColor(12), BF
Schön und gut. Was aber, wenn das zu portierende Programm seine Grafikausgaben mit Textausgaben kombiniert. Aufseiten von Visual Basic ist das keine triviale Angelegenheit. Immerhin, als Schrift für die VGA-Auflösung 640×480 bietet sich »Courier New« in der Größe 8 an. Sie hat eine Höhe von 16 Bildpunkten und eine Breite von 8 Bildpunkten, so dass sich genau die gewünschte 80×30 Zeichenmatrix ergibt. Problematisch ist aber, dass Visual Basic die Eigenschaften CurrentX und CurrentY sowohl für Print als auch für die Grafikmethoden PSet, Line und Circle verwendet. Um ein Programm mit gemischter Text- und Grafikausgabe portieren zu
461
Anspruchsvollere Programme
Wie Sie gesehen haben, war es für den Textmodus nicht erforderlich, ein Koordinatensystem zu setzen. Sobald aber eine Grafikausgabe ins Spiel kommt, führt kein Weg mehr daran vorbei, da ältere Basic-Versionen im Grafikmodus standardmäßig ein Koordinatensystem mit der Einheit »Bildpunkte« verwenden, dessen Ursprung in der linken oberen Ecke des Bildschirms gelegen und dessen vertikale Achse nach unten orientiert ist. Zum Umschalten in den Grafikmodus findet man in älteren Programmen die SCREEN-Anweisung. Diese Anweisung vollständig zu implementieren, ist weder möglich noch in den wenigsten Fällen wirklich erforderlich (unter Windows gibt es eben keine unterschiedlichen Grafikmodi und man kann nicht auf einer Bildschirmseite zeichnen, während man eine andere anzeigt – vom Ansatz her könnte man jedoch versuchen, mit mehreren Fenstern oder mit PictureBox-Steuerelementen zu arbeiten, zwischen denen SCREEN hin- und herschaltet). Eine einfache Implementation der Screen-Anweisung könnte etwa so aussehen:
Anspruchsvollere Programme
können, muss ein Weg gefunden werden, die Ausgabepositionen getrennt zu verwalten. Das geht beispielsweise über zwei zusätzliche Methoden TextQB und GrafikQB sowie vier globale Variablen für die jeweiligen Positionen und eine Zustandsvariable:
Anspruchsvollere Programme
Private Private Private Private
CurrentTextX As Single CurrentTextY As Single CurrentGrafikX As Single CurrentGrafikY As Single
' Positionen in Grafik' und Textmodus getrennt ' verwalten
Enum CurrentStates vbCurrentText = 0 vbCurrentGrafik = 1 End Enum Private CurrentState As CurrentStates Sub TextQB() If CurrentState = vbCurrentGrafik Then CurrentState = vbCurrentText CurrentGrafikX = CurrentX CurrentGrafikY = CurrentY CurrentX = CurrentTextX CurrentY = CurrentTextY End If End Sub Sub GrafikQB() If CurrentState = vbCurrentText Then CurrentState = vbCurrentGrafik CurrentTextX = CurrentX CurrentTextY = CurrentY CurrentX = CurrentGrafikX CurrentY = CurrentGrafikY End If End Sub
Die Implementation mag noch angehen, für die Portierung bedeutet das aber, dass Sie Printund Locate-Anweisungen mit TextQB und PSet-, Circle- und Line- mit GrafikQB flankieren müssen. (Sofern Letztere keine Step-Zusätze haben und alle Parameter angegeben sind, werden sie von Print- und Locate-Anweisungen zwar nicht durcheinander gebracht – umgekehrt aber schon.) Falls das Programm ein eigenes Koordinatensystem verwendet, ist noch dazu eine Implementation von WINDOW erforderlich, die neben dem Scale-Aufruf auch die aktuellen Positionen für den Text- und Grafikmodus umrechnet. Die Implementation ist ein wenig trickreich, da sie eines der beiden Koordinatenpaare vor Einführung des neuen Koordinatensystems in ein Referenzkoordinatensystem transformiert und danach wieder zurücktransformiert. Das andere Koordinatenpaar stimmt mit CurrentX und CurrentY überein und wird implizit von Scale umgerechnet. Außerdem definiert WINDOW ein kartesisches Koordinatensystem, bei dem die vertikale Achse nach oben orientiert ist. Sub Window(x1, y1, x2, y2) ScaleMode = vbUser If CurrentState = vbCurrentGrafik Then
462
Zusammenfassung der Emulation als Standardmodul
Zusammenfassung der Emulation als Standardmodul Im Projekt QBEmulation ist der gesamte Emulationscode in einem weitgehend eigenständigen Standardmodul zusammengefasst und ausgiebig kommentiert; einzig die Behandlungsroutinen für die beiden Tastaturereignisse sowie die Initialisierung der Emulation treten im Formular in Erscheinung. Die Aktivierung der Emulation geschieht durch einen InitQB-Aufruf unter Angabe einer Referenz auf das Formular (Me). In Abänderung zum vorgestellten Code, müssen die Routinen im Standardmodul den Zugriff auf die Eigenschaften und Methoden des Formulars über eine globale Objektvariable des Typs Form abwickeln. Etwas komplizierter als diskutiert ist die Implementation der Anweisung InputQB ausgefallen, ein Tribut an die Unabhängigkeit vom Koordinatensystem. Der Code des Formularmoduls mit ein wenig Testcode sieht so aus: '*********************************************************************** ' Formularmodul : QBEmulationTest ' Autor : 2000 Rudolf Huttary ' : testet Emulationsmodul '*********************************************************************** Option Explicit Private Sub Form_Load() InitQB Me ' Emulation initialisieren ' Testen der Methoden Show Screen 12 Window -1, -1, 1, 1 TextQB Locate 10, 10
463
Anspruchsvollere Programme
' Auf Referenzkoordinatensystem ausweichen CurrentTextX = ScaleX(CurrentTextX – ScaleLeft, vbUser, vbTwips) CurrentTextY = ScaleY(CurrentTextY – ScaleTop, vbUser, vbTwips) f.Scale (x1, IIf(y1 > y2, y1, y2))-(x2, IIf(y1 > y2, y2, y1)) ' Ins neue Koordinatensystem CurrentTextX = ScaleX(CurrentTextX, vbTwips, vbUser) + ScaleLeft CurrentTextY = ScaleY(CurrentTextY, vbTwips, vbUser) + ScaleTop ' akt. Position übernehmen CurrentGrafikX = CurrentX CurrentGrafikY = CurrentY Else ' Auf Referenzkoordinatensystem ausweichen CurrentGrafikX = ScaleX(CurrentGrafikX – ScaleLeft, vbUser, vbTwips) CurrentGrafikY = ScaleY(CurrentGrafikY – ScaleTop, vbUser, vbTwips) f.Scale (x1, IIf(y1 > y2, y1, y2))-(x2, IIf(y1 > y2, y2, y1)) ' Ins neue Koordinatensystem CurrentGrafikX = ScaleX(CurrentGrafikX, vbTwips, vbUser) + ScaleLeft CurrentGrafikY = ScaleY(CurrentGrafikY, vbTwips, vbUser) + ScaleTop ' akt. Position übernehmen CurrentTextX = CurrentX CurrentTextY = CurrentY End If End Sub
Anspruchsvollere Programme
Anspruchsvollere Programme
Print "Hinge"; ' Erstes Textstück GrafikQB ' Grafikmodus Line (-1, -1)-(0, 0) ' erste Linie TextQB ' Textmodus Window -10, -1, 40, 20 ' Koordinatensystem ändern Print "bungs"; ' zweites Textstück GrafikQB ' Grafikmodus Line -(-8, 26) ' zweite Linie direkt dransetzen TextQB ' Textmodus Print "voll"; ' drittes Textstück Locate , 20 Print Pos(0); CsrLin; ' aktuelle Position: (24, 20) Dim s InputQB "Bitte Text eingeben: ", s ' Texteingabe testen Locate 1, 1 ' Eingabe links oben ausgeben Print s End Sub ' Teil der Emulation Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer) If Shift = 0 Then ' Funktionstasten ignorieren InkeyChar = Chr(0) + Chr(KeyCode) ' Tastaturcode merken End If End Sub ' Teil der Emulation Private Sub Form_KeyPress(KeyAscii As Integer) InkeyChar = Chr(KeyAscii) ' Zeichen merken End Sub
Hier der Code des Standardmoduls QBEmulation: '*********************************************************************** ' Standardmodul : QBEmulation ' Autor : 2000 Rudolf Huttary ' Beschreibung : stellt einige Funktionen und Anweisungen ' für das Portieren von Code aus QBasic bereit. ' Initialisierung mit InitQB erforderlich '*********************************************************************** Option Explicit Public InkeyChar As String Private f As Form Private CurrentTextX As Single Private CurrentTextY As Single Private CurrentGrafikX As Single Private CurrentGrafikY As Single Enum CurrentStates vbCurrentText = 0 vbCurrentGrafik = 1 End Enum
464
' für Inkey$ ' akt. Position für Textausgabe ' akt. Position für Grafikausgabe
Zusammenfassung der Emulation als Standardmodul
Private CurrentState As CurrentStates ' Zustandsvariable
Anspruchsvollere Programme
Sub InitQB(Formular As Form) ' Initialisiert Formular für Ausgaben im Textmodus Set f = Formular ' Referenz auf Formular speichern f.AutoRedraw = True ' Ausgabe nicht über Paint f.FontName = "Courier New" ' nichtproportionale Schrift f.FontSize = 8 ' 16x8 Bildpunkte pro Zeichen Color ' Standardfarben setzen f.Width = f.Width – f.ScaleWidth + 80 * f.TextWidth("T") ' 80 Spalten f.Height = f.Height-f.ScaleHeight + 25 * f.TextHeight("T") ' 25 Zeilen End Sub Function InKey$() ' Letztes Zeichen von der Tastatur holen DoEvents ' Wartende Ereignisse verarbeiten InKey$ = InkeyChar ' InkeyChar wird von Formularmodul InkeyChar = "" ' gesetzt! End Function Sub Locate(Optional Zeile = 0, Optional Spalte = 0) ' Setzt (Zeile, Spalte) für nächsten Print-Aufruf (Eins-basiert) If Zeile Then f.CurrentX = f.ScaleLeft + (Zeile – 1) * f.TextWidth("A") If Spalte Then f.CurrentY = f.ScaleTop + (Spalte – 1) * f.TextHeight("A") End Sub Function Pos(dummy) As Integer ' Liefert aktuelle Spalte für Textausgabe Pos = CInt((f.CurrentX – f.ScaleLeft) / f.TextWidth("A") + 1) End Function Function CsrLin() As Integer ' Liefert aktuelle Zeile für Textausgabe CsrLin = (f.CurrentY – f.ScaleTop) / f.TextHeight("A") + 1 End Function Sub Screen(Modus) ' Schaltet auf Grafikmodus um Select Case Modus Case 10 ' 640x350 f.FontName = "Courier New" f.FontSize = 8 f.Width = f.Width – f.ScaleWidth + f.ScaleX(640, vbPixels, vbTwips) f.Height = f.Height – f.ScaleHeight + f.ScaleX(350, vbPixels, _ vbTwips) f.ScaleMode = vbPixels Case 11, 12 ' 640 * 480 f.FontName = "Courier New" f.FontSize = 8 f.Width = f.Width – f.ScaleWidth + f.ScaleX(640, vbPixels, vbTwips) f.Height = f.Height – f.ScaleHeight + f.ScaleX(480, vbPixels, _
465
Anspruchsvollere Programme
Anspruchsvollere Programme
vbTwips) f.ScaleMode = vbPixels Case Else Err.Raise 1000, , "Grafikmodus nicht emuliert" End Select End Sub Sub Color(Optional Vordergrund% = 0, Optional Hintergrund% = 15) ' Setzt Farben f.BackColor = QBColor(Hintergrund%) f.ForeColor = QBColor(Vordergrund%) End Sub Sub TextQB() ' Speichert aktuelle Position für Grafikausgabe und ' stellt aktuelle Position für Textausgabe wieder her ' (muss vor Locate, Print und Input aufgerufen werden) If CurrentState = vbCurrentGrafik Then CurrentState = vbCurrentText CurrentGrafikX = f.CurrentX CurrentGrafikY = f.CurrentY f.CurrentX = CurrentTextX f.CurrentY = CurrentTextY End If End Sub Sub GrafikQB() ' Speichert aktuelle Position für Textausgabe und ' stellt aktuelle Position für Grafikausgabe wieder her If CurrentState = vbCurrentText Then CurrentState = vbCurrentGrafik ' Zustand merken CurrentTextX = f.CurrentX ' Textausgabepos merken CurrentTextY = f.CurrentY f.CurrentX = CurrentGrafikX ' Grafikposition restaurieren f.CurrentY = CurrentGrafikY End If End Sub Sub Window(x1, y1, x2, y2) ' Stellt Koordinatensystem ein, bei dem der Punkt (x1, y1) links oben ' und der Punkt (x2, y2) links unten liegt f.ScaleMode = vbUser ' für ersten Aufruf erforderlich If CurrentState = vbCurrentGrafik Then 'Ist Grafikpos. akt. Position? ' Für Textausgabepos auf Referenzkoordinatensystem ausweichen CurrentTextX = f.ScaleX(CurrentTextX – f.ScaleLeft, vbUser, vbTwips) CurrentTextY = f.ScaleY(CurrentTextY – f.ScaleTop, vbUser, vbTwips) f.Scale (x1, y1)-(x2, y2) ' Textausgabeposition ins neue Koordinatensystem umrechnen CurrentTextX = f.ScaleX(CurrentTextX, vbTwips, vbUser) + f.ScaleLeft CurrentTextY = f.ScaleY(CurrentTextY, vbTwips, vbUser) + f.ScaleTop
466
Zusammenfassung der Emulation als Standardmodul
Anspruchsvollere Programme
' akt. Position übernehmen CurrentGrafikX = f.CurrentX CurrentGrafikY = f.CurrentY Else ' Für Grafikposition auf Referenzkoordinatensystem ausweichen CurrentGrafikX = f.ScaleX(CurrentGrafikX – f.ScaleLeft, vbUser, _ vbTwips) CurrentGrafikY = f.ScaleY(CurrentGrafikY – f.ScaleTop, vbUser, _ vbTwips) f.Scale (x1, y1)-(x2, y2) ' Grafikposition ins neue Koordinatensystem umrechnen CurrentGrafikX = f.ScaleX(CurrentGrafikX, vbTwips, vbUser) + _ f.ScaleLeft CurrentGrafikY = f.ScaleY(CurrentGrafikY, vbTwips, vbUser) + _ f.ScaleTop ' akt. Position übernehmen CurrentTextX = f.CurrentX CurrentTextY = f.CurrentY End If End Sub Sub InputQB(EingabeAufforderung As String, Param) ' emuliert die QBasic-Anweisung INPUT für einen Parameter Dim iForm As New InputForm ' Eingabeformular Dim RandOffsOben As Integer Dim RandOffsLinks As Integer Dim fscalewTwips As Integer Dim fscalehTwips As Integer Dim fCurrentXTwips As Integer Dim fCurrentYTwips As Integer f.Print EingabeAufforderung; ' Höhe und Breite des Clientbereichs, Randoffsets ' sowie aktuelle Position in Twips umrechnen f.ScaleMode = vbUser ' Falls noch nicht geschehen fscalewTwips = f.ScaleX(f.ScaleWidth, vbUser, vbTwips) fscalehTwips = f.ScaleY(f.ScaleHeight, vbUser, vbTwips) RandOffsOben = f.Height – fscalehTwips – (f.Width – fscalewTwips) / 2 RandOffsLinks = (f.Width – fscalewTwips) / 2 fCurrentXTwips = f.ScaleX(f.CurrentX – f.ScaleLeft, vbUser, vbTwips) fCurrentYTwips = f.ScaleY(f.CurrentY – f.ScaleTop, vbUser, vbTwips) ' Eingabeformular für die Eingabe an (CurrentX, CurrentY) vorbereiten iForm.Text1.BorderStyle = vbBSNone iForm.Text1.BackColor = f.BackColor iForm.Text1.ForeColor = f.ForeColor iForm.Left = fCurrentXTwips + f.Left + RandOffsLinks iForm.Top = fCurrentYTwips + f.Top + RandOffsOben iForm.Width = fscalewTwips – fCurrentXTwips iForm.Text1.Font = f.Font iForm.FontSize = f.FontSize
467
Von W ANKEL. BAS
zur W ankelAnimation
Von WANKEL.BAS zur WankelAnimation
iForm.Height = iForm.TextHeight("Test") iForm.Text1.Height = iForm.Height iForm.Text1.Width = fscalewTwips – f.CurrentX iForm.Show vbModal, f ' Formular aufrufen, gibt bei ' Hide die Kontrolle an f zurück Param = iForm.Text1 ' Wert übernehmen f.Print Param ' ausgeben End Sub '*********************************************************************** ' Formularmodul : InputForm ' Autor : 2000 Rudolf Huttary '*********************************************************************** Private Sub Text1_KeyPress(KeyAscii As Integer) If KeyAscii = vbKeyReturn Then Hide End Sub
Ausgabe des Testcodes für das Emulationsmodul
Von WANKEL.BAS zur WankelAnimation Der Fundus eines jeden Programmierers, der schon einige Jahre »im Geschäft« ist, enthält so manches verwaiste Programm, das einst viel Mühe gemacht hat, inzwischen aber einem Plattformwechsel zum Opfer gefallen ist. Mir geht es da nicht anders: Die Halde enthält Hunderte kleiner Programmskizzen, aber auch eine ganze Menge liebevoll ausgestaltete Programme, die ihren Sinn schlicht deshalb verwirkt haben, weil sie mit QuickBasic oder QBasic geschrieben wurden und somit nur in einem DOS-Fenster unter Windows ausführbar sind. Eine dieser Skizzen, das Programm WANKEL.BAS, das ich vor vielen Jahren in QBasic geschrieben habe, um mir den mechanischen Ablauf des Kolbens im Wankelmotor als Animation vor Augen zu bringen, wird Ihnen dieser Abschnitt in originaler und in portierter Fassung vorstellen.
468
Von W ANKEL. BAS zur W ankelAnimation
Von WANKEL.BAS zur WankelAnimation
Animationsphase im Formular von WankelAnimation
Hier zunächst der Code des altehrwürdigen Originals: DECLARE SUB zylinder (mx!, my!) DECLARE SUB kolben (mx!, my!, w!, sicht!) DECLARE SUB zahn (mx!, my!, r!, h!, w!, anz!, sicht!) Rem Konstanten Const pi = 3.141593 Const pi3 = pi / 3 Const pi6 = pi / 6 Const pi2 = 2 * pi Const r = 10 ' Radius Kolbenzahnrad Const r2 = r * 2 / 3 ' Radius innerer Zahnrad Const b = r – r2 Const mx = 0 ' Mittelpunktsverschiebung Const my = 0 ' Mittelpunktsverschiebung Const w = 3 ' Drehkolben-Faktor Const z2 = 24 ' Anzahl der Zahnräder äußeres Rad Const z1 = z2 * r2 / r ' Anzahl der Zahnräder inneres Rad Const wi = 0 ' Winkelgeschw. inn. Rad (0 bei Radius = 2/3) Const w2off = 0 '+ pi / z1 ' Phasenverschiebung inneres Rad 'Variablen initialisieren st = -pi / 60 ' Schleifeninkrement, Animationsgeschwindigkeit x1 = 0 ' Animationswinkel x2 = 0 ' Animationswinkel Screen 12 Print "Zähne:"; z1; "/"; z2; "schneller/langsamer: Cursor, Ende: Esc" WINDOW (-32, -24)-(32, 24) PSet (mx, my) ' Mittelpunkt zylinder mx, my ' Zylinder zeichnen
469
Von W ANKEL. BAS
zur W ankelAnimation
Do ' Animationsschleife kolben mx + b * Cos(x2 * w), my + b * Sin(x2 * w), x2, 0 kolben mx + b * Cos(x1 * w), my + b * Sin(x1 * w), (x1), 15
Von WANKEL.BAS zur WankelAnimation
zahn mx + b * Cos(x2 * w), my + b * Sin(x2 * w), r, r / 10, x2, z2, 0 zahn mx + b * Cos(x1 * w), my + b * Sin(x1 * w), r, r / 10, x1, z2, 15 zahn mx, my, r – b, r / 10, wi * x1, z1, 15 a$ = InKey$ ' Tastaturinteraktion If a$ <> "" Then taste = Asc(Right$(a$, 1)) Select Case taste Case 77: st = st + 0.2 * Abs(st) + 0.001 ' Cursor links Case 75: st = st – 0.2 * Abs(st) – 0.001 ' Cursor rechts Case 27, 79: Exit Do ' Abbruch End Select End If x2 = x1 ' alter Winkel x1 = x1 + st ' Schleifeninkrement Loop Sub kolben(mx, my, winkel, sicht) Dim r0, r1, w1, w2, w3, ef, c1, c2, c3, a, x, y, x1, x2, x3 r0 r1 w1 w2 w3 ef c1 c2 c3
= = = = = = = = =
a = x = y = xx1 xx2 xx3
2 * r Sqr(3) * r * 1 * 0.97 winkel + pi3 w1 + pi3 * 2 w2 + pi3 * 2 0.26 * r2 / r Cos(w1): s1 = Sin(w1) Cos(w2): s2 = Sin(w2) Cos(w3): s3 = Sin(w3)
0 r1 * Sin(a) * ef + r1 * Cos(a) = x * c1 – y * s1: = x * c2 – y * s2: = x * c3 – y * s3:
For a = 0 To pi * x = r1 * Sin(a) y = r1 * Cos(a) x1 = x * c1 – y x2 = x * c2 – y x3 = x * c3 – y Line (xx1 + mx, Line (xx2 + mx, Line (xx3 + mx, xx1 = x1: xx2 =
47 0
r1 / 1.75
' ' ' ' ' ' ' '
Ellipsenradius +60° , 1. Teilellipse +120°, 2. Teilellipse +120°, 3. Teilellipse Ellipsenfaktor Winkelwerte für Drehung Winkelwerte für Drehung Winkelwerte für Drehung
' Ellipse ' Ellipse yy1 = x * s1 + y * c1 yy2 = x * s2 + y * c2 yy3 = x * s3 + y * c3
y-Anteil mit Verschiebung y-Anteil ' Start Teilellipse 1 ' Start Teilellipse 2 ' Start Teilellipse 3
1.01 Step pi / 10 ' Schleife für halbe Ellipse * ef + r1 / 1.75 ' Ellipse y-Anteil mit Verschiebung ' Ellipse y-Anteil * s1: y1 = x * s1 + y * c1 ' Drehung Teilellipse 1 * s2: y2 = x * s2 + y * c2 ' Drehung Teilellipse 2 * s3: y3 = x * s3 + y * c3 ' Drehung Teilellipse 3 yy1 + my)-(x1 + mx, y1 + my), sicht ' Teilellipse 1 yy2 + my)-(x2 + mx, y2 + my), sicht ' Teilellipse 2 yy3 + my)-(x3 + mx, y3 + my), sicht ' Teilellipse 3 x2: xx3 = x3
Von W ANKEL. BAS zur W ankelAnimation
yy1 = y1: yy2 = y2: yy3 = y3 Next a End Sub
Von WANKEL.BAS zur WankelAnimation
Sub zahn(mx, my, r, h, w, anz, sicht) dw = pi / anz rz1 = r – h / 2 rz2 = r + h / 2 Min = 1000 PSet (mx + Cos(w – dw) * rz1, my + Sin(w – dw) * rz1), sicht For y = 0 To anz x = w + 2 * dw * y zx1 = mx + Cos(x + dw) * rz1 zy1 = my + Sin(x + dw) * rz1 di = zx1 * zx1 + zy1 * zy1 If Min > di Then Min = Sqr(di): dx = zx1: dy = zy1 End If Line -(mx + Cos(x) * rz2, my + Sin(x) * rz2), sicht Line -(zx1, zy1), sicht Next End Sub Sub zylinder(mx, my) r0 = 2 * r PSet (r0 * Cos(x) + mx + b * Cos(x * w), _ r0 * Sin(x) + my + b * Sin(x * w)) For x = 0 To pi2 + pi / 40 Step pi / 40 Line -(r0 * Cos(x) + mx + b * Cos(x * w), _ r0 * Sin(x) + my + b * Sin(x * w)) Next x End Sub
Sieht man von den mathematischen Einzelheiten und den vielen Parametern ab, ist das Programm recht einfach aufgebaut. Es enthält ein Hauptmodul, das die Variableninitialisierung vornimmt, den Zylinder zeichnet und ansonsten die Animationsschleife durchläuft, bis der Benutzer die Taste (Esc) drückt. In jeder Animationsphase überzeichnet der Schleifenkörper die vorherige Phase des Kolbens und des äußeren Zahnrads mit Hintergrundfarbe und zeichnet dann den Kolben sowie beide (!) Zahnräder unter Beachtung eines Winkelinkrements neu. Der Benutzer kann die Rotationsgeschwindigkeit des Kolbens verändern, indem er die Cursortasten (Rechts) oder (Links) drückt. In der DOS-Version wird die Grundgeschwindigkeit der Animation schlicht durch die Ausführungsgeschwindigkeit des Programms bestimmt, und die Geschwindigkeitsregelung erfolgt durch Manipulation des Winkelinkrements. Die drei Funktionen des Programms kolben, zylinder und zahn zeichnen die drei unterschiedlichen Figuren, aus denen sich die Animation zusammensetzt. Da der Zylinder feststehend ist (das ist nicht bei allen Wankelmotoren so), braucht er nur ein einziges Mal vor Eintritt in die Animationsschleife gezeichnet zu werden. Das innere Zahnrad steht zwar still, würde aber von dem äußeren Zahnrad mit der Zeit ausradiert werden, so dass es kurzerhand gleichfalls in jeder Phase nachgezeichnet wird.
47 1
Von W ANKEL. BAS
zur W ankelAnimation
Von WANKEL.BAS zur WankelAnimation
Die einzelnen Funktionen geben eigentlich außer der Berechnung der Drehung nicht allzu viel Interessantes her. Eine Drehung um den Winkel w bildet einen Punkt (x, y) auf den Punkt (x', y') ab. Dabei gilt folgende Beziehung: x' =
x · cos(w)
–
y · sin(w)
y' =
x · sin(w)
+
y · cos(w)
Die Funktion zylinder zeichnet den Wankel-Zylinder um den Punkt (mx, my) als vieleckiges Polygon, wobei sich die Eckpunkte des Polygons als Summe zweier Kreisfunktionen ergeben, deren Frequenzen im ganzzahligen Verhältnis 1:3 zueinander stehen. Die zuständige Konstante hat den Bezeichner w und ist als »Drehkolbenfaktor« kommentiert. (Ändern Sie doch den Wert einmal!) Wer das Prinzip nicht kennt: Die Eckpunkte für die polygonale Annäherung an einen Kreis erhält man, indem man eine Variable zwischen 0 und 2 als Winkel laufen lässt, dann in horizontaler Richtung den Sinus sowie in vertikaler Richtung den Cosinus (oder umgekehrt) des Winkels anträgt und schließlich die Werte mit dem gewünschten Radius skaliert. Nimmt man für die beiden Richtungen unterschiedliche Radien, ergibt sich eine (horizontal/vertikal ausgerichtete) Ellipse. Die Funktion zahn zeichnet ein vereinfachtes Zahnrad in Form einer Zickzacklinie. Die Aufrufparameter definieren der Reihe nach: x- und y-Koordinaten des Mittelpunkts, Radius, Zahnhöhe, Phasenwinkel, Zähneanzahl und Linienfarbe. Die Methode kolben zeichnet schließlich den Kolben in angenäherter Form, indem sie drei Ellipsenabschnitte aus vieleckigen Polygonen passend aneinander klebt. (Im Gegensatz zum Zylinder des Wankelmotors ist die geschlossene Darstellung des Kolbens von der Mathematik her recht anspruchsvoll und die Berechnung zu aufwändig für eine Animation – die Ellipsenabschnitte ergeben eine brauchbare Annäherung. Leider lässt sich die Circle-Funktion für das Zeichnen der Ellipsen nicht einsetzen, da Basic Ellipsen nur mit horizontal/vertikal ausgerichteten Achsen zeichnet) Die Parameter der Funktion sind der Reihe nach: x- und y-Koordinaten des Mittelpunkts, Phasenwinkel, Linienfarbe.
Die portierte Fassung Die portierte Fassung WankelAnimation des Codes unterscheidet sich nicht sonderlich von der originalen Fassung, wenn man das zuvor vorgestellte Modul QBEmulation zur Verfügung hat. Änderungen und Ergänzungen erscheinen im Listing fettgedruckt. '*********************************************************************** ' Formularmodul : WankelAnimation ' Autor : 2000 Rudolf Huttary ' Beschreibung : Demonstriert Ablauf im Inneren des Wankelmotors '*********************************************************************** Rem Konstanten Const pi = 3.141593 Const pi3 = pi / 3 Const pi6 = pi / 6 Const pi2 = 2 * pi Const r = 10 ' Radius Kolbenzahnrad Const r2 = r * 2 / 3 ' Radius inneres Zahnrad Const b = r – r2 Const mx = 0 ' Mittelpunktsverschiebung Const my = 0 ' Mittelpunktsverschiebung Const w = 3 ' Drehkolben-Faktor Const z2 = 48 ' Anzahl der Zahnräder äußeres Rad Const z1 = z2 * r2 / r ' Anzahl der Zahnräder inneres Rad
47 2
Die portierte Fassung
Const wi = 0 ' Winkelgeschwindigkeit inneres Rad (0 bei Radius = 2/3) Const w2off = 0 '+ pi / z1 ' Phasenverschiebung inneres Rad Dim st, x1, x2
Von WANKEL.BAS zur WankelAnimation
' Teil der Emulation Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer) If Shift = 0 Then ' Funktionstasten ignorieren InkeyChar = Chr(0) + Chr(KeyCode) ' Tastaturcode merken End If End Sub ' Teil der Emulation Private Sub Form_KeyPress(KeyAscii As Integer) InkeyChar = Chr(KeyAscii) ' Zeichen merken End Sub Private Sub Form_Load() InitQB Me Timer1.Interval = 100 st = -pi / 60 ' x1 = 0 ' x2 = 0 ' Screen 12 Print "Zähne:"; z1; "/"; Window -32, -24, 32, 24 PSet (mx, my) ' zylinder mx, my ' End Sub
Schleifeninkrement, Animationsgeschwindigkeit Animationswinkel Animationswinkel z2; "schneller/langsamer: Cursor, Ende: Esc" Mittelpunkt Zylinder zeichnen
Sub kolben(mx, my, winkel, sicht) Dim r1, w1, w2, w3, ef, c1, c2, c3, r1 = Sqr(3) * r * 1 * 0.97 w1 = winkel + pi3 w2 = w1 + pi3 * 2 w3 = w2 + pi3 * 2 ef = 0.26 * r2 / r c1 = Cos(w1): s1 = Sin(w1) c2 = Cos(w2): s2 = Sin(w2) c3 = Cos(w3): s3 = Sin(w3) a = x = y = xx1 xx2 xx3
0 r1 * Sin(a) * ef + r1 * Cos(a) = x * c1 – y * s1: = x * c2 – y * s2: = x * c3 – y * s3:
r1 / 1.75
a, ' ' ' ' ' ' ' '
x, y, x1, x2, x3 Ellipsenradius +60° , 1. Teilellipse +120°, 2. Teilellipse +120°, 3. Teilellipse Ellipsenfaktor Winkelwerte für Drehung Winkelwerte für Drehung Winkelwerte für Drehung
' Ellipse ' Ellipse yy1 = x * s1 + y * c1 yy2 = x * s2 + y * c2 yy3 = x * s3 + y * c3
y-Anteil mit Verschiebung y-Anteil ' Start Teilellipse 1 ' Start Teilellipse 2 ' Start Teilellipse 3
For a = 0 To pi * 1.01 Step pi / 10 ' Schleife für halbe Ellipse x = r1 * Sin(a) * ef + r1 / 1.75 ' Ellipse y-Anteil mit Verschiebung y = r1 * Cos(a) ' Ellipse y-Anteil
47 3
Von WANKEL.BAS zur WankelAnimation
Von W ANKEL. BAS
zur W ankelAnimation
x1 = x * c1 – y * s1: y1 = x * s1 + x2 = x * c2 – y * s2: y2 = x * s2 + x3 = x * c3 – y * s3: y3 = x * s3 + Line (xx1 + mx, yy1 + my)-(x1 + mx, QBColor(sicht) Line (xx2 + mx, yy2 + my)-(x2 + mx, QBColor(sicht) Line (xx3 + mx, yy3 + my)-(x3 + mx, QBColor(sicht) xx1 = x1: xx2 = x2: xx3 = x3 yy1 = y1: yy2 = y2: yy3 = y3 Next a End Sub
y * c1 ' Drehung Teilellipse 1 y * c2 ' Drehung Teilellipse 2 y * c3 ' Drehung Teilellipse 3 y1 + my), _ ' Teilellipse 1 y2 + my), _ ' Teilellipse 2 y3 + my), _ ' Teilellipse 3
Sub zahn(mx, my, r, h, w, anz, sicht) dw = pi / anz rz1 = r – h / 2 rz2 = r + h / 2 Min = 1000 PSet (mx + Cos(w – dw) * rz1, my + Sin(w – dw) * rz1), QBColor(sicht) For y = 0 To anz x = w + 2 * dw * y zx1 = mx + Cos(x + dw) * rz1 zy1 = my + Sin(x + dw) * rz1 di = zx1 * zx1 + zy1 * zy1 If Min > di Then Min = Sqr(di): dx = zx1: dy = zy1 End If Line -(mx + Cos(x) * rz2, my + Sin(x) * rz2), QBColor(sicht) Line -(zx1, zy1), QBColor(sicht) Next End Sub Sub zylinder(mx, my) r0 = 2 * r PSet (r0 * Cos(x) + mx + b * Cos(x * w), r0 * Sin(x) + my + b * Sin(x * w)) For x = 0 To pi2 + pi / 40 Step pi / 40 Line -(r0 * Cos(x) + mx + b * Cos(x * w), r0 * Sin(x) + my + b * Sin(x * w)) Next x End Sub Private Sub Timer1_Timer() ' Do ' Animationsschleife kolben mx + b * Cos(x2 * w), my + b * Sin(x2 * w), x2, 15 kolben mx + b * Cos(x1 * w), my + b * Sin(x1 * w), (x1), 0 zahn mx + b * Cos(x2 * w), my + b * Sin(x2 * w), r, r / 10, x2, z2, 15 zahn mx + b * Cos(x1 * w), my + b * Sin(x1 * w), r, r / 10, x1, z2, 0 zahn mx, my, r – b, r / 10, wi * x1, z1, 0
47 4
Die portierte Fassung
Als einzige Abweichung von der ursprünglichen Programmlogik ist die Umsetzung der INKEY$Schleife in eine Timer-Behandlungsroutine zu bemerken. Eine Beibehaltung der ursprünglichen Logik wäre vom Prinzip her zwar möglich, jedoch mit Rücksicht auf das Multitasking alles andere als sinnvoll. Das Zeitgeber-Steuerelement hat den Vorteil, dass es eine recht stabile und systemunabhängige Zeitbasis für die Abfolge der einzelnen Animationsphasen bereitstellt.
475
Von WANKEL.BAS zur WankelAnimation
a$ = InKey$ ' Tastaturinteraktion If a$ <> "" Then taste = Asc(Right$(a$, 1)) Select Case taste Case vbKeyLeft: st = st + 0.2 * Abs(st) + 0.001 ' Cursor rechts Case vbKeyRight: st = st – 0.2 * Abs(st) – 0.001 ' Cursor links ' Abbruch Case 27, vbKeyEnd: Unload Me: Exit Sub End Select End If x2 = x1 ' alter Winkel x1 = x1 + st ' Schleifeninkrement ' Loop End Sub
Mathematik und Algorithmen Wer programmiert, tut gut daran, mit der Mathematik auf gutem Fuße zu stehen. Der geschickte und sichere Umgang mit den mathematischen Funktionen der Sprache bringt nicht nur Vorteile für die saubere Implementation schneller und sicherer Algorithmen, er ist zuweilen sogar Voraussetzung, wenn es um die Bewältigung kniffliger Angelegenheiten geht. Da wären zum einen die einfachen linearen Transformationen (Translation und Skalierung), wie sie für den Übergang zwischen verschiedenen Koordinatensystemen auftreten. Um die Anzahl solcher Transformationen klein zu halten, empfiehlt es sich, im Ausgabebereich (Form, Printer, PictureBox, UserControl, UserDocument) mittels Scale das Koordinatensystem so zu wählen, dass keine unübersichtlichen und zeitaufwändigen Umrechnungen erforderlich werden. Während die vordefinierten Koordinatensysteme, die man durch Setzen der Eigenschaft ScaleMode auf eine der vordefinierten Konstanten (vbPixels, vbMillimeters, ...) einstellt, ihren Ursprung links oben in der Ecke der Ausgabefläche haben und nichts weiter als unterschiedliche Skalierungsmaße für die Koordinatenachsen vorgeben, lassen sich mittels Scale Koordinatensysteme mit unterschiedlich orientierten sowie beliebig skalierten Koordinatenachsen bei freier Wahl des Ursprungs einstellen. Man übergibt Scale einfach die Koordinaten des linken oberen Punkts und des rechten unteren Punkts der Ausgabefläche in ihrer aktuellen Gestalt (vgl. Height, Width) und definiert damit den Ausschnitt, die Skalierung, die Orientierung und den Ursprung in einem Aufwasch. Was die Sprache nicht vorsieht, ist eine zusätzliche Drehung der Koordinatenachsen aus der Senkrechten bzw. Waagrechten heraus. Da hilft nur die im vorigen Abschnitt vorgestellte Drehung – nachträglich auf die Punktkoordinaten angewandt. Darüber hinaus ist das mathematische Vokabular der Sprache reichhaltig genug, um selbst komplexeste Berechnungen anstellen zu können. Ein Anwendungsgebiet ist beispielsweise die Visualisierung von Funktionsverläufen mittels zweidimensionaler Funktionsgraphen. Nimmt man die Farbinformation als »dritte« Dimension, lassen sich recht aussagekräftige Darstellungen errechnen. Vom Prinzip her führt man dazu den Graphen (gegebenenfalls durch Projektion) auf eine der folgenden Darstellungen zurück. 1. eindimensionale Abbildungen (aber auch Relationen), bei denen ein Wert aus einem Definitionsbereich einem Wert aus einem Wertebereich zugeordnet wird. Für die Darstellung werden Definitions- und Wertebereich je einer Achse zugeordnet, so dass sich ein Kurvenverlauf ergibt (Beispiele dazu finden Sie im Referenzteil in den Abschnitten »Resize-Methode« und »ScaleLeft-Eigenschaft und ScaleTop-Eigenschaft«, wo es um die Ausgabe einer Sinuskurve bzw. einer approximierten Rechteckkurve geht). Die Kurve wird entweder aus Punkten zusammengesetzt oder aus Liniensegmenten (Polygon). 2. zweidimensionale Abbildungen, bei denen ein zweidimensionaler Wertebereich auf einen eindimensionalen Wert abgebildet wird. Die Darstellung des eindimensionalen Werts erfolgt entweder als Farbwert (vgl. das Projekt Apfelmann, in diesem Abschnitt) oder als Höhenwert bei perspektivischer Projektion (Kurvenschar erscheint dann als »Landschaft«). 3. zweidimensionale Abbildungen, bei denen ein eindimensionaler Definitionsbereich in einen zweidimensionalen Wertebereich abgebildet wird. Die Darstellung zeigt häufig nur den Kurvenverlauf im Wertebereich, kann aber auch nach dem vorgenannten Prinzip erfolgen. Ein Beispiel dafür ist die Kreisfunktion, bei der ein Winkelwert in der einen Dimension auf den Sinus und in der anderen auf den Cosinus des Winkels abgebildet wird – speziell bei der Kreisfunktion lässt sich der Winkel sogar direkt im Wertebereich ablesen, das ist aber nicht die Regel (vgl. die Darstellung des Wankel-Zylinders sowie der Ellipsenabschnitte im vorherigen Abschnitt »Das Programm WANKEL.BAS«). Unter Algorithmus wird landläufig eine »Vorschrift für die Ausführung einer komplexen Operation« verstanden. Auch wenn sich im weiteren Sinne jede Prozedur/Funktion unter diesem Begriff subsumieren lässt, spricht man eigentlich nur dann von einem Algorithmus, wenn es um
477
ApfelmannZoom – eine Fahrt durch die Mandelbrotmenge
ApfelmannZoom eine Fahrt durch die Mandelbrotmenge
die Implementation eines spezifischen Verfahrens geht, das entweder metasprachlich (etwa als mathematische Formel) oder natürlichsprachlich (als Anleitung im Sinne eines Kochrezepts) beschrieben ist. Zu den klassischen Algorithmen zählen Sortierverfahren wie Bubble-Sort (vgl. den Abschnitt »Schleifen«, S. 40) oder Quick-Sort, Iterationsverfahren etwa für die Integralberechnung oder die numerische Approximation, aber auch Berechnungen verschiedenster Art, wie die von Primzahlen (vgl. »Funktionen selbst definieren«), Determinanten, Folgengliedern oder Reihensummen. Wie viel Komplexität und auch »Anmut« hinter einer einfachen Rechenvorschrift stecken kann, demonstriert auf besonders eindrucksvolle Weise die Mandelbrotmenge, deren Graph aufgrund seiner Form als »Apfelmännchen« bezeichnet wird. (Ein wenig mehr Mathematisches findet sich im Abschnitt »Von WANKEL.BAS zur WankelAnimation », S. 468, sowie im Abschnitt »3DAnimation – Drahtgittermodelle frei im Raum gedreht«, S. 577).
ApfelmannZoom eine Fahrt durch die Mandelbrotmenge Die von dem Mathematiker Benoit B. Mandelbrot im Zuge seiner Beschäftigung mit so genannten »Julia«-Mengen Anfang der Siebzigerjahre entdeckte Mandelbrotmenge zählt heute noch zu den augenscheinlichsten Ergebnissen der Chaosforschung und dürfte der noch jungen Wissenschaft zusehends auf die Beine geholfen haben. Mandelbrot benutzte damals einen IBM-Computer, um die Menge erstmals zu visualisieren, und war umgehend von ihrer »bizarren Schönheit« bezaubert. Der mathematische Kern der Mandelbrotmenge ist wahrlich einfach. Man untersucht eine parametrisierte Folge im komplexen Zahlenraum daraufhin, ob sie konvergiert oder divergiert. Die Folgenvorschrift lautet: f0
=
0
fn+1
=
fn² + z
wobei der Parameter z aus der komplexen Ebene gewählt wird. Für die Visualisierung der Menge untersucht man das Verhalten der Folge fn, indem man z systematisch in einem bestimmten (um die Null herum gelegenen) Bereich variiert und für jedes z bis zu cMaxIterat (also endlich viele) Folgenglieder berechnet. Übersteigt der Betrag eines Folgenglieds einen hinreichend groß gewählten Grenzwert cGrenze, bricht man die Iteration ab und wertet die zu c gehörige Folge als divergent – z gehört dann nicht zur Mandelbrotmenge. Die Anzahl der Iterationen, die bis zum Abbruch nötig waren, trägt man als Farbwert für den Punkt z in der komplexen Ebene an. Sind dagegen für ein z alle Folgenglieder vom Betrag her kleiner gleich dem Grenzwert, wertet man die zu z gehörige Folge als konvergent und z als Element der Mandelbrotmenge. Das Ergebnis dieser Vorschrift gehört zu den »Bildern, die um die Welt gingen«. Das Projekt ApfelmannZoom, mit dem die abgebildeten Bildschirme erzeugt wurden, hat – wie der Titel bereits verrät – neben dem reinen Algorithmus für die Berechnung der Mandelbrotmenge noch eine recht komfortable Zoomfunktion zu bieten. Um sich in das Bild hineinzuzoomen, markiert man den gewünschten Ausschnitt mit der Maus unter Verwendung der linken Maustaste. Das geht auch wiederholt, bis die Rechengenauigkeit des Datentyps Double erschöpft ist und das Bild streifig wird. Ein Klick mit der linken Maustaste bringt dagegen das jeweils zuvor berechnete Bild wieder zum Vorschein. Einer Fahrt durch die Mandelbrotmenge steht also nichts mehr im Wege – nun ja, allzu rasant dürfte sie nicht werden, denn unter Basic ist das Laufzeitverhalten des Programms nicht gerade als überzeugend zu bezeichnen. Je nach Größe des Formulars müssen Sie auf einem halbwegs vernünftigen System schon zwischen 10 und mehreren hundert Sekunden je Bild veranschlagen.
478
ApfelmannZoom eine Fahrt durch die Mandelbrotmenge
ApfelmannZoom – eine Fahrt durch die Mandelbrotmenge 479
ApfelmannZoom – eine Fahrt durch die Mandelbrotmenge
ApfelmannZoom eine Fahrt durch die Mandelbrotmenge
Ausgabe des Programms Apfelmann
Ein Kompilieren des Programms mit allen Beschleunigungsoptionen halbiert zwar die Ausführungszeit in etwa, so richtig Beine macht es dem Programm aber auch nicht. Vor einer eingehenderen Diskussion der Implementation zunächst das Listing im Überblick: '*********************************************************************** ' Formularmodul : ApfelmannZoom ' Autor : 2000 Rudolf Huttary ' Beschreibung : Zeichnet Mandelbrotmenge ' : Zoomfähig, Bereichsauswahl mit der Maus '*********************************************************************** ' Konstanten für Startbereich Const cx1 As Single = -2.1 Const cy1 As Single = -1.4 Const cx2 As Single = 0.7 Const cy2 As Single = 1.4 ' Konstanten für Algorithmus Const cMaxIterat As Double = 200 Const cGrenze As Double = 10000 Const cZoom = "Apfelmann – Zoomstufe " ' Gobale Variablen für Bildschirmpunkte Private PixelsX, PixelsY ' Globale Variablen für Gummiband Private MouseStartX As Single, MouseEndX As Single Private MouseStartY As Single, MouseEndY As Single Private Tracking As Boolean Private Break As Boolean
480
ApfelmannZoom eine Fahrt durch die Mandelbrotmenge
' Undo-Info für Zoomfunktion Dim Stack()
ApfelmannZoom – eine Fahrt durch die Mandelbrotmenge
Private Sub Form_Load() ' Verhältnis Höhe/Breite richtig stellen Height = ScaleWidth * (cy2 – cy1) / (cx2 – cx1) + _ (Height – ScaleHeight) ReDim Preserve Stack(4, 0) ' Stack anlegen Caption = cZoom & 0 ' Zoomstufe ausgeben ScaleMode = vbPixels ' Anzahl der Bildpunkte merken PixelsX = ScaleWidth PixelsY = ScaleHeight AutoRedraw = True Scale (cx1, cy1)-(cx2, cy2) ' Startbereich einstellen Show ' Damit man das Zeichnen sieht Apfel End Sub Private Sub Form_MouseDown(Button As Integer, Shift As Integer, _ X As Single, Y As Single) Dim Index As Integer If Button = vbLeftButton Then ' Hineinzoomen? Tracking = True ' Gummiband einschalten MouseStartX = X ' Start- und Endkoordinaten ' vorgeben MouseStartY = Y MouseEndX = X MouseEndY = Y Else ' Herauszoomen! PopFromStack End If End Sub Private Sub Form_MouseMove(Button As Integer, Shift As Integer, _ X As Single, Y As Single) Dim dm If Tracking Then ' Verhältnis Höhe/Breite für Aussschnitt erzwingen Y = (MouseStartY + Sgn(Y – MouseStartY) * Abs(X – MouseStartX) * _ (ScaleHeight / ScaleWidth)) ' altes Gummiband löschen und dann neu zeichnen dm = DrawMode ' alten Zeichenmodus merken DrawMode = vbInvert ' XOR-Zeichenmodus einschalten Line (MouseStartX, MouseStartY)-(MouseEndX, MouseEndY), , B Line (MouseStartX, MouseStartY)-(X, Y), , B DrawMode = dm ' alten Zeichenmodus restaurieren MouseEndX = X: MouseEndY = Y ' vorläufige Endkoordinaten merken End If End Sub
481
ApfelmannZoom eine Fahrt durch die Mandelbrotmenge
Private Sub Form_MouseUp(Button As Integer, Shift As Integer, _ X As Single, Y As Single) If Tracking Then Tracking = False ' Gummiband ausschalten dm = DrawMode ' alten Zeichenmodus merken DrawMode = vbInvert ' XOR-Zeichenmodus einschalten
ApfelmannZoom – eine Fahrt durch die Mandelbrotmenge
' Gummiband löschen Line (MouseStartX, MouseStartY)-(MouseEndX, MouseEndY), , B DrawMode = dm ' alten Zeichenmodus restaurieren ' x-Koordinaten ggf. vertauschen If MouseStartX = X Or MouseStartY = Y Then Exit Sub If MouseStartX > MouseEndX Then MouseEndX = MouseStartX MouseStartX = X Else MouseEndX = X End If ' y-Koordinaten ggf. vertauschen If MouseStartY > MouseEndY Then MouseEndY = MouseStartY MouseStartY = Y Else MouseEndY = Y End If ' Zeichnen des gezoomten Bereichs vorbereiten PushOnStack Cls Scale (MouseStartX, MouseStartY)-(MouseEndX, MouseEndY) Apfel End If End Sub ' Prozedur zeichnet Mandelbrotmenge für geltendes Koordinatensystem Private Sub Apfel() Dim m_x1 As Double, m_x2 As Double, m_y1 As Double, m_y2 As Double Dim step_x As Double, step_y As Double Dim r1 As Double, re As Double, im As Double Dim zr As Double, zi As Double Dim it As Long Static Zoomstufe As Integer Zoomstufe = Zoomstufe + 1 ' Bereichsgrenzen und Schrittweiten ermitteln m_x1 = ScaleLeft m_x2 = ScaleLeft + ScaleWidth m_y1 = ScaleTop m_y2 = ScaleTop + ScaleHeight
482
ApfelmannZoom eine Fahrt durch die Mandelbrotmenge
step_x = (m_x2 – m_x1) / PixelsX step_y = (m_y2 – m_y1) / PixelsY
ApfelmannZoom – eine Fahrt durch die Mandelbrotmenge
For zr = m_x1 To m_x2 Step step_x ' alle Spalten in x-Richtung For zi = m_y1 To m_y2 Step step_y ' alle Punkte in Spalte zr re = 0 ' Realteil initialisieren im = 0 ' Imaginärteil initialisieren For it = 0 To cMaxIterat ' Iteration für Punkt r1 = re * re – im * im + zr im = 2 * re * im + zi re = r1 If re * re + im * im > cGrenze Then ' divergent? PSet (zr, zi), it * 16 ' Ja: Punkt ausgeben Exit For End If Next it ' Iteration beendet Next zi ' Spalte beendet? ' Ereignisse verarbeiten. Abbruch, falls Formular geschlossen wurde ' oder Zoomstufe inzwischen zu klein ist If DoEvents = 0 Or Zoomstufe > UBound(Stack, 2) + 1 Then Zoomstufe = Zoomstufe – 1 Exit Sub Else ' Warten bis Zoomstufe erreicht While Zoomstufe < UBound(Stack, 2) + 1 If DoEvents = 0 Then Exit Sub ' Formular geschlossen? Wend End If Next zr ' Bild fertig? Zoomstufe = Zoomstufe – 1 End Sub Private Sub PushOnStack() Dim Index As Integer Index = UBound(Stack, 2) + 1 ReDim Preserve Stack(4, Index) ' Koordinatensystem merken Stack(0, Index) = ScaleLeft Stack(1, Index) = ScaleTop Stack(2, Index) = ScaleWidth Stack(3, Index) = ScaleHeight Set Stack(4, Index) = Image Caption = cZoom & Index End Sub
' Push-Operation
' Bild merken
Private Sub PopFromStack() Dim Index As Integer Index = UBound(Stack, 2) If Index > 0 Then ' Stack leer? ' Nein: Koordinatensystem wiederherstellen
483
ApfelmannZoom eine Fahrt durch die Mandelbrotmenge
ApfelmannZoom – eine Fahrt durch die Mandelbrotmenge
ScaleLeft = Stack(0, Index) ScaleTop = Stack(1, Index) ScaleWidth = Stack(2, Index) ScaleHeight = Stack(3, Index) ' Gespeichertes Bild zeichnen PaintPicture Stack(4, Index), ScaleLeft, ScaleTop ReDim Preserve Stack(4, Index – 1) ' Pop-Operation Caption = cZoom & Index – 1 End If End Sub
Einstiegspunkt in das Programm ist, wie sollte es anders sein, die Load-Routine. Sie initialisiert das Formular so, dass der Client-Bereich den über Konstanten definierten Startbereich für die Berechnung der Mandelbrotmenge verzerrungsfrei und vollständig abbilden kann. Dies beinhaltet eine Anpassung der Formularhöhe an die zur Entwurfszeit gewählte Formularbreite sowie eine entsprechende Wahl des Koordinatensystems. Darüber hinaus werden noch zwei globale Variablen mit der Anzahl der Bildpunkte in horizontaler und vertikaler Richtung initialisiert, was der Routine Apfel die Berechnung der Schrittweite für die komplexe Ebene erleichtert, sowie ein dynamisches Array Stack, das im Zusammenhang mit dem Speichern der für die Zoomfunktion erforderlichen Informationen seinen Namen alle Ehre macht. Die Ausgabe der jeweils aktuellen Zoomstufe erfolgt über die Caption-Eigenschaft in der Titelzeile des Formulars.
Implementation des Apfelmännchen- Algorithmus An der Routine Apfel ist eigentlich nur die Kernroutine für die Berechnung der einzelnen Elemente der Mandelbrotmenge interessant. Die beiden äußeren Schleifen samt Initialisierung sind obligatorischer Überbau ohne Besonderheiten. Der Iterationsalgorithmus für einen einzelnen Parameter z = zr + i zi ist eine exakte Umsetzung der Visualisierungsvorschrift in die Sprache Basic. re = 0 im = 0 For it = 0 To cMaxIterat r1 = re * re – im * im + zr im = 2 * re * im + zi re = r1 If re * re + im * im > cGrenze Then PSet (zr, zi), it * 16 Exit For End If Next it
' Realteil initialisieren ' Imaginärteil initialisieren ' Iteration für Punkt
' divergent? ' Ja: Punkt ausgeben
' Iteration beendet
Da Basic für komplexe Zahlen keinen passenden Datentyp unterstützt, findet die Berechnung komponentenweise unter Verwendung von Double-Variablen statt. Die Addition ist komponentenweise definiert und macht keine Probleme, einzig die Quadratur (bzw. Multiplikation) einer komplexen Zahl mag ein wenig seltsam anmuten, wenn man das hinter den komplexen Zahlen steckende Prinzip nicht kennt. Die Formel dafür lautet z² = zr² – zi² + i 2zizr. Der Betrag einer komplexen Zahl erhält man dagegen über das gewöhnliche Maß der euklidischen Ebene: |z| = (zr² + zi²)½. Da es sich bei dieser Iteration um die innerste Schleife einer mehrfach verschachtelten Schleife handelt, sollte man natürlich alles dafür tun, das Laufzeitverhalten so günstig wie möglich zu gestalten. Viel Zeit (ca. 50 Prozent) geht allerdings in der PSet-Anweisung verloren, die jeden Punkt einzeln zum Bildschirm »befördert«. PSet erwartet als Farbe
484
Optimierung
einen Long-Wert, bei dem die unteren drei Byte die Sättigung für die Farbkomponenten Rot, Grün und Blau enthalten. Grundlegende Information für die Farbgestaltung ist die Zählvariable it. Sie gibt an, wie schnell die Folge divergiert. Wie die Farbabbildung dann im Einzelnen aussieht, fällt in die Kategorie künstlerische Freiheit. Die Multiplikation mit 16 ist nur eine Variante, wer es bunt liebt, könnte die Anweisung auch so gestalten: PSet (zr, zi), QBColor(it Mod 16)
Oder, wie wäre es mit einer geheimnisvollen grünen Korona? PSet (zr, zi), RGB((it Mod 16) * 16, it * 4, it)
Der Code zeigt verschiedene Optimierungen, die für einen guten Programmierer selbstverständlich sein sollten. So ist beispielsweise die einfache Multiplikation um einiges schneller als die Quadratur mit dem Operator ^, und Zählvariablen vom Typ Long sind schneller als Zählvariablen aller anderen Typen. Auch macht es wenig Sinn, die Wurzel bei der Berechnung des Betrags mittels Sqr zu ziehen, da sich der Vergleichswert ja auch auf das Quadrat des Werts trimmen lässt. Das Zwischenspeichern des zuerst errechneten Realteils in der Variable r1 ist notwendig, da der ursprüngliche Wert noch für die Berechnung des Imaginärteils zi benötigt wird. Analysiert man den Code auf weitere Möglichkeiten für die Optimierung, zeigt sich, dass sich noch einige Multiplikationen durch einfaches Zwischenspeichern einsparen lassen. Das bringt zwar nicht gerade viel, dafür dass es zwei weitere Variablen zzr und zzi erfordert und den Code doch einigermaßen entstellt, aber warum sollte man das Prozent an Laufzeitgewinn verschenken? re = 0 im = 0 zzr = 0 zzi = 0 For it = 0 To cMaxIterat im = 2 * re * im + zi re = zzr – zzi + zr zzr = re * re zzi = im * im If zzr + zzi > cGrenze Then PSet (zr, zi), it * 16 Exit For End If Next it
' Realteil initialisieren ' Imaginärteil initialisieren
' Iteration für Punkt
' divergent? ' Ja: Punkt ausgeben
' Iteration beendet
Ereignisbehandlung mit DoEvents, ein komplexes Problemfeld Ein wichtiger Punkt im Zusammenhang mit zeitaufwändigen Algorithmen ist die Aufrechterhaltung der Reaktivität des Programms gegenüber Benutzeraktionen. Damit der Benutzer nicht das Gefühl bekommt, das Programm sei abgestürzt oder reagiere nur schleppend auf seine Aktionen, empfiehlt es sich, beim Programmentwurf auf die so genannte Zehntelsekundenregel zu achten. Sie besagt, dass ein Ereignis nicht viel länger als eine Zehntelsekunde auf seine Behandlung warten sollte. Da Visual Basic die Behandlungsroutine für ein Ereignis standardmäßig erst dann aufruft, wenn die Behandlung des vorherigen Ereignisses abgeschlossen ist, sollte demnach jede Behandlungsroutine dafür sorgen, dass sie mit 100 Millisekunden an Laufzeit auskommt. Das ist leichter gesagt als getan: Insbesondere eine Routine wie Apfel, egal von welcher Behandlungsroutine sie aufgerufen wird, dürfte mit ihrer mehrsekündigen bis mehrminütigen Laufzeit einen doch recht erheblichen Verstoß gegen diese Regel darstellen. Was also tun, wenn
485
ApfelmannZoom – eine Fahrt durch die Mandelbrotmenge
Optimierung
ApfelmannZoom – eine Fahrt durch die Mandelbrotmenge
ApfelmannZoom eine Fahrt durch die Mandelbrotmenge
der Zeitbedarf einer Routine die Grenze der Zumutung übersteigt? Als Lösung bietet sich der Einsatz der Anweisung DoEvents an. Sie kann als Prozedur oder als Funktion aufgerufen werden und übergibt die Kontrolle temporär an den Ereignisbehandlungsmechanismus des Laufzeitsystems, um die Behandlung des nächsten eventuell anstehenden Ereignisses einzuschieben. Falls kein Ereignis ansteht, geht die Kontrolle umgehend, ansonsten nach Behandlung des nächsten anstehenden Ereignisses an die unterbrochene Routine zurück. Beachtet man die Zehntelsekundenregel, ist der beste Platz für DoEvents nicht nach der innersten Schleife (das wäre Laufzeitverschwendung), sondern nach der zweitinnersten, also jeweils nach Beendigung einer Spalte. So weit, so gut. Bei näherer Betrachtung stellt sich aber gleich die Frage, was passiert, wenn der Benutzer das Formular schließt, bevor die auf diese Weise unterbrochene Routine mit ihrer Arbeit fertig ist und (wie im vorliegenden Fall) das Formular weiterhin benötigt? Nun, Visual Basic lädt das entladene Formular beim nächsten Zugriff auf eine seiner Eigenschaften oder Methoden implizit erneut, mit dem Effekt, dass als Nächstes ein Load-Ereignis zur Behandlung ansteht. Im vorliegenden Programm würde das Load-Ereignis dummerweise erneut die Routine Apfel ausführen, noch ehe der alte Aufruf derselben Routine fertig ist. Da sowohl Apfel als auch DoEvents einen rekursiven Aufruf nicht übelnehmen, wäre das vom Prinzip her kein Problem, wenn nicht die »Befehlsverweigerung« dabei herauskommen würde. Abhilfe schafft der Aufruf in der folgenden Form: If DoEvents = 0 Then Exit Sub
Als Funktion aufgerufen liefert DoEvents nämlich genau dann den Wert 0 zurück, wenn kein Formular der Anwendung mehr offen oder sichtbar ist, das eine Nachrichtenbehandlung vornimmt. Damit die Sache auch funktioniert, muss man jedoch beim Codedesign entweder darauf achten, dass auch in der rufenden Routine – im vorliegenden Fall also in Form_Load bzw. in Form_MouseUp – keine Zugriffe auf Eigenschaften oder Methoden des Formulars mehr erfolgen, denn das hätte natürlich gleichsam den »Stehaufmänncheneffekt«, oder man führt die den DoEvents-Aufruf enthaltende Routine als Funktion aus und lässt sie beispielsweise False zurückgeben, wenn das Formular nicht mehr existiert. (Ein Beispiel für Letzteres findet sich im Projekt DiaProjektor.) Wer das beherzigt, bekommt immerhin schon einmal einen sicheren Programmabbruch hin, gegen einen rekursiven Aufruf der unterbrochenen Routine ist er aber noch nicht gefeit – warum auch, wenn dieser vom Prinzip her kein Problem bereitet. Natürlich kann der rekursive Aufruf, wie im Beispielprogramm, auch zum Problem werden. Dazu folgendes Szenario: Starten Sie das nur mit dieser einfachen DoEvents-Logik ausgestattete Beispielprogramm ApfelmannZoomTest und wählen Sie, noch ehe das Bild fertig gezeichnet ist, mit der Maus einen Teilbereich aus. Die Routine Apfel geht dann in die Rekursion und zeichnet den vergrößerten Ausschnitt. Wenn Sie nun warten, bis der Ausschnitt fertig gezeichnet ist und nach ca. einer weiteren Sekunde mittels der rechten Maustaste auf das Ausgangsbild zurückschalten, werden Sie sehen, dass die ursprünglich unterbrochene Routine weiterzeichnet. Leider hat die Routine sofort nach Fertigstellung des Ausschnittsbilds die Kontrolle zurückerhalten und ihre Arbeit noch während der Anzeige des Ausschnittsbilds wieder aufgenommen, so dass nun im Ausgangsbild ein gutes Stück fehlt. Im Ausschnittsbild selbst ist davon nichts (oder fast nichts) zu bemerken, da die meisten der berechneten Punkte entweder außerhalb liegen oder aufgrund der Koordinatensystemanpassung genau »ins Bild passen«. Wenn Sie dagegen auf das Ausgangsbild zurückschalten, noch ehe das Ausschnittsbild fertig gezeichnet ist, erhält die für das Ausschnittsbild zuständige Routine die Kontrolle zurück und zeichnet dieses im Ausgangsbild fertig. Das Ausgangsbild wird an dieser Stelle aufgrund von Rechenungenauigkeiten und mehrfachen Zeichnens einzelner Bildpunkte gerade am Rand der Mandelbrotmenge unscharf. Was also fehlt, ist eine gewisse Steuerung der Rekursion, die erstens einen noch nicht vollendeten Aufruf für einen Ausschnitt abbricht, wenn der Benutzer auf das Ausgangsbild zurückschaltet, und zweitens einen noch nicht vollendeten Aufruf für das Ausgangsbild lahm legt, solange
486
Benutzerschnittstelle
ein Ausschnitt davon angezeigt wird. Das klingt zwar kompliziert, lässt sich aber im Wesentlichen über eine zusätzliche statische (!) Variable Zoomstufe gepaart mit ein wenig Logik erschlagen.
Anhand eines Vergleichs zwischen der bei Eintritt und Austritt gepflegten Variable Zoomstufe und der Größe des Stacks für die gespeicherten Bilder erfährt die Routine, ob sie abbrechen soll, weil der Benutzer das Zeichnen des Ausschnitts unterbrochen und auf das Ausgangsbild zurückgeschaltet hat, oder ob sie warten soll, bis der Benutzer auf das zugehörige Bild zurückschaltet. Die Logik für das Warten ist als While-Schleife implementiert, die nichts weiter tut, als so lange DoEvents-Aufrufe auszuführen, bis die Zoomstufe zum aktuellen Zustand des Stacks passt oder (wie gehabt) der Benutzer das Formular schließt.
Benutzerschnittstelle Die von dem Programm verwendete Benutzerschnittstelle ist denkbar einfach: Die linke Maustaste zieht ein Gummiband auf und gestattet somit eine Bereichsauswahl unter Bezug auf das jeweils aktuelle Koordinatensystem. Einzelheiten zur Implementation des Gummibandes selbst finden Sie im Abschnitt »Gummiband – Bereiche interaktiv auswählen«, S. 492. Die vorliegende Implementation sorgt dafür, dass das Seitenverhältnis des ausgewählten Bereichs immer dem Seitenverhältnis des Client-Bereichs des Formulars entspricht. Damit errechnet sich die yEndkoordinate des Gummibands letztlich mehr oder weniger direkt aus der x-Endkoordinate: Y = (MouseStartY + Sgn(Y – MouseStartY) * Abs(X – MouseStartX) * _ (ScaleHeight / ScaleWidth))
Weiterhin sorgt eine geeignete Vertauschung der Start- und Endkoordinaten im Rahmen von Form_MouseUp dafür, dass die Orientierung gewahrt bleibt. Die mit dem Abschluss der Bereichsauswahl verbundene Aktion ist schließlich ein PushOnStackAufruf, der in Simulation einer Stack-Operation das Abspeichern des aktuellen Koordinatensystems sowie des Bildes übernimmt, gefolgt von einem Koordinatensystemwechsel und einem erneuten, gegebenenfalls auch rekursiven Aufruf der Routine Apfel. Beachten Sie, dass Apfel in dieser Implementation ohne Parameter auskommt, weil sich die Routine auf das aktuelle Koordinatensystem bezieht und somit einfach den aktuellen Client-Bereich als Bereichsgrenzen verwenden kann.
487
ApfelmannZoom – eine Fahrt durch die Mandelbrotmenge
Static Zoomstufe As Integer Zoomstufe = Zoomstufe + 1 ... ' Ereignisse verarbeiten. Abbruch, falls Formular geschlossen wurde ' oder Zoomstufe inzwischen zu klein ist If DoEvents = 0 Or Zoomstufe > UBound(Stack, 2) + 1 Then Zoomstufe = Zoomstufe – 1 Exit Sub Else ' Warten bis Zoomstufe erreicht While Zoomstufe < UBound(Stack, 2) + 1 If DoEvents = 0 Then Exit Sub ' Formular geschlossen? Wend End If Next zr ' Bild fertig? Zoomstufe = Zoomstufe – 1 End Sub
ApfelmannZoom eine Fahrt durch die Mandelbrotmenge
PushOnStack Cls Scale (MouseStartX, MouseStartY)-(MouseEndX, MouseEndY) Apfel
ApfelmannZoom – eine Fahrt durch die Mandelbrotmenge
Die rechte Maustaste hingegen stellt das Ausgangsbild für den aktuellen Ausschnitt wieder her. Die Aktion ist im Zuge von Form_MouseDown implementiert und besteht letztlich nur aus einem PopFormStack-Aufruf, der das Koordinatensystem wiederherstellt und das gespeicherte Ausgangsbild erneut in den Client-Bereich zeichnet. Dazu gleich mehr.
Der Stack Bleibt also noch das letzte etwas anspruchsvollere Konzept: der Stack. Als Speicher, der nach dem Prinzip des last-in-first-out funktioniert, ist er natürlich prädestiniert für die von dem Programm unterstützte Zoomfunktionalität. Seine Aufgabe besteht darin, die Folge der Ausgangsbilder und deren Bereichsinformationen abzuspeichern und bei Bedarf wieder zu restaurieren. Das Fertigzeichnen unvollständig gezeichneter Bilder ergibt sich aus der DoEvents-Logik der Apfel-Routine und benötigt keine zusätzliche Information, die im Stack abgelegt werden müsste. Die Implementation des Stacks liegt in Form eines auf Modulebene vereinbarten zweidimensionalen dynamischen Arrays vor. Von der Load-Routine angelegt, wird das Array bei jedem PushOnStack-Aufruf in der zweiten Dimension um ein Element vergrößert, so dass Platz für fünf weitere Feldwerte entsteht. Es mag Ihnen vielleicht ungewohnt vorkommen, die »wichtigere« Information an zweiter Position zu finden, da Visual Basic bei mehrdimensionalen dynamischen Arrays jedoch nur die letzte Dimension einer dynamischen Bereichsänderung zugänglich macht, muss man wohl oder übel damit leben. Auf einen Stackzeiger verzichtet die Implementation, da sich die gewünschte Information, wo das oberste Element zu finden ist, recht einfach über die UBound-Funktion ergibt. Für mehrdimensionale Arrays unterstützt diese Funktion einen optionalen zweiten Parameter, über den sich (von 1 beginnend) die Dimension, deren Grenze man ermitteln will, spezifizieren lässt – für LBound gilt natürlich Analoges. Nachdem als Elementtyp des Arrays Variant vereinbart ist, spielt es keine Rolle, dass die zu speichernde Information nicht durchwegs denselben Typ trägt. Das Bild erhält man über die ImageEigenschaft des Formulars, was allerdings voraussetzt, dass die AutoRedraw-Eigenschaft auf True gesetzt wurde. Und die Bereichsinformation ergibt sich aus den ScaleXXX-Eigenschaften. Hier der recht geradlinige Code der Routine: Private Sub PushOnStack() Dim Index As Integer Index = UBound(Stack, 2) + 1 ReDim Preserve Stack(4, Index) ' Koordinatensystem merken Stack(0, Index) = ScaleLeft Stack(1, Index) = ScaleTop Stack(2, Index) = ScaleWidth Stack(3, Index) = ScaleHeight Set Stack(4, Index) = Image Caption = cZoom & Index End Sub
' Push-Operation
' Bild merken
Das Gegenstück PopFromStack zu PushOnStack sieht nicht viel anders aus. Es kommt zum Aufruf, wenn der Benutzer die rechte Maustaste klickt, um auf das Ausgangsbild des aktuellen Ausschnitts zurückzuwechseln. Die Routine liest die zuoberst auf dem Stack gelegene Information unter Verwendung des »Stackzeigers« aus, stellt damit das Koordinatensystem wieder her und
488
Einzelbilder speichern
zeichnet schließlich das Bild mittels PaintPicture. Dabei ist es insbesondere von Vorteil, dass PaintPicture mit dem aktuellen Koordinatensystem arbeitet, was aufwändige Umrechnungen und Transformationen erspart. Vom Prinzip her könnte man das Bild auch als Hintergrundbild der Picture-Eigenschaft des Formulars zuordnen, doch das wäre nicht sauber, weil es dann gegen Cls resistent wäre und auch in neuen Ausschnitten erst einmal durchscheinen würde.
Einzelbilder speichern Falls Ihnen daran gelegen ist, das eine oder andere »Werk« des Programms dauerhaft auf Festplatte zu bannen, etwa um es weiterzuverwenden, können Sie dem Formular ein kleines Menü DATEI mit einem Befehl SPEICHERN spendieren und eine Behandlungsroutine implementieren, die ein Standarddialoge-Steuerelement zur Eingabe des Dateinamens aufruft und das Bild daraufhin mittels SavePicture abspeichert. Der Code dazu könnte etwa so aussehen. Private Sub mnuDateiSpeichern_Click() CommonDialog1.Filter = "Bitmap|*.bmp" CommonDialog1.DefaultExt = "*.bmp" CommonDialog1.ShowSave If CommonDialog1.FileName <> "" Then SavePicture Image, CommonDialog1.FileName End If End Sub
489
ApfelmannZoom – eine Fahrt durch die Mandelbrotmenge
Private Sub PopFromStack() Dim Index As Integer Index = UBound(Stack, 2) If Index > 0 Then ' Stack leer? ' Nein: Koordinatensystem wiederherstellen ScaleLeft = Stack(0, Index) ScaleTop = Stack(1, Index) ScaleWidth = Stack(2, Index) ScaleHeight = Stack(3, Index) ' Gespeichertes Bild zeichnen PaintPicture Stack(4, Index), ScaleLeft, ScaleTop ReDim Preserve Stack(4, Index – 1) ' Pop-Operation Caption = cZoom & Index – 1 End If End Sub
Formulare und Ansichten Für die Arbeit mit mehreren Formularen gibt es unter Visual Basic einen hierarchischen Ansatz, der den Einsatz eines MDI-Formularmoduls mit untergeordneten Formularen vorsieht sowie einen nichthierarchischen Ansatz, bei dem sich die einzelne SDI-Formulare wechselseitig steuern und synchronisieren. MDI-Formulare setzt man im Allgemeinen für Anwendungen ein, in denen der Benutzer mehrere Dokumente gleichen Typs bearbeiten können soll. Sie kennen das von Ihrer Textverarbeitung her. Ein einfaches, aber durchaus leistungsfähiges Beispielprojekt für ein MDI-Formular diskutiert der Referenzteil im Abschnitt »MDIForm-Objekt« (S. 313 ). Es trägt den Namen MDIProjekt. Mit Windows 9x ist die MDI-Anwendung jedoch wieder zunehmend ins Hintertreffen geraten, weil das 32-Bit-Multitasking die Ausführung mehrerer Instanzen ein und desselben Programms sehr einfach macht (und wohl auch, weil man herausgefunden hat, dass die meisten Anwender MDI-Anwendungen ohnehin nur wie SDI-Anwendungen nutzen). Die gewöhnlichen SDI-Formulare sind dagegen das Arbeitspferd des Visual-Basic-Programmierers, wenn es darum geht, Anwendungen zu implementieren, die nicht so recht in das hierarchische Modell passen wollen, weil sie entweder zu einfach gestrickt sind und abgesehen von »modalen Dialogen« (gebundene Anzeige) nur mit einem einzigen Formular auskommen oder eben das eher lose Zusammenspiel mehrerer Formulare erfordern. Es mag daher nicht verwundern, dass mehr als 90 Prozent der in Visual Basic implementierten Anwendungen von SDI-Formularen ausgehen, von den ActiveX-Komponenten einmal abgesehen, die gleichfalls der SDIProgrammierung zuzurechnen sind.
Ereignisbehandlung »Programmieren unter Windows heißt Ereignisse behandeln«. Wer von der prozeduralen Programmierung unter DOS oder UNIX her kommt, wird sich angesichts eines Visual-Basic-Programms erst einmal fragen, wo denn hier »hinten und vorne ist«. In der Tat ist das Umsatteln auf das Programmiermodell von Windows mitunter ein schwieriger Prozess, weil man eben nicht sieht, was hinter den Kulissen vor sich geht. Man muss sich darauf verlassen, dass ein Visual-Basic-Programm in bestimmten Situationen bestimmte Ereignisse signalisiert bekommt, und kann dann den Code sozusagen als Antwort auf diese Ereignisse gestalten. »Programmieren mit Ereignissen« heißt auch »Programmieren mit Objekten«. Nur Objekte können Ereignisse generieren sowie die Ereignisse anderer Objekte behandeln. Das erste Ereignis im Leben eines Objekts ist das Initialize-Ereignis. Das Eintreffen von Initialize sowie seines Gegenstücks Terminate, das letzte Ereignis im Leben eines Objekts, ist so sicher wie das Amen in der Kirche. Was die weiteren Ereignisse betrifft, so hängt es von der Ausstattung und Art des jeweiligen Objekts ab, welche Ereignisse es signalisiert bekommt. Ein Formularobjekt sieht nach dem Laden des ihm zugeordneten Fensters beispielsweise immer ein Load-Ereignis und beim Entladen des Fensters ein QueryUnload-Ereignis, gefolgt von einem Unload-Ereignis. Und da ein Formularobjekt sein Fenster auf das Initialize-Ereignis hin lädt (der Code dafür steckt hinter den Kulissen) folgt beim Starten eines Formulars unmittelbar auf das Initialize-Ereignis ein LoadEreignis. Formularobjekt und Fenster sind also als verschiedene Objekte zu betrachten, die wechselweise miteinander kommunizieren – bzw. sich gegenseitig »Ereignisse« mitteilen. Aus diesem Grund kann das Fenster eines Formularobjekts zur Laufzeit jederzeit mittels der UnloadMethode entladen und später wieder durch einfaches Ansprechen einer Eigenschaft oder Methoden des Formulars erneut geladen werden. Beide Vorgänge werden von den Ereignissen Load bzw. QueryUnload und Unload flankiert. Auf das Load-Ereignis folgen dann das ResizeEreignis und schließlich das Activate-Ereignis, wenn das Fenster des Formulars innerhalb der Anwendung zum aktiven Fenster wird.
491
Ereignisbehandlung
Ereignisbehandlung
Jedes Ereignis berichtet somit von einem bestimmten Vorkommnis, und Vorkommnisse gibt es üblicherweise eine ganze Menge im Leben eines Objekts. So wird das Formular, dessen Fenster das aktive Fenster ist, über jede Benutzeraktion mittels eines Ereignisses benachrichtigt. Die Mausereignisse sind Click, DblClick, MouseDown, MouseMove, MouseUp und die Tastaturereignisse KeyDown, KeyPress und KeyUp. Wieder andere Ereignisse berichten von Aktionen im Zusammenhang mit den Steuerelementen, die auf dem Formular platziert sind. Die meisten Steuerelementobjekte führen ein ähnliches Doppelleben wie Formularobjekte. Das heißt, sie arbeiten gleichfalls innig mit einem Fenster zusammen: dem Fenster, das ihre Darstellung enthält. Ereignisse, die vom Fensterobjekt eines Steuerelements signalisiert werden, gelten daher nicht dem Formularobjekt, sondern dem Steuerelementobjekt. Und so unterschiedlich wie die Natur eines Steuerelements sein kann, so unterschiedlich können auch die Ereignisse sein, die sein Fensterobjekt signalisiert. Ein Ereignis berichtet also, dass sich etwas ereignet hat. Das Programm kann auf dieses Ereignis reagieren, muss aber nicht. Das ist im Vergleich zur prozeduralen Programmierung ein großer Unterschied. Als Visual-Basic-Programmierer kümmert man sich nur um die Ereignisse, die für die Problemstellung relevant sind. Alles andere lässt man »ungehört verhallen«. »Sich um ein Ereignis kümmern« bedeutet: eine Behandlungsroutine dafür implementieren. Eine Behandlungsroutine wird in den meisten Fällen die von dem Ereignis überbrachten »Nachrichten« (Funktionsparameter) auswerten und dann so reagieren, wie es die Inhalte der Nachrichten erforderlich machen. (Dabei ist es durchaus erlaubt, ja sogar gute Programmiertechnik, wenn sich Ereignisbehandlungsroutinen gegenseitig aufrufen.) Die Behandlung des zu einem Menübefehl namens »Datei laden« gehörigen Click-Ereignisses wird also beispielsweise den Ladevorgang einer Datei auslösen, oder das Change-Ereignis einer Bildlaufleiste ein Verschieben der Ansicht im Fenster des Formulars. Nach diesem Prinzip baut sich das gesamte Programm auf. Es gibt keine zentrale Logik mehr (die gibt es natürlich schon, sie steckt aber gleichfalls hinter den Kulissen), die Programmlogik zerfällt vielmehr in ein Mosaik kleiner Teile, die jeweils als spezifische Lösung auf ein ganz spezifisches Problem ausgelegt sind und ihren bescheidenen Teil zum »Großen Ganzen« beitragen.
Gummiband Bereiche interaktiv auswählen Als recht vielseitiges Werkzeug ermöglicht die im Beispielprojekt Gummiband vorgestellte Implementation die grafisch orientierte Bereichsauswahl in Fenstern, etwa zum Zeichnen, zum Zoomen des Fensterinhalts oder zur Auswahl von Objekten. Zur Überraschung vieler Programmierer gehört das Gummiband aber weder zur Ausstattung von Visual Basic noch zu den Diensten des Betriebssystems. Es gilt also, selbst in die Tasten zu greifen. Die Implementierung ist nicht sehr schwer, und man kann dabei eine Menge über die Ereignisprogrammierung mit Windows lernen. Am häufigsten wird das Gummiband als »Verschieben des Mauszeigers bei gedrückter linker Maustaste« implementiert. Der gesamte Vorgang lässt sich (wie die meisten durch Benutzerinteraktion ausgelösten Vorgänge) in drei Phasen aufteilen: Start, Verlauf (mit Feedback für den Benutzer), Ende (mit Bestätigung oder Abbruch). Für das Gummiband sind diese Phasen 1. Drücken der linken Maustaste – markiert Startpunkt 2. Bewegung der Maus bei gedrückter linker Maustaste – zeichnet und löscht Rechteck 3. Loslassen der linken Maustaste – markiert Endpunkt, löscht Rechteck und leitet die mit dem Gummiband verbundene Funktion ein Jede dieser Phasen meldet Windows Ihrem Programm in Form entsprechender Ereignisse. Das Laufzeitsystem von Visual Basic verarbeitet diese Ereignisse automatisch, indem es für jedes die betreffende Ereignisprozedur aufruft – vorausgesetzt, das betroffene Formularobjekt enthält eine Implementation für diese Prozedur. Mit anderen Worten, drückt der Benutzer die Maus-
492
Gummiband Bereiche interaktiv auswählen
taste, kommt die Ereignisprozedur Form_MouseDown zum Aufruf. Lässt er sie wieder los, kommt Form_MouseDown zum Zuge. Ein Bewegen der Maus wird dagegen von Form_MouseMove-Aufrufen begleitet. (Windows zeigt Mausbewegungen durch ein richtig gehendes Stakkato von Ereignissen an, so dass die Animation des Gummibandes in sehr einfacher Weise in die Prozedur Form_MouseMove gepackt werden kann.)
Ereignisbehandlung
Bereichsauswahl mit dem Gummiband Für das Zusammenwirken der drei Prozeduren ist die Vereinbarung einiger globaler Variablen erforderlich. So für die Koordinaten des Start- und Endpunkts und für den Zustand »Maustaste gedrückt« – das Gummiband soll ja nur bei gedrückter Maustaste erscheinen. Hier die Implementierung: '*********************************************************************** ' Formularmodul: Gummiband ' Autor : 2000 Rudolf Huttary ' Beschreibung : demonstriert die Bereichsauswahl mit der Maus '*********************************************************************** ' Globale Variablen Private MouseStartX As Single, MouseEndX As Single Private MouseStartY As Single, MouseEndY As Single Private Tracking As Boolean Private Sub Form_MouseDown(Button As Integer, Shift As Integer, _ X As Single, Y As Single) Tracking = True ' Gummiband einschalten MouseStartX = X ' Start- und Endkoordinaten vorgeben MouseStartY = Y MouseEndX = X MouseEndY = Y End Sub
493
Ereignisbehandlung
Ereignisbehandlung
Private Sub Form_MouseMove(Button As Integer, Shift As Integer, _ X As Single, Y As Single) Dim dm If Tracking Then dm = DrawMode ' alten Zeichenmodus merken DrawMode = vbInvert ' altes Band löschen und dann neu zeichnen Line (MouseStartX, MouseStartY)-(MouseEndX, MouseEndY), , B Line (MouseStartX, MouseStartY)-(X, Y), , B MouseEndX = X: MouseEndY = Y ' vorläufige Endkoordinaten merken DrawMode = dm ' alten Zeichenmodus einstellen End If End Sub Private Sub Form_MouseUp(Button As Integer, Shift As Integer, _ X As Single, Y As Single) Dim dm Tracking = False ' Gummiband ausschalten MouseEndX = X: MouseEndY = Y ' nicht unbedingt erforderlich dm = DrawMode ' alten Zeichenmodus merken DrawMode = vbInvert Line (MouseStartX, MouseStartY)-(MouseEndX, MouseEndY), , B DrawMode = dm ' alten Zeichenmodus einstellen ' Einleiten der gewünschten Aktion, Zeichnen, Zoomen, Auswählen etc. ' Die Koordinaten sind (MouseStartX, MouseStartY)-(MouseEndX, MouseEndY) End Sub
Ein Beispiel für den konkreten Einsatz des Gummibandes entlang dieser Implementierung findet sich im Rahmen des Projekts ApfelmannZoom, wo es für die Auswahl des Zoombereichs zuständig ist. In Abweichung vom vorgestellten Code ist hier das Seitenverhältnis des Gummibandes jedoch fest vorgegeben, damit die relativen Verhältnisse der Fensterabmessungen beim Zoomen erhalten bleiben. Der Zustand »Maustaste gedrückt« lässt sich natürlich auch im Rahmen der Prozedur Form_MouseMove ermitteln, da der Parameter Button bei jedem Aufruf den aktuellen Zustand der Maustasten als Bitvektor übermittelt. In dem Vektor gibt Bit 0 den Zustand der linken, Bit 1 der rechten und Bit 2 der mittleren Maustaste an. Somit lässt sich das Gummiband vollständig auch in Form_MouseMove implementieren. Das Formular Gummiband1 zeigt eine mögliche Lösung: '*********************************************************************** ' Formularmodul: Gummiband1 ' Autor : 2000 Rudolf Huttary ' Beschreibung : demonstriert die Bereichsauswahl mit der Maus '*********************************************************************** ' Globale Variablen Private MouseStartX As Single, MouseEndX As Single Private MouseStartY As Single, MouseEndY As Single Private Tracking As Boolean Private Sub Form_MouseMove(Button As Integer, Shift As Integer, _ X As Single, Y As Single) Dim dm If Button And vbLeftButton Then ' Linke Maustaste gedrückt?
494
DDE- Verbindungen
Ereignisbehandlung
If Tracking Then ' Gummiband oder Startkoordinaten? dm = DrawMode DrawMode = vbInvert ' altes Band löschen und dann neu zeichnen Line (MouseStartX, MouseStartY)-(MouseEndX, MouseEndY), , B Line (MouseStartX, MouseStartY)-(X, Y), , B MouseEndX = X: MouseEndY = Y ' vorläufige Endkoordinaten merken DrawMode = dm Else ' Koordinaten initialisieren Tracking = True ' und Gummiband einschalten MouseStartX = X MouseStartY = Y MouseEndX = X MouseEndY = Y End If Else ' Maustaste nicht gedrückt If Tracking Then dm = DrawMode DrawMode = vbInvert Line (MouseStartX, MouseStartY)-(MouseEndX, MouseEndY), , B DrawMode = dm Tracking = False ' Gummiband ausschalten End If End If End Sub
Dieser Code leistet zwar das Gleiche, er ist aber nicht gerade »schön« und aufgrund der aufwändigen Fallunterscheidungen schlecht lesbar.
DDE- Verbindungen Der dynamische Datenaustausch (DDE) war die erste Möglichkeit für Anwendungen, substanziell miteinander zu kommunizieren. Er umfasst den Austausch von Befehlen und Daten zwischen Formularen und/oder Steuerelementen. Seit Einführung des dokumentenzentrierten OLE2.0 und der 32-Bit-Technologie wird das eher anwendungszentrierte DDE zwar nicht mehr oft eingesetzt, Visual Basic bietet dafür aber noch eine vollwertige Unterstützung. Hier ein Überblick über den Mechanismus: Eine DDE-Verbindung wird zwischen zwei Objekten hergestellt, von denen das eine als Quelle und das andere als Ziel bezeichnet wird. Beide Objekte müssen unterschiedlichen Prozessen angehören. Als Quelle kommen Objekte der Typen Form und MDIForm in Betracht und als Ziel Objekte der Typen Label, PictureBox und TextBox. Ein Formularobjekt kann bis zu 128 unterschiedliche DDE-Verbindungen gleichzeitig als Quelle bedienen. Eine Anwendung kann je nach Erfordernis in die Rolle eines DDE-Client und in die Rolle eines DDE-Servers schlüpfen – auch gleichzeitig. Datenaustausch durch DDE- Verknüpfung
Aus Sicht eines auf einem Formular DDEForm platzierten Steuerelements DEEQuelle sieht die automatische DDE-Verknüpfung wie folgt aus: Damit es als Datenlieferant fungieren kann, muss die Eigenschaft LinkMode seines Formularobjekts den Wert vbSource bzw. 1 aufweisen. Der Wert dieser Eigenschaft lässt sich zur Laufzeit jederzeit zwischen vbLinkNone und vbLinkSource hin und her schalten, sofern er nicht zur Entwurfszeit auf vbLinkNone bzw. 0 gesetzt wurde – dann ist er nämlich zur Laufzeit schreibgeschützt. Für das Steuerelement selbst gibt es keine Auflagen, das Formular erledigt alles.
495
Ereignisbehandlung
Wenn nun ein in einer anderen Anwendung gelegenes Zielobjekt DDEZiel seinen Wert mit dem Datenelement DDEQuelle des Quellobjekts automatisch bei jeder Wertänderung synchronisieren will, muss es eine automatische DDE-Verknüpfung einrichten. Der Code dafür ist:
Ereignisbehandlung
' ggf.bestehende Verbindung zurücksetzen DDEZiel.LinkMode = 0 DDEZiel.LinkTopic = "VBAnwendung|DDEForm" ' Anwendung und Formular DDEZiel.LinkItem = "DDEQuelle" ' Quelle festlegen! DDEZiel.LinkMode = vbAutomatic ' Verbindung aufbauen
Zu beachten gilt es dabei eigentlich nur, dass die Datentypen der beiden beteiligten Steuerelemente kompatibel sein müssen. Zudem sollten beide Anwendungen laufen, wenn die DDE-Verbindung durch Setzen der Eigenschaft LinkMode auf vbLinkAutomatic bzw. eröffnet wird. Der Wert von LinkTopic setzt sich aus dem Namen App.Title der Quellanwendung und dem LinkTopic-Wert des Quellobjekts (auch Thema genannt) zusammen – getrennt durch das Zeichen »|«. (App.Title wird unter PROJEKTEIGENSCHAFTEN auf der Seite ERSTELLEN gesetzt.) Als Wert von LinkItem muss schließlich der Bezeichner des Steuerelements (als Zeichenfolge) angegeben werden, das die Daten für die Verknüpfung liefern soll. Wie eine Analyse der LinkOpen-Ereignisse beim Verbindungsaufbau zeigt (vgl. die folgende Abbildung), ist das Formularobjekt Ansprechpartner des Zielobjekts, nicht das Steuerelement, das die Daten bereitstellt. Ist eine automatische DDE-Verbindung einmal eröffnet, bleibt die Synchronisation zwischen Quelle und Ziel so lange bestehen, bis entweder das Zielobjekt oder das Quellobjekt seine LinkMode-Eigenschaft auf vbLinkNone (0) setzt – ein Vorgang, der auf beiden Seiten das Ereignis LinkClose auslöst. Das Formularobjekt beendet mit diesem Schritt übrigens alle aktuell gehaltenen DDE-Verbindungen und nimmt neue Verbindungsanfragen erst wieder an, wenn LinkMode erneut den Wert vbLinkSource hat. DDE-Verbindungen, bei denen dem Formularobjekt untergeordnete Objekte als Zielobjekte auftreten, bleiben davon allerdings unberührt. Nicht immer ist die automatische Synchronisation einer Verknüpfung erwünscht. Aus diesem Grund lassen sich DDE-Verbindungen auch in den Modi »Manuell« und »Manuell mit Benachrichtigung« eröffnen. Der erste Modus ergibt sich, wenn LinkMode auf vbLinkManual gesetzt wird, und der zweite Modus, wenn LinkMode auf vbLinkNotify gesetzt wird. In beiden Fällen muss das Zielobjekt von sich aus die Methode LinkRequest ausführen, um den aktuellen Wert des Quellobjekts zu erhalten. Der Unterschied besteht darin, dass im zweiten Modus, wenn sich der Wert des Quellobjekts geändert hat, eine Benachrichtigung in Form eines LinkNotify-Ereignisses stattfindet, die im ersten Modus unterbleibt. Das Quellobjekt kann das Zielobjekt im Modus »Manuell mit Benachrichtigung« durch Aufruf der Methode LinkSend des die Daten liefernden Steuerelements auch außer der Reihe zur Aktualisierung auffordern. Das ist beispielsweise notwendig, wenn das Picture-Objekt eines Bildfelds verändert wurde. DDE-Verbindungen sind bilateral. Ein Zielobjekt kann daher auch den Spieß umdrehen und den Wert des Quellobjekts verändern, indem es die Methode LinkPoke ausführt. (Dies wird allerdings nicht von allen Anwendungen unterstützt.) Kommt es während einer DDE-Kommunikation zu einem DDE-spezifischen Fehler, etwa wenn das Datenformat nicht stimmt, tritt das Ereignis LinkError auf. DDE- Kommandos
Neben der einfachen Verknüpfung zwischen Quelle und Ziel bietet DDE einem Zielobjekt auch die Möglichkeit, Kommandos über eine geöffnete DDE-Verbindung an das Quellobjekt zu schicken. Ein solches DDE-Kommando ist nichts weiter als eine Zeichenkette, deren Interpretation keineswegs in irgendeiner Form standardisiert, sondern völlig im Ermessen der Quelle gelegen ist. Den Versand bewerkstelligt ein Aufruf der LinkExecute-Methode des Zielobjekts. Aufseiten
496
DDE- Verbindungen
des Quellobjekts macht sich das Kommando als LinkExecute-Ereignis bemerkbar, das die Kommandozeichenfolge als Parameter anbietet. Angenommen, Sie wollen ein Kommando »Beenden« implementieren, das eine andere DDEAnwendung aus der Ferne beendet. Der Name der DDE-Anwendung sei Quelle und der ihres Hauptformulars QuelleForm. Um das Kommando von einem Zielobjekt Ziel aus zu verschicken, öffnen Sie (falls noch nicht geschehen) eine DDE-Verbindung und rufen die Methode LinkExecute des Zielobjekts auf:
Auf der Gegenseite müssen Sie darauf achten, dass die LinkMode-Eigenschaft des Formulars QuelleForm (bereits beim Entwurf) auf vbLinkSource gesetzt ist. Der Rest ist nichts weiter als Ereignisbehandlung: Private Sub QuelleForm_LinkExecute(CmdStr As String, Cancel As Integer) If CmdStr = "Beenden" Then Unload Me ' Befehl interpretieren Cancel = False End If End Sub
Wenn die Quelle das Kommando erfolgreich ausgeführt hat, muss es den Cancel-Parameter explizit auf False oder 0 setzen, sonst bekommt es die Zielanwendung mit dem Laufzeitfehler 285, »Andere Anwendung führt die DDE-Methode oder -Operation nicht aus«, zu tun. DDE- Demo wenn Programme kommunizieren
Das Thema DDE kommt mittlerweile in der einschlägigen Literatur zu Visual Basic immer kürzer. Aus diesem Grund konzentriert sich das Beispielprojekt DDE-Demo nicht so sehr auf eine konkrete Problemstellung, sondern eher auf Grundsätzliches: Es führt die im vorangestellten Abschnitt erläuterten Techniken schlicht von der Implementationsseite her vor. Die Anwendung besteht aus einem einzelnen Formular namens Form1, auf dem vier Textfelder namens Quelle1, Quelle2, DDEZiel und DDEKommando enthalten sind. Zudem findet sich auf dem Formular eine Standardschaltfläche namens Command1. Startet man das Programm und klickt auf die Schaltfläche Command1 oder das Textfeld DDEZiel, versucht dieses, eine DDE-Verbindung zu der Anwendung »DDEDemo« mit dem Thema »Form1« herzustellen, was nicht glückt, aber doch zeigt, dass DDE-Verbindungen nicht reflexiv (d.h.: innerhalb ein und desselben Programms) hergestellt werden können. Folgt man dem Rat der Fehlermeldung und startet eine weitere Instanz des Programms (dazu muss es gegebenenfalls erst kompiliert werden), klappt der Verbindungsaufbau. Die folgende Abbildung zeigt eine laufende Sitzung mit zwei Instanzen des Programms. Um die Ausgaben im linken Teil des Formulars interpretieren zu können, ist es wichtig, den Aktionsablauf zu rekonstruieren. 1. Start von Instanz1 und Klick auf die Schaltfläche – ergibt eine Fehlermeldung und die Ausgabe »DDEKommando_LinkOpen«. 2. Start von Instanz2 und erneuter Klick auf die Schaltfläche von Instanz1 – ergibt im Formular von Instanz1 die Ausgabe »DDEKommando_LinkOpen« und im Formular von Instanz2 die Ausgabe »Form_LinkOpen«. Das Formularobjekt von Instanz2 fungiert jetzt als Quellobjekt. Es nimmt Befehle entgegen, die in das Textfeld DDEKommando von Instanz1 eingegeben und durch einen Klick auf die Schaltfläche (im Namen von DDEKommando) abgeschickt werden.
497
Ereignisbehandlung
Ziel.LinkMode = 0 ' Verbindung zu anderer Quelle ggf. schließen Ziel.LinkTopic = "Quelle|QuelleForm" ' Anwendung und Thema festlegen Ziel.LinkMode = vbLinkManual ' Verbindung eröffnen Ziel.LinkExecute "Beenden" ' DDE-Kommando verschicken0
Ereignisbehandlung
Ereignisbehandlung
Die beiden Instanzen des Programms DDE- Demo unterhalten DDE- Verbindungen zueinander
3. Erneuter Klick auf die Schaltfläche von Instanz1 – das Formularobjekt von Instanz2 erhält nun ein LinkExecute-Ereignis mit dem Befehlstext »(Befehl eingeben)« und generiert – ohne weitere Interpretation – die Ausgabe »Form_LinkExecute: (Befehl eingeben)«. 4. Klick auf das Textfeld DDEText von Instanz1 – das eröffnet eine automatische DDE-Kommunikation mit dem Textfeld Quelle1 von Instanz2 und überträgt dessen Inhalt in das Textfeld DDEText. Im Formular von Instanz2 weist die Ausgabe »Form_LinkOpen« darauf hin, dass eine zweite DDE-Verbindung eröffnet wurde. Die Ausgabe »DDEText_LinkOpen« im Formular von Instanz1 zeigt, dass DDEText Zielobjekt dieser Verbindung ist. Jeder weitere Klick auf das Textfeld DDEText schaltet die LinkItem-Eigenschaft zwischen »Quelle1« und »Quelle2« hin und her, so dass einmal das eine Textfeld und einmal das andere die Verknüpfung bedient und seinen Wert an DDEText liefert. Ein Auf- und Abbau der Verbindung ist dafür nicht erforderlich. Eingaben in das Textfeld Quelle1 (bzw. Quelle2) von Instanz2 werden aufgrund der gewählten Verbindungsart (LinkMode hat den Wert vbLinkAutomatic) unmittelbar an das in Instanz1 gelegene Zielobjekt DDEText weitergeleitet. 5. Aktivierung von Instanz2 und Klicks auf das Steuerelement DDEText sowie auf die Schaltfläche Command1 – Instanz2 initiiert daraufhin weitere DDE-Verbindungen analog der Aktionen 2 und 4, nur mit vertauschten Rollen – wie sich an den Ausgaben gut ablesen lässt. Insgesamt bestehen dann also vier Verbindungen, wobei jede Instanz zweimal als Quelle und zweimal als Ziel auftritt. 6. Aktivierung von Instanz1 und Eingabe des Kommandos »Beenden« in das Textfeld DDEKommando – die Eingabetaste oder ein Klick auf die Schaltfläche sendet das Kommando, woraufhin Instanz2 seine Ausführung beendet. Wie die Ausgabe im Formular von Instanz1 zeigt, treten dabei vier Ereignisse auf, da Visual Basic implizit alle Verbindungen abbricht. Die folgende Abbildung zeigt das Fenster von Instanz1 nach Beendigung von Instanz2.
Fenster von Instanz1 nac h Beendigung von Instanz2 über einen DDE- Befehl
498
DDE- Verbindungen
Hier der Code: '*********************************************************************** ' Formularmodul : DDE-Demo ' Autor : 2000 Rudolf Huttary ' Beschreibung : demonstriert DDE-Verbindungen '*********************************************************************** Private DDECmdZähler As Integer Private DDETextZähler As Integer Private PictureZähler As Integer
Ereignisbehandlung
Private Sub Form_Load() If App.PrevInstance Then Caption = "DDE – Demo: Instanz2" Quelle1 = "Instanz2-" & "Quelle1" Quelle2 = "Instanz2-" & "Quelle2" Else Caption = "DDE – Demo: Instanz1" Quelle1 = "Instanz1-" & "Quelle1" Quelle2 = "Instanz1-" & "Quelle2" End If DDEText = "(Klick öffnet DDE-Verbindung)" Command1.Caption = "DDE-Verbindung öffnen" End Sub Private Sub Form_LinkClose() Print "Form_LinkClose" End Sub Private Sub Form_LinkOpen(Cancel As Integer) Print "Form_LinkOpen" End Sub Private Sub Form_LinkExecute(CmdStr As String, Cancel As Integer) Print "Form_LinkExecute: " & CmdStr ' Befehl ausgeben Cancel = False If CmdStr = "Beenden" Then Unload Me ' Befehl interpretieren End Sub Private Sub DDEText_LinkClose() Print "DDEText_LinkClose" DDEText = "(Klick öffnet DDE-Verbindung)" DDETextZähler = 0 End Sub Private Sub DDEText_LinkOpen(Cancel As Integer) Print "DDEText_LinkOpen" End Sub Private Sub DDEText_Click() If DDETextZähler = 0 Then
' Beim ersten Klick: Verbindung herstellen
499
Ereignisbehandlung
Ereignisbehandlung
DDEText.LinkMode = 0 DDEText.LinkTopic = "DDE-Demo|Form1" ' Anwendung und Formular DDEText.LinkItem = "Quelle1" ' Quelle festlegen! On Error GoTo StarteAnwendung DDEText.LinkMode = vbLinkAutomatic Else ' Bei weiteren Klicks: Quelle wechseln DDEText.LinkItem = "Quelle" & 1 + DDETextZähler Mod 2 End If DDETextZähler = DDETextZähler + 1 Exit Sub StarteAnwendung: MsgBox (Err.Description & vbCrLf & _ " Abhilfe: Weitere Instanz des Programms starten") Exit Sub End Sub Private Sub Command1_Click() If DDECmdZähler = 0 Then ' Beim ersten Klick: Verbindung herstellen DDEKommando.LinkMode = 0 DDEKommando.LinkTopic = "DDE-Demo|Form1" On Error GoTo StarteAnwendung DDEKommando.LinkMode = vbLinkManual Command1.Caption = "DDE-Kommando senden" Else ' Bei weiteren Klicks: Befehl senden DDEKommando.LinkExecute DDEKommando End If DDECmdZähler = 1 Exit Sub StarteAnwendung: MsgBox ("Fehler " & Err.Number & " " & Err.Description & vbCrLf & _ " Abhilfe: Weitere Instanz des Programms starten") Exit Sub End Sub Private Sub DDEKommando_LinkClose() Print "DDEKommando_LinkClose" DDECmdZähler = 0 Command1.Caption = "DDE-Verbindung öffnen" End Sub Private Sub DDEKommando_LinkOpen(Cancel As Integer) Print "DDEKommando_LinkOpen" End Sub
Wie man sieht, weist der Code an sich trotz der komplexen Funktionalität keine Besonderheiten auf. Damit die DDE-bezogenen Ereignisse besser beobachtet werden können, enthalten die entsprechenden Behandlungsroutinen geeignete Print-Anweisungen. Der Code pflegt für jede Verbindung eine globale Variable, deren Wert eine Aussage darüber macht, ob die Verbindung besteht oder (neu) aufgebaut werden muss. An den beiden Stellen, wo ein Verbindungsaufbau passiert, ist eine Fehlerbehandlung notwendig – für den Fall, dass der Aufbau fehlschlägt.
500
OLE- Drag&Drop
OLE- Drag&Drop
Automatikmodus Die einfachste Situation für eine OLE-Drag&Drop-Operation liegt vor, wenn die betroffene Komponente im Automatikmodus betrieben wird. Es reicht dann, die Eigenschaft OLEDropMode für die Funktion als Zielkomponente bzw. die Eigenschaft OLEDragMode für die Funktion als Quellkomponente geeignet zu initialisieren. Dies kann im Entwurfsmodus geschehen, aber auch zur Laufzeit – beispielsweise in der Initialisierungsroutine Form_Load: Private Sub Form_Load() ... Text1.OLEDragMode = vbOLEDragAutomatic Text1.OLEDropMode = vbOLEDropAutomatic End Sub
' = 1 ' = 2
Das war es bereits. Der Benutzer kann nun zum einen den Inhalt des Steuerelements nach Herzenslust mit der Maus in eine Zielkomponente (beispielsweise in ein Word-Dokument) verschieben bzw. auch kopieren, wenn er beim Ablegen die Taste (Strg) gedrückt hält. Zum anderen lassen sich auf die gleiche Weise auch die Inhalte anderer textorientierter Komponenten in das Steuerelement verschieben bzw. kopieren. Was die Funktion als Zielkomponente betrifft, gibt es nichts weiter zu sagen: Der Automatikmodus bietet keinerlei Handhabe für Änderungen oder Zusätzliches. In der Funktion als Quellkomponente erhält das Steuerelement dagegen im Verlauf der OLE-Drag&Drop-Operation eine Reihe von Ereignissen, die eine gewisse Kontrolle über den Vorgang ermöglichen. Als erstes Ereignis tritt OLEStartDrag auf, das den Start der Operation anzeigt und die beiden Parameter Data und AllowedEffects bereitstellt. Data ist eine Objektreferenz auf ein DataObject-
501
Ereignisbehandlung
Die OLE-Drag&Drop-Operation ist ein Vorgang, bei dem der Benutzer den Inhalt oder einen zuvor ausgewählten Teil des Inhalts einer Quellkomponente mit gedrückter Maustaste über eine Zielkomponente zieht und dort ablegt. Vom Effekt her passiert dabei nichts anderes als bei der Benutzung der Zwischenablage. Im Gegensatz zur gewöhnlichen Drag&Drop-Operation, die nicht über die Grenzen einer Anwendung (lies: EXE-Datei oder DLL) hinausführt, da der Mechanismus mit konkreten Referenzen auf Steuerelemente hantiert, gibt es für die Reichweite der OLE-Drag&Drop-Operation keinerlei Beschränkungen. Sie funktioniert innerhalb eines Formulars, von Formular zu Formular, aber auch von Anwendung zu Anwendung. Der wesentliche Unterschied zwischen den beiden Mechanismen besteht darin, dass die OLE-Drag&Drop-Operation eine dokumentenzentrierte Systemschnittstelle (lies: Systemkomponente) benutzt, die speziell auf die Übermittlung von DataObject-Objekten eingerichtet ist. Während das Ziel einer Drag&Drop-Operation Zugriff auf alle Eigenschaften und Methoden des Quellobjekts erhält, sieht die Zielkomponente einer OLE-Drag&Drop-Operation nur ein Containerobjekt, welches ein in einem oder mehreren standardisierten Formaten vorliegendes Dokument bereitstellt. Die meisten in Visual Basic verfügbaren Steuerelemente unterstützen OLE-Drag&Drop zu einem gewissen Grad. Viele Standardsteuerelemente sowie ActiveX-Steuerelemente (insbesondere die mit den Editionen Professional und Enterprise ausgelieferten Steuerelemente) bieten außer dem manuellen Modus auch eine automatische Unterstützung für OLE-Drag&Drop. Was von der Theorie her ein mächtiges Gebäude ist, erweist sich in der Praxis meist handlicher, als man es erwarten würde. Ein auf den Automatikbetrieb eingerichtetes Steuerelement unterstützt entsprechende Einstellungen für die Eigenschaften OLEDragMode und OLEDropMode. Aus Sicht der Programmierung bleibt dann nichts weiter zu tun, als das Steuerelement für die Rolle als Quellkomponente und/oder als Zielkomponente in den Automatikmodus zu versetzen. Alles Weitere erledigt sich gewissermaßen von selbst. Der manuelle Modus birgt dagegen weitaus mehr Möglichkeiten, auf den Vorgang einzuwirken bzw. diesen zu gestalten.
Ereignisbehandlung
Ereignisbehandlung
Objekt, das den übermittelten Inhalt umhüllt. Der Typ des Inhalts lässt sich durch Aufruf der Methode GetFormat bestimmen, während GetData den Inhalt selbst liefert. Um den Inhalt (samt Typ) neu zu setzen, genügt ein SetData-Aufruf. AllowedEffects ist dagegen ein Bitvektor, der bestimmt, welche Operationen die Quellkomponente für den Inhalt des DataObject-Objekts unterstützt: Ein gesetztes Bit 0 (vbDropEffectCopy) erlaubt das Kopieren und ein gesetztes Bit 1 (vbDropEffectMove) das Verschieben. Die meisten Steuerelemente erlauben beide Operationen und geben den Standardwert 3 vor. Um die Operation zu unterbinden, können Sie den Wert auch auf 0 setzen. Obwohl der SetData-Aufruf den Inhalt für gewöhnlich bereits in Reaktion auf OLEStartDrag in einem oder mehreren Formaten bereitstellt, kann eine Komponente mit der Bereitstellung aufwändiger Formate auch auf das OLESetData-Ereignis warten. Voraussetzung für das Auftreten dieses Ereignisses ist, dass ein SetData-Aufruf der Form Data.SetData , vbCFSpezialFormat
also unter Auslassung des ersten Parameters vorausgegangen ist. In diesem Fall tritt das Ereignis auf, wenn die Zielkomponente auf das OLEDragDrop-Ereignis hin ihren GetData-Aufruf durchführt. Somit ist eine Bereitstellung des Inhalts nur erforderlich, wenn die Operation auch tatsächlich ausgeführt wird – der Vorteil bei umfangreichen Inhalten liegt auf der Hand. Im Automatikmodus überträgt ein Textfeld standardmäßig nur den markierten Teil seines Inhalts. Um auf jeden Fall den ganzen Inhalt zu übertragen und nur die Kopieroperation zuzulassen, sieht die Behandlungsroutine für das OLEStartDrag-Ereignis so aus: Private Sub Text1_OLEStartDrag(Data As DataObject, _ AllowedEffects As Long) AllowedEffects = vbDropEffectCopy ' 1 (nur Kopieren möglich) Data.SetData Text1, vbCFText ' gesamten Wert übermitteln End Sub
Im weiteren Verlauf der Operation treten OLEGiveFeedback-Ereignisse auf, die eine Reaktion auf OLEDragOver-Ereignisse aufseiten der Zielkomponente darstellen. (Im Automatikmodus »beantwortet« eine Zielkomponente OLEDragOver-Ereignisse allerdings intern, ohne dass es zum Aufruf einer gegebenenfalls bereitgestellten Behandlungsroutine für dieses Ereignis kommt.) Der erste Parameter Effect im OLEGiveFeedback-Ereignis ist eine Mitteilung der Zielkomponente über die Art der resultierenden Operation, falls der Benutzer das Objekt ablegt. Eine Quellkomponente kann dieses Ereignis behandeln, um dem Benutzer ein geeignetes optisches Feedback etwa in Form eines benutzerdefinierten Mauszeigers zu geben und ihn so über die Art der zustande kommenden Operation zu unterrichten. Dazu wird die Cursorform über die MouseIcon-Eigenschaft des Screen-Objekts bereitgestellt und durch Setzen des DefaultCursors-Parameters auf den Wert False sowie der Eigenschaft Screen.MousePointer auf vbCustom aktiviert. Hier ein Beispiel Private EffectIcon As Picture Private AltesScreenIcon As Picture Private AlterScreenPointer As Integer Private Sub Quelle_OLEStartDrag(Data As DataObject, _ AllowedEffects As Long) Set AltesScreenIcon = Screen.MouseIcon ' Alte Form merken AlterScreenPointer = Screen.MousePointer ' Alten Zeiger merken If EffectIcon Is Nothing Then ' Form schon geladen? Set EffectIcon = LoadPicture("OLECopy.ico") ' Form laden End If End Sub
502
OLE- Drag&Drop Private Sub Quelle_OLEGiveFeedback(Effect As Long, _ DefaultCursors As Boolean) If Effect And vbDropEffectCopy Then ' Nur fürs Kopieren Set Screen.MouseIcon = EffectIcon ' Andere Form setzen Screen.MousePointer = vbCustom ' Benutzerdefinierte Form DefaultCursors = False ' Eigene Form verwenden End If End Sub
Private Sub Quelle_OLECompleteDrag(Effect As Long) Screen.MouseIcon = AltesScreenIcon ' Alte Form setzen Screen.MousePointer = AlterScreenPointer ' Alten Zeiger setzen End Sub
Manueller Modus
Eine Quellkomponente kann auch »manuell« durch Aufruf der Methode OLEDrag dazu bewegt werden, eine OLE-Drag&Drop-Operation zu beginnen. Im manuellen Modus, wenn die Eigenschaft OLEDragMode den Wert vbOLEDragManual hat, ist dies sogar die einzige Möglichkeit. Der Ablauf ist dann genauso wie im Automatikmodus, außer dass eine Bereitstellung des zu übermittelnden Inhalts bei der Behandlung des OLEStartDrag- oder OLEGetData-Ereignisses erfolgen kann. Ein Inhalt kann übrigens auch in mehreren verschiedenen (auch benutzerdefinierten) Formaten bereitgestellt werden. Ein Gegenstück von OLEDrag, etwa eine Methode namens »OLEDrop« zum Ablegen, gibt es nicht – und würde auch keinen Sinn machen. Wird die Zielkomponente im manuellen Modus vbOLEDropManual (1) betrieben, ergibt sich an zwei Stellen die Möglichkeit, von der Implementation her gestalterisch auf den Vorgang einzuwirken. Da ist zunächst einmal das OLEDragOver-Ereignis. Vergleichbar mit dem MouseMoveEreignis tritt es auf, wenn der Mauszeiger im Verlauf einer OLE-Drag&Drop-Operation in den Bereich der Zielkomponente eintritt, darin bewegt wird oder diesen wieder verlässt. Durch seine stattliche Anzahl an Parametern vermittelt dieses Ereignis der Komponente eine vollständige Vorausschau auf den möglichen Ausgang der Operation. Der Parameter Data macht den eigentlichen Inhalt der Operation sowie dessen Datentyp einer genaueren Analyse zugänglich – eine Manipulation des Inhalts mittels SetData ist jedoch nicht zulässig. Der zuvor schon diskutierte Parameter Effect zeigt an, welche Operationen (Verschieben und/oder Kopieren) die Quellkomponente unterstützt, und erlaubt es der Zielkomponente, die von ihr unterstützten Operationen in umgekehrter Richtung kundzutun. Die Parameter Button und Shift enthalten Bitvektoren, die den aktuellen Zustand der Maus- und Funktionstasten beschreiben. Die Parameter X und Y geben die aktuelle Mausposition wieder, und State zeigt schließlich an, ob der Bereich der Zielkomponente soeben betreten (vbEnter) oder verlassen (vbLeave) wurde oder ob sich einfach nur die Mausposition innerhalb des Bereichs (vbOver) verändert hat. Bei der Auswertung des Effect-Parameters sollten Sie eine geeignete Maskierung verwenden, damit die Kompatibilität des Codes gegenüber künftigen Erweiterungen (Belegung zusätzlicher Bits des Vektors mit Bedeutung) gewahrt bleibt. Nehmen wir an, die Kopieroperation soll stattfinden, wenn keine Funktionstaste gedrückt ist, und die Verschiebeoperation, wenn (Strg) gedrückt ist. Das sieht dann leider ein wenig umständlich aus, lässt sich aber wiederverwenden und sollte als eigenständige Prozedur verpackt von der Behandlungsroutine aus aufgerufen werden:
503
Ereignisbehandlung
Das letzte Ereignis, das die Quellkomponente zu Gesicht bekommt, ist OLECompleteDrag. Es informiert die Komponente, welche Operation nun im Endeffekt passiert ist. Eine Behandlung dieses Ereignisses kann beispielsweise erforderlich sein, wenn die Quellkomponente die verschobenen Daten nicht von selbst löscht (oder nicht alle damit im Zusammenhang stehenden Ressourcen freigibt), aber auch, um einen gegebenenfalls bei der Behandlung von OLEGiveFeedback gesetzten Mauszeiger wieder loszuwerden:
Ereignisbehandlung
Ereignisbehandlung
Private Sub BestimmeEffect(Effect As Long, Button As Integer, _ Shift As Integer) If Shift = 0 And (Effect And vbDropEffectCopy) > 0 Then Effect = vbDropEffectCopy ' Kopieroperation setzen ElseIf (Shift And vbShiftMask) > 0 _ And (Effect And vbDropEffectMove) > 0 _ Then Effect = vbDropEffectMove ' Verschiebeoperation setzen Else Effect = vbDropEffectNone ' Operation nicht unterstützt! End If End Sub Private Sub Ziel_OLEDragOver(Data As DataObject, Effect As Long, _ Button As Integer, Shift As Integer, X As Single, Y As Single, _ State As Integer) BestimmeEffect Effect, Button, Shift if Not Data.GetFormat(vbCFText) Then ' Nur Textformat ist zulässig Effect = vbDropEffectNone End if End Sub
Der Windows Explorer setzt übrigens auch das dritte Bit des Bitvektors, wenn es darum geht, Dateien zu transportieren. Die entsprechende Operation hat die Bedeutung »Verknüpfung mit Datei erstellen« und lässt sich in Visual Basic 6.0 implementieren, auch wenn aktuell noch keine Maskenkonstante dafür definiert ist. Im manuellen Modus sollte die Zielkomponente das OLEDragOver-Ereignis auf jeden Fall dazu nutzen, mittels der GetFormat-Methode des Parameters Data festzustellen, ob sie mit den Daten überhaupt etwas anfangen kann, und wenn ja, Effect auf die Operation zu setzen, die angesichts des aktuellen Zustands der Maustasten und Funktionstasten resultieren würde. Die zweite Möglichkeit für den Eingriff ergibt sich durch das Ereignis OLEDragDrop. Es verkündet der Komponente, dass sie Ziel einer OLE-Drag&Drop-Operation ist und tritt somit auf, sobald der Benutzer die Maustaste loslässt. An Parametern stellt das Ereignis dieselben Werte bereit wie OLEDragOver, so dass für die Ermittlung der durchzuführenden Operation vom Prinzip her die gleiche Logik bzw. eine Prozedur verwendet werden kann. Im Unterschied zu OLEDragOver wird die Behandlungsroutine von OLEDragDrop aber konkret mit dem Parameter Data arbeiten und den eigentlichen »Inhalt« der OLE-Drag&Drop via GetData entgegennehmen. Hier der Code für die Entgegennahme eines Inhalts im Format vbCFText: Private Sub Ziel_OLEDragDrop(Data As DataObject, Effect As Long, _ Button As Integer, Shift As Integer, X As Single, Y As Single) BestimmeEffect Effect, Button, Shift If Effect <> vbDropEffectNone then Ziel = Data.GetData(vbCDText) End If End Sub
Nun fehlt eigentlich nur noch der letzte Schritt, der die Quellkomponente darüber informiert, welche Operation nun tatsächlich im Rahmen von OLEDragDrop ausgeführt wurde. Diese muss ja wissen, ob sie den übermittelten Inhalt bei sich löschen soll oder nicht. Die Quellkomponente erhält daher noch ein letztes Ereignis, nämlich OLEDragComplete. Da die meisten Steuerelemente
504
OLE- Drag&Drop von sich aus aufräumen, ist die Behandlung eigentlich nur dann erforderlich, wenn dies nicht, oder nicht vollständig geschieht oder vielleicht sogar nur unter Vorbehalt geschehen soll.
Ausblick
OLEDragDrop die »eierlegende Wollmilchsau« Das Beispielprojekt OLEDragDrop zu diesem Thema ist so etwas wie eine »eierlegende Wollmilchsau« im Bereich des OLE-Drag&Drop. Es beinhaltet zwei Formulare, von denen das eine, Form1, als Zielkomponente auftritt und mit einer recht ansehnlichen Fülle unterschiedlicher OLE-Drag&Drop-Operationen etwas anfangen kann, während das andere, Form2, einige Steuerelemente enthält, die sich zum Testen der Zielkomponente eignen. Die Zielkomponente unterstützt folgende Operationen: 1. Ablegen einer Zeichenfolge, die aus einer textorientierten Komponente stammt. (Auch RTForientierte Komponenten kommen als Quelle in Frage, die Darstellung berücksichtigt allerdings keine Formatierungen.) 2. Ablegen einer Bitmap, die aus einem PictureBox- oder Image-Steuerelement stammt. (Als Quelle kommt auch eine sonstige bildorientierte Komponente mit OLE-Drag-Fähigkeiten in Frage.) 3. Ablegen einer Grafik im Zwischenformat WMF, die aus einem PictureBox- oder Image-Steuerelement stammt. (Als Quelle kommt auch eine sonstige WMF-orientierte Komponente mit OLE-Drag-Fähigkeiten in Frage.) 4. Ablegen einer oder mehrerer Dateien, die aus einem FileListBox-Steuerelement, einem Fenster des Windows Explorers oder einer sonstigen Komponente stammen, welche die Dateiauswahl gestattet und OLE-Drag-Fähigkeiten besitzt. Dabei akzeptiert die Zielkomponente diverse Textformate (TXT, INI, BAT, LOG, REG, HTM, HTML, INF, ASC) sowie alle gängigen Bildformate und zeigt den Inhalt der Dateien nach Möglichkeit an. Durch einen einfachen Mausklick in den Formularbereich lässt sich die Anzeige auf die jeweils nächste Datei weiterschalten. 5. Für 1. bis 4. unterstützt die Zielkomponente das Kopieren der Inhalte und für 1. bis 3. (bei gedrückter Taste (Umschalt)) zusätzlich das Verschieben der Inhalte.
505
Ereignisbehandlung
Die für Visual Basic definierten Formatkonstanten decken nur einen kleinen Teil der tatsächlich beim System registrierten Formate ab. Registriert man mittels der Win32-API-Funktion RegisterClipBoardFormat ein eigenes Format, steht dieses fortan auf dem System für die OLE-Kommunikation zur Verfügung. Die Funktion erwartet einen Bezeichnerstring für das Format und liefert nach erfolgreicher Registrierung den Wert für die neue Formatkonstante. War das Format bereits registriert, etwa von einer anderen Instanz der gleichen Anwendung, liefert die Funktion schlicht den Handle dieses Formats. Eine Registrierung ist zwar ratsam, aber nicht unbedingt erforderlich, wenn das Format nur »intern« benutzt werden soll. Selbst wenn die Wahrscheinlichkeit einer Kollision mit einem registrierten Format bei Wahl einer arbiträren Zahl als Handle für das Format recht klein ist, empfiehlt sich doch ein Test mit der Win32-APIFunktion GetClipFormatName, ob der Handle im System bereits mit einer anderen Bedeutung belegt ist. Wenn Sie ein Format verwenden, das der Aufzählungstyp ClipBoardConstants nicht mit einer Konstanten bedenkt, erwartet die Methode SetData die zu übermittelnden Daten als ByteArray. Andere Datentypen müssen Sie also vorher geeignet umwandeln. Vergessen Sie dabei nicht, eine Längeninformation voranzustellen, da Windows die benötigte Pufferlänge großzügig aufrundet – mit der Folge, dass GetData zumeist mehr Bytes abholt, als SetData hineinsteckt. Ein kleines Beispiel für die Arbeit mit einem eigenen Format finden Sie im Referenzteil unter »OLEStartDrag-Ereignis« (S. 250 ).
Ereignisbehandlung
Ereignisbehandlung
Da ein Formular in Visual Basic selbst nicht als Quellkomponente auftreten kann, werden Verschiebeoperationen zur »Einbahnstraße« – der Inhalt geht also verloren! Der Aufwand, in das Formular ein PictureBox-Steuerelement einzufügen, das anstelle des Formulars als Zielkomponente auftritt, ist zwar vergleichsweise klein, hätte das Beispielprogramm aber doch unnötig verkompliziert.
Form2 dient als Testplattform für Form1 als Zielkomponente
Form1 nach Ablage der im vorigen Bild gezeigten FileListBox-Auswahl
Hier zunächst einmal der Code für Form1, der, bis auf eine gewisse Ausgestaltung des Funktionsumfangs, den für den manuellen Modus skizzierten Aufbau besitzt. '*********************************************************************** ' Formularmodul : OLEDragDrop ' Autor : 2000 Rudolf Huttary ' Beschreibung : Zielformular für OLE-Drag&Drop-Operationen '*********************************************************************** Private Dateien As DataObjectFiles ' für Dateienliste Private DateiIdx As Integer ' Index für aktuell angezeigte Datei
506
OLE- Drag&Drop Private Form2 As New Form2
' Zweites Formular mit Auswahl
Private Sub Form_Load() ' Formulare untereinander aber in die Mitte des Bildschirms Left = (Screen.Width – Width) / 2 Form2.Left = (Screen.Width – Form2.Width) / 2 ' Form2 wird generiert Top = (Screen.Height – Form2.Height – Height) / 2 Form2.Top = Top + Height Form2.Show ' zweites Formular anzeigen End Sub
Ereignisbehandlung
Private Sub Form_Click() If Not Dateien Is Nothing Then ' schon initialisiert? DateiIdx = (1 + DateiIdx) Mod Dateien.Count ' nächste Datei LadeDatei ' Dateiinhalt anzeigen End If End Sub Private Sub Form_OLEDragDrop(Data As DataObject, Effect As Long, _ Button As Integer, Shift As Integer, X As Single, Y As Single) Dim fs As Object ' Kopieren oder Verschieben? BestimmeEffect Effect, Button, Shift If Effect = vbDropEffectNone Then ' Operation nicht zulässig Exit Sub ' und Tschüß End If ' Bitmap? If Data.GetFormat(vbCFDIB) Or Data.GetFormat(vbCFBitmap) Then Picture = Data.GetData(vbCFDIB) Caption = "Bitmap" End If ' Metafile? If Data.GetFormat(vbCFMetafile) Or Data.GetFormat(vbCFEMetafile) Then Picture = Data.GetData(vbCFMetafile) Caption = "Zwischendatei" End If ' Ordinäre Zeichenfolge? If Data.GetFormat(vbCFText) Or Data.GetFormat(vbCFRTF) Then Picture = Nothing Cls Caption = "Zeichenfolge" Print Data.GetData(vbCFText) End If ' Datei(n)? If Data.GetFormat(vbCFFiles) Then DateiIdx = 0 ' Index initialisieren Set Dateien = Data.Files ' Dateiliste übernehmen LadeDatei ' Erste Datei anzeigen End If
507
Ereignisbehandlung
Ereignisbehandlung
End Sub Private Sub Form_OLEDragOver(Data As DataObject, Effect As Long, _ Button As Integer, Shift As Integer, X As Single, Y As Single, _ State As Integer) BestimmeEffect Effect, Button, Shift If Not (Data.GetFormat(vbCFFiles) Or Data.GetFormat(vbCFDIB) Or _ Data.GetFormat(vbCFBitmap) Or Data.GetFormat(vbCFText) Or _ Data.GetFormat(vbCFMetafile) Or Data.GetFormat(vbCFEMetafile) _ Or Data.GetFormat(vbCFRTF)) _ Then Effect = vbDropEffectNone ' ablehnen,da OLE-Format unbekannt End If End Sub Private Sub BestimmeEffect(Effect As Long, Button As Integer, Shift As Integer) If Shift = 0 And (Effect And vbDropEffectCopy) > 0 Then Effect = vbDropEffectCopy ' Kopieroperation setzen ElseIf (Shift And vbShiftMask) = Shift _ And (Effect And vbDropEffectMove) > 0 _ Then ' nur die Umschalttaste? Effect = vbDropEffectMove ' Verschiebeoperation setzen Else Effect = vbDropEffectNone ' Operation nicht unterstützt! End If End Sub Private Sub LadeDatei() Dim fs As Object Dim Datei As String Dim DateiTyp As String Datei = UCase(Dateien(DateiIdx + 1)) ' Kleiner Bug-Fix, da FileListbox bei Wurzelverzeichnissen ' einen Fehler macht ... If Mid(Datei, 3, 2) = "\\" Then Datei = Left(Datei, 3) + Mid(Datei, 5) End If Caption = Dateien(DateiIdx + 1) ' Dateinamen in Titelleiste anzeigen Cls ' Text löschen Picture = Nothing ' Bild Löschen ' TextDatei? If InStrRev(Datei, ".TXT") Or InStrRev(Datei, ".INI") Or _ InStrRev(Datei, ".INI") Or InStrRev(Datei, ".BAT") Or _ InStrRev(Datei, ".LOG") Or InStrRev(Datei, ".REG") Or _ InStrRev(Datei, ".HTM") Or InStrRev(Datei, ".HTML") Or _ InStrRev(Datei, ".INF") Or InStrRev(Datei, ".ASC") _ Then ' Dateisystem-Objekt anlegen Set fs = CreateObject("Scripting.FileSystemobject") On Error Resume Next ' Falls Datei inzwischen unbekannt ...
508
OLE- Drag&Drop Print fs.OpenTextfile(Datei).ReadAll ' Datei en bloc ausgeben Else On Error Resume Next ' Falls Format unbekannt, passiert nichts Cls ' Text löschen Picture = LoadPicture(Datei) End If End Sub
Über die Load-Routine gibt es nicht viel zu sagen: Sie lädt das Testformular Form2 und ordnet beide Formulare mittig untereinander auf dem Bildschirm an. Danach verharrt Form1 sozusagen in Warteposition, bis es Ziel einer OLE-Drag&Drop-Operation wird. Die Behandlung der zuerst eintreffenden OLEDragOver-Ereignisse besteht aus einer zweistufigen Analyse. Im ersten Schritt kommt die Prozedur BestimmeEffect zum Aufruf, die anhand der Parameter Effect und Shift bestimmt, ob der Benutzer den OLE-Inhalt kopieren oder verschieben ((Umschalt) gedrückt) will, und Effect gleich geeignet setzt. Der zweite Schritt beinhaltet die Auswertung des angebotenen Datenformats. Obwohl das Formular eigentlich nur mit dem Format vbCFPalette nichts Rechtes anfangen kann (das Formular nimmt Paletten zwar problemlos entgegen, man »sieht« nur nichts), ist die Auswertung im Hinblick auf künftige Erweiterungen »positiv« formuliert – und nicht als Ausschluss, was natürlich eleganter wäre. Die Logik für die Behandlung des OLEDragDrop-Ereignisses sieht nicht viel anderes aus: Der Ermittlung der gewünschten Operation durch BestimmeEffect folgt eine Fallunterscheidung für die verschiedenen akzeptierten Datenformate. Bei textuellen Inhalten beschränkt sich das Formular (notgedrungen) auf das Format vbCFText und nimmt die Darstellung mit Print vor. Bitmaps sowie WMF- und EMF-Grafiken lassen sich ohne weitere Umstände in die PictureEigenschaft übernehmen, was zugleich für eine automatische Darstellung sorgt. Ein wenig mehr Arbeit macht dagegen das Format vbCFFiles, das beispielsweise vom Windows Explorer oder von FileListBox-Komponenten geliefert wird. Der von GetData gelieferte Inhalt trägt in diesem Fall den Typ DataObjectFiles und ist eine Files-Auflistung mit den für die OLE-Drag&DropOperation markierten Dateinamen. Die Routine übernimmt die Liste in eine globale Variable und ruft dann die Prozedur LadeDatei auf. Aufgabe von LadeDatei ist die Anzeige der durch DateiIdx spezifizierten »aktuellen« Datei in der Liste. Die Prozedur versucht als Erstes, anhand der Dateierweiterung herauszufinden, ob es sich bei der Datei um eine Textdatei handelt. Falls ja, organisiert sie sich vom Windows Scripting Host eine Instanz des FileSystemObject-Objekts und lässt sich von dieser die Textdatei en bloc als Zeichenfolge für die Ausgabe mit Print liefern – die unkomplizierteste Methode, an den kompletten Inhalt einer Textdatei zu gelangen. Liefert die Dateierweiterung keinen Hinweis auf eine Textdatei, nimmt die Prozedur an, dass es sich um eine Grafikdatei handelt, und versucht, diese mittels LoadPicture zu laden. Falls das schiefgeht, fängt die Routine den Fehler ab und leert nur das Formular. Nun, die Rede war von einer Dateiliste und nicht nur von einer einzelnen Datei: Ein Klick auf das Formular ruft LadeDatei für die jeweils nächste Datei in der Liste auf und erlaubt es dem Benutzer, sich der Reihe nach alle Dateiinhalte anzusehen. Von der »offiziellen« Seite her wäre das alles gewesen, was es über die Implementation von Form1 zu sagen gibt. Bleibt noch der inoffizielle Teil, ein Work-around, der einen Implementationsfehler in der FileListBox-Komponente umschifft. Als Quellkomponente in einer OLEDrag&Drop-Operation setzt die Komponente in der Files-Auflistung nämlich einen fehlerhaften Pfad, wenn die Auswahl dem Wurzelverzeichnis eines Laufwerks angehört: Aus irgendeinem Grund enthält er einen Schrägstrich zu viel. Der Fehler ist in den Titelleisten der beiden Formulare zu sehen (vgl. Abbildungen) und wird gar vom Windows Explorer mit der lapidaren
509
Ereignisbehandlung
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer) Unload Form2 ' zweites Formular ggf. entladen End Sub
Ereignisbehandlung
Ereignisbehandlung
Fehlermeldung »Dateisystemfehler 1026« bedacht, wenn dieser als Zielkomponente getestet wird. Der Work-around tut nichts weiter, als den überflüssigen Schrägstrich zu eliminieren. Ein Versuch, den Fehler aufseiten der Quellkomponente in der Behandlungsroutine für das Ereignis OLEStartDrag zu korrigieren (was eigentlich der vernünftigere Ansatz wäre), scheitert leider, da die Files-Eigenschaft des DataObject-Objekts zur Laufzeit schreibgeschützt ist. Bleibt also zu hoffen, dass Microsoft den Fehler mit der nächsten Version von Visual Basic korrigiert. Die Implementation von Form2 gibt nicht viel mehr her, als das, was bereits im Abschnitt »Anwendung« besprochen wurde. Die Steuerelemente Text1, Image1 und Picture1 arbeiten als Quell- und Zielkomponenten im Automatikmodus. Einzig das FileListBox-Steuerelement File1 ist für den manuellen Modus eingerichtet und zeigt dem Benutzer Smiley-Symbole als Feedback während einer OLE-Drag&Drop-Operation an: '*********************************************************************** ' Formularmodul : Form2 ' Autor : 2000 Rudolf Huttary ' Beschreibung : Quellformular für OLE-Drag&Drop-Operationen '*********************************************************************** Private EffectCopyIcon As Picture Private EffectNoneIcon As Picture Private AltesScreenIcon As Picture Private AlterScreenPointer As Integer Private Sub Form_Load() Caption = File1.Path End Sub
' Pfad in Titelleiste anzeigen
Private Sub Drive1_Change() Dir1 = Drive1 End Sub
' DirListBox aktualisieren
Private Sub Dir1_Change() File1 = Dir1 End Sub
' FileListBox aktualisieren
Private Sub File1_PathChange() Caption = File1.Path End Sub
' Pfad in Titelleiste anzeigen
Private Sub File1_OLEStartDrag(Data As DataObject, _ AllowedEffects As Long) Set AltesScreenIcon = Screen.MouseIcon ' Alte Form merken AlterScreenPointer = Screen.MousePointer Screen.MousePointer = vbCustom ' Benutzerdefinierte Form If EffectCopyIcon Is Nothing Then ' Form schon geladen? Set EffectCopyIcon = LoadPicture(App.Path + "\EffectCopy.ico") Set EffectNoneIcon = LoadPicture(App.Path + "\EffectNone.ico") End If Caption = Data.Files(1) ' Erste Datei in Titelleiste anzeigen ' das deckt Implementationsfehler bei ' Wurzelverzeichnis auf End Sub
51 0
DiaProjektor SDI- Formulare synchronisieren
Private Sub File1_OLECompleteDrag(Effect As Long) Screen.MouseIcon = AltesScreenIcon ' Alte Form setzen Screen.MousePointer = AlterScreenPointer ' Alten Zeiger setzen End Sub
DiaProjektor SDI- Formulare synchronisieren Das in diesem Abschnitt vorgestellte Programm DiaProjektor dürfte das umfangreichste Beispiel in diesem Buch sein. Das Projekt wurde bewusst als Kontrast zu den sonst eher schlicht gehaltenen Beispielprogrammen ausgewählt, da es nicht nur den logischen Aufbau einer durchschnittlichen Anwendung mit mehreren Ansichten und einer ganzen Reihe von Menübefehlen demonstriert, sondern auch, dass »der Teufel letztlich doch wieder im Detail steckt«. Als recht ordentliches Präsentationsprogramm für JPEGs und GIFs verdient das Programm schon fast das Attribut »fertige Anwendung«, wären da nicht noch einige Punkte auf der Wunschliste wie OLE-Drag&Drop, Abspeichern von Indexansichten, Optionen-Dialog usw. Dennoch, die Feature-Liste ist beachtlich und lässt bereits auf einen ganzen Sack voller Konzepte schließen, deren Implementation es am vorliegenden Quellcode zu diskutieren gilt. ● ●
● ●
● ● ● ● ●
Dateiauswahl: einzeln, mehrfach, ganzes Verzeichnis Bildwechsel: wahlweise Menüsteuerung, Tastatursteuerung, Maussteuerung, Zeitgebersteuerung Anzeigen: Vollbildanzeige und Fensteranzeige Voller Funktionsumfang auch in Vollbildansicht durch Tastatursteuerung und Kontextmenü Skalierung: automatisch auf verfügbaren Anzeigebereich Ansichten: »Einzeldia« und »Indexprint« Diaauswahl in Indexansicht per Maus Caching der jeweils letzten Indexansicht in beiden Anzeigearten Infodialog
Damit Sie eine gewisse Vorstellung davon erhalten, was Sie auf der Codeseite erwartet, zunächst eine Funktionsbeschreibung des Programms sowie der Benutzerschnittstelle: Beim Start konfrontiert Sie das Programm umgehend mit dem Standarddialog DATEI ÖFFNEN, über den sich die Bilder für die Diashow auswählen lassen. Nach Wahl des Verzeichnisses können Sie entweder den Vorschlag »(alle)« akzeptieren, um daraus alle im Verzeichnis befindlichen Bilddateien in den Formaten BMP, JPG und GIF (in eben dieser Reihenfolge) zu laden, oder per Mehrfachauswahl nur die Dateien auswählen, die Sie interessieren. Der Vollständigkeit halber kann eine Mehrfachauswahl auch aus einer einzigen Datei bestehen, in diesem Fall dürfte die
51 1
DiaProjektor – SDI- Formulare synchronisieren
Private Sub File1_OLEGiveFeedback(Effect As Long, _ DefaultCursors As Boolean) Select Case Effect Case vbDropEffectCopy ' fürs Kopieren Set Screen.MouseIcon = EffectCopyIcon ' Andere Form setzen Case vbDropEffectNone ' Operation nicht möglich Set Screen.MouseIcon = EffectNoneIcon ' Andere Form setzen Case Else DefaultCursors = True End Select DefaultCursors = False ' Eigene Form verwenden End Sub
DiaProjektor SDI- Formulare synchronisieren
DiaProjektor – SDI- Formulare synchronisieren
Diashow aber recht eintönig werden. Falls Sie den Dialog abbrechen, erhalten Sie ein leeres Formular und können über das Menü DATEI den Befehl wiederholen oder das Programm regulär beenden. Ansonsten präsentiert Ihnen das Programm in der Indexansicht die (maximal) zwanzig ersten Bilder (vgl. Abbildung) beschriftet mit dem jeweiligen Dateinamen. Sie können jederzeit zwischen der Fensteranzeige und der Vollbildanzeige wechseln, indem Sie die Taste (Esc) drücken oder den entsprechenden Befehl aus dem DATEI-Menü bzw. Kontextmenü auswählen. Um den Index-Print für die nächsten zwanzig Bilder zu erhalten, drücken Sie eine der Tasten (Rechts), (Oben) oder (Bild-Oben) oder Sie navigieren über das Menü (Bild). Die Navigation in umgekehrter Richtung erfolgt analog.
Indexansicht der Programms Diaprojektor Falls Sie in die Ansicht »Einzeldia« umschalten wollen, klicken Sie einfach auf das gewünschte Bild oder Sie wechseln die Ansicht über den entsprechenden Menübefehl. Wie nicht anderes zu erwarten, ist die Navigation auch in dieser Ansicht gleichfalls wieder über die Cursortasten möglich, mit dem Unterschied, dass die Tasten (Oben), (Bild-Oben) bzw. (Unten), (Bild-Unten) in das nächste Indexblatt und somit zwanzig Einzelbilder weiter- bzw. zurückführen. Außerdem übernimmt die linke Maustaste nun die Funktion »Nächstes Bild«. Wie in der Abbildung zu sehen, bietet das Menü (Bild) zudem noch zwei TIMER-Funktionen an, die eine automatische Fortschaltung auf das jeweils nächste Einzeldia (nach dem letzen Dia kommt wieder das erste) vornehmen. Die Wirksamkeit dieser Funktionen bleibt allerdings auf diese Ansicht beschränkt. Um den Funktionsumfang auch in der Vollbildanzeige aufrechterhalten zu können, pflegt das Programm ein Kontextmenü, das alle über die Menüleiste angebotenen Menübefehle auflistet.
51 2
DiaProjektor SDI- Formulare synchronisieren
DiaProjektor – SDI- Formulare synchronisieren
Die Einzeldiaansicht von DiaProjektor
Kontextmenü und Dateimenü von DiaProjektor Das Projekt enthält drei Formulare, MainForm, FullScreenForm und About, wobei 95 Prozent des Codes im Modul MainForm konzentriert sind. Diese Verteilung hat sich aufgrund der Tatsache ergeben, dass MainForm und FullScreenForm im Wesentlichen dieselbe Logik verwenden können. Gäbe es in Visual Basic eine Möglichkeit, ein Rahmenfenster im Nachhinein in ein Fenster ohne Rahmen zu wandeln und umgekehrt, wäre das Formular FullScreenForm (und die gesamte für die Synchronisation der beiden Formulare notwendige Logik) nicht notwendig gewesen. So aber gibt das Programm gute Einsichten in die Prinzipien der wechselseitigen Steuerung von
51 3
DiaProjektor SDI- Formulare synchronisieren
DiaProjektor – SDI- Formulare synchronisieren
SDI-Formularen. Das dritte Formular, ein reiner Infodialog, wird als gebundener – oder wie es immer so schön heißt: »modaler« – Dialog über das Hilfemenü ? aufgerufen. Die Logik dafür ist äußerst primitiv.
Der Infodialog von DiaProjektor
Das Programmdesign Wer genau hinsieht, wird es am Programmdesign erkennen, dass der Code von der Einzeldiaansicht in der Fensteranzeige ausgehend über die Vollbildanzeige zur Indexansicht hin »gewachsen« ist. Im letzten Schritt erfolgte schließlich die Implementation des Caching der jeweils aktuellen Indexansicht als Optimierungsmaßnahme für einen schnelleren Bildaufbau. Bei einem monolithischen Entwurf wäre die Logik vermutlich ein wenig einfacher ausgefallen. Doch in der Praxis wird man nicht wegen jedem neuen Feature gleich »das ganze Kartenspiel neu mischen«. Für die Analyse des Programms bietet sich der gleiche Weg an: vom Einfachen zum Komplexen und Feature für Feature. Zuerst aber das Listing im Zusammenhang. '*********************************************************************** ' Projekt : DiaProjektor ' Autor : 2000 Rudolf Huttary ' Formularmodul : MainForm '*********************************************************************** ' Allgemeine Definitionen Option Explicit Const cIdxZeilen = 4 Const cIdxSpalten = 5 Const cLargeStep = cIdxZeilen * cIdxSpalten ' Seitenwert für Bildwechsel Enum vbCaching cCaching = True cNoCaching = False End Enum Private ActualForm As Form Private VollbildForm As Form
' aktuelles Formular ' Formular für Vollbildmodus
Private Private Private Private
' Array für Dateinamen ' Index für aktuelle Datei ' Schalter für Indexansicht
51 4
FileNames() As String FileIndex As Long bIndexView As Boolean Beendet As Boolean
Das Programmdesign
'*********************************************************************** ' Code für Formularereignisse '*********************************************************************** Private Sub Form_Load() Set ActualForm = Me ' Fensteranzeige als Starteinstellung ReDim FileNames(0 To 1) Show mnuÖffnen_Click ' Ordner abfragen End Sub
DiaProjektor – SDI- Formulare synchronisieren
Public Sub Form_KeyDown(KeyCode As Integer, Shift As Integer) ' Tastaturschnittstelle Select Case KeyCode Case vbKeyHome ' zum ersten Bild mnuBildErstes_Click Case vbKeyLeft ' ein Bild zurück mit Überlauf mnuBildVoriges_Click Case vbKeyRight ' ein Bild vor mit Überlauf mnuBildNächstes_Click Case vbKeyEscape ' Vollbildanzeige/Fensteranzeige mnuVollbild_Click Case vbKeyUp, vbKeyPageUp ' mehrere Bilder weiter mit Überlauf FileIndex = (FileIndex + cLargeStep – 1) Mod UBound(FileNames) mnuBildNächstes_Click Case vbKeyDown, vbKeyPageDown ' mehrere Bilder zurück mit Überlauf FileIndex = FileIndex – cLargeStep – 1 If FileIndex < 1 Then FileIndex = FileIndex + UBound(FileNames) mnuBildNächstes_Click Case vbKeyF3 ' Timer ein/aus: 3 Sekunden If Not mnuTimer3.Enabled Then Exit Sub mnuTimer3_Click Case vbKeyF5 ' Timer ein/aus: 5 Sekunden If Not mnuTimer5.Enabled Then Exit Sub mnuTimer5_Click End Select End Sub Private Sub Form_Resize() If Height < 1000 Then Height = 1000 If FileIndex Then Anzeigen cNoCaching End If End Sub
' minimale Größe erzwingen
Public Sub Form_MouseDown(Button As Integer, Shift As Integer, _ x As Single, y As Single) If Button = vbLeftButton Then ' In Indexansicht wählt Klick Einzeldia aus If bIndexView Then ' Treffertest, um Index des Einzeldias zu berechnen FileIndex = FileIndex + _
51 5
DiaProjektor – SDI- Formulare synchronisieren
DiaProjektor SDI- Formulare synchronisieren
Int(y / ActualForm.ScaleHeight * cIdxZeilen) * cIdxSpalten + _ Int(x / ActualForm.ScaleWidth * cIdxSpalten) If FileIndex > UBound(FileNames) Then FileIndex = UBound(FileNames) End If mnuIndex_Click ElseIf mnuBildNächstes.Enabled Then mnuBildNächstes_Click ' Blättern End If Else Kontextmenü ' Kontextmenü anzeigen End If End Sub Private Sub Form_Unload(Cancel As Integer) Beendet = True ' wegen PopupMenu! ' Vollbildformular entladen If Not (VollbildForm Is Nothing) Then Unload VollbildForm End Sub '*********************************************************************** ' Code für Menübefehle '*********************************************************************** Private Sub mnuDateiErstes_Click() mnuBildErstes_Click End Sub Private Sub mnuDateiNächstes_Click() mnuBildNächstes_Click End Sub Private Sub mnuDateiVoriges_Click() mnuBildVoriges_Click End Sub Private Sub mnuDateiTimer3_Click() mnuTimer3_Click End Sub Private Sub mnuDateiTimer5_Click() mnuTimer5_Click End Sub Private Sub mnuIndex_Click() DiaIndex cCaching End Sub Private Sub mnuVollbild_Click() ' Umschalten: Einzeldia/Vollb. ' Aktivierung durch Menübefehl Vollbildanzeige/Fensteranzeige oder Esc Static Viewstate As Integer ResetTimer
51 6
Das Programmdesign
DiaProjektor – SDI- Formulare synchronisieren
If Viewstate = 0 Then ' erster Aufruf? Set VollbildForm = New FullScreenForm ' Vollbildformular anlegen Set VollbildForm.FirstForm = Me ' Referenz auf MainForm Viewstate = 1 End If If Viewstate = 1 Then ' zur Vollbildanzeige? Visible = False Set ActualForm = VollbildForm mnuVollbild.Caption = "&Fensteranzeige" VollbildForm.Show Viewstate = 2 Else ' zur Fensteranzeige VollbildForm.Visible = False Set ActualForm = Me mnuVollbild.Caption = "&Vollbildanzeige" Show Viewstate = 1 End If Anzeigen cCaching End Sub Private Sub mnuÖffnen_Click() Dim bArrayNichtLeer As Boolean StopTimer ' Standdarddialog initialisieren und aufrufen dlgDateiOpen.Filter = "*.jpg;*.gif;*.bmp|*.jpg;*.gif;*.bmp" dlgDateiOpen.CancelError = True dlgDateiOpen.FileName = "(alle)" dlgDateiOpen.Flags = cdlOFNAllowMultiselect + cdlOFNExplorer _ + cdlOFNLongNames On Error GoTo Abbruch ' Falls Benutzer abbricht dlgDateiOpen.ShowOpen ' Standarddialog "Öffnen" ' Der folgende Code wird nicht ausgeführt, ' wenn der Öffnen-Dialog abgebrochen wurde! FileNames(0) = Left(dlgDateiOpen.FileName, _ InStrRev(dlgDateiOpen.FileName, "\")) ' Pfad FileIndex = 1 ' ab erstem Bild anzeigen If dlgDateiOpen.FileTitle = "(alle).jpg" Then ReDim Preserve FileNames(0 To 0) ' FS_FindFiles FileNames ' mit FileSystemObject-Objekt FindFiles FileNames ' mit Dir-Funktion bIndexView = False ' Indexansicht vorbereiten ElseIf InStr(dlgDateiOpen.FileName, Chr(0)) Then FileNames = Split(dlgDateiOpen.FileName, Chr(0)) FileNames(0) = FileNames(0) + "\" bIndexView = False ' Indexansicht vorbereiten Else ReDim FileNames(0 To 1)
51 7
DiaProjektor SDI- Formulare synchronisieren
DiaProjektor – SDI- Formulare synchronisieren
FileNames(1) = dlgDateiOpen.FileTitle bIndexView = True ' Einzeldiaansicht vorbereiten End If ' Menübefehle zum Bildwechsel aktivieren bArrayNichtLeer = FileNames(1) <> "" ' Wurden Dateien gefunden? mnuBildErstes.Enabled = bArrayNichtLeer mnuBildNächstes.Enabled = bArrayNichtLeer mnuBildVoriges.Enabled = bArrayNichtLeer mnuTimer3.Enabled = bArrayNichtLeer mnuTimer5.Enabled = bArrayNichtLeer DiaIndex cNoCaching ' Bild(er) Anzeigen Abbruch: End Sub Private Sub mnuBeenden_Click() On Error Resume Next Unload Me End Sub Private Sub mnuBildErstes_Click() ResetTimer FileIndex = 1 Anzeigen cCaching End Sub
' Erstes Bild anzeigen
Private Sub mnuBildNächstes_Click() ' Nächstes Bild anzeigen ResetTimer If bIndexView Then ' Indexansicht? If FileIndex + cLargeStep > UBound(FileNames) Then ' Überlauf? FileIndex = 1 Else FileIndex = FileIndex + cLargeStep End If Else FileIndex = FileIndex Mod UBound(FileNames) + 1 End If Anzeigen cCaching End Sub Private Sub mnuBildVoriges_Click() ' Voriges Bild anzeigen ResetTimer If bIndexView Then ' Indexansicht? If FileIndex – cLargeStep < 1 Then ' Unterlauf? FileIndex = ((UBound(FileNames) – 2) \ cLargeStep) * cLargeStep + 1 Else FileIndex = FileIndex – cLargeStep End If Else FileIndex = (FileIndex – 2 + UBound(FileNames)) _
51 8
Das Programmdesign
Mod UBound(FileNames) + 1 End If Anzeigen cCaching End Sub
DiaProjektor – SDI- Formulare synchronisieren
Private Sub mnuTimer3_Click() ResetTimer mnuTimer5.Checked = False ' Häkchen aktualisieren mnuDateiTimer5.Checked = False If mnuTimer3.Checked = False Then ' Timer schon gesetzt? Timer1.Interval = 3000 ' Nein: Timer auf 3 Sekunden Timer1.Enabled = True mnuTimer3.Checked = True mnuDateiTimer3.Checked = True Else ' Ja: ausschalten Timer1.Enabled = False mnuTimer3.Checked = False mnuDateiTimer3.Checked = False End If End Sub Private Sub mnuTimer5_Click() ResetTimer mnuTimer3.Checked = False ' Häkchen aktualisieren mnuDateiTimer3.Checked = False If mnuTimer5.Checked = False Then ' Timer schon gesetzt? Timer1.Interval = 5000 ' Nein: Timer auf 5 Sekunden Timer1.Enabled = True mnuTimer5.Checked = True mnuDateiTimer5.Checked = True Else ' Ja: ausschalten Timer1.Enabled = False mnuTimer5.Checked = False mnuDateiTimer5.Checked = False End If End Sub Private Sub mnuAbout_Click() About.Show vbModal End Sub
' Info-Dialog anzeigen
Private Sub Timer1_Timer() mnuBildNächstes_Click End Sub '*********************************************************************** ' Code für Hilfsroutinen '*********************************************************************** Private Sub ResetTimer() If Timer1.Enabled Then
51 9
DiaProjektor SDI- Formulare synchronisieren
DiaProjektor – SDI- Formulare synchronisieren
Timer1.Enabled = False Timer1.Enabled = True End If End Sub Private Sub StopTimer() Timer1.Enabled = False Timer1.Interval = 0 mnuTimer3.Checked = False mnuTimer5.Checked = False mnuDateiTimer3.Checked = False mnuDateiTimer5.Checked = False mnuTimer3.Enabled = False mnuTimer5.Enabled = False mnuDateiTimer3.Enabled = False mnuDateiTimer5.Enabled = False End Sub ' Lädt Bild von Datei und zeichnet ' im übergebenen Bereich Private Sub ShowDia(Index As Long, Optional x As Single, Optional y Optional b As Single, Optional h
' Timer ausschalten
es in maximaler Größe Ansicht As Form, _ As Single, _ As Single)
Dim Pic As Picture Dim BildBreite, BildHöhe If b * h = 0 Then b = Ansicht.ScaleWidth h = Ansicht.ScaleHeight End If
' Parameter weggelassen? ' Abmessungen von Client-Bereich ' verwenden
' Bild einlesen On Error GoTo Fehlerbehandlung ' Falls was schief geht Set Pic = LoadPicture(FileNames(Index)) Caption = "Bild" + Str(Index) + " von" + Str(UBound(FileNames)) + _ " – " + FileNames(0) + FileNames(Index) ' maximale Bildgröße berechnen BildHöhe = Pic.Height BildBreite = b BildHöhe = BildHöhe * BildBreite / Pic.Width If BildHöhe > h Then BildHöhe = h BildBreite = Pic.Width * BildHöhe / Pic.Height End If On Error GoTo 0 ' Bild im aktuellen Formular zentriert zeichnen Ansicht.Line (x, y)-(x + b, y + h), vbBlack, BF Ansicht.PaintPicture Pic, x + (b – BildBreite) / 2, _
520
Das Programmdesign
y + (h – BildHöhe) / 2, BildBreite, BildHöhe Exit Sub
DiaProjektor – SDI- Formulare synchronisieren
Fehlerbehandlung: Dim Text As String With Ansicht If Not bIndexView Then Text = "Bildformat nicht erkannt" ' Fehlertext mittig ausgeben .Cls .CurrentX = (.ScaleWidth – .TextWidth(Text)) / 2 .CurrentY = (.ScaleHeight – .TextHeight(Text)) / 2 ' With funktioniert bei Print und Line nicht!!! Caption = "Bildformat nicht erkannt" Ansicht.Print Caption Else Ansicht.Line (x, y)-(x + b, y + h), RGB(50, 50, 50), BF End If End With End Sub ' Fügt alle auf *.jpg, *.gif oder *.bmp endenden Dateinamen ' in das Array FileNames ein. Erweitert Array! Private Sub FindFiles(FileNames() As String) Dim Name As String Dim UName As String Name = Dir(FileNames(0) + "*.*") ' Erster Dateiname UName = UCase(Name) While Name <> "" If InStr(UName, ".JPG") Or InStr(UName, ".GIF") Or _ InStr(UName, ".BMP") Then ReDim Preserve FileNames(0 To UBound(FileNames) + 1) ' Vergrößern FileNames(UBound(FileNames)) = Name ' Dateiname einfügen End If Name = Dir ' nächster Dateiname UName = UCase(Name) Wend End Sub ' Fügt alle auf *.jpg, *.gif oder *.bmp endenden Dateinamen ' in das Array FileNames ein. Erweitert Array! Private Sub FS_FindFiles(FileNames() As String) Dim fs As New FileSystemObject Dim Datei As File Dim Path As Folder Dim UName As String Dim Idx As Long ' Zähler Set Path = fs.GetFolder(FileNames(0)) Idx = UBound(FileNames)
521
DiaProjektor SDI- Formulare synchronisieren
DiaProjektor – SDI- Formulare synchronisieren
ReDim FileNames(Idx To Path.Files.Count) ' mehr können es nicht werden For Each Datei In Path.Files UName = UCase(Datei.Name) ' Schreibweise einheitlich If InStr(UName, ".JPG") Or InStr(UName, ".GIF") Or _ InStr(UName, ".BMP") Then Idx = Idx + 1 FileNames(Idx) = Datei.Name End If Next ReDim Preserve FileNames(0 To Idx) ' Reduktion auf tatsächl. Anzahl End Sub ' Schaltet zwischen Einzeldia und Indexansicht Private Sub DiaIndex(Optional Caching As vbCaching) If bIndexView Then ' Schalter setzen mnuTimer3.Enabled = True ' Timer-Befehle zulassen mnuTimer5.Enabled = True mnuDateiTimer3.Enabled = True mnuDateiTimer5.Enabled = True mnuIndex.Caption = "&Indexansicht" ' Menübefehl aktualisieren bIndexView = False Else StopTimer mnuIndex.Caption = "&Einzeldia" bIndexView = True End If Anzeigen Caching End Sub ' Stellt Indexansicht zusammen Private Function ShowIndex(ByVal Ansicht As Form) As Boolean Dim x As Single, y As Single, b As Single, h As Single Dim Idx As Long, EndIndex As Long Static statCount As Long Dim dynCount As Long With Ansicht ' Vorbereitungen statCount = (statCount + 1) Mod 10000 ' stat. Rekursionsz. pflegen dynCount = statCount ' dyn. Rekursionszähler init. b = .ScaleWidth / cIdxSpalten ' Spaltenbreite errechnen h = .ScaleHeight / cIdxZeilen ' Zeilenhöhe errechnen EndIndex = (FileIndex + cLargeStep) .FontSize = 7 ' Schriftgröße vorgeben .Cls ' Bilder ab Startindex ausgeben For Idx = FileIndex To EndIndex x = ((Idx – FileIndex) Mod cIdxSpalten) * b + b * 0.025
522
Das Programmdesign
y = ((Idx – FileIndex) \ cIdxSpalten) * h ShowDia Idx, Ansicht, x, y, b * 0.95, h * 0.9
'
' Ereignisse zulassen. Formular geschlossen? ' Indexansicht beendet? Rekusiver Aufruf? If DoEvents = 0 Or bIndexView = False _ Or statCount <> dynCount Then statCount = statCount – 1 ' Rekursionszähler pflegen Exit Function End If
DiaProjektor – SDI- Formulare synchronisieren
' Dateiname unter Bild setzen If Idx <= UBound(FileNames) Then .CurrentX = x .CurrentY = y + h – .TextHeight("a") Ansicht.Print FileNames(Idx) End If Next Idx End With ShowIndex = True End Function ' Zeigt einzelnes Bild oder Index an. Verwendet bei Indexansicht ' das Anzeige-Steuerelement des jeweilgen Formulars als Cache Private Sub Anzeigen(Optional Caching As vbCaching) Static LetzterVollbIdx As Long Static LetzterFensterIdx As Long Dim Idx As Long If bIndexView Then ' Indexansicht? ' Index für erstes Bild der Indexansicht ausrechnen FileIndex = FileIndex – FileIndex Mod (cIdxZeilen * cIdxSpalten) + 1 If FileIndex > UBound(FileNames) Then FileIndex = 1 If Visible Then ' Fensteranzeige' ' Cache aktuell? If FileIndex = LetzterFensterIdx And Caching Then PaintPicture Cache, 0, 0 ' Ja: Cache zeichnen Else Idx = FileIndex ' Nein: Startindex merken If ShowIndex(ActualForm) Then ' Regulär beendet? Cache = Image ' Cache aktualisieren LetzterFensterIdx = Idx ' Index für Cache merken Else Exit Sub End If End If Else ' Vollbildanzeige ' Cache aktuell? If FileIndex = LetzterVollbIdx And Caching Then VollbildForm.PaintPicture VollbildForm.Cache, 0, 0 ' aus Cache Else
523
DiaProjektor – SDI- Formulare synchronisieren
DiaProjektor SDI- Formulare synchronisieren
Idx = FileIndex ' Nein: Startindex merken If ShowIndex(ActualForm) Then ' Regulär beendet? Set VollbildForm.Cache = VollbildForm.Image ' in Cache LetzterVollbIdx = Idx ' Index für Cache merken Else Exit Sub End If End If End If Caption = "Index ab Bild " & FileIndex & " – " _ & FileNames(FileIndex) Else ShowDia FileIndex, ActualForm ' Einzeldiaansicht End If End Sub Private Sub Kontextmenü() mnuDateiErstes.Visible = True ' unsichtbare Befehle in Dateimenü mnuDateiNächstes.Visible = True mnuDateiVoriges.Visible = True mnuDateiTimer3.Visible = True ' sichtbar machen mnuDateiTimer5.Visible = True PopupMenu mnuDatei If Not Beendet Then ' Formular noch geöffnet? mnuDateiErstes.Visible = False ' Befehle wieder verbergen mnuDateiNächstes.Visible = False mnuDateiVoriges.Visible = False mnuDateiTimer3.Visible = False mnuDateiTimer5.Visible = False End If End Sub '*********************************************************************** ' Projekt : DiaProjektor ' Autor : 2000 Rudolf Huttary ' Formularmodul : FullScreenForm '*********************************************************************** Public FirstForm As Form Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer) ' Tastaturbehandlung übernimmt das Formular MainForm FirstForm.Form_KeyDown KeyCode, Shift End Sub Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single) FirstForm.Form_MouseDown Button, Shift, X, Y End Sub '*********************************************************************** ' Projekt : DiaProjektor ' Autor : 2000 Rudolf Huttary
524
Info- Dialog als gebundenes Formular aufrufen
' Formularmodul : About.frm '*********************************************************************** Private Sub OK_Click() Unload Me End Sub
Ein nur oberflächlicher Blick auf die drei Codemodule verrät bereits, dass die gesamte Steuerung des Programmablaufs im Modul MainForm zu finden ist. Grund genug, die Module FullScreenForm und About schnell und nachhaltig abzuhaken.
Das eigentlich nur zur Demonstration des gebundenen Aufrufs sowie der Vollständigkeit halber eingeführte Formular About tut nichts weiter, als sich nach einem Klick auf die Schaltfläche OK gleich mittels Unload Me wieder zu verabschieden – die meiste Arbeit steckt somit in der visuellen Gestaltung. Der Show-Aufruf für das Formular findet sich im Modul MainForm, in der für den Menübefehl ? zuständigen Behandlungsroutine mnuAbout_Click: Private Sub mnuAbout_Click() About.Show vbModal End Sub
' Info-Dialog anzeigen
Immerhin hat der so einfach wirkende Aufruf zwei Besonderheiten: Erstens bewirkt der Wert vbModal für den optionalen Parameter, dass die Programmausführung von MainForm darauf wartet, bis das aufgerufene Formular die Kontrolle wieder zurückgibt – daher die Bezeichnung gebundenes Formular. Zweitens ist noch nicht einmal die Vereinbarung einer Objektvariable für den Aufruf des Formulars erforderlich, weil Visual Basic für jedes Formular eines Projekts implizit eine globale Objektvariable mit dem Bezeichner des Formulardatentyps vereinbart. Auf diese Weise ergibt sich die Möglichkeit der anonymen Instanziierung, die letztlich gar nicht anonym ist.
Ereignisse delegieren und Instanziierung durch One- Shot- Logik Das Formular FullScreenForm steht About an Einfachheit um nicht viel nach. Da es für die Vollbildanzeige bestimmt ist, muss seine BorderStyle-Eigenschaft bereits zur Entwurfszeit auf »0 – Kein« gesetzt werden, weil dies zur Laufzeit nicht mehr möglich ist. Die einzige Aktivität des Formulars besteht darin, eintreffende KeyDown- und MouseDown-Ereignisse unbesehen an seinen Aufrufer weiterzuleiten (Delegation). So also kann MainForm »am Drücker« bleiben, auch wenn FullScreenForm den Fokus hat. Die Umsetzung erfordert eine mit globalem Geltungsbereich vereinbarte Variable FirstForm, die der Aufrufer mit einer Referenz auf sich selbst initialisieren muss, und natürlich eine Public-Deklaration der jeweiligen Behandlungsroutinen aufseiten des Aufrufers: ' Im Modul FullScreenForm ... Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer) ' Tastaturbehandlung übernimmt das Formular MainForm FirstForm.Form_KeyDown KeyCode, Shift End Sub ' Im Modul MainForm ... Public Sub Form_KeyDown(KeyCode As Integer, Shift As Integer) ' Tastaturschnittstelle Select Case KeyCode ...
525
DiaProjektor – SDI- Formulare synchronisieren
Info- Dialog als gebundenes Formular aufrufen
DiaProjektor – SDI- Formulare synchronisieren
DiaProjektor SDI- Formulare synchronisieren
Die Instanziierung des Formulars hätte man freilich in die Load- oder gar Initialize-Behandlung von MainForm verlegen können, nachdem die entsprechenden Routinen ja nur einmal zum Aufruf kommen. Um keine Ressourcen zu vergeuden (vielleicht wird der Benutzer gar nicht in die Vollbildanzeige schalten?), geschieht sie im vorliegenden Fall jedoch erst bei Bedarf. Nämlich beim ersten Aufruf der Routine mnuVollbild_Click von MainForm. Deren Aufgabe besteht darin, zwischen der Fensteranzeige und der Vollbildanzeige hin- und herzuschalten. Zur Vermeidung einer wiederholten Instanziierung setzt die Routine eine One-Shot-Logik ein, welche die einmalige Ausführung des New-Aufrufs garantiert. Nachdem die Routine ohnehin darüber Buch führen muss, welche Art der Anzeige gerade aktiv ist, bietet sich eine statische IntegerVariable Viewstate als Zustandsvariable an, die Visual Basic bekanntlich nur für den ersten Eintritt in die Routine mit 0 initialisiert. Natürlich könnte man VollbildForm auch auf Nothing abfragen und ansonsten mit der Visible-Eigenschaft beispielsweise von MainForm arbeiten. Der Show-Aufruf, der hier nichts anders tut, als die Visible-Eigenschaft auf True zu setzen, erfolgt – wie angedeutet – gleichfalls in Abhängigkeit von Viewstate in derselben Routine. Private Sub mnuVollbild_Click() ' Umschalten: Einzeldia/Vollbild ' Aktivierung durch Menübefehl Vollbildanzeige/Fensteranzeige oder Esc Static Viewstate As Integer ResetTimer If Viewstate = 0 Then ' erster Aufruf? Set VollbildForm = New FullScreenForm ' Vollbildformular anlegen Set VollbildForm.FirstForm = Me ' Referenz auf MainForm Viewstate = 1 End If If Viewstate = 1 Then ' zur Vollbildanzeige? Visible = False Set ActualForm = VollbildForm mnuVollbild.Caption = "&Fensteranzeige" VollbildForm.Show Viewstate = 2 Else ' zur Fensteranzeige VollbildForm.Visible = False Set ActualForm = Me mnuVollbild.Caption = "&Vollbildanzeige" Show Viewstate = 1 End If Anzeigen cCaching End Sub
Bei VollbildForm handelt es sich um eine auf Modulebene vereinbarte Objektvariable des Typs Form, die somit in allen Routinen von MainForm sichtbar und beispielsweise zum expliziten Entladen des Formulars im Zuge der Unload-Behandlung vonnöten ist. Private Sub Form_Unload(Cancel As Integer) Beendet = True ' wegen DoEvents! ' Vollbildformular entladen If Not (VollbildForm Is Nothing) Then Unload VollbildForm End Sub
Die Prüfung, ob VollbildForm überhaupt eine gültige Referenz ist, ist hier unvermeidlich, da Unload (aus welchem Grund auch immer) mit dem Wert Nothing nichts anfangen kann.
526
Programmstart und Auswahl der Bilddateien im Standarddialog Öffnen
Programmstart und Auswahl der Bilddateien im Standarddialog Öffnen
Private Sub Form_Load() Set ActualForm = Me ReDim FileNames(0 To 1) Show mnuÖffnen_Click End Sub
' Fensteranzeige als Starteinstellung
' Ordner abfragen
Mit mnuÖffnen_Click geht das Programm bereits in den »Alltag« über, denn diesen Befehl kann auch der Benutzer jederzeit über die Menüs oder per Tastenbefehl auslösen. Seine Implementation unterteilt sich in drei Blöcke: Aufruf des Standarddialogs Öffnen für die Mehrfachauswahl; Auswertung des Pfads und Zusammenstellen einer Dateiliste im globalen Array FileNames; Vorbereitung und Veranlassung der Anzeige. Private Sub mnuÖffnen_Click() Dim bArrayNichtLeer As Boolean StopTimer ' Standdarddialog initialisieren und aufrufen dlgDateiOpen.Filter = "*.jpg;*.gif;*.bmp|*.jpg;*.gif;*.bmp" dlgDateiOpen.CancelError = True dlgDateiOpen.FileName = "(alle)" dlgDateiOpen.Flags = cdlOFNAllowMultiselect + cdlOFNExplorer _ + cdlOFNLongNames On Error GoTo Abbruch ' Falls Benutzer Dialog abbricht dlgDateiOpen.ShowOpen ' Standarddialog "Öffnen" ' Der folgende Code wird nicht ausgeführt, ' wenn der Öffnen-Dialog abgebrochen wurde! FileNames(0) = Left(dlgDateiOpen.FileName, _ InStrRev(dlgDateiOpen.FileName, "\")) ' Pfad FileIndex = 1 ' ab erstem Bild anzeigen
527
DiaProjektor – SDI- Formulare synchronisieren
Bei Programmen mit mehreren Formularen stellt sich die Frage, welches Formular Visual Basic beim Programmstart wie instanziiert. Dies ist leicht zu beantworten: Das Startformular wird im Eigenschaftsdialog im Listenfeld STARTOBJEKT ausgewählt – im vorliegenden Fall lautet der Eintrag also: MainForm. Die Instanziierung der anderen Formulare obliegt dann dem Startformular. MainForm dient als Formular für die Fensteransicht, das heißt, es hat einen Rahmen und lässt auch Größenänderungen zu. Die Entwurfsansicht fördert drei Steuerelemente zu Tage, ein Standarddialoge-Steuerelement, das die Dateiauswahl ermöglicht, ein Zeitgeber-Steuerelement für die Funktion des automatischen Bildwechsels sowie ein Anzeige-Steuerelement (wegen des schwarzen Hintergrunds schlecht zu sehen), das zur Laufzeit unsichtbar bleibt und als Cache für die Indexansicht fungiert. Da MainForm keine Initialize-Behandlung vornimmt, sieht das Formular als Erstes das LoadEreignis. Die Behandlungsroutine initialisiert die auf Modulebene vereinbarte Objektvariable ActualForm mit einer Referenz auf sich selbst. ActualForm wird später immer auf das Formular verweisen, das dem aktuellen Anzeigemodus entspricht – bei Programmstart ist das die Fensteranzeige – und so insbesondere alle Ausgabeoperationen dirigieren. Der Show-Aufruf noch vor der Dateiauswahl durch mnuÖffnen_Click ist eine kosmetische Maßnahme und ermöglicht es dem Benutzer, den meist zeitintensiven Aufbau der Indexansicht zu beobachten.
DiaProjektor – SDI- Formulare synchronisieren
DiaProjektor SDI- Formulare synchronisieren
If dlgDateiOpen.FileTitle = "(alle).jpg" Then ReDim Preserve FileNames(0 To 0) ' FS_FindFiles FileNames ' mit FileSystemObject-Objekt FindFiles FileNames ' mit Dir-Funktion bIndexView = False ' Indexansicht vorbereiten ElseIf InStr(dlgDateiOpen.FileName, Chr(0)) Then FileNames = Split(dlgDateiOpen.FileName, Chr(0)) FileNames(0) = FileNames(0) + "\" bIndexView = False ' Indexansicht vorbereiten Else ReDim FileNames(0 To 1) FileNames(1) = dlgDateiOpen.FileTitle bIndexView = True ' Einzeldiaansicht vorbereiten End If ' Menübefehle zum Bildwechsel aktivieren bArrayNichtLeer = FileNames(1) <> "" ' Wurden Dateien gefunden? mnuBildErstes.Enabled = bArrayNichtLeer mnuBildNächstes.Enabled = bArrayNichtLeer mnuBildVoriges.Enabled = bArrayNichtLeer mnuTimer3.Enabled = bArrayNichtLeer mnuTimer5.Enabled = bArrayNichtLeer DiaIndex cNoCaching ' Bild(er) Anzeigen Abbruch: End Sub
Der Umgang mit dem Standarddialog Öffnen ist im Wesentlichen business as usual, einzig die Mehrfachauswahl im Stile von Windows 9x erfordert das Setzen einiger zusätzlicher Flags. Ärgerlicherweise ist der Puffer für die Rückgabe des Dateinamens auch bei eingeschalteter Mehrfachauswahl auf etwas über 200 Zeichen begrenzt, so dass die Anzahl der auswählbaren Dateinamen recht klein ist. Einem ernsthaften Einsatz dieser Funktionalität steht weiterhin die recht erratisch implementierte Fehlererkennung des Steuerelements im Wege. Falls der Benutzer den Dialog abbricht, ist der Wert der Eigenschaft FileName nicht gültig. Damit das Steuerelement in diesem Fall einen Laufzeitfehler signalisiert, muss die CancelError-Eigenschaft auf True gesetzt werden. Die Behandlung des Laufzeitfehlers an der Sprungmarke Abbruch sieht nichts weiter als den Abbruch des gesamten Befehls vor – wobei eine gegebenenfalls bereits geladene Dateiliste erhalten bleibt. Nachdem kein On Error Goto 0 folgt, gilt das natürlich auch für alle anderen Laufzeitfehler, falls noch welche in der Routine auftreten sollten – eine nicht gerade penible, aber in verschiedenen Fällen durchaus vertretbare Programmierpraxis. Das Steuerelement tut die Auswahl des Benutzers über die Eigenschaften FileName und FileTitle kund. Dabei gibt es drei Fälle zu unterscheiden: 1. Der Benutzer hat nur eine einzelne Datei ausgewählt. Da FileName dann den qualifizierten Namen enthält, lassen sich Pfad und Dateiname trennen und in das Array FileNames übernehmen. Als Ansicht ist natürlich die Einzeldiaansicht sinnvoll. 2. Der Benutzer hat eine Mehrfachauswahl getroffen. Falls diese einen Pufferüberlauf produziert hat, ist der Dateiname ungültig, was das Programm im weiteren Verlauf mit der Fehlermeldung »Bildformat nicht erkannt« moniert. Eine korrekte Mehrfachauswahl ist daran zu erkennen, dass das Nullzeichen in der Eigenschaft FileName zu finden ist, welches die Namen-
528
Programmstart und Auswahl der Bilddateien im Standarddialog Öffnen
' Fügt alle auf *.jpg, *.gif oder *.bmp endenden Dateinamen ' in das Array FileNames ein. Erweitert Array! Private Sub FindFiles(FileNames() As String) Dim Name As String Dim UName As String Name = Dir(FileNames(0) + "*.*") ' Erster Dateiname UName = UCase(Name) While Name <> "" If InStr(UName, ".JPG") Or InStr(UName, ".GIF") Or _ InStr(UName, ".BMP") Then ReDim Preserve FileNames(0 To UBound(FileNames) + 1) ' Vergrößern FileNames(UBound(FileNames)) = Name ' Dateiname einfügen End If Name = Dir ' nächster Dateiname UName = UCase(Name) Wend End Sub ' Fügt alle auf *.jpg, *.gif oder *.bmp endenden Dateinamen ' in das Array FileNames ein. Erweitert Array! Private Sub FS_FindFiles(FileNames() As String) Dim fs As New FileSystemObject Dim Datei As File Dim Path As Folder Dim UName As String Dim Idx As Long ' Zähler Set Path = fs.GetFolder(FileNames(0)) Idx = UBound(FileNames) ReDim FileNames(Idx To Path.Files.Count) ' mehr können es nicht werden For Each Datei In Path.Files UName = UCase(Datei.Name) ' Schreibweise einheitlich If InStr(UName, ".JPG") Or InStr(UName, ".GIF") Or _ InStr(UName, ".BMP") Then
529
DiaProjektor – SDI- Formulare synchronisieren
seinträge trennt. Für die Aufspaltung in das Array FileNames ist Split das Mittel schlechthin, und auch der Pfadname landet da, wo er hin soll, da der erste Eintrag bei einer Mehrfachauswahl immer den Pfad benennt. Als Ansicht fungiert die Indexansicht. 3. Der Benutzer hat es im ÖFFNEN-Dialog bei der Voreinstellung »(alle)« belassen. In diesem Fall muss eine Liste aller im Verzeichnis befindlichen Grafikdateien des Typs JPG, BMP oder GIF zusammengestellt werden, bevor die Bilder in der Indexansicht angezeigt werden können. Sie finden dafür im Code die beiden Prozeduren FindFiles und FS_FindFiles, die sich alternativ für diese Aufgabe einsetzen lassen. FindFiles arbeitet mit der Dir-Funktion von Visual Basic, während FS_FindFiles ein FileSystemObject-Objekt anfordert, das letztlich die gleiche Information liefert, nur um etliches langsamer – was bereits bei über hundert Dateien unangenehm wird. Im Zusammenhang mit dem FileSystemObject-Objekt könnte man auch auf den Gedanken kommen, das Auswahlkriterium über die Eigenschaft Type des File-Objekts zu formulieren, das ist jedoch nicht empfehlenswert, da gerade für die betroffenen Dateitypen viele unterschiedliche Registrierungen im Umlauf sind.
DiaProjektor SDI- Formulare synchronisieren
DiaProjektor – SDI- Formulare synchronisieren
Idx = Idx + 1 FileNames(Idx) = Datei.Name End If Next ReDim Preserve FileNames(0 To Idx) End Sub
' Reduktion auf tatsächl. Anzahl
Beide Funktionen erwarten den Pfadnamen als Element mit dem Index 0 im Array FileNames und durchsuchen dann – jede auf ihre Weise – das gesamte Verzeichnis nach Dateien des Typs JPG, BMP, GIF. Die gefundenen Dateinamen landen schließlich der Reihe nach im Array (diese Reihenfolge bestimmt später auch die Bildabfolge), das in FindFiles mit jedem gefundenen Dateinamen um ein Element erweitert wird, in FS_FindFiles dagegen nur einmal recht großzügig, und zum Schluss wieder zurechtgestutzt wird. Beachten Sie, dass die Funktionen mit Blick auf eine zukünftige Verwendung keine Vorannahmen über den aktuellen Füllzustand des Arrays treffen. In Kombination mit dem Indexzeiger FileIndex stellt das Array FileNames die Datenbasis für das Anzeigesystem dar. In der vorliegenden Implementation wird die Datenbasis bei jedem ÖFFNEN-Befehl neu angelegt.
Das Anzeigesystem Trotz seiner Vielschichtigkeit ist das Anzeigesystem weitgehend hierarchisch aufgebaut, wie Sie am besten dem in der Grafik dargestellten Aufrufdiagramm entnehmen.
Aufrufzusammenhang zwischen Benutzerschnittstelle und Anzeigesystem Umsc haltung zwisc hen Indexansic ht und Vollbildansic ht
An oberster Stelle der Hierarchie steht die Prozedur DiaIndex, die zwischen der Einzeldiaansicht und der Indexansicht hin- und herschaltet. Sie ist für die Pflege der auf Modulebene bekannten Zustandsvariable bViewState und den Aufruf der Routine Anzeigen verantwortlich. Außerdem berichtigt die Routine den Zeiger FileIndex dahingehend, dass er immer auf das nächst kleinere Vielfache von cLargeStep (hier: 20) plus 1 verweist. Das hat nicht nur Vorteile für das Caching,
53 0
Das Anzeigesystem
Enum vbCaching cCaching = True cNoCaching = False End Enum ... ' Schaltet zwischen Einzeldia und Indexansicht Private Sub DiaIndex(Optional Caching As vbCaching) If bIndexView Then ' Schalter setzen mnuTimer3.Enabled = True ' Timer-Befehle zulassen mnuTimer5.Enabled = True mnuDateiTimer3.Enabled = True mnuDateiTimer5.Enabled = True mnuIndex.Caption = "&Indexansicht" ' Menübefehl aktualisieren bIndexView = False Else StopTimer mnuIndex.Caption = "&Einzeldia" bIndexView = True FileIndex = FileIndex – FileIndex Mod (cIdxZeilen * cIdxSpalten) + 1 End If Anzeigen Caching End Sub ... Private Sub StopTimer() Timer1.Enabled = False ' Timer ausschalten Timer1.Interval = 0 mnuTimer3.Checked = False mnuTimer5.Checked = False mnuDateiTimer3.Checked = False mnuDateiTimer5.Checked = False mnuTimer3.Enabled = False mnuTimer5.Enabled = False mnuDateiTimer3.Enabled = False mnuDateiTimer5.Enabled = False End Sub
Der Code der Routine DiaIndex weist wenig Besonderheiten auf, außer dass der Parameter Caching als optionaler Parameter mit dem Enum-Typ vbCaching definiert ist und unbesehen der
53 1
DiaProjektor – SDI- Formulare synchronisieren
es verleiht der Indexansicht auch den Charakter eines eigenständigen Bildes. Darüber hinaus fällt der Routine die Aufgabe zu, die Menübefehle für den automatischen Bildwechsel für die Indexansicht zu deaktivieren (StopTimer) sowie für die Einzeldiaansicht zu aktivieren. Sie können den StopTimer-Aufruf aber auch auskommentieren, denn vom Prinzip her ist die vom Anzeigesystem verwendete DoEvents-Logik gegenüber einem Bildwechsel während des Bildaufbaus unempfindlich. Bei hochauflösenden Bildern oder leistungsschwachen Systemen kann es allerdings passieren, dass der Bildaufbau der Weiterschaltung nicht hinterherkommt. Von der Programmiertechnik her gesehen, ist die Einflussnahme auf die Enable-Eigenschaft von Menübefehlen ein sehr wirksames und gleichzeitig elegantes Mittel, um in einem spezifischen Kontext bestimmte Befehle lahm zu legen bzw. zuzulassen. Gleichzeitig stellen die Werte der Eigenschaften Visible, Enabled und Checked der Menüeinträge eine Art Zustandsbeschreibung der Benutzerschnittstelle dar. Scheuen Sie sich also nicht, sie zur Steuerung der Programmlogik einzusetzen.
DiaProjektor SDI- Formulare synchronisieren
DiaProjektor – SDI- Formulare synchronisieren
Routine Anzeigen übergeben wird. Enum-Aufzählungen sind unter Visual Basic zwar keine eigenständigen Typen und schränken insbesondere auch den zugrunde liegenden Wertebereich Long nicht ein, sie bieten aber den Vorteil, dass sie Konstanten mit »sprechenden Namen« bereitstellen und dass der Code-Editor die gesamte Aufzählung einblendet, wo ein Wert dieses »Typs« erwartet wird.
Caching Die Logik der Prozedur Anzeigen ist da schon etwas komplizierter. Hauptaufgabe dieser Routine ist das nach Vollbildanzeige und Fensteranzeige getrennte Caching der jeweils aktuellen Indexansicht. Zu diesem Zweck pflegt die Routine getrennt nach Anzeige zwei statische Variablen mit dem Bildindex der jeweils als letztes fertig (!) gezeichneten Indexansicht und überträgt außerdem den über die Image-Eigenschaft zugänglichen Formularinhalt in das unsichtbare (!) Anzeige-Steuerelement Cache des jeweiligen Formulars. Falls der Bildindex der aktuellen Anzeige bei Eintritt in die Routine dem für den Cache gespeicherten Wert entspricht, wird nur der Cache-Inhalt, also die Picture-Eigenschaft des Anzeige-Steuerelements, in das Formular gezeichnet, anderenfalls kommt es zum Aufruf der Funktion ShowIndex, die für das Zeichnen der Indexansicht zuständig ist. Die Funktion liefert True, wenn die Indexansicht fertig gezeichnet werden konnte und steuert die Auffrischung des Cache. Erfolgt der Aufruf der Routine bei aktiver Einzeldiaansicht, passiert nichts weiter als ein ShowDia-Aufruf. Ein Caching der Einzeldiaansicht ist unnötig, weil ein Einzeldia wohl selten mehrmals hintereinander angezeigt wird. Das Caching der Indexansicht hat aber den enormen Vorteil, dass der Benutzer schnell zwischen den beiden Ansichten umschalten kann, wenn er Bilder über die Indexansicht für die Einzeldiaansicht auswählt. ' Zeigt einzelnes Bild oder Index an. Verwendet bei Indexansicht ' das Anzeige-Steuerelement des jeweilgen Formulars als Cache Private Sub Anzeigen(Optional Caching As vbCaching) Static LetzterVollbIdx As Long Static LetzterFensterIdx As Long Dim Idx As Long If bIndexView Then ' Indexansicht? ' Index für erstes Bild der Indexansicht ausrechnen FileIndex = FileIndex – FileIndex Mod (cIdxZeilen * cIdxSpalten) + 1 If FileIndex > UBound(FileNames) Then FileIndex = 1 If Visible Then ' Fensteranzeige' ' Cache aktuell? If FileIndex = LetzterFensterIdx And Caching Then PaintPicture Cache, 0, 0 ' Ja: Cache zeichnen Else Idx = FileIndex ' Nein: Startindex merken If ShowIndex(ActualForm) Then ' Regulär beendet? Cache = Image ' Cache aktualisieren LetzterFensterIdx = Idx ' Index für Cache merken Else Exit Sub End If End If Else ' Vollbildanzeige ' Cache aktuell? If FileIndex = LetzterVollbIdx And Caching Then VollbildForm.PaintPicture VollbildForm.Cache, 0, 0 ' aus Cache
53 2
Das Anzeigesystem
Der Code der Routine enthält eine ganze Reihe von Fallunterscheidungen, die aber alle recht durchsichtig sind. Interessant ist die Situation beim ersten Aufruf der Routine in der Indexansicht. Da Visual Basic die statischen Variablen mit 0 initialisiert und der Index des ersten Bildes 1 ist, kommt es in jedem Fall zum Neuzeichnen der Ansicht. Um das Neuzeichnen der Indexansicht aber auch am Cache vorbei erzwingen zu können, gibt es den Aufrufparameter Caching. Diese Möglichkeit ist speziell für Form_Resize und mnuÖffnen_Click wichtig. Zur Unterscheidung der Vollbildanzeige von der Fensteranzeige wird die Visible-Eigenschaft von MainForm herangezogen. Zeic hnen der Indexansic ht
Die Aufgabe der Funktion ShowIndex besteht darin, die Indexansicht in das aktuelle Formular zu zeichnen. Da sie sich dazu der Prozedur ShowDia bedienen kann, die ein Einzelbild einliest und geeignet skaliert in den angegebenen Bereich zeichnet, muss sie sich nur um die Aufteilung des Anzeigebereichs in Zeilen und Spalten sowie um die Beschriftung der Einzeldias kümmern. In der Praxis eröffnet allerdings die Tatsache, dass der Bildaufbau einige Sekunden in Anspruch nehmen kann, ein weiteres Problemfeld: Es ist eine DoEvents-Ablaufsteuerung erforderlich, damit der Benutzer den Bildaufbau überhaupt verfolgen kann und die Reaktivität der Benutzerschnittstelle während des Bildaufbaus erhalten bleibt. Für gewöhnlich handelt man sich mit DoEvents gleich einen ganzen Sack voller Probleme ein. Hier ist das nicht anders. Probleme gibt es im Zusammenhang mit: 1. 2. 3. 4.
dem Caching bei rekursiven Aufrufen dem Wechsel des Anzeigemodus während des Bildaufbaus dem Umschalten in die Einzeldiaansicht dem vorzeitigen Schließen des Formulars.
Jedem der Probleme wirkt die Implementation gesondert entgegen: 1. Nachdem immer nur die letzte Indexansicht in einem Anzeigemodus fertig gezeichnet werden muss, führt die Routine über einen statischen und einen dynamischen Aufrufzähler darüber Buch, wie tief sie in der Rekursion steckt. Ein ungleicher Zählerstand weist darauf hin, dass der von der Rekursionsstufe gezeichnete Inhalt nicht mehr aktuell ist, und führt zum Abbruch der Rekursionsstufe. 2. Eine optionale Pflege des Aufrufzählers bei Abbruch der Routine kann das Fertigzeichnen einer begonnenen Indexansicht auch bei einem Wechsel des Anzeigemodus sicherstellen. Gezeichnet wird dann im unsichtbaren Formular!
533
DiaProjektor – SDI- Formulare synchronisieren
Else Idx = FileIndex ' Nein: Startindex merken If ShowIndex(ActualForm) Then ' Regulär beendet? Set VollbildForm.Cache = VollbildForm.Image ' in Cache LetzterVollbIdx = Idx ' Index für Cache merken Else Exit Sub End If End If End If Caption = "Index ab Bild " & FileIndex & " – " _ & FileNames(FileIndex) Else ShowDia FileIndex, ActualForm ' Einzeldiaansicht End If End Sub
DiaProjektor SDI- Formulare synchronisieren
DiaProjektor – SDI- Formulare synchronisieren
3. Jeder Wechsel in die Einzeldiaansicht bricht den Bildaufbau der Indexansicht umgehend in allen Rekursionsstufen ab. 4. Eine Auswertung des von DoEvents gelieferten Werts bricht den Bildaufbau der Indexansicht umgehend in allen Rekursionsstadien ab, wenn der Benutzer das Programm beenden will. 5. ShowIndex liefert nur dann den Funktionswert True, wenn die Indexansicht erfolgreich zu Ende gezeichnet werden konnte. Das ist wichtig für das Caching auf der Ebene von DiaIndex. ' Stellt Indexansicht zusammen Private Function ShowIndex(ByVal Ansicht As Form) As Boolean Dim x As Single, y As Single, b As Single, h As Single Dim Idx As Long, EndIndex As Long Static statCount As Long Dim dynCount As Long With Ansicht ' Vorbereitungen statCount = (statCount + 1) Mod 10000 ' stat. Rekursionsz. pflegen dynCount = statCount ' dyn. Rekursionszähler init. b = .ScaleWidth / cIdxSpalten ' Spaltenbreite errechnen h = .ScaleHeight / cIdxZeilen ' Zeilenhöhe errechnen EndIndex = (FileIndex + cLargeStep) .FontSize = 7 ' Schriftgröße vorgeben .Cls ' Bilder ab Startindex ausgeben For Idx = FileIndex To EndIndex x = ((Idx – FileIndex) Mod cIdxSpalten) * b + b * 0.025 y = ((Idx – FileIndex) \ cIdxSpalten) * h ShowDia Idx, Ansicht, x, y, b * 0.95, h * 0.9
'
' Ereignisse zulassen. Formular geschlossen? ' Indexansicht beendet? Rekusiver Aufruf? If DoEvents = 0 Or bIndexView = False _ Or statCount <> dynCount Then statCount = statCount – 1 ' Rekursionszähler pflegen Exit Function End If
' Dateiname unter Bild setzen If Idx <= UBound(FileNames) Then .CurrentX = x .CurrentY = y + h – .TextHeight("a") Ansicht.Print FileNames(Idx) End If Next Idx End With ShowIndex = True End Function
Die Funktion beginnt mit der Pflege der Rekursionszähler: Sie erhöht den für alle Rekursionsstufen nur einmal existierenden statischen Zähler statCount und überträgt den Zählerstand in
53 4
Bilder einlesen und in maximaler Größe zeichnen
Const cIdxZeilen = 4 Const cIdxSpalten = 5 Const cLargeStep = cIdxZeilen * cIdxSpalten ' Seitenwert für Bildwechsel
Bei der Berechnung der Begrenzungsrechtecke für die Miniaturen wird ein Rand von fünf Prozent zwischen den Bildern sowie ein Rand für den direkt unter das Begrenzungsrechteck gezeichneten Dateinamen berücksichtigt. Die Schriftgröße für die Ausgabe ist fest mit 7 Punkt vorgegeben. Es steht Ihnen frei, all diese Werte anders zu definieren oder gar dem Benutzer die Möglichkeit der Einflussnahme im Rahmen eines zusätzlichen Optionen-Dialogs zu überlassen.
Bilder einlesen und in maximaler Größe zeichnen Frei nach dem Motto »den Letzten beißen die Hunde« fällt ShowDia schließlich der Part zu, das über den Parameter Index spezifizierte Bild mit LoadPicture einzulesen und bestmöglich in den verfügbaren Bereich eingepasst zu zeichnen. Eine passende Fehlerbehandlung sorgt dafür, dass Fehler während des Einlesens oder aufgrund von Visual Basic nicht akzeptierter Bildformate in der Einzeldiaansicht mit dem Text »Bildformat nicht erkannt« und in der Indexansicht schlicht mit einem dunkelgrauen Rechteck bedacht werden. Falls alles gut geht, setzt die Routine den Dateinamen in die Titelleiste nebst Bildnummer und Gesamtanzahl der Bilder, berechnet die Größe und zentrierte Lage des Bildes im Anzeigebereich und zeichnet es schließlich mittels PaintPicture, nachdem es ein schwarzes Rechteck in den Bereich gezeichnet hat. ' Lädt Bild von Datei und zeichnet ' im übergebenen Bereich Private Sub ShowDia(Index As Long, Optional x As Single, Optional y Optional b As Single, Optional h
es in maximaler Größe Ansicht As Form, _ As Single, _ As Single)
Dim Pic As Picture Dim BildBreite, BildHöhe If b * h = 0 Then b = Ansicht.ScaleWidth h = Ansicht.ScaleHeight End If
' Parameter weggelassen? ' Abmessungen von Client-Bereich ' verwenden
535
DiaProjektor – SDI- Formulare synchronisieren
den bei jedem Aufruf neu angelegten dynamischen Zähler dynCount. Tritt im Zuge von DoEvents Rekursion auf, kann der Fall eintreten, dass sich die Zählerstände unterscheiden, sobald die gezeichnete Ansicht nicht mehr aktuell ist. Das ergibt ein gutes Kriterium für den Abbruch unterbrochener Rekursionsstufen. Vielleicht ist es Ihnen aufgefallen: Die Funktion zeichnet die Ansicht nicht mittels ActualForm, sondern lässt sich das Zielformular über einen ByVal-Parameter bereitstellen. Obwohl man bei Rekursion generell nicht auf globale Werte zugreifen sollte, wäre es hier kein Problem, mit ActualForm zu arbeiten, schließlich bricht die Rekursion ja immer am tiefsten Punkt ab. Dass der Parameter dennoch da ist, hat folgenden subtilen Hintergrund: Belässt man die im Then-Teil der DoEvents-Analyse auskommentierte Zeile zur Pflege des statischen Zählers im Code, kann die Routine eine angefangene Ansicht in der Rekursion auch im unsichtbaren Formular fertig zeichnen und dafür braucht sie dann eine Referenz auf das Formular, für das der Aufruf ursprünglich stattfand. Ohne die Zeile könnte der Aufrufparameter in der vorliegenden Situation tatsächlich entfallen. Kern der Funktion ist die Schleife, die die Ausgabepositionen für die einzelnen Miniaturen berechnet und ShowDia für die Bildausgabe aufruft. Die Einteilung der Anzeige in Zeilen und Spalten richtet sich nach eigens dafür definierten Konstanten. cLargeStep gibt die Anzahl der Miniaturen pro Ansicht wieder und geht somit auch in die Berechnung des Bildindex beim Blättern in der Indexansicht ein.
DiaProjektor SDI- Formulare synchronisieren
DiaProjektor – SDI- Formulare synchronisieren
' Bild einlesen On Error GoTo Fehlerbehandlung ' Falls was schief geht Set Pic = LoadPicture(FileNames(Index)) Caption = "Bild" + Str(Index) + " von" + Str(UBound(FileNames)) + _ " – " + FileNames(0) + FileNames(Index) ' maximale Bildgröße berechnen BildHöhe = Pic.Height BildBreite = b BildHöhe = BildHöhe * BildBreite / Pic.Width If BildHöhe > h Then BildHöhe = h BildBreite = Pic.Width * BildHöhe / Pic.Height End If On Error GoTo 0 ' Bild im aktuellen Formular zentriert zeichnen Ansicht.Line (x, y)-(x + b, y + h), vbBlack, BF Ansicht.PaintPicture Pic, x + (b – BildBreite) / 2, _ y + (h – BildHöhe) / 2, BildBreite, BildHöhe Exit Sub Fehlerbehandlung: Dim Text As String With Ansicht If Not bIndexView Then Text = "Bildformat nicht erkannt" ' Fehlertext mittig ausgeben .Cls .CurrentX = (.ScaleWidth – .TextWidth(Text)) / 2 .CurrentY = (.ScaleHeight – .TextHeight(Text)) / 2 ' With funktioniert bei Print und Line nicht!!! Caption = "Bildformat nicht erkannt" Ansicht.Print Caption Else Ansicht.Line (x, y)-(x + b, y + h), RGB(50, 50, 50), BF End If End With End Sub
Die Aufrufparameter für die Bereichsdefinition sind als optionale Parameter vereinbart und müssen daher nicht unbedingt angegeben werden. Falls einer der Parameter für die Bereichsabmessungen b oder h den Wert 0 hat, nimmt die Routine dafür die aktuellen Abmessungen des Anzeigebereichs. Damit kann sie für die Einzeldiaansicht ohne Bereichsangaben aufgerufen werden.
Vollbildanzeige Für gewöhnlich sollte sich ein Programm im Hinblick auf andere eventuell gleichzeitig laufende Anwendungen eher bescheiden und zurückhaltend auf Fenster beschränken, die dem Benutzer eine Veränderung der Größe und der Position sowie einen Wechsel des Anzeigezustands ermöglichen. Dennoch gibt es aber Anwendungsbereiche, für die das selbstbewusste Auftreten einer
53 6
Vollbildanzeige
1. Fügen Sie in Ihr Projekt neben dem normalen Ausgabeformular ein eigenes, menüloses Formular ein, das die Vollbildanzeige übernimmt. 2. Setzen Sie die Eigenschaft BorderStyle des Formulars bereits im Entwurfsmodus auf 0. 3. Setzen Sie die Eigenschaft WindowState im Entwurfsmodus auf »2 Maximiert« oder zur Laufzeit auf vbMaximized bzw. 2. 4. Sehen Sie im normalen Ausgabeformular eine Möglichkeit zum Umschalten in den Vollbildmodus vor, etwa durch einen geeigneten Menübefehl. 5. Sehen Sie im Vollbildformular eine Möglichkeit zum Umschalten in den normalen Ausgabemodus vor – etwa durch Implementation von Form_KeyDown, Form_Click oder ein Kontextmenü. Visual Basic 6.0 erlaubt es zwar, die BorderStyle-Eigenschaft (entgegen anders lautender Informationen in der Online-Hilfe) zur Laufzeit zu ändern, die Eigenschaft ControlBox ist jedoch schreibgeschützt. Somit lässt sich ein Fenster mit Rahmen auch zur Laufzeit in den Vollbildmodus versetzen, wenn die Eigenschaft ControlBox zur Entwurfszeit auf False gesetzt wurde. In diesem Fall reicht es, zur Laufzeit Caption auf die leere Zeichenfolge, BorderStyle auf vbSNone bzw. 0 und gegebenenfalls die Eigenschaft Visible aller Menüobjekte auf False zu setzen. Wer nun glaubt, es sei möglich, ohne eigenes Vollbildformular auszukommen, der hat Recht: Im Prinzip geht das; mit einem Hauptformular ohne Systemmenü lässt sich allerdings nicht allzu viel anfangen, da es in der Titelleiste weder Schaltflächen für Maximieren/Wiederherstellen und Minimieren besitzt noch dem Benutzer sonstwie eine Größenveränderung ermöglicht. Das Anlegen des zweiten Formulars, der Umgang mit Benutzereingaben sowie die Zustandspflege der Formulare für die Vollbildanzeige wurden bereits im Abschnitt »Ereignisse delegieren und Instanziierung durch One-Shot-Logik« (S. 525) vorgestellt. Bleibt also noch zu klären, nach welcher Technik die Aktualisierung der Anzeige erfolgt. Falls beide Anzeigen die gleiche Ansicht darstellen, wie es im diskutierten Beispiel der Fall ist, macht es natürlich Sinn, für beide Anzeigen die gleichen Ausgaberoutinen zu verwenden und den Zugriff auf die Eigenschaften und Methoden der Formulare über eine Objektvariable ActualForm des Typs Form zu bewerkstelligen. Die Umschaltung zwischen der Fensteranzeige und der Vollbildanzeige sieht vom Prinzip her so aus: Private ActualForm As Form ' aktuelles Formular ... If Visible Then ' zur Vollbildanzeige? Hide Set ActualForm = VollbildForm VollbildForm.Show
537
DiaProjektor – SDI- Formulare synchronisieren
Anwendung im Vollbildmodus nicht nur erwünscht, sondern als möglicher Anzeigezustand geradezu gefordert ist. Man denke etwa an Malprogramme, Fotoeditoren oder an visuelle Präsentationen, wie sie in vielen Multimediaprodukten und Spielen zu finden sind. Freilich wird man als Programmierer diesen Modus, der die Windows-Oberfläche zu verschleiern vermag, nur als Option anbieten, da er die Benutzerschnittstelle doch weitgehend auf die Möglichkeiten einschränkt, wie sie DOS-Programmen seinerzeit zur Verfügung standen: die Navigation mit der Tastatur und die Reaktion auf Mausereignisse. Viele Anwendungen mit optionaler Vollbildansicht – DiaProjektor macht da keine Ausnahme – erlauben die umgehende Rückkehr in den rahmengebundenen Anzeigemodus über die (Esc)-Taste, einen Mausklick oder ein Kontextmenü. Voraussetzung für die programmtechnische Umsetzung der Vollbildanzeige ist ein Formular ohne Rahmenleiste und ohne Menü. Erfreulicherweise blendet Windows bei maximierter Anzeige eines solchen Formulars auch die Taskleiste aus. Für die Implementation gehen Sie wie folgt vor:
DiaProjektor SDI- Formulare synchronisieren
Else VollbildForm.Hide Set ActualForm = Me Show End If
' zur Fensteranzeige
und die Ausgabe so:
DiaProjektor – SDI- Formulare synchronisieren
ActualForm.PaintPicture Pic, x, y
Trick die erschlichene Vollbildanzeige Wenn Sie wollen, können Sie sich jedoch auch einen Trick zunutze machen, auf höchst einfache und recht ungewöhnliche Weise zu einer Vollbildansicht zu kommen, bei der sogar die gesamte Menüfunktionalität für die Bedienung durch Zugriffstasten vollständig gewährleistet bleibt. Der einzige Nachteil: Die Taskleiste von Windows verschwindet nicht. Der Trick ist verblüffend einfach – wenn man ihn kennt: Sie definieren die Abmessungen und die Position des Fensters einfach so, dass der Rahmen des Fensters vollständig jenseits des sichtbaren Bildbereichs zu liegen kommt und der Client-Bereich exakt den Bereich des ScreenObjekts einnimmt. Top = (ScaleHeight – Height) – (ScaleWidth – Width) / 2 Left = (ScaleWidth – Width) / 2 Height = Height – ScaleHeight + Screen.Height Width = Width – ScaleWidth + Screen.Width Left = ScaleWidth – Width
Menü und Kontextmenü In grafischen Benutzeroberflächen wie Windows sind es Benutzer gewohnt, Anwendungen intuitiv über Menüs bedienen zu können, ohne irgendwelche Tastenkombinationen auswendig lernen zu müssen. Beim Entwurf eines Programms sollten Sie daher darauf achten, alle wichtigen Operationen als Menübefehle zu implementieren. Die Technik dafür ist sehr einfach: 1. Rufen Sie in der Entwurfsansicht das Kontextmenü des Formulars auf und wählen Sie darin den Befehl MENÜ-EDITOR. In dem erscheinenden gleichnamigen Dialog können Sie nun der Reihe nach alle Menüs sowie die dazugehörigen Menübefehle definieren. 2. Legen Sie ein neues Menü (beispielsweise DATEI) an, indem Sie einen neuen Eintrag im Listenfeld markieren und in den Textfeldern CAPTION und NAME den im Menü erscheinenden Menünamen sowie den für das Menüobjekt im Code verwendeten Bezeichner eintragen. Damit das Menü über eine (Alt)-Kombination zugänglich wird, stellen Sie dem zu unterstreichenden Buchstaben das Zeichen »&« voran. 3. Legen Sie die Menübefehle an, indem Sie zuerst auf die Schaltfläche NÄCHSTER und dann auf die Pfeilschaltfläche RECHTS klicken. Sie erhalten einen neuen Eintrag, der etwas eingerückt erscheint. Die Definition des Menübefehlnamens sowie des im Code verwendeten Bezeichners erfolgt wie in 2. Falls Sie für den Befehl zusätzlich noch ein Tastenkürzel vereinbaren wollen, können Sie dieses aus dem Angebot im Listenfeld SHORTCUT auswählen. Das Laufzeitsystem filtert Tastenkürzel heraus und generiert automatisch die dazugehörigen Menüereignisse. Trennlinien fügen Sie ein, indem Sie anstelle eines Menübefehlnamens ein Minuszeichen »-« eintragen. Um mehrere Befehle unter gleichem Bezeichner in einem Bezeichnerarray zusammenzufassen, definieren Sie wie bei Steuerelemente-Arrays die Index-Eigenschaft im Textfeld INDEX. Abgesehen davon müssen alle Bezeichner von Menübefehlen, Menüs und Trennlinien eindeutige Bezeichner tragen.
53 8
Menü und Kontextmenü
4. Falls Sie einen Befehl als Untermenü auslegen wollen, rücken Sie die ihm untergeordneten Befehle mittels der Pfeilschaltfläche RECHTS einfach um eine Stelle weiter ein. Die Einrückung drückt die hierarchische Beziehung aus. 5. Legen Sie die Anzeigeeigenschaften Visible, Enabled und Checked der Menübefehle entweder gleich zur Entwurfszeit oder später zur Laufzeit fest.
DiaProjektor – SDI- Formulare synchronisieren
Der Menüeditor Menübefehle, deren Visible-Eigenschaft auf False gesetzt ist, sind zur Laufzeit nicht nur nicht sichtbar, auch ihre Zugriffstasten funktionieren nicht. Menübefehle, deren Enabled-Eigenschaft auf False gesetzt ist, bleiben zwar sichtbar, erscheinen aber aufgehellt und reagieren weder auf die Maus noch auf Zugriffstasten oder Tastenkürzel. Die vom Menü-Editor standardmäßig auf False gesetzte Checked-Eigenschaft bestimmt, ob der Menübefehl mit oder ohne Häkchen dargestellt wird. Die Visible-Eigenschaft bringt eine gewisse Flexibilität in die ansonsten unter Visual Basic recht dürftig ausgefallenen Möglichkeiten für die codeseitige Gestaltung von Menüs. Visual Basic erlaubt es nicht, bestehende Menüs zur Laufzeit zu erweitern oder Menübefehle einfach nur »umzuhängen«. Auch das dynamische Erweitern von Befehlearrays funktioniert nicht. Der einzige verbleibende Spielraum ergibt sich durch Ein- und Ausblenden von Befehlen über deren Visible-Eigenschaft sowie die Änderung der Caption-Eigenschaft – Tastenkürzel, deren Definition eindeutig sein muss, bleiben immer an die Menüschaltfläche gebunden, unabhängig von deren Beschriftung, Zugriffstasten richten sich nur nach der Beschriftung.
539
DiaProjektor SDI- Formulare synchronisieren
Die Definition, die in der Abbildung des Menü-Editorfensters zu sehen ist, erzeugt sowohl das Dateimenü als auch das Kontextmenü des Beispielprogramms DiaProjektor – die VisibleEigenschaft macht es möglich (!).
DiaProjektor – SDI- Formulare synchronisieren
Kontextmenüs Gerade in einem Programm wie DiaProjektor, das mit einer Vollbildanzeige ausgestattet ist, aber auch in vielen anderen Fällen bietet es Vorteile, wenn der Benutzer die aktuell möglichen Operationen in Form eines Kontextmenüs per rechtem Mausklick im schnellen Zugriff hat. Die Definition eines Kontextmenüs erfolgt genauso wie die eines gewöhnlichen Menüs, außer dass Sie die Visible-Eigenschaft des Menüs auf False setzen sollten, damit es nicht in der Menüleiste erscheint. Sie haben aber auch die Möglichkeit, gewöhnliche Menüs oder Untermenüs als Kontextmenüs aufzurufen, was den Vorteil hat, dass Sie für die Befehle keine eigenen Behandlungsroutinen implementieren müssen, sondern auf die bestehenden Routinen aufbauen können. Das in DiaProjektor verwendete Kontextmenü ist eine Erweiterung des Dateimenüs bzw. andersherum: Das Dateimenü ist ein in Teilen ausgeblendetes Kontextmenü. Der Code für den Aufruf lautet: Private Sub Kontextmenü() mnuDateiErstes.Visible = True ' unsichtbare Befehle in Dateimenü mnuDateiNächstes.Visible = True mnuDateiVoriges.Visible = True mnuDateiTimer3.Visible = True ' sichtbar machen mnuDateiTimer5.Visible = True PopupMenu mnuDatei If Not Beendet Then ' Formular noch geöffnet? mnuDateiErstes.Visible = False ' Befehle wieder verbergen mnuDateiNächstes.Visible = False mnuDateiVoriges.Visible = False mnuDateiTimer3.Visible = False mnuDateiTimer5.Visible = False End If End Sub
Wie die If-Struktur in dieser Implementation erkennen lässt, kann auch für die PopupMenuMethode das im Zusammenhang mit DoEvents bereits andernorts vorgestellte »Stehaufmännchenproblem« auftreten, nämlich, dass ein über das Kontextmenü geschlossenes Formular sofort wieder geladen wird, wenn nach dem PopupMenu-Aufruf noch ein Zugriff auf eine Eigenschaft oder Methode des Formulars erfolgt. Abhilfe schafft eine auf Modulebene vereinbarte Boolean-Variable Beendet, die während der Unload-Behandlung gesetzt wird. Private Sub Form_Unload(Cancel As Integer) Beendet = True ' wegen PopupMenu ' Vollbildformular entladen If Not (VollbildForm Is Nothing) Then Unload VollbildForm End Sub
Menüpflege und Timer Ein gut gepflegtes Menü ist für den Benutzer eine wertvolle Informationsquelle über den Zustand des Programms und seine aktuellen Möglichkeiten. Das betrifft besonders Zustände, die nicht oder nur schlecht anderweitig ersichtlich sind. Eine eingeschaltete Vollbildanzeige erkennt der Benutzer auch ohne Häkchen vor dem entsprechenden Menüeintrag. Mehrere Ansichten auseinander zu halten, ist dagegen schon schwieriger, erfordert aber immer noch
540
Menü und Kontextmenü
keine detektivischen Fähigkeiten, wie das Programm DiaProjektor augenfällig demonstriert. Den Zustand der Timer-Funktion oder anderer unsichtbarer Helferlein muss man dagegen irgendwo ablesen können – am besten dort, wo die Aktivierung/Deaktivierung geschieht: am Menübefehl selbst.
»Bildweitersc haltung
alle 5 Sekunden« ist aktiviert
Die Implementation der Behandlungsroutine für diesen Menübefehl pflegt die Zustände dreier weiterer Menübefehle. Private Sub mnuTimer5_Click() ResetTimer mnuTimer3.Checked = False ' Häkchen aktualisieren mnuDateiTimer3.Checked = False If mnuTimer5.Checked = False Then ' Timer schon gesetzt? Timer1.Interval = 5000 ' Nein: Timer auf 5 Sekunden Timer1.Enabled = True mnuTimer5.Checked = True mnuDateiTimer5.Checked = True Else ' Ja: ausschalten Timer1.Enabled = False mnuTimer5.Checked = False mnuDateiTimer5.Checked = False End If End Sub
Für die Indexansicht von DiaProjektor werden die Timer-Befehle mittels StopTimer deaktiviert. Private Sub StopTimer() Timer1.Enabled = False Timer1.Interval = 0 mnuTimer3.Checked = False mnuTimer5.Checked = False mnuDateiTimer3.Checked = False mnuDateiTimer5.Checked = False mnuTimer3.Enabled = False mnuTimer5.Enabled = False mnuDateiTimer3.Enabled = False mnuDateiTimer5.Enabled = False End Sub
' Timer ausschalten
541
DiaProjektor – SDI- Formulare synchronisieren
Die Timer- Funktion
DiaProjektor SDI- Formulare synchronisieren
Beide Timer- Funktionen sind deaktiviert
DiaProjektor – SDI- Formulare synchronisieren
Benutzerschnittstelle Die Benutzerschnittstelle eines Programms unterteilt sich in Menübefehle, Mausbefehle und Zugriffstasten (die vom System abgefangenen Tastenkürzel zählen zu den Menübefehlen, da sie bei der Anwendung als solche ankommen). Im Beispielprogramm DiaProjektor sind alle drei vertreten. Das Laufzeitsystem von Visual Basic generiert für jeden Menübefehl ein separates Click-Ereignis, so dass die Implementation eines Menübefehls durch Bereitstellung einer Behandlungsroutine für das jeweilige Ereignis erledigt werden kann. Unterschiedliche Menübefehle für denselben Befehl, die in verschiedenen Menüs beheimatet sind, sollten von der gleichen Routine bedient werden, was am besten durch Delegation zu lösen ist: Private Sub mnuDateiNächstes_Click() mnuBildNächstes_Click End Sub
Das gilt auch für Befehle und Funktionen, die sich auf einen Menübefehl zurückführen lassen, so beispielsweise für die Befehle der Tastaturschnittstelle in DiaProjektor: Public Sub Form_KeyDown(KeyCode As Integer, Shift As Integer) ' Tastaturschnittstelle Select Case KeyCode Case vbKeyHome ' zum ersten Bild mnuBildErstes_Click Case vbKeyLeft ' ein Bild zurück mit Überlauf mnuBildVoriges_Click Case vbKeyRight ' ein Bild vor mit Überlauf mnuBildNächstes_Click Case vbKeyEscape ' Vollbildanzeige/Fensteranzeige mnuVollbild_Click Case vbKeyUp, vbKeyPageUp ' mehrere Bilder weiter mit Überlauf FileIndex = (FileIndex + cLargeStep – 1) Mod UBound(FileNames) mnuBildNächstes_Click Case vbKeyDown, vbKeyPageDown ' mehrere Bilder zurück mit Überlauf FileIndex = FileIndex – cLargeStep – 1 If FileIndex < 1 Then FileIndex = FileIndex + UBound(FileNames) mnuBildNächstes_Click Case vbKeyF3 ' Timer ein/aus: 3 Sekunden If Not mnuTimer3.Enabled Then Exit Sub mnuTimer3_Click Case vbKeyF5 ' Timer ein/aus: 5 Sekunden If Not mnuTimer5.Enabled Then Exit Sub mnuTimer5_Click End Select End Sub
542
Benutzerschnittstelle
Aber auch für den Timer: Private Sub Timer1_Timer() mnuBildNächstes_Click End Sub
Public Sub Form_MouseDown(Button As Integer, Shift As Integer, _ x As Single, y As Single) If Button = vbLeftButton Then ' In Indexansicht wählt Klick Einzeldia aus If bIndexView Then ' Treffertest, um Index des Einzeldias zu berechnen FileIndex = FileIndex + _ Int(y / ActualForm.ScaleHeight * cIdxZeilen) * cIdxSpalten + _ Int(x / ActualForm.ScaleWidth * cIdxSpalten) If FileIndex > UBound(FileNames) Then FileIndex = UBound(FileNames) End If mnuIndex_Click ElseIf mnuBildNächstes.Enabled Then mnuBildNächstes_Click ' Blättern End If Else Kontextmenü ' Kontextmenü anzeigen End If End Sub
Das einzig »Anspruchsvolle« an diesem Code ist die Trefferprüfung, die den Index des Bildes an der aktuellen Mausposition zunächst theoretisch errechnet und dann gegebenenfalls auf den Index des letzten Bildes zurechtstutzt. Der Rest ist schiere Fallunterscheidung. Die Bildweiterschaltung in DiaProjektor reduziert sich somit im Wesentlichen auf drei Behandlungsroutinen, die nichts weiter tun, als den Index des nächsten anzuzeigenden Bildes zu berechnen und dann die Routine Anzeigen aufzurufen. Private Sub mnuBildErstes_Click() ResetTimer FileIndex = 1 Anzeigen cCaching End Sub
' Erstes Bild anzeigen
Private Sub mnuBildNächstes_Click() ResetTimer
' Nächstes Bild anzeigen
543
DiaProjektor – SDI- Formulare synchronisieren
Beachten Sie, dass die Tastenbefehle (F3) und (F5) bewusst nicht nur als Zugriffstasten, sondern auch über die Tastaturschnittstelle implementiert sind. Das hat den Vorteil, dass sie auch in der Vollbildanzeige, wo es kein Menü gibt, als Tastenbefehle verfügbar bleiben. In der Fensteranzeige nimmt der Tastenbefehl den Weg über die Menübehandlung des Laufzeitsystems und in der Vollbildanzeige über die Tastaturschnittstelle, was sich leicht über einen Unterbrechungspunkt in der entsprechenden Zeile nachweisen lässt. Die Funktion der linken Maustaste ist von der Ansicht abhängig. In der Einzeldiaansicht schaltet sie auf das jeweils nächste Bild weiter, in der Indexansicht wählt sie das Bild unter der Maus aus und zeigt dieses in der Einzeldiaansicht an. Die rechte Maustaste ruft dagegen das Kontextmenü auf.
Bildlaufleisten für Ansichten einsetzen und auf Größenänderungen
Bildlaufleisten für Ansichten einsetzen und auf Größenänderungen reagieren
If bIndexView Then ' Indexansicht? If FileIndex + cLargeStep > UBound(FileNames) Then ' Überlauf? FileIndex = 1 Else FileIndex = FileIndex + cLargeStep End If Else FileIndex = FileIndex Mod UBound(FileNames) + 1 End If Anzeigen cCaching End Sub Private Sub mnuBildVoriges_Click() ' Voriges Bild anzeigen ResetTimer If bIndexView Then ' Indexansicht? If FileIndex – cLargeStep < 1 Then ' Unterlauf? FileIndex = ((UBound(FileNames) – 2) \ cLargeStep) * cLargeStep + 1 Else FileIndex = FileIndex – cLargeStep End If Else FileIndex = (FileIndex – 2 + UBound(FileNames)) _ Mod UBound(FileNames) + 1 End If Anzeigen cCaching End Sub
Bildlaufleisten für Ansichten einsetzen und auf Größenänderungen reagieren Wahrscheinlich ist es Ihnen bereits aufgefallen: Formulare und auch Bildfelder besitzen im Gegensatz zu Textfeldern und Listenfeldern keine automatischen Bildlaufleisten, wie man sie von der Dokumentenansicht kommerzieller Programme her gewohnt ist. Das einzige, was da übrig bleibt, ist die Arbeit mit einem Steuerelement, das Bildlaufleisten unterstützt (etwa TextBox, wenn nur Text darzustellen ist, ansonsten gegebenenfalls auch RichTextBox), oder eben der Einsatz der Bildlaufleisten-Steuerelemente VScrollBar und HScrollBar (respektive FlatScrollBar) und der beherzte Griff in Tasten für die Implementierung der dafür erforderlichen Logik. Um es gleich vorwegzunehmen, wahre Freude kommt bei der Programmierung mit Bildlaufleisten in Visual Basic nicht auf. Warum die Entwickler den Eigenschaften Value, Min und Max nicht den Datentyp Single sowie das Koordinatensystem des Containerobjekts zugrunde gelegt haben, werden sie wohl selbst nicht beantworten können. Die minimalistische Implementation bietet zudem keine vernünftige Handhabe, um auf die Abmessung der Bildlaufmarke im Sinne einer proportionalen Repräsentation einzuwirken, und außerdem fehlt die wünschenswerte Möglichkeit – etwa durch ein Stilattribut –, zwischen der Operation im Intervall [Min, Max] und der im Intervall [Min, Max – LargeChange] wählen zu können. Ganz zu schweigen von dem Problem, dass die Bildlaufleiste wie ein Cursor zu blinken anfängt, wenn kein Steuerelement auf dem Formular ist, das bereit ist, den Fokus anzunehmen. Wer hofft, die Bildlaufleiste von Visual Basic vernünftig für das einsetzen zu können, wofür sie konzipiert wurde, nämlich für den Bildlauf im Sinne des Verschiebens eines Ausschnitts über einen Bereich, der muss sich durch eine
544
Bildlauf ein kleiner Betrachter für große Bilder
Bildlauf ein kleiner Betrachter für große Bilder Genug der Trauer, sehen wir uns das Beispielprogramm Bildlauf an, das es erlaubt, ein Bild über den Standarddialog Öffnen oder per OLE-Drag&Drop zu laden und den sichtbaren Ausschnitt per Bildlaufleisten zu verschieben. Das Formular ist in seiner Größe veränderlich und passt die Bildlaufleisten sowie deren Zustände an seine jeweiligen Abmessungen an. Die Anzeige der Bildlaufleisten erfolgt nur, wenn das Bild in der jeweiligen Dimension nicht in den Client-Bereich des Formulars passt. Um zu vermeiden, dass eine der Bildlaufleisten blinkt, weil kein anderes Steuerelement den Fokus entgegennimmt, verwendet die Implementation ein Dummy-Textfeld als Fokusfänger, das unsichtbar bleibt, weil es außerhalb des sichtbaren Bereichs positioniert wird.
Das Programm Bildlauf in Aktion
Hier der Code: '*********************************************************************** ' Formularmodul: Bildlauf ' Autor : 2000 Rudolf Huttary ' Beschreibung : demonstriert Implementation von Bildlaufleisten '*********************************************************************** Option Explicit Dim PicWidth As Integer, PicHeight As Integer Dim pic As Picture
545
Bildlaufleisten für Ansichten einsetzen und auf Größenänderungen
Reihe von Widrigkeiten quälen und schließlich häufig doch mit einer halbgaren Lösung zufriedengeben. Der Grund dafür ist mal wieder in der Abwärtskompatibilität zu suchen. Obwohl Windows 9x inzwischen auch eine 32-Bit-Implementation der Bildlaufleiste zur Verfügung stellt, setzen die Standardsteuerelemente sowie auch das »neue« Steuerelement FlatScrollBar von Visual Basic 6.0 (leider) noch auf die 16-Bit-Version auf, was natürlich gewisse Einschränkungen mit sich bringt. Was zunächst einfach aussieht, artet schnell in wüste Umrechnereien aus, so dass schnell der Ruf nach einer Scale-Methode und Eigenschaften vom Typ Single laut wird. Die folgenden beiden Programme BildLauf und HexView verwenden die »alten« Bildlaufleisten HScrollBar und VScrollBar. Sie durch das Steuerelement FlatScrollBar zu ersetzen, erfordert keine Änderung in der Implementation, wenn Sie die Ausrichtung bereits zur Entwurfszeit festlegen.
Bildlaufleisten für Ansichten einsetzen und auf Größenänderungen reagieren
Bildlaufleisten für Ansichten einsetzen und auf Größenänderungen
Private Sub Form_Load() HScroll1.Left = 0 VScroll1.Top = 0 Text1.Locked = True mnuDateiÖffnen_Click End Sub
' Fokusfänger für Eingaben sperren
Private Sub Form_Paint() If Not pic Is Nothing Then ' Bild geladen? ' sichtbaren Ausschnitt zeichnen PaintPicture pic, ScaleLeft, ScaleTop, , , _ 2 * ScaleLeft, 2 * ScaleTop, HScroll1.Width, VScroll1.Height End If End Sub Private Sub Form_Resize() If Not WindowState = vbMinimized Then Height = IIf(Height < 1000, 1000, Height) Width = IIf(Width < 1000, 1000, Width) ' Sichtbarkeit der Bildlaufleisten errechnen If PicWidth > ScaleWidth – VScroll1.Width And _ PicHeight > ScaleHeight – HScroll1.Height Then ' Beide nötig? HScroll1.Visible = True VScroll1.Visible = True ElseIf PicWidth > ScaleWidth Then ' horizontal? HScroll1.Visible = True VScroll1.Visible = False ElseIf PicHeight > ScaleHeight Then ' vertikal? VScroll1.Visible = True HScroll1.Visible = False Else ' Beide unnötig HScroll1.Visible = False VScroll1.Visible = False End If ' Korrektur: Doch beide unnötig? If PicWidth <= ScaleWidth And PicHeight <= ScaleHeight Then HScroll1.Visible = False VScroll1.Visible = False End If ' Abmessungen der Bildlaufleisten VScroll1.Left = ScaleLeft + ScaleWidth – VScroll1.Width VScroll1.Height = ScaleHeight + HScroll1.Height * HScroll1.Visible HScroll1.Top = ScaleTop + ScaleHeight – HScroll1.Height HScroll1.Width = ScaleWidth – IIf(VScroll1.Visible, _ VScroll1.Width, 0) Text1.Left = ScaleLeft + ScaleWidth ' Fokusfänger !! ' Bildlaufparameter anpassen If Not pic Is Nothing Then If VScroll1.Visible Then
546
Bildlauf ein kleiner Betrachter für große Bilder
Bildlaufleisten für Ansichten einsetzen und auf Größenänderungen
VScroll1.Max = PicHeight – VScroll1.Height VScroll1.LargeChange = VScroll1.Height VScroll1.SmallChange = VScroll1.Height / 10 End If If HScroll1.Visible Then HScroll1.Max = PicWidth – HScroll1.Width HScroll1.LargeChange = HScroll1.Width HScroll1.SmallChange = HScroll1.Width / 10 End If End If ' Bildlaufleisten ggf. unsichtbar machen, falls Bild passt Refresh End If End Sub Private Sub mnuDateiBeenden_Click() Unload Me End Sub Private Sub mnuDateiÖffnen_Click() CommonDialog1.Filter = "Bilder (JPG, GIF, BMP)|*.jpg;*.gif;*.bmp" CommonDialog1.ShowOpen ' Bilddatei in Erfahrung bringen If CommonDialog1.FileName <> "" Then ReadPic CommonDialog1.FileName Caption = "Bildlauf – " & CommonDialog1.FileTitle Form_Resize ' Bildlaufleisten aktualisieren End If End Sub Private Sub HScroll1_Change() ' Ursprung nach links verschieben ScaleLeft = HScroll1.Value Refresh End Sub Private Sub HScroll1_Scroll() ' Anloges Verschieben während des Ziehens ScaleLeft = HScroll1.Value Text1.SetFocus ' Fokusfänger Refresh ' Neu zeichnen End Sub Private Sub VScroll1_Change() ' Ursprung nach oben verschieben ScaleTop = VScroll1 Text1.SetFocus ' Fokusfänger Refresh End Sub Private Sub Form_OLEDragDrop(Data As DataObject, Effect As Long, _ Button As Integer, Shift As Integer, x As Single, y As Single)
547
Bildlaufleisten für Ansichten einsetzen und auf Größenänderungen reagieren
Bildlaufleisten für Ansichten einsetzen und auf Größenänderungen
' Nur Datei von Explorer-Fenster entgegennehmen If Data.GetFormat(vbCFFiles) Then ReadPic Data.Files(1) Form_Resize ' Bildlaufleisten aktualisieren End If End Sub ' Bild einlesen Private Sub ReadPic(FileName As String) On Error GoTo Abbruch Set pic = LoadPicture(FileName) ' Bildabmessungen in Twips umrechnen ScaleMode = vbTwips ' Twips einstellen ScaleMode = vbUser ' Twips einstellen On Error GoTo NeuSkalierenW PicWidth = ScaleX(pic.Width, vbHimetric, vbUser) On Error GoTo NeuSkalierenH PicHeight = ScaleY(pic.Height, vbHimetric, vbUser) Exit Sub NeuSkalierenW: ScaleWidth = ScaleWidth / 10 Resume NeuSkalierenH: ScaleHeight = ScaleHeight / 10 Resume Abbruch: End Sub
Beim Programmstart klebt die Load-Routine die Bildlaufleisten an das linke bzw. obere Ende des Client-Bereichs, sperrt das Textfeld für Eingaben und führt dann die Behandlungsroutine mnuDateiÖffnen_Click für den Menübefehl ÖFFNEN aus. Diese ruft den Standarddialog ÖFFNEN und lässt dann die vom Benutzer spezifizierte Datei von ReadPic einlesen. Das Besondere an ReadPic ist, dass die Routine nach dem erfolgreichen Laden des Bildes dessen Abmessungen in die für das Formular geltende Skalierungseinheit Twips umrechnet und versucht, die Werte im Datentyp Integer der dafür vorgesehenen Variablen PicWidth und PicHeight unterzukriegen. Dahinter steckt der Gedanke, das Koordinatensystem Eins-zu-Eins für den von 16-Bit-Bildlaufleisten unterstützten Wertebereich 0 bis 32767 benutzen zu können. Falls das nicht klappt, tritt ein Laufzeitfehler auf, dessen Behandlung die Spreizung der jeweiligen Koordinatenachse um den Faktor 10 gefolgt von einem erneuten Umrechnungsversuch vorsieht. Dies passiert so oft, bis das Koordinatensystem passt. Als Vorbereitung bewirkt der Code ScaleMode = vbTwips ScaleMode = vbUser
' Twips einstellen ' benutzerdef. Koordinatensystem aktivieren
dass sich das benutzerdefinierte Koordinatensystem mit dem Koordinatensystem vbTwips deckt. Mit diesem Verfahren darf das Bild in einer Dimension theoretisch bis zu 232 Twips messen (was etwa 80 km entspricht). Falls Sie die tatsächlichen Spreizungsfaktoren benötigen, schreiben Sie: ScaleFaktX = ScaleX(1, vbUser, vbTwips) ScaleFaktX = ScaleY(1, vbUser, vbTwips)
548
Bildlauf ein kleiner Betrachter für große Bilder
Auf das Einlesen des Bildes folgt der Aufruf Resize_Form. Diese speziell auf die Behandlung des Resize-Ereignisses zugeschnittene Routine aktualisiert die Zustände beider Bildlaufleisten unter Bezugnahme auf die aktuellen Abmessungen des Client-Bereichs und veranlasst dann die Ausgabe des Bildes. Die Zustandsaktualisierung der Bildlaufleisten erfordert im ersten Schritt einen Test, inwieweit ihre Anzeige überhaupt erforderlich ist. Das ist logisch ein wenig verzwickt, da sich die Fälle, dass das Bild gerade noch in den Client-Bereich passt, sowie dass beide Leisten angezeigt werden und das Bild eben gerade deshalb nicht mehr in den Client-Bereich passt, überschneiden. Die vorgestellte Lösung mit der Nachkorrektur des schwierigen Falls ist sicher nicht die eleganteste, aber sie ist gut lesbar und funktioniert:
Sobald die Sichtbarkeit geklärt ist, lässt sich die Länge der Bildlaufleisten errechnen. Der Code stellt zwei äquivalente Lösungen vor, wie man eine Fallunterscheidung geschickt implizit formulieren kann: einmal als Multiplikation mit dem Wahrheitswert des Kriteriums, wobei das Vorzeichen des Werts True (-1) zu beachten ist, und einmal als IIf-Ausdruck. Darüber hinaus sorgt der Code dafür, dass das Dummy-Textfeld Text1 außerhalb des Client-Bereichs bleibt. VScroll1.Left = ScaleLeft + ScaleWidth – VScroll1.Width VScroll1.Height = ScaleHeight + HScroll1.Height * HScroll1.Visible HScroll1.Top = ScaleTop + ScaleHeight – HScroll1.Height HScroll1.Width = ScaleWidth – IIf(VScroll1.Visible, VScroll1.Width, 0) Text1.Left = ScaleLeft + ScaleWidth ' Fokusfänger !!
Es fehlt noch die Anpassung der Eigenschaften Max, LargeChange und SmallChange an die aktuellen Gegebenheiten. Der Bildlaufbereich Max errechnet sich aus der Breite bzw. Höhe des Bildes (im aktuellen Koordinatensystem) abzüglich der aktuellen Ausschnittsbreite bzw. -höhe. Diese Differenz ist nur dann positiv, wenn die Leiste angezeigt wird und erfordert somit eine Fallunterscheidung. LargeChange sollte auf die Breite bzw. Höhe des sichtbaren Ausschnitts gesetzt werden, um einen exakten Vorschub um genau eine Ausschnittsbreite bzw. -höhe zu erhalten. In diesem Fall drückt die Ausdehnung der Bildlaufmarke relativ zum Bildlaufschaft exakt die Ausdehnung des Ausschnitts relativ zur Ausdehnung des Bildes aus. In der Praxis wird LargeChange zuweilen auch etwas kleiner gesetzt, um eine Überlappung beim seitenbasierten Bildlauf (Mausklick in den Bildlaufschaft) zu erreichen. Für LargeChange ist nichts Spezielles vorgeschrieben. Ein guter Wert dafür ist etwa ein Zehntel des Werts von LargeChange für Bilder,
549
Bildlaufleisten für Ansichten einsetzen und auf Größenänderungen
If PicWidth > ScaleWidth – VScroll1.Width And _ PicHeight > ScaleHeight – HScroll1.Height Then ' Beide nötig? HScroll1.Visible = True VScroll1.Visible = True ElseIf PicWidth > ScaleWidth Then ' horizontal? HScroll1.Visible = True VScroll1.Visible = False ElseIf PicHeight > ScaleHeight Then ' vertikal? VScroll1.Visible = True HScroll1.Visible = False Else ' Beide unnötig HScroll1.Visible = False VScroll1.Visible = False End If ' Korrektur: Doch beide unnötig? If PicWidth <= ScaleWidth And PicHeight <= ScaleHeight Then HScroll1.Visible = False VScroll1.Visible = False End If
Bildlaufleisten für Ansichten einsetzen und auf Größenänderungen reagieren
Bildlaufleisten für Ansichten einsetzen und auf Größenänderungen
während man für zeilenorientierte Darstellungen das Verhältnis über die aktuelle Zeilenanzahl pro Seite berechnen wird. If Not pic Is Nothing Then If VScroll1.Visible Then VScroll1.Max = PicHeight – VScroll1.Height VScroll1.LargeChange = VScroll1.Height VScroll1.SmallChange = VScroll1.Height / 10 End If If HScroll1.Visible Then HScroll1.Max = PicWidth – HScroll1.Width HScroll1.LargeChange = HScroll1.Width HScroll1.SmallChange = HScroll1.Width / 10 End If End If
Jetzt gilt es noch eine Hürde zu überwinden, nämlich die Ausgabe des Bildes mit PaintPicture. Sie sollte so erfolgen, dass nur der jeweils relevante Bildausschnitt gezeichnet werden muss. Wenn Sie mit einer Paint-Routine arbeiten, reicht ein Refresh-Aufruf innerhalb von Form_Resize, ansonsten müssen Sie die Paint-Routine (oder deren Äquivalent) explizit aufrufen. Der Code lautet: Private Sub Form_Paint() If Not pic Is Nothing Then ' Bild geladen? ' sichtbaren Ausschnitt zeichnen PaintPicture pic, ScaleLeft, ScaleTop, , , _ 2 * ScaleLeft, 2 * ScaleTop, HScroll1.Width, VScroll1.Height End If End Sub
Auch wenn die Paint-Routine harmlos aussieht, der PaintPicture-Aufruf hat es in sich, was die Findung der Parameterwerte betrifft, weil die Implementation von PaintPicture nicht gerade intuitiv bzw. fehlerhaft ist. Da Bildlaufleisten, Koordinatensystem und Bild aufgrund der besonderen Wahl von Max exakt aufeinander abgestimmt sind, sollte klar sein, was zu tun ist: Der linke obere Punkt für die Ausgabe ist der linke obere Punkt des Client-Bereichs im aktuellen Koordinatensystem, also (ScaleLeft, ScaleHeight). Der linke obere Punkt des Bildausschnitts hat in diesem Koordinatensystem relativ zum Bildursprung gleichfalls die Koordinaten (ScaleLeft, ScaleHeight) und sollte links oben im Client-Bereich erscheinen. Man möchte nun meinen, der Aufruf sollte so aussehen: PaintPicture pic, ScaleLeft, ScaleTop, , , ScaleLeft, ScaleTop, _ HScroll1.Width, VScroll1.Height
denn schließlich zeichnet PaintPicture pic, ScaleLeft, ScaleTop
unabhängig vom Ursprung des Koordinatensystems das Bild artig in die linke obere Ecke. Wie ein Blick auf die »Lösung« verrät, denkt Microsoft darüber jedoch anders: PaintPicture erwartet auch die Koordinaten für einen Bildausschnitt relativ zum Ursprung des Koordinatensystems – somit sind der linke obere Punkt für die Ausgabe und der linke obere Punkt für den Ausschnitt (bezogen auf den Bildursprung) zu addieren. Jetzt sind Sie dran! Die Abmessungen des Ausschnitts ergeben sich über die Werte für die Breite bzw. Höhe der Bildlaufleisten, die Form_Resize ja in jedem Fall richtig berechnet.
550
HexView eine schnelle Textansicht für große Dateien
Bleibt als Letztes ein Blick auf die Change- und Scroll-Behandlung der Bildlaufleisten. Eine Scroll-Behandlung für die vertikale Bildlaufleiste wurde in Bildlauf bewusst weggelassen, damit sich der Unterschied ausprobieren lässt. Das Change-Ereignis tritt in einer einzelnen Bildlaufleistenaktion nur ein einziges Mal auf, nachdem der Benutzer eine Positionsänderung der Bildlaufmarke vollzogen hat, das Scroll-Ereignis tritt dagegen mehrfach auf, wenn der Benutzer die Bildlaufmarke mit der Maus zieht. Damit die Bildlaufleiste nicht zu blinken anfängt, wird der Fokus an das Textfeld weitergeschoben.
Private Sub HScroll1_Scroll() ' Anloges Verschieben während des Ziehens ScaleLeft = HScroll1.Value Refresh ' Neu zeichnen End Sub Private Sub VScroll1_Change() ' Ursprung nach oben verschieben ScaleTop = VScroll1 Text1.SetFocus ' Fokusfänger Refresh End Sub
HexView eine schnelle Textansicht für große Dateien Im Gegensatz zu Bildansichten nach dem Muster der Beispiele DiaProjektor und Bildlauf ist die Ausgabe von Textansichten zuweilen mit einem erheblichen Rechenaufwand verbunden. Und der Umgang mit Bildlaufleisten sieht vom Prinzip her genauso aus – bis auf einige Details, da man sich mit einem vorgegebenen Koordinatensystem herumschlagen muss. Das Beispielprojekt HexView demonstriert die Implementation einer Textansicht und stellt dabei nicht nur eine Reihe von Zeichenfolgenfunktionen vor, sondern auch den Zugriff auf Binärdateien. Das für das Programm verwendete Formular enthält zwei (!) Bildlaufleisten für den horizontalen und eine für den vertikalen Bildlauf, ein Textfeld, dem die leidliche Aufgabe als Fokusfänger zufällt, sowie ein Standarddialoge-Steuerelement, das für die Ermittlung des Dateinamens herangezogen wird. Alternativ öffnet das Programm aber auch Dateien, die per OLE-Drag&Drop in das Fenster der Anwendung abgelegt werden. Wie die Abbildung zeigt, generiert das Programm seine Ansicht so, wie man sie von einem »HexViewer« erwarten würde. Der Zeilenaufbau hat Tradition: Byteposition in hexadezimaler Repräsentation zu Beginn der Zeile, danach die Bytes in hexadezimaler Repräsentation gefolgt von der ASCII-Repräsentation. Im Gegensatz zu vielen anderen als Freeware erhältlichen HexViewern, zeigt das Programm auch bei großen Dateien (in der abgebildeten Sitzung umfasst die Datei 12 MB) keine nennenswerten Verzögerungen, da es jeweils nur die für die aktuelle Ansicht benötigten Daten aus der Binärdatei liest. Der Grund, warum viele vergleichbare Programme bei Dateien ab ca. 512 KB dichtmachen, liegt tatsächlich am eingeschränkten Wertebereich der 16-Bit-Bildlaufleiste. HexView verwendet den Trick, einfach zwei Bildlaufleisten zu kaskadieren, um diese Beschränkung zu überwinden.
551
Bildlaufleisten für Ansichten einsetzen und auf Größenänderungen
Private Sub HScroll1_Change() ' Ursprung nach links verschieben ScaleLeft = HScroll1.Value Text1.SetFocus ' Fokusfänger Refresh End Sub
Bildlaufleisten für Ansichten einsetzen und auf Größenänderungen
Bildlaufleisten für Ansichten einsetzen und auf Größenänderungen reagieren
Das Programm HexView in Aktion
Freilich lässt der Funktionsumfang des Programms noch einiges zu wünschen übrig, beispielsweise eine Suchfunktion oder gar einen Bearbeitungsmodus. Nehmen Sie den Quelltext doch einfach als Ausgangsposition für eigene Implementationsversuche in dieser Richtung. '*********************************************************************** ' Formularmodul: HexViewer ' Autor : 2000 Rudolf Huttary ' Beschreibung : Demonstriert den Einsatz von Bildlaufleisten für ' Textansichten '*********************************************************************** Option Explicit Const cLines = 40 Const cCols = 16 Const cChars = 80 Const cMaxScroll As Long = 32768 Dim BinLines As Long Private Lines As Long
' Zeilenanzahl gesamt ' Zeilenanzahl in Ansicht
Private Sub Form_Load() HScroll1.Left = 0 ' links ankleben VScroll1.Top = 0 ' oben ankleben VScroll2.Top = 0 ' oben ankleben VScroll1.Height = HScroll1.Width ' gleiche "Breite" VScroll2.Height = HScroll1.Width ' gleiche "Breite" Text1.Locked = True ' Fokusfänger für Eingaben sperren FontName = "FixedSys" ' nichtproportionale Schrift Show Height = Height – ScaleHeight + cLines * TextHeight("a") Width = Width – ScaleWidth + cChars * TextWidth("a") + VScroll1.Width Scale (0, 0)-(cChars, cLines)
552
HexView eine schnelle Textansicht für große Dateien
Lines = cLines mnuDateiÖffnen_Click End Sub
Bildlaufleisten für Ansichten einsetzen und auf Größenänderungen
Private Sub Form_Paint() Dim i As Long Dim LastLine As Long If BinLines Then CurrentX = 0 ' ganz links LastLine = ScaleTop + Lines If LastLine > BinLines – 1 Then LastLine = BinLines ScaleTop = BinLines – Lines End If CurrentY = ScaleTop ' zur obersten Zeile For i = ScaleTop + 1 To LastLine Print MakeLine(i) Next i End If End Sub Private Sub Form_Resize() If Not WindowState = vbMinimized Then Height = IIf(Height < 1200, 1200, Height) Width = IIf(Width < 1000, 1000, Width) Text1.Top = ScaleHeight ' Fokusfänger! HScroll1.Visible = ScaleWidth < cChars ' Sichtbarkeit VScroll1.Visible = BinLines > Lines ' Sichtbarkeit VScroll2.Visible = BinLines > cMaxScroll ' Sichtbarkeit Lines = Int(ScaleHeight + HScroll1.Height * HScroll1.Visible) ' Abmessungen der Bildlaufleisten HScroll1.Top = ScaleTop + ScaleHeight – HScroll1.Height HScroll1.Width = ScaleWidth + VScroll1.Width * VScroll1.Visible VScroll1.Left = ScaleLeft + ScaleWidth – VScroll1.Width VScroll1.Height = ScaleHeight + HScroll1.Height * HScroll1.Visible ' horizontale Bildlaufparameter anpassen If HScroll1.Visible Then HScroll1.Max = cChars – ScaleWidth HScroll1.LargeChange = ScaleWidth HScroll1.SmallChange = 1 End If ' vertikale Bildlaufparameter anpassen If BinLines >= cMaxScroll Then VScroll2.Left = VScroll1.Left VScroll2.Height = VScroll1.Height VScroll1.Left = VScroll2.Left – VScroll1.Width VScroll1.Height = VScroll2.Height VScroll2.Max = BinLines / cMaxScroll VScroll2.LargeChange = VScroll2.Max / 10
553
Bildlaufleisten für Ansichten einsetzen und auf Größenänderungen reagieren
Bildlaufleisten für Ansichten einsetzen und auf Größenänderungen
VScroll2.SmallChange = 1 End If If VScroll1.Visible Then VScroll1.Max = BinLines Mod cMaxScroll – Lines VScroll1.LargeChange = Lines – 1 VScroll1.SmallChange = 1 End If Refresh End If End Sub Private Sub mnuDateiÖffnen_Click() CommonDialog1.Filter = "Alle Dateien|*.*" CommonDialog1.ShowOpen ' Datei in Erfahrung bringen If CommonDialog1.FileName <> "" Then OpenBinary CommonDialog1.FileName End If Form_Resize ' Bildlaufl. aktualisieren End Sub Private Sub mnuDateiBeenden_Click() Unload Me End Sub Private Sub OpenBinary(FileName) VScroll1 = 0 ' Re-Initialisierung VScroll2 = 0 ' Re-Initialisierung HScroll1 = 0 ScaleTop = 0 ScaleLeft = 0 BinLines = 0 Caption = "HexView" ' Datei öffnen Close 1 On Error GoTo Abbruch Open FileName For Binary As 1 Caption = "HexView – " & Mid(FileName, InStrRev(FileName, "\") + 1) _ & " (" & Format(LOF(1), "#,#") & " Bytes)" BinLines = LOF(1) / cCols + 1 ' Zeilenanzahl Abbruch: End Sub Private Sub VScroll1_Scroll() UpdateV End Sub Private Sub VScroll1_Change() UpdateV End Sub
554
HexView eine schnelle Textansicht für große Dateien
Private Sub VScroll2_Scroll() UpdateV End Sub Private Sub VScroll2_Change() UpdateV End Sub
Bildlaufleisten für Ansichten einsetzen und auf Größenänderungen
Private Sub UpdateV() ScaleTop = VScroll2 * cMaxScroll + VScroll1 Text1.SetFocus Refresh ' neu zeichnen End Sub Private Sub HScroll1_Scroll() ' Anloges Verschieben während des Ziehens ScaleLeft = HScroll1 Text1.SetFocus Refresh ' neu zeichnen End Sub Private Sub HScroll1_Change() ' Verschieben am Ende der Aktion ScaleLeft = HScroll1 Text1.SetFocus Refresh End Sub
' Fokusfänger ' neu zeichnen
Private Sub Form_OLEDragDrop(Data As DataObject, Effect As Long, _ Button As Integer, Shift As Integer, x As Single, y As Single) ' Nur Datei von Explorer-Fenster entgegennehmen If Data.GetFormat(vbCFFiles) Then OpenBinary Data.Files(1) Form_Resize ' Bildlaufl. aktualisieren End If End Sub ' Liest Length Bytes ab Position FromIdx, gibt Dateilänge zurück Private Function ReadBinary(Bin$, FromIdx As Long, Length As Long) _ As Long If BinLines Then Seek 1, FromIdx Bin = Input(Length, 1) ' Block Lesen ReadBinary = LOF(1) ' Länge End If End Function ' Generiert eine Zeile für die Hexansicht Private Function MakeLine(Zeile As Long) As String Dim i As Long, lenBin As Long Dim h$, Bin$, HexLine$, CharLine$ ' Stringpuffer
555
Bildlaufleisten für Ansichten einsetzen und auf Größenänderungen reagieren
Bildlaufleisten für Ansichten einsetzen und auf Größenänderungen
Dim ByteH As Integer i = (Zeile – 1) * cCols + 1 lenBin = ReadBinary(Bin, i, cCols) ' cCols Zeichen ab i lesen For i = 1 To cCols ' Byteweise durch den String If i + (Zeile – 1) * cCols >= lenBin Then Exit For ByteH = Asc(Mid(Bin, i, 1)) ' byteweise! h = Right("0" + Hex(ByteH), 2) + " " ' nach Hex If ByteH < 32 Then ' nicht druckbares Zeichen? ByteH = Asc(".") End If HexLine = HexLine + h CharLine = CharLine + Chr(ByteH) Next i ' Zeile zusammenbauen ' Hexadezimale Zeilenanfänge ' MakeLine = Right(" " & Hex((Zeile – 1) * cCols), 8) & ": " & _ ' HexLine ' Dezimale Zeilenanfänge MakeLine = Right(" " & Str((Zeile – 1) * cCols), 8) & ": " & _ HexLine MakeLine = Left(MakeLine + Space(45), 58) + "- " + CharLine End Function
Wie für dateiorientierte SDI-Anwendungen üblich, ruft HexView nach Erledigung der Initialisierung noch im Rahmen von Load den Standarddialog ÖFFNEN auf. Durchaus interessant in dieser Routine ist die Berechnung der Voreinstellung für die Formularabmessungen, in die die Zeichenbreite bzw. -höhe der eingestellten nichtproportionalen Schrift eingeht. Height und Width sind unabhängig vom Koordinatensystem immer in Twips anzugeben. Der frühe ShowAufruf ist leider erforderlich, damit auch die Höhe der Menüleiste in die Berechnung für die Zeilenhöhe eingeht. Das Koordinatensystem ergibt sich dann einfach aus den Werten für die Zeilenanzahl und die Zeichenzahl je Zeile und behält seine Skalierung (nicht jedoch seinen Ursprung!) im gesamten Programmablauf bei. Const cLines = 40 Const cCols = 16 Const cChars = 80 Private Sub Form_Load() HScroll1.Left = 0 ' links ankleben VScroll1.Top = 0 ' oben ankleben VScroll2.Top = 0 ' oben ankleben VScroll1.Height = HScroll1.Width ' gleiche "Breite" VScroll2.Height = HScroll1.Width ' gleiche "Breite" Text1.Locked = True ' Fokusfänger für Eingaben sperren FontName = "FixedSys" ' nichtproportionale Schrift Show Height = Height – ScaleHeight + cLines * TextHeight("a") Width = Width – ScaleWidth + cChars * TextWidth("a") + VScroll1.Width Scale (0, 0)-(cChars, cLines) Lines = cLines
556
HexView eine schnelle Textansicht für große Dateien
mnuDateiÖffnen_Click End Sub
Falls der Benutzer den Dialog abbricht, kann er die Routine mnuDateiÖffnen_Click später immer noch per Menübefehl ÖFFNEN aktivieren oder aber er importiert die Daten per OLEDrag&Drop. (Details und Implementationstechniken für OLE-Drag&Drop-Operationen finden Sie im Abschnitt »OLE-Drag&Drop«, S. 501.)
Private Sub Form_OLEDragDrop(Data As DataObject, Effect As Long, _ Button As Integer, Shift As Integer, x As Single, y As Single) ' Nur Datei von Explorer-Fenster entgegennehmen If Data.GetFormat(vbCFFiles) Then OpenBinary Data.Files(1) Form_Resize ' Bildlaufl. aktualisieren End If End Sub
Den Kern beider Routinen bildet der OpenBinary-Aufruf, dessen Aufgabe darin besteht, Bildlaufleisten und Koordinatensystem zu reinitialisieren und die Datei FileName im binären Modus zu öffnen. Außerdem berechnet die Routine die Zeilenanzahl BinLines für die komplette Darstellung der Datei im gewählten Zeilenformat: Private Sub OpenBinary(FileName) VScroll1 = 0 ' Reinitialisierung VScroll2 = 0 HScroll1 = 0 ScaleTop = 0 ScaleLeft = 0 BinLines = 0 Caption = "HexView" ' Datei öffnen Close 1 On Error GoTo Abbruch Open FileName For Binary As 1 Caption = "HexView – " & Mid(FileName, InStrRev(FileName, "\") + 1) _ & " (" & Format(LOF(1), "#,#") & " Bytes)" BinLines = LOF(1) / cCols + 1 ' Zeilenanzahl Abbruch: End Sub
Die Voraussetzung für das korrekte Zeichnen der Ansicht schafft Form_Resize. Diese als Behandlungsroutine für das Resize-Ereignis erforderliche Methode verhindert, dass der ClientBereich zu klein wird und dass die Bildlaufleisten nicht nur an der richtigen Position sitzen, sondern auch den aktuellen Ausschnitt bezogen auf die gesamte Ansicht korrekt visualisieren.
557
Bildlaufleisten für Ansichten einsetzen und auf Größenänderungen
Private Sub mnuDateiÖffnen_Click() CommonDialog1.Filter = "Alle Dateien|*.*" CommonDialog1.ShowOpen ' Datei in Erfahrung bringen If CommonDialog1.FileName <> "" Then OpenBinary CommonDialog1.FileName End If Form_Resize ' Bildlaufl. aktualisieren End Sub
Bildlaufleisten für Ansichten einsetzen und auf Größenänderungen reagieren
Bildlaufleisten für Ansichten einsetzen und auf Größenänderungen
Wegen der 16-Bit-Beschränkung muss deshalb gegebenenfalls noch eine zweite vertikale Bildlaufleiste angezeigt werden. Ansonsten ist die Logik fast die gleiche wie die für eine Bildansicht. Die Routine klärt die Sichtbarkeit der Bildlaufleisten, ermittelt die Anzahl der in der Ansicht darstellbaren Zeilen, korrigiert die Abmessungen der Bildlaufleisten und passt schließlich noch die Bildlaufleistenparameter an die aktuellen Gegebenheiten an, bevor sie die Ansicht via Refresh zum Zeichnen freigibt. Private Sub Form_Resize() If Not WindowState = vbMinimized Then Height = IIf(Height < 1200, 1200, Height) Width = IIf(Width < 1000, 1000, Width) Text1.Top = ScaleHeight ' Fokusfänger! HScroll1.Visible = ScaleWidth < cChars ' Sichtbarkeit VScroll1.Visible = BinLines > Lines ' Sichtbarkeit VScroll2.Visible = BinLines > cMaxScroll ' Sichtbarkeit Lines = Int(ScaleHeight + HScroll1.Height * HScroll1.Visible) ' Abmessungen der Bildlaufleisten HScroll1.Top = ScaleTop + ScaleHeight – HScroll1.Height HScroll1.Width = ScaleWidth + VScroll1.Width * VScroll1.Visible VScroll1.Left = ScaleLeft + ScaleWidth – VScroll1.Width VScroll1.Height = ScaleHeight + HScroll1.Height * HScroll1.Visible ' horizontale Bildlaufparameter anpassen If HScroll1.Visible Then HScroll1.Max = cChars – ScaleWidth HScroll1.LargeChange = ScaleWidth HScroll1.SmallChange = 1 End If ' vertikale Bildlaufparameter anpassen If BinLines >= cMaxScroll Then VScroll2.Left = VScroll1.Left VScroll2.Height = VScroll1.Height VScroll1.Left = VScroll2.Left – VScroll1.Width VScroll1.Height = VScroll2.Height VScroll2.Max = BinLines / cMaxScroll VScroll2.LargeChange = VScroll2.Max / 10 VScroll2.SmallChange = 1 End If If VScroll1.Visible Then VScroll1.Max = BinLines Mod cMaxScroll – Lines VScroll1.LargeChange = Lines – 1 VScroll1.SmallChange = 1 End If Refresh End If End Sub
Die zweite vertikale Bildlaufleiste kommt nur dann ins Spiel, wenn die Zeilenanzahl nicht mehr in den Datentyp Integer passt. In diesem Fall wirkt sie wie der kleine Zeiger einer Uhr: Jeder Einzelschritt der rechten Bildlaufleiste ist ein voller Durchlauf der linken. Die Ereignisbehandlung für die Bildlaufleisten ist überraschend einfach, die Routinen VScroll1_Change,
558
HexView eine schnelle Textansicht für große Dateien
VScroll1_Scroll, VScroll2_Change und VScroll2_Scroll können dafür sogar den identischen Code benutzen, der in UpdateV zusammengefasst ist. Private Sub UpdateV() ScaleTop = VScroll2 * cMaxScroll + VScroll1 Text1.SetFocus Refresh ' neu zeichnen End Sub
Man sieht: Jede Bildlaufleistenoperation verschiebt den Ursprung des Koordinatensystems in vertikaler Richtung. Noch einfacher ist der Fall bei der horizontalen Bildlaufleiste:
Private Sub HScroll1_Change() ' Verschieben am Ende der Aktion ScaleLeft = HScroll1 Text1.SetFocus Refresh End Sub
' Fokusfänger ' neu zeichnen
Die via Refresh auf den Plan gerufene Paint-Routine gibt nur den aktuellen Ausschnitt aus, wobei ScaleTop + 1 die Zeilennummer der ersten auszugebenden Zeile beschreibt. Private Sub Form_Paint() Dim i As Long Dim LastLine As Long CurrentX = 0 LastLine = ScaleTop + Lines If LastLine > BinLines – 1 Then LastLine = BinLines ScaleTop = BinLines – Lines End If CurrentY = ScaleTop For i = ScaleTop + 1 To LastLine Print MakeLine(i) Next i End Sub
' ganz links
' zur obersten Zeile
MakeLine, die fleißigste Routine in diesem Aufbau, übernimmt die Zusammenstellung der einzelnen Zeilen. Dazu lässt sie sich von der Routine ReadBinary die zu der jeweiligen Zeile gehörigen Daten aus der noch geöffneten Datei in Form einer Zeichenfolge sowie die Gesamtlänge der Datei liefern. Unter Hinzunahme der aktuellen Zeilennummer bastelt die Funktion daraus die für hexadezimale Ansichten übliche Zeilendarstellung. ' Liest Length Bytes ab Position FromIdx, gibt Dateilänge zurück Private Function ReadBinary(Bin$, FromIdx As Long, Length As Long) As Long If BinLines Then Seek 1, FromIdx
559
Bildlaufleisten für Ansichten einsetzen und auf Größenänderungen
Private Sub HScroll1_Scroll() ' Anloges Verschieben während des Ziehens ScaleLeft = HScroll1 Text1.SetFocus Refresh ' neu zeichnen End Sub
Bildlaufleisten für Ansichten einsetzen und auf Größenänderungen reagieren
Bin = Input(Length, 1) ReadBinary = LOF(1) End If End Function
' Block Lesen ' Länge
Bildlaufleisten für Ansichten einsetzen und auf Größenänderungen
' Generiert eine Zeile für die Hexansicht Private Function MakeLine(Zeile As Long) As String Dim i As Long, lenBin As Long Dim h$, Bin$, HexLine$, CharLine$ ' Stringpuffer Dim ByteH As Integer i = (Zeile – 1) * cCols + 1 lenBin = ReadBinary(Bin, i, cCols) ' cCols Zeichen ab i lesen For i = 1 To cCols ' Byteweise durch den String If i + (Zeile – 1) * cCols >= lenBin Then Exit For ByteH = AscB(Mid(Bin, i, 1)) ' byteweise! h = Right("0" + Hex(ByteH), 2) + " " ' nach Hex If ByteH < 32 Then ' nicht druckbares Zeichen? ByteH = Asc(".") End If HexLine = HexLine + h CharLine = CharLine + Chr(ByteH) Next i ' Zeile zusammenbauen ' Hexadezimale Zeilenanfänge MakeLine = Right(" " & Hex((Zeile – 1) * cCols), 8) & ": " & _ HexLine ' Dezimale Zeilenanfänge ' MakeLine = Right(" " & Str((Zeile – 1) * cCols), 8) & ": " & _ ' HexLine MakeLine = Left(MakeLine + Space(45), 58) + "- " + CharLine End Function
Nachdem ReadBinary die Funktion Input zum Lesen der Daten einsetzt, liefert sie als Ergebnis eine Unicode-Zeichenfolge, so dass MakeLin mittels Mid die einzelnen Bytes extrahieren kann. Würde ReadBinary mit InputB arbeiten (was von der Laufzeit her günstiger ist), müsste MakeLin für die Zerlegung eben MidB verwenden. Weitere Features?
In der vorgestellten Form bietet der Code einen sehr guten Ausgangspunkt für die Implementation weiterer Features. Als Erstes wird man eine Suchfunktion ergänzen, wobei sich die Eingabe des Suchstrings sinnvollerweise über ein eigenes Formular mit zwei Textfeldern lösen lässt: eines für die Eingabe einer ASCII-Zeichenfolge, eines für die Eingabe einer hexadezimalen Zeichenfolge. Durch sinngemäße Erweiterung des Schemas gelangt man ohne großen Aufwand zur Ersetze-Funktionalität. Etwas mehr Einsatz erfordert da schon die Implementierung eines Bearbeitungsmodus, in dem der Benutzer die Datei wie einen Text im Überschreibmodus editieren kann. HexViewer wird damit zum »Patch-Editor«. Da ein Formular, anders als ein Textfeld, für seinen Client-Bereich keinen Bearbeitungsmodus kennt, kann man sich eines kleinen Tricks bedienen, um dennoch in den Genuss der Eingabefunktionalität zu kommen: Man spendiert dem Formular zwei weitere,
560
Registrierung
Registrierung Vielleicht werden Sie sich noch daran erinnern: Zu Zeiten von Windows 3.x pflegte jedes Programm eine eigene INI-Datei, in der es Einstellungen bis zur nächsten Sitzung persistent abspeichern konnte. Seit Windows 9x gibt es dafür die Systemregistrierung, eine zentrale Datenbank, in der alle Programme gemeinschaftlich ihre persistenten Einstellungen abspeichern können – und sollten. Wenn Sie einmal mit dem Systemprogramm Regedit einen Blick in die Registrierdatenbank geworfen haben, werden Sie vielleicht eine Vorstellung davon bekommen haben, welch immense Datenmengen darin abgelegt sind und wie vielschichtig die hierarchische Struktur der Datenbank doch ist. Schachteltiefen von acht oder neun Schlüsseln sind keine Seltenheit. Und wenn Sie diesen Abschnitt lesen, weil Sie darin auch ein Plätzchen für Ihr Programm suchen, sind Sie genau richtig. Um es gleich vorwegzunehmen, der Zugriff auf die Registrierdatenbank von Visual Basic ist mit gewissen Einschränkungen verbunden, mit denen sich aus der Sicht einer gewöhnlichen Anwendung aber durchaus leben lässt. So landen alle Informationen, die ein Visual-Basic-Programm unter Verwendung der »offiziellen« Methode SaveSetting speichert, in folgendem Zweig: HKEY_CURRENT_USER\Software\VB and VBA Program Settings\
Weiterhin stehen einem Visual-Basic-Programm für die Strukturierung seiner Informationen nicht mehr als zwei Schlüsselebenen zur Verfügung. Die obere Ebene ist für den »Programmnamen« reserviert. Da sich dieser aber frei wählen lässt, kann ein Visual-Basic-Programm ohne Weiteres die Schlüssel anderer Visual-Basic-Programme lesen und manipulieren sowie seine eigene Informationen auch unter mehreren Programmnamen ablegen. In der Ebene darunter finden sich dann die Unterschlüssel für die Kategorien oder Abschnitte mit den eigentlichen Werteinträgen, also Eins-zu-Eins-Zuordnungen zwischen Namen und den eigentlichen Werten. (Achtung: In der deutschen Benutzeroberfläche des Programms Regedit wird unglücklicherweise der Wertname als »Wert« und der Wert an sich als »Daten« bezeichnet!) Damit deckt sich die Struktur der in der Systemregistrierung abgespeicherten Information genau mit der früherer INI-Dateien. Ein Fortschritt ist das nicht, wenn man sich aber an die Regel hält, dass in der Systemdatenbank nur solche Informationen abgelegt werden sollten, die zu den Starteinstellungen eines Programms gehören, lässt sich damit ohne Weiteres auskommen. Das gesamte Set der für den Zugriff auf die Registrierung relevanten Größen umfasst nur vier Methoden, deren Zweck sich allein schon aus der Namensgebung heraus ablesen lässt: SaveSetting, GetSetting, GetAllSettings und DeleteSetting.
561
Registrierung
randlose Textfelder und sorgt dafür, dass diese die gleiche Schriftdefinition wie das Formular sowie eine geeignete Farbdefinition aufweisen. Im zweiten Schritt begrenzt man die Zeichenanzahl und die Abmessungen des einen Textfelds so, dass es die 48 Zeichen für den hexadezimalen Teil einer Zeile aufnehmen kann, und die des anderen Textfelds so, dass die 16 Zeichen des ASCII-Blocks hineinpassen. Positioniert man die Textfelder nun noch an der richtigen Stelle und weist ihnen die von ihnen verdeckten Zeichenfolgen als Wert zu, simuliert das die Eingabe »vor Ort«. Der Rest ist eine Frage der Implementation einer eigenen Tastatur- sowie Mausschnittstelle und der wechselseitigen Aktualisierung der Textfelder. Die Implementation kann die Änderungen dann bei Verlassen der jeweiligen Zeile zurückschreiben. Als weitere Verbesserung bietet sich die Einbeziehung der Zwischenablage an. Ausgerüstet mit der Funktionalität der Zwischenablage kann der Benutzer beispielsweise zwei Instanzen der Anwendung starten und Sequenzen hin- und herkopieren. Die Implementation von OLE-Drag&Drop ist dann nur noch eine reine Frage der Formalität.
Registrierung
RegTest Sitzungen wieder aufnehmen
Registrierung
Gibt es für den Benutzer etwas Schöneres, als seine Anwendung in der nächsten Sitzung wieder in dem Zustand vorzufinden, in dem er sie in der letzten Sitzung verlassen hat? Leider reizen nur die wenigsten Windows-Anwendungen dieses Konzept bis in die letzte Konsequenz aus. (Windows selbst ist davon nicht ausgenommen! Das entsprechende Feature kommt nun erst mit Windows 2000 richtig in Fahrt.) Das kleine Projekt RegTest soll Ihnen eine Vorstellung davon vermitteln, wie einfach die Umsetzung dieses Konzepts eigentlich doch ist.
Das Formular der Anwendung RegTest
Auf dem Formular der Anwendung finden sich vier Schaltflächen, deren Funktion sich über die Beschriftung erschließt, sowie zwei Bildfelder, in denen Farben abgebildet sind und die auf einen Mausklick hin den Standarddialog Farbe für die Auswahl einer anderen Farbe öffnen. Ein Klick auf die Schaltfläche EINSTELLUNGEN SPEICHERN speichert die Farbwerte der Bildfelder in einem Abschnitt namens »Farben« und den aktuellen Anzeigemodus sowie die aktuelle Position und die Abmessungen des Formulars in einem Abschnitt namens »Metrik«. Da Position und Abmessungen nur für den normalen Anzeigemodus (weder maximiert noch minimiert) gelten, werden sie auch nur von diesem Modus aus gespeichert. Für die anderen Anzeigemodi rechnet sich Windows die Werte bekanntlich selbstständig aus. Ein Klick auf die Schaltfläche EINSTELLUNGEN ABRUFEN entnimmt die aktuell gespeicherten Werte der Systemregistrierung und versetzt das Formular in den gespeicherten Zustand zurück. Zudem erscheint im Client-Bereich des Formulars eine Liste mit den gespeicherten Werten. Ein Klick auf die Schaltfläche EINSTELLUNGEN LÖSCHEN entfernt den für das Programm angelegten Schlüssel (samt der beiden Unterschlüssel) schließlich wieder aus der Registrierung, so dass der Befehl EINSTELLUNGEN ABRUFEN wirkungslos bleibt. Wenn das Formular schließlich mittels der Standardschaltfläche BEENDEN, über das Systemmenü oder über eines der SCHLIEßEN-Symbole in der Titelleiste geschlossen wird, erscheint ein Dialog, der nachfragt, ob der aktuelle Zustand gespeichert werden soll – das ist in diesem Fall nötig, weil der Befehl EINSTELLUNGEN LÖSCHEN sonst kleine bleibende Wirkung entfalten würde. Hier der Quelltext: '*********************************************************************** ' Standardmodul : QBEmulation ' Autor : 2000 Rudolf Huttary ' Beschreibung : Demonstriert das Speichern und Laden von Einstellungen ' in der Systemregisterung '*********************************************************************** Option Explicit
562
RegTest Sitzungen wieder aufnehmen
Private Sub Form_Load() cmdLoadSettings_Click End Sub
Registrierung
' Einstellungen aus der Registrierung laden Private Sub cmdLoadSettings_Click() Dim Entries Dim i As Long If Visible Then Cls Entries = GetAllSettings("RegTest", "Farben") If Not IsEmpty(Entries) Then For i = 0 To UBound(Entries, 1) Print Entries(i, 0) & " = " & Entries(i, 1) Next i End If Entries = GetAllSettings("RegTest", "Metrik") If Not IsEmpty(Entries) Then For i = 0 To UBound(Entries, 1) Print Entries(i, 0) & " = " & Entries(i, 1) Next i End If End If picColor1.BackColor = GetSetting("RegTest", "Farben", "ForeColor", _ Str(picColor1.BackColor)) picColor2.BackColor = GetSetting("RegTest", "Farben", "BackColor", _ Str(picColor2.BackColor)) WindowState = GetSetting("RegTest", "Metrik", "WindowState", _ Str(WindowState)) If WindowState = vbNormal Then Left = GetSetting("RegTest", "Metrik", "Left", Str(Left)) Top = GetSetting("RegTest", "Metrik", "Top", Str(Top)) Width = GetSetting("RegTest", "Metrik", "Width", Str(Width)) Height = GetSetting("RegTest", "Metrik", "Height", Str(Height)) End If End Sub ' Einstellungen in der Registrierung speichern Private Sub cmdSaveSettings_Click() SaveSetting "RegTest", "Metrik", "WindowState", Str(WindowState) If WindowState = vbNormal Then SaveSetting "RegTest", "Metrik", "Left", Str(Left) SaveSetting "RegTest", "Metrik", "Top", Str(Top) SaveSetting "RegTest", "Metrik", "Width", Str(Width) SaveSetting "RegTest", "Metrik", "Height", Str(Height) End If SaveSetting "RegTest", "Farben", "ForeColor", Str(picColor1.BackColor) SaveSetting "RegTest", "Farben", "BackColor", Str(picColor2.BackColor) End Sub
563
Registrierung
' Alle Einstellungen aus der Registrierung entfernen Private Sub cmdDeleteSettings_Click() On Error Resume Next DeleteSetting "RegTest" End Sub
Registrierung
' Programm beenden Private Sub cmdQuitt_Click() Unload Me End Sub ' Erste Farbe ändern Private Sub picColor1_Click() CommonDialog1.Color = picColor1.BackColor CommonDialog1.ShowColor picColor1.BackColor = CommonDialog1.Color End Sub ' Zweite Farbe ändern Private Sub picColor2_Click() CommonDialog1.Color = picColor2.BackColor CommonDialog1.ShowColor picColor2.BackColor = CommonDialog1.Color End Sub ' Programm beenden Private Sub Form_Unload(Cancel As Integer) If MsgBox("Einstellungen speichern?", vbYesNo) = vbYes Then cmdSaveSettings_Click End If End Sub
Der Code ist so geradlinig gehalten, dass es neben der bereits erwähnten logischen Notwendigkeit für die Rückfrage im Rahmen der Unload-Behandlung eigentlich nur zwei Besonderheiten gibt: die Fehlerbehandlung für den DeleteSetting-Aufruf und das bedingte Speichern bzw. Auslesen der Position und der Abmessungen des Formulars. Die Methode DeleteSetting generiert einen Laufzeitfehler, wenn sie ihrer Aufgabe, einen Schlüssel, Unterschlüssel oder Werteintrag zu löschen, nicht erfolgreich nachkommen kann, weil dieser nicht existiert. Diese doch recht rüde Maßnahme im Vergleich zur »Schwere« des Fehlers, dass etwas »nicht gelöscht werden kann, weil es entweder schon gelöscht wurde oder gar nicht da war«, wäre besser mit einem Rückgabewert des Typs Boolean gelöst worden. Wie dem auch sei, das Problem lässt sich mittels On Error Resume Next nachhaltig aus dem Weg räumen. Vergessen Sie aber nicht, gleich dahinter noch ein On Error Goto 0 zu setzen, um die Wirkung dieser doch recht ignoranten Fehlerbehandlung wieder aufzuheben, falls in der gleichen Routine noch weiterer Code folgt. Was die zweite Besonderheit betrifft, so kann man sich über das richtigere Vorgehen streiten. Wenn das Formular im Modus »Maximiert« oder »Minimiert« geschlossen wird, darf es die aktuellen Werte für die Abmessungen und die Position eigentlich nicht speichern, denn sonst würde es sich nach dem Wiedereinlesen der dann falschen Werte nicht mehr in den Normalzustand zurückversetzen lassen. Die einfachste Lösung besteht darin, die Werte nur dann zu speichern bzw. zu lesen, wenn das Formular in der normalen Fensteransicht geschlossen wird bzw.
564
RegTest Sitzungen wieder aufnehmen
wurde. Diesen Weg geht auch das Beispielprogramm. Will man, dass sich ein im maximierten oder minimierten Modus gestartetes (weil so beendetes) Programm auch wieder an seine letzte normale Position erinnert, ist einiges an zusätzlicher Logik notwendig. Ob sich der Aufwand jedoch im Verhältnis zum Effekt lohnt, dürfte wohl eher von der sonstigen Problemstellung des Programms abhängen.
Registrierung 565
Objektorientiertes Basic Wer heute mit Visual Basic programmiert, programmiert mit Objekten – ob er will oder nicht und ob er es wahrhaben will oder nicht. Formulare sind Objekte, Steuerelemente sind Objekte und letztlich alles, »was irgendwie mit einem Punkt notiert werden kann«, abgesehen von benutzerdefinierten Type-Datentypen. Da all diese Objekte bereits mit einer vorgefertigten Infrastruktur aufwarten, konzentriert sich die Programmierung nicht nur auf den geschickten Einsatz des Vorhandenen, sie beugt sich auch dessen Eigenheiten und – zuweilen auch – Beschränktheiten. Seit Einführung der Klassenmodule in Visual Basic ist die Programmierung abstrakter Datentypen möglich geworden. Ein abstrakter Datentyp ist die funktionale Definition (lies: Spezifikation) eines Datentyps und seiner Operationen aus der Sicht des Benutzers – wobei der »Benutzer« in diesem Fall der Programmierer ist, der diesen Datentyp in seinen Programmen einsetzt. Konkreter Vertreter eines abstrakten Datentyps ist das Objekt. Wie die Implementation des hinter einem abstrakten Datentyp steckenden Objekts im Einzelnen aussieht, spielt für den Benutzer keine Rolle, nach dem Motto: »Hauptsache, das Ding macht, was es machen soll«. Ja, die Implementation sollte sogar darauf bedacht sein, dass der Benutzer nur das zu sehen bekommt, was er sehen soll und keinen Deut mehr – denn dann kann er auch keinen Unfug anstellen, der das Objekt dazu bringt, sich nicht mehr im Sinne seiner Spezifikation zu verhalten. Man spricht in diesem Fall von Verkapselung. Sie können davon ausgehen, dass alle von Visual Basic angebotenen Modularten außer dem Standardmodul eigene Klassen definieren, die auf eine bereits bestehende Klasse aufsetzen. Für das Formularmodul heißt diese Klasse Form, für ein Benutzersteuerelementmodul UserControl und für ein Klassenmodul ClassModul. All diese Klassen sind bereits fertig implementierte abstrakte Datentypen, die als – zuweilen recht reichhaltig ausgestattete – »Umgebung« für das Modul fungieren. Aus diesem Grund »kann« ein Formular oder ein Benutzersteuerelement bereits eine ganze Menge, noch bevor Sie die erste Codezeile in das Modul geschrieben haben. Im abstrakten Sinne gilt dies auch für das Klassenmodul, dessen Umgebung ClassModul heißt und immerhin zwei Ereignisse generiert: das Initialize-Ereignis, wenn die benutzerdefinierte Klasse instanziiert wird (lies: ein neues Objekt produziert), und das Terminate-Ereignis, wenn diese Instanz wieder zerstört wird. Die folgenden beiden Abschnitte widmen sich dem Klassenmodul und dem Benutzersteuerelementmodul. Indem sie die Implementation abstrakter Datentypen demonstrieren, beleuchten sie die Prinzipien der objektorientierten Programmierung unter Visual Basic von ihrer gestalterischen Seite her.
Klassen selbst definieren Gehören Sie auch zu den Leuten, die sich vor Beginn einer Arbeit den Arbeitsplatz aufräumen und dann die Werkzeuge und Bauteile zurechtlegen? Nein? Wenn Sie mit eigenen Klassen programmieren wollen, sollten Sie aber dazu übergehen. Wer die Wahl hat, hat nämlich die Qual. Was bei der Programmierung mit vorkonfektionierten Objekten eher im Hintergrund steht, drängt sich bei der Implementation eigener Klassen in den Vordergrund: die eigene Konzeption. Die zentralen Überlegungen bei der Konzeption eines abstrakten Datentyps lauten daher: 1. Welche Informationen fasst man zu einer Klasse zusammen und mit welchen Mitteln repräsentiert man diesen Informationen? Das bestimmt die Datenstruktur der Klasse (Innensicht). 2. Was soll der Besitzer eines Objekts dieser Klasse von diesen Information in welcher Form sehen und manipulieren können? Daraus ergeben sich die Eigenschaften der Klasse (Außensicht).
567
Klassen selbst definieren
3. Welche Operationen soll der Besitzer über einem Objekt dieser Klasse ausführen können? Das bestimmt die Methoden der Klasse (Außensicht). 4. Gibt es Vorfälle, die ein Objekt dieser Klasse seinem Besitzer unaufgefordert mitteilen können sollte? Das bestimmt die Ereignisse der Klasse (Außensicht). Erst wenn diese Fragen beantwortet sind, kommt die letzte Überlegung:
Klassen selbst definieren
5. Wie lassen sich die Eigenschaften, Methoden und Ereignisse mit Blick auf die konkrete Datenstruktur prozedural umsetzen? Das bestimmt die Implementation der Klasse.
Ring eine einfache Klasse demonstriert Grundlegendes Bevor Sie damit beginnen, eigene Klassen mit mehrseitigem Code zu implementieren, sollten Sie Ihr Verständnis der grundlegenden Mechanismen des von Visual Basic benutzten Objektbegriffs noch einmal überprüfen. Der wichtigste Unterschied zwischen Variablen mit einem einfachen Datentyp und Objektvariablen besteht darin, dass Erstere einen Wert an sich repräsentieren und Letztere nur einen Verweis auf einen solchen Wert. Mit den Objektvariablen hielt in Visual Basic also durch die Hintertür ein Zeigerkonzept Einzug, das es als solches in der Sprache offiziell – also explizit – nicht gibt. Wer mit Visual Basic nach wie vor klassisch-prozedural programmiert, merkt in der Tat recht wenig davon, außer dass ihn seltsame Compilermeldungen wie »Ungültige Verwendung einer Eigenschaft« oder »Objekt unterstützt diese Eigenschaft oder Methode nicht« zuweilen zur Raison rufen, eine Let-Anweisung gegen eine Set-Anweisung auszutauschen oder umgekehrt. Wer Klassen selbst gestaltet, muss dagegen sehr genau wissen, was hinter den Kulissen vor sich geht. Um eine eigene Klasse schreiben zu können, benötigen Sie als Rahmen ein Projekt, in das Sie ein Klassenmodul einfügen können. Wenn Sie wollen, können Sie das Codeskelett der Klasse (samt Klassenmodul) auch unter Zuhilfenahme des Add-Ins KLASSENGENERATOR in einer interaktiven Sitzung gestalten und weiterhin pflegen. (Das Add-In »VB6 Klassengenerator« muss dazu gegebenenfalls über den Add-In-Manager zuerst geladen werden.) Nach meinem Geschmack bringt das aber nur bei »langweiligen« Klassen etwas. Und da der Klassengenerator unter Verdacht steht, geheimnisvolle Abstürze der Entwicklungsumgebung (aufgrund von internen Stack-überläufen) zu fördern, sollten Sie dazu übergehen, Ihre Arbeit dann am besten vor jedem Compilerlauf zu speichern (dafür gibt es im Dialogfeld OPTIONEN übrigens einen Schalter).
Einsatz des Klassengenerators Hier das Ergebnis einer Sitzung mit dem Add-In für eine Klasse namens Ring, die einen ganzzahligen Ringzähler mit frei wählbarem Zählbereich implementiert und ihren eigenen Zählerstand wahlweise auf einem standardmäßigen Formular oder einem beliebigen Objekt mit Ausgabefläche ausgeben können soll.
Struktur der Klasse im Klassengenerator zusammengestellt
568
Ring eine einfache Klasse demonstriert Grundlegendes
Das vom Klassengenerator erzeugte Codeskelett hat folgende Gestalt: Option Explicit
Klassen selbst definieren
'lokale Variable(n) zum Zuweisen der Eigenschaft(en) Private mvarCounter As Long 'lokale Kopie Private mvarRingForm As Form 'lokale Kopie Private mvarMinVal As Long 'lokale Kopie Private mvarMaxVal As Long 'lokale Kopie Public Sub PrintVal(Optional Target As Object) End Sub Public Property Let MaxVal(ByVal vData As Long) 'wird beim Zuweisen eines Werts zu der Eigenschaft auf der linken Seite 'einer Zuweisung verwendet. 'Syntax: X.MaxVal = 5 mvarMaxVal = vData End Property Public Property Get MaxVal() As Long 'wird beim Ermitteln eines Eigenschaftswerts auf der rechten Seite 'einer Zuweisung verwendet. 'Syntax: Debug.Print X.MaxVal MaxVal = mvarMaxVal End Property Public Property Let MinVal(ByVal vData As Long) 'wird beim Zuweisen eines Werts zu der Eigenschaft auf der linken Seite 'einer Zuweisung verwendet. 'Syntax: X.MinVal = 5 mvarMinVal = vData End Property Public Property Get MinVal() As Long 'wird beim Ermitteln eines Eigenschaftswerts auf der rechten Seite 'einer Zuweisung verwendet. 'Syntax: Debug.Print X.MinVal MinVal = mvarMinVal End Property Public Property Set PrintTarget(ByVal vData As Form) 'wird beim Zuweisen eines Objekts zu der Eigenschaft auf der linken 'Seite einer Set-Anweisung verwendet. 'Syntax: Set x.RingForm = Form1 Set mvarRingForm = vData End Property Public Property Get PrintTarget() As Form 'wird beim Ermitteln eines Eigenschaftswerts auf der rechten Seite 'einer Zuweisung verwendet. 'Syntax: Debug.Print X.RingForm
569
Klassen selbst definieren
Set PrintTarget = mvarRingForm End Property
Klassen selbst definieren
Public Property Let Counter(ByVal vData As Long) 'wird beim Zuweisen eines Werts zu der Eigenschaft auf der linken Seite 'einer Zuweisung verwendet. 'Syntax: X.Counter = 5 mvarCounter = vData End Property Public Property Get Counter() As Long 'wird beim Ermitteln eines Eigenschaftswerts auf der rechten Seite 'einer Zuweisung verwendet. 'Syntax: Debug.Print X.Counter Counter = mvarCounter End Property
Der Klassenknecht hat neben einer Fülle von immer gleichen Standardkommentaren alle Elementvariablen deklariert und Codegerüste für Property-, Sub- und Function-Routinen erzeugt. Elementvariablen tragen das Präfix mvar (von engl.: member variable). Sie wurden durchgängig als Private vereinbart und daher mit je einer Property Get- und Property Let/Set-Routine bedacht. Dass die Eigenschaft Counter das Attribut »Standardeigenschaft« erhalten hat, sieht man dem Code nicht an (!), es lässt sich aber über EXTRAS/PROZEDURATTRIBUTE nachprüfen.
Den Ringzähler implementieren Die fertige Implementation der Klasse sieht so aus: '*********************************************************************** ' Klasse : Ring ' Autor : 2000 Rudolf Huttary ' Beschreibung : Implementiert echten Ringzähler, der bei Überlauf ' und bei Unterlauf überspringt. ' Die Bereichsgrenzen sind frei wählbar. ' Wenn Bereichsgrenzen gleich sind, erfolgt keine ' Ringzählung '*********************************************************************** Option Explicit 'lokale Private Private Private Private
Variable(n) zum Zuweisen der Eigenschaft(en) mvarCounter As Long 'lokale Kopie mvarPrintTarget As Form 'lokale Kopie mvarMinVal As Long 'lokale Kopie mvarMaxVal As Long 'lokale Kopie
' Zähler in Interval einpassen und setzen Public Property Let Counter(ByVal vData As Long) If mvarMinVal = 0 And mvarMaxVal = 0 Then mvarCounter = vData Exit Property End If If vData < mvarMinVal Then vData = vData + (1 + (mvarMinVal – vData) _
570
Ring eine einfache Klasse demonstriert Grundlegendes
\ (mvarMaxVal – mvarMinVal)) * (mvarMaxVal – mvarMinVal) End If mvarCounter = (vData – mvarMinVal) Mod (mvarMaxVal – mvarMinVal) _ + mvarMinVal End Property ' Wert des Zählers liefern (Standardeigenschaft) Public Property Get Counter() As Long Counter = mvarCounter End Property
Klassen selbst definieren
' Obere Bereichsgrenze des Zählers setzen Public Property Let MaxVal(ByVal vData As Long) If vData = MinVal Then ' wenn gleich, Ringzählung ausschalten mvarMinVal = 0 mvarMaxVal = 0 Exit Property ElseIf vData < mvarMinVal Then Err.Raise 1002, "Ring", _ "Ring: MaxVal kleiner oder gleich MinVal nicht möglich" End If mvarMaxVal = vData Counter = mvarCounter ' Wert ggf. anpassen" End Property ' Obere Bereichsgrenze des Zählers liefern Public Property Get MaxVal() As Long MaxVal = mvarMaxVal End Property ' Untere Bereichsgrenze des Zählers setzen Public Property Let MinVal(ByVal vData As Long) If vData = MaxVal Then ' wenn gleich, Ringzählung ausschalten mvarMinVal = 0 mvarMaxVal = 0 Exit Property ElseIf vData > MaxVal Then Err.Raise 1003, "Ring", _ "Ring: MinVal größer gleich oder MaxVal nicht möglich" End If mvarMinVal = vData Counter = Counter ' Wert ggf. anpassen" End Property ' Untere Bereichsgrenze des Zählers liefern Public Property Get MinVal() As Long MinVal = mvarMinVal End Property ' Zielfenster für Ausgabe mit PrintVal setzen Public Property Set PrintTarget(ByVal vData As Form)
57 1
Klassen selbst definieren
Klassen selbst definieren
Set mvarPrintTarget = vData End Property ' Standardwert des Objekts ausgeben Public Sub PrintVal(Optional Target As Object) If Target Is Nothing Then ' Parameter nicht angegeben? If Not mvarPrintTarget Is Nothing Then ' Wurde Zielfenster gesetzt? mvarPrintTarget.Print mvarCounter ' Ja: im Zielfenster ausg. Else Debug.Print mvarCounter ' Nein: im Direktfenster ausg. End If Else Target.Print mvarCounter ' Im angebenen Zielf. ausgeben End If End Sub
Sie sehen, es ist doch allerhand nötig, um ein so einfaches Konzept »einigermaßen wasserdicht« als Objekt zu implementieren. Damit das Objekt mit der Ringzählung überhaupt beginnt, muss zumindest eine Grenze ungleich 0 gesetzt sein – das macht Sinn. Ansonsten können die Bereichsgrenzen beliebig gewählt werden, solange darauf geachtet wird, dass die obere Grenze nicht kleiner als die untere und die untere nicht kleiner als die obere gewählt wird – die Reihenfolge beim Setzen spielt hier also eine Rolle! Der Rest ist Rechnerei. Da die Mod-Operation von Visual Basic für negative Zahlen negative Werte liefert, ist sie unbrauchbar, sobald ein Wert, der kleiner als die untere Grenze ist (Unterlauf des Ringzählers) in den Bereich abgebildet werden muss. Die Lösung sieht nicht sehr freundlich aus, tut aber das, was sie soll: ' Zähler in Interval einpassen und setzen Public Property Let Counter(ByVal vData As Long) If mvarMinVal = 0 And mvarMaxVal = 0 Then mvarCounter = vData Exit Property End If If vData < mvarMinVal Then vData = vData + (1 + (mvarMinVal – vData) _ \ (mvarMaxVal – mvarMinVal)) * (mvarMaxVal – mvarMinVal) End If mvarCounter = (vData – mvarMinVal) Mod (mvarMaxVal – mvarMinVal) _ + mvarMinVal End Property
Nachdem Counter als Standardeigenschaft festgelegt ist, führt bereits die Anweisung Dim r As New Ring r = 10
' ausgeschrieben: r.Counter = 10
zum Aufruf dieser Routine. Sie kommt auch zum Aufruf, wenn Sie weiterschreiben: Dim r1 As New Ring r1 = r
' ausgeschrieben: r1.Counter = r.Counter
Hier findet also mitnichten eine Zuweisung des gesamten Objekts (inklusive Bereichsgrenzen) statt, sondern nur der Eigenschaft Counter, was eine beliebte Fehlerquelle ist. Das Setzen der Bereichsgrenzen geht gleichfalls nicht ohne einen gewissen Analyseaufwand vor sich. Die entsprechende Routine muss den Ringzähler ausschalten, wenn obere und untere
572
Ring eine einfache Klasse demonstriert Grundlegendes
Grenze gleich gesetzt werden, prüfen, ob ansonsten die untere Bereichsgrenze kleiner als die obere ist, und schließlich dafür Sorge tragen, dass der aktuelle Zählerstand in den neuen Bereich abgebildet wird.
Property Let/ Get/ Set
Als zusätzliches »Schmankerl« hat die Klasse noch zwei weitere Features erhalten. Ein RingObjekt kann einen Verweis auf einen Ausgabekontext speichern und ihren Wert (gegebenenfalls unter Verwendung eines abweichenden Ausgabekontexts) selbsttätig ausgeben. Falls kein Ausgabekontext definiert wurde, nimmt es standardmäßig das Direktfenster debug. ' Zielfenster für Ausgabe mit PrintVal setzen Public Property Set PrintTarget(ByVal vData As Form) Set mvarPrintTarget = vData End Property ' Referenz auf Zielfenster für Ausgabe liefern Public Property Get PrintTarget() As Form Set PrintTarget = mvarPrintTarget End Property ' Standardwert des Objekts ausgeben Public Sub PrintVal(Optional Target As Object) If Target Is Nothing Then ' Parameter nicht angegeben? If Not mvarPrintTarget Is Nothing Then ' Wurde Zielfenster gesetzt? mvarPrintTarget.Print mvarCounter ' Ja: im Zielfenster ausgeben Else Debug.Print mvarCounter ' Nein: im Direktfenster ausgeben End If Else Target.Print mvarCounter ' Im angebenen Zielf. ausgeben End If End Sub
Grund für die Ausgestaltung dieser Funktionalität war die Demonstration einer Property SetRoutine, wo die anderen Eigenschaften doch alle mit Property Let-Routinen ausgestattet sind. Der Unterschied zwischen den beiden Routinentypen liegt in der Art der Zuweisung, die erfolgt. Falls es sich um die Zuweisung eines konkreten Werts (elementarer Datentyp oder dynamisches Array) oder um die echte Kopie eines Objekts handelt, schreiben Sie eine Let-Routine, und falls
57 3
Klassen selbst definieren
' Untere Bereichsgrenze des Zählers setzen Public Property Let MinVal(ByVal vData As Long) If vData = MaxVal Then ' wenn gleich, Ringzählung ausschalten mvarMinVal = 0 mvarMaxVal = 0 Exit Property ElseIf vData > MaxVal Then Err.Raise 1003, "Ring", _ "Ring: MinVal größer gleich oder MaxVal nicht möglich" End If mvarMinVal = vData Counter = Counter ' Wert ggf. anpassen" End Property
Klassen selbst definieren
es sich nur um die Zuweisung einer Objektreferenz handelt, eine Set-Routine. Die Herausgabe des Werts einer Eigenschaft erfolgt dagegen unterschiedslos mittels einer Property Get-Routine. Property Get/Let/Set-Routinen kommen somit immer dann ins Spiel, wenn ein Gleichheitszeichen auftaucht – bei Rechtswerten die Get-Routine und bei Linkswerten je nachdem die Setoder die Let-Routine.
Klassen selbst definieren
Parameterübergabe bei Funktionsaufrufen Die Parameterübergabe bei Funktionsaufrufen kann wirklich zu lästigen Fehlern führen, wenn man nicht genau weiß, was dabei vor sich geht. Sub Set5(ByVal r As Ring) r = 5 End Sub Sub Set6(ByRef r As Ring) r = 6 End Sub Private Sub Form_Load() Dim RingVar As New Ring Set5 RingVar r.PrintVal Set6 RingVar r.PrintVal
' Ausgabe 5 ' Ausgabe 6
End Sub
So ist als Erstes zu bemerken, dass es in diesem Beispiel wirklich keinen Unterschied macht, ob die Objektvariable der Funktionen als ByRef oder als ByVal vereinbart ist. Den Unterschied gibt es natürlich, nur auf anderer Ebene: Wenn Set5 den Wert der Objektvariable r ändert, dieser also ein anderes Objekt zuweist, hat das für RingVar keine Konsequenzen, wenn Set6 dies macht, würde sich auch der Wert von RingVal ändern. Zweitens, wie der Debugger leicht zeigt, kommt weder bei dem einen Aufruf noch bei dem anderen Aufruf eine Get- oder Let-Routine zur Ausführung. Welche auch, es gibt ja keine Eigenschaft, die den Typ Ring hat. Selbst wenn es eine solche gäbe – geben wir ihr den Namen Value – und diese dazu noch Standardeigenschaft wäre, käme bei Übergabe eines Funktionsparameters weder die Get-Routine noch die Set-Routine zum Aufruf, da der Compiler zuerst prüft, ob der Objekttyp passt, und nur wenn dieser nicht passt, den Standardparameter auswertet. Anders verhält es sich jedoch, wenn Sie den Wert bewusst oder aus Versehen klammern: Function SetValue(ByRef r As Ring) Set r = AndererWert End Function ... SetValue (RingVar) ' Standardparameter
Nun kommt schon mal die Get Value-Routine zum Aufruf, weil eine Auswertung des Standardparameters stattfindet. Man möchte nun meinen, dass auch noch die Set Value-Routine für RingVar zum Aufruf kommen kann, weil SetValue ja einen ByRef-Parameter einsetzt. Das geht aber nicht, weil die Typen wieder gleich sind und das Objekt selbst erneut das Rennen macht – es sei denn, man schreibt den Standardparameter explizit hin.
574
Ring eine einfache Klasse demonstriert Grundlegendes
Ein viel schwerwiegenderes Problem ist aber die Tatsache, dass man einer Routine Eigenschaften eines Objekts zwar als Parameter übergeben kann, Änderungen des Werts, auch wenn die Übergabe mit ByRef erfolgt ist, außerhalb der Routine grundsätzlich nicht sichtbar werden. Auch wenn dieses Verhalten logisch erscheint, müsste die Routine doch den Zugriff auf den Wert weiterhin über Get und Let/Set handhaben, so ist es hochgradig inkonsistent mit den allgemeinen Regeln der Parameterübergabe und führt leicht zu schwer auffindbaren Fehlern – ein echter Pferdefuß in der Objektimplementation von Visual Basic. Dass der Aufruf Set5 die Eigenschaft MaxValue nicht ändern kann, ist korrekt:
Klassen selbst definieren
RingVar.MaxVal = 0 Set5 RingVar.MaxVal Print RingVar.MaxVal
' Ausgabe: 0
aber dass auch Set6 damit nicht durchkommt, ist ärgerlich: RingVar.MaxVal = 0 Set6 RingVar.MaxVal Print RingVar.MaxVal
' Ausgabe: 0
Denn der folgende Aufruf sollte semantisch äquivalent sein: Dim Max As Long RingVar.MaxVal = 0 Max = RingVar.MaxVal = 0 Set6 Max RingVar.MaxVal = Max Print RingVar.MaxVal
' Ausgabe: 6
Kopien von Objekten anfertigen Wie vervielfältigt man Objekte? Die Set-Anweisung hantiert bekanntlich nur mit Objektreferenzen. Vom Prinzip her könnte man die Vervielfältigung eines Objekts zwar als Let-Anweisung für eine Standardeigenschaft formulieren, die den gleichen Typ wie das Objekt hat, in diesem Fall müsste das zu kopierende Objekt dem anderen Objekt jedoch seinen gesamten Zustand entweder über Public-Variablen oder über entsprechende Public- bzw. Friend-Eigenschaften zumindest zugänglich machen – dazu wären die entsprechenden Get-Routinen erforderlich. Das ist natürlich nicht gerade im Sinne des Konzepts »abstrakter Datentyp« und absorbiert außerdem die wichtige Ressource »Standardeigenschaft«, von der es je Klasse ja immer nur eine gibt. Der einzige Weg, mit legalen Mitteln an die Kopie eines Objekts zu kommen, ist der, dass das Objekt diese selbst anfertigt – sprich, eine Methode Clone vorsieht, die ein neues Objekt gleichen Typs instanziiert und schrittweise in den gleichen Zustand versetzt. Aber auch dieser Weg ist nicht immer möglich, wenn es schreibgeschützte Eigenschaften gibt, an die kein Rankommen ist. Im Folgenden ein Vorschlag, wie die Clone-Methode für die Klasse Ring aussehen könnte. Eine Kopie »von außerhalb des Objekts« zu erstellen, ist nicht möglich, da die Eigenschaft PrintTarget lesegeschützt ist und somit keine Get-Routine besitzt. ' Kopie des Objekts erzeugen und Verweis darauf liefern Public Function Clone() As Ring Set Copy = New Ring ' neues Objekt anlegen If mvarMaxVal > 0 Then ' Fehlermeldung vermeiden Copy.MaxVal = mvarMaxVal ' restliche Eigenschaften Copy.MinVal = mvarMinVal Else Copy.MinVal = mvarMinVal
575
Klassen selbst definieren
Klassen selbst definieren
Copy.MaxVal = mvarMaxVal ' restliche Eigenschaften End If Copy = Me ' Standardwert! Set Copy.PrintTarget = mvarPrintTarget End Function
Sobald die Implementation einer Klasse fertig ist (falls möglich auch bereits zwischendurch), sollten Sie einen speziellen Testcode zusammenstellen, der alle Funktionalitäten der Klasse auf Herz und Nieren prüft. Bei der Fehlersuche und -analyse ist der Debugger übrigens ein ausgesprochen wertvolles, wenn nicht gar unverzichtbares Hilfsmittel. Hier ein Testcode für die Klasse Ring, bei dem Anweisungen, die bewusst Fehler und spezielle Zustände erzeugen, auskommentiert wurden. ' Testcode für die Klasse Ring Option Explicit Sub Form_Load() Dim a As New Ring, b As New Ring Dim i Set a.PrintTarget = Me a.MaxVal = 10 a.MinVal = 3 'Set a.PrintTarget = Nothing ' Debug-Ausgabe testen! 'a.MaxVal = 2 ' Fehlererkennung testen 'a.MinVal = 10 ' Fehlererkennung testen a = 9 ' Wert im Bereich testen a.PrintVal ' Ausgabe: 9 a = 19 ' Wert oberhalb testen a.PrintVal ' Ausgabe: 5 'a.MinVal = a.MaxVal ' Ringzählung ausschalten a = -19 ' Wert unterhalb testen a.PrintVal ' Ausgabe: 9 'a.PrintVal Me a = b a.PrintVal 'Set a = b
' mit Argument testen ' weist nur Wert zu! : aus 0 wird 7! ' Ausgabe: 7 ' ändert Referenz von a auf b!
Set b = a.Clone ' setzt a auf Kopie von b! Print a.Counter, a.MaxVal, a.MinVal ' Ausgabe: 7 10 3 Print b.Counter, b.MaxVal, b.MinVal ' Ausgabe: 7 10 3 b = -19 ' aus -19 wird 9 a.PrintVal ' Ausgabe: 7 b.PrintVal ' Ausgabe: 9 add(a, b).PrintVal Me End Sub
' Bereich ist (0,0), Wert ist 16
Function add(ByVal a As Ring, b As Ring) As Ring Set add = New Ring ' Neues Objekt
576
3DAnimation Drahtgittermodelle frei im Raum gedreht
add = a + b End Function
3DAnimation Drahtgittermodelle frei im Raum gedreht Ziel dieses Abschnitts wird es sein, ein Drahtgittermodell im dreidimensionalen Raum frei drehen und in der Projektion von allen Seiten betrachten zu können. Darüber hinaus sollte man das Objekt in seinem aktuellen Zustand speichern und wieder einlesen können. Die Antworten auf die eingangs des übergeordneten Abschnitts angestellten Überlegungen sehen für das Drahtgitter als Klasse Shape3D etwa so aus.
57 7
Klassen selbst definieren
1. Die Repräsentation ist klar: Ein Drahtgitter der Klasse Shape3D besteht aus einer Ansammlung von Linien des Typs Line3D, die ihrerseits durch Punkte des Typs Point3D in einem gemeinsamen Koordinatensystem beschrieben werden. 2. Eigenschaften sind eigentlich keine nötig, wenn sich das Objekt selbst darstellen, speichern und laden kann. Rotation und Linienfarbe kann man via Parameter übergeben. Sinnvoll ist aber sicher eine Value-Eigenschaft, die eine Kopie der Datenstruktur ermöglicht, wenn es keine Clone-Methode gibt. Denkbar sind aber auch noch weitere Eigenschaften wie: standardmäßige Linienfarbe, Linienstärke und Perspektive der Darstellung sowie vielleicht Bewegungsrichtung und -geschwindigkeit, Rotationsachse und -geschwindigkeit und gar noch die dazugehörigen Beschleunigungen. 3. An Methoden benötigt das Objekt eine Möglichkeit, eine bestehende Beschreibung zu löschen (Clear), eine Linie in eine bestehende Beschreibung einzufügen (Insert), sich darzustellen (Draw), sich in einer Datei zu speichern (Save), eine bestehende Repräsentation aus einer Datei zu laden (Load) und sich zu drehen (Rot3D). Mit Blick auf weitere Einsatzbereiche wären sicher auch noch eine Translation sowie eine Skalierung sinnvoll. 4. Was die vierte Überlegung betrifft: Das Objekt könnte beispielsweise Kollisionen mit anderen Objekten bemerken und melden, das macht aber in dem vorgestellten einfachen Rahmen keinen Sinn.
Klassen selbst definieren
Klassen selbst definieren
Zwei Phasen der Figur Pyramide in 3D- Animation
Antwort auf Überlegung 5 ist die Implementation selbst. Sie sollte schrittweise geschehen, wenn – wie im vorliegenden Fall – keine exakte Spezifikation vorliegt. Dazu zäumt man das Pferd am besten von hinten auf und kümmert sich als Erstes um die Klasse Point3D sowie um die Art und Weise, wie sich ein Objekt dieser Klasse am geschicktesten drehen lässt. Die Abbildung zeigt einen kleinen Vorgeschmack. Rotation3D eine Klasse, deren Objekte » Drehungen« sind
Vom Prinzip her könnte man die Drehung eines einzelnen Punkts – und darauf lässt sich ja das Drehen eines Drahtgitters reduzieren – vollständig in eine Methode der Klasse Point3D packen. Die Methode erhält dann drei Double-Parameter zur Entgegennahme der Winkel für die drei möglichen Drehebenen und dreht dann die aktuellen Koordinaten des Punkts in einer vielleicht etwas wüsten Rechnerei – Klappe zu, Affe tot. Das hätte jedoch nicht nur Laufzeitnachteile, weil für die Drehung eines jeden einzelnen Punkts doch eine ganze Reihe von Sinus- und Cosinus-Operationen anfallen würden, es wäre auch viel zu »prozedural« gedacht. Macht man sich den objektorientierten Gedanken zu Eigen, erkennt man schnell, dass sich eine Drehung wunderbar als eigenständiges Objekt auffassen lässt. Als Operationen bieten sich an: Zurücksetzen auf die neutrale Drehung, die einen Punkt auf sich selbst abbildet, (Init-Methode); Weiterdrehen um einen gegebenen Winkel auf der xy-Ebene (RotXY), der xz-Ebene (RotXZ) und der zyEbene (RotYZ); Drehen eines Punkts (Rot3D) und schließlich die Komposition (lies: »aus Zwei mach Eins«) zweier Drehungen (RotXYZ). Bleibt noch die Frage nach der Repräsentation und der tatsächlichen Berechnung. In der Mathematik ist es üblich, eine Rotation im dreidimensionalen Raum als lineare Abbildung zu betrachten, deren Repräsentation über eine 3×3-Matrix erfolgt. Betrachtet man einen Punkt als 1×3Matrix, kann die Anwendung einer Linearen Abbildung auf den Punkt nach den Gesetzen der Matrizenrechnung als Multiplikation der Matrix mit dem Punkt erfolgen. Und die Komposition zweier Drehungen ergibt sich aus dem Produkt der beiden Matrizen. Wie aber kommt man vom Winkel zur Matrix? Die Drehung auf einer von zwei Koordinatenachsen aufgespannten Ebene entspricht der bereits im Zusammenhang mit dem Projekt WankelAnimation vorgestellten Drehung im Zweidimensionalen mit unveränderter dritter Koordinate. Für eine Drehung um die xy-Ebene also: x' =
578
cos(w) · x
-
sin(w) · y
+
0·z
y' =
sin(w) · x
+
cos(w) · y
+
0·z
z' =
0·x
+
0·y
+
1· z
3DAnimation Drahtgittermodelle frei im Raum gedreht
Die anderen Drehungen ergeben sich nach dem gleichen Schema. In die Matrizenrechnung umgesetzt, sieht das Ganze dann so aus:
−
Klassen selbst definieren
−
−
Für die Multiplikation zweier Matrizen M und N gilt die Formel:
=∑
⋅
und für die Multiplikation einer Matrize M mit einem Punkt P:
=∑
⋅
Keine Angst vor den Formeln! In der Implementation sieht die Sache eher harmlos aus. Um die neue Klasse zu implementieren, legen Sie am besten ein neues Projekt 3DAnimation des Typs »Standard-EXE« an und fügen ein Klassenmodul mit dem Namen Rotation3D ein. Die Entwicklungsumgebung von Visual Basic bietet Ihnen für die Gestaltung der neuen Klasse einen Klassengenerator in Form eines Add-Ins an, er bringt allerdings nicht allzu viel, weil man im Allgemeinen über die Hälfte des von ihm produzierten Codes wieder über den Jordan schickt und im weiteren Verlauf an seine Benennungskonvention gebunden ist. Außerdem hat der Klassengenerator so seine Macken, nicht nur wenn es um die Hierarchisierung von Klassen geht. Die fertige Implementation der Klasse sieht so aus. '*********************************************************************** ' Klasse : Rotation3D ' Autor : 2000 Rudolf Huttary ' Beschreibung : Repräsentiert Drehung im 3-dimensionalen Raum ' : Standardeigenschaft ist Value '*********************************************************************** Option Explicit ' Repräsentation Private Matrix() As Double Private m() As Double
' Rotationsmatrix ' Hilfsmatrix
Private Pi4 As Double
57 9
Klassen selbst definieren
' Echte Zuweisung (Standardeigenschaft) Public Property Let Value(New_Value() As Double) Matrix = New_Value ' Echte Kopie! End Property
Klassen selbst definieren
' Liefert Referenz auf Objekt Public Property Get Value() As Double() Value = Matrix End Property ' Rotiert Punkt in Koordinatendarstellung Public Sub Rot3D(x As Double, y As Double, z As Double) Dim x1 As Double, y1 As Double x1 = Matrix(0, 0) * x + Matrix(0, 1) * y + Matrix(0, 2) * z y1 = Matrix(1, 0) * x + Matrix(1, 1) * y + Matrix(1, 2) * z z = Matrix(2, 0) * x + Matrix(2, 1) * y + Matrix(2, 2) * z x = x1: y = y1 End Sub ' Rotation um Degrees Grad auf XY-Ebene Public Sub RotXY(ByVal Degrees As Double) ReDim m(2, 2) As Double DegToRad Degrees m(0, 0) = Cos(Degrees) m(0, 1) = -Sin(Degrees) m(1, 0) = Sin(Degrees) m(1, 1) = Cos(Degrees) m(2, 2) = 1 RotXYZ m End Sub ' Rotation um Degrees Grad auf XZ-Ebene Public Sub RotXZ(ByVal Degrees As Double) ReDim m(2, 2) As Double DegToRad Degrees m(0, 0) = Cos(Degrees) m(0, 2) = -Sin(Degrees) m(1, 1) = 1 m(2, 0) = Sin(Degrees) m(2, 2) = Cos(Degrees) RotXYZ m End Sub ' Rotation um Degrees Grad auf YZ-Ebene Public Sub RotYZ(ByVal Degrees As Double) ReDim m(2, 2) As Double DegToRad Degrees m(0, 0) = 1 m(1, 1) = Cos(Degrees) m(1, 2) = -Sin(Degrees) m(2, 1) = Sin(Degrees)
580
3 DAnimation Drahtgittermodelle frei im Raum gedreht
m(2, 2) = Cos(Degrees) RotXYZ m End Sub ' Grad ins Bogenmaß umrechnen Private Sub DegToRad(Degrees As Double) Degrees = Degrees * Pi4 / 45 End Sub
Klassen selbst definieren
' Initialisierungscode Private Sub Class_Initialize() Pi4 = Atn(1) Init End Sub ' Rotation ist identische Abbildung Public Sub Init() ReDim Matrix(2, 2) Matrix(0, 0) = 1 Matrix(1, 1) = 1 Matrix(2, 2) = 1 End Sub ' Hilfsroutine, multipliziert Matrizen und aufgrund der Wahl und ' Implementation der Standardeigenschaft auch Rotation3D-Objekte ' Aufruf: Dim r1 As Rotation3D, r2 As Rotation3D ' r1.RotXYZ (r2) ' Klammern sind wichtig Public Sub RotXYZ(Matrix1() As Double) Dim i As Long, j As Long, k As Long Dim res(2, 2) As Double For i = 0 To 2 For j = 0 To 2 For k = 0 To 2 res(i, j) = res(i, j) + Matrix(i, k) * Matrix1(k, j) Next k, j, i Matrix = res End Sub
Über den Code gibt es eigentlich nicht viel zu sagen, was über das Mathematische hinausgeht. Die Initialisierung der Matrix mit den Werten für die neutralen Drehung erfolgt im Zuge der Initialize-Behandlung durch Aufruf von Init. Sinnvollerweise gehört Init zu den Methoden der Klasse, so dass es dem Besitzer eines Objekts jederzeit möglich ist, das Objekt in einen definierten Zustand zu versetzen. Die drei auf die Ebenendrehung spezialisierten Methoden RotXY, RotXZ und RotYZ haben eine additive Bedeutung: »Weiterdrehen in der jeweiligen Ebene um so und soviel Grad«. Dazu errechnen sie jeweils die zu der Drehung um den gegebenen Winkel passende Matrix und multiplizieren diese dann mit der bereits bestehenden Drehungsmatrix. Das Resultat ist die Komposition beider Drehungen. Einzig die Standardeigenschaft Value verbreitet einen gewissen »Flair« – zumindest aus Sicht der objektorientierten Programmierung – , da sie die folgende Schreibweise gestattet: Dim r1 As Rotation3D, r2 As Rotation3D r1.RotXYZ (r2) ' r1.RotXYZ r2 erzeugt Laufzeitfehler!
581
Klassen selbst definieren
Die Klammerung veranlasst Visual Basic zur Auswertung des Objekts und führt somit zum Aufruf der Get Value-Methode. Genauso gut könnte man aber auch schreiben: r1.RotXYZ r2.Value
Klassen selbst definieren
So gesehen ist auch die Let Value-Methode etwas Besonderes, weil sie eine echte Kopie der Matrix anfertigt (was bei Objekten nicht immer der Fall ist!). Man kann also schreiben: Dim r1 As New Rotation3D, r2 As New Rotation3D ... r2 = r1 r2.RotXY Winkel ' r1 bleibt unverändert
Point3D ein Punkt im Raum Die Spezifikation der zu erstellenden Klasse Point3D lautet: 1. Ein Punkt im dreidimensionalen Raum hat drei Koordinaten, so dass sich das Objekt über drei Double-Variablen X, Y, Z repräsentieren lässt. Die Variablen werden als Public vereinbart, was den freien Schreib- und Lesezugriff gestattet. Letzteres ist vorteilhaft, weil das Linienobjekt später bequem auf die Koordinaten zugreifen kann. 2. Das Objekt soll eine Zeichenfolgendarstellung nach dem Muster »P(x, y, z)« haben, mit der es auch initialisiert werden kann (Str-Eigenschaft). Darüber hinaus soll es eine Value-Eigenschaft haben, die es gestattet, das Objekt einem anderen gleichartigen Objekt als Wert zuzuweisen (dafür ist es erforderlich, dass die Eigenschaften X, Y, Z zugänglich sind). 3. An Operationen soll das Objekt eine Konvertierungsoperation unterstützen, die verschiedene Darstellungen in den eigenen Datentyp überführt (CPoint3D), es soll seinen Wert in einer geöffneten Binärdatei speichern (Save) und lesen (Load) können und natürlich soll es den Punkt, den es repräsentiert, drehen (Rot3D) und auch zeichnen (Draw) können. Die Implementation sieht so aus: '*********************************************************************** ' Klasse : Point3D ' Autor : 2000 Rudolf Huttary ' Beschreibung : Repräsentiert Punkt im 3-dimensionalen Raum ' : Standardeigenschaft ist Value '*********************************************************************** Option Explicit ' Repräsentation Public x As Double Public y As Double Public z As Double ' Echte Zuweisung Public Property Let Value(New_Value As Point3D) x = New_Value.x y = New_Value.y z = New_Value.z End Property ' Liefert Referenz auf Objekt Public Property Get Value() As Point3D
582
Point3D ein Punkt im Raum
Set Value = Me End Property ' Liefert eine echte Kopie des Punkts Public Function Clone() As Point3D Set Clone = New Point3D Clone = Me End Function Objekts
Klassen selbst definieren
' Liefert Zeichenfolgendarstellung des Public Property Get Str() As String Str = "P(" & Conversion.Str(Round(x, Conversion.Str(Round(y, 4)) & Conversion.Str(Round(z, 4)) & End Property
4)) & "," & _ "," & _ ")"
' Initialisiert Punkt über Zeichenfolgendarstellung Public Property Let Str(s As String) If Left(s, 1) = "P" Then x = Val(Mid(s, 3)) y = Val(Mid(s, InStr(s, ",") + 1)) z = Val(Mid(s, InStrRev(s, ",") + 1)) Else Err.Raise 1000, "Point3D", "Point3D.Str: Falsches Format" End If End Property ' Initialisiert Punkt. Akzeptiert verschiedene Formate Public Function CPoint3D(param1, ParamArray params()) As Point3D Select Case TypeName(param1) Case "String" Str = param1 Case "Point3D" Me = param1 Case Else x = param1 y = params(0) z = params(1) End Select Set CPoint3D = Me End Function ' Punkt in Objekt Target als Kreis zeichnen Public Sub Draw(Target As Object, Optional Color As Long, _ Optional Radius As Single = 1) If Radius > 0 Then Target.Circle (x, y), Radius, Color End If End Sub ' Rotiert Punkt unter Verwendung des Rotationsobjekts
583
Klassen selbst definieren
Klassen selbst definieren
Public Function Rot3D(r As Rotation3D) As Point3D r.Rot3D x, y, z Set Rot3D = Me End Function ' Schreibt Punkt binär in Datei FileNr Public Sub Save(ByVal FileNr As Integer) Put FileNr, , x Put FileNr, , y Put FileNr, , z End Sub ' Liest Punkt binär aus Datei FileNr Public Sub Load(ByVal FileNr As Integer) Get FileNr, , x Get FileNr, , y Get FileNr, , z End Sub
Bei der Durchsicht des Codes werden Sie feststellen, dass die Klasse eine Clone-Methode besitzt. Sie ist eigentlich nicht notwendig, da Let Value ja bereits die Erstellung echter Kopien ermöglicht (Clone basiert auf Let Value). Wäre der Lesezugriff für die Eigenschaften X, Y, Z versagt, was er aufgrund der Public-Deklaration nicht ist, gäbe es noch die Möglichkeit, den Wert über die Str-Eigenschaft zu kopieren – wenn auch mit Genauigkeitsverlust wegen der Rundung auf vier Stellen nach dem Komma. Str ist so implementiert, dass Let Str die von Get Str erzeugte Zeichenfolgenrepräsentation wieder korrekt einliest. In der Routine Get Str, die zur Ausbügelung von Rechenfehlern im Bereich der 17ten Stelle hinter dem Komma alle Koordinaten mittels Round auf vier Dezimalstellen rundet, würde es einen Namenskonflikt geben, wenn die für die (nicht gebietsspezifische Zahlenwandlung eingesetzte Standardfunktion Str zur Namensraumauflösung nicht mit dem Namen ihres Moduls Conversion qualifiziert wäre. (Solche Benennungsprobleme führen zur Laufzeit zu hässlichen StackÜberlauf-Fehlern.) Im Projekt 3DAnimation hat die Methode zwar keine direkte Anwendung, nachdem sie aber die Koordinaten des repräsentierten Punkts im numerischen Zeichenfolgenformat liefert, sollte sie in einer Klasse wie Point3D nicht fehlen. Die Konversionsfunktion CPoint3D überführt verschiedene Formate in den Datentyp Point3D und ermöglicht so die typtolerante Initialisierung. Da die Methode eine Referenz auf das Objekt liefert, lässt sie sich auch als Linkswert in einer Set-Zuweisung angeben – ganz im Stile der Vorbilder CStr, CLng usw. Auch diese Methode ist reicher ausgelegt, als es das Projekt 3DAnimation erfordern würde, sollte aber gleichfalls in einer Klasse wie Point3D nicht fehlen. Von der Implementation der Methoden Save und Load ausgehend lässt sich bereits gut auf das in 3DAnimation verfolgte Gesamtkonzept schließen. Nachdem die beiden Methoden die Übergabe einer Dateinummer erwarten, gehen sie davon aus, dass sie in eine bereits geöffnete Binärdatei schreiben bzw. aus einer solchen lesen. Ihr Code ist natürlich komplementär ausgelegt. Besonders einfach ist die Methode Rot3D ausgefallen. Sie verlässt sich völlig auf die gleichnamige Methode des im Parameter übergebenen Rotation3D-Objekts. Beachten Sie, dass aus den im übergeordneten Abschnitt bereits erwähnten Gründen ein Einsatz von Rotation3D.Rot3D nach folgendem Muster eigentlich ausgeschlossen sein sollte: Dim p As New Point3D Dim r As New Rotation3D ... r.Rot3D p.x, p.y, p.z
584
' Vorsicht!!!!
Point3D ein Punkt im Raum
Private Sub TestPoint3D() Dim p As Point3D Dim p1 As New Point3D Dim p2 As New Point3D Dim p3 As New Point3D Dim p4 As New Point3D Dim r As New Rotation3D ' Grundfunktionen p1.CPoint3D 1, 0, 1 Print p1.Str p2 = p1 Print p2.Str Set p = p3.CPoint3D(p2) Print p3.Str p4.CPoint3D "P(2,0,0)" Print p4.Str p1.Str = p4.Str ' Drehen r.RotXY 90 Print p4.Rot3D(r).Str r.Init r.RotYZ 90 Print p4.Rot3D(r).Str r.Init r.RotXZ 90 Print p4.Rot3D(r).Str r.RotXY 90 r.RotXZ 90 Print p4.Rot3D(r).Str r.Rot3D p4.x, p4.y, p4.z Print p4.Rot3D(r).Str Scale (-3, 3)-(3, -3) p1.Draw Me, , 0.1 p3.Draw Me, , 0.2 p4.Draw Me, , 0.3 End Sub
' Testroutine für Point3D, Rotation3D
' Initialisieren ' Ausgabe: P(1,0,1) ' Kopie erstellen ' Ausgabe: P(1,0,1) ' Initialisierung mit Objekt ' Ausgabe: P(1,0,1) ' Initialisierung mit Zeichenfolge ' Ausgabe: P(2,0,0) ' Wertzuweisung über Str (!) ' 90° auf xy-Ebene drehen ' Rotieren, Ausgabe: P(0,2,0) ' Rotation zurück setzen ' 90° auf yz-Ebene drehen ' Rotieren, Ausgabe: P(0,0,-2) ' Rotation zurück setzen ' 90° auf xz-Ebene drehen ' Rotieren, Ausgabe: P(-2,0,0) ' 90° auf xy-Ebene hinzu ' 90° auf xz-Ebene hinzu ' Rotieren, Ausgabe: P(2,0,0) ' funktioniert !!!! ' Rotieren, Ausgabe: P(-2,0,0)
585
Klassen selbst definieren
Dass dieser Aufruf »wider Erwarten« dennoch korrekt funktioniert, liegt an der Public-Vereinbarung der drei Elementvariablen. Er beweist erstens, dass Visual Basic für Public-Variablen implizit scheinbar keine Let- und Get-Routinen generiert, und zweites, dass in der Semantik von Visual Basic ein gravierender Fehler existiert – denn woher soll der Besitzer eines Objekts wissen, ob die von ihm benutzte Eigenschaft als Public-Elementvariable oder als Property GetFunktion implementiert ist. Die beiden Implementationen verhalten sich aber leider unterschiedlich. Die Methode Draw zeichnet den Punkt als Kreis mittels der Circle-Methode. Damit ein Objekt einer selbst definierten Klasse jedoch überhaupt eine Zeichenoperation durchführen kann, benötigt es ein Objekt, das die Ausgabefläche und die Zeichenoperation stellt. In diesem Fall wird es über den Parameter Target gestellt, der den flexiblen Typ Object trägt. Nun ist ein guter Zeitpunkt für einen Test der beiden Klassen gekommen. Der folgende einfache Testcode lässt darauf schließen, dass die Implementation soweit in Ordnung ist und das tut, was sie soll.
Klassen selbst definieren
Line3D eine Linie aus zwei Punkten
Klassen selbst definieren
Die Spezifikation der Klasse Line3D lautet: 1. Eine Linie im dreidimensionalen Raum wird vollständig durch einen Startpunkt StartP und einen Endpunkt EndP beschrieben. Die Repräsentation erfolgt durch zwei Private-Elementvariablen mvarStartp und mvarEndP des Typs Point3D, auf die ein Get-Zugriff möglich sein soll. 2. Das Objekt soll auf den Datentyp Point3D aufbauend eine Zeichenfolgendarstellung nach dem Muster »L(P(x, y, z), P(x, y, z))« haben, mit der es auch initialisiert werden kann (StrEigenschaft). Darüber hinaus soll es eine Value-Eigenschaft haben, die es gestattet, das Objekt einem anderen gleichartigen Objekt als Wert zuzuweisen (dafür ist es erforderlich, dass die Eigenschaften StartP und EndP zugänglich sind). 3. An Operationen soll das Objekt eine Konvertierungsoperation CLine3D unterstützen, die verschiedene Darstellungen in den eigenen Datentyp überführt. Außerdem soll es seinen Wert in einer geöffneten Binärdatei speichern (Save-Methode) und lesen (Load-Methode) können und natürlich die Linie, die es repräsentiert, drehen (Rot3D-Methode) und – als Projektion auf die xy-Ebene – zeichnen können (Draw). Darüber hinaus soll das Objekt den Komfort einer Clone-Methode bieten, die eine Referenz auf eine echte Kopie des Objekts liefert. Die Implementation der Klasse Line3D profitiert von dem konsequent objektorientierten Layout der Klasse Point3D. Aus rein prozeduraler Sicht wäre es natürlich genauso gut möglich gewesen, die Operationen Save, Load, Clone und Rot3D nur auf der Ebene von Line3D zu implementieren, dann würde sich aber die Frage stellen, wozu man die Klasse Point3D überhaupt braucht, schließlich könnte Line3D auch sechs Double-Elementvariablen verwenden ... Die objektorientierte Sicht bietet demgegenüber natürlich den Vorteil, dass die Implementation von Point3D auch für die Gestaltung anderer Klassen, etwa Triangle3D, zur Verfügung steht. '*********************************************************************** ' Klasse : Line3D ' Autor : 2000 Rudolf Huttary ' Beschreibung : Repräsentiert Linie im 3-dimensionalen Raum ' : Standardeigenschaft ist Value '*********************************************************************** Option Explicit Private mvarStartP As New Point3D Private mvarEndP As New Point3D ' Echte Zuweisung Public Property Let Value(New_Value As Line3D) mvarStartP = New_Value.StartP mvarEndP = New_Value.EndP ' Str = New_Value.Str ' Zuweisung wäre auch über Str möglich!! End Property Public Property Get Value() As Line3D Set Value = Me ' Referenz auf sich selbst liefern End Property Public Property Get StartP() As Point3D Set StartP = mvarStartP ' Referenz auf sich selbst liefern End Property
586
Line3D eine Linie aus zwei Punkten
Public Property Get EndP() As Point3D Set EndP = mvarEndP ' Referenz auf sich selbst liefern End Property
Klassen selbst definieren
' Initialisiert Objekt über Zeichenfolgendarstellung Public Property Let Str(s As String) ' Punkte aus Zeichenfolge einlesen If Left(s, 4) = "L(P(" And Len(s) >= 20 Then mvarStartP.Str = Mid(s, 3, InStr(4, s, "P(") – 5) mvarEndP.Str = Mid(s, InStr(4, s, "P(")) Else Err.Raise 1001, "Line3D", "Line3D.Str: Falsches Format" End If End Property ' Liefert Zeichenfolgendarstellung des Objekts Public Property Get Str() As String Str = "L(" & mvarStartP.Str & "," & mvarEndP.Str & ")" End Property ' Erzeugt eine Kopie des Objekts und liefert Referenz darauf Public Function Clone() As Line3D Set Clone = New Line3D Clone = Me End Function ' Initialisiert Objekt (verschiedene Datentypen zulässig) Public Function CLine3D(param1, ParamArray params()) As Line3D Select Case TypeName(param1) Case "String" ' Initialisierung mit Zeichenfolge Str = param1 Case "Point3D" ' Initialisierung durch anderen Punkt Set mvarStartP = param1 Set mvarEndP = params(0) Case Else ' Versuch: Initialisierung mit Koordinaten If UBound(params) >= 4 Then mvarStartP.CPoint3D param1, params(0), params(1) mvarEndP.CPoint3D params(2), params(3), params(4) Else Err.Raise 1002, "Line3D", "Line3D: zu wenig Parameter" End If End Select Set CLine3D = Me End Function ' Linie rotieren Public Sub Rot3D(r As Rotation3D) mvarStartP.Rot3D r mvarEndP.Rot3D r End Sub
' Reduktion auf Punkte ' Reduktion auf Punkte
587
Klassen selbst definieren
Klassen selbst definieren
' Linie in Objekt Target zeichnen Public Sub Draw(Target As Object, Optional Color As Long, _ Optional Radius As Single = 1) Target.Line (mvarStartP.x, mvarStartP.y)-(mvarEndP.x, mvarEndP.y), _ Color mvarStartP.Draw Target, Color, Radius mvarEndP.Draw Target, Color, Radius End Sub ' Linie in Datei FileNr speichern Public Sub Save(ByVal FileNr) mvarStartP.Save (FileNr) mvarEndP.Save (FileNr) ' Reduktion auf Punkte End Sub ' Linie aus Datei FileNr lesen Public Sub Load(ByVal FileNr) mvarStartP.Load (FileNr) ' Reduktion auf Punkte mvarEndP.Load (FileNr) End Sub
Der Implementation der Klasse Line3D liegt das gleiche Schema zugrunde wie der von Point3D. Auch hier ist Value als Standardeigenschaft vereinbart und spielt eine wichtige Rolle, wenn es darum geht, eine Kopie des Objekts zu erzeugen. Am New-Operator in der Deklaration der Elementvariablen ist zu erkennen, dass Line3D-Objekte je einen eigenen Satz an Point3D-Objekten besitzen. Das wäre vom Prinzip her nicht nötig gewesen, weil man eine Linie auch mit Verweisen auf gegebene Point3D-Objekte aufbauen kann, es erleichtert aber die Implementation und den Umgang mit Linienobjekten. In einem punktorientierten Ansatz, wo ein Liniensystem über einer bestehenden Punktmenge definiert wird, sollten sich Transformationen der Punktmenge auch auf das Liniensystem auswirken. Einer solchen Technik verschließt sich die Implementation jedoch nicht, da die für die typtolerante Initialisierung zuständige Methode CLine3D eine Set-Initialisierung der Elementvariablen mit bestehenden Punktobjekten ermöglicht. Interessant ist, wie CLine3D mit einer flexiblen Anzahl an Parametern sowie unterschiedlichen Typen zurechtkommt. Der erste Teil des Geheimnisses steckt im Zusatz ParamArray bei der Deklaration des zweiten Parameters. Public Function CLine3D(param1, ParamArray params()) As Line3D
Damit kann die Methode mit einer beliebigen Parameteranzahl größer 1 aufgerufen werden, wobei der erste Wert im obligatorischen Parameter param1 landet und alle weiteren optionalen Werte im dynamischen Array params. Zudem ist für alle Parameter implizit der allumfassende Typ Variant vereinbart. Für die Typanalyse leistet die TypeName-Funktion beste Dienste. Das für diesen Zweck eigentlich zuständige TypeOf-Schlüsselwort lässt sich nur für Objektdatentypen verwenden! Ansonsten ist die Implementation schon fast business as usual. Die Methoden Load, Save, Str und Rot3D sind allesamt nach dem gleichen Muster gestrickt: Sie delegieren den wesentlichen, wenn nicht gesamten Teil der Arbeit an die Klasse Point3D. Hier ein Beispiel: ' Linie in Datei FileNr speichern Public Sub Save(ByVal FileNr) mvarStartP.Save (FileNr) mvarEndP.Save (FileNr) ' Reduktion auf Punkte End Sub
588
Line3D eine Linie aus zwei Punkten
Draw zeichnet zunächst die Linie und dann die beiden Punkte. Die Reihenfolge liegt darin begründet, dass der Besitzer des Objekts die Punkte vielleicht ausgefüllt gezeichnet haben will, indem er die Eigenschaften FillColor und FillStyle für das Objekt Target geeignet festlegt.
Klassen selbst definieren
Public Sub Draw(Target As Object, Optional Color As Long, _ Optional Radius As Single = 1) Target.Line (mvarStartP.x, mvarStartP.y)-(mvarEndP.x, mvarEndP.y), _ Color mvarStartP.Draw Target, Color, Radius mvarEndP.Draw Target, Color, Radius End Sub
Der Testcode für die Klasse sieht etwa so aus: Private Sub TestLine3D() Dim p1 As New Point3D Dim p2 As New Point3D Dim l1 As Line3D Dim l2 As New Line3D Set l1 = l2.CLine3D("L(P(0, 0, 0),P(0,10,0))") ' Referenz zuweisen l2.CLine3D ("L(P(1, 1, 1),P(10,5,0))") ' Wert ändern Print l1.Str ' Ausgabe: "L(P(1, 1, 1),P(10,5,0))" Print l2.Str ' Ausgabe: "L(P(1, 1, 1),P(10,5,0))" ' Let l2 = l1.Clone ' Zuweisung einer Kopie von l1 an l1!!! Set l2 = l1.Clone ' Zuweisung einer Kopie l2.EndP.CPoint3D -1, -1, -1 Print l1.Str ' Ausgabe: "L(P(1, 1, 1),P(10,10,0))" Print l2.Str ' Ausgabe: "L(P(1, 1, 1),P(-1,-1,-1))" Scale (-3, 7)-(12, -2) l1.Draw Me, , 0.1 l2.Draw Me, , 0.1 Dim r As New Rotation3D r.RotXY 35 l2.Rot3D r l2.Draw Me, , 0.1 End Sub
Etwas speziell ist der auskommentierte Aufruf. Man möchte zunächst meinen, dass l2 dadurch eine Kopie von l1 zugewiesen wird, die ab dann von l1 nicht mehr abhängig ist. Das würde auch stimmen, wenn l2 nicht dummerweise als Objektvariable auf l1 verweisen würde. Es passiert hier also Folgendes: l1 generiert eine echte Kopie von sich selbst. Diese Kopie wird dem Wert von l2, also wiederum l1, zugewiesen. Dabei verliert l1 seinen »alten« Wert, erhält ihn aber von der Kopie zurück. Und l2 verweist als Objektvariable weiterhin auf l1.
Shape3D ein Drahtgitter aus Linien Die Spezifikation der Klasse Shape3D wurde ja bereits informal vorweggenommen. 1. Ein Drahtgitter der Klasse Shape3D besteht aus einer Ansammlung von Linien des Typs Line3D, die ihrerseits durch Punkte des Typs Point3D in einem gemeinsamen Koordinatensystem beschrieben werden. Die Repräsentation der Linien erfolgt in einem dynamischen Array des Typs Line3D.
589
Klassen selbst definieren
2. Das Objekt soll eine Value-Eigenschaft bieten, die einen Get/Let-Zugriff auf das Array ermöglicht (Let deshalb, weil Visual Basic dynamische Arrays grundsätzlich en bloc kopiert), mehr nicht. 3. An Methoden benötigt das Objekt eine Möglichkeit, eine bestehende Beschreibung zu löschen (Clear), eine Linie in eine bestehende Beschreibung einzufügen (Insert), sich darzustellen (Draw), sich in einer Datei zu speichern (Save), eine bestehende Repräsentation aus einer Datei zu laden (Load) und sich zu drehen (Rot3D).
Klassen selbst definieren
Hier die Implementation: '*********************************************************************** ' Klasse : Shape3D ' Autor : 2000 Rudolf Huttary ' Beschreibung : Repräsentiert Figur im 3-dimensionalen Raum ' : Keine Standardeigenschaft definiert '*********************************************************************** Option Explicit Const Shape3D = "Shape3D" ' Formatkennung für Dateioperationen ' Repräsentation Private Lines() As Line3D Public Property Get Value() As Line3D() Value = Lines End Property Public Property Let Value(vdata() As Line3D) Lines = vdata ' en bloc-Zuweisung End Property ' Fügt Kopie der Linie in Figur ein Public Sub Insert(ByVal Line3D As Line3D) ReDim Preserve Lines(UBound(Lines) + 1) Set Lines(UBound(Lines)) = Line3D.Clone End Sub ' Löscht Figur Public Sub Clear() ReDim Lines(0) As Line3D End Sub ' Rotiert Figur Public Sub Rot3D(r As Rotation3D) Dim l As Long For l = 1 To UBound(Lines) ' Auf einzelne Linien reduzieren Lines(l).Rot3D r Next End Sub ' Zeichnet Figur in Objekt Target Public Sub Draw(Target As Object, Optional Color As Long) Dim l As Long For l = 1 To UBound(Lines)
590
Line3 D eine Linie aus zwei Punkten
Lines(l).Draw Target, Color Next End Sub ' Initalisiert Array Private Sub Class_Initialize() ReDim Lines(0) As Line3D End Sub
Klassen selbst definieren
' Speichert Figur in bereits geöffneter Datei FileNr Public Function Save(FileNr As Integer) As Boolean Dim i As Long On Error GoTo Abbruch Put 1, , Shape3D ' Formatkennung Put 1, , UBound(Lines) ' Anzahl der Linien For i = 1 To UBound(Lines) Lines(i).Save (FileNr) ' Reduzieren auf Linie speichern Next Save = True Abbruch: End Function ' Lädt Figur aus bereits geöffneter Datei Public Function Load(FileNr As Integer) As Boolean Dim i As Long, count As Long Dim l As New Line3D ' Variable zu Laden On Error GoTo Abbruch ' Falls was schieft geht ' Formatkennung OK? If Input(Len(Shape3D), 1) <> Shape3D Then Exit Function Get 1, , count ' Anzahl der Linien in der Figur For i = 1 To count l.Load (FileNr) ' Reduzieren auf Linie lesen Insert l Next Load = True ' Erfolgreich gelesen Abbruch: End Function
Als zentrale Datenstruktur für die Speicherung der Line3D-Objekte fungiert das dynamische Array Lines. Um die Logik der Methoden Insert und Clear einfach zu halten, wird Lines vom Konstruktor Class-Initialize der Klasse sowie von Clear auf ein Element dimensioniert, das ansonsten keinem weiteren Zweck dient. Jeder Insert-Aufruf erweitert das Array um ein neues Element, wobei die aktuelle Obergrenze UBound(Lines) jeweils den Index liefert. Der Besitzer eines Shape3D-Objekts kann also davon ausgehen, dass die Methode eine echte Kopie des Line3D-Objekts erstellt und sich das Objekt ohne die Gefahr von Seiteneffekten weiterverwenden lässt. ' Fügt Kopie der Linie in Figur ein Public Sub Insert(ByVal Line3D As Line3D) ReDim Preserve Lines(UBound(Lines) + 1) Set Lines(UBound(Lines)) = Line3D.Clone End Sub
591
Klassen selbst definieren
Klassen selbst definieren
Wenig spannend ist der Code der Methoden Draw und Rot3D. Ihr Code durchläuft das Array und veranlasst jedes einzelne Line3D-Objekt, sich mit der gleichnamigen Methode zu zeichnen bzw. zu drehen. Nach dem gleichen Prinzip funktionieren auch die Methoden Save und Load, mit dem Zusatz, dass Save zuvor noch die Formatkennung »Shape3D« sowie die Anzahl der zu erwartenden Linienobjekte in die Datei schreibt. Const Shape3D = "Shape3D" ' Formatkennung für Dateioperationen ' Speichert Figur in bereits geöffneter Datei FileNr Public Function Save(FileNr As Integer) As Boolean Dim i As Long On Error GoTo Abbruch Put 1, , Shape3D ' Formatkennung Put 1, , UBound(Lines) ' Anzahl der Linien For i = 1 To UBound(Lines) Lines(i).Save (FileNr) ' Reduzieren auf Linie speichern Next Save = True Abbruch: End Function
Umgekehrt liest und analysiert die Methode Load als Erstes die Formatkennung Shape3D und ermittelt dann, sofern die Formatkennung korrekt ist, wie viele Line3D-Objekte eingelesen werden müssen. Den Rest erledigt dann eine Schleife, in der ein eigens für diesen Zweck instanziiertes Line3D-Objekt die Lesearbeit verrichtet. Da Insert wie bereits erwähnt eine echte Kopie seines Parameters anfertigt, kommt die Schleife mit einem einzigen Objekt aus. ' Lädt Figur aus bereits geöffneter Datei Public Function Load(FileNr As Integer) As Boolean Dim i As Long, count As Long Dim l As New Line3D ' Variable zum Laden On Error GoTo Abbruch ' Falls was schieft geht ' Formatkennung OK? If Input(Len(Shape3D), 1) <> Shape3D Then Exit Function Get 1, , count ' Anzahl der Linien in der Figur For i = 1 To count l.Load (FileNr) ' Reduzieren auf: Linie lesen Insert l Next Load = True ' Erfolgreich gelesen Abbruch: End Function
Animation3D Das Testformular Mit der Klasse Shape3D sind nun alle Voraussetzungen vorhanden, um ein Drahtgittermodell zu definieren und im dreidimensionalen Raum in Bewegung zu versetzen. Es fehlt nur noch ein halbwegs komfortabler Rahmen, um Tests durchzuführen. Das Projekt 3DAnimation enthält für diesen Zweck ein Testformular namens Animation3D, das neben dem bereits vorgestellten Testcode für die einzelnen Klassen mit dem Nötigsten ausgestattet ist, um eine eindrucksvolle visuelle Demonstration der Objekte zu ermöglichen. Wenn Sie den Testcode ausführen wollen, müssen Sie nur die Kommentare in der Load-Routine entsprechend ändern – es ist alles vorbereitet. Ansonsten verzweigt die Anwendung gleich nach dem Start automatisch zur Würfelanimation, die ein Zeitgeber-Steuerelement vorantreibt. Die Drehwinkel zwischen je zwei Animati-
592
Line3D eine Linie aus zwei Punkten
onsphasen lassen sich über die drei am oberen Rand des Client-Bereichs befindlichen Schieberegler-Steuerelemente vorgeben – zur Auswahl stehen Winkel zwischen 0 und 10 Grad. Im Menü FIGUR stehen drei weitere Drahtgittermodelle im Angebot: eine Pyramide, eine Linie und eine zufällig erzeugte zusammenhängende Linienschar. Darüber hinaus lässt sich die Animation per Mausklick anhalten und wieder starten.
Klassen selbst definieren
Die Figuren »Würfel« und »Knäuel« Hier der Code: '*********************************************************************** ' Formularmodul : Animation3D ' Autor : 2000 Rudolf Huttary ' Beschreibung : Demoprogramm für die Klassen Shape3D, Line3D, ' : Rotation3D und Point3D '*********************************************************************** Option Explicit
593
Klassen selbst definieren
Dim Dim Dim Dim
l As New Line3D RotObj As New Rotation3D ShapeObj As New Shape3D o As Object
Klassen selbst definieren
Private Sub Form_Click() mnuTimer_Click End Sub Private Sub Form_Load() Dim c As Control ShowControls False Width = Width – ScaleWidth + 5000 Height = Height – ScaleHeight + 5000 'TestPoint3D 'TestLine3D StartAnimation End Sub
' Testcode für Klasse Point3D ' Testcode für Klasse Line3D ' Testcode für Drahtgittermodell
Private Sub ShowControls(State As Boolean) Slider1.Visible = State Slider2.Visible = State Slider3.Visible = State Label1.Visible = State Label2.Visible = State Label3.Visible = State End Sub Private Sub TestPoint3D() Dim p As Point3D Dim p1 As New Point3D Dim p2 As New Point3D Dim p3 As New Point3D Dim p4 As New Point3D Dim r As New Rotation3D ' Grundfunktionen p1.CPoint3D 1, 0, 1 Print p1.Str p2 = p1 Print p2.Str Set p = p3.CPoint3D(p2) Print p3.Str p4.CPoint3D "P(2,0,0)" Print p4.Str p1.Str = p4.Str ' Drehen r.RotXY 90 Print p4.Rot3D(r).Str
594
' Testroutine für Point3D, Rotation3D
' Initialisieren ' Ausgabe: P(1,0,1) ' Kopie erstellen ' Ausgabe: P(1,0,1) ' Initialisierung mit Objekt ' Ausgabe: P(1,0,1) ' Initialisierung mit Zeichenfolge ' Ausgabe: P(2,0,0) ' Wertzuweisung über Str (!) ' 90° auf xy-Ebene drehen ' Rotieren, Ausgabe: P(0,2,0)
Line3D eine Linie aus zwei Punkten
' Rotation zurück setzen ' 90° auf yz-Ebene drehen ' Rotieren, Ausgabe: P(0,0,-2) ' Rotation zurück setzen ' 90° auf xz-Ebene drehen ' Rotieren, Ausgabe: P(-2,0,0) ' 90° auf xy-Ebene hinzu ' 90° auf xz-Ebene hinzu ' Rotieren, Ausgabe: P(2,0,0) ' funktioniert !!!! ' Rotieren, Ausgabe: P(-2,0,0)
Klassen selbst definieren
r.Init r.RotYZ 90 Print p4.Rot3D(r).Str r.Init r.RotXZ 90 Print p4.Rot3D(r).Str r.RotXY 90 r.RotXZ 90 Print p4.Rot3D(r).Str r.Rot3D p4.x, p4.y, p4.z Print p4.Rot3D(r).Str Scale (-3, 3)-(3, -3) p1.Draw Me, , 0.1 p3.Draw Me, , 0.2 p4.Draw Me, , 0.3 End Sub Private Sub TestLine3D() Dim p1 As New Point3D Dim p2 As New Point3D Dim l1 As Line3D Dim l2 As New Line3D
Set l1 = l2.CLine3D("L(P(0, 0, 0),P(0,10,0))") ' Referenz zuweisen l2.CLine3D ("L(P(1, 1, 1),P(10,5,0))") ' Wert ändern Print l1.Str ' Ausgabe: "L(P(1, 1, 1),P(10,5,0))" Print l2.Str ' Ausgabe: "L(P(1, 1, 1),P(10,5,0))" ' Let l2 = l1.Clone ' Zuweisung einer Kopie von l1 an l1!!! Set l2 = l1.Clone ' Zuweisung einer Kopie l2.EndP.CPoint3D -1, -1, -1 Print l1.Str ' Ausgabe: "L(P(1, 1, 1),P(10,10,0))" Print l2.Str ' Ausgabe: "L(P(1, 1, 1),P(-1,-1,-1))" Scale (-3, 7)-(12, -2) l1.Draw Me, , 0.1 l2.Draw Me, , 0.1 Dim r As New Rotation3D r.RotXY 35 l2.Rot3D r l2.Draw Me, , 0.1 End Sub Private Sub StartAnimation() Dim c As Control Scale (-20, 20)-(20, -20) Slider1 = 1 ' XY-Rotation Slider2 = 2 ' XZ-Rotation Slider3 = 3 ' YZ-Rotation ShowControls True FillStyle = vbSolid
595
Klassen selbst definieren
Klassen selbst definieren
FillColor = BackColor mnuCube_Click ' Würfel anzeigen Timer1.Interval = 80 End Sub ' Figur aus Datei laden Private Sub mnuOpen_Click() SetTimer False If Not o Is Nothing Then o.Draw Me, BackColor ShapeObj.Clear Open App.Path + "\Shape3D.s3d" For Binary As 1 ShapeObj.Load 1 Close 1 Set o = ShapeObj SetTimer True End Sub ' Figur in Datei speichern Private Sub mnuSpeichern_Click() Open App.Path + "\Shape3D.s3d" For Binary As 1 ShapeObj.Save 1 Close 1 End Sub Private Sub mnuQuitt_Click() Unload Me End Sub Private Sub mnuTimer_Click() SetTimer (Not mnuTimer.Checked) End Sub Private Sub SetTimer(OnOff As Boolean) mnuTimer.Checked = OnOff Timer1.Enabled = OnOff End Sub ' Einzelne Linie Private Sub mnuLine_Click() SetTimer False If Not o Is Nothing Then o.Draw Me, BackColor ShapeObj.Clear ShapeObj.Insert l.CLine3D(0, 0, 0, 10, 10, 0) Set o = ShapeObj o.Draw Me SetTimer True End Sub ' Würfel Private Sub mnuCube_Click() SetTimer False If Not o Is Nothing Then o.Draw Me, BackColor
596
Line3D eine Linie aus zwei Punkten
ShapeObj.Clear ShapeObj.Insert ShapeObj.Insert ShapeObj.Insert ShapeObj.Insert
l.CLine3D(-10, 10, -10, 10, 10, -10) l.CLine3D(10, 10, -10, 10, -10, -10) l.CLine3D(10, -10, -10, -10, -10, -10) l.CLine3D(-10, -10, -10, -10, 10, -10)
ShapeObj.Insert ShapeObj.Insert ShapeObj.Insert ShapeObj.Insert
l.CLine3D(-10, 10, 10, 10, 10, 10) l.CLine3D(10, 10, 10, 10, -10, 10) l.CLine3D(10, -10, 10, -10, -10, 10) l.CLine3D(-10, -10, 10, -10, 10, 10)
Klassen selbst definieren
ShapeObj.Insert l.CLine3D(-10, 10, -10, -10, 10, 10) ShapeObj.Insert l.CLine3D(10, 10, -10, 10, 10, 10) ShapeObj.Insert l.CLine3D(10, -10, -10, 10, -10, 10) ShapeObj.Insert l.CLine3D(-10, -10, -10, -10, -10, 10) Set o = ShapeObj o.Draw Me SetTimer True End Sub ' Zufällig erzeugtes, zusammenhängendes Linengewirr Private Sub mnuRandom_Click() Dim i As Long Dim x As Double, y As Double, z As Double Dim p As New Point3D Dim l As New Line3D SetTimer False If Not o Is Nothing Then o.Draw Me, BackColor ShapeObj.Clear Randomize Timer x = ScaleHeight * Rnd – ScaleHeight / 2 y = ScaleHeight * Rnd – ScaleHeight / 2 z = ScaleHeight * Rnd – ScaleHeight / 2 For i = 0 To 10 p.CPoint3D x, y, z x = ScaleHeight * Rnd – ScaleHeight / 2 y = ScaleHeight * Rnd – ScaleHeight / 2 z = ScaleHeight * Rnd – ScaleHeight / 2 ShapeObj.Insert l.CLine3D(p.x, p.y, p.z, x, y, z) Next i Set o = ShapeObj o.Draw Me SetTimer True End Sub ' Pyramide Private Sub mnuPyramide_Click() SetTimer False If Not o Is Nothing Then o.Draw Me, BackColor ShapeObj.Clear ShapeObj.Insert l.CLine3D(-10, 10, -10, 10, 10, -10)
597
Klassen selbst definieren
Klassen selbst definieren
ShapeObj.Insert l.CLine3D(10, 10, -10, 10, -10, -10) ShapeObj.Insert l.CLine3D(10, -10, -10, -10, -10, -10) ShapeObj.Insert l.CLine3D(-10, -10, -10, -10, 10, -10) ShapeObj.Insert l.CLine3D(-10, 10, -10, 0, 0, 10) ShapeObj.Insert l.CLine3D(10, 10, -10, 0, 0, 10) ShapeObj.Insert l.CLine3D(10, -10, -10, 0, 0, 10) ShapeObj.Insert l.CLine3D(-10, -10, -10, 0, 0, 10) Dim shapeObj1 As New Shape3D shapeObj1.Value = ShapeObj.Value Set o = shapeObj1 o.Draw Me SetTimer True End Sub Private Sub Slider1_Change() RotObj.Init RotObj.RotXY Slider1 RotObj.RotXZ Slider2 RotObj.RotYZ Slider3 End Sub Private Sub Slider2_Change() RotObj.Init RotObj.RotXY Slider1 RotObj.RotXZ Slider2 RotObj.RotYZ Slider3 End Sub Private Sub Slider3_Change() RotObj.Init RotObj.RotXY Slider1 RotObj.RotXZ Slider2 RotObj.RotYZ Slider3 End Sub Private Sub Timer1_Timer() If Not o Is Nothing Then o.Draw Me, BackColor o.Rot3D RotObj o.Draw Me End If End Sub
Wie man sieht, enthält der Code des Formularmoduls außer den schon zuvor vorgestellten Testroutinen für die einzelnen Klassen keine besonderen Finessen. Zum Testen der Methoden Save und Load des zentralen Shape3D-Objekts enthält das Menü DATEI die Befehle LADEN und SPEICHERN – eine Dateiauswahl ist nicht vorgesehen, ließe sich aber problemlos nachrüsten.
598
ActiveX- Steuerelemente und Benutzersteuerelemente
Zu verbessern gibt es noch einiges. Wer beispielsweise genau hinsieht, wird bemerken, dass das Programm zuweilen im Hintergrund gelegene Punkte über im Vordergrund gelegene Linien zeichnet. In diesem Moment kann die Perspektive des Bilds optisch »kippen«, Sie kennen das. Abhilfe mit einfachen Mitteln für dieses bekannte »Problem der verdeckten Linien« dürfte es nicht geben. Es führt unweigerlich in die Fänge des ray tracing.
ActiveX- Steuerelemente und Benutzersteuerelemente
LongTimer der Timer mit Ausdauer So nützlich das Zeitgeber-Steuerelement (Timer) von Visual Basic ist, »größere Sprünge« lassen sich damit im wahrsten Sinne des Wortes nicht machen – ebenso wenig wie ganz kleine (doch das ist ein anderes Thema). Zufriedenstellend funktioniert der Zeitgeber nur, wenn Intervalle zwischen 20 ms und 65 Sekunden gefragt sind.
Anwendung Was aber, wenn man ein Ereignis auf Minuten, Stunden, Tage oder Monate vorausplanen möchte, und das auf die Sekunde genau? Die Lösung an sich ist naheliegend, die Umsetzung in ein für die Zukunft allzeit bereit stehendes Benutzersteuerelement mit dem Namen LongTimer eher solides »Handwerk«. Beginnen wir mit der Spezifikation. Das Steuerelement soll: 1. Ein registriertes ActiveX-Steuerelement werden, das fortan allen Anwendungen als Timer zur Verfügung steht. 2. Wie das Zeitgeber-Steuerelement keine visuelle Darstellung haben. 3. Wahlweise einen Sekundenwert oder einen Datums-/Zeitwert akzeptieren. Die entsprechenden Eigenschaften seien LongInterval und TriggerTime. LongInterval soll zur Entwurfszeit und zur Laufzeit gelesen und geschrieben werden können, TriggerTime nur zur Laufzeit. 4. Bei Setzen eines Sekundenwerts LongInterval ein Timer-Ereignis wiederholt signalisieren, bis ein Sekundenwert von 0 gesetzt wird. 5. Bei Angabe eines Datums-/Zeitwerts ein Timer-Ereignis einmalig signalisieren (sofern der Wert in der Zukunft liegt). Die schreibgeschützte Eigenschaft OneShot soll anzeigen, ob das Ereignis periodisch oder nur einmalig signalisiert wird. 6. Bei Änderung des Sekundenwerts oder des Datums-/Zeitwerts vor Ablauf des aktuellen Intervalls ein neues Intervall beginnen.
599
ActiveX- Steuerelemente und Benutzersteuerelemente
Das Thema Benutzersteuerelemente hat in Visual Basic bereits eine lange Tradition und war seit jeher eines der stärksten Argumente, auf Visual Basic einzuschwenken. Wer mit den Standardsteuerelementen von Visual Basic programmieren kann, hat alles begriffen und wird weder mit dem Umgang der mitgelieferten ActiveX-Steuerelemente noch mit benutzerdefinierten Steuerelementen, die letztlich nichts anderes als ActiveX-Steuerelemente sind, irgendwelche Schwierigkeiten haben, die sich nicht auf die fehlerhafte Implementation oder Dokumentation des Steuerelements zurückführen lassen. Die Programmierung eigener Benutzersteuerelemente ist da schon eine andere Geschichte. Sie ist eine Fortsetzung der Programmierung eigener Klassen »mit anderen Mitteln«. Die Mittel sind in diesem Fall: das allen Benutzersteuerelementen von Visual Basic automatisch zugrunde gelegte UserControl-Objekt sowie das zugehörige Fenster für die sichtbare Repräsentation des Steuerelements etwa in einem Formular oder einem anderen ActiveX-Steuerelement. Dieser Abschnitt stellt Ihnen den Werdegang zweier Benutzersteuerelemente vor, von denen das eine zur Laufzeit keine sichtbare Repräsentation hat, weil es das Zeitgeber-Steuerelement nachbessert und das andere eine Funktionserweiterung des bestehenden Standardsteuerelements Textfeld bis ins letzte Detail verkörpert.
ActiveX- Steuerelemente und Benutzersteuerelemente
Umsetzung
ActiveX- Steuerelemente und Benutzersteuerelemente
Die programmtechnische Umsetzung dieser Spezifikation in das Projekt LongTimer erfordert zunächst einmal die strategische Überlegung, das neue Steuerelement auf dem Rücken des alten Zeitgeber-Steuerelements aufzubauen. Intervalle kleiner als 60 Sekunden lassen sich dann gleich direkt an das Zeitgeber-Steuerelement delegieren. Größere Intervalle müssen dagegen in Minutenintervalle plus ein Restintervall gestückelt werden. Die Logik ist somit einfach, so dass sich die vorliegende Beschreibung auf die Feinheiten einer sauberen Implementation konzentrieren kann. Beginnen Sie wie folgt: 1. Legen Sie eine neues Projekt des Typs »Standard-EXE« an, das als Testanwendung für das Steuerelement fungieren wird. 2. Fügen Sie über den Menübefehl DATEI/PROJEKT HINZUFÜGEN ein neues Projekt des Typs »ActiveX-Steuerelement« hinzu. Legen Sie im Eigenschaftsfenster den Namen LongTimer für das Steuerelement fest, setzen Sie die Eigenschaft InvisibleAtRuntime auf True und weisen Sie dem Steuerelement eine passende Bitmap als Picture- und als ToolboxBitmap-Eigenschaft zu. 3. Schließen Sie das Entwurfsfenster des Benutzersteuerelements. In der Werkzeugsammlung müsste nun ein neues Steuerelement mit dem gewählten Bitmap als Symbol zu finden sein. 4. Öffnen Sie die Entwurfsansicht des Steuerelements erneut und platzieren Sie ein Zeitgebersteuerelement aus der Werkzeugsammlung darauf. Passen Sie gegebenenfalls noch die Größe des Entwurfsbereichs an die anzeigte Bitmap an – was jedoch nichts weiter zur Sache beiträgt.
Das Benutzersteuerelement in der Entwurfsansicht und in der Werkzeugleiste Damit haben Sie den passenden Rahmen für die weitere Entwicklung: Das Steuerelement ist zwar zur Laufzeit unsichtbar, zur Entwurfszeit zeigt es aber die Picture-Eigenschaft an, so dass Sie es auf dem Formular der Testanwendung platzieren können – und später auch wiederfinden. Soviel zu Punkt 2 der Spezifikation. Die anderen Punkte betreffen die Implementation der Eigenschaften und die Signalisierung des Ereignisses. Beginnen wir mit OneShot. Diese Eigenschaft wird beim Setzen der anderen beiden Eigenschaften sowie bei der objekteigenen Timer-Behandlung gepflegt. Da sie zur Laufzeit und zur Entwurfszeit schreibgeschützt sein soll, reicht es, nur eine Property Get-Prozedur bereitzustellen. Damit taucht sie erst gar nicht im Eigenschaftsfenster des Benutzersteuerelements auf. Private m_OneShot As Boolean ' Nur-Lesen Eigenschaft Public Property Get OneShot() As Boolean OneShot = m_OneShot End Property
Die Eigenschaft LongInterval setzt die Sekunden für den periodischen Timer und erlaubt jede Form des Zugriffs. Private m_LongInterval As Long Private m_TriggerTime As Date
600
LongTimer der Timer mit Ausdauer
Public Property Get LongInterval() As Long LongInterval = m_LongInterval End Property
Die Set-Prozedur pflegt die drei Elementvariablen, mit denen das Objekt die Zustände der Ereignisse intern repräsentiert, und die private SetTimer-Methode aktiviert bzw. deaktiviert das Zeitgeber-Steuerelement Timer1, das die Zeitbasis liefert. Sie setzt das Intervall von Timer1 auf die gewünschte Anzahl von Sekunden oder auf eine Minute, wenn das LongTimer-Intervall eine Minute oder länger dauern soll. Private Sub SetTimer(Diff As If Diff <= 0 Then Timer1.Interval = 0 m_OneShot = False ElseIf Diff < 60 Then Timer1.Interval = Diff * Else Timer1.Interval = 60000 End If End Sub
Long) ' Timer stoppen? ' Standardwert setzen ' Weniger als 1 Minute? 1000 ' Großer Zeitraum
Diffiziler wird die Angelegenheit bei TriggerTime. Da die Eigenschaft zur Entwurfszeit den Schreibzugriff ausschließen soll, benötigt sie eine Let-Prozedur, die unterscheiden kann, ob ihr Aufruf zur Laufzeit oder zur Entwurfszeit des Containers erfolgt ist. Handhabe dafür bietet die Ambient-Eigenschaft des untergelegten UserControl-Objekts. Sie enthält eine Referenz auf ein AmbientProperties-Objekt, dessen Aufgabe darin besteht, die Umgebungseigenschaften eines Containers (meist: Formulars) zu repräsentieren. Dieses Objekt, das wie eine Hülle angelegt ist, simuliert so etwas wie ein Containerobjekt, indem es das Benutzersteuerelement mit Standardwerten für bestimmte Eigenschaften (Font, BackColor usw.) versorgt, die dieses zur Laufzeit bei seinem Container vorfindet. Insbesondere liefert es in der Eigenschaft UserMode auch die Information, ob die aktuelle Instanz des Benutzersteuerelements für die Entwurfsansicht des Containers angelegt wurde (False) oder zu dessen Laufzeit (True) – was natürlich nicht ausschließt, dass die Instanz dieses Containers wiederum für die Entwurfsansicht eines weiteren Containers angelegt wurde. Wen das verwirrt, dem sei ins Gedächtnis zurückgerufen, dass die Entwicklungsumgebung von Visual Basic für jedes Benutzersteuerelement eine Entwurfsinstanz anlegt, wenn dieses von der Werkzeugsammlung aus im Entwurfsbereich eines Formulars oder eines anderen Benutzersteuerelements platziert wird. Diese Entwurfsinstanz wird beim Start des Formulars wieder abgebaut und durch eine Laufzeitinstanz ersetzt, wobei natürlich das AmbientProperties-Objekt die Änderung der Ausführungsumgebung reflektiert. Um nun einen Schreiboder Lesezugriff zur Entwurfs- oder Laufzeit zu verweigern, kann ein Steuerelement die Eigenschaft UserMode auswerten (genau das macht übrigens auch die Implementation der Eigenschaft InvisibleAtRuntime) und gegebenenfalls einen Laufzeitfehler signalisieren. In Visual Basic sind dafür die folgenden Fehlernummern vorgesehen:
601
ActiveX- Steuerelemente und Benutzersteuerelemente
Public Property Let LongInterval(NewInterval As Long) m_LongInterval = NewInterval m_TriggerTime = DateAdd("s", NewInterval, Now) SetTimer NewInterval m_OneShot = False ' Timer-Ereignis periodisch End Property
ActiveX- Steuerelemente und Benutzersteuerelemente
Fehlernummer
Fehlertext
382 (Laufzeit)
Set wird zur Laufzeit nicht unterstützt
383 (Entwurfszeit)
Set wird nicht unterstützt (schreibgeschützte Eigenschaft)
393 (Laufzeit)
Get wird zur Laufzeit nicht unterstützt
394 (Entwurfszeit)
Get wird nicht unterstützt (Eigenschaft kann nur gesetzt werden)
ActiveX- Steuerelemente und Benutzersteuerelemente
Die Implementation der TriggerTime-Eigenschaft sieht damit so aus: Public Property Get TriggerTime() As Date TriggerTime = m_TriggerTime End Property Public Property Let TriggerTime(NewTriggerTime As Date) If Not Ambient.UserMode Then Err.Raise 382 ' Beim Entwurf nur lesen m_TriggerTime = NewTriggerTime SetTimer DateDiff("s", Now, m_TriggerTime) m_OneShot = True ' Timer-Ereignis einmalig End Property
Damit wären die Eigenschaften erst einmal unter Dach und Fach. Bleibt noch zu klären, wie die Implementation des Zeitintervalls an sich aussieht. SetTimer setzt das Intervall des als Basis verwendeten originären Zeitgeber-Steuerelements auf maximal 60 Sekunden. Längere Zeitintervalle müssen also minutenweise gestückelt werden. Da das Timer-Ereignis von der tatsächlichen Intervalllänge her nicht gerade sehr zuverlässig ist, bringt es Vorteile, die Systemzeit zur Berechnung der restlichen Sekunden heranzuziehen. Verbleibt weniger als eine Minute, kann Timer1 direkt auf die Restzeit programmiert werden. Ist die Restzeit kleiner gleich 0 geworden, kann das Steuerelement seinerseits ein Timer-Ereignis signalisieren und in Abhängigkeit von m_OneShot das Intervall entweder auffrischen oder es bei diesem einen Ereignis belassen: Event Timer()
' wird vom Benutzersteuerelement signalisiert
Private Sub Timer1_Timer() Dim Diff As Long Diff = DateDiff("s", Now, m_TriggerTime) If Diff < 60 Then ' Weniger als 1 Minute? If Diff <= 0 Then ' Schon um? RaiseEvent Timer If m_OneShot Then ' Timer neu setzen? Timer1.Interval = 0 m_OneShot = False ' Standardwert setzen Else LongInterval = m_LongInterval ' Neu setzen End If Else Timer1.Interval = 1000 * Diff ' Timer auf restliche Sekunden End If End If End Sub
602
LongTimer der Timer mit Ausdauer
Das war nicht nur das Grobe, sondern auch bereits die eine oder andere Feinheit. Den letzten Schliff erhält das Steuerelement, wenn sich LongInterval nun auch noch zur Entwurfszeit setzen lässt. Vom Prinzip her ist es bereits jetzt schon möglich, die Eigenschaft zu setzen, und die Entwurfsinstanz fängt dann auch brav das Ticken an, was sich ganz einfach durch Ergänzen einer Beep-Anweisung in Timer1_Timer nachweisen lässt. (Setzen Sie dazu das Intervall auf eine oder zwei Sekunden.) Um der Entwurfsinstanz das Ticken auszutreiben, sollten Sie in SetTimer die folgende erste Zeile ergänzen: If Not Ambient.Usermode Then Exit Sub
Function Objekt.ReadProperty(Name As String[, Default]]) As Variant Sub Objekt.WriteProperty(Name As String, Value[, Default])
Für Name geben Sie am besten (jedoch nicht zwingend) den Bezeichner der Eigenschaft als Zeichenfolge an und für Value ihren Wert. Den optionalen Parameter Default sollten Sie nach Möglichkeit mit einem Wert versorgen, der den Standardwert der Eigenschaft darstellt. Falls die Werte von Value und Default gleich sind, spart sich WriteProperty so nämlich den Eintrag, und ReadProperty liefert den Wert von Default, wenn der über Name spezifizierte Eintrag nicht existiert. Da die Eigenschaft LongInterval zur Entwurfszeit gesetzt werden kann, sollte ihr Wert der Laufzeitinstanz zugänglich gemacht werden. Das Setzen der anderen Eigenschaften kann die Set-Prozedur von LongInterval besorgen. Hier der restliche Code: Private Sub UserControl_ReadProperties(PropBag As PropertyBag) LongInterval = PropBag.ReadProperty("LongInterval", 0) End Sub Private Sub UserControl_WriteProperties(PropBag As PropertyBag) PropBag.WriteProperty "LongInterval", m_LongInterval, 0 End Sub
Das Steuerelement veröffentlichen Im letzten Schritt können Sie das fertige Steuerelement nun noch veröffentlichen, um es fortan lokal auf dem System oder netzwerkweit in der Komponentenbibliothek zur Verfügung zu haben. Klicken Sie dazu im Kontextmenü des Projekts für das Benutzersteuerelement oder im Menü BEARBEITEN auf den Befehl VERÖFFENTLICHEN und wählen Sie im Untermenü den (vom Namen her wirklich verhunzten) Eintrag ERSTELLUNGSAUSGABEN. Damit teilen Sie dem Visual Component Manager (VCM) Ihres Systems mit, dass Sie eine kompilierte Fassung des Benutzersteuerelements als Komponente registrieren wollen, um sie anderen Anwendungen zur Verfügung zu
603
ActiveX- Steuerelemente und Benutzersteuerelemente
Da die Entwurfsinstanz, wie bereits ausgeführt, bei Instanziierung des Containers vernichtet und durch eine Laufzeitinstanz ersetzt wird, muss diese eine Möglichkeit haben, irgendwie an den Wert von LongInterval zu kommen. Genau für diesen Zweck signalisiert das UserControlObjekt unmittelbar vor dem Terminate-Ereignis das WriteProperties-Ereignis und unmittelbar nach dem Initialize-Ereignis das ReadProperties-Ereignis. Beide Ereignisse stellen über ihren Parameter ein und dasselbe PropertyBag-Objekt bereit, das die besondere Eigenschaft hat, persistent zu sein (d.h.: es behält seinen Wert von Instanz zu Instanz bei), und dem Benutzersteuerelement wie eine Ressource zugeordnet ist. Das PropertyBag-Objekt kann wie eine »Tasche« beliebig viele Einträge speichern, die eine Zuordnung zwischen einer Zeichenfolge und einem beliebigen Wert darstellen, und diese Einträge in der nächsten Instanz des Benutzersteuerelements wieder hervorzaubern. Die Methoden für das Ein- und Auspacken haben die Deklarationen:
ActiveX- Steuerelemente und Benutzersteuerelemente
ActiveX- Steuerelemente und Benutzersteuerelemente
stellen. Der VCM kompiliert den Code zuerst in eine OCX-Datei und startet dann den Veröffentlichungs-Assistent. (Wenn Sie Ihre Komponenten in einem speziellen Verzeichnis sammeln wollen, sollten Sie die Übersetzung manuell über das Menü DATEI unter Angabe des gewünschten Pfads vornehmen.) Im zweiten Dialog legen Sie den öffentlichen Namen des Steuerelements fest sowie eine Datenbank, in der die Komponente gespeichert wird. Wählen Sie in der Local Database den Ordner Visual Basic\Templates\User Controls und folgen Sie den Anweisungen des Assistenten bis zur Fertigstellung. Wenn Sie nun die Komponentenbibliothek über PROJEKT/ KOMPONENTEN öffnen, sehen Sie das neue ActiveX-Steuerelement.
Das Benutzersteuerelement ist nun registriertes Ac tiveX- Steuerelement
Um die Registrierung des Steuerelements wieder loszuwerden oder es an einen anderen Speicherort zu verfrachten, umzubenennen usw. können Sie den VCM auch direkt über Ansicht/ Visual Component Manager starten und in einem im Stile des Explorers gehaltenen Fenster die gewünschten Änderungen durchführen. Die Bedienung ist recht intuitiv. Hier noch einmal der vollständige Code: '*********************************************************************** ' Benutzersteuerelement: LongTimer ' Autor : 2000 Rudolf Huttary ' Schnittstelle : LongInterval ' auf periodische Intervallzeit in Sek. setzen ' Wenn 0, dann Timer aus. ' TriggerTime ' auf Zeit(/Datum) setzen, an der ein einzelnes ' Timer-Ereignis auftreten soll '*********************************************************************** Option Explicit Event Timer()
' wird vom Benutzersteuerelement signalisiert
Private m_LongInterval As Long Private m_TriggerTime As Date
604
LongTimer der Timer mit Ausdauer
Private m_OneShot As Boolean ' Nur-Lesen Eigenschaft Public Property Get OneShot() As Boolean OneShot = m_OneShot End Property Public Property Get LongInterval() As Long LongInterval = m_LongInterval End Property
ActiveX- Steuerelemente und Benutzersteuerelemente
Public Property Let LongInterval(NewInterval As Long) m_LongInterval = NewInterval m_TriggerTime = DateAdd("s", NewInterval, Now) SetTimer NewInterval m_OneShot = False ' Timer-Ereignis periodisch End Property Public Property Get TriggerTime() As Date TriggerTime = m_TriggerTime End Property Public Property Let TriggerTime(NewTriggerTime As Date) If Not Ambient.UserMode Then Err.Raise 382 ' Beim Entwurf nur lesen m_TriggerTime = NewTriggerTime SetTimer DateDiff("s", Now, m_TriggerTime) m_OneShot = True ' Timer-Ereignis einmalig End Property Private Sub SetTimer(Diff As Long) ' If Not Ambient.UserMode Then Exit Sub If Diff <= 0 Then ' Timer stoppen? Timer1.Interval = 0 m_OneShot = False ' Standardwert setzen ElseIf Diff < 60 Then ' Weniger als 1 Minute? Timer1.Interval = Diff * 1000 Else Timer1.Interval = 60000 ' Großer Zeitraum End If End Sub Private Sub Timer1_Timer() Dim Diff As Long Beep Diff = DateDiff("s", Now, m_TriggerTime) If Diff < 60 Then ' Weniger als 1 Minute? If Diff <= 0 Then ' Schon um? RaiseEvent Timer If m_OneShot Then ' Timer neu setzen? Timer1.Interval = 0 m_OneShot = False ' Standardwert setzen
605
ActiveX- Steuerelemente und Benutzersteuerelemente
ActiveX- Steuerelemente und Benutzersteuerelemente
Else LongInterval = m_LongInterval ' Neu setzen End If Else Timer1.Interval = 1000 * Diff ' Timer auf restliche Sekunden End If End If End Sub Private Sub UserControl_ReadProperties(PropBag As PropertyBag) LongInterval = PropBag.ReadProperty("LongInterval", 0) End Sub Private Sub UserControl_WriteProperties(PropBag As PropertyBag) PropBag.WriteProperty "LongInterval", m_LongInterval, 0 End Sub
Transparenz und Drag&Drop Die Überlagerung mehrerer Bilder mit durchscheinendem Hintergrund war schon immer eine delikate Aufgabe und fällt seit jeher in den Bereich der Animationsprogrammierung. Wer sich nicht auf Pixel-Krämerei einlassen und das Problem von der Wurzel her anpacken will oder auf eine entsprechende Bibliothek zurückgreifen kann, ist darauf angewiesen, mit dem zurechtzukommen, was Visual Basic von sich aus bietet. Das ist zwar nicht gerade viel, reicht aber beispielsweise für die Programmierung einfacher Brettspiele mit Figuren, Puzzles und Ähnlichem. Die Eigenschaft Picture eines Formularobjekts taugt dazu, ein Hintergrundbild anzuzeigen. Vor einem solchen Hintergrundbild gegebenenfalls weitere Bilder als Vordergrundbilder einer Szene einzublenden und/oder zu animieren, ist mit den beiden Standardsteuerelementen Image und PictureBox zwar möglich, ein Blumentopf lässt sich damit aber nicht gerade gewinnen, da diese partout darauf bestehen, ihre Bilder als Rechtecke zu zeichnen. Häufig ist aber gerade eine unregelmäßige Form mit durchsichtigen Bereichen erwünscht. Ist der Hintergrund einfarbig, gelingt zwar die pseudotransparente Darstellung, da alle Bereiche mit Hintergrundfarbe in einem über den Hintergrund gezeichneten Bild ohne Rand transparent wirken, doch bereits bei Verwendung eines Hintergrundbildes oder bei Überlagerung mehrerer Vordergrundbilder führt diese Methode nicht mehr zum Ziel. Wie also lässt sich die Ausgabe von Bildern mit transparenten Bereichen generell bewerkstelligen? Ein Weg führt über die MaskColor-Eigenschaft des ImageList-Steuerelements (zu finden in der Komponente mscomctl.ocx) in Kombination mit der Overlay-Methode. Dieser Ansatz hat aber den Nachteil, dass die Methode darauf besteht, beide an der Operation beteiligten Bilder auf die gleiche Größe zu skalieren. Mithin eignet er sich nur für die Kombination von Bildern, die als Vordergrundbilder in einer Szene eingesetzt werden. Bei vielen Problemstellungen führt aber ein anderer Ansatz in befriedigender Weise zum Ziel: die Definition eines Benutzersteuerelements. Da das Benutzersteuerelementmodul auf ein UserControl-Objekt aufsetzt, das die notwendigen Eigenschaften für eine transparente Darstellung über einem beliebigen Hintergrund bereits mitbringt, stellt sich nur noch die Frage der Realisierung und welche »Kröten es dabei zu schlucken« gilt. Die programmtechnische Umsetzung ist geradezu simpel: 1. Fügen Sie in das Projekt ein neues Benutzersteuerelementmodul Figur.ctl ein und sorgen Sie dafür, dass die Klasse die Eigenschaften MaskPicture und Picture des untergelegten UserControl-Objekts offenlegt.
606
Transparenz und Drag&Drop 2. Setzen Sie beim Entwurf des Steuerelements (respektive in der Initialisierungsroutine Initialize) die Eigenschaften FillStyle auf vbFSTransparent und BackColor auf vbTransparent. Weiterhin setzen Sie MaskColor auf die Farbe, die in dem für das Steuerelement vorgesehenen Bild die transparenten Bereiche markiert. 3. Platzieren Sie das Steuerelement auf das vorgesehene Formular und setzen Sie im Formularmodul die Eigenschaften MaskPicture und Picture auf ein Bild Ihrer Wahl vom Typ Picture, dessen Transparenzfarbe mit der Eigenschaft MaskColor des Steuerelements korrespondiert. 4. Passen Sie die Größe des Steuerelements im Formularmodul an die Bildgröße an.
Schach klare Sicht auf Hintergründliches Das in Form des Projekts Schach vorliegende Programm zur Illustration dieser Technik ist etwas komplexer ausgefallen. Es demonstriert nicht nur den Einsatz transparenter Bitmaps, sondern zudem eine Reihe weiterer Techniken: 1. 2. 3. 4. 5.
die Verdrahtung von Menübefehlen die Arbeit mit Steuerelementefeldern das dynamische Generieren von Steuerelementen zur Laufzeit die Arbeit mit dem Bildausschnitt-Steuerelement (PictureClip) Drag&Drop-Operationen mit Steuerelementen.
Das Programm Schach macht seinem Namen alle Ehre: Man kann immerhin damit Schach spielen. Wer nun erwartet, in dem kurzen Programm auch einen würdigen Gegner zum Spielen zu finden, der muss hier leider enttäuscht werden. Die Grundlagen für eine Schach-Engine würden ein Buch ähnlichen Umfangs füllen, und als Zielsprache für eine Implementation würde man sicher nicht Visual Basic wählen, weil Laufzeit in einem solchen Programm ein zu kostbares Gut ist. Für Visual Basic bliebe höchstens der Part der visuellen Darstellung des Spiels. Der Einfachheit halber beschränkt sich das vorliegende Programm auf eben dies, nämlich auf das nackte Spiel, und verzichtet auch auf jegliche Schiedsrichtertätigkeit wie den Ausschluss falscher Züge, die Ansage von Schach oder die Überwachung des Seitenwechsels. Wird eine Figur geschlagen, verlässt sie »freiwillig« das Feld. An definierten Stellungen gibt es die Grundstellungen »Weiß unten« und »Schwarz unten« sowie das »Leere Brett«. Geschlagene oder herausgestellte Figuren landen außerhalb des Bretts in Gefangenschaft der gegnerischen Farbe. Sie können jederzeit zurückgestellt werden. In dieser einfachen Form eignet sich das Programm ganz gut zum Studieren und Nachspielen von Stellungen. Da es auf eine Repräsentation der aktuellen Stellung verzichtet, ersetzt es wohl das Schachbrett und die Figuren – immerhin –, jedoch nicht viel mehr. Hier zuerst der Quelltext des Benutzersteuerelements: '*********************************************************************** ' Benutzersteuerelementmodul: Figur.ctl ' Autor : 2000 Rudolf Huttary ' Beschreibung : Dummy-Steuerelement, ermöglicht ' transparenten Hintergrund. ' Eigenschaft Windowless muss zur ' Entwurfszeit gesetzt werden '***********************************************************************
607
ActiveX- Steuerelemente und Benutzersteuerelemente
Das war es bereits. Um Ressourcen zu sparen, empfiehlt es sich zudem, die Windowless-Eigenschaft des Benutzersteuerelements beim Entwurf auf True zu setzen. Das Steuerelement fordert dann keinen Fenster-Handle hWnd an, der ohnehin nur im Zusammenhang mit Aufrufen der Win32-API benötigt wird. Ach ja, die Kröten. Es gibt welche. Mehr dazu aber im Anschluss an die Besprechung des Programmcodes unter der Überschrift »Diskussion«.
ActiveX- Steuerelemente und Benutzersteuerelemente
Public Property Set Picture(new_Image As Picture) UserControl.Picture = new_Image End Property
ActiveX- Steuerelemente und Benutzersteuerelemente
Public Property Set MaskPicture(ByVal New_MaskPicture As Picture) Set UserControl.MaskPicture = New_MaskPicture End Property Private Sub UserControl_Initialize() UserControl.BackStyle = vbTransparent UserControl.FillStyle = vbFSTransparent UserControl.MaskColor = vbWhite End Sub
Damit Sie das Element nach Fertigstellung auf dem Formular platzieren können, schließen Sie die Fenster mit der Code- und Objektansicht des Steuerelements und öffnen die Objektansicht des Formulars, in dem Sie das Steuerelement verwenden wollen. In der Werkzeugsammlung müssten Sie nun ein weiteres Element mit dem Standardsymbol für Benutzersteuerelemente vorfinden. Platzieren Sie eine Instanz dieses Elements auf dem Formular, geben Sie ihm im Eigenschaftsfenster den Namen Figur und setzen Sie die Eigenschaft Index auf den Wert 0. Sobald Visual Basic im Entwurfsmodus für ein Steuerelement einen Wert für die Eigenschaft Index vorfindet, generiert es von sich aus ein Steuerelemente-Array mit dem gewählten Namen. Dieses Array lässt sich dann im Code dynamisch erweitern. Das erste Objekt eines Steuerelementtyps fungiert dabei als »Samen« für weitere Instanzen und muss zur Entwurfszeit auf dem Formular platziert werden. Weitere Objekte lassen sich dann zur Laufzeit mittels Load ins Leben rufen. (Seit Visual Basic 6.0 besteht zwar auch die Möglichkeit, ActiveX-Objekte, also auch ActiveXSteuerelemente, aus dem Nichts zur Laufzeit anzulegen, doch das wäre im vorliegenden Fall sinnlose akademische Disziplin.) Als zweites Nicht-Standardsteuerelement verwendet das Programm das in der ActiveX-Komponente Picclip32.ocx enthaltene Bildausschnitt-Steuerelement PictureClip. Es dient als Quelle für die Einzelbilder der Schachfiguren, die der Einfachheit halber in einer einzigen Bitmap zusammengefasst sind. Wie der Name schon sagt, ist es die Spezialität des Bildausschnitt-Steuerelements, Teile einer Bitmap als Einzelbilder zu liefern. Ordnet man dem Steuerelement bereits zur Entwurfszeit das die Figuren enthaltende Bild zu und gibt ihm auch die Anzahl der Zeilen und Spalten bekannt, in denen die Einzelbilder organisiert sind (Letzteres ist nur zur Entwurfszeit möglich), sieht der Zugriff auf ein Einzelbild nicht anders aus als der Zugriff auf ein ArrayElement – die GraphicCell-Eigenschaft macht es möglich. Die folgende Abbildung zeigt das Formular mit dem Schachbrett und den Figuren. Neben dem obligatorischen Befehl BEENDEN im DATEI-Menü finden sich im AUFSTELLEN-Menü drei weitere Befehle: WEIß UNTEN, SCHWARZ UNTEN und LEER. Damit lassen sich die drei Grundstellungen herbeiführen, um ein Spiel zu beginnen oder eine Stellung aufzustellen. Gezogen wird mit der Maus. Geschlagene Figuren stellt das Programm von sich aus neben das Brett, von wo sie jederzeit wieder geholt werden können, etwa für die Verwandlung eines durchgebrochenen Bauern. Vor der Besprechung weiterer Einzelheiten hier zunächst einmal der Code des Formularmoduls. '*********************************************************************** ' Projekt : Schach ' Autor : 2000 Rudolf Huttary ' Beschreibung : Demonstriert Transparenz und Drag&Drop '***********************************************************************
608
Transparenz und Drag&Drop
ActiveX- Steuerelemente und Benutzersteuerelemente
Springer zieht als Verteidigung gegen den guten alten »Schäferzug«
Option Explicit Private bSchwarzUnten As Boolean Const FeldBreite = 55 Const FigurBreite = 51 Const FigurHöhe = 51 Const Pfad = "Schach\" Private Sub Form_Load() Dim FigIdx() Dim i As Integer AutoRedraw = True BackColor = vbBlack ScaleMode = vbPixels ' Es rechnet sich besser in Bildpunkten ' Formulargröße gestalten: Height und Width erwarten Twips! Height = ScaleY(11 * FeldBreite, vbPixels, vbTwips) Width = ScaleX(13 * FeldBreite, vbPixels, vbTwips) ' Zuordnung zwischen Figur-Bildern und Figur-Steuerelementen FigIdx = Array(0, 0, 0, 0, 0, 0, 0, 0, 3, 2, 1, 4, 5, 1, 2, 3, _ 6, 6, 6, 6, 6, 6, 6, 6, 9, 8, 7, 10, 11, 7, 8, 9) With PictureClip1 Picture = LoadPicture(App.Path + "\Schachfiguren.bmp") .Rows = 2 .Cols = 6 End With For i = 0 To 31 If i > 0 Then Load figur(i)
' Element mit Index 0 ist "Same" ' Weitere Instanzen des Steuer-
609
ActiveX- Steuerelemente und Benutzersteuerelemente
ActiveX- Steuerelemente und Benutzersteuerelemente
figur(i).Visible = True ' elements dynamisch laden und figur(i).Height = FigurHöhe ' initialisieren figur(i).Width = FigurBreite figur(i).DragMode = vbAutomatic ' Figur-Bilder aus Bildausschnitt-Steuerelement gewinnen Set figur(i).MaskPicture = PictureClip1.GraphicCell(FigIdx(i)) Set figur(i).Picture = PictureClip1.GraphicCell(FigIdx(i)) Next i
' Image-Steuerelemente initialisieren With imgWeißunten ' Schachbrett für "Weiß unten" laden .Visible = False .Picture = LoadPicture(Pfad + "SchachbrettWU.gif") .Width = ScaleX(.Picture.Width, vbHimetric, vbPixels) .Height = ScaleY(.Picture.Height, vbHimetric, vbPixels) .Left = 0: .Top = 0 End With With imgSchwarzunten ' Schachbrett für "Schwarz unten" laden .Visible = False .Picture = LoadPicture(Pfad + "SchachbrettSU.gif") .Width = ScaleX(.Picture.Width, vbHimetric, vbPixels) .Height = ScaleY(.Picture.Height, vbHimetric, vbPixels) .Left = 0: .Top = 0 End With mnuAufstellenWeißUnten_Click End Sub
' Grundstellung
' Figur wird auf neue Position verschoben Private Sub Form_DragDrop(Source As Control, x As Single, y As Single) Dim KoordX As Single, KoordY As Single ' exakte Feldposition berechnen PixelZuFeld x, y FeldZuPixel x, y, KoordX, KoordY ' Figur verschieben Source.Left = KoordX Source.Top = KoordY End Sub ' Falls Figur über einer anderen abgelegt wird, wird diese geschlagen Private Sub figur_DragDrop(Index As Integer, Source As Control, _ x As Single, y As Single) x = figur(Index).Left ' Schlagende Figur nimmt Platz der y = figur(Index).Top ' geschlagenen ein FigurRaus Index ' Erst die Figur raus, dann verschieben! Source.Left = x Source.Top = y End Sub
61 0
Transparenz und Drag&Drop Private Sub FeldZuPixel(ByVal FeldX As Single, ByVal FeldY As Single, _ ByRef x As Single, ByRef y As Single) If Not bSchwarzUnten Then FeldX = 9 – FeldX ' gelb unten? y = FeldBreite * FeldX + (FeldBreite – FigurHöhe) / 2 x = FeldBreite * FeldY + (FeldBreite – FigurBreite) / 2 End Sub
ActiveX- Steuerelemente und Benutzersteuerelemente
Private Sub PixelZuFeld(x As Single, y As Single) Dim z As Single z = y \ FeldBreite y = x \ FeldBreite If bSchwarzUnten Then x = z Else x = 9 – z End Sub Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer) Dim i As Integer For i = 1 To 31 ' Dynamisch angeforderte Steuerelemente freigeben Unload figur(i) Next End Sub Private Sub mnuAufstellenGrundstellung_Click() Dim x As Single, y As Single Dim i As Integer, j As Integer mnuAufstellenLeer_Click For j = 1 To 8 i = j If j = 4 And bSchwarzUnten Then i = 5 ' If j = 5 And bSchwarzUnten Then i = 4 ' FeldZuPixel 8, j, x, y ' rote Bauern figur(i + 15).Left = x: figur(i + 15).Top = FeldZuPixel 2, j, x, y ' gelbe Bauern figur(i – 1).Left = x: figur(i – 1).Top = y FeldZuPixel 1, j, x, y ' gelbe Hauptfiguren figur(i + 7).Left = x: figur(i + 7).Top = y FeldZuPixel 7, j, x, y ' rote Bauern figur(i + 15).Left = x: figur(i + 15).Top = FeldZuPixel 8, j, x, y ' rote Hauptfiguren figur(i + 23).Left = x: figur(i + 23).Top = Next j End Sub
König/Dame vertauschen? König/Dame vertauschen? y
y y
Private Sub mnuAufstellenLeer_Click() Dim i As Integer For i = 0 To 31 ' Alle Figuren rausstellen FigurRaus (i) Next i End Sub Private Sub mnuAufstellenSchwarzUnten_Click() Picture = imgSchwarzunten.Picture
61 1
ActiveX- Steuerelemente und Benutzersteuerelemente
bSchwarzUnten = True mnuAufstellenGrundstellung_Click End Sub
ActiveX- Steuerelemente und Benutzersteuerelemente
Private Sub mnuAufstellenWeißUnten_Click() Picture = imgWeißunten.Picture bSchwarzUnten = False mnuAufstellenGrundstellung_Click End Sub Private Sub mnuDateiBeenden_Click() Unload Me ' löst QueryUnload aus! End Sub ' Figur neben Brett aufstellen Private Sub FigurRaus(Index As Integer) figur(Index).Top = FeldBreite * (1 + Index \ 4) figur(Index).Left = 9.5 * FeldBreite + FigurBreite / 2 * (Index Mod 4) End Sub
Die Logik des Programms ist relativ einfach. In der Initialisierungsroutine Form_Load findet sich der gesamte Code für die Initialisierung des Formulars und der darauf platzierten Steuerelemente PictureClip1 (Bildausschnitt-Steuerelement), imgSchwarzunten und imgWeißunten (Anzeige-Steuerelemente) sowie figur (Benutzersteuerelement). Diese Prozedur erweckt insbesondere auch je Schachfigur ein weiteres figur-Benutzersteuerelement »zum Leben« und ordnet diesem als Maske sowie als Bild die der Spielfigur entsprechende Zelle des Bildausschnitt-Steuerelements zu. Der Schlüssel für die Zuordnung steckt in dem dynamisch initialisierten Array FigIdx und ergibt eine einfache zeilenweise Abzählung der Spielfiguren. Die Behandlungsroutinen für die Befehle im AUFSTELLEN-Menü bedienen sich für die Berechnung der Figurenpositionen verschiedener Hilfsroutinen. Die dabei verwendete Mathematik und Logik ist etwas knifflig, aber durchaus geradlinig. Zu bemerken wäre, dass das Programm die Anzeige-Steuerelemente dazu »missbraucht«, die Hintergrundbilder für die beiden Aufstellungen zu liefern. Der Einfachheit halber pflegt das Programm das Hintergrundbild über die Picture-Eigenschaft des Formulars. Das Interessante an dem Programm ist, wie einfach letztlich der Mechanismus für die Bewegung und das Schlagen der Figuren geraten ist. Nachdem der Benutzer die Figuren mit der Maus ziehen und ablegen können soll, bietet sich für die Implementation dieser Funktionalität der Drag&Drop-Mechanismus geradezu an. Damit eine Instanz des figur-Steuerelements auf Drag&Drop reagiert, ist nichts weiter zu tun, als die Eigenschaft DragMode auf vbAutomatic zu setzen – um den Rest kümmert sich Windows. Insbesondere schickt Windows dem Steuerelement bzw. Fenster eine Nachricht, in dem der »Drop« stattfindet. Zieht der Benutzer eine Figur in ein leeres Feld, ist das Formular Ziel der Benachrichtigung und erhält ein DragDrop-Ereignis. Die entsprechende Behandlungsroutine Form_DragDrop hat dann nichts weiter zu tun, als die Positionskoordinaten des über den Parameter Source identifizierbaren Steuerelements geeignet anzupassen. Ist ein anderes Steuerelement Ziel der Operation, schlägt die gezogene Figur die Figur dieses Steuerelements. Die dafür zuständige Behandlungsroutine figur_DragDrop bekommt als zusätzlichen Parameter den Index des Zielobjekts im Steuerelemente-Array übergeben. Dieses Objekt überlässt dem »schlagenden« Steuerelement (Source) seine Position und verzieht sich dann in die ihm zugeordnete Leerposition neben dem Brett, die schlicht aus ihrem Index abgeleitet wird.
61 2
Transparenz und Drag&Drop Schach1 unter den Blinden ist der Einäugige König
1 Belassen Sie die Eigenschaft DragMode aller Instanzen des figur-Steuerelements auf vbManual (0). 2. Erweitern Sie das Benutzersteuerelement figur dahingehend, dass es die Ereignisse MouseDown, MouseUp und MouseMove delegiert (d.h. an das Formularobjekt signalisiert). 3. Sehen Sie im Formularobjekt Behandlungsroutinen für die neuen Ereignisse vor. Die Routine figur_MouseDown überträgt die Startkoordinaten der Operation in globale Variablen und setzt die Z-Ordnung des Steuerelements so, dass es über allen anderen gezeichnet wird. Die Routine figur_MouseMove aktualisiert die Position des Steuerelements. Der Routine figur_MouseUp fällt schließlich die Aufgabe zu, die Zielkoordinaten zu errechnen, zu testen, ob eine Figur gegebenenfalls geschlagen werden muss, die Z-Ordnung wieder herzustellen (optional) und schließlich das Steuerelement auf die neue Position zu verschieben. Der zusätzliche Code hält sich in Grenzen. Für das figur-Steuerelement lautet er: Event MouseMove(Button As Integer, Shift As Integer, X As Single, _ Y As Single) 'MappingInfo=UserControl,UserControl,-1,MouseMove Event MouseUp(Button As Integer, Shift As Integer, X As Single, _ Y As Single) 'MappingInfo=UserControl,UserControl,-1,MouseUp Event MouseDown(Button As Integer, Shift As Integer, X As Single, _ Y As Single) 'MappingInfo=UserControl,UserControl,-1,MouseDown Private Sub UserControl_MouseDown(Button As Integer, Shift As Integer, _ X As Single, Y As Single) RaiseEvent MouseDown(Button, Shift, X, Y) End Sub Private Sub UserControl_MouseMove(Button As Integer, Shift As Integer, _ X As Single, Y As Single) RaiseEvent MouseMove(Button, Shift, X, Y) End Sub
61 3
ActiveX- Steuerelemente und Benutzersteuerelemente
Haben Sie es gemerkt? Nein? Die Transparenz hat die Programmierung zwar vereinfacht, für das Ergebnis war sie aber nicht unbedingt nötig. Eigentlich sieht man sie nur da, wo der Fehler sitzt und wo die Figuren nebeneinander außerhalb des Bretts stehen. Vom Prinzip her, hätte sich die Geschichte auch mit gewöhnlichen Bitmaps erledigen lassen, wenn man die Bilder der Figuren immer passend zum Hintergrund auswechselt. Einfachheit hat jedoch seinen Preis. Es gibt zwei größere Kröten zu schlucken: Erstens, das System spielt einem Benutzersteuerelement das DragDrop-Ereignis nur dann zu, wenn sich der Mauszeiger beim Loslassen der Maustaste auch wirklich innerhalb der Maske dieses Steuerelements befindet. Zielt der Benutzer auch nur ein wenig daneben, kommen die Figuren übereinander zu liegen – wir spielen aber Schach und nicht Dame. (Immerhin, hier wird die Transparenz deutlich.) Zweitens, der Benutzer sieht nicht, welche Figur er zieht, da das System während automatisierter Drag&Drop-Operationen an der aktuellen Ziehposition nicht das Objekt, sondern nur seinen Umriss als schlichtes Rechteck zeichnet. Um es gleich vorwegzunehmen, für das erste Problem gibt es keine »objektorientierte« Lösung mehr, wenn auf die Drag&Drop-Automatik verzichtet wird. Kam das Programm in der gezeigten Version noch ohne eine explizite Repräsentation der Stellung aus, wird man für die explizite Trefferprüfung doch eher dazu tendieren, explizit darüber Buch zu führen, auf welchem Feld welche Figur zu finden ist – das ist für einen weiteren Ausbau des Programms ohnehin unumgänglich. Die folgende, verbesserte Version Schach1 scheut diesen Aufwand noch und testet nur, ob eine der anderen Figuren auf der Zielposition sitzt. Das zweite Problem lässt sich mit ein wenig Mehraufwand zufriedenstellend in Griff bekommen. Hier der Ansatz:
ActiveX- Steuerelemente und Benutzersteuerelemente
ActiveX- Steuerelemente und Benutzersteuerelemente
Private Sub UserControl_MouseUp(Button As Integer, Shift As Integer, _ X As Single, Y As Single) RaiseEvent MouseUp(Button, Shift, X, Y) End Sub
Anhand der Kommentare erkennt man, dass die Routinen und Ereignisdeklarationen durch den als Add-In zur Verfügung stehenden ActiveX-Steuerelement-Assistenten eingefügt wurden. (Der Assistent muss gegebenenfalls vom Add-In-Manager geladen werden.) Um die Routinen zu »verdrahten«, ordnen Sie den Ereignissen im vierten Dialog des Assistenten das Steuerelement UserControl zu. Visual Basic ergänzt dann die Deklarationen der Ereignisroutinen. Im Formularmodul müssen jetzt die Behandlungsroutinen für diese zusätzlichen Ereignisse ergänzt werden. Dafür sind die Routinen Form_DragDrop und figur_DragDrop obsolet geworden. Private Sub figur_MouseDown(Index As Integer, Button As Integer, _ Shift As Integer, X As Single, Y As Single) ElemX = X ' Startkoordinaten merken ElemY = Y ' Z-Ordnung verändern, damit Figur über allen anderen angezeigt wird figur(Index).ZOrder (0) End Sub Private Sub figur_MouseMove(Index As Integer, Button As Integer, _ Shift As Integer, X As Single, Y As Single) If ElemX Then figur(Index).Left = figur(Index).Left – ScaleX((ElemX – X), _ vbTwips, vbPixels) figur(Index).Top = figur(Index).Top – ScaleY((ElemY – Y), _ vbTwips, vbPixels) End If End Sub Private Sub figur_MouseUp(Index As Integer, Button As Integer, _ Shift As Integer, X As Single, Y As Single) Dim KoordX As Single, KoordY As Single ElemX = 0 ' Drag-Operation zu Ende X = figur(Index).Left + ScaleX((X), vbTwips, vbPixels) Y = figur(Index).Top + ScaleY((Y), vbTwips, vbPixels) ' exakte Feldposition berechnen PixelZuFeld X, Y FeldZuPixel X, Y, KoordX, KoordY ' Trefferprüfung: Wird eine Figur geschlagen? For i = 0 To 31 If figur(i).Left = KoordX Then If figur(i).Top = KoordY Then If i <> Index Then FigurRaus i End If End If Next i ' Z-Ordnung wieder herstellen For i = 31 To 0 Step -1 figur(i).ZOrder (0) Next i
61 4
MemoryEdit das Textfeld mit Gedächtnis
' Figur verschieben figur(Index).Left = KoordX figur(Index).Top = KoordY End Sub
Voilà, ab jetzt funktioniert die Trefferprüfung für das gesamte Feld, und die Figur wird auch während der Ziehoperation laufend gezeichnet. Außerdem erscheint sie vor allen anderen, weil ein entsprechender ZOrder-Aufruf erfolgt. Vergessen Sie nicht, die Zeile ' figur(i).DragMode = vbAutomatic
' Schalter für verbesserte Version
»Schach matt« durch den bekannten Schäferzug
MemoryEdit das Textfeld mit Gedächtnis Verbesserungswürdig ist es allemal, das gute alte Textfeld. Oder hatten Sie noch nie das Verlangen nach einem Überschreibmodus, einem Hexadezimalmodus, einer Suchfunktion oder der Möglichkeit, bei einem mehrzeiligen Textfeld Zeilen- und Spalteninformationen bezogen auf
61 5
ActiveX- Steuerelemente und Benutzersteuerelemente
in der Routine Form_Load auszukommentieren, sonst ändert sich nämlich nichts, sofern die Routinen Form_DragDrop und figur_DragDrop noch vorhanden sind. Das Kommentarzeichen ist der Schalter zwischen der einfachen und der verbesserten Version – wenn es doch nur immer so wäre. Ein wenig exotisch mag die Berechnung der neuen Figurkoordinaten in der Methode Figur_MouseMove anmuten. Windows liefert die Mauskoordinaten nämlich relativ zur linken oberen Ecke des Steuerelements und in Twips – da muss man erst einmal drauf kommen. Damit die Figur dem Mauszeiger folgt, muss die Routine den Offset der Maus innerhalb des Steuerelements aufrechterhalten. Das geht so: Figur_MouseDown speichert die Mausposition als Offset zu Beginn der Operation in globalen Variablen. Figur_MouseMove berechnet die Differenz zwischen der aktuellen Mausposition und dem Offset, wandelt diese in das im Formular geltende Maßsystem um und verschiebt das Steuerelement dann um diese Differenz.
ActiveX- Steuerelemente und Benutzersteuerelemente
ActiveX- Steuerelemente und Benutzersteuerelemente
den aktuellen Umbruch ermitteln zu können? Das einzige, was Microsoft in dieser Richtung bisher »verlauten« ließ, ist das ActiveX-Steuerelement MaskEdBox, ein Versuch, die formatierte Eingabe in Griff zu bekommen – in Hinblick auf die Datenbankprogrammierung natürlich. Auf den ersten Blick ist der Funktionsumfang des Steuerelements zwar beeindruckend, bei näherer Hinsicht muss man sich aber schon wundern, warum die Syntax für die Formatbeschreibung dann letztlich doch so ausdrucksschwach ausgefallen ist – wo bleiben nur die aus der Unix-Welt bekannten »regulären Ausdrücke»? Haben Sie eben bei der »Suchfunktion« gelächelt? So abwegig ist das nicht, auch wenn ein Instr-Aufruf für die Eigenschaft Text mit nachfolgender Definition von SelStart und SelLength zum gewünschten Ergebnis führt und ungleich weniger Aufwand bedeutet als die Programmierung eines eigenen Benutzersteuerelements. Nicht mehr in die Kategorie »mit Hausmitteln zu lösen« fällt dagegen das intelligente Textfeld, das sich die letzten 10 oder auch 100 Eingaben merkt und eifrig vorschlägt, während der Benutzer tippt – Sie kennen das. Am Beispiel des Projekts MemoryEdit stellt Ihnen dieser Abschnitt nicht nur die Implementation eigener Benutzersteuerelemente »bis zur letzten Konsequenz«, nämlich dem ultimativen Einsatz des ActiveX-Schnittstellen-Assistenten, vor, sondern auch den Umgang mit der wirklich vielseitigen Funktion SendMessage der Win32-API. Spezifikation Hier die Spezifikation der zu implementierenden Funktionalität. Das Textfeld soll:
1. die letzten n Eingaben speichern und passende Eingaben nach Möglichkeit während des Tippens als markierten Vorschlag unterbreiten (MemoryEdit), 2. sich nach Möglichkeit so wie jedes andere Textfeld verhalten (MemoryEditX). Beginnen wir mit Punkt 1, der den fakultativen – zu Neudeutsch: wahlfreien – Teil der Implementation beschreibt. Vom Prinzip her lässt sich diese Funktionalität auf Basis eines dynamischen Arrays oder Collection-Objekts implementieren, das bei jeder Eingabe daraufhin durchsucht wird, ob sich eines seiner Elemente als Vorschlag eignet – ein würdiges Finale für dieses Buch wäre ein solches Vorgehen aber eigentlich nicht. Im festen Glauben, dass Ihnen dieser Standardansatz in eigener Regie keine Schwierigkeiten bereiten dürfte, begibt sich dieses Beispiel auf einen gewagteren Pfad: Das Benutzersteuerelement verwendet als Speicher ein unsichtbares Listenfeld und bedient sich dessen Suchfunktion. Suchfunktion? Ja, Suchfunktion. Auch wenn die mit Visual Basic vorliegende Implementation des Steuerelements eine solche Methode nicht kennt, es gibt sie. Um sie von Visual Basic aus aufzurufen, muss man dem Steuerelement nur eine geeignete Nachricht schicken. Nachricht? Auch dafür gibt es (leider) kein Standardmittel in Visual Basic, so dass ein Ausflug in die Win32-API angesagt ist. Die Funktion trägt den Bezeichner SendMessage und das gesamte Zubehör für ihren Aufruf lässt sich mit dem APIViewer, einem reichlich »dummen«, aber funktionalen Add-In der Visual-Basic-Entwicklungsumgebung importieren. Der Rest, also die Kopplung der beiden Steuerelemente in einem Benutzersteuerelementmodul, ist wieder einmal solides Handwerk. Punkt 2 besagt, dass das neue ActiveX-Steuerelement nicht nur die gleichen Eigenschaften und Methoden wie ein Textfeld haben soll, sondern auch die gleichen Ereignisse. Angesichts der schieren Fülle dieser Elemente würde »der Spaß« wohl schnell aufhören, hätte Microsoft Visual Basic 6.0 nicht den ActiveX-Schnittstellen-Assistenten beigepackt, der einem den drögen Teil der Arbeit erstaunlich gründlich abnimmt, dafür aber mit Code nur so um sich schmeißt. Dem sofortigen Start des Benutzersteuerelements stehen dann allerdings noch einige Fehler im Wege, die zum einen aus der wohl zu eifrigen Übersetzung der Konstanten False und True resultieren, in anders gelagerten Fällen zuweilen aber auch unbekannte Datentypen involvieren können, für die man noch Verweise auf die jeweiligen Module einrichten muss.
61 6
MemoryEdit das Textfeld mit Gedächtnis
Die beiden vorgestellten Varianten des Steuerelements in Aktion
Umsetzung der Grundfunktionalität
1. Legen Sie ein neues Projekt des Typs »Standard-EXE« an und speichern Sie die Projektdatei als MemoryEdit.vbp und das Formular als MemoryEditTest.frm. 2. Fügen Sie entweder ein weiteres Projekt mit dem Typ ActiveX-Steuerelement hinzu oder ergänzen Sie schlicht in dem ersten Projekt ein Benutzersteuerelementmodul. Nennen Sie das Steuerelement MemoryEdit und speichern Sie es unter dem gleichen Namen (MemoryEdit.ctl). 3. Ziehen Sie das Textfeld Text1 sowie ein Listenfeld List1 in die Entwurfsansicht des Steuerelements. Abmessungen und Position sind egal. 4. Wechseln Sie in die Codeansicht und starten Sie den API-Viewer, den Sie gegebenenfalls über den Add-In-Manager im Menü ADD-INS erst einmal laden müssen. Damit der API-Viewer das macht, was er machen soll, ist es erforderlich, dass Sie nach seinem Start die Datei \Visual Studio\Common\Tools\Winapi\Win32api.txt laden (und zwar dummerweise jedes Mal wieder, wenn Sie ihn aufrufen). Wählen Sie in der Kategorie (API-TYP) Deklarationen die Funktion SendMessage und in der Kategorie Konstanten die Bezeichner LB_FINDSTRING sowie LB_FINDSTRINGEXACT aus. Dann fügen Sie die entsprechenden Deklarationen mit der Option PRIVATE in den Quelltext ein.
Auswahl der für
SendMessage
erforderlic hen API- Deklarationen im API- Viewer
61 7
ActiveX- Steuerelemente und Benutzersteuerelemente
Wenn es Ihre Zeit erlaubt, sollten Sie das Beispielprojekt schrittweise nachvollziehen, da der Umgang mit den Assistenten ein Learning-by-Doing erfordert. Hier die einzelnen Schritte.
ActiveX- Steuerelemente und Benutzersteuerelemente
Der vom API-Viewer eingefügte Code lautet:
ActiveX- Steuerelemente und Benutzersteuerelemente
Option Explicit Private Declare Function SendMessage Lib "user32" _ Alias "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, _ ByVal wParam As Long, lParam As Any) _ As Long Private Const LB_FINDSTRING = &H18F Private Const LB_FINDSTRINGEXACT = &H1A2
5. Ergänzen Sie die Implementation einer Eigenschaft namens MaxRemember, deren Aufgabe es ist, die Anzahl der Einträge im Listenfeld nach oben hin zu begrenzen. Sehen Sie für die interne Darstellung eine Integer-Variable m_MaxRemember und als Standardwert für die Initialisierung der Variable eine Konstante m_def_MaxRemember vor. (Halten Sie sich an das Benennungsschema, da später noch der ActiveX-Schnittstellen-Assistent in dem Code »herummalt«.) Implementieren Sie weiterhin die einschlägigen Methoden. Private Const m_def_MaxRemember = 0 Private m_MaxRemember Public Property Let MaxRemember(New_MaxRemember As Integer) m_MaxRemember = IIf(New_MaxRemember >= 0, New_MaxRemember, 0) While m_MaxRemember < List1.ListCount ' ggf. Einträge löschen List1.RemoveItem (List1.ListCount – 1) Wend End Property Public Property Get MaxRemember() As Integer MaxRemember = m_MaxRemember End Property Private Sub UserControl_ReadProperties(PropBag As PropertyBag) m_MaxRemember = PropBag.ReadProperty("MaxRemember", _ m_def_MaxRemember) End Sub Private Sub UserControl_WriteProperties(PropBag As PropertyBag) PropBag.WriteProperty "MaxRemember", m_MaxRemember, _ m_def_MaxRemember End Sub Private Sub UserControl_InitProperties() m_MaxRemember = m_def_MaxRemember End Sub
6. Ergänzen Sie den obligatorischen Code für die Initialisierung und für Größenänderungen. Da das Benutzersteuerelement letztlich nur aus dem Textfeld Text1 besteht, sollten die Abmessungen identisch sein. Private Sub UserControl_Initialize() List1.Visible = False Text1.Top = 0 Text1.Left = 0 End Sub
61 8
MemoryEdit das Textfeld mit Gedächtnis
Private Sub UserControl_Resize() Text1.Height = Height Text1.Width = Width Height = Text1.Height ' Textfeld erzwingt minimale Abmessungen Width = Text1.Width End Sub
Private Sub Text1_Change() Dim SelIdx As Integer Dim ListIdx As Integer ' Bisherige Eingabe in Liste suchen.. ListIdx = SendMessage(List1.hWnd, LB_FINDSTRING, 0, _ ByVal Text1.Text) If ListIdx > -1 Then ' Gefunden? SelIdx = Text1.SelStart ' Cursorposition merken Text1 = List1.List(ListIdx) ' Listeneintrag übernehmen Text1.SelStart = SelIdx ' Markierung Text1.SelLength = Len(Text1.Text) – SelIdx End If End Sub
8. Diese Routine erledigt zwar die Hauptarbeit, reicht aber nicht für alle Fälle, weil sie dummerweise die Taste (Rück) außer Kraft setzt. Abhilfe schafft eine Sonderbehandlung der Taste im Rahmen der Behandlungsroutine für das KeyDown-Ereignis. Und wenn man schon dabei ist, kann man auch gleich die Tasten (Oben) und (Unten) dahingehend ummünzen, dass sie ein explizites Durchwandern der Liste vom aktuellen Wert bzw. von Anfang an ermöglichen. Private Sub Text1_KeyDown(KeyCode As Integer, Shift As Integer) Dim SelLen As Integer Dim ListIdx As Integer Select Case KeyCode Case vbKeyBack ' Rück-Taste? If Text1.SelStart > 0 And Text1.SelLength > 0 Then ' Markierung? ' Markierung um eine Zeichenposition verschieben SelLen = Text1.SelLength Text1.SelStart = Text1.SelStart – 1 Text1.SelLength = SelLen + 1
61 9
ActiveX- Steuerelemente und Benutzersteuerelemente
7. Damit wäre der obligatorische Teil (des fakultativen Teils) erledigt. Nun geht es darum, das Listenfeld zu pflegen und die Eingaben des Benutzers zu überwachen, um sie gegebenenfalls durch einen passenden Vorschlag in Form einer Markierung zu ergänzen. Nehmen Sie an, die Liste enthält bereits Einträge (ein Zustand, der sich ja jederzeit provisorisch herstellen lässt). Für die Überwachung der Eingaben passt man am besten das Change-Ereignis des Textfelds ab und sucht für den bereits eingegebenen Wert einen passenden Eintrag im Listenfeld. All dies erledigt ein einziger SendMessage-Aufruf. Die API-Funktion verlangt dazu den FensterHandle hWnd des Listenfelds, die erwartete Aktion LB_FINDSTRING, einen Dummy-Wert, der vom Listenfeld für diese Aktion nicht beachtet wird, und schließlich die gesuchte Zeichenfolge, also den Wert des Textfelds. Beachten Sie die im Referenzteil unter »Routinen aus DLLs und der Windows-API einsetzen« (S. 185) näher ausgeführte Besonderheit, dass die Übergabe der Zeichenfolge hier mit dem Zusatz ByVal erfolgen muss! Ergebnis des Aufrufs ist der Wert -1, wenn kein Eintrag passt, oder eben der Index des passenden Listeneintrags. Falls ein Eintrag gefunden wurde, wird er in das Textfeld übernommen und der ergänzte Teil durch entsprechende Definition der Eigenschaften SelStart und SelLength markiert.
ActiveX- Steuerelemente und Benutzersteuerelemente
ActiveX- Steuerelemente und Benutzersteuerelemente
KeyCode = 0 End If Case vbKeyDown, vbKeyUp ' Unten-/Oben-Taste? ' Aktuellen Wert in Liste suchen ListIdx = SendMessage(List1.hWnd, LB_FINDSTRING, 0, _ ByVal Text1.Text) If ListIdx > -1 Then ' Gefunden? If KeyCode = vbKeyDown Then ' Unten-Taste? ' Ja: nächsten Eintrag nehmen ListIdx = (ListIdx + 1) Mod List1.ListCount Else ' Nein: vorigen Eintrag nehmen ListIdx = IIf(ListIdx, ListIdx – 1, List1.ListCount – 1) End If Text1 = List1.List(ListIdx) ElseIf List1.ListCount Then ' Nein: Gibt es Einträge? Text1 = List1.List(0) ' Ja: Ersten nehmen End If KeyCode = 0 End Select End Sub
9. Bleibt noch zu klären, woher die Liste ihre Einträge erhält. Das ist nicht schwer, weil man sich dafür des LostFocus-Ereignisses bedienen kann. Um mehrfache Einträge für ein und denselben Wert zu vermeiden, lässt die Behandlungsroutine den aktuellen Wert des Textfelds noch einmal via SendMessage vom Listenfeld suchen und fügt den Wert, sofern er noch nicht existiert, zuvorderst in die Liste ein – freilich nicht, ohne gegebenenfalls den ältesten Eintrag zuvor zu löschen, wenn MaxRemember erreicht ist. Private Sub Text1_LostFocus() If MaxRemember > 0 Then ' Ist Eintrag bereits in Liste? If -1 = SendMessage(List1.hWnd, LB_FINDSTRINGEXACT, 0, _ ByVal Text1.Text) _ Then If List1.ListCount = MaxRemember Then ' Nein: Ist Liste voll? List1.RemoveItem (List1.ListCount – 1) ' Ja: Letzten Eintrag End If List1.AddItem Text1, 0 ' Neuen Eintr. einfügen End If End If End Sub
10. Nun können Sie das Steuerelement testen. Schließen Sie dazu die Entwurfsansicht des Steuerelements und öffnen Sie die Entwurfsansicht des Formulars. In der Werkzeugleiste müsste nun ein weiteres Symbol erschienen sein, dessen QuickInfo »MemoryEdit« lautet. Ziehen Sie eine Instanz des Steuerelements auf das Formular. Falls Sie bei der Implementation einen Fehler gemacht haben, müssten Sie nun die ersten Fehlermeldungen des Compilers erhalten, da Visual Basic das Steuerelement kompiliert, um eine Entwurfsinstanz davon zu starten. Bevor Sie das gesamte Projekt starten – Code ist keiner erforderlich –, sollten Sie aber noch ein weiteres Steuerelement, am besten ein Textfeld, auf dem Formular platzieren, damit das LostFocus-Ereignis überhaupt auftreten kann. »Wie Sie sehen, sehen Sie nichts.« Oder haben Sie
620
MemoryEdit das Textfeld mit Gedächtnis
daran gedacht, die Eigenschaft MaxRemember im Eigenschaftsfenster des MemoryEdit-Steuerelements auf einen Wert größer 0 zu setzen? Dass die Laufzeitinstanz des Steuerelements diesen Wert ebenfalls zu sehen bekommt, liegt wie im Abschnitt »LongTimer – der Timer mit Ausdauer« (S. 599) bereits näher ausgeführt, an den Behandlungsroutinen für die Ereignisse WriteProperties und ReadProperties. Spätestens jetzt sollte sich das Steuerelement nicht mehr gegen seine Natur wehren. Erweiterung zum vollwertigen Textfeld- Ersatz
Grundausstattung des neuen Steuerelements ermitteln (Bild montiert!)
Natürlich können Sie die Bestandsaufnahme auch anhand des Objektkatalogs durchführen. Wenn Sie den Katalog der Eigenschaften, Methoden und Ereignisse des Textfelds (vgl. Tabelle) dagegenhalten, erhalten Sie eine Vorstellung davon, was noch zu implementieren ist. Eigenschaften
Methoden
Ereignisse
Alignment, Appearance, BackColor, BorderStyle, CausesValidation, Container, DataChanged, DataField, DataFormat, DataMember, DataSource, DragIcon, DragMode, Enabled, Font, FontBold, FontItalic, FontName, FontSize, FontStrikethru, FontUnderline, ForeColor, Height, HelpContextID, HideSelection, hWnd, Index, Left, LinkItem, LinkMode, LinkTimeout, LinkTopic, Locked, MaxLength, MouseIcon, MousePointer, MultiLine, Name, OLEDragMode, OLEDropMode, Parent, PasswordChar, RightToLeft, ScrollBars, SelLength, SelStart, SelText, TabIndex, TabStop, Tag, Text, Top, ToolTipText, Visible, WhatsThisHelpID, Width
Drag, LinkExecute, LinkPoke, LinkRequest, LinkSend, Move, OLEDrag, Refresh, SetFocus, ShowWhatsThis, ZOrder
Change, Click, DblClick, DragDrop, DragOver, GotFocus, KeyDown, KeyPress, KeyUp, LinkClose, LinkError, LinkNotify, LinkOpen, LostFocus, MouseDown, MouseMove, MouseUp, OLECompleteDrag, OLEDragDrop, OLEDragOver, OLEGiveFeedback, OLESetData, OLEStartDrag, Validate
Katalog der Eigenschaften, Methoden und Ereignisse eines Textfelds
621
ActiveX- Steuerelemente und Benutzersteuerelemente
Ein Blick auf das Eigenschaftsfenster des neuen Steuerelements verrät es bereits: Ein vollwertiger Ersatz für ein Textfeld ist MemoryEdit noch nicht. Neben der explizit über Property Let/Get publizierten Eigenschaft MaxRemember finden sich dort nur obligatorische Eigenschaften wie Name, Left, Top, Width, Height usw., die dem untergelegten UserControl-Objekt entstammen. Eine genauere Analyse dessen, was da ist, ermöglicht die visuelle Hilfestellung des Editors, wenn Sie den Bezeichner der Steuerelementinstanz im Formularmodul tippen.
ActiveX- Steuerelemente und Benutzersteuerelemente
ActiveX- Steuerelemente und Benutzersteuerelemente
Damit Ihnen die Tipparbeit erspart bleibt, hat Microsoft der Entwicklungsumgebung von Visual Basic 6.0 den VB6 ActiveX-Schnittstellen-Assistenten in Form eines Add-Ins beigepackt. Nachdem die Leistung des Assistenten beachtlich (wenn auch nicht vollkommen) ist, sollten Sie auf seinen Einsatz keinesfalls verzichten – selbst wenn Sie auch nur drei oder vier Elemente aus dem Katalog in die Implementation aufnehmen wollen. Im Folgenden eine Beschreibung der Vorgehensweise. 1. Da der Assistent nicht nur einen mehrseitigen Wust an Code ausspuckt, sondern auch Ergänzungen am bestehenden Code vornimmt, empfiehlt es sich vor seinem Einsatz, eine Kopie des Codes zu erstellen. Am besten, Sie benennen das Steuerelement in MemoryTextX um, speichern das Codemodul unter einem anderen Namen (MemoryTextX.ctl) und fügen dem Projekt das alte Steuerelementmodul MemoryText.ctl wieder hinzu, so dass Sie später den Quelltext beider Steuerelemente im Zugriff haben. 2. Starten Sie den Assistenten, den Sie gegebenenfalls zuvor über den Add-In-Manager laden müssen. Folgen Sie den Anweisungen in den einzelnen Dialogen des Assistenten und seien Sie gewissenhaft, denn Nachlässigkeiten können später zu verzwickten Fehlern im Code oder zumindest zu erheblichen Ausbesserungsarbeiten führen. 3. Nach Auswahl des Benutzersteuerelements müssen Sie sich dafür entscheiden, welche Elemente Sie aus dem riesigen Fundus der für das Steuerelement verfügbaren Bezeichner in die Schnittstelle aufnehmen wollen. (Vergessene Bezeichner lassen sich durch einen erneuten Aufruf des Assistenten später jederzeit noch nachrüsten.) Wählen Sie nur die Bezeichner aus, die in dem Elementkatalog des Textfelds (vgl. Tabelle) aufgeführt sind – nicht jedoch die Eigenschaften DataFormat, DataSource, DataMember, denn Datenbankverbindungen mit ActiveX-Steuerelementen werden mit anderen Mitteln ermöglicht. Die Bezeichner der Elemente, die das UserControl-Objekt bereits standardmäßig beisteuert, stehen nicht zur Auswahl – so beispielsweise Left, Top, Width, Height, aber auch ZOrder, Move, DragDrop usw. Gegebenenfalls bieten sich noch die vom Listenfeld stammenden Bezeichner AddItem, RemoveItem List und ListCount an, wenn die Liste öffentlich zugänglich sein soll.
Elemente für die Schnittstelle auswählen
4. Das nächste Fenster des Assistenten soll die Gestaltung benutzerdefinierter Eigenschaften ermöglichen. Wie Sie sehen, hat der Assistent die bereits implementierte Eigenschaft MaxRemember erkannt und bietet sie an. Wechseln Sie in das nächste Fenster, es sei denn, Sie wollen weitere Größen des Codes publizieren.
622
MemoryEdit das Textfeld mit Gedächtnis
5. Im nun erscheinenden Dialogfeld des Assistenten legen Sie fest, welches Schnittstellenelement an welches Objekt (und welches Element dieses Objekts) delegiert wird. Zur Auswahl stehen die Objekte Text1, List1 und UserControl. Die Standardantwort wird hier Text1 und das vorgeschlagene Element sein (hier könnte der Assistent einem noch ein wenig Arbeit abnehmen, wenn er eine Standardvorgabe für das Objekt benutzen würde!), vielleicht außer Eigenschaften wie Visible oder Enabled, die besser dem UserControl-Objekt zugeordnet werden. Einzig die vom Code selbst beigesteuerte MaxRemember-Eigenschaft benötigt keine Zuordnung – wiewohl der Assistent eine solche vom Prinzip her akzeptieren würde.
6. Das letzte Dialogfeld vor dem Epilog ermöglicht es, die Datentypen sowie Vorgabewerte nicht zugeordneter Elemente (hier also für MaxRemember) festzulegen. Belassen Sie es bei der Voreinstellung und klicken Sie auf FERTIG, um den Assistenten auf den Code loszulassen. Hier das – manuell noch ein wenig nachbearbeitete – Ergebnis (zur Nachbereitung gleich noch mehr): '*********************************************************************** ' Projekt : MemoryEdit ' Autor : 2000 Rudolf Huttary ' Benutzersteuerelementmodul: MemoryEditX ' Funktion : Wie MemoryEdit, delegiert jedoch ' Eigenschaften, Methoden und Ereignisse '*********************************************************************** Option Explicit Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" _ (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _ lParam As Any) As Long Private Const LB_FINDSTRING = &H18F Private Const LB_FINDSTRINGEXACT = &H1A2 'Standard-Eigenschaftswerte: Const m_def_MaxRemember = 0 'Eigenschaftsvariablen:
623
ActiveX- Steuerelemente und Benutzersteuerelemente
Delegation der Schnittstellenelemente
ActiveX- Steuerelemente und Benutzersteuerelemente
ActiveX- Steuerelemente und Benutzersteuerelemente
Dim m_MaxRemember As Integer 'Ereignisdeklarationen: Event Change() 'MappingInfo=Text1,Text1,-1,Change Event Click() 'MappingInfo=Text1,Text1,-1,Click Event DblClick() 'MappingInfo=Text1,Text1,-1,DblClick Event KeyDown(KeyCode As Integer, Shift As Integer) _ 'MappingInfo=Text1,Text1,-1,KeyDown Event KeyPress(KeyAscii As Integer) 'MappingInfo=Text1,Text1,-1,KeyPress Event KeyUp(KeyCode As Integer, Shift As Integer) _ 'MappingInfo=Text1,Text1,-1,KeyUp Event MouseDown(Button As Integer, Shift As Integer, X As Single, _ Y As Single) 'MappingInfo=Text1,Text1,-1,MouseDown Event MouseMove(Button As Integer, Shift As Integer, X As Single, _ Y As Single) 'MappingInfo=Text1,Text1,-1,MouseMove Event MouseUp(Button As Integer, Shift As Integer, X As Single, _ Y As Single) 'MappingInfo=Text1,Text1,-1,MouseUp Event OLECompleteDrag(Effect As Long) _ 'MappingInfo=Text1,Text1,-1,OLECompleteDrag Event OLEDragDrop(Data As DataObject, Effect As Long, Button As Integer, Shift As Integer, X As Single, Y As Single) _ 'MappingInfo=Text1,Text1,-1,OLEDragDrop Event OLEDragOver(Data As DataObject, Effect As Long, _ Button As Integer, Shift As Integer, X As Single, Y As Single, _ State As Integer) 'MappingInfo=Text1,Text1,-1,OLEDragOver Event OLEGiveFeedback(Effect As Long, DefaultCursors As Boolean) _ 'MappingInfo=Text1,Text1,-1,OLEGiveFeedback Event OLESetData(Data As DataObject, DataFormat As Integer) _ 'MappingInfo=Text1,Text1,-1,OLESetData Event OLEStartDrag(Data As DataObject, AllowedEffects As Long) _ 'MappingInfo=Text1,Text1,-1,OLEStartDrag Event Validate(Cancel As Boolean) 'MappingInfo=Text1,Text1,-1,Validate Private Sub Text1_KeyDown(KeyCode As Integer, Shift As Integer) RaiseEvent KeyDown(KeyCode, Shift) Dim SelLen As Integer Dim ListIdx As Integer Select Case KeyCode Case vbKeyBack ' Rück-Taste? If Text1.SelStart > 0 And Text1.SelLength > 0 Then ' Markierung? ' Markierung um eine Zeichenposition verschieben SelLen = Text1.SelLength Text1.SelStart = Text1.SelStart – 1 Text1.SelLength = SelLen + 1 KeyCode = 0 End If Case vbKeyDown, vbKeyUp ' Unten-/Oben-Taste? ' Aktuellen Wert in Liste suchen ListIdx = SendMessage(List1.hWnd, LB_FINDSTRING, -1, _ ByVal Text1.Text) If ListIdx > -1 Then ' Gefunden? If KeyCode = vbKeyDown Then ' Unten-Taste?
624
MemoryEdit das Textfeld mit Gedächtnis
ActiveX- Steuerelemente und Benutzersteuerelemente
' Ja: nächsten Eintrag nehmen ListIdx = (ListIdx + 1) Mod List1.ListCount Else ' Nein: vorigen Eintrag nehmen ListIdx = IIf(ListIdx, ListIdx – 1, List1.ListCount – 1) End If Text1 = List1.List(ListIdx) ElseIf List1.ListCount Then ' Nein: Gibt es Einträge? Text1 = List1.List(0) ' Ja: Ersten nehmen End If KeyCode = 0 End Select End Sub Private Sub Text1_LostFocus() If MaxRemember > 0 Then ' Ist Eintrag bereits in Liste? If -1 = SendMessage(List1.hWnd, LB_FINDSTRINGEXACT, 0, _ ByVal Text1.Text) _ Then If List1.ListCount = MaxRemember Then ' Nein: Ist Liste voll? List1.RemoveItem (List1.ListCount – 1) ' Ja: Letzten Eintrag End If List1.AddItem Text1, 0 ' Neuen Eintr. einfügen End If End If End Sub Private Sub UserControl_Initialize() List1.Visible = False Text1.Top = 0 Text1.Left = 0 End Sub Private Sub UserControl_Resize() Text1.Height = Height Text1.Width = Width Height = Text1.Height ' Textfeld erzwingt minimale Abmessungen Width = Text1.Width End Sub
Private Sub UserControl_ReadProperties(PropBag As PropertyBag) m_MaxRemember = PropBag.ReadProperty("MaxRemember", m_def_MaxRemember) Text1.Alignment = PropBag.ReadProperty("Alignment", 0) Text1.Appearance = PropBag.ReadProperty("Appearance", 1) Text1.BackColor = PropBag.ReadProperty("BackColor", &H80000005) Text1.BorderStyle = PropBag.ReadProperty("BorderStyle", 1) Text1.CausesValidation = PropBag.ReadProperty("CausesValidation", _ True) UserControl.Enabled = PropBag.ReadProperty("Enabled", True)
625
ActiveX- Steuerelemente und Benutzersteuerelemente
ActiveX- Steuerelemente und Benutzersteuerelemente
Set Text1.Font = PropBag.ReadProperty("Font", Ambient.Font) Text1.FontBold = PropBag.ReadProperty("FontBold", Ambient.Font.Bold) Text1.FontItalic = PropBag.ReadProperty("FontItalic",_ Ambient.Font.Italic) Text1.FontName = PropBag.ReadProperty("FontName", Ambient.Font.Name) Text1.FontSize = PropBag.ReadProperty("FontSize", Ambient.Font.Size) Text1.FontStrikethru = PropBag.ReadProperty("FontStrikethru", _ Ambient.Font.Strikethrough) Text1.FontUnderline = PropBag.ReadProperty("FontUnderline", _ Ambient.Font.Underline) Text1.ForeColor = PropBag.ReadProperty("ForeColor", &H80000008) Text1.LinkItem = PropBag.ReadProperty("LinkItem", "") Text1.LinkMode = PropBag.ReadProperty("LinkMode", 0) Text1.LinkTimeout = PropBag.ReadProperty("LinkTimeout", 50) Text1.LinkTopic = PropBag.ReadProperty("LinkTopic", "") Text1.Locked = PropBag.ReadProperty("Locked", False) Text1.MaxLength = PropBag.ReadProperty("MaxLength", 0) Set MouseIcon = PropBag.ReadProperty("MouseIcon", Nothing) Text1.MousePointer = PropBag.ReadProperty("MousePointer", 0) Text1.OLEDragMode = PropBag.ReadProperty("OLEDragMode", 0) Text1.OLEDropMode = PropBag.ReadProperty("OLEDropMode", 0) Text1.PasswordChar = PropBag.ReadProperty("PasswordChar", "") Text1.SelLength = PropBag.ReadProperty("SelLength", 0) Text1.SelStart = PropBag.ReadProperty("SelStart", 0) Text1.SelText = PropBag.ReadProperty("SelText", "") Text1.Text = PropBag.ReadProperty("Text", Ambient.DisplayName) Text1.ToolTipText = PropBag.ReadProperty("ToolTipText", "") Text1.WhatsThisHelpID = PropBag.ReadProperty("WhatsThisHelpID", 0) End Sub Private Sub UserControl_WriteProperties(PropBag As PropertyBag) PropBag.WriteProperty "MaxRemember", m_MaxRemember, _ m_def_MaxRemember Call PropBag.WriteProperty("Alignment", Text1.Alignment, 0) Call PropBag.WriteProperty("Appearance", Text1.Appearance, 1) Call PropBag.WriteProperty("BackColor", Text1.BackColor, &H80000005) Call PropBag.WriteProperty("BorderStyle", Text1.BorderStyle, 1) Call PropBag.WriteProperty("CausesValidation", _ Text1.CausesValidation True) Call PropBag.WriteProperty("Enabled", UserControl.Enabled, True) Call PropBag.WriteProperty("Font", Text1.Font, Ambient.Font) Call PropBag.WriteProperty("FontBold", Text1.FontBold, 0) Call PropBag.WriteProperty("FontItalic", Text1.FontItalic, 0) Call PropBag.WriteProperty("FontName", Text1.FontName, "") Call PropBag.WriteProperty("FontSize", Text1.FontSize, 0) Call PropBag.WriteProperty("FontStrikethru", Text1.FontStrikethru, 0) Call PropBag.WriteProperty("FontUnderline", Text1.FontUnderline, 0) Call PropBag.WriteProperty("ForeColor", Text1.ForeColor, &H80000008) Call PropBag.WriteProperty("LinkItem", Text1.LinkItem, "") Call PropBag.WriteProperty("LinkMode", Text1.LinkMode, 0) Call PropBag.WriteProperty("LinkTimeout", Text1.LinkTimeout, 50)
626
MemoryEdit das Textfeld mit Gedächtnis
ActiveX- Steuerelemente und Benutzersteuerelemente
Call PropBag.WriteProperty("LinkTopic", Text1.LinkTopic, "") Call PropBag.WriteProperty("Locked", Text1.Locked, False) Call PropBag.WriteProperty("MaxLength", Text1.MaxLength, 0) Call PropBag.WriteProperty("MouseIcon", MouseIcon, Nothing) Call PropBag.WriteProperty("MousePointer", Text1.MousePointer, 0) Call PropBag.WriteProperty("OLEDragMode", Text1.OLEDragMode, 0) Call PropBag.WriteProperty("OLEDropMode", Text1.OLEDropMode, 0) Call PropBag.WriteProperty("PasswordChar", Text1.PasswordChar, "") Call PropBag.WriteProperty("SelLength", Text1.SelLength, 0) Call PropBag.WriteProperty("SelStart", Text1.SelStart, 0) Call PropBag.WriteProperty("SelText", Text1.SelText, "") Call PropBag.WriteProperty("Text", Text1.Text, Ambient.DisplayName) Call PropBag.WriteProperty("ToolTipText", Text1.ToolTipText, "") Call PropBag.WriteProperty("WhatsThisHelpID", Text1.WhatsThisHelpID,0) End Sub 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER ' VERÄNDERN! 'MappingInfo=Text1,Text1,-1,Alignment Public Property Get Alignment() As Integer Alignment = Text1.Alignment End Property Public Property Let Alignment(ByVal New_Alignment As Integer) Text1.Alignment() = New_Alignment PropertyChanged "Alignment" End Property 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,Appearance Public Property Get Appearance() As Integer Appearance = Text1.Appearance End Property Public Property Let Appearance(ByVal New_Appearance As Integer) Text1.Appearance() = New_Appearance PropertyChanged "Appearance" End Property 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,BackColor Public Property Get BackColor() As OLE_COLOR BackColor = Text1.BackColor End Property Public Property Let BackColor(ByVal New_BackColor As OLE_COLOR) Text1.BackColor() = New_BackColor PropertyChanged "BackColor" End Property
627
ActiveX- Steuerelemente und Benutzersteuerelemente
ActiveX- Steuerelemente und Benutzersteuerelemente
'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,BorderStyle Public Property Get BorderStyle() As Integer BorderStyle = Text1.BorderStyle End Property Public Property Let BorderStyle(ByVal New_BorderStyle As Integer) Text1.BorderStyle() = New_BorderStyle PropertyChanged "BorderStyle" End Property 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,CausesValidation Public Property Get CausesValidation() As Boolean CausesValidation = Text1.CausesValidation End Property Public Property Let CausesValidation(ByVal New_CausesValidation _ As Boolean) Text1.CausesValidation() = New_CausesValidation PropertyChanged "CausesValidation" End Property 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=UserControl,UserControl,-1,Enabled Public Property Get Enabled() As Boolean Enabled = UserControl.Enabled End Property Public Property Let Enabled(ByVal New_Enabled As Boolean) UserControl.Enabled() = New_Enabled PropertyChanged "Enabled" End Property 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,Font Public Property Get Font() As Font Set Font = Text1.Font End Property Public Property Set Font(ByVal New_Font As Font) Set Text1.Font = New_Font PropertyChanged "Font" End Property 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER
628
MemoryEdit das Textfeld mit Gedächtnis
'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,FontBold Public Property Get FontBold() As Boolean FontBold = Text1.FontBold End Property Public Property Let FontBold(ByVal New_FontBold As Boolean) Text1.FontBold() = New_FontBold PropertyChanged "FontBold" End Property
ActiveX- Steuerelemente und Benutzersteuerelemente
'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,FontItalic Public Property Get FontItalic() As Boolean FontItalic = Text1.FontItalic End Property Public Property Let FontItalic(ByVal New_FontItalic As Boolean) Text1.FontItalic() = New_FontItalic PropertyChanged "FontItalic" End Property 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,FontName Public Property Get FontName() As String FontName = Text1.FontName End Property Public Property Let FontName(ByVal New_FontName As String) Text1.FontName() = New_FontName PropertyChanged "FontName" End Property 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,FontSize Public Property Get FontSize() As Single FontSize = Text1.FontSize End Property Public Property Let FontSize(ByVal New_FontSize As Single) Text1.FontSize() = New_FontSize PropertyChanged "FontSize" End Property 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,FontStrikethru Public Property Get FontStrikethru() As Boolean
629
ActiveX- Steuerelemente und Benutzersteuerelemente
FontStrikethru = Text1.FontStrikethru End Property
ActiveX- Steuerelemente und Benutzersteuerelemente
Public Property Let FontStrikethru(ByVal New_FontStrikethru As Boolean) Text1.FontStrikethru() = New_FontStrikethru PropertyChanged "FontStrikethru" End Property 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,FontUnderline Public Property Get FontUnderline() As Boolean FontUnderline = Text1.FontUnderline End Property Public Property Let FontUnderline(ByVal New_FontUnderline As Boolean) Text1.FontUnderline() = New_FontUnderline PropertyChanged "FontUnderline" End Property 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,ForeColor Public Property Get ForeColor() As OLE_COLOR ForeColor = Text1.ForeColor End Property Public Property Let ForeColor(ByVal New_ForeColor As OLE_COLOR) Text1.ForeColor() = New_ForeColor PropertyChanged "ForeColor" End Property 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,HideSelection Public Property Get HideSelection() As Boolean HideSelection = Text1.HideSelection End Property 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,LinkItem Public Property Get LinkItem() As String LinkItem = Text1.LinkItem End Property Public Property Let LinkItem(ByVal New_LinkItem As String) Text1.LinkItem() = New_LinkItem PropertyChanged "LinkItem" End Property
63 0
MemoryEdit das Textfeld mit Gedächtnis
'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,LinkMode Public Property Get LinkMode() As Integer LinkMode = Text1.LinkMode End Property
ActiveX- Steuerelemente und Benutzersteuerelemente
Public Property Let LinkMode(ByVal New_LinkMode As Integer) Text1.LinkMode() = New_LinkMode PropertyChanged "LinkMode" End Property 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,LinkExecute Public Sub LinkExecute(ByVal Command As String) Text1.LinkExecute Command End Sub 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,LinkPoke Public Sub LinkPoke() Text1.LinkPoke End Sub 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,LinkRequest Public Sub LinkRequest() Text1.LinkRequest End Sub 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,LinkSend Public Sub LinkSend() Text1.LinkSend End Sub 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,LinkTimeout Public Property Get LinkTimeout() As Integer LinkTimeout = Text1.LinkTimeout End Property Public Property Let LinkTimeout(ByVal New_LinkTimeout As Integer) Text1.LinkTimeout() = New_LinkTimeout PropertyChanged "LinkTimeout" End Property
63 1
ActiveX- Steuerelemente und Benutzersteuerelemente
ActiveX- Steuerelemente und Benutzersteuerelemente
'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,LinkTopic Public Property Get LinkTopic() As String LinkTopic = Text1.LinkTopic End Property Public Property Let LinkTopic(ByVal New_LinkTopic As String) Text1.LinkTopic() = New_LinkTopic PropertyChanged "LinkTopic" End Property 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,Locked Public Property Get Locked() As Boolean Locked = Text1.Locked End Property Public Property Let Locked(ByVal New_Locked As Boolean) Text1.Locked() = New_Locked PropertyChanged "Locked" End Property 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,MaxLength Public Property Get MaxLength() As Long MaxLength = Text1.MaxLength End Property Public Property Let MaxLength(ByVal New_MaxLength As Long) Text1.MaxLength() = New_MaxLength PropertyChanged "MaxLength" End Property 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,MouseIcon Public Property Get MouseIcon() As Picture Set MouseIcon = Text1.MouseIcon End Property Public Property Set MouseIcon(ByVal New_MouseIcon As Picture) Set Text1.MouseIcon = New_MouseIcon PropertyChanged "MouseIcon" End Property Private Sub Text1_MouseMove(Button As Integer, Shift As Integer, _ X As Single, Y As Single)
63 2
MemoryEdit das Textfeld mit Gedächtnis
RaiseEvent MouseMove(Button, Shift, X, Y) End Sub Private Sub Text1_MouseUp(Button As Integer, Shift As Integer, _ X As Single, Y As Single) RaiseEvent MouseUp(Button, Shift, X, Y) End Sub
ActiveX- Steuerelemente und Benutzersteuerelemente
'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,MousePointer Public Property Get MousePointer() As Integer MousePointer = Text1.MousePointer End Property Public Property Let MousePointer(ByVal New_MousePointer As Integer) Text1.MousePointer() = New_MousePointer PropertyChanged "MousePointer" End Property 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,MultiLine Public Property Get MultiLine() As Boolean MultiLine = Text1.MultiLine End Property Private Sub Text1_OLECompleteDrag(Effect As Long) RaiseEvent OLECompleteDrag(Effect) End Sub 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,OLEDrag Public Sub OLEDrag() Text1.OLEDrag End Sub Private Sub Text1_OLEDragDrop(Data As DataObject, Effect As Long, _ Button As Integer, Shift As Integer, X As Single, Y As Single) RaiseEvent OLEDragDrop(Data, Effect, Button, Shift, X, Y) End Sub 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,OLEDragMode Public Property Get OLEDragMode() As Integer OLEDragMode = Text1.OLEDragMode End Property
633
ActiveX- Steuerelemente und Benutzersteuerelemente
Public Property Let OLEDragMode(ByVal New_OLEDragMode As Integer) Text1.OLEDragMode() = New_OLEDragMode PropertyChanged "OLEDragMode" End Property
ActiveX- Steuerelemente und Benutzersteuerelemente
Private Sub Text1_OLEDragOver(Data As DataObject, Effect As Long, Button As Integer, Shift As Integer, X As Single, Y As Single, _ State As Integer) RaiseEvent OLEDragOver(Data, Effect, Button, Shift, X, Y, State) End Sub 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,OLEDropMode Public Property Get OLEDropMode() As Integer OLEDropMode = Text1.OLEDropMode End Property Public Property Let OLEDropMode(ByVal New_OLEDropMode As Integer) Text1.OLEDropMode() = New_OLEDropMode PropertyChanged "OLEDropMode" End Property Private Sub Text1_OLEGiveFeedback(Effect As Long, _ DefaultCursors As Boolean) RaiseEvent OLEGiveFeedback(Effect, DefaultCursors) End Sub Private Sub Text1_OLESetData(Data As DataObject, DataFormat As Integer) RaiseEvent OLESetData(Data, DataFormat) End Sub Private Sub Text1_OLEStartDrag(Data As DataObject, _ AllowedEffects As Long) RaiseEvent OLEStartDrag(Data, AllowedEffects) End Sub 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,PasswordChar Public Property Get PasswordChar() As String PasswordChar = Text1.PasswordChar End Property Public Property Let PasswordChar(ByVal New_PasswordChar As String) Text1.PasswordChar() = New_PasswordChar PropertyChanged "PasswordChar" End Property
63 4
MemoryEdit das Textfeld mit Gedächtnis
'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,ScrollBars Public Property Get ScrollBars() As Integer ScrollBars = Text1.ScrollBars End Property
ActiveX- Steuerelemente und Benutzersteuerelemente
'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,SelLength Public Property Get SelLength() As Long SelLength = Text1.SelLength End Property Public Property Let SelLength(ByVal New_SelLength As Long) Text1.SelLength() = New_SelLength PropertyChanged "SelLength" End Property 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,SelStart Public Property Get SelStart() As Long SelStart = Text1.SelStart End Property Public Property Let SelStart(ByVal New_SelStart As Long) Text1.SelStart() = New_SelStart PropertyChanged "SelStart" End Property 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,SelText Public Property Get SelText() As String SelText = Text1.SelText End Property Public Property Let SelText(ByVal New_SelText As String) Text1.SelText() = New_SelText PropertyChanged "SelText" End Property 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,Text Public Property Get Text() As String Text = Text1.Text End Property
635
ActiveX- Steuerelemente und Benutzersteuerelemente
ActiveX- Steuerelemente und Benutzersteuerelemente
Public Property Let Text(ByVal New_Text As String) Text1.Text() = New_Text PropertyChanged "Text" End Property 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,ToolTipText Public Property Get ToolTipText() As String ToolTipText = Text1.ToolTipText End Property Public Property Let ToolTipText(ByVal New_ToolTipText As String) Text1.ToolTipText() = New_ToolTipText PropertyChanged "ToolTipText" End Property 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,WhatsThisHelpID Public Property Get WhatsThisHelpID() As Long WhatsThisHelpID = Text1.WhatsThisHelpID End Property Public Property Let WhatsThisHelpID(ByVal New_WhatsThisHelpID As Long) Text1.WhatsThisHelpID() = New_WhatsThisHelpID PropertyChanged "WhatsThisHelpID" End Property 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MappingInfo=Text1,Text1,-1,hWnd Public Property Get hWnd() As Long hWnd = Text1.hWnd End Property Private Sub Text1_Change() RaiseEvent Change Dim SelIdx As Integer Dim ListIdx As Integer ' Bisherige Eingabe in Liste suchen.. ListIdx = SendMessage(List1.hWnd, LB_FINDSTRING, 0, _ ByVal Text1.Text) If ListIdx > -1 Then ' Gefunden? SelIdx = Text1.SelStart ' Cursorpositon merken Text1 = List1.List(ListIdx) ' Listeneintrag übernehmen Text1.SelStart = SelIdx ' Markierung Text1.SelLength = Len(Text1.Text) – SelIdx End If End Sub
63 6
MemoryEdit das Textfeld mit Gedächtnis
Private Sub Text1_Click() RaiseEvent Click End Sub Private Sub Text1_DblClick() RaiseEvent DblClick End Sub
ActiveX- Steuerelemente und Benutzersteuerelemente
Private Sub Text1_KeyUp(KeyCode As Integer, Shift As Integer) RaiseEvent KeyUp(KeyCode, Shift) End Sub Private Sub Text1_KeyPress(KeyAscii As Integer) RaiseEvent KeyPress(KeyAscii) End Sub Private Sub Text1_MouseDown(Button As Integer, Shift As Integer, _ X As Single, Y As Single) RaiseEvent MouseDown(Button, Shift, X, Y) End Sub Private Sub Text1_Validate(Cancel As Boolean) RaiseEvent Validate(Cancel) End Sub 'ACHTUNG! DIE FOLGENDEN KOMMENTIERTEN ZEILEN NICHT ENTFERNEN ODER 'VERÄNDERN! 'MemberInfo=7,0,0,0 Public Property Get MaxRemember() As Integer MaxRemember = m_MaxRemember End Property Public Property Let MaxRemember(ByVal New_MaxRemember As Integer) m_MaxRemember = IIf(New_MaxRemember >= 0, New_MaxRemember, 0) While m_MaxRemember < List1.ListCount ' ggf. Einträge löschen List1.RemoveItem (List1.ListCount – 1) Wend PropertyChanged "MaxRemember" End Property 'Eigenschaften für Benutzersteuerelement initialisieren Private Sub UserControl_InitProperties() m_MaxRemember = m_def_MaxRemember If Not Ambient.UserMode Then Set Text1.Font = Ambient.Font End If Text1 = Ambient.DisplayName End Sub
Abgesehen von einigen »Flüchtigkeitsfehlern« ist das Ergebnis des Assistenten ganz brauchbar. Der Compiler weist Sie der Reihe nach auf die Fehler hin, wenn Sie versuchen, eine Instanz des
637
ActiveX- Steuerelemente und Benutzersteuerelemente
ActiveX- Steuerelemente und Benutzersteuerelemente
MemoryEditX-Steuerelements in das Formular zu platzieren. Als Erstes fällt auf, dass jemand Fleißiges bei der Lokalisierung der Datenbasis des Assistenten ins Deutsche die Konstanten True und False scheinbar »mitlokalisiert« hat. Dem lässt sich entweder durch Nachholen der Definitionen oder durch schlichtes Rückübersetzen entgegenwirken. Als weitere Nachlässigkeit bemängelt der Compiler ungültige Vorgabewerte für die Eigenschaften FontName und FontSize in der Routine UserControl_ReadProperties – während die Eigenschaft Font dagegen korrekt mit Ambient.Font versorgt wurde. Tragen Sie die entsprechenden Eigenschaften des AmbientObjekts als Vorgabe ein: Text1.FontBold = PropBag.ReadProperty("FontBold", Ambient.Font.Bold) Text1.FontItalic = PropBag.ReadProperty("FontItalic",_ Ambient.Font.Italic) Text1.FontName = PropBag.ReadProperty("FontName", Ambient.Font.Name) Text1.FontSize = PropBag.ReadProperty("FontSize", Ambient.Font.Size) Text1.FontStrikethru = PropBag.ReadProperty("FontStrikethru", _ Ambient.Font.Strikethrough) Text1.FontUnderline = PropBag.ReadProperty("FontUnderline", _ Ambient.Font.Underline)
Falls Sie das Programm dafür beendet haben, entfernen Sie das Steuerelement noch einmal vom Formular und fügen es wieder neu ein – ansonsten starten Sie den vom Debugger unterbrochenen Code einfach wieder. Nun müsste sowohl die Entwurfsinstanz als auch die Laufzeitinstanz des Steuerelements problemfrei starten, und wenn Sie einen Blick auf das Eigenschaftsfenster werfen, werden Sie feststellen, dass dieses nun gut gefüllt ist. Gleiches gilt für das Methodenund Ereignisangebot. Zwei Nachbesserungen gibt es jedoch noch: Erstens zeigt das Textfeld nach dem Einfügen nicht in der gewohnten Weise den Namen des Steuerelements plus Instanzzählung an, sondern eben stur »Text1« (das ist kein Wunder, denn das ist der vom Assistenten vergebene Vorgabewert), und zweitens übernimmt das Steuerelement die Vorgaben für die Schrifteinstellungen des aktuellen Containers nicht. Ersteres liegt daran, dass das Textfeld eben seit dem Entwurf »Text1« heißt, was auch dem PropertyBag-Objekt als Standardwert kundgetan wird. Den richtigen Namen der Entwurfsinstanz liefert das Ambient-Objekt über seine Eigenschaft DisplayName und die Schrifteinstellungen über seine Font-Eigenschaft. Der richtige Ort für Initialisierungen dieser Art ist die Behandlungsroutine UserControl_InitProperties. Nehmen Sie die Änderung auch in den Routinen UserControl_ReadProperties und UserControl_WriteProperties vor. ... Text1.Text = PropBag.ReadProperty("Text", Ambient.DisplayName) ... Call PropBag.WriteProperty("Text", Text1.Text, Ambient.DisplayName) ... Private Sub UserControl_InitProperties() m_MaxRemember = m_def_MaxRemember If Not Ambient.UserMode Then Set Text1.Font = Ambient.Font End If Text1 = Name Text1 = Ambient.DisplayName End Sub
Wenn Sie das Steuerelement nun auf einem Formular mit veränderten Schrifteinstellungen platzieren, erhält das Textfeld als Vorgabewert den vom Entwurfseditor für das Steuerelement
63 8
MemoryEdit das Textfeld mit Gedächtnis
automatisch generierten Namen in der Schrift des Formulars. Natürlich darf dies nur zur Entwurfszeit geschehen, Laufzeitinstanzen erhalten ihre Schrifteinstellungen aus dem PropertyBagObjekt, dafür sorgt nicht zuletzt der Zustand der Eigenschaft Ambient.UserMode.
Einige letzte Schönheitsoperationen noch. Das Steuerelement hat noch keine Standardeigenschaft, und falls Sie die Eigenschaft Text im Eigenschaftsfenster mit einem neuen Wert versorgen, werden Sie bemerken, dass der Wert erst dann in das Steuerelement MemoryEditX übernommen wird, wenn das Eingabefeld den Fokus abgibt, nicht bereits während des Tippens, wie Sie es vom Textfeld her gewohnt sind. Auch fehlen MemoryEditX die Eigenschaften für die Datenanbindung und last but not least erscheinen im Eigenschaftsfenster überflüssigerweise die Eigenschaften FontName, FontSize, FontBold, FontItalic, FontStrikethru, FontUnderline, was beim Textfeld nicht der Fall ist. Alle diese Schönheitsfehler lassen sich gewissermaßen in einem Streich – im wahrsten Sinne des Wortes – abhaken, indem Sie die folgenden Schritte nachvollziehen: 1. Schalten Sie in die Entwurfsansicht des MemoryEditX-Steuerelements und öffnen Sie über das Menü EXTRAS das Dialogfeld PROZEDURATTRIBUTE. 2. Stellen Sie im Listenfeld NAME die Eigenschaft Text ein und suchen Sie dafür im Kombinationsfeld PROZEDUR-ID den gleichlautenden Eintrag »Text«. Das sichert die Synchronisation zwischen dem Eigenschaftsfenster und der Entwurfsansicht, hat aber den Nachteil, dass Sie die Eigenschaft nicht mehr als Standardeigenschaft definieren können, denn dafür müssen Sie den Wert »(Voreinstellung)« im Kombinationsfeld PROZEDUR-ID auswählen – Letzteres dürfte wahrscheinlich der »höhere Zweck« sein. Wenn Sie zudem die Kontrollkästchen DATENGEBUNDENE EIGENSCHAFT und AN DATENFELD GEBUNDEN mit einem Häkchen versehen, eröffnen Sie dem Steuerelement die Möglichkeit einer Bindung an eine Datenquelle und zwar wie Sie es vom Textfeld her gewohnt sind: über die Eigenschaften DataField, DataSource, DataMember und DataFormat. Das Kontrollkästchen ZUR ENTWURFSZEIT IN DATABINDINGS-AUFLISTUNG ANZEIGEN beschert dem Steuerelement dagegen eine DataBindingsEigenschaft, die ähnlich wie das Font-Objekt ein Sammelobjekt für die genannten DataXXXEigenschaften darstellt und die Gestaltung der Anbindung im Rahmen eines Eigenschaftsfensters ermöglicht. Es macht wenig Sinn, beide Kontrollkästchen abzuhaken, da die Eigenschaften redundant sind. Über die restlichen beiden Kontrollkästchen regeln Sie das »Wie« der Bindung. 3. Stellen Sie im Listenfeld NAME der Reihe nach die Eigenschaften FontName, FontSize, FontBold, FontItalic, Fontstrikthru, FontUnderline ein und spendieren Sie ihnen je ein Häkchen im Kontrollkästchen IM EIGENSCHAFTENKATALOG NICHT ANZEIGEN. Das hat den Effekt, dass diese Eigenschaften aus dem Eigenschaftsfenster verschwinden, in der vom Code-Editor eingeblendeten Hilfeliste mit den Eigenschaften und Methoden für das Steuerelementobjekt aber noch geführt werden – wie beim Textfeld eben. Falls Sie wollen, dass ein Element weder im Eigenschaftsfenster noch in der Liste erscheint, können Sie es über das Kontrollkästchen DIESES MITGLIED AUSBLENDEN verschwinden lassen. Als geheimgehaltenes Public-Element ist es dann zwar noch ansprechbar, aber eben nicht mehr für jedermann sichtbar (seit Visual Basic 6.0 arbeitet man besser mit Friend-Deklarationen).
639
ActiveX- Steuerelemente und Benutzersteuerelemente
Ausgabe des Testcodes für das Emulationsmodul
ActiveX- Steuerelemente und Benutzersteuerelemente
ActiveX- Steuerelemente und Benutzersteuerelemente
Die Qual der W ahl:
s ofortige Anzeige im
Entwurfsmodus
oder Standardeigensc haft?
Das wäre es gewesen. MemoryEditX lässt sich von nun an nach Belieben anstelle eines Textfelds verwenden und verhält sich auch so, abgesehen von dem zusätzlichen Komfort. Um das Steuerelement zu veröffentlichen, folgen Sie der Anleitung im Abschnitt »Das Steuerelement veröffentlichen« (S. 603).
640
MemoryEdit das Textfeld mit Gedächtnis
ActiveX- Steuerelemente und Benutzersteuerelemente
Die Eigenschaftsfenster des Textfelds und des MemoryEditX-Textfelds im Vergleich
641
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
Eine Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic kann eine große Hilfe für den schnellen Umstieg in die Programmierung mit Visual Basic sein. Wer vergessenes Wissen schnell auffrischen will oder Basic noch von früher her kennt, dem gibt die Tabelle nicht nur einen schnellen Überblick über den sprachspezifischen Befehlsvorrat, sondern auch wichtige Hinweise über einen gegebenenfalls erfolgten Bedeutungswandel. Befehl
QBasic
Visual Basic
$DYNAMIC
Metabefehl, der festlegt, wie Arrays standardmäßig gespeichert werden
Verschwunden (ersatzlos)
$STATIC
Metabefehl für statische Erzeugung von statischen Arrays und Zeichenfolgen
Verschwunden
Abs
Liefert den Absolutwert eines Zahlenwerts.
Unverändert
ABSOLUTE
Ruft maschinensprachliche Routine auf
Verschwunden (Anbindung an DLL-Routinen jetzt mittels Declare)
Access
Gibt die Art der Zugriffsberechti- Unverändert gung beim Öffnen einer Datei an. Zur Auswahl stehen die Zusätze Read, Write oder Read Write
Any
Steht bei der expliziten Deklaration einer Funktion/Prozedur im Rahmen Declare für jeden beliebigen Datentyp
Unverändert, allerdings hat sich die Bedeutung von Declare gewandelt
Append
Zusatz bei der Open-Anweisung, der spezifiziert, dass die Datei im Textmodus mit Dateizeiger am Ende geöffnet wird
Unverändert
Array
Fehlt
Liefert Aufrufparameter als Array
As
Deklariert einen Datentyp für einen Bezeichner
Unverändert
Asc
Liefert den ANSI-Code des ersten Unverändert Zeichens einer Zeichenfolge (auf DBCS-Systemen, DBCS-Code)
AscB
Fehlt (jedoch wie ASC)
Liefert ANSI-Code des ersten Bytes einer Zeichenfolge
AscW
Fehlt
Liefert Unicode des ersten Doppel-Bytes einer Zeichenfolge
Atn
Liefert den Arcustangens einer Zahl als Winkel im Bogenmaß
Unverändert
642
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
QBasic
Visual Basic
Beep
Gibt einen Piepser über den Systemlautsprecher aus
Gibt den Klang »Standardsignal« über den Systemlautsprecher aus
Binary
Zusatz bei der Open-Anweisung, der spezifiziert, dass die Datei im binären Modus geöffnet wird
Unverändert
BLOAD
Lädt eine Binärdatei in einen Speicherbereich
Verschwunden (für Bilddateien ist jetzt LoadPicture zuständig)
BSAVE
Speichert den Inhalt eines Speicherbereichs in eine Datei
Verschwunden (für Bilddateien ist jetzt SavePicture zuständig)
Byte
Fehlt
Datentyp für und Ganzzahlen zwischen 0 und 255
Call
Aufruf einer Prozedur nach der Syntax von Funktionen
Unverändert
CALL ABSOLUTE
Übergabe der Kontrolle an eine maschinensprachliche Routine
Verschwunden (Anbindung an DLL-Routinen jetzt mittels Declare)
Case
Fallunterscheidung in SelectKonstrukten
Unverändert
CDate
Fehlt, da Datentyp Date fehlt
Interpretiert Zeichenfolge oder Zahlenwert als Datums-/Zeitwert
CDbl
Wandelt numerischen Ausdruck in Double um
Unverändert
CHAIN
Übergibt die Kontrolle an ein anderes Basic-Programm
Verschwunden (Aufruf anderer Visual-Basic-Programme jetzt mittels Shell als eigenständige Instanz möglich)
CHAIN
Lädt anderes Programm und übergibt Kontrolle
Verschwunden, lässt sich durch modulare Programmierung ersetzen
ChDir
Ändert aktuelles Arbeitsverzeichnis
Unverändert
ChDrive
Fehlt (nur über SHELL)
Setzt aktuelles logisches Laufwerk
Chr
Liefert ANSI-Zeichen zu ANSICode
Liefert Unicode-Zeichen zu ANSICode
ChrB
Fehlt (jedoch wie Chr)
Liefert ANSI-Zeichen zu ANSICode
ChrW
Fehlt
Liefert Unicode-Zeichen zu Unicode
CInt
Wandelt numerischen Ausdruck in Intger um
Unverändert
Circle
Zeichnet Kreis, Ellipse oder Ausschnitt davon
Unverändert
643
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
Befehl
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
Befehl
QBasic
Visual Basic
CLEAR
Schließt alle Dateien, gibt alle Puffer und initialisiert alle globalen Variablen erneut mit dem Standardwert
Verschwunden (nur noch als Methode verschiedener Objekte zu finden; löscht dann Elemente des Objekts)
CLng
Wandelt numerischen Ausdruck in Long um
Unverändert
Close
Unverändert
Schließt eine oder mehrere zuvor geöffnete Dateien
Cls
Löscht den Bildschirm
Löscht den Client-Bereich des jeweiligen Objekts
Color
Setzt Vordergrund und HinterVerschwunden (Farbraum wurde grundfarbe für die Text- und Gra- verändert, vgl. die Eigenschaften Backcolor, ForeColor und FillCofikausgabe lor)
COM
Steuert Ereignisverfolgung der seriellen Schnittstelle
Verschwunden (Zugriff und Steuerung der seriellen Schnittstellen über MS Comm Control 6.0 möglich)
COMMON
Deklaration globaler Variablen, die von mehreren Modulen gleichermaßen genutzt werden können; erweitert Geltungsbereich von Variablen auf mehrere Programm-Module
Verschwunden; bedingt vergleichbar mit Public
Const
Vereinbarung einer Konstanten
Unverändert
Cos
Liefert den Cosinus zu einem Winkel im Bogenmaß
Unverändert
CSng
Wandelt numerischen Ausdruck in Single um
Unverändert
CSRLIN
Liefert die Zeile, in der sich der Cursor für die Ein-/Ausgabe befindet
Verschwunden (bedingt vergleichbar: CurrentY)
CurDir
Fehlt (nur über SHELL)
Liefert das aktuelle Arbeitsverzeichnis auf dem angegebenen logischen Laufwerk
Currency
Fehlt
Datentyp für Ganzzahl mit Skalierung in 10.000stel
CV...
Typumwandlungen für datensatz- Bedingt vergleichbar mit impliziorientierte Dateioperationen ter Funktionalität von LSet
CVD, CVDMBF, CVI, CVL, CVS, CVSMBF
Konvertierungsfunktionen für die Verschwunden (DatensatzoperatiEntnahme von Daten im Zusam- onen sind jetzt direkt mit TypeDatentypen möglich) menhang mit der Field-Anweisung
644
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
QBasic
Visual Basic
DATA
Definiert Daten für das Einlesen mit READ
Verschwunden (ersatzlos)
Date (Datentyp)
Fehlt
Datentyp für Datums- und Zeitangaben
Date (Funktion)
Fehlt, da Datentyp Date fehlt. Via Liefert das aktuelle Systemdatum Zeichenfolge, siehe DATE$ als Datums-/Zeitwert
Date (Anweisung)
Fehlt, da Datentyp Date fehlt. Via Setzt Datumsanteil von Datums-/ Zeichenfolge, siehe DATE$ Zeitwert als aktuelles Systemdatum
DATE$
Liefert oder setzt das aktuelle Sys- Verschwunden temdatum via Zeichenfolge
DateSerial
Fehlt
Setzt Datums-/Zeitwert aus Ganzzahlwerten (Tag, Monat und Jahr) zusammen
DateValue
Fehlt
Interpretiert Zeichenfolge oder Zahlenwert als Datumsanteil von Datums-/Zeitwert
Day
Fehlt
Liefert Tag in einem Datums-/ Zeitwert als Zahlenwert (1 – 31).
DDB
Fehlt
Liefert den Abschreibungswert eines Vermögenswerts über einen bestimmten Zeitraum bei geometrisch degressiver Abschreibung
Decimal
Fehlt
Datentyp für 29-stellige Dezimalzahl ohne Exponent, aber mit Komma
Declare
Deklariert BASIC-Funktionen und Prozeduren in einem Modul
Verändert – deklariert Funktionen und Prozeduren für den Import aus einer DLL
DEF FN
Definiert Funktion, deren Bezeichner einen FN-Präfix trägt
Verschwunden, bedingt vergleichbar mit Function
DEF SEG
Definiert aktuelle Segmentadresse Verschwunden (ersatzlos)
DefType
Einem Bezeichnerbereich den Datentyp Type für implizite Deklaration zuordnen.
Dim
Explizite Deklaration von Arrays Variablendeklaration auf Modulmit mehr als zehn Elementen und und Prozedurebene. Auf Moduleexplizite Typdeklaration für Vari- bene durch Public zu ersetzen ablen auf Hauptmodul- und Prozedurebene
Dir
Fehlt (nur über SHELL)
Liefert den nächsten Dateinamen, der auf Suchmuster passt
Do...Loop
Allgemeine Schleife
Unverändert
Unverändert
645
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
Befehl
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
Befehl
QBasic
Visual Basic
Double
Unverändert
Datentyp für Fließkommazahlen doppelter Genauigkeit
DRAW
Interpretiert Zeichenfolge Anwei- Verschwunden (ersatzlos) sungsfolge für Zeichenbefehle
End
Beendet Programm ordentlich unter Ausführung von Clear
Beendet Programm unordentlich (lässt beispielsweise Formulare stehen)
Environ, Environ$
ENVIRON$ liefert eine oder alle Umgebungsvariablen, ENVIRON setzt Umgebungsvariable
Unverändert
EOF
Testet, ob Dateiende erreicht ist
Unverändert
Eqv
Äquivalenz-Operator
Unverändert
Erase
Reinitialisiert statisches Array oder gibt Speicher für dynamisches Array frei
Unverändert
ERDEV, ERDEF$
Liefert letzten Fehlercode, den ein Verschwunden (bedingt mit ErrGerät ERDEF$ produziert hat Objekt vergleichbar)
ERL
Liefert Zeile des letzten Laufzeitfehlers, wenn Zeilennummern vorhanden
Verschwunden (ersatzlos)
ERR
Liefert Fehlercode des letzten Laufzeitfehlers
Verschwunden (bedingt mit ErrObjekt vergleichbar)
Error
Löst Laufzeitfehler aus
Unverändert
Exit
Beendet DEF-, Do-, For-, Functionund Sub-Konstrukte
Beendet Do-, For-, For EachFunction-, Property- und Sub-Konstrukte
Exp
Liefert den Wert der Exponential- Unverändert funktion (mit Basis e) zu einer Zahl.
FIELD
Reserviert Speicher für Datensatz Verschwunden, jede String-Variin indexsequenzielle Datei able kann Puffer sein; Datensatzoperationen sind auch direkt mit Type-Datentypen möglich
FileAttr
Ermittelt den Öffnungsmodus einer Datei
Unverändert
FileCopy
Fehlt (nur via SHELL)
Kopiert eine Datei
FileDateTime
Fehlt (nur via SHELL)
Liefert den Zeitpunkt der Erstellung bzw. letzten Änderung einer Datei als Datums-/Zeitwert
FileLen
Fehlt (nur via SHELL)
Liefert die Länge einer Datei in Bytes
646
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
QBasic
Visual Basic
FILES
Listet Inhalt von Verzeichnis CHDIR$ auf
Verschwunden (bedingt vergleichbar mit FileListBox-Steuerelement)
Filter
Fehlt
Durchsucht String-Array nach Zeichenfolge und liefert die Array-Elemente, in denen die Zeichenfolge enthalten ist.
Fix
Liefert den Vorkommaanteil einer Unverändert Dezimalzahl als Ganzzahl.
For...Next
Zählschleife
Unverändert
Format
Fehlt, bedingt vergleichbar mit USING
Liefert Zeichenfolgendarstellung eines Werts im spezifizierten Format
FormatCurreny
Fehlt
Liefert Darstellung eines Währungswerts im spezifizierten Format
FormatDateTime
Fehlt
Liefert Darstellung eines Datums-/ Zeitwerts als Zeichenfolge im spezifizierten Format
FormatDateTime
Fehlt
Liefert Darstellung eines Datumswerts im spezifizierten Format
FormatNumber
Fehlt
Liefert Darstellung eines Zahlenwerts im spezifizierten Format
FormatPercent
Fehlt
Liefert Darstellung eines Prozentwerts im spezifizierten Format
FRE
Liefert Größe des verfügbaren Freispeichers
Verschwunden (ersatzlos)
FreeFile
Liefert die nächste freie Dateinummer
Liefert die nächste freie Dateinummer
Function
Definiert Funktion
Unverändert (Geltungsbereichsvereinbarungen für Variablen verändert)
Function...End Function
Definiert Funktion
Wie gehabt, jedoch mit zusätzlichen Möglichkeiten bei der Parametervereinbarung und anderen Geltungsbereichen
FV
Fehlt
Liefert den künftigen Wert einer Annuität (Ansparung oder Kredit) bei konstanter Zahlung, konstantem Zins und fester Laufzeit
Get
Liest Daten aus einer indexseUnverändert quenziellen Datei oder Binärdatei
647
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
Befehl
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
Befehl
QBasic
Visual Basic
GET
Schnelles Kopieren von Grafikausschnitten
Verschwunden (bedingt vergleichbar mit PaintPicture)
GetAttr
Fehlt (nur via SHELL)
Liefert die Attribute einer Datei oder eines Ordners bzw. Verzeichnisses
GoSub
Verzweigt in Subroutine, Rücksprung mit Return
Unverändert
GoSub...Return
Unterprogrammaufruf
Unverändert
GoTo
Setzt Programmausführung an der über Zeilennummer oder Sprungmarke definierten Anweisung fort
Unverändert
GoTo
Unbedingter Sprung
Unverändert
Hex, Hex$
Liefert die hexadezimale Notation eines Werts
Unverändert
Hour
Fehlt
Liefert Stunde in einem Datums-/ Zeitwert als Zahlenwert (0 bis 23)
If...Then...Else
Konstrukt für die bedingte Ausführung von Programmcode
Unverändert
Imp
Implikationsoperator
Unverändert
INKEY$
Liefert nächstes Zeichen aus dem Tastaturpuffer
Verschwunden (KeyXXX-Ereignisse bedingt vergleichbar)
INP
Liest Port aus
Verschwunden
INPUT
Liest den Wert einer oder mehrerer Variablen aus einer Textdatei
Verschwunden (Tastatureingabe jetzt via Steuerelemente und Ereignisroutinen)
Input #
Liest den Wert einer oder mehrerer Variablen aus einer Textdatei
Unverändert
Input, Input$
Liest die angegebene Anzahl an Zeichen aus einer Datei und liefert diese als ANSI-Zeichenfolge
Liest die angegebene Anzahl an Zeichen aus einer Datei und liefert diese als Unicode-Zeichenfolge
InputB
Fehlt (jedoch wie INPUT$)
Liest die angegebene Anzahl an Bytes aus einer Datei und liefert diese als Byte-Array (ANSI-Zeichenfolge).
InStr, InStr$
Liefert Startposition einer Zeichenfolge in einer anderen Zeichenfolge
Unverändert
InStrB
Fehlt (jedoch wie INSTR)
Liefert Startposition einer ANSIZeichenfolge in einer anderen ANSI-Zeichenfolge
648
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
QBasic
Visual Basic
InStrRev
Fehlt
Wie InStr, beginnt die Suche aber von hinten
Int
Liefert den nächsten ganzzahligen Unverändert Wert kleiner oder gleich einer Zahl
Integer
Datentyp für Ganzzahlen zwischen -32.768 bis 32.767
Unverändert
IOCTL, IOCTL$
Überträgt Steuerzeichenfolge an Gerätetreiber IOCTL$
Verschwunden (ersatzlos)
IPmt
Fehlt
Liefert den Zinsanteil für eine bestimmte Periode bei Abzahlung oder Ansparung mit konstanten Raten und konstantem Zinssatz
IRR
Fehlt
Liefert internen Ertragssatz für eine Folge regelmäßiger Ein- und Auszahlungen
Is
Vergleichsoperator für Select Case-Ausdrücke
Vergleichsoperator für Select Case-Ausdrücke und Objektreferenzen
IsArray
Fehlt
Testet, ob Variablenwert ein Array ist
Join
Fehlt
Setzt alle Zeichenfolgen eines Arrays zu einer einzigen Zeichenfolge zusammen (Trennzeichen optional); Umkehrfunktion zu Split
KEY
Steuert Ereignisverfolgung für Tastatureingabe
Verschwunden (bedingt vergleichbar mit KeyXXX-Ereignissen)
Kill
Löscht Dateien, die auf das ange- Unverändert gebene Suchmuster passen
LBound
Liefert unter Indexgrenze für eine Unverändert Array-Dimension
LCase, LCase$
Übersetzt Zeichenfolge in Kleinschreibung
Unverändert
Left, Left$
Liefert linken Teil einer Zeichenfolge
Unverändert
LeftB
Fehlt (jedoch wie LEFT$)
Liefert linken Teil einer ANSI-Zeichenfolge
649
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
Befehl
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
Befehl
QBasic
Len
Liefert Anzahl der Zeichen in Zei- Unverändert chenfolge. Trägt der Parameter nicht den Typ String, liefert die Funktion die Anzahl der zum Speichern dieses Werts erforderlichen Bytes
LenB
Fehlt (jedoch wie LEN)
Line Input #
Liest eine Zeile aus einer geöffne- Unverändert ten sequenziellen Datei in eine Variable vom Typ String ein
Loc
Liefert die aktuelle Schreib- bzw. Leseposition einer geöffneten Datei
LOCATE
Setzt die Position für die Textein- Verschwunden (vgl. die Eigenund -ausgabe schaften CurrentX und CurrentY des Formulars)
Lock
Regelt die Zugriffsmöglichkeiten anderer Programm auf eine bereits geöffnete Datei
Unverändert
LOF
Liefert die Länge einer Datei in Bytes
Unverändert
Log
Liefert den natürlichen Logarithmus einer Zahl
Unverändert
Long
Datentyp für lange Ganzzahlen zwischen -2.147.483.648 und 2.147.483.647
Unverändert
LPOS
Liefert die Anzahl der Zeichen, die seit dem letzten Wagenrücklauf an den Drucker gesendet wurden
Verschwunden (vgl. die Eigenschaften CurrentX und CurrentY des Printer-Objekts)
LPRINT
Unformatierte Ausgabe auf Druckerschnittstelle LPT
Verschwunden (vgl. PrinterObjekt)
LPRINT USING
Formatierte Ausgabe auf Druckerschnittstelle LPT
Verschwunden (vgl. PrinterObjekt)
LSet
Unverändert Byteorientierte, linksbündige Zuweisungsoperation für beliebige Datentypen, ohne Längenanpassung des Linkswerts; Linkswert ist Zeichenfolgenvariable oder benutzerdefinierter Typ
650
Visual Basic
Liefert Anzahl der Bytes, die für die Darstellung des Werts (zum Beispiel bei Speichern) erforderlich ist. Liefert damit auch die Länge einer ANSI-Zeichenfolge
Unverändert
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
QBasic
Visual Basic
LTrim, LTrim$
Liefert Zeichenfolge ohne führende Leerzeichen zurück
Unverändert
Mid
Zuweisungsoperation für mittleren Teil einer ANSI-Zeichenfolge
Zuweisungsoperation für mittleren Teil einer Unicode-Zeichenfolge
Mid, Mid$
Liefert mittleren Teil einer Zeichenfolge
Unverändert
MidB
Fehlt (jedoch wie MID$)
Liefert mittleren Teil einer Zeichenfolge als ANSI-Zeichenfolge
MidB
Fehlt (jedoch wie MID)
Zuweisungsoperation für mittleren Teil einer ANSI-Zeichenfolge
Minute
Fehlt
Liefert Minute in einem Datums-/ Zeitwert als Zahlenwert (0 bis 59)
MIRR
Fehlt
Liefert den modifizierten internen Ertragssatz für eine Folge regelmäßiger, mit unterschiedlichen Zinssätzen behafteten Ein- und Auszahlungen
MkDir
Generiert ein neues Verzeichnis
Unverändert
MKD, MKI, MKL, MKS, MKSMBF, MKDMBF
Byteorientierte Typumwandlung Wird nun von LSet übernommen numerischer Werte in Zeichenfolgen
Mod
Operator für Restwertdivision
Unverändert
Month
Fehlt
Liefert Monat in einem Datums-/ Zeitwert als Zahlenwert (1-12)
MonthName
Fehlt
Liefert Monatsname als Zeichenfolge für Zahlenwert zwischen 1 und 12
Name
Unverändert (jedoch Verschieben Benennt eine Datei um oder verschiebt diese in ein anderes Verauf logisches Laufwerk zeichnis beschränkt)
Now
Fehlt
Liefert Systemdatum und -zeit als Datums-/Zeitwert
Not
Operator für logische und bitweise Negation
Unverändert
NPer
Fehlt
Liefert die Anzahl der Zeiträume für eine Annuität (Ansparung oder Kredit) bei konstanter Zahlung und konstantem Zins
NPV
Fehlt
Liefert den Netto-Barwert einer Investition bei regelmäßigen Ausund Einzahlungen und konstantem Diskontsatz
651
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
Befehl
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
Befehl
QBasic
Visual Basic
Object
Fehlt
Datentyp für Objektreferenz
Oct, Oct$
Liefert die oktale Notation eines Werts
Unverändert
On...GoSub
Fallunterscheidung mit bedingtem Unterprogrammaufruf
Unverändert
On...GoTo
Fallunterscheidung mit bedingtem Sprung, Fehlerbehandlung
Unverändert
ON COM, ON KEY, ON PEN, ON PLAY, ON STRIG, ON TIMER
Installiert Ereignisbehandlung für Verschwunden; Gerätezugriff nun Gerät über Steuerelemente
Open
Öffnet eine Datei zum Lesen oder Unverändert Schreiben
Option Base
Setzt untere Indexgrenze für Arrays auf 0 oder 1
Unverändert
Option Compare
Fehlt
Standardvorgabe für Zeichenfolgenvergleiche auf binären Vergleich oder Textvergleich (ohne Beachtung der Großschreibung) setzen
Or
Operator für logische und bitweise ODER-Operation
Unverändert
OUT
Schreibt Wert in Port
Verschwunden
PAINT
Füllt einen Grafikbereich mit einer Farbe aus
Verschwunden; vgl. FillColorEigenschaft
PALETTE
Wechselt die Farbzuweisungen der Farbattribute für den aktuellen Bildschirmmodus
Verschwunden; vgl. PaletteEigenschaft
PCOPY
Kopiert eine Seite des Video-Spei- Ersatzlos verschwunden chers in eine andere
PEEK
Liefert das unter einer Speichera- Ersatzlos verschwunden dresse im Hauptspeicher befindliche Datenbyte
PEN
Liefert/setzt Einstellungen des Lichtgriffels
Ersatzlos verschwunden
PLAY (Anweisung)
Spielt Noten ab
Ersatzlos verschwunden
PLAY (Funktion)
Liefert Anzahl der Noten in War- Ersatzlos verschwunden teschlange
PMAP
Rechnet Fensterkoordinaten in WINDOW-Koordinaten um
652
Verschwunden (bedingt vergleichbar mit ScaleX, ScaleY)
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
QBasic
Visual Basic
Point
Liefert die Koordinaten des grafi- Als solche verschwunden; Koordischen Cursors oder den Farbwert naten der Grafikausgabe lassen eines Bildpunkts sich über die Eigenschaften CurrentX und CurrentY ermitteln, der Farbwert eines Bildpunkts im Ausgabebereich eines Objekts über die Methode Point
POKE
Schreibt Datenbyte an der spezifi- Ersatzlos verschwunden zierten Adresse in den Hauptspeicher
POS
Liefert aktuelle Spalte des Textcursors
Ersatzlos verschwunden; Koordinaten der Grafikausgabe (gelten auch für Print) lassen sich über die Eigenschaften CurrentX und CurrentY ermitteln
PPmt
Fehlt
Liefert den Kapitalanteil (bei Kredit: Tilgung) eines bestimmten Zeitraums für eine Annuität (Ansparung oder Kredit) mit einer festen Anzahl von Zeiträumen, konstanter Zahlung und konstantem Zins
PRESET
Ausgabe eines Bildpunkts (standardmäßig in Hintergrundfarbe)
Verschwunden; nur noch als PSetMethode vorhanden
Print
Gibt Text zeilenorientiert auf Bildschirm aus, mit Umbruch am Zeilenende
Gibt Text zeilenorientiert in Fenster aus, ohne Umbruch am Zeilenende
Print #
Schreibt Daten im gebietsspezifischen Format in sequenzielle Datei
Unverändert
Private
Entspricht DIM
Deklaration lokaler Variablen auf Modulebene; entspricht Dim
PSet
Ausgabe eines Bildpunkts
Jetzt als Methode von Objekt mit Ausgabebereich; Farbmodell ist verändert
Public
Bedingt vergleichbar mit COMMON
Deklaration globaler Variablen auf Modulebene
Put
Schreibt Daten in eine indexsequenzielle Datei oder Binärdatei
Unverändert
PV
Fehlt
Liefert den Barwert für eine Annuität (Ansparung oder Kredit) bei konstanter Zahlung, konstantem Zins und fester Laufzeit
653
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
Befehl
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
Befehl
QBasic
Visual Basic
Randomize
Initialisiert den Zufallsgenerator mit einem Startwert
Unverändert
Rate
Fehlt
Liefert den Zinssatz zu einer Annuität (Anzahlung oder Kredit) mit einer festen Anzahl von Zeiträumen, konstanter Zahlung und konstantem Zins
READ
Liest das nächste Datum aus einer Verschwunden Data-Zeile
ReDim
Ändert Dimensionen und Indexbereiche dynamischer Arrays
Unverändert
Rem
Anweisung wird als Kommentar behandelt
Unverändert
Replace
Fehlt
Sucht Vorkommen einer Suchzeichenfolge in Zeichenfolge, ersetzt diese gegen Ersatzzeichenfolge und liefert resultierende Zeichenfolge
Reset
Schließt alle geöffneten Dateien
Unverändert
RESTORE
Legt aktuelle DATA-Zeile fest
Verschwunden
Resume
Rücksprung aus Fehlerbehandlungsroutine
Unverändert
Return
Rücksprung aus GoSub-Routine
Unverändert
Right, Right$
Liefert rechten Teil einer ANSIZeichenfolge
Liefert rechten Teil einer UnicodeZeichenfolge
RightB
Fehlt (eigentlich Right$)
Liefert rechten Teil einer ANSIZeichenfolge
RmDir
Löscht ein Verzeichnis
Unverändert
Rnd
Liefert eine gleichverteilte Zufallszahl zwischen 0 und 1
Unverändert
Round
Liefert den Wert einer Zahl auf eine bestimmte Dezimalstelle gerundet
Unverändert
RSet
Rechtsbündige Zuweisungsopera- Unverändert tion für Zeichenfolgen ohne Längenänderung des Linkswerts. Füllt mit Leerzeichen auf oder schneidet ab
RTrim, RTrim$
Liefert Zeichenfolge ohne Unverändert abschließende Leerzeichen zurück
654
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
QBasic
Visual Basic
RUN
Startet anderes Basic-Programm und übergibt diesem die Kontrolle
Verschwunden (vgl. Shell)
SCREEN (Funktion)
Liefert das Farbattribut des Zeichens, das an der angegebenen Position im Textbildschirm dargestellt wird
Verschwunden (ersatzlos)
SCREEN (Anweisung)
Schaltet zwischen verschiedenen Bildschirmmodi und -seiten um
Verschwunden (ersatzlos)
Second
Fehlt
Liefert Sekunde in einem Datums-/ Zeitwert als Zahlenwert (0 bis 59)
Seek
Liefert/setzt die aktuelle Schreiboder Leseposition in einer geöffneten Datei
Unverändert
Select Case
Fallunterscheidung
Unverändert
SetAttr
Fehlt
Setzt ein oder mehrere Attribute für eine Datei oder ein Verzeichnis
Sgn
Liefert das Vorzeichen eines Werts als Faktor (-1, 0 oder 1)
Unverändert
SHARED
Macht Variablen des Hauptprogramms in Funktionen und Prozeduren sichtbar
Bedingt vergleichbar mit Private
Shell
Führt eine Kommandozeile im DOS-Kommandointerpreter aus
Startet eine andere Anwendung und liefert deren Task-ID als Funktionswert
Sin
Liefert den Sinus zu einem Winkel Unverändert im Bogenmaß
Single
Datentyp für Fließkommazahlen einfacher Genauigkeit
SLEEP
Unterbricht die Programmausfüh- Verschwunden (vgl. Zeitgeberrung für die angebebene Anzahl steuerelement) an Sekunden
SLN
Fehlt
SOUND
Verschwunden Erzeugt einen Ton mit einer frei wählbaren Frequenz im Lautsprecher
Space, Space$
Unverändert Liefert Zeichenfolge mit der gewünschten Anzahl von Leerzeichen
Unverändert
Liefert den periodischen Abschreibungswert eines Vermögenswerts bei linearer Abschreibung über einen bestimmten Zeitraum
655
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
Befehl
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
Befehl
QBasic
Visual Basic
Spc
Fügt Leerzeichen in die PrintAusgabe ein
Unverändert
Split
Fehlt
Durchsucht Zeichenfolge nach Trennzeichen und liefert die Zeichenfolgenabschnitte als Array; Umkehrfunktion zu Join
Sqr
Liefert die Quadratwurzel eines positiven Zahlenwerts
Unverändert
Static
Deklaration statischer Variablen auf Prozedurebene
Deklaration statischer Variablen auf Prozedurebene sowie statischer Funktionen und Prozeduren. Alle lokalen Variablen statischer Prozeduren und Funktion sind statisch
$Static
Metabefehl für Speicherung von Arrays
Verschwunden
Stop
Unterbricht die Programmausfüh- Unverändert rung (wie Haltepunkt im Debugger)
Str, Str$
Liefert standardmäßige Repräsen- Liefert standardmäßige Repräsentation eines Zahlenwerts als tation eines Zahlenwerts als UniANSI-Zeichenfolge code-Zeichenfolge
StrComp
Fehlt (ersatzweise: <-Operator)
Führt alphanumerischen Zeichenfolgenvergleich durch und unterscheidet dabei vier Fälle
StrConv
Fehlt
Konvertiert Zeichenfolge nach einem von sieben möglichen Konvertierungsschemata und liefert konvertierte Version
String (Datentyp)
Datentyp für Zeichenfolgen vari- Datentyp für Zeichenfolgen variabler oder fester Länge ohne Uni- abler oder fester Länge mit Unicode-Unterstützung code
String (Funktion)
Fehlt als Funktion
Liefert Zeichenfolge mit der gewünschten Anzahl eines einzelnen Zeichencodes
StrReverse
Fehlt
Liefert Zeichenfolge mit rückwärts verkehrter Zeichenfolge
Sub...End Sub
Definiert Prozedur
Wie gehabt, jedoch mit zusätzlichen Möglichkeiten bei der Parametervereinbarung und anderen Geltungsbereichen
SWAP
Tauscht die Werte zweier Variab- Verschwunden (ersatzlos) len
656
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
QBasic
Visual Basic
SYD
Fehlt
Liefert den Abschreibungswert eines Vermögenswerts für eine bestimmte Periode nach dem Modell der Jahresummengewichtung
SYSTEM
Schließt alle geöffneten Dateien und gibt die Kontrolle an das Betriebssystem zurück
Verschwunden (ersatzlos)
Tab
Ermöglicht die Positionierung eines Werts an eine bestimme Spaltenposition; nur in PrintAnweisung zulässig
Unverändert
Tan
Liefert den Tangens zu einem Winkel im Bogenmaß
Unverändert
Time
Fehlt, da Datentyp Date fehlt. Via Setzt/liefert die aktuelle Systemzeit Zeichenfolge, siehe Time$. als Datums-/Zeitwert.
TIME$
Liefert bzw. setzt die aktuelle Sys- Verschwunden temzeit via Zeichenfolge
Timer
Liefert den Wert des SystemTimers (Sekunden seit Mitternacht)
Unverändert
TIMER ON/OFF/STOP
Steuert Timer-Interrupt
Fehlt (jetzt via Zeitgeber-Steuerelement Timer)
TimeSerial
Fehlt
Setzt Datums-/Zeitwert aus Ganzzahlwerten (Sekunde, Minute, Stunde) zusammen
TimeValue
Fehlt
Interpretiert Zeichenfolge oder Zahlenwert als Zeitanteil von Datums-/Zeitwert
Trim
Fehlt (ersatzweise: RTRIM$ und LTRIM$)
Liefert Zeichenfolge ohne führende und abschließende Leerzeichen
TRON, TROFF1
Schalten die Verfolgung von Pro- Verschwunden (vgl. Debuggrammanweisungen ein und aus Objekt)
Type...End Type
Definition eines benutzerdefinier- Wie gehabt, jedoch mit erweiterten Datentypen ten Möglichkeiten
UBound
Liefert obere Indexgrenze für eine Unverändert Array-Dimension
UCase, UCase$
Übersetzt Zeichenfolge in Großschreibung
Unverändert
Unlock
Regelt die Zugriffsmöglichkeiten anderer Programme auf eine bereits geöffnete Datei
Unverändert
657
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
Befehl
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
Kurzreferenz der Schlüsselwörter mit Vergleich zu QBasic
Befehl
QBasic
Visual Basic
USING
Formatierte Print-Ausgabe
Verschwunden; bedingt vergleichbar mit Format-Funktion
Val
Liefert den Zahlenwert einer Zei- Unverändert chenfolge
Variant
Fehlt
Allgemeiner Datentyp, der Werte aller Datentypen annehmen kann
VARPTR, VARSEG
Liefert die Offset- und Segmentadresse einer Variablen
Ersatzlos verschwunden
VIEW
Definert einen Fensterbereich (Teilbereich) für die Grafikausgabe
Ersatzlos verschwunden; bedingt vergleichbar mit Bildfeld-Steuerelement, das auf Formular platziert wird
WAIT
Wartet, bis an Port ein bestimmtes Bitmuster anliegt
Ersatzlos verschwunden
WeekDay
Fehlt
Liefert Wochentag in einem Datums-/Zeitwert als Zahlenwert (1-7)
WeekDayName
Fehlt
Liefert Wochentag als Zeichenfolge für Zahlenwert zwischen 1 und 7
While...Wend
Abweisende Schleife
Unverändert
WIDTH
Setzt eine maximale Zeilenlänge für die Bildschirmausgabe
Verschwunden
WIDTH #
Setzt eine maximale Zeilenlänge für eine sequenzielle Datei
Verschwunden
WINDOW
Legt ein Koordinatensystem für die Grafikausgabe fest
Verschwunden; bedingt vergleichbar mit Scale-Methode
Write
Schreibt Werte in standardisiertem Format auf den Bildschirm
Verschwunden
Write #
Schreibt Werte in standardisiertem Format in eine sequenzielle Datei
Unverändert
Xor
Operator für logische und bitweise Exclusiv-Oder-Operation
Unverändert
Year
Fehlt
Liefert Jahr in einem Datums-/ Zeitwert als Zahlenwert
658
Tabellenindex
659
Tabellenindex
Funktion / Befehl ! $ ' (Operator) ^ (Operator) _ (Operator) A Abbildungsliste-Steuerelement (ImageList) Abs-Funktion abstrakter Datentyp Access Activate-Ereignis ActiveControl-Eigenschaft ActiveX-DLL ActiveX-EXE ActiveX-Steuerelemente Add-Methode AddressOf Adodc-Steuerelement Aktivierreihenfolge Algorithmus Alignment-Eigenschaft Ambient-Eigenschaft AmbientProperties-Objekt And Anführungszeichen als literales Zeichen Animation-Steuerelement anonyme Instanziierung Ansicht – Bildansicht – Textansicht ANSI-Code Anweisung Anzeige-Steuerelement (Image) Apfelmann API-Viewer AppActivate-Methode Appearance-Eigenschaft App-Objekt Arbeitsverzeichnis – aktuelles – standardmäßiges
660
Referenz 63 33 54 33 435, 436 94 150 208, 212 308 274 274 322, 433, 440 304 186 340 375
Praxis
484
487 567
599
477 327
54 29 435
601 601 503
525 511, 545 551 63 33 388, 390
158, 224 328 264 131, 134 134
532, 606 494 616
Tabellenindex
Funktion / Befehl
Referenz
arithmetische Operatoren Arrays AscB-Funktion Asc-Funktion AscW-Funktion Atn-Funktion AufAb-Steuerelement Auflistungen Aufzählungen Ausdruck Ausgabeparameter Automatisierungsobjekt – aufrufen Automatisierungsschnittstelle AutoRedraw-Eigenschaft AutoSize-Eigenschaft B BackColor-Eigenschaft BackStyle-Eigenschaft bedingte Verzweigung Befehl Befehlsschaltfläche-Steuerelement (CommandButton) Benannte Argumente benutzerdefiniertes Koordinatensystem Benutzerschnittstelle Benutzersteuerelement – veröffentlichen Benutzersteuerelementmodul Bezeichner Bezeichnerbereich Bezeichnungsfeld-Steuerelement (Label) Bildansicht Bildausschnitt-Steuerelement (PictureClip) Bildfeld-Steuerelement (PictureBox) Bildlaufleisten Bildlaufleisten-Steuerelemente (HScrollBar, VScrollBar) Bildschirmmodus Binärdatei Binary
54 56 66 66 66 95 435 304 62 27, 29 178 269 273 212, 308 329
Praxis 488, 529 560 469 579 509
574
488
330 331 37 27, 33
460, 562 607
388, 392 176 370
497, 562
26 34, 161 167 388, 393 442 388, 395
388, 398 300 130 150
461 542 599 603 567
495 511, 545 608 562, 606 544 545, 551 557, 584 557
661
Tabellenindex
Funktion / Befehl
Referenz
bitweise Operatoren Boolean BorderStyle-Eigenschaft BSTR Bubble-Sort-Algorithmus ByRef Byte ByVal C Calendar-Eigenschaft CallByName-Methode Caption-Eigenschaft Case CausesValidation-Eigenschaft CBool-Funktion CByte-Funktion CCur-Funktion CDate-Funktion CDbl-Funktion CDec-Funktion Change-Ereignis ChDir-Anweisung ChDrive-Anweisung CheckBox-Steuerelement Choose ChrB-Funktion Chr-Funktion ChrW-Funktion CInt-Funktion Circle-Methode ClassModul-Schnittstelle Click-Ereignis Clipboard-Objekt ClipControls-Eigenschaft CLng-Funktion Close-Anweisung Cls-Methode Collection-Objekt ComboBox-Steuerelement CommandButton-Steuerelement Command-Methode
54 49, 52 332 189 184 178 49, 52 178
662
261 262, 269 334 38 223, 335 57 57 57 57, 116 57 57 214 131 132 387, 392, 409 38 67 67 67 57 311 320 208, 215 270 308 58 133 311 304 387, 407 387 262, 273
Praxis 527 457, 525, 537
534
484, 526 542
551, 558, 619
469, 560 460 585 567 489, 542
584 557, 598 487, 488
497, 562
Tabellenindex
Funktion / Befehl
Referenz
Common Controls CommonDialog-Steuerelement
433
COM-Objekte, frühe vs. späte Bindung Compiler Compileranweisung Container-Eigenschaft ControlBox-Eigenschaft Controls-Auflistung CoolBar-Steuerelement Cos-Funktion Count-Eigenschaft CreateObject-Methode CSng-Funktion CStr-Funktion CurDir-Funktion Currency – Funktionen und Anweisungen CurrentX-Eigenschaft CurrentY-Eigenschaft CVar-Funktion CVDate-Funktion CVErr-Funktion D Data Access Objects DataChanged-Eigenschaft Data-Environment-Designer DataEnvironment-Komponenten DataField-Eigenschaft DataFormat-Eigenschaft DataMember-Eigenschaft DataObject-Objekt Data-Report-Designer DataSource-Eigenschaft Data-Steuerelement Date – Funktionen und Anweisungen DateAdd-Funktion Date-Anweisung DateDiff-Funktion Date-Funktion
444 269 23 33 335 308 304, 308 435 95 305 262, 273 58 58 134, 431 49, 52 92 308 308 58 58 44 404 336 26 340 336 338 339 26 339, 405 340, 387 49 114 116 116 117 116
Praxis 489, 527, 551, 562 458, 574
537
469 506, 514 506 584
461 461
639 639 639 501 639
600
663
Tabellenindex
Funktion / Befehl
Referenz
Praxis
Dateilistenfeld-Steuerelement (FileListBox) Dateinummer Dateizeiger Dateizugriff, Funktionen und Anweisungen Datenquelle Datensteuerelement (Data) Datentypen, einfache vs. komplexe DatePart-Funktion DateSerial-Funktion DateTimePicker-Steuerelement DateValue-Funktion Day-Funktion DBCS (Double-Byte Character Set) DblClick-Ereignis DCOM DDB-Funktion DDE Deactivate-Ereignis Decimal – Funktionen und Anweisungen Declare DefBool DefByte DefCur DefDate DefDbl DefDec DefInt DefLng DefObj DefSng DefStr DefTyp DefVar Delegation DeleteSetting-Methode Designermodul dezimale Literale DHTML-Designer Dim Direktfenster, Ausgeben in
388, 400 149 149 126 339 388, 402 161 118 119 435 120 121 63 208, 217 196 96
510 584
664
639 572
495 208, 212 49, 53 92 185 167 167 167 167 167 167 167 167 167 167 167 167 167 262, 275 26 29 26 162, 196
455
525 562
456 454
Tabellenindex
Funktion / Befehl
Referenz
Direkthilfe Dir-Funktion DirListBox-Steuerelement DisabledPicture-Eigenschaft DLLs, Anbindung an Do...Loop DoEvents-Methode Double – Funktionen und Anweisungen DownPicture-Eigenschaft Drag&Drop-Operation DragDrop-Ereignis DragIcon-Eigenschaft Drag-Methode DragMode-Eigenschaft DragOver-Ereignis DrawMode-Eigenschaft DrawStyle-Eigenschaft Drehfeld-Steuerelement DriveListBox-Steuerelement Drucker Druckvorschau DTPicker-Steuerelement dynamische Arrays Dynamischer Datenaustausch E Eigenschaften Eigenschaftsseitenmodul einfache Datentypen Einfügereihenfolge Eingabeparameter Elementare Datentypen ElseIf Empty Enabled-Eigenschaft – Menübefehle Endlosschleifen Entwurfsinstanz Enum-Aufzählungen Environ-Funktion Eqv
353 135 387, 431 341
Praxis 529
618 41 469 43, 206, 262, 276 459, 485, 533 484 49, 51 92 341 606 218, 221 208, 218 612, 613 222, 342 219, 379 219, 343 612 208, 221 344 480 346 435 387, 410 284 294 435 56 488 495 202 26 161 387 178 49 36 50, 169 347 41 62 136 54
527 562 527 531 459 601 461, 531
665
Tabellenindex
Funktion / Befehl Ereignisbehandlung Ereignisroutinen Ereignisse Err-Objekt Error Event Exit Function Exit Property Exit Sub Exp-Funktion ExtractIcon-Methode F False Fehlerbehandlung Figur-Steuerelement (Shape) FileAttr-Funktion FileCopy-Anweisung FileDateTime-Funktion FileLen-Funktion FileListBox-Steuerelement FileName-Eigenschaft FileSystemObject-Objekt FileTitle-Eigenschaft FillColor-Eigenschaft FillStyle-Eigenschaft Filter-Funktion FindClose FindFirstFile FindNextFile Fix-Funktion Flache Bildlaufleiste-Steuerelement FlatScrollBar-Steuerelement Folder FontBold-Eigenschaft Font-Eigenschaft FontItalic-Eigenschaft FontName-Eigenschaft Fonts-Auflistung FontSize-Eigenschaft FontStrikethru-Eigenschaft FontUnderline-Eigenschaft
666
Referenz 204 207 44, 277 43, 50 204 181 183 97 437 39, 52 44 388, 406 138 138 121, 139 139 387, 400 269 348 349 68 135 135 135 97 435 435 350 349 350 350 304 350 350 350
Praxis 491 485 568 461, 499, 570
455 572 486, 532
535
510 528 529 528 589 589
544 544 529 639 637 639 463, 556, 639 463, 639 639 639
Tabellenindex
Funktion / Befehl
Referenz
Praxis
For Each...Next For...Next ForeColor-Eigenschaft Form_MouseDown Form_MouseMove Form_MouseUp FormatCurrency-Funktion FormatDateTime-Funktion Format-Funktion FormatNumber-Funktion FormatPercent-Funktion Form-Objekt Forms-Auflistung Formular – Ereignis zusätzliches – gebundenes Formularmodul Fortschrittsleiste-Steuerelement Frame-Steuerelement FreeFile-Funktion Function Funktionsaufruf Funktionszeiger FV-Funktion G Gebietsschema gebundenes Formular GetAllSettings-Methode Get-Anweisung GetAttr-Funktion GetFolder GetObject-Methode GetSetting-Methode GoSub GotFocus-Ereignis GoTo H HasDC-Eigenschaft hDC-Eigenschaft Height-Eigenschaft HelpContextID-Eigenschaft
40 40 330
529 469, 484 456, 460 492 492 492
73 74, 121 69 75 75 307 279, 304 306 206 25 435 387, 426 139 137, 181 176 192 98
551
463, 537 491 525 463, 567
454, 455
60, 89 262, 279 140 142
525 562 584 529
262, 280 262, 281 36 208, 222 36 350 287, 351 351 353
562
461, 549
667
Tabellenindex
Funktion / Befehl
Referenz
hexadezimale Literale Hex-Funktion Hide-Methode Hintergrundbild Hour-Funktion HScrollBar-Steuerelement hWnd-Eigenschaft I If...Then...Else IIf-Funktion ImageCombo-Steuerelement Image-Eigenschaft ImageList-Steuerelement Image-Steuerelement Imp Implements Importdeklaration Index-Eigenschaft Info-Dialog Initialize-Ereignis Input #-Anweisung InputB-Funktion InputBox-Methode Input-Funktion Instanziierung – anonyme InStrB-Funktion InStr-Funktion InStrRev-Funktion Integer – Funktionen und Anweisungen Interpreter Int-Funktion InvisibleAtRuntime-Eigenschaft IPmt-Funktion IRR-Funktion Is IsArray-Funktion IsDate-Funktion IsEmpty-Funktion IsError-Funktion
29 76 259, 311 253 121 387, 398 354
668
36 38 435 354 435, 436 387, 390 54 319 175 356 208, 224 143 142 262, 281, 429 142 196 76 76 77 49, 51 92 23 99 99 100 54 55 58 49 44, 49
Praxis 560 457, 537 469 488 545, 551 607, 618, 619 487, 532 462, 549 488 505
525 527, 567, 581 560 455 560 567 525 529 527
514 600
502
562
Tabellenindex
Funktion / Befehl
Referenz
IsMissing-Funktion IsNull-Funktion IsNumeric-Funktion IsObject-Funktion J Join-Funktion K Kalender-Steuerelement KeyDown-Ereignis KeyPress-Ereignis
178, 179 49 49, 91 49
Praxis
78 435 209, 225 209, 228 308 209, 225 144 196 195 26 388, 407, 435 273 33 161 27 353
KeyPreview-Eigenschaft KeyUp-Ereignis Kill-Anweisung Klassen Klassenbegriff Klassenmodul Kombinationsfeld-Steuerelement (ComboBox) Kommandozeilenparameter Kommentar komplexe Datentypen Konstante Kontextbezogene Hilfe Kontextmenü Kontrollkästchen-Steuerelement (CheckBox) 388, 409 Kontrollstrukturen 33 Koordinatensystem 367, 368, 369 – drehen L Label-Steuerelement 387, 393 Ländereinstellung 169 länderspezifische Darstellung 60 Laufwerk, standardmäßiges 132 Laufwerklistenfeld-Steuerelement (DriveListBox) 388, 410 Laufwerkspfad 129 Laufzeitfehler behandeln 44 Laufzeitmessung 111 LBound-Funktion 55 LCase-Funktion 79 LeftB-Funktion 79 Left-Eigenschaft 357
459, 525, 542, 619 459 459 567 567
538, 540
461, 462, 477 472
510
484 488
456
669
Tabellenindex
Funktion / Befehl
Referenz
Praxis
Left-Funktion LenB-Funktion Len-Funktion Let Line Input #-Anweisung Line-Methode Linie-Steuerelement (Line) LinkClose-Ereignis Linker LinkError-Ereignis LinkExecute-Ereignis LinkExecute-Methode LinkItem-Methode LinkMode-Eigenschaft LinkNotify-Ereignis LinkOpen-Ereignis LinkPoke-Methode LinkRequest-Methode LinkSend-Methode LinkTopic-Methode ListBox-Steuerelement Listenfeld-Steuerelement (ListBox) ListImages-Auflistung ListView-Steuerelement Literal literaler Ausdruck Load-Ereignis Load-Methode LoadPicture-Methode LoadResData-Methode LoadResPicture-Methode LoadResString-Methode Loc-Funktion Lock-Anweisung LOF-Funktion Log-Funktion logische Operatoren Long – Funktionen und Anweisungen LostFocus-Ereignis
79 79 79 33, 168 145 311 388, 411 209, 230 23 209, 231 209, 232 380
527
670
230, 234, 382 233 209, 233 381 382 382
388, 412 436 435 27, 28 29 209, 236 263, 282 263, 283 263, 284 263, 284 263, 284 146 146 147 101 54 49, 51 92 209, 222
619
461, 462, 469 495 495 495 495 495 495 495 495 495 495 495 616 616
486, 527 484 535
557
529 620
Tabellenindex
Funktion / Befehl
Referenz
LSet-Anweisung LTrim-Funktion M Main MaskColor-Eigenschaft MaskEdBox-Steuerelement MaskPicture-Eigenschaft Mauszeigerkontrolle durch Screen-Objekt MaxButton-Eigenschaft MDIChild-Eigenschaft MDIForm-Objekt MDI-Formularmodul Me (Objektvariable für Ich-Bezug) Mehrfachauswahl Menü Menübefehle – pflegen Methode Microsoft Jet-Engine Mid-Anweisung MidB-Anweisung MidB-Funktion Mid-Funktion MinButton-Eigenschaft Minute-Funktion MIRR-Funktion MkDir-Anweisung Mod Module – Arten – Benutzersteuerelement – Designer – Eigenschaftsseiten – Formular – Klassen – MDI-Formular – Standardmodul Month-Funktion MonthName-Funktion MonthView-Steuerelement MouseDown-Ereignis
80, 148 81 24, 183 358, 437 359 300 308 309 313 25, 315 201 402, 448
200 404 82 82 81 81 308 121 102 148 54 24 25 26 26 26 25 26 25 25 122 82, 122 435 209, 237
Praxis
455 606 615 606 502
463, 526 528 489, 538 542 530 568
560 560
567
463 567 464
487, 525
67 1
Tabellenindex
Funktion / Befehl
Referenz
Praxis
MouseIcon-Eigenschaft MouseMove-Ereignis MousePointer-Eigenschaft MouseUp-Ereignis Moveable-Eigenschaft Move-Methode MultiLine-Eigenschaft N Nachrichtenschleife Name-Anweisung Name-Eigenschaft Namensraum New Not Nothing Now-Funktion NPer-Funktion NPV-Funktion Null O Object Objektbegriff Objektdatentypen Objektkatalog objektorientierte Programmierung Objektvariable Oct-Funktion OCX-Datei oktale Literale OLE 2.0 OLECompleteDrag-Ereignis OLE-Container-Steuerelement (OLE) OLE-Drag&Drop-Operation OLEDragDrop-Ereignis OLEDrag-Methode OLEDragMode-Eigenschaft OLEDragOver-Ereignis OLEDropAllowed-Eigenschaft OLEDropMode-Eigenschaft OLEGiveFeedback-Ereignis OLESetData-Ereignis
359 209, 239 360 209, 237 309 383 361
502 480, 492 502 487
672
204 148 361 24, 34 163, 169, 196 54 50, 168, 196 122 103 104 50 49, 169, 197 195 196 199 196 82
514 526, 584 526 540 526, 540
585 568 567 568 603
29 196 209, 241 387, 388, 415 363 209, 242 383 210, 245 362 363 210, 247 210, 248
503 503 501 503 501 502 501 502 502
Tabellenindex
Funktion / Befehl
Referenz
Praxis
OLEStartDrag-Ereignis OLETypeAllowed-Eigenschaft On Error GoTo On Error Resume Next On...GoSub On...GoTo Open-Anweisung Operand Operationen Operatoren Optimierung Option Compare-Anweisung Option Explicit Option Private Optional optionale Argumente OptionButton-Steuerelement Optionsfeld-Steuerelement (OptionButton) Or Overlay-Methode P Paint-Ereignis PaintPicture-Methode Palette-Eigenschaft PaletteMode-Eigenschaft ParamArray Parameterübergabe Parent-Eigenschaft Partition-Funktion P-Code Pfad PICCLP32.OCX PictureBox-Steuerelement PictureClip-Steuerelement Picture-Eigenschaft Pmt-Funktion Point-Methode PopupMenu-Methode Portierung älterer Programme PPmt-Funktion Print #-Anweisung
250 362 44 44 38 38 149 29 27 29, 54 111 83 166 201 178 176 387, 424 388, 424 54 437
501
210, 253 311 309 309 179 178 364 85 24 129 272 387, 395 442 365 105 311 311
527, 535 506
557, 598
484, 485 455 531, 532, 535 525
480 606
488, 535, 550
588 574
608 488, 532
540 457
106 151
67 3
Tabellenindex
Funktion / Befehl
Referenz
Printer-Objekt Printers-Auflistung PrintForm-Methode Print-Methode Private Programm – portieren ProgressBar-Steuerelement Projekt Property Let/Get/Set Property Let/Get/Set-Funktion PropertyBag-Objekt Prozedur Prozeduraufruf PSet-Methode Pseudotransparenz Public Punkt/Komma-Problematik Put-Anweisung PV-Funktion Q QBColor-Methode Qualifizierungskontext Quellkomponente QueryUnload-Ereignis QuickInfo R Rahmen-Steuerelement (Frame) RaiseEvent Random Randomize-Anweisung Rate ReadProperties-Ereignis ReDim Reentranz Refresh-Methode Rekursion rekursive Funktion/Prozedur Rem RemoteData-Steuerelement Remove-Methode
284 297, 304 294, 312 312 34, 162, 196 23
674
Praxis
454, 460 492 453
435 24 201
175 176 312 359 196 169 153 106 263, 298 61 210, 256 353, 371, 377 388, 426 204 149 107 108 56 206 384 43, 177, 206 175 33 340 305
454 573 600 603
469, 484 525 584
461, 484 501 506
602, 613 593 603 488, 529 486 559 486, 534 486 454
Tabellenindex
Funktion / Befehl
Referenz
Replace-Funktion Reset-Anweisung Resize-Ereignis Return RGB-Methode RICHTX32.OCX RightB-Funktion Right-Funktion RightToLeft-Eigenschaft RmDir-Anweisung Rnd-Anweisung Rnd-Funktion Round-Funktion RSet-Anweisung RTF-Format RTrim-Funktion Rückruffunktion S SavePicture-Methode SaveSetting-Methode ScaleHeight-Eigenschaft ScaleLeft-Eigenschaft Scale-Methode
84 154 210, 257 36 263, 299 272 85 85 366 155 109 109 110 86 270 86 192
ScaleMode-Eigenschaft ScaleTop-Eigenschaft ScaleWidth-Eigenschaft ScaleX-Methode ScaleY-Methode Schaltflächen Schieberegler-Steuerelement (Slider) Schleifen Schlüsselwörter Screen-Objekt Scroll-Ereignis Second-Funktion Seek-Anweisung Seek-Funktion selbst definierte Klassen Select Case SendKeys-Methode
263, 299 263, 300 368 366 312 369 366 368 312 312 392 436 41 34 300 122 156 156 318 38 263, 302
Praxis
514, 533, 557 485
472, 559
593 584
489 561, 562 460, 462 461, 462, 477, 487 461, 462 460, 462 462 462 497, 562 592 484 456, 502 551 559 567 469, 542
675
Tabellenindex
Funktion / Befehl
Referenz
SendMessage-Funktion 295 sequenzielle Datei 130 Set 168, 169, 196 SetAttr-Anweisung 157 SetFocus-Methode 223, 385 Sgn-Funktion 110 Shape-Steuerelement 387, 406 Shell-Anweisung 158 ShowInTaskbar-Eigenschaft 309 Show-Methode 212, 259, 312 ShowTips-Eigenschaft 371 ShowWhatsThis-Methode 386 Sin-Funktion 112 Single 49, 51 – Funktionen und Anweisungen 92 Slider-Steuerelement 436 SLN-Funktion 112 Space-Funktion 86 Spc 152, 160 Split-Funktion 87 Sprungmarke 37 Sqr-Funktion 113 Sqr-Methode Stack Standarddialoge-Steuerelement (CommonDialog) 444 – Öffnen, Mehrfachauswahl Standardeigenschaft 200, 319 Standard-EXE 25, 26 Standardmodul 25 Standardparameter Standardschaltfläche 392 Standardsteuerelemente 322, 387 Startobjekt 24 StartupPosition-Eigenschaft 309 Static 162, 196, 200 statische Arrays 56 Statusanzeige 353 StatusBar-Steuerelement 436 Steuerelemente-Arrays 356 StrComp-Funktion 88
676
Praxis 616, 619 526 545 487
457, 525, 526
469 535
551 527 528 469 484 488 489, 527, 551, 562 528 570, 575, 639 454, 464 574 497, 562 527 486, 526, 532
608
Tabellenindex
Funktion / Befehl
Referenz
StrConv-Funktion Str-Funktion String String-Funktion StrReverse-Funktion Strukturansicht-Steuerelement strukturierte Programmierung Style-Eigenschaft Sub Sub Main Subroutine Switch SYD-Funktion Symbolleiste-Steuerelement Systemregistrierung
89, 169 87 49 90 90 436 175 372 183 24, 183 24 38 113 436 263, 275, 279, 300
T Tab TabIndex-Eigenschaft TabStop-Eigenschaft TabStrip-Steuerelement Tabulatorordnung Tabulatorreihenfolge Tag-Eigenschaft Tan-Funktion Tastenkürzel Terminate-Ereignis Textansicht TextBox-Steuerelement Textdatei Textfeld-Steuerelement (TextBox) TextHeight-Methode TextWidth-Methode Time-Anweisung Time-Funktion Timer-Ereignis Timer-Funktion Timer-Steuerelement TimeSerial-Funktion TimeValue-Funktion ToolBar-Steuerelement
Praxis 584 529
567
455
561
152, 160 223, 375 376 435 375 223, 387 376 114 210, 258 387 130 388, 427 312 312 123 123
538, 567, 551 456, 506 456, 460, 460,
542 603 615 615 535 535
599 124 387, 432 124 125 436
475, 540, 599
677
Tabellenindex
Funktion / Befehl ToolboxBitmap-Eigenschaft ToolTipText-Eigenschaft Top-Eigenschaft Transparenz TreeView-Steuerelement Trim-Funktion True Type-Datentypen TypeName-Funktion TypeOf Typkennzeichen U UBound-Funktion UCase-Funktion UNC-Pfad ungarische Notation Unicode Unload-Ereignis Unload-Methode Unlock-Anweisung Unterprogramm UpDown-Steuerelement UseMaskColor-Eigenschaft UserControl-Objekt UserMode-Eigenschaft V Val-Funktion ValidateControls-Methode Validate-Ereignis Variablen – deklarieren – initialisieren – statische vs. automatische Variant VBA VBX Vergleichsoperatoren Verkapselung Verzeichnislistenfeld-Steuerelement (DirListBox) Visible-Eigenschaft
678
Referenz
Praxis 600
377 357 359 436 90 39, 52 61 168 54 167 55 91 129 35 63 210, 259 236, 259, 263, 303 159 37 435 377, 437
456 606
567 588 588
488 529
526, 564 497, 499, 525, 526
599 601 91 312 210, 260 27, 28, 161 162 168 162 49, 50 195 195 54 388, 431 259, 378
455
471 488
567 510 526, 533
Tabellenindex
Funktion / Befehl Visual Component Manager VScrollBar-Steuerelement W WebClass-Designer Weekday-Funktion WeekdayName-Funktion Werkzeugsammlung erweitern WhatsThisButton-Eigenschaft WhatsThisHelp-Eigenschaft WhatsThisHelpID-Eigenschaft WhatsThisMode-Methode While...Wend Width-Eigenschaft Win32-API Win32api.txt Windows-Standardsteuerelemente WindowState-Eigenschaft With...End With WithEvents Wrapper-Klasse Write #-Anweisung WriteProperties-Ereignis X Xor Y Year-Funktion Z Zahlenliterale Zählschleifen Zeichenfolgenliterale Zeichenfolgenoperator Zeilennummer Zeit, Funktionen für Zeitgeber-Steuerelement (Timer) Zielkomponente ZOrder-Methode Z-Ordnung Zugriffstaste Zuweisung Zwischenablage
Referenz
Praxis
387, 398
603 545, 551
26 125 92, 126 434 310 378 353 312 40 351 187 322, 433 310 61 162, 196, 204 206 159
487 461, 549 616
557 534
603 54 126 29 41 29 54 37 114 388, 432 254, 386 387 375 29, 33 270
484
475, 599 501 615 613 538, 542, 543 572 501
679